From ea266cad53e3d49771fa38103913d3ec7a166694 Mon Sep 17 00:00:00 2001 From: dim Date: Mon, 10 Jun 2013 20:45:12 +0000 Subject: [PATCH] Vendor import of clang tags/RELEASE_33/final r183502 (effectively, 3.3 release): http://llvm.org/svn/llvm-project/cfe/tags/RELEASE_33/final@183502 --- CMakeLists.txt | 7 +- bindings/python/clang/cindex.py | 50 + bindings/python/tests/cindex/test_type.py | 63 + bindings/python/tests/cindex/util.py | 4 +- docs/ClangFormat.rst | 34 +- docs/ClangTools.rst | 41 +- docs/LanguageExtensions.rst | 107 +- docs/LibASTMatchersReference.html | 28 + docs/LibASTMatchersTutorial.rst | 84 +- docs/MemorySanitizer.rst | 9 +- docs/Modules.rst | 4 +- docs/ReleaseNotes.rst | 124 +- docs/ThreadSanitizer.rst | 13 +- docs/UsersManual.rst | 38 + docs/tools/clang.pod | 8 +- include/clang-c/Index.h | 160 +- include/clang/AST/ASTContext.h | 27 +- include/clang/AST/ASTUnresolvedSet.h | 15 +- include/clang/AST/CommentCommands.td | 16 +- include/clang/AST/CommentLexer.h | 10 +- include/clang/AST/Decl.h | 122 +- include/clang/AST/DeclBase.h | 33 +- include/clang/AST/DeclCXX.h | 101 +- include/clang/AST/DeclFriend.h | 2 +- include/clang/AST/DeclObjC.h | 19 +- include/clang/AST/EvaluatedExprVisitor.h | 11 +- include/clang/AST/Expr.h | 20 +- include/clang/AST/ExprCXX.h | 106 ++ include/clang/AST/ExprObjC.h | 3 +- include/clang/AST/Mangle.h | 10 + include/clang/AST/RawCommentList.h | 20 +- include/clang/AST/RecursiveASTVisitor.h | 30 +- include/clang/AST/Stmt.h | 287 +++- include/clang/AST/Type.h | 76 +- include/clang/AST/TypeNodes.def | 2 +- include/clang/ASTMatchers/ASTMatchers.h | 46 +- include/clang/Basic/Attr.td | 4 + include/clang/Basic/BuiltinsAArch64.def | 18 + include/clang/Basic/CapturedStmt.h | 23 + include/clang/Basic/CommentOptions.h | 5 + include/clang/Basic/DeclNodes.td | 8 +- include/clang/Basic/Diagnostic.h | 30 +- include/clang/Basic/DiagnosticASTKinds.td | 42 +- include/clang/Basic/DiagnosticCommentKinds.td | 4 + include/clang/Basic/DiagnosticDriverKinds.td | 2 - .../clang/Basic/DiagnosticFrontendKinds.td | 2 + include/clang/Basic/DiagnosticGroups.td | 32 +- include/clang/Basic/DiagnosticLexKinds.td | 15 +- include/clang/Basic/DiagnosticOptions.def | 1 - include/clang/Basic/DiagnosticParseKinds.td | 38 + include/clang/Basic/DiagnosticSemaKinds.td | 179 ++- .../Basic/DiagnosticSerializationKinds.td | 4 + include/clang/Basic/IdentifierTable.h | 2 +- include/clang/Basic/LangOptions.def | 2 +- include/clang/Basic/OnDiskHashTable.h | 2 +- include/clang/Basic/SourceManager.h | 11 + include/clang/Basic/Specifiers.h | 30 +- include/clang/Basic/StmtNodes.td | 3 + include/clang/Basic/TargetBuiltins.h | 9 + include/clang/Basic/TargetInfo.h | 16 +- include/clang/Basic/TokenKinds.def | 6 + include/clang/Basic/arm_neon.td | 180 ++- include/clang/Driver/ArgList.h | 11 +- include/clang/Driver/CC1Options.td | 3 - include/clang/Driver/Driver.h | 7 +- include/clang/Driver/Options.td | 24 +- include/clang/Driver/ToolChain.h | 13 +- include/clang/Format/Format.h | 11 +- .../Frontend/ChainedDiagnosticConsumer.h | 6 - .../clang/Frontend/ChainedIncludesSource.h | 2 +- include/clang/Frontend/CodeGenOptions.def | 3 +- include/clang/Frontend/CodeGenOptions.h | 2 +- include/clang/Frontend/CompilerInstance.h | 8 +- include/clang/Frontend/LogDiagnosticPrinter.h | 2 - include/clang/Frontend/TextDiagnosticBuffer.h | 2 - .../clang/Frontend/TextDiagnosticPrinter.h | 1 - .../clang/Frontend/VerifyDiagnosticConsumer.h | 14 +- {lib => include/clang}/Lex/MacroArgs.h | 0 include/clang/Lex/ModuleMap.h | 3 +- include/clang/Lex/PPCallbacks.h | 39 +- include/clang/Lex/PreprocessingRecord.h | 2 +- include/clang/Lex/Preprocessor.h | 10 +- include/clang/Parse/CMakeLists.txt | 5 + include/clang/Parse/Makefile | 8 +- include/clang/Parse/Parser.h | 39 +- .../clang/Rewrite/Frontend/FixItRewriter.h | 2 - include/clang/Sema/AttributeList.h | 89 +- include/clang/Sema/DeclSpec.h | 47 +- include/clang/Sema/Initialization.h | 37 +- include/clang/Sema/ObjCMethodList.h | 23 +- include/clang/Sema/Ownership.h | 9 + include/clang/Sema/ScopeInfo.h | 52 +- include/clang/Sema/Sema.h | 118 +- include/clang/Sema/Template.h | 40 +- include/clang/Sema/TemplateDeduction.h | 4 +- include/clang/Serialization/ASTBitCodes.h | 8 + include/clang/Serialization/ASTReader.h | 17 +- include/clang/Serialization/ASTWriter.h | 4 + .../clang/Serialization/GlobalModuleIndex.h | 5 + .../StaticAnalyzer/Core/AnalyzerOptions.h | 2 +- .../Core/BugReporter/BugReporter.h | 9 - .../Core/BugReporter/BugReporterVisitor.h | 7 +- .../Core/BugReporter/PathDiagnostic.h | 23 +- .../StaticAnalyzer/Core/CheckerManager.h | 24 +- .../Core/PathSensitive/BasicValueFactory.h | 2 +- .../Core/PathSensitive/CheckerContext.h | 4 - .../Core/PathSensitive/ExplodedGraph.h | 4 + .../Core/PathSensitive/MemRegion.h | 32 +- .../Core/PathSensitive/ProgramState.h | 11 +- .../Core/PathSensitive/SValBuilder.h | 9 +- .../StaticAnalyzer/Core/PathSensitive/SVals.h | 18 +- .../StaticAnalyzer/Core/PathSensitive/Store.h | 15 - .../Core/PathSensitive/SymbolManager.h | 67 +- .../Frontend/CheckerRegistration.h | 2 +- lib/ARCMigrate/ARCMT.cpp | 8 +- lib/ARCMigrate/TransAPIUses.cpp | 4 +- lib/ARCMigrate/TransRetainReleaseDealloc.cpp | 4 +- lib/ARCMigrate/Transforms.h | 2 +- lib/AST/ASTContext.cpp | 209 ++- lib/AST/ASTDumper.cpp | 7 +- lib/AST/ASTImporter.cpp | 11 +- lib/AST/Comment.cpp | 2 +- lib/AST/CommentLexer.cpp | 7 +- lib/AST/CommentParser.cpp | 29 +- lib/AST/Decl.cpp | 111 +- lib/AST/DeclBase.cpp | 70 +- lib/AST/DeclCXX.cpp | 52 +- lib/AST/DeclObjC.cpp | 75 +- lib/AST/DeclPrinter.cpp | 53 +- lib/AST/Expr.cpp | 40 +- lib/AST/ExprCXX.cpp | 14 +- lib/AST/ExprClassification.cpp | 5 + lib/AST/ExprConstant.cpp | 1251 ++++++++++++---- lib/AST/ItaniumMangle.cpp | 34 +- lib/AST/MicrosoftCXXABI.cpp | 20 +- lib/AST/MicrosoftMangle.cpp | 191 +-- lib/AST/RawCommentList.cpp | 21 +- lib/AST/Stmt.cpp | 145 +- lib/AST/StmtPrinter.cpp | 35 +- lib/AST/StmtProfile.cpp | 14 + lib/AST/Type.cpp | 24 +- lib/AST/TypePrinter.cpp | 6 +- lib/Analysis/BodyFarm.cpp | 8 +- lib/Analysis/CFG.cpp | 5 + lib/Analysis/ThreadSafety.cpp | 58 +- lib/Basic/Diagnostic.cpp | 17 + lib/Basic/IdentifierTable.cpp | 2 +- lib/Basic/SourceManager.cpp | 41 +- lib/Basic/TargetInfo.cpp | 5 +- lib/Basic/Targets.cpp | 306 +++- lib/Basic/Version.cpp | 2 +- lib/CodeGen/ABIInfo.h | 2 + lib/CodeGen/CGAtomic.cpp | 11 +- lib/CodeGen/CGBlocks.cpp | 70 +- lib/CodeGen/CGBuiltin.cpp | 31 +- lib/CodeGen/CGCXXABI.cpp | 15 + lib/CodeGen/CGCXXABI.h | 44 +- lib/CodeGen/CGCall.cpp | 30 +- lib/CodeGen/CGClass.cpp | 11 +- lib/CodeGen/CGCleanup.cpp | 12 +- lib/CodeGen/CGDebugInfo.cpp | 35 +- lib/CodeGen/CGDebugInfo.h | 5 +- lib/CodeGen/CGDecl.cpp | 11 +- lib/CodeGen/CGDeclCXX.cpp | 89 +- lib/CodeGen/CGException.cpp | 12 +- lib/CodeGen/CGExpr.cpp | 303 ++-- lib/CodeGen/CGExprAgg.cpp | 11 +- lib/CodeGen/CGExprComplex.cpp | 4 + lib/CodeGen/CGExprConstant.cpp | 6 + lib/CodeGen/CGExprScalar.cpp | 8 +- lib/CodeGen/CGObjC.cpp | 12 +- lib/CodeGen/CGObjCMac.cpp | 20 +- lib/CodeGen/CGObjCRuntime.cpp | 2 +- lib/CodeGen/CGRTTI.cpp | 6 + lib/CodeGen/CGRecordLayoutBuilder.cpp | 8 +- lib/CodeGen/CGStmt.cpp | 49 +- lib/CodeGen/CodeGenFunction.cpp | 66 +- lib/CodeGen/CodeGenFunction.h | 78 +- lib/CodeGen/CodeGenModule.cpp | 157 +- lib/CodeGen/CodeGenModule.h | 100 +- lib/CodeGen/CodeGenTBAA.cpp | 82 +- lib/CodeGen/CodeGenTBAA.h | 12 +- lib/CodeGen/CodeGenTypes.cpp | 14 +- lib/CodeGen/CodeGenTypes.h | 13 +- lib/CodeGen/ItaniumCXXABI.cpp | 192 ++- lib/CodeGen/MicrosoftCXXABI.cpp | 488 ++++++- lib/CodeGen/ModuleBuilder.cpp | 14 +- lib/CodeGen/TargetInfo.cpp | 526 +++++-- lib/Driver/ArgList.cpp | 15 + lib/Driver/Driver.cpp | 56 +- lib/Driver/SanitizerArgs.h | 8 +- lib/Driver/ToolChain.cpp | 8 + lib/Driver/ToolChains.cpp | 249 +++- lib/Driver/ToolChains.h | 13 + lib/Driver/Tools.cpp | 440 ++++-- lib/Driver/WindowsToolChain.cpp | 4 + lib/Edit/EditedSource.cpp | 2 + lib/Format/BreakableToken.cpp | 179 +++ lib/Format/BreakableToken.h | 240 +++ lib/Format/CMakeLists.txt | 4 +- lib/Format/Format.cpp | 747 +++------- lib/Format/TokenAnnotator.cpp | 198 +-- lib/Format/TokenAnnotator.h | 55 +- lib/Format/UnwrappedLineParser.cpp | 132 +- lib/Format/UnwrappedLineParser.h | 24 +- lib/Format/WhitespaceManager.cpp | 211 +++ lib/Format/WhitespaceManager.h | 119 ++ lib/Frontend/ASTMerge.cpp | 7 +- lib/Frontend/ASTUnit.cpp | 57 +- lib/Frontend/ChainedIncludesSource.cpp | 1 + lib/Frontend/CompilerInstance.cpp | 20 +- lib/Frontend/CompilerInvocation.cpp | 33 +- lib/Frontend/DiagnosticRenderer.cpp | 5 +- lib/Frontend/FrontendAction.cpp | 2 + lib/Frontend/InitHeaderSearch.cpp | 9 +- lib/Frontend/InitPreprocessor.cpp | 7 +- lib/Frontend/LogDiagnosticPrinter.cpp | 5 - lib/Frontend/PrintPreprocessedOutput.cpp | 87 +- lib/Frontend/SerializedDiagnosticPrinter.cpp | 4 - lib/Frontend/TextDiagnostic.cpp | 4 +- lib/Frontend/TextDiagnosticBuffer.cpp | 3 - lib/Frontend/TextDiagnosticPrinter.cpp | 5 - lib/Frontend/VerifyDiagnosticConsumer.cpp | 90 +- lib/Frontend/Warnings.cpp | 1 - lib/Headers/CMakeLists.txt | 1 + lib/Headers/avxintrin.h | 48 +- lib/Headers/emmintrin.h | 24 +- lib/Headers/stddef.h | 14 + lib/Headers/stdint.h | 47 + lib/Headers/xopintrin.h | 4 +- lib/Lex/Lexer.cpp | 17 +- lib/Lex/LiteralSupport.cpp | 9 +- lib/Lex/MacroArgs.cpp | 2 +- lib/Lex/ModuleMap.cpp | 60 +- lib/Lex/PPDirectives.cpp | 19 +- lib/Lex/PPMacroExpansion.cpp | 29 +- lib/Lex/Pragma.cpp | 267 ++-- lib/Lex/PreprocessingRecord.cpp | 3 +- lib/Lex/Preprocessor.cpp | 8 +- lib/Lex/TokenLexer.cpp | 2 +- lib/Parse/CMakeLists.txt | 1 + lib/Parse/ParseCXXInlineMethods.cpp | 39 +- lib/Parse/ParseDecl.cpp | 256 +++- lib/Parse/ParseDeclCXX.cpp | 79 +- lib/Parse/ParseExpr.cpp | 11 +- lib/Parse/ParseExprCXX.cpp | 6 +- lib/Parse/ParseInit.cpp | 2 +- lib/Parse/ParseObjc.cpp | 22 +- lib/Parse/ParsePragma.cpp | 96 ++ lib/Parse/ParsePragma.h | 8 + lib/Parse/ParseStmt.cpp | 400 ++++- lib/Parse/ParseTemplate.cpp | 133 +- lib/Parse/ParseTentative.cpp | 14 +- lib/Parse/Parser.cpp | 14 +- lib/Rewrite/Frontend/FixItRewriter.cpp | 5 - lib/Rewrite/Frontend/InclusionRewriter.cpp | 160 +- lib/Sema/AnalysisBasedWarnings.cpp | 8 +- lib/Sema/AttributeList.cpp | 2 + lib/Sema/DeclSpec.cpp | 126 +- lib/Sema/ScopeInfo.cpp | 1 + lib/Sema/Sema.cpp | 43 +- lib/Sema/SemaAccess.cpp | 18 +- lib/Sema/SemaCast.cpp | 16 +- lib/Sema/SemaChecking.cpp | 21 +- lib/Sema/SemaCodeComplete.cpp | 13 +- lib/Sema/SemaDecl.cpp | 509 ++++--- lib/Sema/SemaDeclAttr.cpp | 14 +- lib/Sema/SemaDeclCXX.cpp | 1156 ++++++++++----- lib/Sema/SemaDeclObjC.cpp | 93 +- lib/Sema/SemaExceptionSpec.cpp | 10 +- lib/Sema/SemaExpr.cpp | 398 +++-- lib/Sema/SemaExprCXX.cpp | 145 +- lib/Sema/SemaExprMember.cpp | 71 +- lib/Sema/SemaExprObjC.cpp | 78 +- lib/Sema/SemaInit.cpp | 402 +++-- lib/Sema/SemaLambda.cpp | 10 +- lib/Sema/SemaLookup.cpp | 58 +- lib/Sema/SemaObjCProperty.cpp | 49 +- lib/Sema/SemaOpenMP.cpp | 4 +- lib/Sema/SemaOverload.cpp | 328 +++-- lib/Sema/SemaPseudoObject.cpp | 131 +- lib/Sema/SemaStmt.cpp | 308 +++- lib/Sema/SemaStmtAsm.cpp | 314 +--- lib/Sema/SemaTemplate.cpp | 4 +- lib/Sema/SemaTemplateDeduction.cpp | 270 ++-- lib/Sema/SemaTemplateInstantiate.cpp | 7 +- lib/Sema/SemaTemplateInstantiateDecl.cpp | 132 +- lib/Sema/SemaTemplateVariadic.cpp | 1 + lib/Sema/SemaType.cpp | 118 +- lib/Sema/TreeTransform.h | 173 ++- lib/Serialization/ASTCommon.cpp | 3 + lib/Serialization/ASTReader.cpp | 212 ++- lib/Serialization/ASTReaderDecl.cpp | 24 +- lib/Serialization/ASTReaderInternals.h | 2 + lib/Serialization/ASTReaderStmt.cpp | 125 +- lib/Serialization/ASTWriter.cpp | 83 +- lib/Serialization/ASTWriterDecl.cpp | 27 +- lib/Serialization/ASTWriterStmt.cpp | 90 +- lib/Serialization/GlobalModuleIndex.cpp | 31 + .../Checkers/AllocationDiagnostics.cpp | 24 + .../Checkers/AllocationDiagnostics.h | 31 + .../Checkers/BasicObjCFoundationChecks.cpp | 112 +- lib/StaticAnalyzer/Checkers/CMakeLists.txt | 3 +- .../Checkers/CStringChecker.cpp | 62 + .../Checkers/CStringSyntaxChecker.cpp | 2 + .../Checkers/CheckSecuritySyntaxOnly.cpp | 6 +- lib/StaticAnalyzer/Checkers/Checkers.td | 9 +- .../Checkers/DeadStoresChecker.cpp | 14 +- lib/StaticAnalyzer/Checkers/DebugCheckers.cpp | 2 +- .../Checkers/DynamicTypePropagation.cpp | 18 +- .../Checkers/IvarInvalidationChecker.cpp | 47 +- lib/StaticAnalyzer/Checkers/MallocChecker.cpp | 220 ++- .../Checkers/MallocSizeofChecker.cpp | 2 +- .../Checkers/RetainCountChecker.cpp | 158 +- .../Checkers/ReturnUndefChecker.cpp | 13 +- lib/StaticAnalyzer/Core/BugReporter.cpp | 717 +++++++-- .../Core/BugReporterVisitors.cpp | 206 ++- lib/StaticAnalyzer/Core/CallEvent.cpp | 14 +- lib/StaticAnalyzer/Core/Environment.cpp | 42 +- lib/StaticAnalyzer/Core/ExprEngine.cpp | 48 +- lib/StaticAnalyzer/Core/ExprEngineC.cpp | 79 +- .../Core/ExprEngineCallAndReturn.cpp | 48 +- lib/StaticAnalyzer/Core/MemRegion.cpp | 62 +- lib/StaticAnalyzer/Core/PathDiagnostic.cpp | 89 +- lib/StaticAnalyzer/Core/PlistDiagnostics.cpp | 2 + lib/StaticAnalyzer/Core/ProgramState.cpp | 12 +- lib/StaticAnalyzer/Core/RegionStore.cpp | 276 ++-- lib/StaticAnalyzer/Core/SValBuilder.cpp | 105 +- lib/StaticAnalyzer/Core/SVals.cpp | 20 +- .../Core/SimpleConstraintManager.cpp | 23 +- lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp | 10 +- lib/StaticAnalyzer/Core/Store.cpp | 88 +- lib/StaticAnalyzer/Core/SymbolManager.cpp | 4 +- .../Frontend/CheckerRegistration.cpp | 5 +- test/ARCMT/check-with-serialized-diag.m | 12 +- test/ARCMT/migrate-plist-output.m | 6 +- test/ARCMT/objcmt-subscripting-literals.m | 1 + .../objcmt-subscripting-literals.m.result | 1 + test/ASTMerge/function.c | 8 +- .../Inputs/system-header-simulator-cxx.h | 6 + .../Analysis/Inputs/system-header-simulator.h | 11 +- ...Malloc+MismatchedDeallocator+NewDelete.cpp | 46 +- .../Malloc+NewDelete_intersections.cpp | 7 +- ... => MismatchedDeallocator-checker-test.mm} | 0 .../MismatchedDeallocator-path-notes.cpp | 159 ++ test/Analysis/NSContainers.m | 16 +- ...te+MismatchedDeallocator_intersections.cpp | 3 +- test/Analysis/NewDelete-checker-test.cpp | 83 +- test/Analysis/NewDelete-custom.cpp | 35 +- test/Analysis/NewDelete-intersections.mm | 18 +- test/Analysis/NewDelete-path-notes.cpp | 670 ++++++--- test/Analysis/NewDelete-variadic.cpp | 2 +- test/Analysis/analyzer-config.c | 6 +- test/Analysis/analyzer-config.cpp | 6 +- ...{bool-assignment.cpp => bool-assignment.c} | 23 +- test/Analysis/bool-assignment2.c | 35 - test/Analysis/casts.c | 38 +- .../conditional-operator-path-notes.c | 16 +- test/Analysis/conditional-operator.cpp | 17 + test/Analysis/coverage.c | 12 +- test/Analysis/cstring-syntax-cxx.cpp | 5 + test/Analysis/dead-stores.c | 22 + test/Analysis/derived-to-base.cpp | 87 ++ .../diagnostics/deref-track-symbolic-region.c | 354 ++++- .../diagnostics/explicit-suppression.cpp | 65 +- test/Analysis/diagnostics/undef-value-param.c | 90 +- test/Analysis/diagnostics/undef-value-param.m | 193 +-- test/Analysis/enum.cpp | 26 + test/Analysis/global-region-invalidation.c | 5 +- test/Analysis/global_region_invalidation.mm | 155 +- test/Analysis/inline-plist.c | 186 ++- test/Analysis/inline-unique-reports.c | 454 +++--- test/Analysis/inline.cpp | 31 +- test/Analysis/inlining/containers.cpp | 13 +- .../inlining/dyn-dispatch-bifurcate.cpp | 5 + .../inlining/eager-reclamation-path-notes.c | 144 +- .../inlining/eager-reclamation-path-notes.cpp | 36 +- .../inlining/false-positive-suppression.c | 21 + .../inlining/false-positive-suppression.m | 38 + .../inlining/inline-defensive-checks.c | 13 + .../inlining/inline-defensive-checks.cpp | 18 + test/Analysis/inlining/path-notes.c | 942 +++++++++--- test/Analysis/inlining/path-notes.cpp | 1299 +++++++++++++++-- test/Analysis/inlining/path-notes.m | 438 ++++-- test/Analysis/malloc-annotations.c | 14 +- test/Analysis/malloc-interprocedural.c | 8 +- test/Analysis/malloc-plist.c | 630 +++++++- test/Analysis/malloc.c | 46 +- test/Analysis/malloc.cpp | 8 +- test/Analysis/malloc.mm | 12 +- test/Analysis/misc-ps.c | 12 + test/Analysis/new.cpp | 20 +- test/Analysis/null-deref-path-notes.m | 91 +- test/Analysis/objc-boxing.m | 2 +- test/Analysis/objc-for.m | 14 +- test/Analysis/objc-string.mm | 39 + test/Analysis/objc-subscript.m | 4 +- test/Analysis/objc_invalidation.m | 41 + test/Analysis/operator-calls.cpp | 36 + test/Analysis/plist-output-alternate.m | 80 +- test/Analysis/plist-output.m | 738 ++++++++-- test/Analysis/pointer-to-member.cpp | 37 + test/Analysis/properties.m | 4 +- test/Analysis/reference.cpp | 12 +- test/Analysis/retain-release-path-notes-gc.m | 46 +- test/Analysis/retain-release-path-notes.m | 841 ++++++++++- test/Analysis/retain-release.m | 101 +- test/Analysis/retain-release.mm | 55 +- test/Analysis/stack-addr-ps.cpp | 41 +- test/Analysis/stackaddrleak.c | 28 +- test/Analysis/string.c | 51 + test/Analysis/svalbuilder-logic.c | 8 + test/Analysis/taint-tester.c | 15 +- test/Analysis/temporaries.cpp | 35 +- test/Analysis/uninit-vals-ps.c | 17 +- test/Analysis/uninit-vals.m | 141 ++ test/Analysis/unix-fns.c | 76 +- .../basic.lookup/basic.lookup.unqual/p14.cpp | 23 + test/CXX/basic/basic.types/p10.cpp | 24 +- test/CXX/class/class.friend/p6.cpp | 12 +- test/CXX/dcl.dcl/dcl.link/p7-2.cpp | 7 + .../CXX/dcl.dcl/dcl.spec/dcl.constexpr/p1.cpp | 4 +- .../CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp | 202 ++- .../CXX/dcl.dcl/dcl.spec/dcl.constexpr/p4.cpp | 38 +- .../CXX/dcl.dcl/dcl.spec/dcl.constexpr/p5.cpp | 2 +- .../CXX/dcl.dcl/dcl.spec/dcl.constexpr/p6.cpp | 4 +- .../CXX/dcl.dcl/dcl.spec/dcl.constexpr/p8.cpp | 14 +- .../dcl.spec/dcl.type/dcl.spec.auto/p2.cpp | 2 +- .../dcl.spec/dcl.type/dcl.spec.auto/p3-1y.cpp | 74 + .../dcl.spec/dcl.type/dcl.spec.auto/p5.cpp | 2 +- .../dcl.spec/dcl.type/dcl.spec.auto/p6-1y.cpp | 97 ++ .../dcl.type/dcl.type.simple/p5-cxx0x.cpp | 6 +- test/CXX/dcl.dcl/p4-0x.cpp | 8 +- .../dcl.fct.def/dcl.fct.def.default/p2.cpp | 4 +- .../dcl.decl/dcl.init/dcl.init.aggr/p1-0x.cpp | 13 +- .../dcl.decl/dcl.init/dcl.init.aggr/p7.cpp | 23 + .../dcl.decl/dcl.init/dcl.init.ref/p5-0x.cpp | 34 + .../CXX/dcl.decl/dcl.init/dcl.init.ref/p5.cpp | 23 + test/CXX/except/except.spec/p1.cpp | 2 +- test/CXX/except/except.spec/p14.cpp | 23 + test/CXX/expr/expr.ass/p9-cxx11.cpp | 4 +- test/CXX/expr/expr.const/p2-0x.cpp | 8 +- test/CXX/expr/expr.const/p3-0x.cpp | 2 +- test/CXX/expr/expr.const/p5-0x.cpp | 12 +- .../expr/expr.post/expr.const.cast/p1-0x.cpp | 17 +- .../expr.prim.lambda/default-arguments.cpp | 2 +- .../expr/expr.prim/expr.prim.lambda/p5.cpp | 8 +- test/CXX/expr/expr.unary/expr.sizeof/p1.cpp | 20 + test/CXX/over/over.oper/over.literal/p2.cpp | 9 + test/CXX/special/class.inhctor/elsewhere.cpp | 7 + test/CXX/special/class.inhctor/p1.cpp | 41 +- test/CXX/special/class.inhctor/p2.cpp | 36 +- test/CXX/special/class.inhctor/p3.cpp | 10 + test/CXX/special/class.inhctor/p4.cpp | 6 +- test/CXX/special/class.inhctor/p7.cpp | 18 + test/CXX/special/class.inhctor/p8.cpp | 9 + .../stmt.stmt/stmt.iter/stmt.ranged/p1.cpp | 10 + .../stmt.select/stmt.switch/p2-0x.cpp | 4 +- .../temp/temp.arg/temp.arg.nontype/p1-11.cpp | 11 +- .../multi-level-substitution.cpp | 10 +- .../temp/temp.spec/temp.explicit/p1-0x.cpp | 2 +- test/CodeGen/2010-02-10-PointerName.c | 4 +- test/CodeGen/arm-asm-diag.c | 23 + test/CodeGen/builtins-aarch64.c | 6 + test/CodeGen/c-strings.c | 13 +- test/CodeGen/le32-regparm.c | 41 +- test/CodeGen/linetable-endscope.c | 17 + test/CodeGen/linux-arm-atomic.c | 11 + test/CodeGen/may-alias.c | 15 +- test/CodeGen/mips-inline-asm-modifiers.c | 35 + test/CodeGen/ms-inline-asm-64.c | 34 +- test/CodeGen/ms-inline-asm.c | 116 +- test/CodeGen/ms-inline-asm.cpp | 99 +- test/CodeGen/mult-alt-generic.c | 2 + test/CodeGen/pragma-pack-1.c | 63 +- test/CodeGen/sparc-target-data.c | 5 + test/CodeGen/systemz-inline-asm.c | 131 ++ test/CodeGen/tbaa-class.cpp | 226 +++ test/CodeGen/tbaa-struct.cpp | 30 +- test/CodeGen/tbaa.cpp | 134 +- test/CodeGen/thread-specifier.c | 3 + test/CodeGen/x86_32-arguments-win32.c | 6 +- .../cxx0x-initializer-stdinitializerlist.cpp | 25 + .../cxx11-thread-local-reference.cpp | 26 + test/CodeGenCXX/cxx11-thread-local.cpp | 173 +++ .../cxx1y-initializer-aggregate.cpp | 74 + test/CodeGenCXX/debug-info-namespace.cpp | 24 +- test/CodeGenCXX/extern-c.cpp | 27 + test/CodeGenCXX/inheriting-constructor.cpp | 10 + test/CodeGenCXX/linetable-cleanup.cpp | 24 + test/CodeGenCXX/mangle-ms-arg-qualifiers.cpp | 45 + .../mangle-ms-return-qualifiers.cpp | 9 + test/CodeGenCXX/mangle-ms-templates.cpp | 28 +- test/CodeGenCXX/mangle-ms.cpp | 7 +- .../microsoft-abi-member-pointers.cpp | 326 ++++- .../microsoft-abi-sret-and-byval.cpp | 169 +++ test/CodeGenCXX/pr15753.cpp | 12 + test/CodeGenCXX/scoped-enums-debug-info.cpp | 26 + test/CodeGenCXX/scoped-enums.cpp | 8 + test/CodeGenCXX/throw-expressions.cpp | 28 +- test/CodeGenCXX/tls-init-funcs.cpp | 10 + test/CodeGenCXX/vtable-debug-info.cpp | 2 +- test/CodeGenObjC/arc-blocks.m | 39 + test/CodeGenObjC/arc-linetable.m | 101 ++ test/CodeGenObjC/autorelease.m | 27 +- .../debug-info-block-captured-self.m | 1 - test/CodeGenObjC/debug-info-block-line.m | 11 +- test/CodeGenObjC/debug-info-blocks.m | 5 +- test/CodeGenObjC/encode-test-3.m | 8 +- test/CodeGenObjC/metadata-symbols-32.m | 54 +- test/CodeGenObjC/metadata-symbols-64.m | 69 +- test/CodeGenObjC/metadata_symbols.m | 8 +- test/CodeGenObjC/objc-align.m | 18 +- test/CodeGenObjC/objc-fixed-enum.m | 64 + test/CodeGenObjC/synthesize_ivar-cont-class.m | 4 +- test/CodeGenObjC/tentative-cfconstantstring.m | 43 + test/CodeGenObjCXX/arc.mm | 22 + test/CodeGenObjCXX/lambda-expressions.mm | 22 + test/CodeGenObjCXX/mangle.mm | 26 +- test/Driver/Inputs/fedora_18_tree/lib/.keep | 0 .../Inputs/fedora_18_tree/usr/lib/crt1.o | 0 .../Inputs/fedora_18_tree/usr/lib/crti.o | 0 .../Inputs/fedora_18_tree/usr/lib/crtn.o | 0 .../4.7.2/crtbegin.o | 0 .../4.7.2/crtend.o | 0 test/Driver/Inputs/mips_cs_tree/bin/.keep | 0 .../gcc/mips-linux-gnu/4.6.3/64/crtbegin.o | 0 .../lib/gcc/mips-linux-gnu/4.6.3/64/crtend.o | 0 .../lib/gcc/mips-linux-gnu/4.6.3/crtbegin.o | 0 .../lib/gcc/mips-linux-gnu/4.6.3/crtend.o | 0 .../gcc/mips-linux-gnu/4.6.3/el/64/crtbegin.o | 0 .../gcc/mips-linux-gnu/4.6.3/el/64/crtend.o | 0 .../gcc/mips-linux-gnu/4.6.3/el/crtbegin.o | 0 .../lib/gcc/mips-linux-gnu/4.6.3/el/crtend.o | 0 .../4.6.3/include-fixed/64/.keep | 0 .../4.6.3/include-fixed/el/64/.keep | 0 .../4.6.3/include-fixed/micromips/el/.keep | 0 .../micromips/soft-float/el/.keep | 0 .../4.6.3/include-fixed/mips16/el/.keep | 0 .../include-fixed/mips16/soft-float/el/.keep | 0 .../4.6.3/include-fixed/soft-float/64/.keep | 0 .../include-fixed/soft-float/el/64/.keep | 0 .../gcc/mips-linux-gnu/4.6.3/include/.keep | 0 .../mips-linux-gnu/4.6.3/micromips/crtbegin.o | 0 .../mips-linux-gnu/4.6.3/micromips/crtend.o | 0 .../4.6.3/micromips/el/crtbegin.o | 0 .../4.6.3/micromips/el/crtend.o | 0 .../4.6.3/micromips/soft-float/crtbegin.o | 0 .../4.6.3/micromips/soft-float/crtend.o | 0 .../4.6.3/micromips/soft-float/el/crtbegin.o | 0 .../4.6.3/micromips/soft-float/el/crtend.o | 0 .../mips-linux-gnu/4.6.3/mips16/crtbegin.o | 0 .../gcc/mips-linux-gnu/4.6.3/mips16/crtend.o | 0 .../mips-linux-gnu/4.6.3/mips16/el/crtbegin.o | 0 .../mips-linux-gnu/4.6.3/mips16/el/crtend.o | 0 .../4.6.3/mips16/soft-float/crtbegin.o | 0 .../4.6.3/mips16/soft-float/crtend.o | 0 .../4.6.3/mips16/soft-float/el/crtbegin.o | 0 .../4.6.3/mips16/soft-float/el/crtend.o | 0 .../4.6.3/soft-float/64/crtbegin.o | 0 .../4.6.3/soft-float/64/crtend.o | 0 .../4.6.3/soft-float/crtbegin.o | 0 .../mips-linux-gnu/4.6.3/soft-float/crtend.o | 0 .../4.6.3/soft-float/el/64/crtbegin.o | 0 .../4.6.3/soft-float/el/64/crtend.o | 0 .../4.6.3/soft-float/el/crtbegin.o | 0 .../4.6.3/soft-float/el/crtend.o | 0 .../include/c++/4.6.3/mips-linux-gnu/.keep | 0 .../mips_cs_tree/mips-linux-gnu/lib/el/.keep | 0 .../mips-linux-gnu/lib/micromips/el/.keep | 0 .../lib/micromips/soft-float/el/.keep | 0 .../mips-linux-gnu/lib/mips16/el/.keep | 0 .../lib/mips16/soft-float/el/.keep | 0 .../mips-linux-gnu/lib/soft-float/el/.keep | 0 .../mips-linux-gnu/lib64/el/.keep | 0 .../mips-linux-gnu/lib64/soft-float/el/.keep | 0 .../mips-linux-gnu/libc/el/lib/.keep | 0 .../mips-linux-gnu/libc/el/lib64/.keep | 0 .../mips-linux-gnu/libc/el/usr/lib/crt1.o | 0 .../mips-linux-gnu/libc/el/usr/lib/crti.o | 0 .../mips-linux-gnu/libc/el/usr/lib/crtn.o | 0 .../mips-linux-gnu/libc/el/usr/lib64/crt1.o | 0 .../mips-linux-gnu/libc/el/usr/lib64/crti.o | 0 .../mips-linux-gnu/libc/el/usr/lib64/crtn.o | 0 .../mips-linux-gnu/libc/lib/.keep | 0 .../mips-linux-gnu/libc/lib64/.keep | 0 .../libc/micromips/el/lib/.keep | 0 .../libc/micromips/el/usr/lib/crt1.o | 0 .../libc/micromips/el/usr/lib/crti.o | 0 .../libc/micromips/el/usr/lib/crtn.o | 0 .../mips-linux-gnu/libc/micromips/lib/.keep | 0 .../libc/micromips/soft-float/el/lib/.keep | 0 .../micromips/soft-float/el/usr/lib/crt1.o | 0 .../micromips/soft-float/el/usr/lib/crti.o | 0 .../micromips/soft-float/el/usr/lib/crtn.o | 0 .../libc/micromips/soft-float/lib/.keep | 0 .../libc/micromips/soft-float/usr/lib/crt1.o | 0 .../libc/micromips/soft-float/usr/lib/crti.o | 0 .../libc/micromips/soft-float/usr/lib/crtn.o | 0 .../libc/micromips/usr/lib/crt1.o | 0 .../libc/micromips/usr/lib/crti.o | 0 .../libc/micromips/usr/lib/crtn.o | 0 .../mips-linux-gnu/libc/mips16/el/lib/.keep | 0 .../libc/mips16/el/usr/lib/crt1.o | 0 .../libc/mips16/el/usr/lib/crti.o | 0 .../libc/mips16/el/usr/lib/crtn.o | 0 .../mips-linux-gnu/libc/mips16/lib/.keep | 0 .../libc/mips16/soft-float/el/lib/.keep | 0 .../libc/mips16/soft-float/el/usr/lib/crt1.o | 0 .../libc/mips16/soft-float/el/usr/lib/crti.o | 0 .../libc/mips16/soft-float/el/usr/lib/crtn.o | 0 .../libc/mips16/soft-float/lib/.keep | 0 .../libc/mips16/soft-float/usr/lib/crt1.o | 0 .../libc/mips16/soft-float/usr/lib/crti.o | 0 .../libc/mips16/soft-float/usr/lib/crtn.o | 0 .../mips-linux-gnu/libc/mips16/usr/lib/crt1.o | 0 .../mips-linux-gnu/libc/mips16/usr/lib/crti.o | 0 .../mips-linux-gnu/libc/mips16/usr/lib/crtn.o | 0 .../libc/soft-float/el/lib/.keep | 0 .../libc/soft-float/el/lib64/.keep | 0 .../libc/soft-float/el/usr/lib/crt1.o | 0 .../libc/soft-float/el/usr/lib/crti.o | 0 .../libc/soft-float/el/usr/lib/crtn.o | 0 .../libc/soft-float/el/usr/lib64/crt1.o | 0 .../libc/soft-float/el/usr/lib64/crti.o | 0 .../libc/soft-float/el/usr/lib64/crtn.o | 0 .../mips-linux-gnu/libc/soft-float/lib/.keep | 0 .../libc/soft-float/lib64/.keep | 0 .../libc/soft-float/usr/lib/crt1.o | 0 .../libc/soft-float/usr/lib/crti.o | 0 .../libc/soft-float/usr/lib/crtn.o | 0 .../libc/soft-float/usr/lib64/crt1.o | 0 .../libc/soft-float/usr/lib64/crti.o | 0 .../libc/soft-float/usr/lib64/crtn.o | 0 .../mips-linux-gnu/libc/usr/include/.keep | 0 .../mips-linux-gnu/libc/usr/lib/crt1.o | 0 .../mips-linux-gnu/libc/usr/lib/crti.o | 0 .../mips-linux-gnu/libc/usr/lib/crtn.o | 0 .../mips-linux-gnu/libc/usr/lib64/crt1.o | 0 .../mips-linux-gnu/libc/usr/lib64/crti.o | 0 .../mips-linux-gnu/libc/usr/lib64/crtn.o | 0 test/Driver/Ofast.c | 39 + test/Driver/autolink_integrated_as.c | 6 + test/Driver/clang_cpp.c | 5 +- test/Driver/clang_f_opts.c | 11 +- test/Driver/color-diagnostics.c | 53 + test/Driver/debug-comp-dir.S | 3 - test/Driver/debug.c | 3 - test/Driver/dragonfly.c | 6 +- test/Driver/flags.c | 34 +- test/Driver/fparse-all-comments.c | 5 + test/Driver/fsanitize.c | 21 +- test/Driver/hexagon-toolchain-elf.c | 2 +- test/Driver/hexagon-toolchain.c | 2 +- test/Driver/linux-ld.c | 16 + test/Driver/mips-abi.c | 36 + test/Driver/mips-as.c | 46 +- test/Driver/mips-cs-header-search.cpp | 257 ++++ test/Driver/mips-cs-ld.c | 288 ++++ test/Driver/mips-eleb.c | 10 +- test/Driver/mips-features.c | 14 +- test/Driver/mips-float.c | 20 +- test/Driver/modules.m | 6 - test/Driver/modules_integrated_as.c | 6 - test/Driver/objc++-cpp-output.mm | 4 +- test/Driver/objc-cpp-output.m | 2 +- test/Driver/output-file-is-dir.c | 7 +- test/Driver/pic.c | 4 + test/Driver/r600-mcpu.cl | 14 +- test/Driver/sanitizer-ld.c | 4 + test/Driver/save-temps.c | 19 + test/Driver/split-debug.s | 21 + test/FixIt/fixit-cxx1y-compat.cpp | 12 + test/Format/basic.cpp | 2 +- test/Format/multiple-inputs-error.cpp | 6 + test/Format/multiple-inputs-inplace.cpp | 8 + test/Format/multiple-inputs.cpp | 7 + test/Format/ranges.cpp | 2 +- test/Frontend/Inputs/rewrite-includes8.h | 5 + .../rewrite-includes-invalid-hasinclude.c | 17 + test/Frontend/rewrite-includes-missing.c | 2 +- test/Frontend/rewrite-includes-modules.c | 20 + test/Frontend/rewrite-includes.c | 19 + test/Frontend/rewrite-macros.c | 14 +- test/Frontend/verify.c | 16 + test/Headers/c11.c | 13 + test/Headers/cxx11.cpp | 7 + test/Headers/ms-wchar.c | 15 + test/Index/annotate-module.m | 7 + test/Index/annotate-tokens.cpp | 55 +- test/Index/annotate-tokens.m | 10 +- test/Index/c-index-api-loadTU-test.m | 2 +- test/Index/comment-cplus11-specific.cpp | 27 + test/Index/comment-misc-tags.m | 110 ++ test/Index/comment-unqualified-objc-pointer.m | 36 + test/Index/comment-with-preamble.c | 13 + test/Index/get-cursor.cpp | 25 + test/Index/index-refs.m | 9 + test/Index/load-classes.cpp | 20 +- test/Index/parse-all-comments.c | 62 + test/Index/print-type-size.cpp | 428 ++++++ test/Index/print-type.c | 4 +- test/Index/print-type.cpp | 5 + test/Index/print-type.m | 7 +- test/Index/properties-class-extensions.m | 10 +- test/Index/subclass-comment.mm | 107 ++ test/Index/targeted-annotation.c | 8 +- test/Index/usrs.m | 7 +- test/Lexer/cxx1y_binary_literal.cpp | 19 + test/Lexer/has_extension_cxx.cpp | 6 + test/Lexer/has_feature_c1x.c | 11 +- test/Lexer/has_feature_cxx0x.cpp | 194 ++- test/Lexer/pragma-message.c | 16 + test/Lexer/pragma-message2.c | 19 + test/Misc/ast-dump-decl.c | 2 +- test/Misc/ast-dump-decl.cpp | 5 +- test/Misc/warn-in-system-header.c | 2 +- test/Modules/Inputs/ModuleDiags/has_errors.h | 2 + .../Modules/Inputs/ModuleDiags/has_warnings.h | 3 + test/Modules/Inputs/ModuleDiags/module.map | 7 + .../Inputs/System/usr/include/dbl_max.h | 1 + .../Inputs/System/usr/include/module.map | 11 + .../System/usr/include/uses_other_constants.h | 3 + test/Modules/auto-module-import.m | 4 +- test/Modules/autolink.m | 6 +- test/Modules/compiler_builtins.m | 1 - test/Modules/cstd.m | 7 +- test/Modules/cycles.c | 4 +- test/Modules/decldef.m | 3 +- test/Modules/decldef.mm | 3 +- test/Modules/diamond-pch.c | 25 +- test/Modules/diamond.c | 22 +- test/Modules/linkage-merge.cpp | 13 +- test/Modules/linkage-merge.m | 23 +- test/Modules/lookup.cpp | 2 +- test/Modules/lookup.m | 18 +- test/Modules/macros.c | 14 +- test/Modules/method_pool.m | 8 +- test/Modules/module-private.cpp | 2 +- test/Modules/namespaces.cpp | 4 +- test/Modules/normal-module-map.cpp | 6 +- test/Modules/objc-categories.m | 9 +- test/Modules/on-demand-build.m | 2 +- test/Modules/redecl-merge.m | 18 +- test/Modules/redecls/a.h | 3 + test/Modules/redecls/b.h | 1 + test/Modules/redecls/main.m | 27 + test/Modules/redecls/module.map | 2 + test/Modules/serialized-diags.m | 32 + test/Modules/subframeworks.m | 2 +- test/Modules/system_version.m | 32 + test/PCH/captured-stmt.cpp | 42 + test/PCH/cxx-typeid.cpp | 6 +- test/PCH/cxx-typeid.h | 43 +- test/PCH/cxx-using.cpp | 7 +- test/PCH/cxx11-statement-attributes.cpp | 3 +- test/PCH/cxx1y-decltype-auto.cpp | 24 + test/PCH/cxx1y-default-initializer.cpp | 30 + test/PCH/functions.c | 6 +- test/PCH/headersearch.cpp | 6 +- test/PCH/method_pool.m | 12 +- test/PCH/nonvisible-external-defs.c | 2 +- test/PCH/reloc.c | 4 +- test/PCH/tentative-defs.c | 3 +- test/PCH/thread-local.cpp | 20 + test/PCH/typo.cpp | 19 +- test/PCH/typo.m | 3 +- test/Parser/MicrosoftExtensions.c | 2 +- test/Parser/MicrosoftExtensions.cpp | 29 + test/Parser/captured-statements.c | 14 + test/Parser/cxx0x-ambig.cpp | 6 +- test/Parser/cxx0x-decl.cpp | 7 +- test/Parser/objc-boxing.m | 8 + .../objc-error-qualified-implementation.m | 21 + test/Parser/objcxx11-initialized-temps.mm | 38 + test/Parser/pragma-options.c | 12 + test/Parser/pragma-pack.c | 14 + test/Preprocessor/aarch64-target-features.c | 36 +- test/Preprocessor/cxx_oper_spelling.cpp | 3 +- test/Preprocessor/dependencies-and-pp.c | 17 +- test/Preprocessor/init.c | 144 +- test/Preprocessor/line-directive.c | 10 +- test/Preprocessor/pp-modules.c | 15 + test/Preprocessor/pp-modules.h | 1 + test/Preprocessor/pragma-captured.c | 13 + test/Preprocessor/pragma_sysheader.c | 2 +- test/Preprocessor/predefined-arch-macros.c | 46 + test/Preprocessor/stdint.c | 107 ++ .../rewrite-byref-in-nested-blocks.mm | 2 +- test/Sema/MicrosoftCompatibility.cpp | 4 + test/Sema/arm-neon-types.c | 12 + test/Sema/array-init.c | 2 +- test/Sema/asm.c | 16 + test/Sema/atomic-expr.c | 47 + test/Sema/bitfield.c | 15 + test/Sema/builtins-aarch64.c | 16 + test/Sema/captured-statements.c | 78 + test/Sema/crash-invalid-array.c | 7 + test/Sema/extern-redecl.c | 29 + test/Sema/function-redecl.c | 4 +- test/Sema/function.c | 11 + .../no-documentation-warn-tagdecl-specifier.c | 85 ++ test/Sema/parentheses.c | 89 +- test/Sema/parentheses.cpp | 66 +- test/Sema/pragma-arc-cf-code-audited.c | 2 +- test/Sema/return.c | 8 +- test/Sema/thread-specifier.c | 108 +- test/Sema/var-redecl.c | 4 +- test/Sema/warn-documentation.cpp | 2 +- test/Sema/warn-documentation.m | 1 + test/Sema/warn-duplicate-enum.c | 9 + test/SemaCXX/Inputs/warn-unused-variables.h | 11 + test/SemaCXX/MicrosoftExtensions.cpp | 125 ++ test/SemaCXX/access.cpp | 76 +- test/SemaCXX/alignof.cpp | 52 + test/SemaCXX/ast-print.cpp | 11 + test/SemaCXX/attr-noreturn.cpp | 83 ++ test/SemaCXX/captured-statements.cpp | 166 +++ test/SemaCXX/compound-literal.cpp | 66 +- test/SemaCXX/condition.cpp | 8 +- test/SemaCXX/constant-expression-cxx11.cpp | 84 +- test/SemaCXX/constant-expression-cxx1y.cpp | 459 ++++++ test/SemaCXX/constexpr-printing.cpp | 2 +- test/SemaCXX/constexpr-value-init.cpp | 2 +- test/SemaCXX/cxx11-ast-print.cpp | 2 + test/SemaCXX/cxx11-crashes.cpp | 4 +- test/SemaCXX/cxx11-inheriting-ctors.cpp | 28 + test/SemaCXX/cxx11-thread-local-print.cpp | 9 + test/SemaCXX/cxx11-thread-local.cpp | 23 + .../cxx11-user-defined-literals-unused.cpp | 13 + test/SemaCXX/cxx1y-array-runtime-bound.cpp | 68 + test/SemaCXX/cxx1y-constexpr-not-const.cpp | 18 + test/SemaCXX/cxx1y-deduced-return-type.cpp | 338 +++++ test/SemaCXX/cxx1y-initializer-aggregates.cpp | 63 + test/SemaCXX/cxx98-compat-pedantic.cpp | 11 + test/SemaCXX/enum-unscoped-nonexistent.cpp | 8 +- test/SemaCXX/for-range-unused.cpp | 4 +- test/SemaCXX/i-c-e-cxx.cpp | 2 +- test/SemaCXX/linkage-spec.cpp | 8 + test/SemaCXX/linkage.cpp | 9 + test/SemaCXX/linkage2.cpp | 12 + test/SemaCXX/pascal-strings.cpp | 2 + test/SemaCXX/trailing-return-0x.cpp | 9 + test/SemaCXX/undefined-internal.cpp | 7 + test/SemaCXX/uninitialized.cpp | 12 + test/SemaCXX/warn-c++11-extensions.cpp | 2 + test/SemaCXX/warn-overloaded-virtual.cpp | 18 + test/SemaCXX/warn-thread-safety-analysis.cpp | 90 +- test/SemaCXX/warn-unused-filescoped.cpp | 21 + test/SemaCXX/warn-unused-variables-error.cpp | 10 + test/SemaCXX/warn-unused-variables.cpp | 11 +- test/SemaObjC/arc-repeated-weak.mm | 26 + test/SemaObjC/arc-system-header.m | 13 +- test/SemaObjC/arc-unavailable-for-weakref.m | 30 +- test/SemaObjC/arc.m | 11 + test/SemaObjC/attr-availability.m | 32 +- ...-ref.m => deprecated-objc-introspection.m} | 16 +- test/SemaObjC/format-arg-attribute.m | 2 +- test/SemaObjC/format-strings-objc.m | 6 + test/SemaObjC/message.m | 12 + test/SemaObjC/method-conflict-2.m | 22 + test/SemaObjC/property-category-4.m | 105 ++ test/SemaObjC/property-category-impl.m | 5 +- test/SemaObjC/property-deprecated-warning.m | 38 +- .../property-noninherited-availability-attr.m | 14 +- test/SemaObjC/property-user-setter.m | 2 +- test/SemaObjC/property.m | 5 +- test/SemaObjC/protocol-lookup-2.m | 23 + test/SemaObjC/typo-correction.m | 5 +- test/SemaObjC/warn-missing-super.m | 2 +- test/SemaObjCXX/arc-system-header.mm | 3 +- test/SemaObjCXX/foreach.mm | 16 + test/SemaObjCXX/property-reference.mm | 18 + test/SemaObjCXX/references.mm | 19 +- test/SemaOpenCL/event_t.cl | 2 +- test/SemaOpenCL/storageclass.cl | 2 + test/SemaTemplate/attributes.cpp | 22 +- test/SemaTemplate/dependent-names.cpp | 21 +- test/SemaTemplate/example-dynarray.cpp | 178 --- test/SemaTemplate/local-member-templates.cpp | 76 + ...ms-function-specialization-class-scope.cpp | 1 + .../ms-lookup-template-base-classes.cpp | 26 +- test/SemaTemplate/overload-candidates.cpp | 17 + test/SemaTemplate/temp_arg_nontype.cpp | 9 + test/Tooling/clang-check-args.cpp | 3 - test/Tooling/clang-check-builtin-headers.cpp | 3 - test/Tooling/clang-check-chdir.cpp | 3 - test/Tooling/clang-check.cpp | 3 - test/Tooling/multi-jobs.cpp | 3 - test/lit.cfg | 19 +- tools/c-index-test/c-index-test.c | 133 +- tools/clang-format/ClangFormat.cpp | 87 +- .../clang-format-bbedit.applescript | 27 + tools/clang-format/clang-format-diff.py | 11 +- tools/clang-format/clang-format.el | 31 + tools/clang-format/clang-format.py | 7 +- tools/libclang/CIndex.cpp | 91 ++ tools/libclang/CIndexCXX.cpp | 2 +- tools/libclang/CXCursor.cpp | 24 +- tools/libclang/CXSourceLocation.cpp | 11 + tools/libclang/CXTranslationUnit.h | 2 + tools/libclang/CXType.cpp | 132 +- tools/libclang/IndexBody.cpp | 9 +- tools/libclang/IndexDecl.cpp | 5 + tools/libclang/Indexing.cpp | 9 +- tools/libclang/IndexingContext.cpp | 6 + tools/libclang/IndexingContext.h | 3 + tools/libclang/RecursiveASTVisitor.h | 28 +- tools/libclang/libclang.exports | 9 + tools/scan-build/c++-analyzer | 9 +- tools/scan-build/ccc-analyzer | 2 +- tools/scan-build/scan-build | 16 +- tools/scan-build/scan-build.bat | 1 + tools/scan-view/ScanView.py | 16 +- unittests/AST/CommentLexer.cpp | 2 +- unittests/AST/CommentParser.cpp | 2 +- unittests/AST/SourceLocationTest.cpp | 8 +- unittests/ASTMatchers/ASTMatchersTest.cpp | 43 +- unittests/Basic/SourceManagerTest.cpp | 2 +- unittests/Format/FormatTest.cpp | 412 +++++- utils/TableGen/ClangAttrEmitter.cpp | 42 + .../ClangCommentCommandInfoEmitter.cpp | 3 + utils/TableGen/ClangDiagnosticsEmitter.cpp | 2 +- utils/TableGen/NeonEmitter.cpp | 572 +++++++- utils/TableGen/TableGen.cpp | 8 + utils/TableGen/TableGenBackends.h | 1 + utils/analyzer/SATestBuild.py | 5 +- www/OpenProjects.html | 24 +- www/analyzer/available_checks.html | 21 +- www/analyzer/checker_dev_manual.html | 15 +- www/analyzer/dev_cxx.html | 57 - www/analyzer/latest_checker.html.incl | 2 +- www/analyzer/menu.html.incl | 4 +- www/analyzer/open_projects.html | 180 +++ www/analyzer/potential_checkers.html | 83 +- www/analyzer/release_notes.html | 24 + www/comparison.html | 1 - www/cxx_status.html | 99 +- www/get_started.html | 4 - www/index.html | 2 +- 940 files changed, 34600 insertions(+), 8951 deletions(-) create mode 100644 include/clang/Basic/BuiltinsAArch64.def create mode 100644 include/clang/Basic/CapturedStmt.h rename {lib => include/clang}/Lex/MacroArgs.h (100%) create mode 100644 lib/Format/BreakableToken.cpp create mode 100644 lib/Format/BreakableToken.h create mode 100644 lib/Format/WhitespaceManager.cpp create mode 100644 lib/Format/WhitespaceManager.h create mode 100644 lib/StaticAnalyzer/Checkers/AllocationDiagnostics.cpp create mode 100644 lib/StaticAnalyzer/Checkers/AllocationDiagnostics.h rename test/Analysis/{alloc-match-dealloc.mm => MismatchedDeallocator-checker-test.mm} (100%) create mode 100644 test/Analysis/MismatchedDeallocator-path-notes.cpp rename test/Analysis/{bool-assignment.cpp => bool-assignment.c} (66%) delete mode 100644 test/Analysis/bool-assignment2.c create mode 100644 test/Analysis/conditional-operator.cpp create mode 100644 test/Analysis/enum.cpp create mode 100644 test/Analysis/inlining/false-positive-suppression.m create mode 100644 test/Analysis/objc-string.mm create mode 100644 test/CXX/dcl.dcl/dcl.link/p7-2.cpp create mode 100644 test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p3-1y.cpp create mode 100644 test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p6-1y.cpp create mode 100644 test/CXX/dcl.decl/dcl.init/dcl.init.aggr/p7.cpp create mode 100644 test/CXX/expr/expr.unary/expr.sizeof/p1.cpp create mode 100644 test/CodeGen/arm-asm-diag.c create mode 100644 test/CodeGen/builtins-aarch64.c create mode 100644 test/CodeGen/linetable-endscope.c create mode 100644 test/CodeGen/linux-arm-atomic.c create mode 100644 test/CodeGen/mips-inline-asm-modifiers.c create mode 100644 test/CodeGen/sparc-target-data.c create mode 100644 test/CodeGen/systemz-inline-asm.c create mode 100644 test/CodeGen/tbaa-class.cpp create mode 100644 test/CodeGenCXX/cxx11-thread-local-reference.cpp create mode 100644 test/CodeGenCXX/cxx11-thread-local.cpp create mode 100644 test/CodeGenCXX/cxx1y-initializer-aggregate.cpp create mode 100644 test/CodeGenCXX/linetable-cleanup.cpp create mode 100644 test/CodeGenCXX/microsoft-abi-sret-and-byval.cpp create mode 100644 test/CodeGenCXX/pr15753.cpp create mode 100644 test/CodeGenCXX/scoped-enums-debug-info.cpp create mode 100644 test/CodeGenCXX/tls-init-funcs.cpp create mode 100644 test/CodeGenObjC/arc-linetable.m create mode 100644 test/CodeGenObjC/objc-fixed-enum.m create mode 100644 test/CodeGenObjC/tentative-cfconstantstring.m create mode 100644 test/Driver/Inputs/fedora_18_tree/lib/.keep create mode 100644 test/Driver/Inputs/fedora_18_tree/usr/lib/crt1.o create mode 100644 test/Driver/Inputs/fedora_18_tree/usr/lib/crti.o create mode 100644 test/Driver/Inputs/fedora_18_tree/usr/lib/crtn.o create mode 100644 test/Driver/Inputs/fedora_18_tree/usr/lib/gcc/armv7hl-redhat-linux-gnueabi/4.7.2/crtbegin.o create mode 100644 test/Driver/Inputs/fedora_18_tree/usr/lib/gcc/armv7hl-redhat-linux-gnueabi/4.7.2/crtend.o create mode 100644 test/Driver/Inputs/mips_cs_tree/bin/.keep create mode 100644 test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/64/crtbegin.o create mode 100644 test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/64/crtend.o create mode 100644 test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/crtbegin.o create mode 100644 test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/crtend.o create mode 100644 test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/el/64/crtbegin.o create mode 100644 test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/el/64/crtend.o create mode 100644 test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/el/crtbegin.o create mode 100644 test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/el/crtend.o create mode 100644 test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/include-fixed/64/.keep create mode 100644 test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/include-fixed/el/64/.keep create mode 100644 test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/include-fixed/micromips/el/.keep create mode 100644 test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/include-fixed/micromips/soft-float/el/.keep create mode 100644 test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/include-fixed/mips16/el/.keep create mode 100644 test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/include-fixed/mips16/soft-float/el/.keep create mode 100644 test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/include-fixed/soft-float/64/.keep create mode 100644 test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/include-fixed/soft-float/el/64/.keep create mode 100644 test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/include/.keep create mode 100644 test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/micromips/crtbegin.o create mode 100644 test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/micromips/crtend.o create mode 100644 test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/micromips/el/crtbegin.o create mode 100644 test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/micromips/el/crtend.o create mode 100644 test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/micromips/soft-float/crtbegin.o create mode 100644 test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/micromips/soft-float/crtend.o create mode 100644 test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/micromips/soft-float/el/crtbegin.o create mode 100644 test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/micromips/soft-float/el/crtend.o create mode 100644 test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/mips16/crtbegin.o create mode 100644 test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/mips16/crtend.o create mode 100644 test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/mips16/el/crtbegin.o create mode 100644 test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/mips16/el/crtend.o create mode 100644 test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/mips16/soft-float/crtbegin.o create mode 100644 test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/mips16/soft-float/crtend.o create mode 100644 test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/mips16/soft-float/el/crtbegin.o create mode 100644 test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/mips16/soft-float/el/crtend.o create mode 100644 test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/soft-float/64/crtbegin.o create mode 100644 test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/soft-float/64/crtend.o create mode 100644 test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/soft-float/crtbegin.o create mode 100644 test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/soft-float/crtend.o create mode 100644 test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/soft-float/el/64/crtbegin.o create mode 100644 test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/soft-float/el/64/crtend.o create mode 100644 test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/soft-float/el/crtbegin.o create mode 100644 test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/soft-float/el/crtend.o create mode 100644 test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/include/c++/4.6.3/mips-linux-gnu/.keep create mode 100644 test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/lib/el/.keep create mode 100644 test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/lib/micromips/el/.keep create mode 100644 test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/lib/micromips/soft-float/el/.keep create mode 100644 test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/lib/mips16/el/.keep create mode 100644 test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/lib/mips16/soft-float/el/.keep create mode 100644 test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/lib/soft-float/el/.keep create mode 100644 test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/lib64/el/.keep create mode 100644 test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/lib64/soft-float/el/.keep create mode 100644 test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/el/lib/.keep create mode 100644 test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/el/lib64/.keep create mode 100644 test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/el/usr/lib/crt1.o create mode 100644 test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/el/usr/lib/crti.o create mode 100644 test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/el/usr/lib/crtn.o create mode 100644 test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/el/usr/lib64/crt1.o create mode 100644 test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/el/usr/lib64/crti.o create mode 100644 test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/el/usr/lib64/crtn.o create mode 100644 test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/lib/.keep create mode 100644 test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/lib64/.keep create mode 100644 test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/micromips/el/lib/.keep create mode 100644 test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/micromips/el/usr/lib/crt1.o create mode 100644 test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/micromips/el/usr/lib/crti.o create mode 100644 test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/micromips/el/usr/lib/crtn.o create mode 100644 test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/micromips/lib/.keep create mode 100644 test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/micromips/soft-float/el/lib/.keep create mode 100644 test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/micromips/soft-float/el/usr/lib/crt1.o create mode 100644 test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/micromips/soft-float/el/usr/lib/crti.o create mode 100644 test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/micromips/soft-float/el/usr/lib/crtn.o create mode 100644 test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/micromips/soft-float/lib/.keep create mode 100644 test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/micromips/soft-float/usr/lib/crt1.o create mode 100644 test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/micromips/soft-float/usr/lib/crti.o create mode 100644 test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/micromips/soft-float/usr/lib/crtn.o create mode 100644 test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/micromips/usr/lib/crt1.o create mode 100644 test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/micromips/usr/lib/crti.o create mode 100644 test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/micromips/usr/lib/crtn.o create mode 100644 test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/mips16/el/lib/.keep create mode 100644 test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/mips16/el/usr/lib/crt1.o create mode 100644 test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/mips16/el/usr/lib/crti.o create mode 100644 test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/mips16/el/usr/lib/crtn.o create mode 100644 test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/mips16/lib/.keep create mode 100644 test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/mips16/soft-float/el/lib/.keep create mode 100644 test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/mips16/soft-float/el/usr/lib/crt1.o create mode 100644 test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/mips16/soft-float/el/usr/lib/crti.o create mode 100644 test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/mips16/soft-float/el/usr/lib/crtn.o create mode 100644 test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/mips16/soft-float/lib/.keep create mode 100644 test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/mips16/soft-float/usr/lib/crt1.o create mode 100644 test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/mips16/soft-float/usr/lib/crti.o create mode 100644 test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/mips16/soft-float/usr/lib/crtn.o create mode 100644 test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/mips16/usr/lib/crt1.o create mode 100644 test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/mips16/usr/lib/crti.o create mode 100644 test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/mips16/usr/lib/crtn.o create mode 100644 test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/soft-float/el/lib/.keep create mode 100644 test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/soft-float/el/lib64/.keep create mode 100644 test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/soft-float/el/usr/lib/crt1.o create mode 100644 test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/soft-float/el/usr/lib/crti.o create mode 100644 test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/soft-float/el/usr/lib/crtn.o create mode 100644 test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/soft-float/el/usr/lib64/crt1.o create mode 100644 test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/soft-float/el/usr/lib64/crti.o create mode 100644 test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/soft-float/el/usr/lib64/crtn.o create mode 100644 test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/soft-float/lib/.keep create mode 100644 test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/soft-float/lib64/.keep create mode 100644 test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/soft-float/usr/lib/crt1.o create mode 100644 test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/soft-float/usr/lib/crti.o create mode 100644 test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/soft-float/usr/lib/crtn.o create mode 100644 test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/soft-float/usr/lib64/crt1.o create mode 100644 test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/soft-float/usr/lib64/crti.o create mode 100644 test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/soft-float/usr/lib64/crtn.o create mode 100644 test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/usr/include/.keep create mode 100644 test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/usr/lib/crt1.o create mode 100644 test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/usr/lib/crti.o create mode 100644 test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/usr/lib/crtn.o create mode 100644 test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/usr/lib64/crt1.o create mode 100644 test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/usr/lib64/crti.o create mode 100644 test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/usr/lib64/crtn.o create mode 100644 test/Driver/Ofast.c create mode 100644 test/Driver/autolink_integrated_as.c create mode 100644 test/Driver/color-diagnostics.c create mode 100644 test/Driver/fparse-all-comments.c create mode 100644 test/Driver/mips-abi.c create mode 100644 test/Driver/mips-cs-header-search.cpp create mode 100644 test/Driver/mips-cs-ld.c delete mode 100644 test/Driver/modules_integrated_as.c create mode 100644 test/Driver/save-temps.c create mode 100644 test/Driver/split-debug.s create mode 100644 test/FixIt/fixit-cxx1y-compat.cpp create mode 100644 test/Format/multiple-inputs-error.cpp create mode 100644 test/Format/multiple-inputs-inplace.cpp create mode 100644 test/Format/multiple-inputs.cpp create mode 100644 test/Frontend/Inputs/rewrite-includes8.h create mode 100644 test/Frontend/rewrite-includes-invalid-hasinclude.c create mode 100644 test/Frontend/rewrite-includes-modules.c create mode 100644 test/Headers/ms-wchar.c create mode 100644 test/Index/comment-cplus11-specific.cpp create mode 100644 test/Index/comment-misc-tags.m create mode 100644 test/Index/comment-unqualified-objc-pointer.m create mode 100644 test/Index/comment-with-preamble.c create mode 100644 test/Index/parse-all-comments.c create mode 100644 test/Index/print-type-size.cpp create mode 100644 test/Index/subclass-comment.mm create mode 100644 test/Lexer/cxx1y_binary_literal.cpp create mode 100644 test/Lexer/pragma-message2.c create mode 100644 test/Modules/Inputs/ModuleDiags/has_errors.h create mode 100644 test/Modules/Inputs/ModuleDiags/has_warnings.h create mode 100644 test/Modules/Inputs/ModuleDiags/module.map create mode 100644 test/Modules/Inputs/System/usr/include/dbl_max.h create mode 100644 test/Modules/Inputs/System/usr/include/uses_other_constants.h create mode 100644 test/Modules/redecls/a.h create mode 100644 test/Modules/redecls/b.h create mode 100644 test/Modules/redecls/main.m create mode 100644 test/Modules/redecls/module.map create mode 100644 test/Modules/serialized-diags.m create mode 100644 test/Modules/system_version.m create mode 100644 test/PCH/captured-stmt.cpp create mode 100644 test/PCH/cxx1y-decltype-auto.cpp create mode 100644 test/PCH/cxx1y-default-initializer.cpp create mode 100644 test/PCH/thread-local.cpp create mode 100644 test/Parser/captured-statements.c create mode 100644 test/Parser/objc-error-qualified-implementation.m create mode 100644 test/Parser/objcxx11-initialized-temps.mm create mode 100644 test/Preprocessor/pp-modules.c create mode 100644 test/Preprocessor/pp-modules.h create mode 100644 test/Preprocessor/pragma-captured.c create mode 100644 test/Sema/MicrosoftCompatibility.cpp create mode 100644 test/Sema/atomic-expr.c create mode 100644 test/Sema/builtins-aarch64.c create mode 100644 test/Sema/captured-statements.c create mode 100644 test/Sema/no-documentation-warn-tagdecl-specifier.c create mode 100644 test/SemaCXX/Inputs/warn-unused-variables.h create mode 100644 test/SemaCXX/alignof.cpp create mode 100644 test/SemaCXX/captured-statements.cpp create mode 100644 test/SemaCXX/constant-expression-cxx1y.cpp create mode 100644 test/SemaCXX/cxx11-inheriting-ctors.cpp create mode 100644 test/SemaCXX/cxx11-thread-local-print.cpp create mode 100644 test/SemaCXX/cxx11-thread-local.cpp create mode 100644 test/SemaCXX/cxx11-user-defined-literals-unused.cpp create mode 100644 test/SemaCXX/cxx1y-array-runtime-bound.cpp create mode 100644 test/SemaCXX/cxx1y-constexpr-not-const.cpp create mode 100644 test/SemaCXX/cxx1y-deduced-return-type.cpp create mode 100644 test/SemaCXX/cxx1y-initializer-aggregates.cpp create mode 100644 test/SemaCXX/warn-unused-variables-error.cpp rename test/SemaObjC/{warn-isa-ref.m => deprecated-objc-introspection.m} (75%) delete mode 100644 test/SemaTemplate/example-dynarray.cpp create mode 100644 test/SemaTemplate/local-member-templates.cpp create mode 100644 tools/clang-format/clang-format-bbedit.applescript create mode 100644 tools/clang-format/clang-format.el mode change 120000 => 100755 tools/scan-build/c++-analyzer create mode 100644 tools/scan-build/scan-build.bat delete mode 100644 www/analyzer/dev_cxx.html create mode 100644 www/analyzer/open_projects.html diff --git a/CMakeLists.txt b/CMakeLists.txt index 6efcd4a7bda..5d05a4cdb55 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -137,7 +137,12 @@ configure_file( # Add appropriate flags for GCC if (LLVM_COMPILER_IS_GCC_COMPATIBLE) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-common -Woverloaded-virtual -Wcast-qual -fno-strict-aliasing -pedantic -Wno-long-long -Wall -W -Wno-unused-parameter -Wwrite-strings") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-common -Woverloaded-virtual -Wcast-qual -fno-strict-aliasing") + + # Enable -pedantic for Clang even if it's not enabled for LLVM. + if (NOT LLVM_ENABLE_PEDANTIC) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pedantic -Wno-long-long") + endif () check_cxx_compiler_flag("-Werror -Wnested-anon-types" CXX_SUPPORTS_NO_NESTED_ANON_TYPES_FLAG) if( CXX_SUPPORTS_NO_NESTED_ANON_TYPES_FLAG ) diff --git a/bindings/python/clang/cindex.py b/bindings/python/clang/cindex.py index 70f4f36a2cf..880a1502a4b 100644 --- a/bindings/python/clang/cindex.py +++ b/bindings/python/clang/cindex.py @@ -1314,6 +1314,18 @@ def get_tokens(self): """ return TokenGroup.get_tokens(self._tu, self.extent) + def is_bitfield(self): + """ + Check if the field is a bitfield. + """ + return conf.lib.clang_Cursor_isBitField(self) + + def get_bitfield_width(self): + """ + Retrieve the width of a bitfield. + """ + return conf.lib.clang_getFieldDeclBitWidth(self) + @staticmethod def from_result(res, fn, args): assert isinstance(res, Cursor) @@ -1613,6 +1625,24 @@ def get_array_size(self): """ return conf.lib.clang_getArraySize(self) + def get_align(self): + """ + Retrieve the alignment of the record. + """ + return conf.lib.clang_Type_getAlignOf(self) + + def get_size(self): + """ + Retrieve the size of the record. + """ + return conf.lib.clang_Type_getSizeOf(self) + + def get_offset(self, fieldname): + """ + Retrieve the offset of a field in the record. + """ + return conf.lib.clang_Type_getOffsetOf(self, c_char_p(fieldname)) + def __eq__(self, other): if type(other) != type(self): return False @@ -2623,6 +2653,10 @@ def cursor(self): [Type], c_longlong), + ("clang_getFieldDeclBitWidth", + [Cursor], + c_int), + ("clang_getCanonicalCursor", [Cursor], Cursor, @@ -3038,6 +3072,22 @@ def cursor(self): [Cursor, c_uint], Cursor, Cursor.from_result), + + ("clang_Cursor_isBitField", + [Cursor], + bool), + + ("clang_Type_getAlignOf", + [Type], + c_longlong), + + ("clang_Type_getOffsetOf", + [Type, c_char_p], + c_longlong), + + ("clang_Type_getSizeOf", + [Type], + c_ulonglong), ] class LibclangError(Exception): diff --git a/bindings/python/tests/cindex/test_type.py b/bindings/python/tests/cindex/test_type.py index 28e4411c779..9bbed5aa940 100644 --- a/bindings/python/tests/cindex/test_type.py +++ b/bindings/python/tests/cindex/test_type.py @@ -298,3 +298,66 @@ def test_is_restrict_qualified(): assert isinstance(i.type.is_restrict_qualified(), bool) assert i.type.is_restrict_qualified() assert not j.type.is_restrict_qualified() + +def test_record_layout(): + """Ensure Cursor.type.get_size, Cursor.type.get_align and + Cursor.type.get_offset works.""" + + source =""" +struct a { + long a1; + long a2:3; + long a3:4; + long long a4; +}; +""" + tries=[(['-target','i386-linux-gnu'],(4,16,0,32,35,64)), + (['-target','nvptx64-unknown-unknown'],(8,24,0,64,67,128)), + (['-target','i386-pc-win32'],(8,16,0,32,35,64)), + (['-target','msp430-none-none'],(2,14,0,32,35,48))] + for flags, values in tries: + align,total,a1,a2,a3,a4 = values + + tu = get_tu(source, flags=flags) + teststruct = get_cursor(tu, 'a') + fields = list(teststruct.get_children()) + + assert teststruct.type.get_align() == align + assert teststruct.type.get_size() == total + assert teststruct.type.get_offset(fields[0].spelling) == a1 + assert teststruct.type.get_offset(fields[1].spelling) == a2 + assert teststruct.type.get_offset(fields[2].spelling) == a3 + assert teststruct.type.get_offset(fields[3].spelling) == a4 + assert fields[0].is_bitfield() == False + assert fields[1].is_bitfield() == True + assert fields[1].get_bitfield_width() == 3 + assert fields[2].is_bitfield() == True + assert fields[2].get_bitfield_width() == 4 + assert fields[3].is_bitfield() == False + +def test_offset(): + """Ensure Cursor.get_record_field_offset works in anonymous records""" + source=""" +struct Test { + struct { + int bariton; + union { + int foo; + }; + }; + int bar; +};""" + tries=[(['-target','i386-linux-gnu'],(4,16,0,32,64)), + (['-target','nvptx64-unknown-unknown'],(8,24,0,32,64)), + (['-target','i386-pc-win32'],(8,16,0,32,64)), + (['-target','msp430-none-none'],(2,14,0,32,64))] + for flags, values in tries: + align,total,bariton,foo,bar = values + tu = get_tu(source) + teststruct = get_cursor(tu, 'Test') + fields = list(teststruct.get_children()) + assert teststruct.type.get_offset("bariton") == bariton + assert teststruct.type.get_offset("foo") == foo + assert teststruct.type.get_offset("bar") == bar + + diff --git a/bindings/python/tests/cindex/util.py b/bindings/python/tests/cindex/util.py index 2323839306d..8614b02ad25 100644 --- a/bindings/python/tests/cindex/util.py +++ b/bindings/python/tests/cindex/util.py @@ -3,7 +3,7 @@ from clang.cindex import Cursor from clang.cindex import TranslationUnit -def get_tu(source, lang='c', all_warnings=False): +def get_tu(source, lang='c', all_warnings=False, flags=[]): """Obtain a translation unit from source and language. By default, the translation unit is created from source file "t." @@ -14,8 +14,8 @@ def get_tu(source, lang='c', all_warnings=False): all_warnings is a convenience argument to enable all compiler warnings. """ + args = list(flags) name = 't.c' - args = [] if lang == 'cpp': name = 't.cpp' args.append('-std=c++11') diff --git a/docs/ClangFormat.rst b/docs/ClangFormat.rst index 92d7fc319e1..964fc84d7bc 100644 --- a/docs/ClangFormat.rst +++ b/docs/ClangFormat.rst @@ -18,7 +18,6 @@ to format C/C++/Obj-C code. $ clang-format --help OVERVIEW: A tool to format C/C++/Obj-C code. - Currently supports LLVM and Google style guides. If no arguments are specified, it formats the code from standard input and writes the result to the standard output. If is given, it reformats the file. If -i is specified together @@ -66,6 +65,37 @@ It operates on the current, potentially unsaved buffer and does not create or save any files. To revert a formatting, just undo. +Emacs Integration +================= + +Similar to the integration for :program:`vim`, there is an integration for +:program:`emacs`. It can be found at `clang/tools/clang-format/clang-format.el` +and used by adding this to your `.emacs`: + +.. code-block:: common-lisp + + (load "/tools/clang-format/clang-format.el") + (global-set-key [C-M-tab] 'clang-format-region) + +This binds the function `clang-format-region` to C-M-tab, which then formats the +current line or selected region. + + +BBEdit Integration +================== + +:program:`clang-format` cannot be used as a text filter with BBEdit, but works +well via a script. The AppleScript to do this integration can be found at +`clang/tools/clang-format/clang-format-bbedit.applescript`; place a copy in +`~/Library/Application Support/BBEdit/Scripts`, and edit the path within it to +point to your local copy of :program:`clang-format`. + +With this integration you can select the script from the Script menu and +:program:`clang-format` will format the selection. Note that you can rename the +menu item by renaming the script, and can assign the menu item a keyboard +shortcut in the BBEdit preferences, under Menus & Shortcuts. + + Script for patch reformatting ============================= @@ -81,7 +111,7 @@ a unified diff and reformats all contained lines with :program:`clang-format`. optional arguments: -h, --help show this help message and exit -p P strip the smallest prefix containing P slashes - -style STYLE formatting style to apply (LLVM, Google) + -style STYLE formatting style to apply (LLVM, Google, Chromium) So to reformat all the lines in the latest :program:`git` commit, just do: diff --git a/docs/ClangTools.rst b/docs/ClangTools.rst index b7f7c7b0462..9312e6baf09 100644 --- a/docs/ClangTools.rst +++ b/docs/ClangTools.rst @@ -100,7 +100,11 @@ Currently it can: * convert loops to range-based for loops; -* convert null pointer constants (like ``NULL`` or ``0``) to C++11 ``nullptr``. +* convert null pointer constants (like ``NULL`` or ``0``) to C++11 ``nullptr``; + +* replace the type specifier in variable declarations with the ``auto`` type specifier; + +* add the ``override`` specifier to applicable member functions. Extra Clang Tools ================= @@ -120,6 +124,27 @@ Ideas for new Tools ``foo.begin()`` into ``begin(foo)`` and similarly for ``end()``, where ``foo`` is a standard container. We could also detect similar patterns for arrays. +* ``make_shared`` / ``make_unique`` conversion. Part of this transformation +can be incorporated into the ``auto`` transformation. Will convert + + .. code-block:: c++ + + std::shared_ptr sp(new Foo); + std::unique_ptr up(new Foo); + + func(std::shared_ptr(new Foo), bar()); + + into: + + .. code-block:: c++ + + auto sp = std::make_shared(); + auto up = std::make_unique(); // In C++14 mode. + + // This also affects correctness. For the cases where bar() throws, + // make_shared() is safe and the original code may leak. + func(std::make_shared(), bar()); + * ``tr1`` removal tool. Will migrate source code from using TR1 library features to C++11 library. For example: @@ -150,3 +175,17 @@ Ideas for new Tools that don't want to use ``auto`` because they are afraid that they might lose control over their code. +* C++14: less verbose operator function objects (`N3421 + `_). + For example: + + .. code-block:: c++ + + sort(v.begin(), v.end(), greater()); + + should be rewritten to: + + .. code-block:: c++ + + sort(v.begin(), v.end(), greater<>()); + diff --git a/docs/LanguageExtensions.rst b/docs/LanguageExtensions.rst index c870d20b876..324feafa98f 100644 --- a/docs/LanguageExtensions.rst +++ b/docs/LanguageExtensions.rst @@ -645,8 +645,7 @@ C++11 inheriting constructors ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use ``__has_feature(cxx_inheriting_constructors)`` to determine if support for -inheriting constructors is enabled. Clang does not currently implement this -feature. +inheriting constructors is enabled. C++11 inline namespaces ^^^^^^^^^^^^^^^^^^^^^^^ @@ -727,6 +726,12 @@ Use ``__has_feature(cxx_static_assert)`` or ``__has_extension(cxx_static_assert)`` to determine if support for compile-time assertions using ``static_assert`` is enabled. +C++11 ``thread_local`` +^^^^^^^^^^^^^^^^^^^^^^ + +Use ``__has_feature(cxx_thread_local)`` to determine if support for +``thread_local`` variables is enabled. + C++11 type inference ^^^^^^^^^^^^^^^^^^^^ @@ -774,6 +779,98 @@ Use ``__has_feature(cxx_variadic_templates)`` or ``__has_extension(cxx_variadic_templates)`` to determine if support for variadic templates is enabled. +C++1y +----- + +The features listed below are part of the committee draft for the C++1y +standard. As a result, all these features are enabled with the ``-std=c++1y`` +or ``-std=gnu++1y`` option when compiling C++ code. + +C++1y binary literals +^^^^^^^^^^^^^^^^^^^^^ + +Use ``__has_feature(cxx_binary_literals)`` or +``__has_extension(cxx_binary_literals)`` to determine whether +binary literals (for instance, ``0b10010``) are recognized. Clang supports this +feature as an extension in all language modes. + +C++1y contextual conversions +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Use ``__has_feature(cxx_contextual_conversions)`` or +``__has_extension(cxx_contextual_conversions)`` to determine if the C++1y rules +are used when performing an implicit conversion for an array bound in a +*new-expression*, the operand of a *delete-expression*, an integral constant +expression, or a condition in a ``switch`` statement. Clang does not yet +support this feature. + +C++1y decltype(auto) +^^^^^^^^^^^^^^^^^^^^ + +Use ``__has_feature(cxx_decltype_auto)`` or +``__has_extension(cxx_decltype_auto)`` to determine if support +for the ``decltype(auto)`` placeholder type is enabled. + +C++1y default initializers for aggregates +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Use ``__has_feature(cxx_aggregate_nsdmi)`` or +``__has_extension(cxx_aggregate_nsdmi)`` to determine if support +for default initializers in aggregate members is enabled. + +C++1y generalized lambda capture +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Use ``__has_feature(cxx_generalized_capture)`` or +``__has_extension(cxx_generalized_capture`` to determine if support for +generalized lambda captures is enabled +(for instance, ``[n(0)] { return ++n; }``). +Clang does not yet support this feature. + +C++1y generic lambdas +^^^^^^^^^^^^^^^^^^^^^ + +Use ``__has_feature(cxx_generic_lambda)`` or +``__has_extension(cxx_generic_lambda)`` to determine if support for generic +(polymorphic) lambdas is enabled +(for instance, ``[] (auto x) { return x + 1; }``). +Clang does not yet support this feature. + +C++1y relaxed constexpr +^^^^^^^^^^^^^^^^^^^^^^^ + +Use ``__has_feature(cxx_relaxed_constexpr)`` or +``__has_extension(cxx_relaxed_constexpr)`` to determine if variable +declarations, local variable modification, and control flow constructs +are permitted in ``constexpr`` functions. +Clang's implementation of this feature is incomplete. + +C++1y return type deduction +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Use ``__has_feature(cxx_return_type_deduction)`` or +``__has_extension(cxx_return_type_deduction)`` to determine if support +for return type deduction for functions (using ``auto`` as a return type) +is enabled. +Clang's implementation of this feature is incomplete. + +C++1y runtime-sized arrays +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Use ``__has_feature(cxx_runtime_array)`` or +``__has_extension(cxx_runtime_array)`` to determine if support +for arrays of runtime bound (a restricted form of variable-length arrays) +is enabled. +Clang's implementation of this feature is incomplete. + +C++1y variable templates +^^^^^^^^^^^^^^^^^^^^^^^^ + +Use ``__has_feature(cxx_variable_templates)`` or +``__has_extension(cxx_variable_templates)`` to determine if support for +templated variable declarations is enabled. +Clang does not yet support this feature. + C11 --- @@ -818,6 +915,12 @@ Use ``__has_feature(c_static_assert)`` or ``__has_extension(c_static_assert)`` to determine if support for compile-time assertions using ``_Static_assert`` is enabled. +C11 ``_Thread_local`` +^^^^^^^^^^^^^^^^^^^^^ + +Use ``__has_feature(c_thread_local)`` to determine if support for +``_Thread_local`` variables is enabled. + Checks for Type Traits ====================== diff --git a/docs/LibASTMatchersReference.html b/docs/LibASTMatchersReference.html index b476065b849..e80e4426f4b 100644 --- a/docs/LibASTMatchersReference.html +++ b/docs/LibASTMatchersReference.html @@ -1513,6 +1513,34 @@ Usable as: Matcher<CXXMethodDecl>isOverride +
Matches if the given method declaration overrides another method.
+
+Given
+  class A {
+   public:
+    virtual void x();
+  };
+  class B : public A {
+   public:
+    virtual void x();
+  };
+  matches B::x
+
+ + +Matcher<CXXMethodDecl>isVirtual +
Matches if the given method declaration is virtual.
+
+Given
+  class A {
+   public:
+    virtual void x();
+  };
+  matches A::x
+
+ + Matcher<CXXOperatorCallExpr>hasOverloadedOperatorNameStringRef Name
Matches overloaded operator names.
 
diff --git a/docs/LibASTMatchersTutorial.rst b/docs/LibASTMatchersTutorial.rst
index ba568e3594e..5bd62a13364 100644
--- a/docs/LibASTMatchersTutorial.rst
+++ b/docs/LibASTMatchersTutorial.rst
@@ -27,6 +27,8 @@ guide `_.
       git clone http://llvm.org/git/llvm.git
       cd llvm/tools
       git clone http://llvm.org/git/clang.git
+      cd clang/tools
+      git clone http://llvm.org/git/clang-tools-extra.git extra
 
 Next you need to obtain the CMake build system and Ninja build tool. You
 may already have CMake installed, but current binary versions of CMake
@@ -163,7 +165,7 @@ You should now be able to run the syntax checker, which is located in
 
 .. code-block:: console
 
-      cat "void main() {}" > test.cpp
+      cat "int main() { return 0; }" > test.cpp
       bin/loop-convert test.cpp --
 
 Note the two dashes after we specify the source file. The additional
@@ -275,8 +277,9 @@ Add the following to ``LoopConvert.cpp``:
       class LoopPrinter : public MatchFinder::MatchCallback {
       public :
         virtual void run(const MatchFinder::MatchResult &Result) {
-        if (const ForStmt *FS = Result.Nodes.getNodeAs("forLoop"))
-          FS->dump();
+          if (const ForStmt *FS = Result.Nodes.getNodeAs("forLoop"))
+            FS->dump();
+        }
       };
 
 And change ``main()`` to:
@@ -411,13 +414,13 @@ previous iteration of loop-convert, shows us the answer:
             (IntegerLiteral 0x173afa8 'int' 0)")
         <<>>
         (BinaryOperator 0x173b060 '_Bool' '<'
-          (ImplicitCastExpr 0x173b030 'int' 
+          (ImplicitCastExpr 0x173b030 'int'
             (DeclRefExpr 0x173afe0 'int' lvalue Var 0x173af50 'i' 'int'))
-          (ImplicitCastExpr 0x173b048 'int' 
+          (ImplicitCastExpr 0x173b048 'int'
             (DeclRefExpr 0x173b008 'const int' lvalue Var 0x170fa80 'N' 'const int')))
         (UnaryOperator 0x173b0b0 'int' lvalue prefix '++'
           (DeclRefExpr 0x173b088 'int' lvalue Var 0x173af50 'i' 'int'))
-        (CompoundStatement …
+        (CompoundStatement ...
 
 We already know that the declaration and increments both match, or this
 loop wouldn't have been dumped. The culprit lies in the implicit cast
@@ -460,32 +463,60 @@ Since we bind three variables (identified by ConditionVarName,
 InitVarName, and IncrementVarName), we can obtain the matched nodes by
 using the ``getNodeAs()`` member function.
 
-In ``LoopActions.cpp``:
+In ``LoopConvert.cpp`` add
 
 .. code-block:: c++
 
       #include "clang/AST/ASTContext.h"
 
+Change ``LoopMatcher`` to
+
+.. code-block:: c++
+
+      StatementMatcher LoopMatcher =
+          forStmt(hasLoopInit(declStmt(
+                      hasSingleDecl(varDecl(hasInitializer(integerLiteral(equals(0))))
+                                        .bind("initVarName")))),
+                  hasIncrement(unaryOperator(
+                      hasOperatorName("++"),
+                      hasUnaryOperand(declRefExpr(
+                          to(varDecl(hasType(isInteger())).bind("incVarName")))))),
+                  hasCondition(binaryOperator(
+                      hasOperatorName("<"),
+                      hasLHS(ignoringParenImpCasts(declRefExpr(
+                          to(varDecl(hasType(isInteger())).bind("condVarName"))))),
+                      hasRHS(expr(hasType(isInteger())))))).bind("forLoop");
+
+And change ``LoopPrinter::run`` to
+
+.. code-block:: c++
+
       void LoopPrinter::run(const MatchFinder::MatchResult &Result) {
         ASTContext *Context = Result.Context;
-        const ForStmt *FS = Result.Nodes.getStmtAs(LoopName);
+        const ForStmt *FS = Result.Nodes.getStmtAs("forLoop");
         // We do not want to convert header files!
         if (!FS || !Context->getSourceManager().isFromMainFile(FS->getForLoc()))
           return;
-        const VarDecl *IncVar = Result.Nodes.getNodeAs(IncrementVarName);
-        const VarDecl *CondVar = Result.Nodes.getNodeAs(ConditionVarName);
-        const VarDecl *InitVar = Result.Nodes.getNodeAs(InitVarName);
+        const VarDecl *IncVar = Result.Nodes.getNodeAs("incVarName");
+        const VarDecl *CondVar = Result.Nodes.getNodeAs("condVarName");
+        const VarDecl *InitVar = Result.Nodes.getNodeAs("initVarName");
+
+        if (!areSameVariable(IncVar, CondVar) || !areSameVariable(IncVar, InitVar))
+          return;
+        llvm::outs() << "Potential array-based loop discovered.\n";
+      }
 
-Now that we have the three variables, represented by their respective
-declarations, let's make sure that they're all the same, using a helper
-function I call ``areSameVariable()``.
+Clang associates a ``VarDecl`` with each variable to represent the variable's
+declaration. Since the "canonical" form of each declaration is unique by
+address, all we need to do is make sure neither ``ValueDecl`` (base class of
+``VarDecl``) is ``NULL`` and compare the canonical Decls.
 
 .. code-block:: c++
 
-      if (!areSameVariable(IncVar, CondVar) || !areSameVariable(IncVar, InitVar))
-        return;
-      llvm::outs() << "Potential array-based loop discovered.\n";
-    }
+      static bool areSameVariable(const ValueDecl *First, const ValueDecl *Second) {
+        return First && Second &&
+               First->getCanonicalDecl() == Second->getCanonicalDecl();
+      }
 
 If execution reaches the end of ``LoopPrinter::run()``, we know that the
 loop shell that looks like
@@ -498,21 +529,8 @@ For now, we will just print a message explaining that we found a loop.
 The next section will deal with recursively traversing the AST to
 discover all changes needed.
 
-As a side note, here is the implementation of ``areSameVariable``. Clang
-associates a ``VarDecl`` with each variable to represent the variable's
-declaration. Since the "canonical" form of each declaration is unique by
-address, all we need to do is make sure neither ``ValueDecl`` (base
-class of ``VarDecl``) is ``NULL`` and compare the canonical Decls.
-
-.. code-block:: c++
-
-      static bool areSameVariable(const ValueDecl *First, const ValueDecl *Second) {
-        return First && Second &&
-               First->getCanonicalDecl() == Second->getCanonicalDecl();
-      }
-
-It's not as trivial to test if two expressions are the same, though
-Clang has already done the hard work for us by providing a way to
+As a side note, it's not as trivial to test if two expressions are the same,
+though Clang has already done the hard work for us by providing a way to
 canonicalize expressions:
 
 .. code-block:: c++
diff --git a/docs/MemorySanitizer.rst b/docs/MemorySanitizer.rst
index fdb8a810232..439acc47fa2 100644
--- a/docs/MemorySanitizer.rst
+++ b/docs/MemorySanitizer.rst
@@ -46,7 +46,7 @@ to disable inlining (just use ``-O1``) and tail call elimination
       return 0;
     }
 
-    % clang -fsanitize=memory -fPIE -pie -fno-omit-frame-pointer -g -O2 umr.cc
+    % clang -fsanitize=memory -fno-omit-frame-pointer -g -O2 umr.cc
 
 If a bug is detected, the program will print an error message to
 stderr and exit with a non-zero exit code. Currently, MemorySanitizer
@@ -103,7 +103,7 @@ the example above,
 
 .. code-block:: console
 
-    % clang -fsanitize=memory -fsanitize-memory-track-origins -fPIE -pie -fno-omit-frame-pointer -g -O2 umr.cc
+    % clang -fsanitize=memory -fsanitize-memory-track-origins -fno-omit-frame-pointer -g -O2 umr.cc
     % ./a.out 2>log
     % projects/compiler-rt/lib/asan/scripts/asan_symbolize.py / < log | c++filt
     ==14425==  WARNING: MemorySanitizer: UMR (uninitialized-memory-read)
@@ -160,7 +160,10 @@ Limitations
   address space. This means that tools like ``ulimit`` may not work as
   usually expected.
 * Static linking is not supported.
-* Non-position-independent executables are not supported.
+* Non-position-independent executables are not supported.  Therefore, the
+  ``fsanitize=memory`` flag will cause Clang to act as though the ``-fPIE``
+  flag had been supplied if compiling without ``-fPIC``, and as though the
+  ``-pie`` flag had been supplied if linking an executable.
 * Depending on the version of Linux kernel, running without ASLR may
   be not supported. Note that GDB disables ASLR by default. To debug
   instrumented programs, use "set disable-randomization off".
diff --git a/docs/Modules.rst b/docs/Modules.rst
index 8a6b8b6d2ef..fdf597a5a92 100644
--- a/docs/Modules.rst
+++ b/docs/Modules.rst
@@ -168,8 +168,8 @@ Command-line parameters
 ``-fmodules-cache-path=``
   Specify the path to the modules cache. If not provided, Clang will select a system-appropriate default.
 
-``-f[no-]modules-autolink``
-  Enable of disable automatic linking against the libraries associated with imported modules.
+``-fno-autolink``
+  Disable automatic linking against the libraries associated with imported modules.
 
 ``-fmodules-ignore-macro=macroname``
   Instruct modules to ignore the named macro when selecting an appropriate module variant. Use this for macros defined on the command line that don't affect how modules are built, to improve sharing of compiled module files.
diff --git a/docs/ReleaseNotes.rst b/docs/ReleaseNotes.rst
index d9a3364606b..e764a09d536 100644
--- a/docs/ReleaseNotes.rst
+++ b/docs/ReleaseNotes.rst
@@ -1,6 +1,6 @@
-=====================================
-Clang 3.3 (In-Progress) Release Notes
-=====================================
+=======================
+Clang 3.3 Release Notes
+=======================
 
 .. contents::
    :local:
@@ -8,41 +8,33 @@ Clang 3.3 (In-Progress) Release Notes
 
 Written by the `LLVM Team `_
 
-.. warning::
-
-   These are in-progress notes for the upcoming Clang 3.3 release. You may
-   prefer the `Clang 3.2 Release Notes
-   `_.
-
 Introduction
 ============
 
 This document contains the release notes for the Clang C/C++/Objective-C
 frontend, part of the LLVM Compiler Infrastructure, release 3.3. Here we
-describe the status of Clang in some detail, including major
-improvements from the previous release and new feature work. For the
-general LLVM release notes, see `the LLVM
-documentation `_. All LLVM
-releases may be downloaded from the `LLVM releases web
-site `_.
-
-For more information about Clang or LLVM, including information about
-the latest release, please check out the main please see the `Clang Web
-Site `_ or the `LLVM Web
-Site `_.
-
-Note that if you are reading this file from a Subversion checkout or the
-main Clang web page, this document applies to the *next* release, not
-the current one. To see the release notes for a specific release, please
-see the `releases page `_.
+describe the status of Clang in some detail, including major improvements from
+the previous release and new feature work. For the general LLVM release notes,
+see `the LLVM documentation `_. All LLVM
+releases may be downloaded from the `LLVM releases web site
+`_.
+
+For more information about Clang or LLVM, including information about the latest
+release, please check out the main please see the `Clang Web Site
+`_ or the `LLVM Web Site `_.
+
+Note that if you are reading this file from a Subversion checkout or the main
+Clang web page, this document applies to the *next* release, not the current
+one. To see the release notes for a specific release, please see the `releases
+page `_.
 
 What's New in Clang 3.3?
 ========================
 
 Some of the major new features and improvements to Clang are listed
 here. Generic improvements to Clang as a whole or to its underlying
-infrastructure are described first, followed by language-specific
-sections with improvements to Clang's support for those languages.
+infrastructure are described first, followed by language-specific sections with
+improvements to Clang's support for those languages.
 
 Major New Features
 ------------------
@@ -54,8 +46,6 @@ Clang's diagnostics are constantly being improved to catch more issues,
 explain them more clearly, and provide more accurate source information
 about them. The improvements since the 3.2 release include:
 
--  ...
-
 Extended Identifiers: Unicode Support and Universal Character Names
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
@@ -65,31 +55,17 @@ specified by the active language standard; these characters can be written
 directly in the source file using the UTF-8 encoding, or referred to using
 *universal character names* (``\u00E0``, ``\U000000E0``).
 
-New Compiler Flags
-------------------
-
--  ...
-
 C Language Changes in Clang
 ---------------------------
 
-C11 Feature Support
-^^^^^^^^^^^^^^^^^^^
-
-...
-
 C++ Language Changes in Clang
 -----------------------------
 
-C++11 Feature Support
-^^^^^^^^^^^^^^^^^^^^^
-
-...
-
-Objective-C Language Changes in Clang
--------------------------------------
-
-...
+- Clang now correctly implements language linkage for functions and variables.
+  This means that, for example, it is now possible to overload static functions
+  declared in an ``extern "C"`` context. For backwards compatibility, an alias
+  with the unmangled name is still emitted if it is the only one and has the
+  ``used`` attribute.
 
 Internal API Changes
 --------------------
@@ -118,16 +94,40 @@ Storage Class
 For each variable and function Clang used to keep the storage class as written
 in the source, the linkage and a semantic storage class. This was a bit
 redundant and the semantic storage class has been removed. The method
-getStorageClass now returns what is written it the source code for that decl.
+getStorageClass now returns what is written in the source code for that decl.
+
+libclang
+--------
 
-...
+The clang_CXCursorSet_contains() function previously incorrectly returned 0
+if it contained a CXCursor, contrary to what the documentation stated.  This
+has been fixed so that the function returns a non-zero value if the set
+contains a cursor.  This is API breaking change, but matches the intended
+original behavior.  Moreover, this also fixes the issue of an invalid CXCursorSet
+appearing to contain any CXCursor.
+
+Static Analyzer
+---------------
+
+The static analyzer (which contains additional code checking beyond compiler
+warnings) has improved significantly in both in the core analysis engine and 
+also in the kinds of issues it can find.
+
+Core Analysis Improvements
+==========================
 
-Python Binding Changes
-----------------------
+- Support for interprocedural reasoning about constructors and destructors.
+- New false positive suppression mechanisms that reduced the number of false
+  null pointer dereference warnings due to interprocedural analysis.
+- Major performance enhancements to speed up interprocedural analysis
 
-The following methods have been added:
+New Issues Found
+================
 
--  ...
+- New memory error checks such as use-after-free with C++ 'delete'.
+- Detection of mismatched allocators and deallocators (e.g., using 'new' with
+  'free()', 'malloc()' with 'delete').
+- Additional checks for misuses of Apple Foundation framework collection APIs.
 
 Significant Known Problems
 ==========================
@@ -135,13 +135,11 @@ Significant Known Problems
 Additional Information
 ======================
 
-A wide variety of additional information is available on the `Clang web
-page `_. The web page contains versions of the
-API documentation which are up-to-date with the Subversion version of
-the source code. You can access versions of these documents specific to
-this release by going into the "``clang/docs/``" directory in the Clang
-tree.
+A wide variety of additional information is available on the `Clang web page
+`_. The web page contains versions of the API
+documentation which are up-to-date with the Subversion version of the source
+code. You can access versions of these documents specific to this release by
+going into the "``clang/docs/``" directory in the Clang tree.
 
-If you have any questions or comments about Clang, please feel free to
-contact us via the `mailing
-list `_.
+If you have any questions or comments about Clang, please feel free to contact
+us via the `mailing list `_.
diff --git a/docs/ThreadSanitizer.rst b/docs/ThreadSanitizer.rst
index c0c576b44a5..5e5ee48f7f4 100644
--- a/docs/ThreadSanitizer.rst
+++ b/docs/ThreadSanitizer.rst
@@ -25,9 +25,9 @@ platforms is problematic and not yet planned.
 Usage
 -----
 
-Simply compile your program with ``-fsanitize=thread -fPIE`` and link it with
-``-fsanitize=thread -pie``.  To get a reasonable performance add ``-O1`` or
-higher.  Use ``-g`` to get file names and line numbers in the warning messages.
+Simply compile and link your program with ``-fsanitize=thread``.  To get a
+reasonable performance add ``-O1`` or higher.  Use ``-g`` to get file names
+and line numbers in the warning messages.
 
 Example:
 
@@ -48,7 +48,7 @@ Example:
     return Global;
   }
 
-  $ clang -fsanitize=thread -g -O1 tiny_race.c -fPIE -pie
+  $ clang -fsanitize=thread -g -O1 tiny_race.c
 
 If a bug is detected, the program will print an error message to stderr.
 Currently, ThreadSanitizer symbolizes its output using an external
@@ -107,7 +107,10 @@ Limitations
 * ThreadSanitizer maps (but does not reserve) a lot of virtual address space.
   This means that tools like ``ulimit`` may not work as usually expected.
 * Libc/libstdc++ static linking is not supported.
-* ThreadSanitizer requires ``-fPIE -pie`` compiler flags.
+* Non-position-independent executables are not supported.  Therefore, the
+  ``fsanitize=thread`` flag will cause Clang to act as though the ``-fPIE``
+  flag had been supplied if compiling without ``-fPIC``, and as though the
+  ``-pie`` flag had been supplied if linking an executable.
 
 Current Status
 --------------
diff --git a/docs/UsersManual.rst b/docs/UsersManual.rst
index 6cc836130f9..3dc07aba901 100644
--- a/docs/UsersManual.rst
+++ b/docs/UsersManual.rst
@@ -652,6 +652,31 @@ supports the GCC pragma, Clang and GCC do not support the exact same set
 of warnings, so even when using GCC compatible #pragmas there is no
 guarantee that they will have identical behaviour on both compilers.
 
+In addition to controlling warnings and errors generated by the compiler, it is
+possible to generate custom warning and error messages through the following
+pragmas:
+
+.. code-block:: c
+
+  // The following will produce warning messages
+  #pragma message "some diagnostic message"
+  #pragma GCC warning "TODO: replace deprecated feature"
+
+  // The following will produce an error message
+  #pragma GCC error "Not supported"
+
+These pragmas operate similarly to the ``#warning`` and ``#error`` preprocessor
+directives, except that they may also be embedded into preprocessor macros via
+the C99 ``_Pragma`` operator, for example:
+
+.. code-block:: c
+
+  #define STR(X) #X
+  #define DEFER(M,...) M(__VA_ARGS__)
+  #define CUSTOM_ERROR(X) _Pragma(STR(GCC error(X " at line " DEFER(STR,__LINE__))))
+
+  CUSTOM_ERROR("Feature not available");
+
 Controlling Diagnostics in System Headers
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
@@ -1005,6 +1030,19 @@ below. If multiple flags are present, the last one is used.
 
   Generate complete debug info.
 
+Comment Parsing Options
+--------------------------
+
+Clang parses Doxygen and non-Doxygen style documentation comments and attaches
+them to the appropriate declaration nodes.  By default, it only parses
+Doxygen-style comments and ignores ordinary comments starting with ``//`` and
+``/*``.
+
+.. option:: -fparse-all-comments
+
+  Parse all comments as documentation comments (including ordinary comments
+  starting with ``//`` and ``/*``).
+
 .. _c:
 
 C Language Features
diff --git a/docs/tools/clang.pod b/docs/tools/clang.pod
index 9628d473b7c..d2394a84a57 100644
--- a/docs/tools/clang.pod
+++ b/docs/tools/clang.pod
@@ -7,7 +7,7 @@ clang - the Clang C, C++, and Objective-C compiler
 =head1 SYNOPSIS
 
 B [B<-c>|B<-S>|B<-E>] B<-std=>I B<-g>
-  [B<-O0>|B<-O1>|B<-O2>|B<-Os>|B<-Oz>|B<-O3>|B<-O4>]
+  [B<-O0>|B<-O1>|B<-O2>|B<-Os>|B<-Oz>|B<-O3>|B<-Ofast>|B<-O4>]
   B<-W>I B<-pedantic>
   B<-I>I B<-L>I
   B<-D>I
@@ -263,7 +263,7 @@ may not exist on earlier ones.
 
 =over
 
-=item B<-O0> B<-O1> B<-O2> B<-Os> B<-Oz> B<-O3> B<-O4>
+=item B<-O0> B<-O1> B<-O2> B<-Os> B<-Oz> B<-O3> B<-Ofast> B<-O4>
 
 Specify which optimization level to use.  B<-O0> means "no optimization": this
 level compiles the fastest and generates the most debuggable code.  B<-O2> is a
@@ -271,7 +271,9 @@ moderate level of optimization which enables most optimizations.  B<-Os> is like
 B<-O2> with extra optimizations to reduce code size.  B<-Oz> is like B<-Os> 
 (and thus B<-O2>), but reduces code size further.  B<-O3> is like B<-O2>,
 except that it enables optimizations that take longer to perform or that may
-generate larger code (in an attempt to make the program run faster).  On
+generate larger code (in an attempt to make the program run faster).
+B<-Ofast> enables all the optimizations from B<-O3> along with other aggressive
+optimizations that may violate strict compliance with language standards. On
 supported platforms, B<-O4> enables link-time optimization; object files are
 stored in the LLVM bitcode file format and whole program optimization is done at
 link time. B<-O1> is somewhere between B<-O0> and B<-O2>.
diff --git a/include/clang-c/Index.h b/include/clang-c/Index.h
index 787c44a2e4f..d8c37ebd70f 100644
--- a/include/clang-c/Index.h
+++ b/include/clang-c/Index.h
@@ -32,7 +32,7 @@
  * compatible, thus CINDEX_VERSION_MAJOR is expected to remain stable.
  */
 #define CINDEX_VERSION_MAJOR 0
-#define CINDEX_VERSION_MINOR 15
+#define CINDEX_VERSION_MINOR 19
 
 #define CINDEX_VERSION_ENCODE(major, minor) ( \
       ((major) * 10000)                       \
@@ -408,6 +408,11 @@ CINDEX_LINKAGE CXSourceLocation clang_getLocationForOffset(CXTranslationUnit tu,
                                                            CXFile file,
                                                            unsigned offset);
 
+/**
+ * \brief Returns non-zero if the given source location is in a system header.
+ */
+CINDEX_LINKAGE int clang_Location_isInSystemHeader(CXSourceLocation location);
+
 /**
  * \brief Retrieve a NULL (invalid) source range.
  */
@@ -1898,7 +1903,11 @@ enum CXCursorKind {
    */
   CXCursor_ObjCBoolLiteralExpr           = 145,
 
-  CXCursor_LastExpr                      = CXCursor_ObjCBoolLiteralExpr,
+  /** \brief Represents the "self" expression in a ObjC method.
+   */
+  CXCursor_ObjCSelfExpr                  = 146,
+
+  CXCursor_LastExpr                      = CXCursor_ObjCSelfExpr,
 
   /* Statements */
   CXCursor_FirstStmt                     = 200,
@@ -2900,6 +2909,83 @@ CINDEX_LINKAGE CXType clang_getArrayElementType(CXType T);
  */
 CINDEX_LINKAGE long long clang_getArraySize(CXType T);
 
+/**
+ * \brief List the possible error codes for \c clang_Type_getSizeOf,
+ *   \c clang_Type_getAlignOf, \c clang_Type_getOffsetOf and
+ *   \c clang_Cursor_getOffsetOf.
+ *
+ * A value of this enumeration type can be returned if the target type is not
+ * a valid argument to sizeof, alignof or offsetof.
+ */
+enum CXTypeLayoutError {
+  /**
+   * \brief Type is of kind CXType_Invalid.
+   */
+  CXTypeLayoutError_Invalid = -1,
+  /**
+   * \brief The type is an incomplete Type.
+   */
+  CXTypeLayoutError_Incomplete = -2,
+  /**
+   * \brief The type is a dependent Type.
+   */
+  CXTypeLayoutError_Dependent = -3,
+  /**
+   * \brief The type is not a constant size type.
+   */
+  CXTypeLayoutError_NotConstantSize = -4,
+  /**
+   * \brief The Field name is not valid for this record.
+   */
+  CXTypeLayoutError_InvalidFieldName = -5
+};
+
+/**
+ * \brief Return the alignment of a type in bytes as per C++[expr.alignof]
+ *   standard.
+ *
+ * If the type declaration is invalid, CXTypeLayoutError_Invalid is returned.
+ * If the type declaration is an incomplete type, CXTypeLayoutError_Incomplete
+ *   is returned.
+ * If the type declaration is a dependent type, CXTypeLayoutError_Dependent is
+ *   returned.
+ * If the type declaration is not a constant size type,
+ *   CXTypeLayoutError_NotConstantSize is returned.
+ */
+CINDEX_LINKAGE long long clang_Type_getAlignOf(CXType T);
+
+/**
+ * \brief Return the size of a type in bytes as per C++[expr.sizeof] standard.
+ *
+ * If the type declaration is invalid, CXTypeLayoutError_Invalid is returned.
+ * If the type declaration is an incomplete type, CXTypeLayoutError_Incomplete
+ *   is returned.
+ * If the type declaration is a dependent type, CXTypeLayoutError_Dependent is
+ *   returned.
+ */
+CINDEX_LINKAGE long long clang_Type_getSizeOf(CXType T);
+
+/**
+ * \brief Return the offset of a field named S in a record of type T in bits
+ *   as it would be returned by __offsetof__ as per C++11[18.2p4]
+ *
+ * If the cursor is not a record field declaration, CXTypeLayoutError_Invalid
+ *   is returned.
+ * If the field's type declaration is an incomplete type,
+ *   CXTypeLayoutError_Incomplete is returned.
+ * If the field's type declaration is a dependent type,
+ *   CXTypeLayoutError_Dependent is returned.
+ * If the field's name S is not found,
+ *   CXTypeLayoutError_InvalidFieldName is returned.
+ */
+CINDEX_LINKAGE long long clang_Type_getOffsetOf(CXType T, const char *S);
+
+/**
+ * \brief Returns non-zero if the cursor specifies a Record member that is a
+ *   bitfield.
+ */
+CINDEX_LINKAGE unsigned clang_Cursor_isBitField(CXCursor C);
+
 /**
  * \brief Returns 1 if the base class specified by the cursor with kind
  *   CX_CXXBaseSpecifier is virtual.
@@ -2918,9 +3004,11 @@ enum CX_CXXAccessSpecifier {
 };
 
 /**
- * \brief Returns the access control level for the C++ base specifier
- * represented by a cursor with kind CXCursor_CXXBaseSpecifier or
- * CXCursor_AccessSpecifier.
+ * \brief Returns the access control level for the referenced object.
+ *
+ * If the cursor refers to a C++ declaration, its access control level within its
+ * parent scope is returned. Otherwise, if the cursor refers to a base specifier or
+ * access specifier, the specifier itself is returned.
  */
 CINDEX_LINKAGE enum CX_CXXAccessSpecifier clang_getCXXAccessSpecifier(CXCursor);
 
@@ -3275,6 +3363,61 @@ CINDEX_LINKAGE int clang_Cursor_isDynamicCall(CXCursor C);
  */
 CINDEX_LINKAGE CXType clang_Cursor_getReceiverType(CXCursor C);
 
+/**
+ * \brief Property attributes for a \c CXCursor_ObjCPropertyDecl.
+ */
+typedef enum {
+  CXObjCPropertyAttr_noattr    = 0x00,
+  CXObjCPropertyAttr_readonly  = 0x01,
+  CXObjCPropertyAttr_getter    = 0x02,
+  CXObjCPropertyAttr_assign    = 0x04,
+  CXObjCPropertyAttr_readwrite = 0x08,
+  CXObjCPropertyAttr_retain    = 0x10,
+  CXObjCPropertyAttr_copy      = 0x20,
+  CXObjCPropertyAttr_nonatomic = 0x40,
+  CXObjCPropertyAttr_setter    = 0x80,
+  CXObjCPropertyAttr_atomic    = 0x100,
+  CXObjCPropertyAttr_weak      = 0x200,
+  CXObjCPropertyAttr_strong    = 0x400,
+  CXObjCPropertyAttr_unsafe_unretained = 0x800
+} CXObjCPropertyAttrKind;
+
+/**
+ * \brief Given a cursor that represents a property declaration, return the
+ * associated property attributes. The bits are formed from
+ * \c CXObjCPropertyAttrKind.
+ *
+ * \param reserved Reserved for future use, pass 0.
+ */
+CINDEX_LINKAGE unsigned clang_Cursor_getObjCPropertyAttributes(CXCursor C,
+                                                             unsigned reserved);
+
+/**
+ * \brief 'Qualifiers' written next to the return and parameter types in
+ * ObjC method declarations.
+ */
+typedef enum {
+  CXObjCDeclQualifier_None = 0x0,
+  CXObjCDeclQualifier_In = 0x1,
+  CXObjCDeclQualifier_Inout = 0x2,
+  CXObjCDeclQualifier_Out = 0x4,
+  CXObjCDeclQualifier_Bycopy = 0x8,
+  CXObjCDeclQualifier_Byref = 0x10,
+  CXObjCDeclQualifier_Oneway = 0x20
+} CXObjCDeclQualifierKind;
+
+/**
+ * \brief Given a cursor that represents an ObjC method or parameter
+ * declaration, return the associated ObjC qualifiers for the return type or the
+ * parameter respectively. The bits are formed from CXObjCDeclQualifierKind.
+ */
+CINDEX_LINKAGE unsigned clang_Cursor_getObjCDeclQualifiers(CXCursor C);
+
+/**
+ * \brief Returns non-zero if the given cursor is a variadic function or method.
+ */
+CINDEX_LINKAGE unsigned clang_Cursor_isVariadic(CXCursor C);
+
 /**
  * \brief Given a cursor that represents a declaration, return the associated
  * comment's source range.  The range may include multiple consecutive comments
@@ -3321,6 +3464,13 @@ typedef void *CXModule;
  */
 CINDEX_LINKAGE CXModule clang_Cursor_getModule(CXCursor C);
 
+/**
+ * \param Module a module object.
+ *
+ * \returns the module file where the provided module object came from.
+ */
+CINDEX_LINKAGE CXFile clang_Module_getASTFile(CXModule Module);
+
 /**
  * \param Module a module object.
  *
diff --git a/include/clang/AST/ASTContext.h b/include/clang/AST/ASTContext.h
index d4878a99a6f..c5d3337fd22 100644
--- a/include/clang/AST/ASTContext.h
+++ b/include/clang/AST/ASTContext.h
@@ -867,6 +867,9 @@ class ASTContext : public RefCountedBase {
   const FunctionType *adjustFunctionType(const FunctionType *Fn,
                                          FunctionType::ExtInfo EInfo);
 
+  /// \brief Change the result type of a function type once it is deduced.
+  void adjustDeducedFunctionResultType(FunctionDecl *FD, QualType ResultType);
+
   /// \brief Return the uniqued reference to the type for a complex
   /// number with the specified element type.
   QualType getComplexType(QualType T) const;
@@ -1100,7 +1103,8 @@ class ASTContext : public RefCountedBase {
                                  UnaryTransformType::UTTKind UKind) const;
 
   /// \brief C++11 deduced auto type.
-  QualType getAutoType(QualType DeducedType) const;
+  QualType getAutoType(QualType DeducedType, bool IsDecltypeAuto,
+                       bool IsDependent = false) const;
 
   /// \brief C++11 deduction pattern for 'auto' type.
   QualType getAutoDeductType() const;
@@ -1451,7 +1455,18 @@ class ASTContext : public RefCountedBase {
     qs.addObjCLifetime(lifetime);
     return getQualifiedType(type, qs);
   }
-
+  
+  /// getUnqualifiedObjCPointerType - Returns version of
+  /// Objective-C pointer type with lifetime qualifier removed.
+  QualType getUnqualifiedObjCPointerType(QualType type) const {
+    if (!type.getTypePtr()->isObjCObjectPointerType() ||
+        !type.getQualifiers().hasObjCLifetime())
+      return type;
+    Qualifiers Qs = type.getQualifiers();
+    Qs.removeObjCLifetime();
+    return getQualifiedType(type.getUnqualifiedType(), Qs);
+  }
+  
   DeclarationNameInfo getNameForTemplate(TemplateName Name,
                                          SourceLocation NameLoc) const;
 
@@ -1578,6 +1593,14 @@ class ASTContext : public RefCountedBase {
   /// beneficial for performance to overalign a data type.
   unsigned getPreferredTypeAlign(const Type *T) const;
 
+  /// \brief Return the alignment in bits that should be given to a
+  /// global variable with type \p T.
+  unsigned getAlignOfGlobalVar(QualType T) const;
+
+  /// \brief Return the alignment in characters that should be given to a
+  /// global variable with type \p T.
+  CharUnits getAlignOfGlobalVarInChars(QualType T) const;
+
   /// \brief Return a conservative estimate of the alignment of the specified
   /// decl \p D.
   ///
diff --git a/include/clang/AST/ASTUnresolvedSet.h b/include/clang/AST/ASTUnresolvedSet.h
index c709895fc0f..5a56b4d2b46 100644
--- a/include/clang/AST/ASTUnresolvedSet.h
+++ b/include/clang/AST/ASTUnresolvedSet.h
@@ -41,10 +41,6 @@ class ASTUnresolvedSet {
   const_iterator begin() const { return const_iterator(Decls.begin()); }
   const_iterator end() const { return const_iterator(Decls.end()); }
 
-  void addDecl(ASTContext &C, NamedDecl *D) {
-    addDecl(C, D, AS_none);
-  }
-
   void addDecl(ASTContext &C, NamedDecl *D, AccessSpecifier AS) {
     Decls.push_back(DeclAccessPair::make(D, AS), C);
   }
@@ -52,10 +48,13 @@ class ASTUnresolvedSet {
   /// Replaces the given declaration with the new one, once.
   ///
   /// \return true if the set changed
-  bool replace(const NamedDecl* Old, NamedDecl *New) {
-    for (DeclsTy::iterator I = Decls.begin(), E = Decls.end(); I != E; ++I)
-      if (I->getDecl() == Old)
-        return (I->setDecl(New), true);
+  bool replace(const NamedDecl* Old, NamedDecl *New, AccessSpecifier AS) {
+    for (DeclsTy::iterator I = Decls.begin(), E = Decls.end(); I != E; ++I) {
+      if (I->getDecl() == Old) {
+        I->set(New, AS);
+        return true;
+      }
+    }
     return false;
   }
 
diff --git a/include/clang/AST/CommentCommands.td b/include/clang/AST/CommentCommands.td
index 9587ace87ca..8c88494e9ae 100644
--- a/include/clang/AST/CommentCommands.td
+++ b/include/clang/AST/CommentCommands.td
@@ -67,14 +67,12 @@ class DeclarationVerbatimLineCommand :
 }
 
 class FunctionDeclarationVerbatimLineCommand :
-      VerbatimLineCommand {
-  let IsDeclarationCommand = 1;
+      DeclarationVerbatimLineCommand {
   let IsFunctionDeclarationCommand = 1;
 }
 
 class RecordLikeDeclarationVerbatimLineCommand :
-      VerbatimLineCommand {
-  let IsDeclarationCommand = 1;
+      DeclarationVerbatimLineCommand {
   let IsRecordLikeDeclarationCommand = 1;
 }
 
@@ -121,6 +119,7 @@ def Headerfile : BlockCommand<"headerfile"> { let IsHeaderfileCommand = 1; }
 // We don't do any additional semantic analysis for the following
 // BlockCommands.  It might be a good idea to do something extra for them, but
 // for now we model them as plain BlockCommands.
+def Arg        : BlockCommand<"arg">;
 def Attention  : BlockCommand<"attention">;
 def Author     : BlockCommand<"author">;
 def Authors    : BlockCommand<"authors">;
@@ -128,7 +127,9 @@ def Bug        : BlockCommand<"bug">;
 def Copyright  : BlockCommand<"copyright">;
 def Date       : BlockCommand<"date">;
 def Invariant  : BlockCommand<"invariant">;
+def Li         : BlockCommand<"li">;
 def Note       : BlockCommand<"note">;
+def Par        : BlockCommand<"par">;
 def Post       : BlockCommand<"post">;
 def Pre        : BlockCommand<"pre">;
 def Remark     : BlockCommand<"remark">;
@@ -140,9 +141,11 @@ def Todo       : BlockCommand<"todo">;
 def Version    : BlockCommand<"version">;
 def Warning    : BlockCommand<"warning">;
 // HeaderDoc commands
+def Abstract      : BlockCommand<"abstract">;
 def ClassDesign   : RecordLikeDetailCommand<"classdesign">;
 def CoClass       : RecordLikeDetailCommand<"coclass">;
 def Dependency    : RecordLikeDetailCommand<"dependency">;
+def Discussion    : BlockCommand<"discussion">;
 def Helper        : RecordLikeDetailCommand<"helper">;
 def HelperClass   : RecordLikeDetailCommand<"helperclass">;
 def Helps         : RecordLikeDetailCommand<"helps">;
@@ -150,6 +153,7 @@ def InstanceSize  : RecordLikeDetailCommand<"instancesize">;
 def Ownership     : RecordLikeDetailCommand<"ownership">;
 def Performance   : RecordLikeDetailCommand<"performance">;
 def Security      : RecordLikeDetailCommand<"security">;
+def SeeAlso       : BlockCommand<"seealso">;
 def SuperClass    : RecordLikeDetailCommand<"superclass">;
 
 //===----------------------------------------------------------------------===//
@@ -173,6 +177,10 @@ def  FDollar  : VerbatimBlockCommand<"f$">; // Inline LaTeX formula
 defm FBracket : VerbatimBlockCommand<"f[", "f]">; // Displayed LaTeX formula
 defm FBrace   : VerbatimBlockCommand<"f{", "f}">; // LaTeX environment
 
+// HeaderDoc commands
+defm Textblock    : VerbatimBlockCommand<"textblock", "/textblock">;
+defm Link         : VerbatimBlockCommand<"link", "/link">;
+
 //===----------------------------------------------------------------------===//
 // VerbatimLineCommand
 //===----------------------------------------------------------------------===//
diff --git a/include/clang/AST/CommentLexer.h b/include/clang/AST/CommentLexer.h
index 4179f45e80e..f152c778c9a 100644
--- a/include/clang/AST/CommentLexer.h
+++ b/include/clang/AST/CommentLexer.h
@@ -15,6 +15,7 @@
 #define LLVM_CLANG_AST_COMMENT_LEXER_H
 
 #include "clang/Basic/SourceManager.h"
+#include "clang/Basic/Diagnostic.h"
 #include "llvm/ADT/SmallString.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringRef.h"
@@ -227,6 +228,8 @@ class Lexer {
   /// computed (for example, resolved decimal character references).
   llvm::BumpPtrAllocator &Allocator;
 
+  DiagnosticsEngine &Diags;
+  
   const CommandTraits &Traits;
 
   const char *const BufferStart;
@@ -316,6 +319,10 @@ class Lexer {
     return FileLoc.getLocWithOffset(CharNo);
   }
 
+  DiagnosticBuilder Diag(SourceLocation Loc, unsigned DiagID) {
+    return Diags.Report(Loc, DiagID);
+  }
+
   /// Eat string matching regexp \code \s*\* \endcode.
   void skipLineStartingDecorations();
 
@@ -346,7 +353,8 @@ class Lexer {
   void lexHTMLEndTag(Token &T);
 
 public:
-  Lexer(llvm::BumpPtrAllocator &Allocator, const CommandTraits &Traits,
+  Lexer(llvm::BumpPtrAllocator &Allocator, DiagnosticsEngine &Diags,
+        const CommandTraits &Traits,
         SourceLocation FileLoc,
         const char *BufferStart, const char *BufferEnd);
 
diff --git a/include/clang/AST/Decl.h b/include/clang/AST/Decl.h
index 7927279ddd6..a0c76c069b8 100644
--- a/include/clang/AST/Decl.h
+++ b/include/clang/AST/Decl.h
@@ -219,10 +219,6 @@ class NamedDecl : public Decl {
     return getLinkage() == ExternalLinkage;
   }
 
-  /// \brief True if this decl has external linkage. Don't cache the linkage,
-  /// because we are not finished setting up the redecl chain for the decl.
-  bool hasExternalLinkageUncached() const;
-
   /// \brief Determines the visibility of this entity.
   Visibility getVisibility() const {
     return getLinkageAndVisibility().getVisibility();
@@ -641,6 +637,13 @@ class VarDecl : public DeclaratorDecl, public Redeclarable {
     ListInit  ///< Direct list-initialization (C++11)
   };
 
+  /// \brief Kinds of thread-local storage.
+  enum TLSKind {
+    TLS_None,   ///< Not a TLS variable.
+    TLS_Static, ///< TLS with a known-constant initializer.
+    TLS_Dynamic ///< TLS with a dynamic initializer.
+  };
+
 protected:
   /// \brief Placeholder type used in Init to denote an unparsed C++ default
   /// argument.
@@ -664,7 +667,7 @@ class VarDecl : public DeclaratorDecl, public Redeclarable {
     friend class ASTDeclReader;
 
     unsigned SClass : 3;
-    unsigned ThreadSpecified : 1;
+    unsigned TSCSpec : 2;
     unsigned InitStyle : 2;
 
     /// \brief Whether this variable is the exception variable in a C++ catch
@@ -687,7 +690,7 @@ class VarDecl : public DeclaratorDecl, public Redeclarable {
     /// \brief Whether this variable is (C++0x) constexpr.
     unsigned IsConstexpr : 1;
   };
-  enum { NumVarDeclBits = 14 };
+  enum { NumVarDeclBits = 12 };
 
   friend class ASTDeclReader;
   friend class StmtIteratorBase;
@@ -771,9 +774,23 @@ class VarDecl : public DeclaratorDecl, public Redeclarable {
   }
   void setStorageClass(StorageClass SC);
 
-  void setThreadSpecified(bool T) { VarDeclBits.ThreadSpecified = T; }
-  bool isThreadSpecified() const {
-    return VarDeclBits.ThreadSpecified;
+  void setTSCSpec(ThreadStorageClassSpecifier TSC) {
+    VarDeclBits.TSCSpec = TSC;
+  }
+  ThreadStorageClassSpecifier getTSCSpec() const {
+    return static_cast(VarDeclBits.TSCSpec);
+  }
+  TLSKind getTLSKind() const {
+    switch (VarDeclBits.TSCSpec) {
+    case TSCS_unspecified:
+      return TLS_None;
+    case TSCS___thread: // Fall through.
+    case TSCS__Thread_local:
+      return TLS_Static;
+    case TSCS_thread_local:
+      return TLS_Dynamic;
+    }
+    llvm_unreachable("Unknown thread storage class specifier!");
   }
 
   /// hasLocalStorage - Returns true if a variable with function scope
@@ -813,6 +830,14 @@ class VarDecl : public DeclaratorDecl, public Redeclarable {
   /// external, C linkage.
   bool isExternC() const;
 
+  /// \brief Determines whether this variable's context is, or is nested within,
+  /// a C++ extern "C" linkage spec.
+  bool isInExternCContext() const;
+
+  /// \brief Determines whether this variable's context is, or is nested within,
+  /// a C++ extern "C++" linkage spec.
+  bool isInExternCXXContext() const;
+
   /// isLocalVarDecl - Returns true for local variable declarations
   /// other than parameters.  Note that this includes static variables
   /// inside of functions. It also includes variables inside blocks.
@@ -1698,6 +1723,14 @@ class FunctionDecl : public DeclaratorDecl, public DeclContext,
   /// external, C linkage.
   bool isExternC() const;
 
+  /// \brief Determines whether this function's context is, or is nested within,
+  /// a C++ extern "C" linkage spec.
+  bool isInExternCContext() const;
+
+  /// \brief Determines whether this function's context is, or is nested within,
+  /// a C++ extern "C++" linkage spec.
+  bool isInExternCXXContext() const;
+
   /// \brief Determines whether this is a global function.
   bool isGlobal() const;
 
@@ -2394,7 +2427,7 @@ class TagDecl
   bool IsScopedUsingClassTag : 1;
 
   /// IsFixed - True if this is an enumeration with fixed underlying type. Only
-  /// possible in C++11 or Microsoft extensions mode.
+  /// possible in C++11, Microsoft extensions, or Objective C mode.
   bool IsFixed : 1;
 
   /// \brief Indicates whether it is possible for declarations of this kind
@@ -2780,18 +2813,18 @@ class EnumDecl : public TagDecl {
     NumNegativeBits = Num;
   }
 
-  /// \brief Returns true if this is a C++0x scoped enumeration.
+  /// \brief Returns true if this is a C++11 scoped enumeration.
   bool isScoped() const {
     return IsScoped;
   }
 
-  /// \brief Returns true if this is a C++0x scoped enumeration.
+  /// \brief Returns true if this is a C++11 scoped enumeration.
   bool isScopedUsingClassTag() const {
     return IsScopedUsingClassTag;
   }
 
-  /// \brief Returns true if this is a C++0x enumeration with fixed underlying
-  /// type.
+  /// \brief Returns true if this is an Objective-C, C++11, or
+  /// Microsoft-style enumeration with a fixed underlying type.
   bool isFixed() const {
     return IsFixed;
   }
@@ -3162,6 +3195,67 @@ class BlockDecl : public Decl, public DeclContext {
   }
 };
 
+/// \brief This represents the body of a CapturedStmt, and serves as its
+/// DeclContext.
+class CapturedDecl : public Decl, public DeclContext {
+private:
+  /// \brief The number of parameters to the outlined function.
+  unsigned NumParams;
+  /// \brief The body of the outlined function.
+  Stmt *Body;
+
+  explicit CapturedDecl(DeclContext *DC, unsigned NumParams)
+    : Decl(Captured, DC, SourceLocation()), DeclContext(Captured),
+      NumParams(NumParams), Body(0) { }
+
+  ImplicitParamDecl **getParams() const {
+    return reinterpret_cast(
+             const_cast(this) + 1);
+  }
+
+public:
+  static CapturedDecl *Create(ASTContext &C, DeclContext *DC, unsigned NumParams);
+  static CapturedDecl *CreateDeserialized(ASTContext &C, unsigned ID,
+                                          unsigned NumParams);
+
+  Stmt *getBody() const { return Body; }
+  void setBody(Stmt *B) { Body = B; }
+
+  unsigned getNumParams() const { return NumParams; }
+
+  ImplicitParamDecl *getParam(unsigned i) const {
+    assert(i < NumParams);
+    return getParams()[i];
+  }
+  void setParam(unsigned i, ImplicitParamDecl *P) {
+    assert(i < NumParams);
+    getParams()[i] = P;
+  }
+
+  /// \brief Retrieve the parameter containing captured variables.
+  ImplicitParamDecl *getContextParam() const { return getParam(0); }
+  void setContextParam(ImplicitParamDecl *P) { setParam(0, P); }
+
+  typedef ImplicitParamDecl **param_iterator;
+  /// \brief Retrieve an iterator pointing to the first parameter decl.
+  param_iterator param_begin() const { return getParams(); }
+  /// \brief Retrieve an iterator one past the last parameter decl.
+  param_iterator param_end() const { return getParams() + NumParams; }
+
+  // Implement isa/cast/dyncast/etc.
+  static bool classof(const Decl *D) { return classofKind(D->getKind()); }
+  static bool classofKind(Kind K) { return K == Captured; }
+  static DeclContext *castToDeclContext(const CapturedDecl *D) {
+    return static_cast(const_cast(D));
+  }
+  static CapturedDecl *castFromDeclContext(const DeclContext *DC) {
+    return static_cast(const_cast(DC));
+  }
+
+  friend class ASTDeclReader;
+  friend class ASTDeclWriter;
+};
+
 /// \brief Describes a module import declaration, which makes the contents
 /// of the named module visible in the current translation unit.
 ///
diff --git a/include/clang/AST/DeclBase.h b/include/clang/AST/DeclBase.h
index 852bb9ab040..754facfb591 100644
--- a/include/clang/AST/DeclBase.h
+++ b/include/clang/AST/DeclBase.h
@@ -372,10 +372,13 @@ class Decl {
     return const_cast(this)->getDeclContext();
   }
 
-  /// Finds the innermost non-closure context of this declaration.
-  /// That is, walk out the DeclContext chain, skipping any blocks.
-  DeclContext *getNonClosureContext();
-  const DeclContext *getNonClosureContext() const {
+  /// Find the innermost non-closure ancestor of this declaration,
+  /// walking up through blocks, lambdas, etc.  If that ancestor is
+  /// not a code context (!isFunctionOrMethod()), returns null.
+  ///
+  /// A declaration may be its own non-closure context.
+  Decl *getNonClosureContext();
+  const Decl *getNonClosureContext() const {
     return const_cast(this)->getNonClosureContext();
   }
 
@@ -402,6 +405,12 @@ class Decl {
     return AccessSpecifier(Access);
   }
 
+  /// \brief Retrieve the access specifier for this declaration, even though
+  /// it may not yet have been properly set.
+  AccessSpecifier getAccessUnsafe() const {
+    return AccessSpecifier(Access);
+  }
+
   bool hasAttrs() const { return HasAttrs; }
   void setAttrs(const AttrVec& Attrs) {
     return setAttrsImpl(Attrs, getASTContext());
@@ -1040,6 +1049,7 @@ class DeclContext {
   bool isFunctionOrMethod() const {
     switch (DeclKind) {
     case Decl::Block:
+    case Decl::Captured:
     case Decl::ObjCMethod:
       return true;
     default:
@@ -1086,14 +1096,6 @@ class DeclContext {
   /// C++0x scoped enums), and C++ linkage specifications.
   bool isTransparentContext() const;
 
-  /// \brief Determines whether this context is, or is nested within,
-  /// a C++ extern "C" linkage spec.
-  bool isExternCContext() const;
-
-  /// \brief Determines whether this context is, or is nested within,
-  /// a C++ extern "C++" linkage spec.
-  bool isExternCXXContext() const;
-
   /// \brief Determine whether this declaration context is equivalent
   /// to the declaration context DC.
   bool Equals(const DeclContext *DC) const {
@@ -1107,8 +1109,8 @@ class DeclContext {
   /// \brief Find the nearest non-closure ancestor of this context,
   /// i.e. the innermost semantic parent of this context which is not
   /// a closure.  A context may be its own non-closure ancestor.
-  DeclContext *getNonClosureAncestor();
-  const DeclContext *getNonClosureAncestor() const {
+  Decl *getNonClosureAncestor();
+  const Decl *getNonClosureAncestor() const {
     return const_cast(this)->getNonClosureAncestor();
   }
 
@@ -1402,6 +1404,9 @@ class DeclContext {
 
   /// @brief Removes a declaration from this context.
   void removeDecl(Decl *D);
+    
+  /// @brief Checks whether a declaration is in this context.
+  bool containsDecl(Decl *D) const;
 
   /// lookup_iterator - An iterator that provides access to the results
   /// of looking up a name within this context.
diff --git a/include/clang/AST/DeclCXX.h b/include/clang/AST/DeclCXX.h
index 05ff49c64ce..c483dde1f51 100644
--- a/include/clang/AST/DeclCXX.h
+++ b/include/clang/AST/DeclCXX.h
@@ -254,6 +254,16 @@ class CXXBaseSpecifier {
   TypeSourceInfo *getTypeSourceInfo() const { return BaseTypeInfo; }
 };
 
+/// The inheritance model to use for member pointers of a given CXXRecordDecl.
+enum MSInheritanceModel {
+  MSIM_Single,
+  MSIM_SinglePolymorphic,
+  MSIM_Multiple,
+  MSIM_MultiplePolymorphic,
+  MSIM_Virtual,
+  MSIM_Unspecified
+};
+
 /// CXXRecordDecl - Represents a C++ struct/union/class.
 /// FIXME: This class will disappear once we've properly taught RecordDecl
 /// to deal with C++-specific things.
@@ -1744,8 +1754,6 @@ class CXXCtorInitializer {
 
   /// \brief The argument used to initialize the base or member, which may
   /// end up constructing an object (when multiple arguments are involved).
-  /// If 0, this is a field initializer, and the in-class member initializer
-  /// will be used.
   Stmt *Init;
 
   /// LParenLoc - Location of the left paren of the ctor-initializer.
@@ -1830,7 +1838,7 @@ class CXXCtorInitializer {
   /// implicit ctor initializer generated for a field with an initializer
   /// defined on the member declaration.
   bool isInClassMemberInitializer() const {
-    return !Init;
+    return isa(Init);
   }
 
   /// isDelegatingInitializer - Returns true when this initializer is creating
@@ -1957,14 +1965,8 @@ class CXXCtorInitializer {
                                getNumArrayIndices());
   }
 
-  /// \brief Get the initializer. This is 0 if this is an in-class initializer
-  /// for a non-static data member which has not yet been parsed.
-  Expr *getInit() const {
-    if (!Init)
-      return getAnyMember()->getInClassInitializer();
-
-    return static_cast(Init);
-  }
+  /// \brief Get the initializer.
+  Expr *getInit() const { return static_cast(Init); }
 };
 
 /// CXXConstructorDecl - Represents a C++ constructor within a
@@ -2349,38 +2351,49 @@ class LinkageSpecDecl : public Decl, public DeclContext {
   };
 private:
   /// Language - The language for this linkage specification.
-  LanguageIDs Language;
+  unsigned Language : 3;
+  /// True if this linkage spec has brances. This is needed so that hasBraces()
+  /// returns the correct result while the linkage spec body is being parsed.
+  /// Once RBraceLoc has been set this is not used, so it doesn't need to be
+  /// serialized.
+  unsigned HasBraces : 1;
   /// ExternLoc - The source location for the extern keyword.
   SourceLocation ExternLoc;
   /// RBraceLoc - The source location for the right brace (if valid).
   SourceLocation RBraceLoc;
 
   LinkageSpecDecl(DeclContext *DC, SourceLocation ExternLoc,
-                  SourceLocation LangLoc, LanguageIDs lang,
-                  SourceLocation RBLoc)
+                  SourceLocation LangLoc, LanguageIDs lang, bool HasBraces)
     : Decl(LinkageSpec, DC, LangLoc), DeclContext(LinkageSpec),
-      Language(lang), ExternLoc(ExternLoc), RBraceLoc(RBLoc) { }
+      Language(lang), HasBraces(HasBraces), ExternLoc(ExternLoc),
+      RBraceLoc(SourceLocation()) { }
 
 public:
   static LinkageSpecDecl *Create(ASTContext &C, DeclContext *DC,
                                  SourceLocation ExternLoc,
                                  SourceLocation LangLoc, LanguageIDs Lang,
-                                 SourceLocation RBraceLoc = SourceLocation());
+                                 bool HasBraces);
   static LinkageSpecDecl *CreateDeserialized(ASTContext &C, unsigned ID);
   
   /// \brief Return the language specified by this linkage specification.
-  LanguageIDs getLanguage() const { return Language; }
+  LanguageIDs getLanguage() const { return LanguageIDs(Language); }
   /// \brief Set the language specified by this linkage specification.
   void setLanguage(LanguageIDs L) { Language = L; }
 
   /// \brief Determines whether this linkage specification had braces in
   /// its syntactic form.
-  bool hasBraces() const { return RBraceLoc.isValid(); }
+  bool hasBraces() const {
+    assert(!RBraceLoc.isValid() || HasBraces);
+    return HasBraces;
+  }
 
   SourceLocation getExternLoc() const { return ExternLoc; }
   SourceLocation getRBraceLoc() const { return RBraceLoc; }
   void setExternLoc(SourceLocation L) { ExternLoc = L; }
-  void setRBraceLoc(SourceLocation L) { RBraceLoc = L; }
+  void setRBraceLoc(SourceLocation L) {
+    RBraceLoc = L;
+    HasBraces = RBraceLoc.isValid();
+  }
 
   SourceLocation getLocEnd() const LLVM_READONLY {
     if (hasBraces())
@@ -2974,6 +2987,56 @@ class StaticAssertDecl : public Decl {
   friend class ASTDeclReader;
 };
 
+/// An instance of this class represents the declaration of a property
+/// member.  This is a Microsoft extension to C++, first introduced in
+/// Visual Studio .NET 2003 as a parallel to similar features in C#
+/// and Managed C++.
+///
+/// A property must always be a non-static class member.
+///
+/// A property member superficially resembles a non-static data
+/// member, except preceded by a property attribute:
+///   __declspec(property(get=GetX, put=PutX)) int x;
+/// Either (but not both) of the 'get' and 'put' names may be omitted.
+///
+/// A reference to a property is always an lvalue.  If the lvalue
+/// undergoes lvalue-to-rvalue conversion, then a getter name is
+/// required, and that member is called with no arguments.
+/// If the lvalue is assigned into, then a setter name is required,
+/// and that member is called with one argument, the value assigned.
+/// Both operations are potentially overloaded.  Compound assignments
+/// are permitted, as are the increment and decrement operators.
+///
+/// The getter and putter methods are permitted to be overloaded,
+/// although their return and parameter types are subject to certain
+/// restrictions according to the type of the property.
+///
+/// A property declared using an incomplete array type may
+/// additionally be subscripted, adding extra parameters to the getter
+/// and putter methods.
+class MSPropertyDecl : public DeclaratorDecl {
+  IdentifierInfo *GetterId, *SetterId;
+
+public:
+  MSPropertyDecl(DeclContext *DC, SourceLocation L,
+                 DeclarationName N, QualType T, TypeSourceInfo *TInfo,
+                 SourceLocation StartL, IdentifierInfo *Getter,
+                 IdentifierInfo *Setter):
+  DeclaratorDecl(MSProperty, DC, L, N, T, TInfo, StartL), GetterId(Getter),
+  SetterId(Setter) {}
+
+  static MSPropertyDecl *CreateDeserialized(ASTContext &C, unsigned ID);
+
+  static bool classof(const Decl *D) { return D->getKind() == MSProperty; }
+
+  bool hasGetter() const { return GetterId != NULL; }
+  IdentifierInfo* getGetterId() const { return GetterId; }
+  bool hasSetter() const { return SetterId != NULL; }
+  IdentifierInfo* getSetterId() const { return SetterId; }
+
+  friend class ASTDeclReader;
+};
+
 /// Insertion operator for diagnostics.  This allows sending an AccessSpecifier
 /// into a diagnostic with <<.
 const DiagnosticBuilder &operator<<(const DiagnosticBuilder &DB,
diff --git a/include/clang/AST/DeclFriend.h b/include/clang/AST/DeclFriend.h
index 253c23c199a..3a12878e741 100644
--- a/include/clang/AST/DeclFriend.h
+++ b/include/clang/AST/DeclFriend.h
@@ -101,7 +101,7 @@ class FriendDecl : public Decl {
                             SourceLocation L, FriendUnion Friend_,
                             SourceLocation FriendL,
                             ArrayRef FriendTypeTPLists
-                            = ArrayRef());
+                            = None);
   static FriendDecl *CreateDeserialized(ASTContext &C, unsigned ID,
                                         unsigned FriendTypeNumTPLists);
 
diff --git a/include/clang/AST/DeclObjC.h b/include/clang/AST/DeclObjC.h
index c29492298bf..40de0135a74 100644
--- a/include/clang/AST/DeclObjC.h
+++ b/include/clang/AST/DeclObjC.h
@@ -1136,7 +1136,8 @@ class ObjCInterfaceDecl : public ObjCContainerDecl
   // Lookup a method. First, we search locally. If a method isn't
   // found, we search referenced protocols and class categories.
   ObjCMethodDecl *lookupMethod(Selector Sel, bool isInstance,
-                               bool shallowCategoryLookup= false) const;
+                               bool shallowCategoryLookup= false,
+                               const ObjCCategoryDecl *C= 0) const;
   ObjCMethodDecl *lookupInstanceMethod(Selector Sel,
                             bool shallowCategoryLookup = false) const {
     return lookupMethod(Sel, true/*isInstance*/, shallowCategoryLookup);
@@ -1155,6 +1156,15 @@ class ObjCInterfaceDecl : public ObjCContainerDecl
     return lookupPrivateMethod(Sel, false);
   }
 
+  /// \brief Lookup a setter or getter in the class hierarchy,
+  /// including in all categories except for category passed
+  /// as argument.
+  ObjCMethodDecl *lookupPropertyAccessor(const Selector Sel,
+                                         const ObjCCategoryDecl *Cat) const {
+    return lookupMethod(Sel, true/*isInstance*/,
+                        false/*shallowCategoryLookup*/, Cat);
+  }
+                          
   SourceLocation getEndOfDefinitionLoc() const { 
     if (!hasDefinition())
       return getLocation();
@@ -1788,6 +1798,8 @@ class ObjCImplementationDecl : public ObjCImplDecl {
   virtual void anchor();
   /// Implementation Class's super class.
   ObjCInterfaceDecl *SuperClass;
+  SourceLocation SuperLoc;
+
   /// \@implementation may have private ivars.
   SourceLocation IvarLBraceLoc;
   SourceLocation IvarRBraceLoc;
@@ -1808,10 +1820,11 @@ class ObjCImplementationDecl : public ObjCImplDecl {
                          ObjCInterfaceDecl *classInterface,
                          ObjCInterfaceDecl *superDecl,
                          SourceLocation nameLoc, SourceLocation atStartLoc,
+                         SourceLocation superLoc = SourceLocation(),
                          SourceLocation IvarLBraceLoc=SourceLocation(), 
                          SourceLocation IvarRBraceLoc=SourceLocation())
     : ObjCImplDecl(ObjCImplementation, DC, classInterface, nameLoc, atStartLoc),
-       SuperClass(superDecl), IvarLBraceLoc(IvarLBraceLoc), 
+       SuperClass(superDecl), SuperLoc(superLoc), IvarLBraceLoc(IvarLBraceLoc),
        IvarRBraceLoc(IvarRBraceLoc),
        IvarInitializers(0), NumIvarInitializers(0),
        HasNonZeroConstructors(false), HasDestructors(false) {}
@@ -1821,6 +1834,7 @@ class ObjCImplementationDecl : public ObjCImplDecl {
                                         ObjCInterfaceDecl *superDecl,
                                         SourceLocation nameLoc,
                                         SourceLocation atStartLoc,
+                                     SourceLocation superLoc = SourceLocation(),
                                         SourceLocation IvarLBraceLoc=SourceLocation(), 
                                         SourceLocation IvarRBraceLoc=SourceLocation());
 
@@ -1893,6 +1907,7 @@ class ObjCImplementationDecl : public ObjCImplDecl {
 
   const ObjCInterfaceDecl *getSuperClass() const { return SuperClass; }
   ObjCInterfaceDecl *getSuperClass() { return SuperClass; }
+  SourceLocation getSuperClassLoc() const { return SuperLoc; }
 
   void setSuperClass(ObjCInterfaceDecl * superCls) { SuperClass = superCls; }
 
diff --git a/include/clang/AST/EvaluatedExprVisitor.h b/include/clang/AST/EvaluatedExprVisitor.h
index eb186c217e3..2e3cbfad919 100644
--- a/include/clang/AST/EvaluatedExprVisitor.h
+++ b/include/clang/AST/EvaluatedExprVisitor.h
@@ -55,7 +55,7 @@ class EvaluatedExprVisitor : public StmtVisitor {
     // Only the selected subexpression matters; the other one is not evaluated.
     return this->Visit(E->getChosenSubExpr(Context));
   }
-                 
+
   void VisitDesignatedInitExpr(DesignatedInitExpr *E) {
     // Only the actual initializer matters; the designators are all constant
     // expressions.
@@ -72,6 +72,15 @@ class EvaluatedExprVisitor : public StmtVisitor {
       return static_cast(this)->VisitExpr(CE);
   }
 
+  void VisitLambdaExpr(LambdaExpr *LE) {
+    // Only visit the capture initializers, and not the body.
+    for (LambdaExpr::capture_init_iterator I = LE->capture_init_begin(),
+                                           E = LE->capture_init_end();
+         I != E; ++I)
+      if (*I)
+        this->Visit(*I);
+  }
+
   /// \brief The basis case walks all of the children of the statement or
   /// expression, assuming they are all potentially evaluated.
   void VisitStmt(Stmt *S) {
diff --git a/include/clang/AST/Expr.h b/include/clang/AST/Expr.h
index 36d70d8b0b7..4ff1257b7dc 100644
--- a/include/clang/AST/Expr.h
+++ b/include/clang/AST/Expr.h
@@ -427,12 +427,24 @@ class Expr : public Stmt {
 
 public:
 
+  /// \brief Returns true if this expression is a gl-value that
+  /// potentially refers to a bit-field.
+  ///
+  /// In C++, whether a gl-value refers to a bitfield is essentially
+  /// an aspect of the value-kind type system.
+  bool refersToBitField() const { return getObjectKind() == OK_BitField; }
+
   /// \brief If this expression refers to a bit-field, retrieve the
   /// declaration of that bit-field.
-  FieldDecl *getBitField();
+  ///
+  /// Note that this returns a non-null pointer in subtly different
+  /// places than refersToBitField returns true.  In particular, this can
+  /// return a non-null pointer even for r-values loaded from
+  /// bit-fields, but it will return null for a conditional bit-field.
+  FieldDecl *getSourceBitField();
 
-  const FieldDecl *getBitField() const {
-    return const_cast(this)->getBitField();
+  const FieldDecl *getSourceBitField() const {
+    return const_cast(this)->getSourceBitField();
   }
 
   /// \brief If this expression is an l-value for an Objective C
@@ -2644,7 +2656,7 @@ class CastExpr : public Expr {
          (ty->isInstantiationDependentType() ||
           (op && op->isInstantiationDependent())),
          (ty->containsUnexpandedParameterPack() ||
-          op->containsUnexpandedParameterPack())),
+          (op && op->containsUnexpandedParameterPack()))),
     Op(op) {
     assert(kind != CK_Invalid && "creating cast with invalid cast kind");
     CastExprBits.Kind = kind;
diff --git a/include/clang/AST/ExprCXX.h b/include/clang/AST/ExprCXX.h
index 04f6fb64cfd..91e5b21eacf 100644
--- a/include/clang/AST/ExprCXX.h
+++ b/include/clang/AST/ExprCXX.h
@@ -29,6 +29,7 @@ class CXXConstructorDecl;
 class CXXDestructorDecl;
 class CXXMethodDecl;
 class CXXTemporary;
+class MSPropertyDecl;
 class TemplateArgumentListInfo;
 class UuidAttr;
 
@@ -560,6 +561,64 @@ class CXXTypeidExpr : public Expr {
   }
 };
 
+/// A member reference to an MSPropertyDecl.  This expression always
+/// has pseudo-object type, and therefore it is typically not
+/// encountered in a fully-typechecked expression except within the
+/// syntactic form of a PseudoObjectExpr.
+class MSPropertyRefExpr : public Expr {
+  Expr *BaseExpr;
+  MSPropertyDecl *TheDecl;
+  SourceLocation MemberLoc;
+  bool IsArrow;
+  NestedNameSpecifierLoc QualifierLoc;
+
+public:
+  MSPropertyRefExpr(Expr *baseExpr, MSPropertyDecl *decl, bool isArrow,
+                    QualType ty, ExprValueKind VK,
+                    NestedNameSpecifierLoc qualifierLoc,
+                    SourceLocation nameLoc)
+  : Expr(MSPropertyRefExprClass, ty, VK, OK_Ordinary,
+         /*type-dependent*/ false, baseExpr->isValueDependent(),
+         baseExpr->isInstantiationDependent(),
+         baseExpr->containsUnexpandedParameterPack()),
+    BaseExpr(baseExpr), TheDecl(decl),
+    MemberLoc(nameLoc), IsArrow(isArrow),
+    QualifierLoc(qualifierLoc) {}
+
+  MSPropertyRefExpr(EmptyShell Empty) : Expr(MSPropertyRefExprClass, Empty) {}
+
+  SourceRange getSourceRange() const LLVM_READONLY {
+    return SourceRange(getLocStart(), getLocEnd());
+  }
+  bool isImplicitAccess() const {
+    return getBaseExpr() && getBaseExpr()->isImplicitCXXThis();
+  }
+  SourceLocation getLocStart() const {
+    if (!isImplicitAccess())
+      return BaseExpr->getLocStart();
+    else if (QualifierLoc)
+      return QualifierLoc.getBeginLoc();
+    else
+        return MemberLoc;
+  }
+  SourceLocation getLocEnd() const { return getMemberLoc(); }
+
+  child_range children() {
+    return child_range((Stmt**)&BaseExpr, (Stmt**)&BaseExpr + 1);
+  }
+  static bool classof(const Stmt *T) {
+    return T->getStmtClass() == MSPropertyRefExprClass;
+  }
+
+  Expr *getBaseExpr() const { return BaseExpr; }
+  MSPropertyDecl *getPropertyDecl() const { return TheDecl; }
+  bool isArrow() const { return IsArrow; }
+  SourceLocation getMemberLoc() const { return MemberLoc; }
+  NestedNameSpecifierLoc getQualifierLoc() const { return QualifierLoc; }
+
+  friend class ASTStmtReader;
+};
+
 /// CXXUuidofExpr - A microsoft C++ @c __uuidof expression, which gets
 /// the _GUID that corresponds to the supplied type or expression.
 ///
@@ -825,6 +884,53 @@ class CXXDefaultArgExpr : public Expr {
   friend class ASTStmtWriter;
 };
 
+/// \brief This wraps a use of a C++ default initializer (technically,
+/// a brace-or-equal-initializer for a non-static data member) when it
+/// is implicitly used in a mem-initializer-list in a constructor
+/// (C++11 [class.base.init]p8) or in aggregate initialization
+/// (C++1y [dcl.init.aggr]p7).
+class CXXDefaultInitExpr : public Expr {
+  /// \brief The field whose default is being used.
+  FieldDecl *Field;
+
+  /// \brief The location where the default initializer expression was used.
+  SourceLocation Loc;
+
+  CXXDefaultInitExpr(ASTContext &C, SourceLocation Loc, FieldDecl *Field,
+                     QualType T);
+
+  CXXDefaultInitExpr(EmptyShell Empty) : Expr(CXXDefaultInitExprClass, Empty) {}
+
+public:
+  // Field is the non-static data member whose default initializer is used
+  // by this expression.
+  static CXXDefaultInitExpr *Create(ASTContext &C, SourceLocation Loc,
+                                    FieldDecl *Field) {
+    return new (C) CXXDefaultInitExpr(C, Loc, Field, Field->getType());
+  }
+
+  // Get the field whose initializer will be used.
+  FieldDecl *getField() { return Field; }
+  const FieldDecl *getField() const { return Field; }
+
+  // Get the initialization expression that will be used.
+  const Expr *getExpr() const { return Field->getInClassInitializer(); }
+  Expr *getExpr() { return Field->getInClassInitializer(); }
+
+  SourceLocation getLocStart() const LLVM_READONLY { return Loc; }
+  SourceLocation getLocEnd() const LLVM_READONLY { return Loc; }
+
+  static bool classof(const Stmt *T) {
+    return T->getStmtClass() == CXXDefaultInitExprClass;
+  }
+
+  // Iterators
+  child_range children() { return child_range(); }
+
+  friend class ASTReader;
+  friend class ASTStmtReader;
+};
+
 /// CXXTemporary - Represents a C++ temporary.
 class CXXTemporary {
   /// Destructor - The destructor that needs to be called.
diff --git a/include/clang/AST/ExprObjC.h b/include/clang/AST/ExprObjC.h
index dfd45279dd1..a94c69a115d 100644
--- a/include/clang/AST/ExprObjC.h
+++ b/include/clang/AST/ExprObjC.h
@@ -476,7 +476,8 @@ class ObjCIvarRefExpr : public Expr {
                   SourceLocation l, SourceLocation oploc,
                   Expr *base,
                   bool arrow = false, bool freeIvar = false) :
-    Expr(ObjCIvarRefExprClass, t, VK_LValue, OK_Ordinary,
+    Expr(ObjCIvarRefExprClass, t, VK_LValue,
+         d->isBitField() ? OK_BitField : OK_Ordinary,
          /*TypeDependent=*/false, base->isValueDependent(), 
          base->isInstantiationDependent(),
          base->containsUnexpandedParameterPack()), 
diff --git a/include/clang/AST/Mangle.h b/include/clang/AST/Mangle.h
index 94faa19f1b3..b6d22cfb5fd 100644
--- a/include/clang/AST/Mangle.h
+++ b/include/clang/AST/Mangle.h
@@ -141,6 +141,16 @@ class MangleContext {
                                           raw_ostream &) {
     llvm_unreachable("Target does not support mangling guard variables");
   }
+  // FIXME: Revisit this once we know what we need to do for MSVC compatibility.
+  virtual void mangleItaniumThreadLocalInit(const VarDecl *D,
+                                            raw_ostream &) {
+    llvm_unreachable("Target does not support mangling thread_local variables");
+  }
+  virtual void mangleItaniumThreadLocalWrapper(const VarDecl *D,
+                                               raw_ostream &) {
+    llvm_unreachable("Target does not support mangling thread_local variables");
+  }
+
   /// @}
 };
 
diff --git a/include/clang/AST/RawCommentList.h b/include/clang/AST/RawCommentList.h
index 3a8b2183a55..84a6e96fa01 100644
--- a/include/clang/AST/RawCommentList.h
+++ b/include/clang/AST/RawCommentList.h
@@ -10,6 +10,7 @@
 #ifndef LLVM_CLANG_AST_RAW_COMMENT_LIST_H
 #define LLVM_CLANG_AST_RAW_COMMENT_LIST_H
 
+#include "clang/Basic/CommentOptions.h"
 #include "clang/Basic/SourceManager.h"
 #include "llvm/ADT/ArrayRef.h"
 
@@ -40,7 +41,7 @@ class RawComment {
   RawComment() : Kind(RCK_Invalid), IsAlmostTrailingComment(false) { }
 
   RawComment(const SourceManager &SourceMgr, SourceRange SR,
-             bool Merged = false);
+             bool Merged, bool ParseAllComments);
 
   CommentKind getKind() const LLVM_READONLY {
     return (CommentKind) Kind;
@@ -82,7 +83,8 @@ class RawComment {
 
   /// Returns true if this comment is not a documentation comment.
   bool isOrdinary() const LLVM_READONLY {
-    return (Kind == RCK_OrdinaryBCPL) || (Kind == RCK_OrdinaryC);
+    return ((Kind == RCK_OrdinaryBCPL) || (Kind == RCK_OrdinaryC)) &&
+        !ParseAllComments;
   }
 
   /// Returns true if this comment any kind of a documentation comment.
@@ -90,6 +92,11 @@ class RawComment {
     return !isInvalid() && !isOrdinary();
   }
 
+  /// Returns whether we are parsing all comments.
+  bool isParseAllComments() const LLVM_READONLY {
+    return ParseAllComments;
+  }
+
   /// Returns raw comment text with comment markers.
   StringRef getRawText(const SourceManager &SourceMgr) const {
     if (RawTextValid)
@@ -135,6 +142,10 @@ class RawComment {
   bool IsTrailingComment : 1;
   bool IsAlmostTrailingComment : 1;
 
+  /// When true, ordinary comments starting with "//" and "/*" will be
+  /// considered as documentation comments.
+  bool ParseAllComments : 1;
+
   mutable bool BeginLineValid : 1; ///< True if BeginLine is valid
   mutable bool EndLineValid : 1;   ///< True if EndLine is valid
   mutable unsigned BeginLine;      ///< Cached line number
@@ -142,10 +153,12 @@ class RawComment {
 
   /// \brief Constructor for AST deserialization.
   RawComment(SourceRange SR, CommentKind K, bool IsTrailingComment,
-             bool IsAlmostTrailingComment) :
+             bool IsAlmostTrailingComment,
+             bool ParseAllComments) :
     Range(SR), RawTextValid(false), BriefTextValid(false), Kind(K),
     IsAttached(false), IsTrailingComment(IsTrailingComment),
     IsAlmostTrailingComment(IsAlmostTrailingComment),
+    ParseAllComments(ParseAllComments),
     BeginLineValid(false), EndLineValid(false)
   { }
 
@@ -207,4 +220,3 @@ class RawCommentList {
 } // end namespace clang
 
 #endif
-
diff --git a/include/clang/AST/RecursiveASTVisitor.h b/include/clang/AST/RecursiveASTVisitor.h
index 0191964bbfe..b5a4b5e36d7 100644
--- a/include/clang/AST/RecursiveASTVisitor.h
+++ b/include/clang/AST/RecursiveASTVisitor.h
@@ -1228,8 +1228,9 @@ bool RecursiveASTVisitor::TraverseDeclContextHelper(DeclContext *DC) {
   for (DeclContext::decl_iterator Child = DC->decls_begin(),
            ChildEnd = DC->decls_end();
        Child != ChildEnd; ++Child) {
-    // BlockDecls are traversed through BlockExprs.
-    if (!isa(*Child))
+    // BlockDecls and CapturedDecls are traversed through BlockExprs and
+    // CapturedStmts respectively.
+    if (!isa(*Child) && !isa(*Child))
       TRY_TO(TraverseDecl(*Child));
   }
 
@@ -1258,6 +1259,14 @@ DEF_TRAVERSE_DECL(BlockDecl, {
     return true;
   })
 
+DEF_TRAVERSE_DECL(CapturedDecl, {
+    TRY_TO(TraverseStmt(D->getBody()));
+    // This return statement makes sure the traversal of nodes in
+    // decls_begin()/decls_end() (done in the DEF_TRAVERSE_DECL macro)
+    // is skipped - don't remove it.
+    return true;
+  })
+
 DEF_TRAVERSE_DECL(EmptyDecl, { })
 
 DEF_TRAVERSE_DECL(FileScopeAsmDecl, {
@@ -1671,6 +1680,10 @@ bool RecursiveASTVisitor::TraverseDeclaratorHelper(DeclaratorDecl *D) {
   return true;
 }
 
+DEF_TRAVERSE_DECL(MSPropertyDecl, {
+    TRY_TO(TraverseDeclaratorHelper(D));
+  })
+
 DEF_TRAVERSE_DECL(FieldDecl, {
     TRY_TO(TraverseDeclaratorHelper(D));
     if (D->isBitField())
@@ -2058,6 +2071,10 @@ DEF_TRAVERSE_STMT(CXXTypeidExpr, {
       TRY_TO(TraverseTypeLoc(S->getTypeOperandSourceInfo()->getTypeLoc()));
   })
 
+DEF_TRAVERSE_STMT(MSPropertyRefExpr, {
+  TRY_TO(TraverseNestedNameSpecifierLoc(S->getQualifierLoc()));
+})
+
 DEF_TRAVERSE_STMT(CXXUuidofExpr, {
     // The child-iterator will pick up the arg if it's an expression,
     // but not if it's a type.
@@ -2153,6 +2170,7 @@ DEF_TRAVERSE_STMT(CompoundLiteralExpr, {
 DEF_TRAVERSE_STMT(CXXBindTemporaryExpr, { })
 DEF_TRAVERSE_STMT(CXXBoolLiteralExpr, { })
 DEF_TRAVERSE_STMT(CXXDefaultArgExpr, { })
+DEF_TRAVERSE_STMT(CXXDefaultInitExpr, { })
 DEF_TRAVERSE_STMT(CXXDeleteExpr, { })
 DEF_TRAVERSE_STMT(ExprWithCleanups, { })
 DEF_TRAVERSE_STMT(CXXNullPtrLiteralExpr, { })
@@ -2177,7 +2195,10 @@ DEF_TRAVERSE_STMT(ObjCEncodeExpr, {
 })
 DEF_TRAVERSE_STMT(ObjCIsaExpr, { })
 DEF_TRAVERSE_STMT(ObjCIvarRefExpr, { })
-DEF_TRAVERSE_STMT(ObjCMessageExpr, { })
+DEF_TRAVERSE_STMT(ObjCMessageExpr, {
+  if (TypeSourceInfo *TInfo = S->getClassReceiverTypeInfo())
+    TRY_TO(TraverseTypeLoc(TInfo->getTypeLoc()));
+})
 DEF_TRAVERSE_STMT(ObjCPropertyRefExpr, { })
 DEF_TRAVERSE_STMT(ObjCSubscriptRefExpr, { })
 DEF_TRAVERSE_STMT(ObjCProtocolExpr, { })
@@ -2210,6 +2231,9 @@ DEF_TRAVERSE_STMT(UnresolvedMemberExpr, {
 DEF_TRAVERSE_STMT(SEHTryStmt, {})
 DEF_TRAVERSE_STMT(SEHExceptStmt, {})
 DEF_TRAVERSE_STMT(SEHFinallyStmt,{})
+DEF_TRAVERSE_STMT(CapturedStmt, {
+  TRY_TO(TraverseDecl(S->getCapturedDecl()));
+})
 
 DEF_TRAVERSE_STMT(CXXOperatorCallExpr, { })
 DEF_TRAVERSE_STMT(OpaqueValueExpr, { })
diff --git a/include/clang/AST/Stmt.h b/include/clang/AST/Stmt.h
index cf8fc249c59..74c9ec20536 100644
--- a/include/clang/AST/Stmt.h
+++ b/include/clang/AST/Stmt.h
@@ -16,10 +16,12 @@
 
 #include "clang/AST/DeclGroup.h"
 #include "clang/AST/StmtIterator.h"
+#include "clang/Basic/CapturedStmt.h"
 #include "clang/Basic/IdentifierTable.h"
 #include "clang/Basic/LLVM.h"
 #include "clang/Basic/SourceLocation.h"
 #include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/PointerIntPair.h"
 #include "llvm/Support/Compiler.h"
 #include "llvm/Support/ErrorHandling.h"
 #include 
@@ -31,6 +33,7 @@ namespace llvm {
 namespace clang {
   class ASTContext;
   class Attr;
+  class CapturedDecl;
   class Decl;
   class Expr;
   class IdentifierInfo;
@@ -39,6 +42,7 @@ namespace clang {
   class PrinterHelper;
   struct PrintingPolicy;
   class QualType;
+  class RecordDecl;
   class SourceManager;
   class StringLiteral;
   class SwitchStmt;
@@ -1384,7 +1388,6 @@ class AsmStmt : public Stmt {
   unsigned NumInputs;
   unsigned NumClobbers;
 
-  IdentifierInfo **Names;
   Stmt **Exprs;
 
   AsmStmt(StmtClass SC, SourceLocation asmloc, bool issimple, bool isvolatile,
@@ -1392,10 +1395,12 @@ class AsmStmt : public Stmt {
     Stmt (SC), AsmLoc(asmloc), IsSimple(issimple), IsVolatile(isvolatile),
     NumOutputs(numoutputs), NumInputs(numinputs), NumClobbers(numclobbers) { }
 
+  friend class ASTStmtReader;
+
 public:
   /// \brief Build an empty inline-assembly statement.
   explicit AsmStmt(StmtClass SC, EmptyShell Empty) :
-    Stmt(SC, Empty), Names(0), Exprs(0) { }
+    Stmt(SC, Empty), Exprs(0) { }
 
   SourceLocation getAsmLoc() const { return AsmLoc; }
   void setAsmLoc(SourceLocation L) { AsmLoc = L; }
@@ -1418,17 +1423,6 @@ class AsmStmt : public Stmt {
 
   unsigned getNumOutputs() const { return NumOutputs; }
 
-  IdentifierInfo *getOutputIdentifier(unsigned i) const {
-    return Names[i];
-  }
-
-  StringRef getOutputName(unsigned i) const {
-    if (IdentifierInfo *II = getOutputIdentifier(i))
-      return II->getName();
-
-    return StringRef();
-  }
-
   /// getOutputConstraint - Return the constraint string for the specified
   /// output operand.  All output constraints are known to be non-empty (either
   /// '=' or '+').
@@ -1451,17 +1445,6 @@ class AsmStmt : public Stmt {
 
   unsigned getNumInputs() const { return NumInputs; }
 
-  IdentifierInfo *getInputIdentifier(unsigned i) const {
-    return Names[i + NumOutputs];
-  }
-
-  StringRef getInputName(unsigned i) const {
-    if (IdentifierInfo *II = getInputIdentifier(i))
-      return II->getName();
-
-    return StringRef();
-  }
-
   /// getInputConstraint - Return the specified input constraint.  Unlike output
   /// constraints, these can be empty.
   StringRef getInputConstraint(unsigned i) const;
@@ -1532,6 +1515,9 @@ class GCCAsmStmt : public AsmStmt {
   // FIXME: If we wanted to, we could allocate all of these in one big array.
   StringLiteral **Constraints;
   StringLiteral **Clobbers;
+  IdentifierInfo **Names;
+
+  friend class ASTStmtReader;
 
 public:
   GCCAsmStmt(ASTContext &C, SourceLocation asmloc, bool issimple,
@@ -1542,7 +1528,7 @@ class GCCAsmStmt : public AsmStmt {
 
   /// \brief Build an empty inline-assembly statement.
   explicit GCCAsmStmt(EmptyShell Empty) : AsmStmt(GCCAsmStmtClass, Empty),
-    Constraints(0), Clobbers(0) { }
+    Constraints(0), Clobbers(0), Names(0) { }
 
   SourceLocation getRParenLoc() const { return RParenLoc; }
   void setRParenLoc(SourceLocation L) { RParenLoc = L; }
@@ -1607,6 +1593,17 @@ class GCCAsmStmt : public AsmStmt {
 
   //===--- Output operands ---===//
 
+  IdentifierInfo *getOutputIdentifier(unsigned i) const {
+    return Names[i];
+  }
+
+  StringRef getOutputName(unsigned i) const {
+    if (IdentifierInfo *II = getOutputIdentifier(i))
+      return II->getName();
+
+    return StringRef();
+  }
+
   StringRef getOutputConstraint(unsigned i) const;
 
   const StringLiteral *getOutputConstraintLiteral(unsigned i) const {
@@ -1624,6 +1621,17 @@ class GCCAsmStmt : public AsmStmt {
 
   //===--- Input operands ---===//
 
+  IdentifierInfo *getInputIdentifier(unsigned i) const {
+    return Names[i + NumOutputs];
+  }
+
+  StringRef getInputName(unsigned i) const {
+    if (IdentifierInfo *II = getInputIdentifier(i))
+      return II->getName();
+
+    return StringRef();
+  }
+
   StringRef getInputConstraint(unsigned i) const;
 
   const StringLiteral *getInputConstraintLiteral(unsigned i) const {
@@ -1640,6 +1648,7 @@ class GCCAsmStmt : public AsmStmt {
     return const_cast(this)->getInputExpr(i);
   }
 
+private:
   void setOutputsAndInputsAndClobbers(ASTContext &C,
                                       IdentifierInfo **Names,
                                       StringLiteral **Constraints,
@@ -1648,6 +1657,7 @@ class GCCAsmStmt : public AsmStmt {
                                       unsigned NumInputs,
                                       StringLiteral **Clobbers,
                                       unsigned NumClobbers);
+public:
 
   //===--- Other ---===//
 
@@ -1674,7 +1684,7 @@ class GCCAsmStmt : public AsmStmt {
 ///
 class MSAsmStmt : public AsmStmt {
   SourceLocation LBraceLoc, EndLoc;
-  std::string AsmStr;
+  StringRef AsmStr;
 
   unsigned NumAsmToks;
 
@@ -1682,11 +1692,13 @@ class MSAsmStmt : public AsmStmt {
   StringRef *Constraints;
   StringRef *Clobbers;
 
+  friend class ASTStmtReader;
+
 public:
   MSAsmStmt(ASTContext &C, SourceLocation asmloc, SourceLocation lbraceloc,
             bool issimple, bool isvolatile, ArrayRef asmtoks,
             unsigned numoutputs, unsigned numinputs,
-            ArrayRef names, ArrayRef constraints,
+            ArrayRef constraints,
             ArrayRef exprs, StringRef asmstr,
             ArrayRef clobbers, SourceLocation endloc);
 
@@ -1705,10 +1717,7 @@ class MSAsmStmt : public AsmStmt {
   Token *getAsmToks() { return AsmToks; }
 
   //===--- Asm String Analysis ---===//
-
-  const std::string *getAsmString() const { return &AsmStr; }
-  std::string *getAsmString() { return &AsmStr; }
-  void setAsmString(StringRef &E) { AsmStr = E.str(); }
+  StringRef getAsmString() const { return AsmStr; }
 
   /// Assemble final IR asm string.
   std::string generateAsmString(ASTContext &C) const;
@@ -1716,6 +1725,7 @@ class MSAsmStmt : public AsmStmt {
   //===--- Output operands ---===//
 
   StringRef getOutputConstraint(unsigned i) const {
+    assert(i < NumOutputs);
     return Constraints[i];
   }
 
@@ -1728,6 +1738,7 @@ class MSAsmStmt : public AsmStmt {
   //===--- Input operands ---===//
 
   StringRef getInputConstraint(unsigned i) const {
+    assert(i < NumInputs);
     return Constraints[i + NumOutputs];
   }
 
@@ -1740,7 +1751,27 @@ class MSAsmStmt : public AsmStmt {
 
   //===--- Other ---===//
 
-  StringRef getClobber(unsigned i) const { return Clobbers[i]; }
+  ArrayRef getAllConstraints() const {
+    return ArrayRef(Constraints, NumInputs + NumOutputs);
+  }
+  ArrayRef getClobbers() const {
+    return ArrayRef(Clobbers, NumClobbers);
+  }
+  ArrayRef getAllExprs() const {
+    return ArrayRef(reinterpret_cast(Exprs),
+                           NumInputs + NumOutputs);
+  }
+
+  StringRef getClobber(unsigned i) const { return getClobbers()[i]; }
+
+private:
+  void initialize(ASTContext &C,
+                  StringRef AsmString,
+                  ArrayRef AsmToks,
+                  ArrayRef Constraints,
+                  ArrayRef Exprs,
+                  ArrayRef Clobbers);
+public:
 
   SourceLocation getLocStart() const LLVM_READONLY { return AsmLoc; }
   SourceLocation getLocEnd() const LLVM_READONLY { return EndLoc; }
@@ -1882,6 +1913,198 @@ class SEHTryStmt : public Stmt {
   }
 };
 
+/// \brief This captures a statement into a function. For example, the following
+/// pragma annotated compound statement can be represented as a CapturedStmt,
+/// and this compound statement is the body of an anonymous outlined function.
+/// @code
+/// #pragma omp parallel
+/// {
+///   compute();
+/// }
+/// @endcode
+class CapturedStmt : public Stmt {
+public:
+  /// \brief The different capture forms: by 'this' or by reference, etc.
+  enum VariableCaptureKind {
+    VCK_This,
+    VCK_ByRef
+  };
+
+  /// \brief Describes the capture of either a variable or 'this'.
+  class Capture {
+    llvm::PointerIntPair VarAndKind;
+    SourceLocation Loc;
+
+  public:
+    /// \brief Create a new capture.
+    ///
+    /// \param Loc The source location associated with this capture.
+    ///
+    /// \param Kind The kind of capture (this, ByRef, ...).
+    ///
+    /// \param Var The variable being captured, or null if capturing this.
+    ///
+    Capture(SourceLocation Loc, VariableCaptureKind Kind, VarDecl *Var = 0)
+      : VarAndKind(Var, Kind), Loc(Loc) {
+      switch (Kind) {
+      case VCK_This:
+        assert(Var == 0 && "'this' capture cannot have a variable!");
+        break;
+      case VCK_ByRef:
+        assert(Var && "capturing by reference must have a variable!");
+        break;
+      }
+    }
+
+    /// \brief Determine the kind of capture.
+    VariableCaptureKind getCaptureKind() const { return VarAndKind.getInt(); }
+
+    /// \brief Retrieve the source location at which the variable or 'this' was
+    /// first used.
+    SourceLocation getLocation() const { return Loc; }
+
+    /// \brief Determine whether this capture handles the C++ 'this' pointer.
+    bool capturesThis() const { return getCaptureKind() == VCK_This; }
+
+    /// \brief Determine whether this capture handles a variable.
+    bool capturesVariable() const { return getCaptureKind() != VCK_This; }
+
+    /// \brief Retrieve the declaration of the variable being captured.
+    ///
+    /// This operation is only valid if this capture does not capture 'this'.
+    VarDecl *getCapturedVar() const {
+      assert(!capturesThis() && "No variable available for 'this' capture");
+      return VarAndKind.getPointer();
+    }
+    friend class ASTStmtReader;
+  };
+
+private:
+  /// \brief The number of variable captured, including 'this'.
+  unsigned NumCaptures;
+
+  /// \brief The pointer part is the implicit the outlined function and the 
+  /// int part is the captured region kind, 'CR_Default' etc.
+  llvm::PointerIntPair CapDeclAndKind;
+
+  /// \brief The record for captured variables, a RecordDecl or CXXRecordDecl.
+  RecordDecl *TheRecordDecl;
+
+  /// \brief Construct a captured statement.
+  CapturedStmt(Stmt *S, CapturedRegionKind Kind, ArrayRef Captures,
+               ArrayRef CaptureInits, CapturedDecl *CD, RecordDecl *RD);
+
+  /// \brief Construct an empty captured statement.
+  CapturedStmt(EmptyShell Empty, unsigned NumCaptures);
+
+  Stmt **getStoredStmts() const {
+    return reinterpret_cast(const_cast(this) + 1);
+  }
+
+  Capture *getStoredCaptures() const;
+
+  void setCapturedStmt(Stmt *S) { getStoredStmts()[NumCaptures] = S; }
+
+public:
+  static CapturedStmt *Create(ASTContext &Context, Stmt *S,
+                              CapturedRegionKind Kind,
+                              ArrayRef Captures,
+                              ArrayRef CaptureInits,
+                              CapturedDecl *CD, RecordDecl *RD);
+
+  static CapturedStmt *CreateDeserialized(ASTContext &Context,
+                                          unsigned NumCaptures);
+
+  /// \brief Retrieve the statement being captured.
+  Stmt *getCapturedStmt() { return getStoredStmts()[NumCaptures]; }
+  const Stmt *getCapturedStmt() const {
+    return const_cast(this)->getCapturedStmt();
+  }
+
+  /// \brief Retrieve the outlined function declaration.
+  CapturedDecl *getCapturedDecl() { return CapDeclAndKind.getPointer(); }
+  const CapturedDecl *getCapturedDecl() const {
+    return const_cast(this)->getCapturedDecl();
+  }
+
+  /// \brief Set the outlined function declaration.
+  void setCapturedDecl(CapturedDecl *D) {
+    assert(D && "null CapturedDecl");
+    CapDeclAndKind.setPointer(D);
+  }
+
+  /// \brief Retrieve the captured region kind.
+  CapturedRegionKind getCapturedRegionKind() const {
+    return CapDeclAndKind.getInt();
+  }
+
+  /// \brief Set the captured region kind.
+  void setCapturedRegionKind(CapturedRegionKind Kind) {
+    CapDeclAndKind.setInt(Kind);
+  }
+
+  /// \brief Retrieve the record declaration for captured variables.
+  const RecordDecl *getCapturedRecordDecl() const { return TheRecordDecl; }
+
+  /// \brief Set the record declaration for captured variables.
+  void setCapturedRecordDecl(RecordDecl *D) {
+    assert(D && "null RecordDecl");
+    TheRecordDecl = D;
+  }
+
+  /// \brief True if this variable has been captured.
+  bool capturesVariable(const VarDecl *Var) const;
+
+  /// \brief An iterator that walks over the captures.
+  typedef Capture *capture_iterator;
+  typedef const Capture *const_capture_iterator;
+
+  /// \brief Retrieve an iterator pointing to the first capture.
+  capture_iterator capture_begin() { return getStoredCaptures(); }
+  const_capture_iterator capture_begin() const { return getStoredCaptures(); }
+
+  /// \brief Retrieve an iterator pointing past the end of the sequence of
+  /// captures.
+  capture_iterator capture_end() const {
+    return getStoredCaptures() + NumCaptures;
+  }
+
+  /// \brief Retrieve the number of captures, including 'this'.
+  unsigned capture_size() const { return NumCaptures; }
+
+  /// \brief Iterator that walks over the capture initialization arguments.
+  typedef Expr **capture_init_iterator;
+
+  /// \brief Retrieve the first initialization argument.
+  capture_init_iterator capture_init_begin() const {
+    return reinterpret_cast(getStoredStmts());
+  }
+
+  /// \brief Retrieve the iterator pointing one past the last initialization
+  /// argument.
+  capture_init_iterator capture_init_end() const {
+    return capture_init_begin() + NumCaptures;
+  }
+
+  SourceLocation getLocStart() const LLVM_READONLY {
+    return getCapturedStmt()->getLocStart();
+  }
+  SourceLocation getLocEnd() const LLVM_READONLY {
+    return getCapturedStmt()->getLocEnd();
+  }
+  SourceRange getSourceRange() const LLVM_READONLY {
+    return getCapturedStmt()->getSourceRange();
+  }
+
+  static bool classof(const Stmt *T) {
+    return T->getStmtClass() == CapturedStmtClass;
+  }
+
+  child_range children();
+
+  friend class ASTStmtReader;
+};
+
 }  // end namespace clang
 
 #endif
diff --git a/include/clang/AST/Type.h b/include/clang/AST/Type.h
index 23fa3e87656..39f10d3393b 100644
--- a/include/clang/AST/Type.h
+++ b/include/clang/AST/Type.h
@@ -1326,10 +1326,20 @@ class Type : public ExtQualsTypeCommonBase {
     unsigned AttrKind : 32 - NumTypeBits;
   };
 
+  class AutoTypeBitfields {
+    friend class AutoType;
+
+    unsigned : NumTypeBits;
+
+    /// Was this placeholder type spelled as 'decltype(auto)'?
+    unsigned IsDecltypeAuto : 1;
+  };
+
   union {
     TypeBitfields TypeBits;
     ArrayTypeBitfields ArrayTypeBits;
     AttributedTypeBitfields AttributedTypeBits;
+    AutoTypeBitfields AutoTypeBits;
     BuiltinTypeBitfields BuiltinTypeBits;
     FunctionTypeBitfields FunctionTypeBits;
     ObjCObjectTypeBitfields ObjCObjectTypeBits;
@@ -1443,8 +1453,8 @@ class Type : public ExtQualsTypeCommonBase {
   }
 
   /// isLiteralType - Return true if this is a literal type
-  /// (C++0x [basic.types]p10)
-  bool isLiteralType() const;
+  /// (C++11 [basic.types]p10)
+  bool isLiteralType(ASTContext &Ctx) const;
 
   /// \brief Test if this type is a standard-layout type.
   /// (C++0x [basic.type]p9)
@@ -1609,6 +1619,10 @@ class Type : public ExtQualsTypeCommonBase {
     return TypeBits.InstantiationDependent;
   }
 
+  /// \brief Determine whether this type is an undeduced type, meaning that
+  /// it somehow involves a C++11 'auto' type which has not yet been deduced.
+  bool isUndeducedType() const;
+
   /// \brief Whether this type is a variably-modified type (C99 6.7.5).
   bool isVariablyModifiedType() const { return TypeBits.VariablyModified; }
 
@@ -2092,14 +2106,6 @@ class RValueReferenceType : public ReferenceType {
   }
 };
 
-/// The inheritance model to use for this member pointer.
-enum MSInheritanceModel {
-  MSIM_Single,
-  MSIM_Multiple,
-  MSIM_Virtual,
-  MSIM_Unspecified
-};
-
 /// MemberPointerType - C++ 8.3.3 - Pointers to members
 ///
 class MemberPointerType : public Type, public llvm::FoldingSetNode {
@@ -2135,10 +2141,6 @@ class MemberPointerType : public Type, public llvm::FoldingSetNode {
     return !PointeeType->isFunctionProtoType();
   }
 
-  /// Returns the number of pointer and integer slots used to represent this
-  /// member pointer in the MS C++ ABI.
-  std::pair getMSMemberPointerSlots() const;
-
   const Type *getClass() const { return Class; }
 
   bool isSugared() const { return false; }
@@ -3554,41 +3556,48 @@ class SubstTemplateTypeParmPackType : public Type, public llvm::FoldingSetNode {
   }
 };
 
-/// \brief Represents a C++0x auto type.
+/// \brief Represents a C++11 auto or C++1y decltype(auto) type.
 ///
-/// These types are usually a placeholder for a deduced type. However, within
-/// templates and before the initializer is attached, there is no deduced type
-/// and an auto type is type-dependent and canonical.
+/// These types are usually a placeholder for a deduced type. However, before
+/// the initializer is attached, or if the initializer is type-dependent, there
+/// is no deduced type and an auto type is canonical. In the latter case, it is
+/// also a dependent type.
 class AutoType : public Type, public llvm::FoldingSetNode {
-  AutoType(QualType DeducedType)
+  AutoType(QualType DeducedType, bool IsDecltypeAuto, bool IsDependent)
     : Type(Auto, DeducedType.isNull() ? QualType(this, 0) : DeducedType,
-           /*Dependent=*/DeducedType.isNull(),
-           /*InstantiationDependent=*/DeducedType.isNull(),
+           /*Dependent=*/IsDependent, /*InstantiationDependent=*/IsDependent,
            /*VariablyModified=*/false, /*ContainsParameterPack=*/false) {
-    assert((DeducedType.isNull() || !DeducedType->isDependentType()) &&
-           "deduced a dependent type for auto");
+    assert((DeducedType.isNull() || !IsDependent) &&
+           "auto deduced to dependent type");
+    AutoTypeBits.IsDecltypeAuto = IsDecltypeAuto;
   }
 
   friend class ASTContext;  // ASTContext creates these
 
 public:
-  bool isSugared() const { return isDeduced(); }
+  bool isDecltypeAuto() const { return AutoTypeBits.IsDecltypeAuto; }
+
+  bool isSugared() const { return !isCanonicalUnqualified(); }
   QualType desugar() const { return getCanonicalTypeInternal(); }
 
+  /// \brief Get the type deduced for this auto type, or null if it's either
+  /// not been deduced or was deduced to a dependent type.
   QualType getDeducedType() const {
-    return isDeduced() ? getCanonicalTypeInternal() : QualType();
+    return !isCanonicalUnqualified() ? getCanonicalTypeInternal() : QualType();
   }
   bool isDeduced() const {
-    return !isDependentType();
+    return !isCanonicalUnqualified() || isDependentType();
   }
 
   void Profile(llvm::FoldingSetNodeID &ID) {
-    Profile(ID, getDeducedType());
+    Profile(ID, getDeducedType(), isDecltypeAuto(), isDependentType());
   }
 
-  static void Profile(llvm::FoldingSetNodeID &ID,
-                      QualType Deduced) {
+  static void Profile(llvm::FoldingSetNodeID &ID, QualType Deduced,
+                      bool IsDecltypeAuto, bool IsDependent) {
     ID.AddPointer(Deduced.getAsOpaquePtr());
+    ID.AddBoolean(IsDecltypeAuto);
+    ID.AddBoolean(IsDependent);
   }
 
   static bool classof(const Type *T) {
@@ -4638,7 +4647,7 @@ inline QualType QualType::getUnqualifiedType() const {
 
   return QualType(getSplitUnqualifiedTypeImpl(*this).Ty, 0);
 }
-
+  
 inline SplitQualType QualType::getSplitUnqualifiedType() const {
   if (!getTypePtr()->getCanonicalTypeInternal().hasLocalQualifiers())
     return split();
@@ -4670,7 +4679,7 @@ inline void QualType::removeLocalCVRQualifiers(unsigned Mask) {
 inline unsigned QualType::getAddressSpace() const {
   return getQualifiers().getAddressSpace();
 }
-
+  
 /// getObjCGCAttr - Return the gc attribute of this type.
 inline Qualifiers::GC QualType::getObjCGCAttr() const {
   return getQualifiers().getObjCGCAttr();
@@ -5031,6 +5040,11 @@ inline bool Type::isBooleanType() const {
   return false;
 }
 
+inline bool Type::isUndeducedType() const {
+  const AutoType *AT = getContainedAutoType();
+  return AT && !AT->isDeduced();
+}
+
 /// \brief Determines whether this is a type for which one can define
 /// an overloaded operator.
 inline bool Type::isOverloadableType() const {
diff --git a/include/clang/AST/TypeNodes.def b/include/clang/AST/TypeNodes.def
index d5c485f8a3b..840e07d94a0 100644
--- a/include/clang/AST/TypeNodes.def
+++ b/include/clang/AST/TypeNodes.def
@@ -94,7 +94,7 @@ DEPENDENT_TYPE(TemplateTypeParm, Type)
 NON_CANONICAL_TYPE(SubstTemplateTypeParm, Type)
 DEPENDENT_TYPE(SubstTemplateTypeParmPack, Type)
 NON_CANONICAL_UNLESS_DEPENDENT_TYPE(TemplateSpecialization, Type)
-NON_CANONICAL_UNLESS_DEPENDENT_TYPE(Auto, Type)
+TYPE(Auto, Type)
 DEPENDENT_TYPE(InjectedClassName, Type)
 DEPENDENT_TYPE(DependentName, Type)
 DEPENDENT_TYPE(DependentTemplateSpecialization, Type)
diff --git a/include/clang/ASTMatchers/ASTMatchers.h b/include/clang/ASTMatchers/ASTMatchers.h
index f10addcb7af..ab62dd0c3e8 100644
--- a/include/clang/ASTMatchers/ASTMatchers.h
+++ b/include/clang/ASTMatchers/ASTMatchers.h
@@ -129,7 +129,8 @@ typedef internal::Matcher NestedNameSpecifierLocMatcher;
 /// \endcode
 ///
 /// Usable as: Any Matcher
-inline internal::PolymorphicMatcherWithParam0 anything() {
+inline internal::PolymorphicMatcherWithParam0
+anything() {
   return internal::PolymorphicMatcherWithParam0();
 }
 
@@ -157,6 +158,17 @@ const internal::VariadicAllOfMatcher decl;
 /// \endcode
 const internal::VariadicDynCastAllOfMatcher namedDecl;
 
+/// \brief Matches a declaration of a namespace.
+///
+/// Given
+/// \code
+///   namespace {}
+///   namespace test {}
+/// \endcode
+/// namespaceDecl()
+///   matches "namespace {}" and "namespace test {}"
+const internal::VariadicDynCastAllOfMatcher namespaceDecl;
+
 /// \brief Matches C++ class declarations.
 ///
 /// Example matches \c X, \c Z
@@ -2514,6 +2526,38 @@ AST_MATCHER_P(CXXMethodDecl, ofClass,
           InnerMatcher.matches(*Parent, Finder, Builder));
 }
 
+/// \brief Matches if the given method declaration is virtual.
+///
+/// Given
+/// \code
+///   class A {
+///    public:
+///     virtual void x();
+///   };
+/// \endcode
+///   matches A::x
+AST_MATCHER(CXXMethodDecl, isVirtual) {
+  return Node.isVirtual();
+}
+
+/// \brief Matches if the given method declaration overrides another method.
+///
+/// Given
+/// \code
+///   class A {
+///    public:
+///     virtual void x();
+///   };
+///   class B : public A {
+///    public:
+///     virtual void x();
+///   };
+/// \endcode
+///   matches B::x
+AST_MATCHER(CXXMethodDecl, isOverride) {
+  return Node.size_overridden_methods() > 0;
+}
+
 /// \brief Matches member expressions that are called with '->' as opposed
 /// to '.'.
 ///
diff --git a/include/clang/Basic/Attr.td b/include/clang/Basic/Attr.td
index 37aa332b812..441a79a23b2 100644
--- a/include/clang/Basic/Attr.td
+++ b/include/clang/Basic/Attr.td
@@ -928,6 +928,10 @@ def TypeTagForDatatype : InheritableAttr {
 
 // Microsoft-related attributes
 
+def MsProperty : Attr {
+  let Spellings = [Declspec<"property">];
+}
+
 def MsStruct : InheritableAttr {
   let Spellings = [Declspec<"ms_struct">];
 }
diff --git a/include/clang/Basic/BuiltinsAArch64.def b/include/clang/Basic/BuiltinsAArch64.def
new file mode 100644
index 00000000000..9e9f6d0875d
--- /dev/null
+++ b/include/clang/Basic/BuiltinsAArch64.def
@@ -0,0 +1,18 @@
+//===-- BuiltinsAArch64.def - AArch64 Builtin function database -*- 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 the AArch64-specific builtin function database.  Users of
+// this file must define the BUILTIN macro to make use of this information.
+//
+//===----------------------------------------------------------------------===//
+
+// The format of this database matches clang/Basic/Builtins.def.
+
+// In libgcc
+BUILTIN(__clear_cache, "vv*v*", "")
diff --git a/include/clang/Basic/CapturedStmt.h b/include/clang/Basic/CapturedStmt.h
new file mode 100644
index 00000000000..484bbb1feee
--- /dev/null
+++ b/include/clang/Basic/CapturedStmt.h
@@ -0,0 +1,23 @@
+//===--- CapturedStmt.h - Types for CapturedStmts ---------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+
+#ifndef LLVM_CLANG_BASIC_CAPTUREDSTMT_H
+#define LLVM_CLANG_BASIC_CAPTUREDSTMT_H
+
+namespace clang {
+
+/// \brief The different kinds of captured statement.
+enum CapturedRegionKind {
+  CR_Default
+};
+
+} // end namespace clang
+
+#endif // LLVM_CLANG_BASIC_CAPTUREDSTMT_H
diff --git a/include/clang/Basic/CommentOptions.h b/include/clang/Basic/CommentOptions.h
index 79b9a6b8835..7991875838a 100644
--- a/include/clang/Basic/CommentOptions.h
+++ b/include/clang/Basic/CommentOptions.h
@@ -27,6 +27,11 @@ struct CommentOptions {
   /// \brief Command names to treat as block commands in comments.
   /// Should not include the leading backslash.
   BlockCommandNamesTy BlockCommandNames;
+
+  /// \brief Treat ordinary comments as documentation comments.
+  bool ParseAllComments;
+
+  CommentOptions() : ParseAllComments(false) { }
 };
 
 }  // end namespace clang
diff --git a/include/clang/Basic/DeclNodes.td b/include/clang/Basic/DeclNodes.td
index 45742bc6655..ad2afa7a57c 100644
--- a/include/clang/Basic/DeclNodes.td
+++ b/include/clang/Basic/DeclNodes.td
@@ -34,14 +34,15 @@ def Named : Decl<1>;
     def UnresolvedUsingValue : DDecl;
     def IndirectField : DDecl;
     def Declarator : DDecl;
+      def Field : DDecl;
+        def ObjCIvar : DDecl;
+        def ObjCAtDefsField : DDecl;
+      def MSProperty : DDecl;
       def Function : DDecl, DeclContext;
         def CXXMethod : DDecl;
           def CXXConstructor : DDecl;
           def CXXDestructor : DDecl;
           def CXXConversion : DDecl;
-      def Field : DDecl;
-        def ObjCIvar : DDecl;
-        def ObjCAtDefsField : DDecl;
       def Var : DDecl;
         def ImplicitParam : DDecl;
         def ParmVar : DDecl;
@@ -72,6 +73,7 @@ def Friend : Decl;
 def FriendTemplate : Decl;
 def StaticAssert : Decl;
 def Block : Decl, DeclContext;
+def Captured : Decl, DeclContext;
 def ClassScopeFunctionSpecialization : Decl;
 def Import : Decl;
 def OMPThreadPrivate : Decl;
diff --git a/include/clang/Basic/Diagnostic.h b/include/clang/Basic/Diagnostic.h
index 032763672b4..3e125944a36 100644
--- a/include/clang/Basic/Diagnostic.h
+++ b/include/clang/Basic/Diagnostic.h
@@ -175,7 +175,6 @@ class DiagnosticsEngine : public RefCountedBase {
   bool SuppressAllDiagnostics;   // Suppress all diagnostics.
   bool ElideType;                // Elide common types of templates.
   bool PrintTemplateTree;        // Print a tree when comparing templates.
-  bool WarnOnSpellCheck;         // Emit warning when spellcheck is initiated.
   bool ShowColors;               // Color printing is enabled.
   OverloadsShown ShowOverloads;  // Which overload candidates to show.
   unsigned ErrorLimit;           // Cap of # errors emitted, 0 -> no limit.
@@ -467,10 +466,6 @@ class DiagnosticsEngine : public RefCountedBase {
   /// tree format.
   void setPrintTemplateTree(bool Val = false) { PrintTemplateTree = Val; }
   bool getPrintTemplateTree() { return PrintTemplateTree; }
-
-  /// \brief Warn when spellchecking is initated, for testing.
-  void setWarnOnSpellCheck(bool Val = false) { WarnOnSpellCheck = Val; }
-  bool getWarnOnSpellCheck() { return WarnOnSpellCheck; }
  
   /// \brief Set color printing, so the type diffing will inject color markers
   /// into the output.
@@ -1301,10 +1296,6 @@ class DiagnosticConsumer {
   /// warnings and errors.
   virtual void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
                                 const Diagnostic &Info);
-  
-  /// \brief Clone the diagnostic consumer, producing an equivalent consumer
-  /// that can be used in a different context.
-  virtual DiagnosticConsumer *clone(DiagnosticsEngine &Diags) const = 0;
 };
 
 /// \brief A diagnostic client that ignores all diagnostics.
@@ -1314,9 +1305,24 @@ class IgnoringDiagConsumer : public DiagnosticConsumer {
                         const Diagnostic &Info) {
     // Just ignore it.
   }
-  DiagnosticConsumer *clone(DiagnosticsEngine &Diags) const {
-    return new IgnoringDiagConsumer();
-  }
+};
+
+/// \brief Diagnostic consumer that forwards diagnostics along to an
+/// existing, already-initialized diagnostic consumer.
+///
+class ForwardingDiagnosticConsumer : public DiagnosticConsumer {
+  DiagnosticConsumer &Target;
+
+public:
+  ForwardingDiagnosticConsumer(DiagnosticConsumer &Target) : Target(Target) {}
+
+  virtual ~ForwardingDiagnosticConsumer();
+
+  virtual void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
+                                const Diagnostic &Info);
+  virtual void clear();
+
+  virtual bool IncludeInDiagnosticCounts() const;
 };
 
 // Struct used for sending info about how a type should be printed.
diff --git a/include/clang/Basic/DiagnosticASTKinds.td b/include/clang/Basic/DiagnosticASTKinds.td
index 9be32af9ecc..c69f85f18d3 100644
--- a/include/clang/Basic/DiagnosticASTKinds.td
+++ b/include/clang/Basic/DiagnosticASTKinds.td
@@ -12,7 +12,7 @@ let Component = "AST" in {
 // Constant expression diagnostics. These (and their users) belong in Sema.
 def note_expr_divide_by_zero : Note<"division by zero">;
 def note_constexpr_invalid_cast : Note<
-  "%select{reinterpret_cast|dynamic_cast|cast which performs the conversions of"
+  "%select{reinterpret_cast|dynamic_cast|cast that performs the conversions of"
   " a reinterpret_cast|cast from %1}0 is not allowed in a constant expression">;
 def note_constexpr_invalid_downcast : Note<
   "cannot cast object of dynamic type %0 to type %1">;
@@ -26,6 +26,8 @@ def note_constexpr_lshift_discards : Note<"signed left shift discards bits">;
 def note_constexpr_invalid_function : Note<
   "%select{non-constexpr|undefined}0 %select{function|constructor}1 %2 cannot "
   "be used in a constant expression">;
+def note_constexpr_no_return : Note<
+  "control reached end of constexpr function">;
 def note_constexpr_virtual_call : Note<
   "cannot evaluate virtual function call in a constant expression">;
 def note_constexpr_virtual_base : Note<
@@ -82,11 +84,20 @@ def note_constexpr_depth_limit_exceeded : Note<
 def note_constexpr_call_limit_exceeded : Note<
   "constexpr evaluation hit maximum call limit">;
 def note_constexpr_lifetime_ended : Note<
-  "read of %select{temporary|variable}0 whose lifetime has ended">;
-def note_constexpr_ltor_volatile_type : Note<
-  "read of volatile-qualified type %0 is not allowed in a constant expression">;
-def note_constexpr_ltor_volatile_obj : Note<
-  "read of volatile %select{temporary|object %1|member %1}0 is not allowed in "
+  "%select{read of|assignment to|increment of|decrement of}0 "
+  "%select{temporary|variable}1 whose lifetime has ended">;
+def note_constexpr_access_uninit : Note<
+  "%select{read of|assignment to|increment of|decrement of}0 "
+  "object outside its lifetime is not allowed in a constant expression">;
+def note_constexpr_modify_const_type : Note<
+  "modification of object of const-qualified type %0 is not allowed "
+  "in a constant expression">;
+def note_constexpr_access_volatile_type : Note<
+  "%select{read of|assignment to|increment of|decrement of}0 "
+  "volatile-qualified type %1 is not allowed in a constant expression">;
+def note_constexpr_access_volatile_obj : Note<
+  "%select{read of|assignment to|increment of|decrement of}0 volatile "
+  "%select{temporary|object %2|member %2}1 is not allowed in "
   "a constant expression">;
 def note_constexpr_ltor_mutable : Note<
   "read of mutable member %0 is not allowed in a constant expression">;
@@ -94,14 +105,19 @@ def note_constexpr_ltor_non_const_int : Note<
   "read of non-const variable %0 is not allowed in a constant expression">;
 def note_constexpr_ltor_non_constexpr : Note<
   "read of non-constexpr variable %0 is not allowed in a constant expression">;
-def note_constexpr_read_past_end : Note<
-  "read of dereferenced one-past-the-end pointer is not allowed in a "
-  "constant expression">;
-def note_constexpr_read_inactive_union_member : Note<
-  "read of member %0 of union with %select{active member %2|no active member}1 "
+def note_constexpr_access_null : Note<
+  "%select{read of|assignment to|increment of|decrement of}0 "
+  "dereferenced null pointer is not allowed in a constant expression">;
+def note_constexpr_access_past_end : Note<
+  "%select{read of|assignment to|increment of|decrement of}0 "
+  "dereferenced one-past-the-end pointer is not allowed in a constant expression">;
+def note_constexpr_access_inactive_union_member : Note<
+  "%select{read of|assignment to|increment of|decrement of}0 "
+  "member %1 of union with %select{active member %3|no active member}2 "
   "is not allowed in a constant expression">;
-def note_constexpr_read_uninit : Note<
-  "read of uninitialized object is not allowed in a constant expression">;
+def note_constexpr_modify_global : Note<
+  "a constant expression cannot modify an object that is visible outside "
+  "that expression">;
 def note_constexpr_calls_suppressed : Note<
   "(skipping %0 call%s0 in backtrace; use -fconstexpr-backtrace-limit=0 to "
   "see all)">;
diff --git a/include/clang/Basic/DiagnosticCommentKinds.td b/include/clang/Basic/DiagnosticCommentKinds.td
index 3880e0e5b4f..c913e31ebad 100644
--- a/include/clang/Basic/DiagnosticCommentKinds.td
+++ b/include/clang/Basic/DiagnosticCommentKinds.td
@@ -156,5 +156,9 @@ def warn_verbatim_block_end_without_start : Warning<
   "'%select{\\|@}0%1' command does not terminate a verbatim text block">,
   InGroup, DefaultIgnore;
 
+def warn_unknown_comment_command_name : Warning<
+  "unknown command tag name">,
+  InGroup, DefaultIgnore;
+
 } // end of documentation issue category
 } // end of AST component
diff --git a/include/clang/Basic/DiagnosticDriverKinds.td b/include/clang/Basic/DiagnosticDriverKinds.td
index 15b89486606..db457b15369 100644
--- a/include/clang/Basic/DiagnosticDriverKinds.td
+++ b/include/clang/Basic/DiagnosticDriverKinds.td
@@ -105,8 +105,6 @@ def err_arc_unsupported_on_toolchain : Error< // feel free to generalize this
   "-fobjc-arc is not supported on versions of OS X prior to 10.6">;
 def err_drv_mg_requires_m_or_mm : Error<
   "option '-MG' requires '-M' or '-MM'">;
-def err_drv_asan_android_requires_pie : Error<
-  "AddressSanitizer on Android requires '-pie'">;
 def err_drv_unknown_objc_runtime : Error<
   "unknown or ill-formed Objective-C runtime '%0'">;
 
diff --git a/include/clang/Basic/DiagnosticFrontendKinds.td b/include/clang/Basic/DiagnosticFrontendKinds.td
index 111622e0fe9..f05fb9be82c 100644
--- a/include/clang/Basic/DiagnosticFrontendKinds.td
+++ b/include/clang/Basic/DiagnosticFrontendKinds.td
@@ -67,6 +67,8 @@ def warn_fe_serialized_diag_failure : Warning<
 
 def err_verify_missing_line : Error<
     "missing or invalid line number following '@' in expected %0">;
+def err_verify_missing_file : Error<
+    "file '%0' could not be located in expected %1">;
 def err_verify_invalid_range : Error<
     "invalid range following '-' in expected %0">;
 def err_verify_missing_start : Error<
diff --git a/include/clang/Basic/DiagnosticGroups.td b/include/clang/Basic/DiagnosticGroups.td
index a12a4f974ef..d5f777d3d71 100644
--- a/include/clang/Basic/DiagnosticGroups.td
+++ b/include/clang/Basic/DiagnosticGroups.td
@@ -27,7 +27,9 @@ def ArrayBoundsPointerArithmetic : DiagGroup<"array-bounds-pointer-arithmetic">;
 def Availability : DiagGroup<"availability">;
 def Section : DiagGroup<"section">;
 def AutoImport : DiagGroup<"auto-import">;
-def ConstantConversion : DiagGroup<"constant-conversion">;
+def BitFieldConstantConversion : DiagGroup<"bitfield-constant-conversion">;
+def ConstantConversion :
+  DiagGroup<"constant-conversion", [ BitFieldConstantConversion ] >;
 def LiteralConversion : DiagGroup<"literal-conversion">;
 def StringConversion : DiagGroup<"string-conversion">;
 def SignConversion : DiagGroup<"sign-conversion">;
@@ -66,7 +68,9 @@ def : DiagGroup<"discard-qual">;
 def : DiagGroup<"div-by-zero">;
 
 def DocumentationHTML : DiagGroup<"documentation-html">;
-def DocumentationPedantic : DiagGroup<"documentation-pedantic">;
+def DocumentationUnknownCommand : DiagGroup<"documentation-unknown-command">;
+def DocumentationPedantic : DiagGroup<"documentation-pedantic",
+                                      [DocumentationUnknownCommand]>;
 def DocumentationDeprecatedSync : DiagGroup<"documentation-deprecated-sync">;
 def Documentation : DiagGroup<"documentation",
                               [DocumentationHTML,
@@ -80,6 +84,11 @@ def ExtraSemi : DiagGroup<"extra-semi", [CXX11ExtraSemi]>;
 def FormatExtraArgs : DiagGroup<"format-extra-args">;
 def FormatZeroLength : DiagGroup<"format-zero-length">;
 
+// Warnings for C++1y code which is not compatible with prior C++ standards.
+def CXXPre1yCompat : DiagGroup<"cxx98-cxx11-compat">;
+def CXXPre1yCompatPedantic : DiagGroup<"cxx98-cxx11-compat-pedantic",
+                                       [CXXPre1yCompat]>;
+
 def CXX98CompatBindToTemporaryCopy :
   DiagGroup<"c++98-compat-bind-to-temporary-copy">;
 def CXX98CompatLocalTypeTemplateArgs :
@@ -90,9 +99,12 @@ def CXX98CompatUnnamedTypeTemplateArgs :
 def CXX98Compat : DiagGroup<"c++98-compat",
                             [CXX98CompatBindToTemporaryCopy,
                              CXX98CompatLocalTypeTemplateArgs,
-                             CXX98CompatUnnamedTypeTemplateArgs]>;
+                             CXX98CompatUnnamedTypeTemplateArgs,
+                             CXXPre1yCompat]>;
 // Warnings for C++11 features which are Extensions in C++98 mode.
-def CXX98CompatPedantic : DiagGroup<"c++98-compat-pedantic", [CXX98Compat]>;
+def CXX98CompatPedantic : DiagGroup<"c++98-compat-pedantic",
+                                    [CXX98Compat,
+                                     CXXPre1yCompatPedantic]>;
 
 def CXX11Narrowing : DiagGroup<"c++11-narrowing">;
 
@@ -110,8 +122,11 @@ def ReservedUserDefinedLiteral :
 
 def CXX11Compat : DiagGroup<"c++11-compat",
                             [CXX11Narrowing,
-                             CXX11CompatReservedUserDefinedLiteral]>;
+                             CXX11CompatReservedUserDefinedLiteral,
+                             CXXPre1yCompat]>;
 def : DiagGroup<"c++0x-compat", [CXX11Compat]>;
+def CXX11CompatPedantic : DiagGroup<"c++11-compat-pedantic",
+                                    [CXXPre1yCompatPedantic]>;
 
 def : DiagGroup<"effc++">;
 def DivZero : DiagGroup<"division-by-zero">;
@@ -122,6 +137,7 @@ def GlobalConstructors : DiagGroup<"global-constructors">;
 def BitwiseOpParentheses: DiagGroup<"bitwise-op-parentheses">;
 def LogicalOpParentheses: DiagGroup<"logical-op-parentheses">;
 def ShiftOpParentheses: DiagGroup<"shift-op-parentheses">;
+def OverloadedShiftOpParentheses: DiagGroup<"overloaded-shift-op-parentheses">;
 def DanglingElse: DiagGroup<"dangling-else">;
 def DanglingField : DiagGroup<"dangling-field">;
 def DistributedObjectModifiers : DiagGroup<"distributed-object-modifiers">;
@@ -253,7 +269,6 @@ def : DiagGroup<"strict-overflow=5">;
 def : DiagGroup<"strict-overflow">;
 
 def InvalidOffsetof : DiagGroup<"invalid-offsetof">;
-def LambdaExtensions : DiagGroup<"lambda-extensions">;
 def : DiagGroup<"strict-prototypes">;
 def StrictSelector : DiagGroup<"strict-selector-match">;
 def MethodDuplicate : DiagGroup<"duplicate-method-match">;
@@ -352,6 +367,7 @@ def Parentheses : DiagGroup<"parentheses",
                             [LogicalOpParentheses,
                              BitwiseOpParentheses,
                              ShiftOpParentheses,
+                             OverloadedShiftOpParentheses,
                              ParenthesesOnEquality,
                              DanglingElse]>;
 
@@ -476,6 +492,10 @@ def NonGCC : DiagGroup<"non-gcc",
 // earlier C++ versions.
 def CXX11 : DiagGroup<"c++11-extensions", [CXX11ExtraSemi, CXX11LongLong]>;
 
+// A warning group for warnings about using C++1y features as extensions in
+// earlier C++ versions.
+def CXX1y : DiagGroup<"c++1y-extensions">;
+
 def : DiagGroup<"c++0x-extensions", [CXX11]>;
 def DelegatingCtorCycles :
   DiagGroup<"delegating-ctor-cycles">;
diff --git a/include/clang/Basic/DiagnosticLexKinds.td b/include/clang/Basic/DiagnosticLexKinds.td
index 339788b75da..2c16000d339 100644
--- a/include/clang/Basic/DiagnosticLexKinds.td
+++ b/include/clang/Basic/DiagnosticLexKinds.td
@@ -174,6 +174,11 @@ def ext_hexconstant_invalid : Extension<
   "hexadecimal floating constants are a C99 feature">, InGroup;
 def ext_binary_literal : Extension<
   "binary integer literals are a GNU extension">, InGroup;
+def ext_binary_literal_cxx1y : Extension<
+  "binary integer literals are a C++1y extension">, InGroup;
+def warn_cxx11_compat_binary_literal : Warning<
+  "binary integer literals are incompatible with C++ standards before C++1y">,
+  InGroup, DefaultIgnore;
 def err_pascal_string_too_long : Error<"Pascal string is too long">;
 def warn_octal_escape_too_large : ExtWarn<"octal escape sequence out of range">;
 def warn_hex_escape_too_large : ExtWarn<"hex escape sequence out of range">;
@@ -403,16 +408,15 @@ def warn_pragma_include_alias_expected_filename :
 
 def err__Pragma_malformed : Error<
   "_Pragma takes a parenthesized string literal">;
-def err_pragma_comment_malformed : Error<
-  "pragma comment requires parenthesized identifier and optional string">;
 def err_pragma_message_malformed : Error<
-  "pragma message requires parenthesized string">;
+  "pragma %select{message|warning|error}0 requires parenthesized string">;
 def err_pragma_push_pop_macro_malformed : Error<
    "pragma %0 requires a parenthesized string">;
 def warn_pragma_pop_macro_no_push : Warning<
    "pragma pop_macro could not pop '%0', no matching push_macro">;
 def warn_pragma_message : Warning<"%0">,
    InGroup, DefaultWarnNoWerror;
+def err_pragma_message : Error<"%0">;
 def warn_pragma_ignored : Warning<"unknown pragma ignored">,
    InGroup, DefaultIgnore;
 def ext_stdc_pragma_ignored : ExtWarn<"unknown pragma in STDC namespace">,
@@ -446,7 +450,6 @@ def warn_pragma_diagnostic_unknown_warning :
 def warn_pragma_debug_unexpected_command : Warning<
   "unexpected debug command '%0'">;
 
-def err_pragma_comment_unknown_kind : Error<"unknown kind of pragma comment">;
 def err_defined_macro_name : Error<"'defined' cannot be used as a macro name">;
 def err_paste_at_start : Error<
   "'##' cannot appear at start of macro expansion">;
@@ -476,9 +479,9 @@ def ext_pp_line_zero : Extension<
 def err_pp_line_invalid_filename : Error<
   "invalid filename for #line directive">;
 def warn_pp_line_decimal : Warning<
-  "#line directive interprets number as decimal, not octal">;
+  "%select{#line|GNU line marker}0 directive interprets number as decimal, not octal">;
 def err_pp_line_digit_sequence : Error<
-  "#line directive requires a simple digit sequence">;
+  "%select{#line|GNU line marker}0 directive requires a simple digit sequence">;
 def err_pp_linemarker_requires_integer : Error<
   "line marker directive requires a positive integer argument">;
 def err_pp_linemarker_invalid_filename : Error<
diff --git a/include/clang/Basic/DiagnosticOptions.def b/include/clang/Basic/DiagnosticOptions.def
index 8e5562c8630..41bbff2edec 100644
--- a/include/clang/Basic/DiagnosticOptions.def
+++ b/include/clang/Basic/DiagnosticOptions.def
@@ -72,7 +72,6 @@ DIAGOPT(VerifyDiagnostics, 1, 0) /// Check that diagnostics match the expected
 
 DIAGOPT(ElideType, 1, 0)         /// Elide identical types in template diffing
 DIAGOPT(ShowTemplateTree, 1, 0)  /// Print a template tree when diffing
-DIAGOPT(WarnOnSpellCheck, 1, 0)  /// -fwarn-on-spellcheck
 
 VALUE_DIAGOPT(ErrorLimit, 32, 0)           /// Limit # errors emitted.
 /// Limit depth of macro expansion backtrace.
diff --git a/include/clang/Basic/DiagnosticParseKinds.td b/include/clang/Basic/DiagnosticParseKinds.td
index 04a433c0a6a..e001bd46a23 100644
--- a/include/clang/Basic/DiagnosticParseKinds.td
+++ b/include/clang/Basic/DiagnosticParseKinds.td
@@ -18,6 +18,13 @@ def w_asm_qualifier_ignored : Warning<"ignored %0 qualifier on asm">,
 def warn_file_asm_volatile : Warning<
   "meaningless 'volatile' on asm outside function">, CatInlineAsm;
 
+let CategoryName = "Inline Assembly Issue" in {
+def err_asm_empty : Error<"__asm used with no assembly instructions">;
+def err_inline_ms_asm_parsing : Error<"%0">;
+def err_msasm_unsupported_arch : Error<
+  "Unsupported architecture '%0' for MS-style inline assembly">;
+}
+
 let CategoryName = "Parse Issue" in {
 
 def ext_empty_translation_unit : Extension<
@@ -277,6 +284,11 @@ def warn_auto_storage_class : Warning<
 def ext_auto_storage_class : ExtWarn<
   "'auto' storage class specifier is not permitted in C++11, and will not "
   "be supported in future releases">, InGroup>;
+def ext_decltype_auto_type_specifier : ExtWarn<
+  "'decltype(auto)' type specifier is a C++1y extension">, InGroup;
+def warn_cxx11_compat_decltype_auto_type_specifier : Warning<
+  "'decltype(auto)' type specifier is incompatible with C++ standards before "
+  "C++1y">, InGroup, DefaultIgnore;
 def ext_for_range : ExtWarn<
   "range-based for loop is a C++11 extension">, InGroup;
 def warn_cxx98_compat_for_range : Warning<
@@ -352,6 +364,8 @@ def warn_cxx98_compat_static_assert : Warning<
   InGroup, DefaultIgnore;
 def err_paren_after_colon_colon : Error<
   "unexpected parenthesis after '::'">;
+def err_function_definition_not_allowed : Error<
+  "function definition is not allowed here">;
 
 /// Objective-C parser diagnostics
 def err_expected_minus_or_plus : Error<
@@ -411,6 +425,8 @@ def err_missing_catch_finally : Error<
 def err_objc_concat_string : Error<"unexpected token after Objective-C string">;
 def err_expected_objc_container : Error<
   "'@end' must appear in an Objective-C context">;
+def err_unexpected_protocol_qualifier : Error<
+  "@implementation declaration can not be protocol qualified">;
 def err_objc_unexpected_atend : Error<
   "'@end' appears where closing brace '}' is expected">;
 def error_property_ivar_decl : Error<
@@ -527,6 +543,22 @@ def err_ms_declspec_type : Error<
   "__declspec attributes must be an identifier or string literal">;
 def warn_ms_declspec_unknown : Warning<
   "unknown __declspec attribute %0 ignored">, InGroup;
+def err_ms_property_no_getter_or_putter : Error<
+  "property does not specify a getter or a putter">;
+def err_ms_property_unknown_accessor : Error<
+  "expected 'get' or 'put' in property declaration">;
+def err_ms_property_has_set_accessor : Error<
+  "putter for property must be specified as 'put', not 'set'">;
+def err_ms_property_missing_accessor_kind : Error<
+  "missing 'get=' or 'put='">;
+def err_ms_property_expected_equal : Error<
+  "expected '=' after '%0'">;
+def err_ms_property_duplicate_accessor : Error<
+  "property declaration specifies '%0' accessor twice">;
+def err_ms_property_expected_accessor_name : Error<
+  "expected name of accessor method">;
+def err_ms_property_expected_comma_or_rparen : Error<
+  "expected ',' or ')' at end of property accessor list">;
 
 /// C++ Templates
 def err_expected_template : Error<"expected template">;
@@ -670,6 +702,8 @@ def err_duplicate_virt_specifier : Error<
 
 def err_scoped_enum_missing_identifier : Error<
   "scoped enumeration requires a name">;
+def ext_scoped_enum : ExtWarn<
+  "scoped enumerations are a C++11 extension">, InGroup;
 def warn_cxx98_compat_scoped_enum : Warning<
   "scoped enumerations are incompatible with C++98">,
   InGroup, DefaultIgnore;
@@ -751,6 +785,10 @@ def warn_pragma_unused_expected_punc : Warning<
 def err_pragma_fp_contract_scope : Error<
   "'#pragma fp_contract' should only appear at file scope or at the start of a "
   "compound expression">; 
+// - #pragma comment
+def err_pragma_comment_malformed : Error<
+  "pragma comment requires parenthesized identifier and optional string">;
+def err_pragma_comment_unknown_kind : Error<"unknown kind of pragma comment">;
 
 
 // OpenCL Section 6.8.g
diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td
index c4815cd7ea6..f5345eb8c35 100644
--- a/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/include/clang/Basic/DiagnosticSemaKinds.td
@@ -78,6 +78,9 @@ def ext_vla : Extension<"variable length arrays are a C99 feature">,
   InGroup;
 def warn_vla_used : Warning<"variable length array used">,
   InGroup, DefaultIgnore;
+def warn_cxx11_compat_array_of_runtime_bound : Warning<
+  "arrays of runtime bound are incompatible with C++ standards before C++1y">,
+  InGroup, DefaultIgnore;
 def err_vla_non_pod : Error<"variable length array of non-POD element type %0">;
 def err_vla_in_sfinae : Error<
   "variable length array cannot be formed during template argument deduction">;
@@ -296,9 +299,9 @@ def warn_exit_time_destructor : Warning<
   InGroup, DefaultIgnore;
 
 def err_invalid_thread : Error<
-  "'__thread' is only allowed on variable declarations">;
+  "'%0' is only allowed on variable declarations">;
 def err_thread_non_global : Error<
-  "'__thread' variables must have global storage">;
+  "'%0' variables must have global storage">;
 def err_thread_unsupported : Error<
   "thread-local storage is unsupported for the current target">;
 
@@ -639,6 +642,10 @@ def warn_objc_isa_use : Warning<
 def warn_objc_isa_assign : Warning<
   "assignment to Objective-C's isa is deprecated in favor of "
   "object_setClass()">, InGroup;
+def warn_objc_pointer_masking : Warning<
+  "bitmasking for introspection of Objective-C object pointers is strongly "
+  "discouraged">,
+  InGroup>;
 def warn_objc_property_default_assign_on_object : Warning<
   "default property attribute 'assign' not appropriate for non-GC object">,
   InGroup;
@@ -1300,7 +1307,8 @@ def err_member_function_call_bad_cvr : Error<"member function %0 not viable: "
     "volatile or restrict|const, volatile, or restrict}2">;
 
 def err_reference_bind_to_bitfield : Error<
-  "%select{non-const|volatile}0 reference cannot bind to bit-field %1">;
+  "%select{non-const|volatile}0 reference cannot bind to "
+  "bit-field%select{| %1}2">;
 def err_reference_bind_to_vector_element : Error<
   "%select{non-const|volatile}0 reference cannot bind to vector element">;
 def err_reference_var_requires_init : Error<
@@ -1421,7 +1429,7 @@ def err_auto_not_allowed : Error<
   "|in non-static union member|in non-static class member|in interface member"
   "|in exception declaration|in template parameter|in block literal"
   "|in template argument|in typedef|in type alias|in function return type"
-  "|here}0">;
+  "|in conversion function type|here}0">;
 def err_auto_var_requires_init : Error<
   "declaration of variable %0 with type %1 requires an initializer">;
 def err_auto_new_requires_ctor_arg : Error<
@@ -1447,7 +1455,8 @@ def err_auto_var_deduction_failure_from_init_list : Error<
 def err_auto_new_deduction_failure : Error<
   "new expression for type %0 has incompatible constructor argument of type %1">;
 def err_auto_different_deductions : Error<
-  "'auto' deduced as %0 in declaration of %1 and deduced as %2 in declaration of %3">;
+  "'%select{auto|decltype(auto)}0' deduced as %1 in declaration of %2 and "
+  "deduced as %3 in declaration of %4">;
 def err_implied_std_initializer_list_not_found : Error<
   "cannot deduce type of initializer list because std::initializer_list was "
   "not found; include ">;
@@ -1458,6 +1467,34 @@ def warn_dangling_std_initializer_list : Warning<
   "%select{the full-expression|the constructor}0">,
   InGroup>;
 
+// C++1y decltype(auto) type
+def err_decltype_auto_cannot_be_combined : Error<
+  "'decltype(auto)' cannot be combined with other type specifiers">;
+def err_decltype_auto_function_declarator_not_declaration : Error<
+  "'decltype(auto)' can only be used as a return type "
+  "in a function declaration">;
+def err_decltype_auto_compound_type : Error<
+  "cannot form %select{pointer to|reference to|array of}0 'decltype(auto)'">;
+def err_decltype_auto_initializer_list : Error<
+  "cannot deduce 'decltype(auto)' from initializer list">;
+
+// C++1y deduced return types
+def err_auto_fn_deduction_failure : Error<
+  "cannot deduce return type %0 from returned value of type %1">;
+def err_auto_fn_different_deductions : Error<
+  "'%select{auto|decltype(auto)}0' in return type deduced as %1 here but "
+  "deduced as %2 in earlier return statement">;
+def err_auto_fn_used_before_defined : Error<
+  "function %0 with deduced return type cannot be used before it is defined">;
+def err_auto_fn_no_return_but_not_auto : Error<
+  "cannot deduce return type %0 for function with no return statements">;
+def err_auto_fn_return_void_but_not_auto : Error<
+  "cannot deduce return type %0 from omitted return expression">;
+def err_auto_fn_return_init_list : Error<
+  "cannot deduce return type from initializer list">;
+def err_auto_fn_virtual : Error<
+  "function with deduced return type cannot be virtual">;
+
 // C++11 override control
 def override_keyword_only_allowed_on_virtual_member_functions : Error<
   "only virtual member functions can be marked '%0'">;
@@ -1547,6 +1584,11 @@ def note_for_range_begin_end : Note<
 def warn_cxx98_compat_constexpr : Warning<
   "'constexpr' specifier is incompatible with C++98">,
   InGroup, DefaultIgnore;
+// FIXME: Maybe this should also go in -Wc++1y-compat?
+def warn_cxx1y_compat_constexpr_not_const : Warning<
+  "'constexpr' non-static member function will not be implicitly 'const' "
+  "in C++1y; add 'const' to avoid a change in behavior">,
+  InGroup>;
 def err_invalid_constexpr : Error<
   "%select{function parameter|typedef|non-static data member}0 "
   "cannot be constexpr">;
@@ -1585,20 +1627,54 @@ def err_constexpr_non_literal_param : Error<
   "not a literal type">;
 def err_constexpr_body_invalid_stmt : Error<
   "statement not allowed in constexpr %select{function|constructor}0">;
-def err_constexpr_type_definition : Error<
-  "types cannot be defined in a constexpr %select{function|constructor}0">;
+def ext_constexpr_body_invalid_stmt : ExtWarn<
+  "use of this statement in a constexpr %select{function|constructor}0 "
+  "is a C++1y extension">, InGroup;
+def warn_cxx11_compat_constexpr_body_invalid_stmt : Warning<
+  "use of this statement in a constexpr %select{function|constructor}0 "
+  "is incompatible with C++ standards before C++1y">,
+  InGroup, DefaultIgnore;
+def ext_constexpr_type_definition : ExtWarn<
+  "type definition in a constexpr %select{function|constructor}0 "
+  "is a C++1y extension">, InGroup;
+def warn_cxx11_compat_constexpr_type_definition : Warning<
+  "type definition in a constexpr %select{function|constructor}0 "
+  "is incompatible with C++ standards before C++1y">,
+  InGroup, DefaultIgnore;
 def err_constexpr_vla : Error<
   "variably-modified type %0 cannot be used in a constexpr "
   "%select{function|constructor}1">;
-def err_constexpr_var_declaration : Error<
-  "variables cannot be declared in a constexpr %select{function|constructor}0">;
+def ext_constexpr_local_var : ExtWarn<
+  "variable declaration in a constexpr %select{function|constructor}0 "
+  "is a C++1y extension">, InGroup;
+def warn_cxx11_compat_constexpr_local_var : Warning<
+  "variable declaration in a constexpr %select{function|constructor}0 "
+  "is incompatible with C++ standards before C++1y">,
+  InGroup, DefaultIgnore;
+def err_constexpr_local_var_static : Error<
+  "%select{static|thread_local}1 variable not permitted in a constexpr "
+  "%select{function|constructor}0">;
+def err_constexpr_local_var_non_literal_type : Error<
+  "variable of non-literal type %1 cannot be defined in a constexpr "
+  "%select{function|constructor}0">;
+def err_constexpr_local_var_no_init : Error<
+  "variables defined in a constexpr %select{function|constructor}0 must be "
+  "initialized">;
 def ext_constexpr_function_never_constant_expr : ExtWarn<
   "constexpr %select{function|constructor}0 never produces a "
   "constant expression">, InGroup>, DefaultError;
 def err_constexpr_body_no_return : Error<
   "no return statement in constexpr function">;
-def err_constexpr_body_multiple_return : Error<
-  "multiple return statements in constexpr function">;
+def warn_cxx11_compat_constexpr_body_no_return : Warning<
+  "constexpr function with no return statements is incompatible with C++ "
+  "standards before C++1y">, InGroup, DefaultIgnore;
+def ext_constexpr_body_multiple_return : ExtWarn<
+  "multiple return statements in constexpr function is a C++1y extension">,
+  InGroup;
+def warn_cxx11_compat_constexpr_body_multiple_return : Warning<
+  "multiple return statements in constexpr function "
+  "is incompatible with C++ standards before C++1y">,
+  InGroup, DefaultIgnore;
 def note_constexpr_body_previous_return : Note<
   "previous return statement is here">;
 def err_constexpr_function_try_block : Error<
@@ -1810,6 +1886,19 @@ def err_attribute_section_local_variable : Error<
 def warn_mismatched_section : Warning<
   "section does not match previous declaration">, InGroup
; +def err_anonymous_property: Error< + "anonymous property is not supported">; +def err_property_is_variably_modified: Error< + "property '%0' has a variably modified type">; +def err_no_getter_for_property : Error< + "no getter defined for property '%0'">; +def err_no_setter_for_property : Error< + "no setter defined for property '%0'">; +def error_cannot_find_suitable_getter : Error< + "cannot find suitable getter for property '%0'">; +def error_cannot_find_suitable_setter : Error< + "cannot find suitable setter for property '%0'">; + def err_attribute_aligned_not_power_of_two : Error< "requested alignment is not a power of 2">; def err_attribute_aligned_greater_than_8192 : Error< @@ -2088,7 +2177,7 @@ def warn_impcast_integer_precision_constant : Warning< InGroup; def warn_impcast_bitfield_precision_constant : Warning< "implicit truncation from %2 to bitfield changes value from %0 to %1">, - InGroup; + InGroup; def warn_impcast_literal_float_to_integer : Warning< "implicit conversion from %0 to %1 changes value from %2 to %3">, InGroup; @@ -2370,6 +2459,11 @@ def note_ovl_candidate_failed_overload_resolution : Note< "function %0">; def note_ovl_candidate_non_deduced_mismatch : Note< "candidate template ignored: could not match %diff{$ against $|types}0,1">; +// This note is needed because the above note would sometimes print two +// different types with the same name. Remove this note when the above note +// can handle that case properly. +def note_ovl_candidate_non_deduced_mismatch_qualified : Note< + "candidate template ignored: could not match %q0 against %q1">; // Note that we don't treat templates differently for this diagnostic. def note_ovl_candidate_arity : Note<"candidate " @@ -3364,6 +3458,9 @@ def err_non_thread_thread : Error< "non-thread-local declaration of %0 follows thread-local declaration">; def err_thread_non_thread : Error< "thread-local declaration of %0 follows non-thread-local declaration">; +def err_thread_thread_different_kind : Error< + "thread-local declaration of %0 with %select{static|dynamic}1 initialization " + "follows declaration with %select{dynamic|static}1 initialization">; def err_redefinition_different_type : Error< "redefinition of %0 with a different type%diff{: $ vs $|}1,2">; def err_redefinition_different_kind : Error< @@ -3722,8 +3819,10 @@ def err_arc_unsupported_weak_class : Error< def err_arc_weak_unavailable_assign : Error< "assignment of a weak-unavailable object to a __weak object">; def err_arc_weak_unavailable_property : Error< - "synthesis of a weak-unavailable property is disallowed " - "because it requires synthesis of an instance variable of the __weak object">; + "synthesizing __weak instance variable of type %0, which does not " + "support weak references">; +def note_implemented_by_class : Note< + "when implemented by class %0">; def err_arc_convesion_of_weak_unavailable : Error< "%select{implicit conversion|cast}0 of weak-unavailable object of type %1 to" " a __weak object of type %2">; @@ -3956,6 +4055,8 @@ def err_sizeof_alignof_incomplete_type : Error< "incomplete type %1">; def err_sizeof_alignof_bitfield : Error< "invalid application of '%select{sizeof|alignof}0' to bit-field">; +def err_alignof_member_of_incomplete_type : Error< + "invalid application of 'alignof' to a field of a class still being defined">; def err_vecstep_non_scalar_vector_type : Error< "'vec_step' requires built-in scalar or vector type, %0 invalid">; def err_offsetof_incomplete_type : Error< @@ -4020,6 +4121,13 @@ def warn_bitwise_and_in_bitwise_or : Warning< def warn_logical_and_in_logical_or : Warning< "'&&' within '||'">, InGroup; +def warn_overloaded_shift_in_comparison :Warning< + "overloaded operator %select{>>|<<}0 has lower precedence than " + "comparison operator">, + InGroup; +def note_evaluate_comparison_first :Note< + "place parentheses around comparison expression to evaluate it first">; + def warn_addition_in_bitshift : Warning< "operator '%0' has lower precedence than '%1'; " "'%1' will be evaluated first">, InGroup; @@ -4468,8 +4576,7 @@ def err_catch_param_not_objc_type : Error< def err_illegal_qualifiers_on_catch_parm : Error< "illegal qualifiers on @catch parameter">; def err_storage_spec_on_catch_parm : Error< - "@catch parameter cannot have storage specifier %select{|'typedef'|'extern'|" - "'static'|'auto'|'register'|'__private_extern__'|'mutable'}0">; + "@catch parameter cannot have storage specifier '%0'">; def warn_register_objc_catch_parm : Warning< "'register' storage specifier on @catch parameter will be ignored">; def err_qualified_objc_catch_parm : Error< @@ -4523,6 +4630,9 @@ def err_bad_cxx_cast_generic : Error< def err_bad_cxx_cast_rvalue : Error< "%select{const_cast|static_cast|reinterpret_cast|dynamic_cast|C-style cast|" "functional-style cast}0 from rvalue to reference type %2">; +def err_bad_cxx_cast_bitfield : Error< + "%select{const_cast|static_cast|reinterpret_cast|dynamic_cast|C-style cast|" + "functional-style cast}0 from bit-field lvalue to reference type %2">; def err_bad_cxx_cast_qualifiers_away : Error< "%select{const_cast|static_cast|reinterpret_cast|dynamic_cast|C-style cast|" "functional-style cast}0 from %1 to %2 casts away qualifiers">; @@ -4686,7 +4796,16 @@ def warn_overloaded_virtual : Warning< "%q0 hides overloaded virtual %select{function|functions}1">, InGroup, DefaultIgnore; def note_hidden_overloaded_virtual_declared_here : Note< - "hidden overloaded virtual function %q0 declared here">; + "hidden overloaded virtual function %q0 declared here" + "%select{|: different classes%diff{ ($ vs $)|}2,3" + "|: different number of parameters (%2 vs %3)" + "|: type mismatch at %ordinal2 parameter%diff{ ($ vs $)|}3,4" + "|: different return type%diff{ ($ vs $)|}2,3" + "|: different qualifiers (" + "%select{none|const|restrict|const and restrict|volatile|const and volatile|" + "volatile and restrict|const, volatile, and restrict}2 vs " + "%select{none|const|restrict|const and restrict|volatile|const and volatile|" + "volatile and restrict|const, volatile, and restrict}3)}1">; def warn_using_directive_in_header : Warning< "using namespace directive in global context in header">, InGroup, DefaultIgnore; @@ -4753,9 +4872,6 @@ let CategoryName = "Lambda Issue" in { "incomplete result type %0 in lambda expression">; def err_lambda_objc_object_result : Error< "non-pointer Objective-C class type %0 in lambda expression result">; - def ext_lambda_default_arguments : ExtWarn< - "C++11 forbids default arguments for lambda expressions">, - InGroup; def err_noreturn_lambda_has_return_expr : Error< "lambda declared 'noreturn' should not return">; def warn_maybe_falloff_nonvoid_lambda : Warning< @@ -4775,6 +4891,9 @@ let CategoryName = "Lambda Issue" in { "here">; } +def err_return_in_captured_stmt : Error< + "cannot return from %0">; + def err_operator_arrow_circular : Error< "circular pointer delegation detected">; def err_pseudo_dtor_base_not_scalar : Error< @@ -5313,16 +5432,13 @@ let CategoryName = "Inline Assembly Issue" in { def err_asm_tying_incompatible_types : Error< "unsupported inline asm: input with type " "%diff{$ matching output with type $|}0,1">; + def err_asm_incomplete_type : Error<"asm operand has incomplete type %0">; def err_asm_unknown_register_name : Error<"unknown register name '%0' in asm">; - def err_asm_empty : Error<"__asm used with no assembly instructions">; def err_asm_invalid_input_size : Error< "invalid input size for constraint '%0'">; def err_invalid_asm_cast_lvalue : Error< "invalid use of a cast in a inline asm context requiring an l-value: " "remove the cast or build with -fheinous-gnu-extensions">; - def err_inline_ms_asm_parsing : Error<"%0">; - def err_msasm_unsupported_arch : Error< - "Unsupported architecture '%0' for MS-style inline assembly">; def warn_asm_label_on_auto_decl : Warning< "ignored asm label '%0' on automatic variable">; @@ -5402,6 +5518,13 @@ def ext_in_class_initializer_non_constant : Extension< "in-class initializer for static data member is not a constant expression; " "folding it to a constant is a GNU extension">, InGroup; +def err_thread_dynamic_init : Error< + "initializer for thread-local variable must be a constant expression">; +def err_thread_nontrivial_dtor : Error< + "type of thread-local variable has non-trivial destruction">; +def note_use_thread_local : Note< + "use 'thread_local' to allow this">; + // C++ anonymous unions and GNU anonymous structs/unions def ext_anonymous_union : Extension< "anonymous unions are a C11 extension">, InGroup; @@ -6020,8 +6143,10 @@ def ext_mixed_decls_code : Extension< "ISO C90 forbids mixing declarations and code">, InGroup>; -def err_non_variable_decl_in_for : Error< +def err_non_local_variable_decl_in_for : Error< "declaration of non-local variable in 'for' loop">; +def err_non_variable_decl_in_for : Error< + "non-variable declaration in 'for' loop">; def err_toomany_element_decls : Error< "only one element declaration is allowed">; def err_selector_element_not_lvalue : Error< @@ -6205,7 +6330,9 @@ def err_sampler_argument_required : Error< "sampler_t variable required - got %0">; def err_wrong_sampler_addressspace: Error< "sampler type cannot be used with the __local and __global address space qualifiers">; - +def err_opencl_global_invalid_addr_space : Error< + "global variables must have a constant address space qualifier">; + // OpenMP support. def err_omp_expected_var_arg_suggest : Error< "%0 is not a global variable, static local variable or static data member%select{|; did you mean %2?}1">; diff --git a/include/clang/Basic/DiagnosticSerializationKinds.td b/include/clang/Basic/DiagnosticSerializationKinds.td index 7137404a690..1b45b10c122 100644 --- a/include/clang/Basic/DiagnosticSerializationKinds.td +++ b/include/clang/Basic/DiagnosticSerializationKinds.td @@ -58,6 +58,10 @@ def err_pch_undef : Error< "%select{command line contains|precompiled header was built with}0 " "'-undef' but %select{precompiled header was not built with it|" "it is not present on the command line}0">; +def err_pch_pp_detailed_record : Error< + "%select{command line contains|precompiled header was built with}0 " + "'-detailed-preprocessing-record' but %select{precompiled header was not " + "built with it|it is not present on the command line}0">; def err_not_a_pch_file : Error< "'%0' does not appear to be a precompiled header file">, DefaultFatal; diff --git a/include/clang/Basic/IdentifierTable.h b/include/clang/Basic/IdentifierTable.h index c04a893c6f6..d4d53390bdb 100644 --- a/include/clang/Basic/IdentifierTable.h +++ b/include/clang/Basic/IdentifierTable.h @@ -394,7 +394,7 @@ class IdentifierInfoLookup { /// /// \returns A new iterator into the set of known identifiers. The /// caller is responsible for deleting this iterator. - virtual IdentifierIterator *getIdentifiers() const; + virtual IdentifierIterator *getIdentifiers(); }; /// \brief An abstract class used to resolve numerical identifier diff --git a/include/clang/Basic/LangOptions.def b/include/clang/Basic/LangOptions.def index 3de0107dff6..b17dfbc9b52 100644 --- a/include/clang/Basic/LangOptions.def +++ b/include/clang/Basic/LangOptions.def @@ -156,7 +156,7 @@ ENUM_LANGOPT(StackProtector, StackProtectorMode, 2, SSPOff, ENUM_LANGOPT(SignedOverflowBehavior, SignedOverflowBehaviorTy, 2, SOB_Undefined, "signed integer overflow handling") -BENIGN_LANGOPT(InstantiationDepth, 32, 512, +BENIGN_LANGOPT(InstantiationDepth, 32, 256, "maximum template instantiation depth") BENIGN_LANGOPT(ConstexprCallDepth, 32, 512, "maximum constexpr call depth") diff --git a/include/clang/Basic/OnDiskHashTable.h b/include/clang/Basic/OnDiskHashTable.h index 06cb1438e62..ee301237f91 100644 --- a/include/clang/Basic/OnDiskHashTable.h +++ b/include/clang/Basic/OnDiskHashTable.h @@ -103,7 +103,7 @@ inline uint32_t ReadLE32(const unsigned char *&Data) { // Hosts that directly support little-endian 32-bit loads can just // use them. Big-endian hosts need a bswap. uint32_t V = *((const uint32_t*)Data); - if (llvm::sys::isBigEndianHost()) + if (llvm::sys::IsBigEndianHost) V = llvm::ByteSwap_32(V); Data += 4; return V; diff --git a/include/clang/Basic/SourceManager.h b/include/clang/Basic/SourceManager.h index 00c96c3da0d..f82b196929a 100644 --- a/include/clang/Basic/SourceManager.h +++ b/include/clang/Basic/SourceManager.h @@ -646,6 +646,13 @@ class SourceManager : public RefCountedBase { // Statistics for -print-stats. mutable unsigned NumLinearScans, NumBinaryProbes; + /// \brief Associates a FileID with its "included/expanded in" decomposed + /// location. + /// + /// Used to cache results from and speed-up \c getDecomposedIncludedLoc + /// function. + mutable llvm::DenseMap > IncludedLocMap; + /// The key value into the IsBeforeInTUCache table. typedef std::pair IsBeforeInTUCacheKey; @@ -1127,6 +1134,10 @@ class SourceManager : public RefCountedBase { return getDecomposedSpellingLocSlowCase(E, Offset); } + /// \brief Returns the "included/expanded in" decomposed location of the given + /// FileID. + std::pair getDecomposedIncludedLoc(FileID FID) const; + /// \brief Returns the offset from the start of the file that the /// specified SourceLocation represents. /// diff --git a/include/clang/Basic/Specifiers.h b/include/clang/Basic/Specifiers.h index 8706179a171..eb3fc659a87 100644 --- a/include/clang/Basic/Specifiers.h +++ b/include/clang/Basic/Specifiers.h @@ -38,8 +38,8 @@ namespace clang { TST_void, TST_char, TST_wchar, // C++ wchar_t - TST_char16, // C++0x char16_t - TST_char32, // C++0x char32_t + TST_char16, // C++11 char16_t + TST_char32, // C++11 char32_t TST_int, TST_int128, TST_half, // OpenCL half, ARM NEON __fp16 @@ -57,11 +57,12 @@ namespace clang { TST_typename, // Typedef, C++ class-name or enum name, etc. TST_typeofType, TST_typeofExpr, - TST_decltype, // C++0x decltype - TST_underlyingType, // __underlying_type for C++0x - TST_auto, // C++0x auto - TST_unknown_anytype, // __unknown_anytype extension - TST_atomic, // C11 _Atomic + TST_decltype, // C++11 decltype + TST_underlyingType, // __underlying_type for C++11 + TST_auto, // C++11 auto + TST_decltype_auto, // C++1y decltype(auto) + TST_unknown_anytype, // __unknown_anytype extension + TST_atomic, // C11 _Atomic TST_image1d_t, // OpenCL image1d_t TST_image1d_array_t, // OpenCL image1d_array_t TST_image1d_buffer_t, // OpenCL image1d_buffer_t @@ -145,7 +146,7 @@ namespace clang { TSK_ExplicitSpecialization, /// This template specialization was instantiated from a template /// due to an explicit instantiation declaration request - /// (C++0x [temp.explicit]). + /// (C++11 [temp.explicit]). TSK_ExplicitInstantiationDeclaration, /// This template specialization was instantiated from a template /// due to an explicit instantiation definition request @@ -153,6 +154,19 @@ namespace clang { TSK_ExplicitInstantiationDefinition }; + /// \brief Thread storage-class-specifier. + enum ThreadStorageClassSpecifier { + TSCS_unspecified, + /// GNU __thread. + TSCS___thread, + /// C++11 thread_local. Implies 'static' at block scope, but not at + /// class scope. + TSCS_thread_local, + /// C11 _Thread_local. Must be combined with either 'static' or 'extern' + /// if used at block scope. + TSCS__Thread_local + }; + /// \brief Storage classes. enum StorageClass { // These are legal on both functions and variables. diff --git a/include/clang/Basic/StmtNodes.td b/include/clang/Basic/StmtNodes.td index 8f6a1c9f989..cbfce83c4b5 100644 --- a/include/clang/Basic/StmtNodes.td +++ b/include/clang/Basic/StmtNodes.td @@ -27,6 +27,7 @@ def DeclStmt : Stmt; def SwitchCase : Stmt<1>; def CaseStmt : DStmt; def DefaultStmt : DStmt; +def CapturedStmt : Stmt; // Asm statements def AsmStmt : Stmt<1>; @@ -107,6 +108,7 @@ def CXXNullPtrLiteralExpr : DStmt; def CXXThisExpr : DStmt; def CXXThrowExpr : DStmt; def CXXDefaultArgExpr : DStmt; +def CXXDefaultInitExpr : DStmt; def CXXScalarValueInitExpr : DStmt; def CXXNewExpr : DStmt; def CXXDeleteExpr : DStmt; @@ -163,6 +165,7 @@ def BlockExpr : DStmt; def OpaqueValueExpr : DStmt; // Microsoft Extensions. +def MSPropertyRefExpr : DStmt; def CXXUuidofExpr : DStmt; def SEHTryStmt : Stmt; def SEHExceptStmt : Stmt; diff --git a/include/clang/Basic/TargetBuiltins.h b/include/clang/Basic/TargetBuiltins.h index 1d5004c3700..66e378fa9b4 100644 --- a/include/clang/Basic/TargetBuiltins.h +++ b/include/clang/Basic/TargetBuiltins.h @@ -21,6 +21,15 @@ namespace clang { + /// \brief AArch64 builtins + namespace AArch64 { + enum { + LastTIBuiltin = clang::Builtin::FirstTSBuiltin-1, +#define BUILTIN(ID, TYPE, ATTRS) BI##ID, +#include "clang/Basic/BuiltinsAArch64.def" + LastTSBuiltin + }; + } /// \brief ARM builtins namespace ARM { enum { diff --git a/include/clang/Basic/TargetInfo.h b/include/clang/Basic/TargetInfo.h index c05f062aee9..49b26ac8e37 100644 --- a/include/clang/Basic/TargetInfo.h +++ b/include/clang/Basic/TargetInfo.h @@ -66,6 +66,7 @@ class TargetInfo : public RefCountedBase { unsigned char LongWidth, LongAlign; unsigned char LongLongWidth, LongLongAlign; unsigned char SuitableAlign; + unsigned char MinGlobalAlign; unsigned char MaxAtomicPromoteWidth, MaxAtomicInlineWidth; unsigned short MaxVectorAlign; const char *DescriptionString; @@ -156,7 +157,16 @@ class TargetInfo : public RefCountedBase { /// __builtin_va_list as defined by ARM AAPCS ABI /// http://infocenter.arm.com // /help/topic/com.arm.doc.ihi0042d/IHI0042D_aapcs.pdf - AAPCSABIBuiltinVaList + AAPCSABIBuiltinVaList, + + // typedef struct __va_list_tag + // { + // long __gpr; + // long __fpr; + // void *__overflow_arg_area; + // void *__reg_save_area; + // } va_list[1]; + SystemZBuiltinVaList }; protected: @@ -266,6 +276,10 @@ class TargetInfo : public RefCountedBase { /// object with a fundamental alignment requirement. unsigned getSuitableAlign() const { return SuitableAlign; } + /// getMinGlobalAlign - Return the minimum alignment of a global variable, + /// unless its alignment is explicitly reduced via attributes. + unsigned getMinGlobalAlign() const { return MinGlobalAlign; } + /// getWCharWidth/Align - Return the size of 'wchar_t' for this target, in /// bits. unsigned getWCharWidth() const { return getTypeWidth(WCharType); } diff --git a/include/clang/Basic/TokenKinds.def b/include/clang/Basic/TokenKinds.def index a254faef76b..bcf0f31dcbf 100644 --- a/include/clang/Basic/TokenKinds.def +++ b/include/clang/Basic/TokenKinds.def @@ -262,6 +262,7 @@ KEYWORD(_Generic , KEYALL) KEYWORD(_Imaginary , KEYALL) KEYWORD(_Noreturn , KEYALL) KEYWORD(_Static_assert , KEYALL) +KEYWORD(_Thread_local , KEYALL) KEYWORD(__func__ , KEYALL) KEYWORD(__objc_yes , KEYALL) KEYWORD(__objc_no , KEYALL) @@ -614,6 +615,11 @@ ANNOTATION(pragma_pack) // handles them. ANNOTATION(pragma_parser_crash) +// Annotation for #pragma clang __debug captured... +// The lexer produces these so that they only take effect when the parser +// handles them. +ANNOTATION(pragma_captured) + // Annotation for #pragma ms_struct... // The lexer produces these so that they only take effect when the parser // handles them. diff --git a/include/clang/Basic/arm_neon.td b/include/clang/Basic/arm_neon.td index 3373e017dec..77bc797c505 100644 --- a/include/clang/Basic/arm_neon.td +++ b/include/clang/Basic/arm_neon.td @@ -77,9 +77,23 @@ class Inst { Op Operand = o; bit isShift = 0; bit isVCVT_N = 0; + + // Certain intrinsics have different names than their representative + // instructions. This field allows us to handle this correctly when we + // are generating tests. + string InstName = ""; + + // Certain intrinsics even though they are not a WOpInst or LOpInst, + // generate a WOpInst/LOpInst instruction (see below for definition + // of a WOpInst/LOpInst). For testing purposes we need to know + // this. Ex: vset_lane which outputs vmov instructions. + bit isHiddenWInst = 0; + bit isHiddenLInst = 0; } -// Used to generate Builtins.def: +// The following instruction classes are implemented via builtins. +// These declarations are used to generate Builtins.def: +// // SInst: Instruction with signed/unsigned suffix (e.g., "s8", "u8", "p8") // IInst: Instruction with generic integer suffix (e.g., "i8") // WInst: Instruction with only bit size suffix (e.g., "8") @@ -87,6 +101,22 @@ class SInst : Inst {} class IInst : Inst {} class WInst : Inst {} +// The following instruction classes are implemented via operators +// instead of builtins. As such these declarations are only used for +// the purpose of generating tests. +// +// SOpInst: Instruction with signed/unsigned suffix (e.g., "s8", +// "u8", "p8"). +// IOpInst: Instruction with generic integer suffix (e.g., "i8"). +// WOpInst: Instruction with bit size only suffix (e.g., "8"). +// LOpInst: Logical instruction with no bit size suffix. +// NoTestOpInst: Intrinsic that has no corresponding instruction. +class SOpInst : Inst {} +class IOpInst : Inst {} +class WOpInst : Inst {} +class LOpInst : Inst {} +class NoTestOpInst : Inst {} + // prototype: return (arg, arg, ...) // v: void // t: best-fit integer (int/poly args) @@ -123,9 +153,10 @@ class WInst : Inst {} //////////////////////////////////////////////////////////////////////////////// // E.3.1 Addition -def VADD : Inst<"vadd", "ddd", "csilfUcUsUiUlQcQsQiQlQfQUcQUsQUiQUl", OP_ADD>; -def VADDL : Inst<"vaddl", "wdd", "csiUcUsUi", OP_ADDL>; -def VADDW : Inst<"vaddw", "wwd", "csiUcUsUi", OP_ADDW>; +def VADD : IOpInst<"vadd", "ddd", + "csilfUcUsUiUlQcQsQiQlQfQUcQUsQUiQUl", OP_ADD>; +def VADDL : SOpInst<"vaddl", "wdd", "csiUcUsUi", OP_ADDL>; +def VADDW : SOpInst<"vaddw", "wwd", "csiUcUsUi", OP_ADDW>; def VHADD : SInst<"vhadd", "ddd", "csiUcUsUiQcQsQiQUcQUsQUi">; def VRHADD : SInst<"vrhadd", "ddd", "csiUcUsUiQcQsQiQUcQUsQUi">; def VQADD : SInst<"vqadd", "ddd", "csilUcUsUiUlQcQsQiQlQUcQUsQUiQUl">; @@ -134,12 +165,12 @@ def VRADDHN : IInst<"vraddhn", "hkk", "silUsUiUl">; //////////////////////////////////////////////////////////////////////////////// // E.3.2 Multiplication -def VMUL : Inst<"vmul", "ddd", "csifUcUsUiQcQsQiQfQUcQUsQUi", OP_MUL>; +def VMUL : IOpInst<"vmul", "ddd", "csifUcUsUiQcQsQiQfQUcQUsQUi", OP_MUL>; def VMULP : SInst<"vmul", "ddd", "PcQPc">; -def VMLA : Inst<"vmla", "dddd", "csifUcUsUiQcQsQiQfQUcQUsQUi", OP_MLA>; -def VMLAL : Inst<"vmlal", "wwdd", "csiUcUsUi", OP_MLAL>; -def VMLS : Inst<"vmls", "dddd", "csifUcUsUiQcQsQiQfQUcQUsQUi", OP_MLS>; -def VMLSL : Inst<"vmlsl", "wwdd", "csiUcUsUi", OP_MLSL>; +def VMLA : IOpInst<"vmla", "dddd", "csifUcUsUiQcQsQiQfQUcQUsQUi", OP_MLA>; +def VMLAL : SOpInst<"vmlal", "wwdd", "csiUcUsUi", OP_MLAL>; +def VMLS : IOpInst<"vmls", "dddd", "csifUcUsUiQcQsQiQfQUcQUsQUi", OP_MLS>; +def VMLSL : SOpInst<"vmlsl", "wwdd", "csiUcUsUi", OP_MLSL>; def VQDMULH : SInst<"vqdmulh", "ddd", "siQsQi">; def VQRDMULH : SInst<"vqrdmulh", "ddd", "siQsQi">; def VQDMLAL : SInst<"vqdmlal", "wwdd", "si">; @@ -149,9 +180,10 @@ def VQDMULL : SInst<"vqdmull", "wdd", "si">; //////////////////////////////////////////////////////////////////////////////// // E.3.3 Subtraction -def VSUB : Inst<"vsub", "ddd", "csilfUcUsUiUlQcQsQiQlQfQUcQUsQUiQUl", OP_SUB>; -def VSUBL : Inst<"vsubl", "wdd", "csiUcUsUi", OP_SUBL>; -def VSUBW : Inst<"vsubw", "wwd", "csiUcUsUi", OP_SUBW>; +def VSUB : IOpInst<"vsub", "ddd", + "csilfUcUsUiUlQcQsQiQlQfQUcQUsQUiQUl", OP_SUB>; +def VSUBL : SOpInst<"vsubl", "wdd", "csiUcUsUi", OP_SUBL>; +def VSUBW : SOpInst<"vsubw", "wwd", "csiUcUsUi", OP_SUBW>; def VQSUB : SInst<"vqsub", "ddd", "csilUcUsUiUlQcQsQiQlQUcQUsQUiQUl">; def VHSUB : SInst<"vhsub", "ddd", "csiUcUsUiQcQsQiQUcQUsQUi">; def VSUBHN : IInst<"vsubhn", "hkk", "silUsUiUl">; @@ -159,23 +191,29 @@ def VRSUBHN : IInst<"vrsubhn", "hkk", "silUsUiUl">; //////////////////////////////////////////////////////////////////////////////// // E.3.4 Comparison -def VCEQ : Inst<"vceq", "udd", "csifUcUsUiPcQcQsQiQfQUcQUsQUiQPc", OP_EQ>; -def VCGE : Inst<"vcge", "udd", "csifUcUsUiQcQsQiQfQUcQUsQUi", OP_GE>; -def VCLE : Inst<"vcle", "udd", "csifUcUsUiQcQsQiQfQUcQUsQUi", OP_LE>; -def VCGT : Inst<"vcgt", "udd", "csifUcUsUiQcQsQiQfQUcQUsQUi", OP_GT>; -def VCLT : Inst<"vclt", "udd", "csifUcUsUiQcQsQiQfQUcQUsQUi", OP_LT>; +def VCEQ : IOpInst<"vceq", "udd", "csifUcUsUiPcQcQsQiQfQUcQUsQUiQPc", OP_EQ>; +def VCGE : SOpInst<"vcge", "udd", "csifUcUsUiQcQsQiQfQUcQUsQUi", OP_GE>; +let InstName = "vcge" in +def VCLE : SOpInst<"vcle", "udd", "csifUcUsUiQcQsQiQfQUcQUsQUi", OP_LE>; +def VCGT : SOpInst<"vcgt", "udd", "csifUcUsUiQcQsQiQfQUcQUsQUi", OP_GT>; +let InstName = "vcgt" in +def VCLT : SOpInst<"vclt", "udd", "csifUcUsUiQcQsQiQfQUcQUsQUi", OP_LT>; +let InstName = "vacge" in { def VCAGE : IInst<"vcage", "udd", "fQf">; def VCALE : IInst<"vcale", "udd", "fQf">; +} +let InstName = "vacgt" in { def VCAGT : IInst<"vcagt", "udd", "fQf">; def VCALT : IInst<"vcalt", "udd", "fQf">; +} def VTST : WInst<"vtst", "udd", "csiUcUsUiPcQcQsQiQUcQUsQUiQPc">; //////////////////////////////////////////////////////////////////////////////// // E.3.5 Absolute Difference def VABD : SInst<"vabd", "ddd", "csiUcUsUifQcQsQiQUcQUsQUiQf">; -def VABDL : Inst<"vabdl", "wdd", "csiUcUsUi", OP_ABDL>; -def VABA : Inst<"vaba", "dddd", "csiUcUsUiQcQsQiQUcQUsQUi", OP_ABA>; -def VABAL : Inst<"vabal", "wwdd", "csiUcUsUi", OP_ABAL>; +def VABDL : SOpInst<"vabdl", "wdd", "csiUcUsUi", OP_ABDL>; +def VABA : SOpInst<"vaba", "dddd", "csiUcUsUiQcQsQiQUcQUsQUi", OP_ABA>; +def VABAL : SOpInst<"vabal", "wwdd", "csiUcUsUi", OP_ABAL>; //////////////////////////////////////////////////////////////////////////////// // E.3.6 Max/Min @@ -264,35 +302,43 @@ def VST4_LANE : WInst<"vst4_lane", "vp4i", "QUsQUiQsQiQhQfQPsUcUsUicsihfPcPs">; //////////////////////////////////////////////////////////////////////////////// // E.3.16 Extract lanes from a vector +let InstName = "vmov" in def VGET_LANE : IInst<"vget_lane", "sdi", "UcUsUicsiPcPsfQUcQUsQUiQcQsQiQPcQPsQflUlQlQUl">; //////////////////////////////////////////////////////////////////////////////// // E.3.17 Set lanes within a vector +let InstName = "vmov" in def VSET_LANE : IInst<"vset_lane", "dsdi", "UcUsUicsiPcPsfQUcQUsQUiQcQsQiQPcQPsQflUlQlQUl">; //////////////////////////////////////////////////////////////////////////////// // E.3.18 Initialize a vector from bit pattern -def VCREATE: Inst<"vcreate", "dl", "csihfUcUsUiUlPcPsl", OP_CAST>; +def VCREATE : NoTestOpInst<"vcreate", "dl", "csihfUcUsUiUlPcPsl", OP_CAST>; //////////////////////////////////////////////////////////////////////////////// // E.3.19 Set all lanes to same value -def VDUP_N : Inst<"vdup_n", "ds", - "UcUsUicsiPcPsfQUcQUsQUiQcQsQiQPcQPsQflUlQlQUl", OP_DUP>; -def VMOV_N : Inst<"vmov_n", "ds", - "UcUsUicsiPcPsfQUcQUsQUiQcQsQiQPcQPsQflUlQlQUl", OP_DUP>; -def VDUP_LANE : Inst<"vdup_lane", "dgi", - "UcUsUicsiPcPsfQUcQUsQUiQcQsQiQPcQPsQflUlQlQUl",OP_DUP_LN>; +let InstName = "vmov" in { +def VDUP_N : WOpInst<"vdup_n", "ds", + "UcUsUicsiPcPsfQUcQUsQUiQcQsQiQPcQPsQflUlQlQUl", OP_DUP>; +def VMOV_N : WOpInst<"vmov_n", "ds", + "UcUsUicsiPcPsfQUcQUsQUiQcQsQiQPcQPsQflUlQlQUl", OP_DUP>; +} +let InstName = "" in +def VDUP_LANE: WOpInst<"vdup_lane", "dgi", + "UcUsUicsiPcPsfQUcQUsQUiQcQsQiQPcQPsQflUlQlQUl", + OP_DUP_LN>; //////////////////////////////////////////////////////////////////////////////// // E.3.20 Combining vectors -def VCOMBINE : Inst<"vcombine", "kdd", "csilhfUcUsUiUlPcPs", OP_CONC>; +def VCOMBINE : NoTestOpInst<"vcombine", "kdd", "csilhfUcUsUiUlPcPs", OP_CONC>; //////////////////////////////////////////////////////////////////////////////// // E.3.21 Splitting vectors -def VGET_HIGH : Inst<"vget_high", "dk", "csilhfUcUsUiUlPcPs", OP_HI>; -def VGET_LOW : Inst<"vget_low", "dk", "csilhfUcUsUiUlPcPs", OP_LO>; +let InstName = "vmov" in { +def VGET_HIGH : NoTestOpInst<"vget_high", "dk", "csilhfUcUsUiUlPcPs", OP_HI>; +def VGET_LOW : NoTestOpInst<"vget_low", "dk", "csilhfUcUsUiUlPcPs", OP_LO>; +} //////////////////////////////////////////////////////////////////////////////// // E.3.22 Converting vectors @@ -313,39 +359,46 @@ def VQMOVUN : SInst<"vqmovun", "ek", "sil">; //////////////////////////////////////////////////////////////////////////////// // E.3.23-24 Table lookup, Extended table lookup +let InstName = "vtbl" in { def VTBL1 : WInst<"vtbl1", "ddt", "UccPc">; def VTBL2 : WInst<"vtbl2", "d2t", "UccPc">; def VTBL3 : WInst<"vtbl3", "d3t", "UccPc">; def VTBL4 : WInst<"vtbl4", "d4t", "UccPc">; +} +let InstName = "vtbx" in { def VTBX1 : WInst<"vtbx1", "dddt", "UccPc">; def VTBX2 : WInst<"vtbx2", "dd2t", "UccPc">; def VTBX3 : WInst<"vtbx3", "dd3t", "UccPc">; def VTBX4 : WInst<"vtbx4", "dd4t", "UccPc">; +} //////////////////////////////////////////////////////////////////////////////// // E.3.25 Operations with a scalar value -def VMLA_LANE : Inst<"vmla_lane", "dddgi", "siUsUifQsQiQUsQUiQf", OP_MLA_LN>; -def VMLAL_LANE : Inst<"vmlal_lane", "wwddi", "siUsUi", OP_MLAL_LN>; -def VQDMLAL_LANE : Inst<"vqdmlal_lane", "wwddi", "si", OP_QDMLAL_LN>; -def VMLS_LANE : Inst<"vmls_lane", "dddgi", "siUsUifQsQiQUsQUiQf", OP_MLS_LN>; -def VMLSL_LANE : Inst<"vmlsl_lane", "wwddi", "siUsUi", OP_MLSL_LN>; -def VQDMLSL_LANE : Inst<"vqdmlsl_lane", "wwddi", "si", OP_QDMLSL_LN>; -def VMUL_N : Inst<"vmul_n", "dds", "sifUsUiQsQiQfQUsQUi", OP_MUL_N>; -def VMUL_LANE : Inst<"vmul_lane", "ddgi", "sifUsUiQsQiQfQUsQUi", OP_MUL_LN>; -def VMULL_N : SInst<"vmull_n", "wda", "siUsUi">; -def VMULL_LANE : Inst<"vmull_lane", "wddi", "siUsUi", OP_MULL_LN>; -def VQDMULL_N : SInst<"vqdmull_n", "wda", "si">; -def VQDMULL_LANE : Inst<"vqdmull_lane", "wddi", "si", OP_QDMULL_LN>; -def VQDMULH_N : SInst<"vqdmulh_n", "dda", "siQsQi">; -def VQDMULH_LANE : Inst<"vqdmulh_lane", "ddgi", "siQsQi", OP_QDMULH_LN>; -def VQRDMULH_N : SInst<"vqrdmulh_n", "dda", "siQsQi">; -def VQRDMULH_LANE : Inst<"vqrdmulh_lane", "ddgi", "siQsQi", OP_QRDMULH_LN>; -def VMLA_N : Inst<"vmla_n", "ddda", "siUsUifQsQiQUsQUiQf", OP_MLA_N>; -def VMLAL_N : Inst<"vmlal_n", "wwda", "siUsUi", OP_MLAL_N>; -def VQDMLAL_N : SInst<"vqdmlal_n", "wwda", "si">; -def VMLS_N : Inst<"vmls_n", "ddds", "siUsUifQsQiQUsQUiQf", OP_MLS_N>; -def VMLSL_N : Inst<"vmlsl_n", "wwda", "siUsUi", OP_MLSL_N>; -def VQDMLSL_N : SInst<"vqdmlsl_n", "wwda", "si">; +def VMLA_LANE : IOpInst<"vmla_lane", "dddgi", + "siUsUifQsQiQUsQUiQf", OP_MLA_LN>; +def VMLAL_LANE : SOpInst<"vmlal_lane", "wwddi", "siUsUi", OP_MLAL_LN>; +def VQDMLAL_LANE : SOpInst<"vqdmlal_lane", "wwddi", "si", OP_QDMLAL_LN>; +def VMLS_LANE : IOpInst<"vmls_lane", "dddgi", + "siUsUifQsQiQUsQUiQf", OP_MLS_LN>; +def VMLSL_LANE : SOpInst<"vmlsl_lane", "wwddi", "siUsUi", OP_MLSL_LN>; +def VQDMLSL_LANE : SOpInst<"vqdmlsl_lane", "wwddi", "si", OP_QDMLSL_LN>; +def VMUL_N : IOpInst<"vmul_n", "dds", "sifUsUiQsQiQfQUsQUi", OP_MUL_N>; +def VMUL_LANE : IOpInst<"vmul_lane", "ddgi", + "sifUsUiQsQiQfQUsQUi", OP_MUL_LN>; +def VMULL_N : SInst<"vmull_n", "wda", "siUsUi">; +def VMULL_LANE : SOpInst<"vmull_lane", "wddi", "siUsUi", OP_MULL_LN>; +def VQDMULL_N : SInst<"vqdmull_n", "wda", "si">; +def VQDMULL_LANE : SOpInst<"vqdmull_lane", "wddi", "si", OP_QDMULL_LN>; +def VQDMULH_N : SInst<"vqdmulh_n", "dda", "siQsQi">; +def VQDMULH_LANE : SOpInst<"vqdmulh_lane", "ddgi", "siQsQi", OP_QDMULH_LN>; +def VQRDMULH_N : SInst<"vqrdmulh_n", "dda", "siQsQi">; +def VQRDMULH_LANE : SOpInst<"vqrdmulh_lane", "ddgi", "siQsQi", OP_QRDMULH_LN>; +def VMLA_N : IOpInst<"vmla_n", "ddda", "siUsUifQsQiQUsQUiQf", OP_MLA_N>; +def VMLAL_N : SOpInst<"vmlal_n", "wwda", "siUsUi", OP_MLAL_N>; +def VQDMLAL_N : SInst<"vqdmlal_n", "wwda", "si">; +def VMLS_N : IOpInst<"vmls_n", "ddds", "siUsUifQsQiQUsQUiQf", OP_MLS_N>; +def VMLSL_N : SOpInst<"vmlsl_n", "wwda", "siUsUi", OP_MLSL_N>; +def VQDMLSL_N : SInst<"vqdmlsl_n", "wwda", "si">; //////////////////////////////////////////////////////////////////////////////// // E.3.26 Vector Extract @@ -354,16 +407,16 @@ def VEXT : WInst<"vext", "dddi", //////////////////////////////////////////////////////////////////////////////// // E.3.27 Reverse vector elements -def VREV64 : Inst<"vrev64", "dd", "csiUcUsUiPcPsfQcQsQiQUcQUsQUiQPcQPsQf", +def VREV64 : WOpInst<"vrev64", "dd", "csiUcUsUiPcPsfQcQsQiQUcQUsQUiQPcQPsQf", OP_REV64>; -def VREV32 : Inst<"vrev32", "dd", "csUcUsPcPsQcQsQUcQUsQPcQPs", OP_REV32>; -def VREV16 : Inst<"vrev16", "dd", "cUcPcQcQUcQPc", OP_REV16>; +def VREV32 : WOpInst<"vrev32", "dd", "csUcUsPcPsQcQsQUcQUsQPcQPs", OP_REV32>; +def VREV16 : WOpInst<"vrev16", "dd", "cUcPcQcQUcQPc", OP_REV16>; //////////////////////////////////////////////////////////////////////////////// // E.3.28 Other single operand arithmetic def VABS : SInst<"vabs", "dd", "csifQcQsQiQf">; def VQABS : SInst<"vqabs", "dd", "csiQcQsQi">; -def VNEG : Inst<"vneg", "dd", "csifQcQsQiQf", OP_NEG>; +def VNEG : SOpInst<"vneg", "dd", "csifQcQsQiQf", OP_NEG>; def VQNEG : SInst<"vqneg", "dd", "csiQcQsQi">; def VCLS : SInst<"vcls", "dd", "csiQcQsQi">; def VCLZ : IInst<"vclz", "dd", "csiUcUsUiQcQsQiQUcQUsQUi">; @@ -373,12 +426,13 @@ def VRSQRTE : SInst<"vrsqrte", "dd", "fUiQfQUi">; //////////////////////////////////////////////////////////////////////////////// // E.3.29 Logical operations -def VMVN : Inst<"vmvn", "dd", "csiUcUsUiPcQcQsQiQUcQUsQUiQPc", OP_NOT>; -def VAND : Inst<"vand", "ddd", "csilUcUsUiUlQcQsQiQlQUcQUsQUiQUl", OP_AND>; -def VORR : Inst<"vorr", "ddd", "csilUcUsUiUlQcQsQiQlQUcQUsQUiQUl", OP_OR>; -def VEOR : Inst<"veor", "ddd", "csilUcUsUiUlQcQsQiQlQUcQUsQUiQUl", OP_XOR>; -def VBIC : Inst<"vbic", "ddd", "csilUcUsUiUlQcQsQiQlQUcQUsQUiQUl", OP_ANDN>; -def VORN : Inst<"vorn", "ddd", "csilUcUsUiUlQcQsQiQlQUcQUsQUiQUl", OP_ORN>; +def VMVN : LOpInst<"vmvn", "dd", "csiUcUsUiPcQcQsQiQUcQUsQUiQPc", OP_NOT>; +def VAND : LOpInst<"vand", "ddd", "csilUcUsUiUlQcQsQiQlQUcQUsQUiQUl", OP_AND>; +def VORR : LOpInst<"vorr", "ddd", "csilUcUsUiUlQcQsQiQlQUcQUsQUiQUl", OP_OR>; +def VEOR : LOpInst<"veor", "ddd", "csilUcUsUiUlQcQsQiQlQUcQUsQUiQUl", OP_XOR>; +def VBIC : LOpInst<"vbic", "ddd", "csilUcUsUiUlQcQsQiQlQUcQUsQUiQUl", OP_ANDN>; +def VORN : LOpInst<"vorn", "ddd", "csilUcUsUiUlQcQsQiQlQUcQUsQUiQUl", OP_ORN>; +let isHiddenLInst = 1 in def VBSL : SInst<"vbsl", "dudd", "csilUcUsUiUlfPcPsQcQsQiQlQUcQUsQUiQUlQfQPcQPs">; @@ -391,7 +445,7 @@ def VUZP : WInst<"vuzp", "2dd", "csiUcUsUifPcPsQcQsQiQUcQUsQUiQfQPcQPs">; //////////////////////////////////////////////////////////////////////////////// // E.3.31 Vector reinterpret cast operations def VREINTERPRET - : Inst<"vreinterpret", "dd", + : NoTestOpInst<"vreinterpret", "dd", "csilUcUsUiUlhfPcPsQcQsQiQlQUcQUsQUiQUlQhQfQPcQPs", OP_REINT>; //////////////////////////////////////////////////////////////////////////////// diff --git a/include/clang/Driver/ArgList.h b/include/clang/Driver/ArgList.h index 3967dcc21d1..9db170c4a25 100644 --- a/include/clang/Driver/ArgList.h +++ b/include/clang/Driver/ArgList.h @@ -237,10 +237,19 @@ namespace driver { /// true if the option is present, false if the negation is present, and /// \p Default if neither option is given. If both the option and its /// negation are present, the last one wins. - bool hasFlag(OptSpecifier Pos, OptSpecifier Neg, bool Default=true) const; + bool hasFlag(OptSpecifier Pos, OptSpecifier Neg, bool Default = true) const; + + /// hasFlag - Given an option \p Pos, an alias \p PosAlias and its negative + /// form \p Neg, return true if the option or its alias is present, false if + /// the negation is present, and \p Default if none of the options are + /// given. If multiple options are present, the last one wins. + bool hasFlag(OptSpecifier Pos, OptSpecifier PosAlias, OptSpecifier Neg, + bool Default = true) const; /// AddLastArg - Render only the last argument match \p Id0, if present. void AddLastArg(ArgStringList &Output, OptSpecifier Id0) const; + void AddLastArg(ArgStringList &Output, OptSpecifier Id0, + OptSpecifier Id1) const; /// AddAllArgs - Render all arguments matching the given ids. void AddAllArgs(ArgStringList &Output, OptSpecifier Id0, diff --git a/include/clang/Driver/CC1Options.td b/include/clang/Driver/CC1Options.td index e4dd34509ef..96a50fc5c66 100644 --- a/include/clang/Driver/CC1Options.td +++ b/include/clang/Driver/CC1Options.td @@ -245,9 +245,6 @@ def fmessage_length : Separate<["-"], "fmessage-length">, MetaVarName<"">, def Wno_rewrite_macros : Flag<["-"], "Wno-rewrite-macros">, HelpText<"Silence ObjC rewriting warnings">; -def fwarn_on_spellcheck : Flag<["-"], "fwarn-on-spellcheck">, - HelpText<"Emit warning if spell-check is initiated, for testing">; - //===----------------------------------------------------------------------===// // Frontend Options //===----------------------------------------------------------------------===// diff --git a/include/clang/Driver/Driver.h b/include/clang/Driver/Driver.h index 1330e95ac95..d9053d1972a 100644 --- a/include/clang/Driver/Driver.h +++ b/include/clang/Driver/Driver.h @@ -335,6 +335,7 @@ class Driver { const ToolChain *TC, const char *BoundArch, bool AtTopLevel, + bool MultipleArchs, const char *LinkingOutput, InputInfo &Result) const; @@ -346,11 +347,15 @@ class Driver { /// \param JA - The action of interest. /// \param BaseInput - The original input file that this action was /// triggered by. + /// \param BoundArch - The bound architecture. /// \param AtTopLevel - Whether this is a "top-level" action. + /// \param MultipleArchs - Whether multiple -arch options were supplied. const char *GetNamedOutputPath(Compilation &C, const JobAction &JA, const char *BaseInput, - bool AtTopLevel) const; + const char *BoundArch, + bool AtTopLevel, + bool MultipleArchs) const; /// GetTemporaryPath - Return the pathname of a temporary file to use /// as part of compilation; the file will have the given prefix and suffix. diff --git a/include/clang/Driver/Options.td b/include/clang/Driver/Options.td index 112feb77b1c..3a5358a7e35 100644 --- a/include/clang/Driver/Options.td +++ b/include/clang/Driver/Options.td @@ -181,6 +181,7 @@ def ObjCXX : Flag<["-"], "ObjC++">, Flags<[DriverOption]>, def ObjC : Flag<["-"], "ObjC">, Flags<[DriverOption]>, HelpText<"Treat source input files as Objective-C inputs">; def O : Joined<["-"], "O">, Group, Flags<[CC1Option]>; +def Ofast : Joined<["-"], "Ofast">, Group, Flags<[CC1Option]>; def P : Flag<["-"], "P">, Flags<[CC1Option]>, HelpText<"Disable linemarker output in -E mode">; def Qn : Flag<["-"], "Qn">; @@ -310,6 +311,11 @@ def fastcp : Flag<["-"], "fastcp">, Group; def fastf : Flag<["-"], "fastf">, Group; def fast : Flag<["-"], "fast">, Group; def fasynchronous_unwind_tables : Flag<["-"], "fasynchronous-unwind-tables">, Group; + +def fautolink : Flag <["-"], "fautolink">, Group; +def fno_autolink : Flag <["-"], "fno-autolink">, Group, Flags<[NoForward, CC1Option]>, + HelpText<"Disable generation of linker directives for automatic library linking">; + def fblocks : Flag<["-"], "fblocks">, Group, Flags<[CC1Option]>, HelpText<"Enable the 'blocks' language feature">; def fbootclasspath_EQ : Joined<["-"], "fbootclasspath=">, Group; @@ -327,9 +333,12 @@ def fcatch_undefined_behavior : Flag<["-"], "fcatch-undefined-behavior">, Group< def fclasspath_EQ : Joined<["-"], "fclasspath=">, Group; def fcolor_diagnostics : Flag<["-"], "fcolor-diagnostics">, Group, Flags<[CC1Option]>, HelpText<"Use colors in diagnostics">; +def fdiagnostics_color : Flag<["-"], "fdiagnostics-color">, Group; +def fdiagnostics_color_EQ : Joined<["-"], "fdiagnostics-color=">, Group; def fcomment_block_commands : CommaJoined<["-"], "fcomment-block-commands=">, Group, Flags<[CC1Option]>, HelpText<"Treat each comma separated argument in as a documentation comment block command">, MetaVarName<"">; +def fparse_all_comments : Flag<["-"], "fparse-all-comments">, Group, Flags<[CC1Option]>; def fcommon : Flag<["-"], "fcommon">, Group; def fcompile_resource_EQ : Joined<["-"], "fcompile-resource=">, Group; def fconstant_cfstrings : Flag<["-"], "fconstant-cfstrings">, Group; @@ -403,7 +412,7 @@ def fsanitize_EQ : CommaJoined<["-"], "fsanitize=">, Group, def fno_sanitize_EQ : CommaJoined<["-"], "fno-sanitize=">, Group; def fsanitize_address_zero_base_shadow : Flag<["-"], "fsanitize-address-zero-base-shadow">, Group, Flags<[CC1Option]>, - HelpText<"Make AddressSanitizer map shadow memory" + HelpText<"Make AddressSanitizer map shadow memory " "at zero offset">; def fno_sanitize_address_zero_base_shadow : Flag<["-"], "fno-sanitize-address-zero-base-shadow">, Group; @@ -507,10 +516,6 @@ def fmodules_prune_after : Joined<["-"], "fmodules-prune-after=">, Group; def fmodules : Flag <["-"], "fmodules">, Group, Flags<[NoForward,CC1Option]>, HelpText<"Enable the 'modules' language feature">; -def fmodules_autolink : Flag <["-"], "fmodules-autolink">, Group, Flags<[NoForward,CC1Option]>, - HelpText<"Enable autolinking of the libraries for imported modules">; -def fno_modules_autolink : Flag <["-"], "fno-modules-autolink">, Group, - HelpText<"Disable autolinking of the libraries for imported modules">; def fmodules_ignore_macro : Joined<["-"], "fmodules-ignore-macro=">, Group, Flags<[CC1Option]>, HelpText<"Ignore the definition of the given macro when building and loading modules">; def fretain_comments_from_system_headers : Flag<["-"], "fretain-comments-from-system-headers">, Group, Flags<[CC1Option]>; @@ -536,6 +541,7 @@ def fno_builtin : Flag<["-"], "fno-builtin">, Group, Flags<[CC1Option]> def fno_caret_diagnostics : Flag<["-"], "fno-caret-diagnostics">, Group, Flags<[CC1Option]>; def fno_color_diagnostics : Flag<["-"], "fno-color-diagnostics">, Group; +def fno_diagnostics_color : Flag<["-"], "fno-diagnostics-color">, Group; def fno_common : Flag<["-"], "fno-common">, Group, Flags<[CC1Option]>, HelpText<"Compile common globals like normal definitions">; def fno_constant_cfstrings : Flag<["-"], "fno-constant-cfstrings">, Group, @@ -699,6 +705,9 @@ def fno_tree_vectorize : Flag<["-"], "fno-tree-vectorize">, Alias def fslp_vectorize : Flag<["-"], "fslp-vectorize">, Group, HelpText<"Enable the superword-level parallelism vectorization passes">; def fno_slp_vectorize : Flag<["-"], "fno-slp-vectorize">, Group; +def fslp_vectorize_aggressive : Flag<["-"], "fslp-vectorize-aggressive">, Group, + HelpText<"Enable the BB vectorization passes">; +def fno_slp_vectorize_aggressive : Flag<["-"], "fno-slp-vectorize-aggressive">, Group; def ftree_slp_vectorize : Flag<["-"], "ftree-slp-vectorize">, Alias; def fno_tree_slp_vectorize : Flag<["-"], "fno-tree-slp-vectorize">, Alias; def Wlarge_by_value_copy_def : Flag<["-"], "Wlarge-by-value-copy">, @@ -928,6 +937,7 @@ def msoft_float : Flag<["-"], "msoft-float">, Group, Flags<[CC1Option]> HelpText<"Use software floating point">; def mno_implicit_float : Flag<["-"], "mno-implicit-float">, Group, HelpText<"Don't generate implicit floating point instructions">; +def mimplicit_float : Flag<["-"], "mimplicit-float">, Group; def msse2 : Flag<["-"], "msse2">, Group; def msse3 : Flag<["-"], "msse3">, Group; def msse4a : Flag<["-"], "msse4a">, Group; @@ -954,12 +964,16 @@ def mprfchw : Flag<["-"], "mprfchw">, Group; def mrdseed : Flag<["-"], "mrdseed">, Group; def mips16 : Flag<["-"], "mips16">, Group; def mno_mips16 : Flag<["-"], "mno-mips16">, Group; +def mmicromips : Flag<["-"], "mmicromips">, Group; +def mno_micromips : Flag<["-"], "mno-micromips">, Group; def mxgot : Flag<["-"], "mxgot">, Group; def mno_xgot : Flag<["-"], "mno-xgot">, Group; def mdsp : Flag<["-"], "mdsp">, Group; def mno_dsp : Flag<["-"], "mno-dsp">, Group; def mdspr2 : Flag<["-"], "mdspr2">, Group; def mno_dspr2 : Flag<["-"], "mno-dspr2">, Group; +def msingle_float : Flag<["-"], "msingle-float">, Group; +def mdouble_float : Flag<["-"], "mdouble-float">, Group; def mips32 : Flag<["-"], "mips32">, Group, HelpText<"Equivalent to -march=mips32">, Flags<[HelpHidden]>; def mips32r2 : Flag<["-"], "mips32r2">, Group, diff --git a/include/clang/Driver/ToolChain.h b/include/clang/Driver/ToolChain.h index ae9e397644e..aae3d79936b 100644 --- a/include/clang/Driver/ToolChain.h +++ b/include/clang/Driver/ToolChain.h @@ -82,6 +82,9 @@ class ToolChain { static void addExternCSystemInclude(const ArgList &DriverArgs, ArgStringList &CC1Args, const Twine &Path); + static void addExternCSystemIncludeIfExists(const ArgList &DriverArgs, + ArgStringList &CC1Args, + const Twine &Path); static void addSystemIncludes(const ArgList &DriverArgs, ArgStringList &CC1Args, ArrayRef Paths); @@ -195,9 +198,13 @@ class ToolChain { /// \brief Test whether this toolchain defaults to PIC. virtual bool isPICDefault() const = 0; - /// \brief Tests whether this toolchain forces its default for PIC or non-PIC. - /// If this returns true, any PIC related flags should be ignored and instead - /// the result of \c isPICDefault() is used exclusively. + /// \brief Test whether this toolchain defaults to PIE. + virtual bool isPIEDefault() const = 0; + + /// \brief Tests whether this toolchain forces its default for PIC, PIE or + /// non-PIC. If this returns true, any PIC related flags should be ignored + /// and instead the results of \c isPICDefault() and \c isPIEDefault() are + /// used exclusively. virtual bool isPICDefaultForced() const = 0; /// SupportsProfiling - Does this tool chain support -pg. diff --git a/include/clang/Format/Format.h b/include/clang/Format/Format.h index d6cc114e3cd..5304dc7ba22 100644 --- a/include/clang/Format/Format.h +++ b/include/clang/Format/Format.h @@ -57,9 +57,6 @@ struct FormatStyle { /// instead of \c A> for LS_Cpp03. LanguageStandard Standard; - /// \brief If \c true, analyze the formatted file for C++03 compatibility. - bool DeriveBackwardsCompatibility; - /// \brief Indent case labels one level from the switch statement. /// /// When false, use the same indentation level as for the switch statement. @@ -91,6 +88,10 @@ struct FormatStyle { /// \brief Add a space in front of an Objective-C protocol list, i.e. use /// Foo instead of Foo. bool ObjCSpaceBeforeProtocolList; + + /// \brief If \c true, aligns escaped newlines as far left as possible. + /// Otherwise puts them into the right-most column. + bool AlignEscapedNewlinesLeft; }; /// \brief Returns a format style complying with the LLVM coding standards: @@ -105,6 +106,10 @@ FormatStyle getGoogleStyle(); /// http://www.chromium.org/developers/coding-style. FormatStyle getChromiumStyle(); +/// \brief Returns a format style complying with Mozilla's style guide: +/// https://developer.mozilla.org/en-US/docs/Developer_Guide/Coding_Style. +FormatStyle getMozillaStyle(); + /// \brief Reformats the given \p Ranges in the token stream coming out of /// \c Lex. /// diff --git a/include/clang/Frontend/ChainedDiagnosticConsumer.h b/include/clang/Frontend/ChainedDiagnosticConsumer.h index ce2b242296e..b7dc7c7b529 100644 --- a/include/clang/Frontend/ChainedDiagnosticConsumer.h +++ b/include/clang/Frontend/ChainedDiagnosticConsumer.h @@ -60,12 +60,6 @@ class ChainedDiagnosticConsumer : public DiagnosticConsumer { Primary->HandleDiagnostic(DiagLevel, Info); Secondary->HandleDiagnostic(DiagLevel, Info); } - - DiagnosticConsumer *clone(DiagnosticsEngine &Diags) const { - return new ChainedDiagnosticConsumer(Primary->clone(Diags), - Secondary->clone(Diags)); - } - }; } // end namspace clang diff --git a/include/clang/Frontend/ChainedIncludesSource.h b/include/clang/Frontend/ChainedIncludesSource.h index e14580ed69b..aa30460040a 100644 --- a/include/clang/Frontend/ChainedIncludesSource.h +++ b/include/clang/Frontend/ChainedIncludesSource.h @@ -26,9 +26,9 @@ class ChainedIncludesSource : public ExternalSemaSource { static ChainedIncludesSource *create(CompilerInstance &CI); -private: ExternalSemaSource &getFinalReader() const { return *FinalReader; } +private: std::vector CIs; OwningPtr FinalReader; diff --git a/include/clang/Frontend/CodeGenOptions.def b/include/clang/Frontend/CodeGenOptions.def index 1c0b9fa99e7..f6e2472cb9b 100644 --- a/include/clang/Frontend/CodeGenOptions.def +++ b/include/clang/Frontend/CodeGenOptions.def @@ -28,6 +28,7 @@ CODEGENOPT(Name, Bits, Default) CODEGENOPT(Name, Bits, Default) #endif +CODEGENOPT(Autolink , 1, 1) ///< -fno-autolink CODEGENOPT(AsmVerbose , 1, 0) ///< -dA, -fverbose-asm. CODEGENOPT(ObjCAutoRefCountExceptions , 1, 0) ///< Whether ARC should be EH-safe. CODEGENOPT(CoverageExtraChecksum, 1, 0) ///< Whether we need a second checksum for functions in GCNO files. @@ -120,8 +121,6 @@ VALUE_CODEGENOPT(StackAlignment , 32, 0) ///< Overrides default stack CODEGENOPT(DebugColumnInfo, 1, 0) ///< Whether or not to use column information ///< in debug info. -CODEGENOPT(ModulesAutolink, 1, 0) ///< Whether to auto-link imported modules - /// The user specified number of registers to be used for integral arguments, /// or 0 if unspecified. VALUE_CODEGENOPT(NumRegisterParameters, 32, 0) diff --git a/include/clang/Frontend/CodeGenOptions.h b/include/clang/Frontend/CodeGenOptions.h index d0bbf30918c..db6b4186731 100644 --- a/include/clang/Frontend/CodeGenOptions.h +++ b/include/clang/Frontend/CodeGenOptions.h @@ -137,7 +137,7 @@ class CodeGenOptions : public CodeGenOptionsBase { #include "clang/Frontend/CodeGenOptions.def" RelocationModel = "pic"; - memcpy(CoverageVersion, "*204", 4); + memcpy(CoverageVersion, "402*", 4); } }; diff --git a/include/clang/Frontend/CompilerInstance.h b/include/clang/Frontend/CompilerInstance.h index 0d674629fd4..dbd76066b94 100644 --- a/include/clang/Frontend/CompilerInstance.h +++ b/include/clang/Frontend/CompilerInstance.h @@ -427,6 +427,7 @@ class CompilerInstance : public ModuleLoader { /// { ASTReader *getModuleManager() const { return ModuleManager; } + void setModuleManager(ASTReader *Reader) { ModuleManager = Reader; } /// } /// @name Code Completion @@ -492,12 +493,8 @@ class CompilerInstance : public ModuleLoader { /// /// \param ShouldOwnClient If Client is non-NULL, specifies whether /// the diagnostic object should take ownership of the client. - /// - /// \param ShouldCloneClient If Client is non-NULL, specifies whether that - /// client should be cloned. void createDiagnostics(DiagnosticConsumer *Client = 0, - bool ShouldOwnClient = true, - bool ShouldCloneClient = true); + bool ShouldOwnClient = true); /// Create a DiagnosticsEngine object with a the TextDiagnosticPrinter. /// @@ -521,7 +518,6 @@ class CompilerInstance : public ModuleLoader { createDiagnostics(DiagnosticOptions *Opts, DiagnosticConsumer *Client = 0, bool ShouldOwnClient = true, - bool ShouldCloneClient = true, const CodeGenOptions *CodeGenOpts = 0); /// Create the file manager and replace any existing one with it. diff --git a/include/clang/Frontend/LogDiagnosticPrinter.h b/include/clang/Frontend/LogDiagnosticPrinter.h index 0c700a7671b..e8a6bb357ca 100644 --- a/include/clang/Frontend/LogDiagnosticPrinter.h +++ b/include/clang/Frontend/LogDiagnosticPrinter.h @@ -70,8 +70,6 @@ class LogDiagnosticPrinter : public DiagnosticConsumer { virtual void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info); - - DiagnosticConsumer *clone(DiagnosticsEngine &Diags) const; }; } // end namespace clang diff --git a/include/clang/Frontend/TextDiagnosticBuffer.h b/include/clang/Frontend/TextDiagnosticBuffer.h index 6f1c0e8aeaf..93ac299da30 100644 --- a/include/clang/Frontend/TextDiagnosticBuffer.h +++ b/include/clang/Frontend/TextDiagnosticBuffer.h @@ -45,8 +45,6 @@ class TextDiagnosticBuffer : public DiagnosticConsumer { /// FlushDiagnostics - Flush the buffered diagnostics to an given /// diagnostic engine. void FlushDiagnostics(DiagnosticsEngine &Diags) const; - - virtual DiagnosticConsumer *clone(DiagnosticsEngine &Diags) const; }; } // end namspace clang diff --git a/include/clang/Frontend/TextDiagnosticPrinter.h b/include/clang/Frontend/TextDiagnosticPrinter.h index 470438e7bd9..dc8047066c1 100644 --- a/include/clang/Frontend/TextDiagnosticPrinter.h +++ b/include/clang/Frontend/TextDiagnosticPrinter.h @@ -50,7 +50,6 @@ class TextDiagnosticPrinter : public DiagnosticConsumer { void BeginSourceFile(const LangOptions &LO, const Preprocessor *PP); void EndSourceFile(); void HandleDiagnostic(DiagnosticsEngine::Level Level, const Diagnostic &Info); - DiagnosticConsumer *clone(DiagnosticsEngine &Diags) const; }; } // end namespace clang diff --git a/include/clang/Frontend/VerifyDiagnosticConsumer.h b/include/clang/Frontend/VerifyDiagnosticConsumer.h index 06a3b24f3a3..95d7752517d 100644 --- a/include/clang/Frontend/VerifyDiagnosticConsumer.h +++ b/include/clang/Frontend/VerifyDiagnosticConsumer.h @@ -61,6 +61,18 @@ class FileEntry; /// The line number may be absolute (as above), or relative to the current /// line by prefixing the number with either '+' or '-'. /// +/// If the diagnostic is generated in a separate file, for example in a shared +/// header file, it may be beneficial to be able to declare the file in which +/// the diagnostic will appear, rather than placing the expected-* directive in +/// the actual file itself. This can be done using the following syntax: +/// +/// \code +/// // expected-error@path/include.h:15 {{error message}} +/// \endcode +/// +/// The path can be absolute or relative and the same search paths will be used +/// as for #include directives. +/// /// The simple syntax above allows each specification to match exactly one /// error. You can use the extended syntax to customize this. The extended /// syntax is "expected- {{diag text}}", where \ is one of @@ -254,8 +266,6 @@ class VerifyDiagnosticConsumer: public DiagnosticConsumer, virtual void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info); - - virtual DiagnosticConsumer *clone(DiagnosticsEngine &Diags) const; }; } // end namspace clang diff --git a/lib/Lex/MacroArgs.h b/include/clang/Lex/MacroArgs.h similarity index 100% rename from lib/Lex/MacroArgs.h rename to include/clang/Lex/MacroArgs.h diff --git a/include/clang/Lex/ModuleMap.h b/include/clang/Lex/ModuleMap.h index 1c9c67314ae..dc75f1803cb 100644 --- a/include/clang/Lex/ModuleMap.h +++ b/include/clang/Lex/ModuleMap.h @@ -161,7 +161,7 @@ class ModuleMap { /// \param LangOpts Language options for this translation unit. /// /// \param Target The target for this translation unit. - ModuleMap(FileManager &FileMgr, const DiagnosticConsumer &DC, + ModuleMap(FileManager &FileMgr, DiagnosticConsumer &DC, const LangOptions &LangOpts, const TargetInfo *Target, HeaderSearch &HeaderInfo); @@ -177,7 +177,6 @@ class ModuleMap { void setBuiltinIncludeDir(const DirectoryEntry *Dir) { BuiltinIncludeDir = Dir; } - const DirectoryEntry *getBuiltinIncludeDir() { return BuiltinIncludeDir; } /// \brief Retrieve the module that owns the given header file, if any. /// diff --git a/include/clang/Lex/PPCallbacks.h b/include/clang/Lex/PPCallbacks.h index 96359a2aa3a..db2ecd247f4 100644 --- a/include/clang/Lex/PPCallbacks.h +++ b/include/clang/Lex/PPCallbacks.h @@ -27,6 +27,7 @@ namespace clang { class Token; class IdentifierInfo; class MacroDirective; + class MacroArgs; /// \brief This interface provides a way to observe the actions of the /// preprocessor as it does its thing. @@ -159,10 +160,31 @@ class PPCallbacks { const std::string &Str) { } + /// \brief Callback invoked when a \#pragma clang __debug directive is read. + /// \param Loc The location of the debug directive. + /// \param DebugType The identifier following __debug. + virtual void PragmaDebug(SourceLocation Loc, StringRef DebugType) { + } + + /// \brief Determines the kind of \#pragma invoking a call to PragmaMessage. + enum PragmaMessageKind { + /// \brief \#pragma message has been invoked. + PMK_Message, + + /// \brief \#pragma GCC warning has been invoked. + PMK_Warning, + + /// \brief \#pragma GCC error has been invoked. + PMK_Error + }; + /// \brief Callback invoked when a \#pragma message directive is read. /// \param Loc The location of the message directive. + /// \param Namespace The namespace of the message directive. + /// \param Kind The type of the message directive. /// \param Str The text of the message directive. - virtual void PragmaMessage(SourceLocation Loc, StringRef Str) { + virtual void PragmaMessage(SourceLocation Loc, StringRef Namespace, + PragmaMessageKind Kind, StringRef Str) { } /// \brief Callback invoked when a \#pragma gcc dianostic push directive @@ -185,7 +207,7 @@ class PPCallbacks { /// \brief Called by Preprocessor::HandleMacroExpandedIdentifier when a /// macro invocation is found. virtual void MacroExpands(const Token &MacroNameTok, const MacroDirective *MD, - SourceRange Range) { + SourceRange Range, const MacroArgs *Args) { } /// \brief Hook called whenever a macro definition is seen. @@ -330,9 +352,10 @@ class PPChainedCallbacks : public PPCallbacks { Second->PragmaComment(Loc, Kind, Str); } - virtual void PragmaMessage(SourceLocation Loc, StringRef Str) { - First->PragmaMessage(Loc, Str); - Second->PragmaMessage(Loc, Str); + virtual void PragmaMessage(SourceLocation Loc, StringRef Namespace, + PragmaMessageKind Kind, StringRef Str) { + First->PragmaMessage(Loc, Namespace, Kind, Str); + Second->PragmaMessage(Loc, Namespace, Kind, Str); } virtual void PragmaDiagnosticPush(SourceLocation Loc, @@ -354,9 +377,9 @@ class PPChainedCallbacks : public PPCallbacks { } virtual void MacroExpands(const Token &MacroNameTok, const MacroDirective *MD, - SourceRange Range) { - First->MacroExpands(MacroNameTok, MD, Range); - Second->MacroExpands(MacroNameTok, MD, Range); + SourceRange Range, const MacroArgs *Args) { + First->MacroExpands(MacroNameTok, MD, Range, Args); + Second->MacroExpands(MacroNameTok, MD, Range, Args); } virtual void MacroDefined(const Token &MacroNameTok, const MacroDirective *MD) { diff --git a/include/clang/Lex/PreprocessingRecord.h b/include/clang/Lex/PreprocessingRecord.h index b13b2be7f31..db74352aa81 100644 --- a/include/clang/Lex/PreprocessingRecord.h +++ b/include/clang/Lex/PreprocessingRecord.h @@ -559,7 +559,7 @@ namespace clang { private: virtual void MacroExpands(const Token &Id, const MacroDirective *MD, - SourceRange Range); + SourceRange Range, const MacroArgs *Args); virtual void MacroDefined(const Token &Id, const MacroDirective *MD); virtual void MacroUndefined(const Token &Id, const MacroDirective *MD); virtual void InclusionDirective(SourceLocation HashLoc, diff --git a/include/clang/Lex/Preprocessor.h b/include/clang/Lex/Preprocessor.h index 7a912ec0f81..c5981777cd2 100644 --- a/include/clang/Lex/Preprocessor.h +++ b/include/clang/Lex/Preprocessor.h @@ -397,6 +397,14 @@ class Preprocessor : public RefCountedBase { /// allocation. MacroInfoChain *MICache; + struct DeserializedMacroInfoChain { + MacroInfo MI; + unsigned OwningModuleID; // MUST be immediately after the MacroInfo object + // so it can be accessed by MacroInfo::getOwningModuleID(). + DeserializedMacroInfoChain *Next; + }; + DeserializedMacroInfoChain *DeserialMIChainHead; + public: Preprocessor(IntrusiveRefCntPtr PPOpts, DiagnosticsEngine &diags, LangOptions &opts, @@ -1444,8 +1452,6 @@ class Preprocessor : public RefCountedBase { void HandlePragmaPoison(Token &PoisonTok); void HandlePragmaSystemHeader(Token &SysHeaderTok); void HandlePragmaDependency(Token &DependencyTok); - void HandlePragmaComment(Token &CommentTok); - void HandlePragmaMessage(Token &MessageTok); void HandlePragmaPushMacro(Token &Tok); void HandlePragmaPopMacro(Token &Tok); void HandlePragmaIncludeAlias(Token &Tok); diff --git a/include/clang/Parse/CMakeLists.txt b/include/clang/Parse/CMakeLists.txt index d1ff2abfee6..d20708e58c5 100644 --- a/include/clang/Parse/CMakeLists.txt +++ b/include/clang/Parse/CMakeLists.txt @@ -1,3 +1,8 @@ +clang_tablegen(AttrExprArgs.inc -gen-clang-attr-expr-args-list + -I ${CMAKE_CURRENT_SOURCE_DIR}/../../ + SOURCE ../Basic/Attr.td + TARGET ClangAttrExprArgs) + clang_tablegen(AttrLateParsed.inc -gen-clang-attr-late-parsed-list -I ${CMAKE_CURRENT_SOURCE_DIR}/../../ SOURCE ../Basic/Attr.td diff --git a/include/clang/Parse/Makefile b/include/clang/Parse/Makefile index 296892c5b6e..fb63175ba9b 100644 --- a/include/clang/Parse/Makefile +++ b/include/clang/Parse/Makefile @@ -1,11 +1,17 @@ CLANG_LEVEL := ../../.. TD_SRC_DIR = $(PROJ_SRC_DIR)/../Basic -BUILT_SOURCES = AttrLateParsed.inc +BUILT_SOURCES = AttrExprArgs.inc AttrLateParsed.inc TABLEGEN_INC_FILES_COMMON = 1 include $(CLANG_LEVEL)/Makefile +$(ObjDir)/AttrExprArgs.inc.tmp : $(TD_SRC_DIR)/Attr.td $(CLANG_TBLGEN) \ + $(ObjDir)/.dir + $(Echo) "Building Clang attribute expression arguments table with tblgen" + $(Verb) $(ClangTableGen) -gen-clang-attr-expr-args-list -o $(call SYSPATH, $@) \ + -I $(PROJ_SRC_DIR)/../../ $< + $(ObjDir)/AttrLateParsed.inc.tmp : $(TD_SRC_DIR)/Attr.td $(CLANG_TBLGEN) \ $(ObjDir)/.dir $(Echo) "Building Clang attribute late-parsed table with tblgen" diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index 8cc60a29dfa..1029a90c55c 100644 --- a/include/clang/Parse/Parser.h +++ b/include/clang/Parse/Parser.h @@ -149,6 +149,7 @@ class Parser : public CodeCompletionHandler { OwningPtr OpenCLExtensionHandler; OwningPtr CommentSemaHandler; OwningPtr OpenMPHandler; + OwningPtr MSCommentHandler; /// Whether the '>' token acts as an operator or not. This will be /// true except when we are parsing an expression within a C++ @@ -172,6 +173,25 @@ class Parser : public CodeCompletionHandler { /// The "depth" of the template parameters currently being parsed. unsigned TemplateParameterDepth; + /// \brief RAII class that manages the template parameter depth. + class TemplateParameterDepthRAII { + unsigned &Depth; + unsigned AddedLevels; + public: + explicit TemplateParameterDepthRAII(unsigned &Depth) + : Depth(Depth), AddedLevels(0) {} + + ~TemplateParameterDepthRAII() { + Depth -= AddedLevels; + } + + void operator++() { + ++Depth; + ++AddedLevels; + } + unsigned getDepth() const { return Depth; } + }; + /// Factory object for creating AttributeList objects. AttributeFactory AttrFactory; @@ -422,6 +442,10 @@ class Parser : public CodeCompletionHandler { /// #pragma OPENCL EXTENSION... void HandlePragmaOpenCLExtension(); + /// \brief Handle the annotation token produced for + /// #pragma clang __debug captured + StmtResult HandlePragmaCaptured(); + /// GetLookAheadToken - This peeks ahead N tokens and returns that token /// without consuming any tokens. LookAhead(0) returns 'Tok', LookAhead(1) /// returns the token after Tok, etc. @@ -454,19 +478,13 @@ class Parser : public CodeCompletionHandler { /// \brief Read an already-translated primary expression out of an annotation /// token. static ExprResult getExprAnnotation(Token &Tok) { - if (Tok.getAnnotationValue()) - return ExprResult((Expr *)Tok.getAnnotationValue()); - - return ExprResult(true); + return ExprResult::getFromOpaquePointer(Tok.getAnnotationValue()); } /// \brief Set the primary expression corresponding to the given annotation /// token. static void setExprAnnotation(Token &Tok, ExprResult ER) { - if (ER.isInvalid()) - Tok.setAnnotationValue(0); - else - Tok.setAnnotationValue(ER.get()); + Tok.setAnnotationValue(ER.getAsOpaquePointer()); } public: @@ -1204,6 +1222,11 @@ class Parser : public CodeCompletionHandler { // Expr that doesn't include commas. ExprResult ParseAssignmentExpression(TypeCastState isTypeCast = NotTypeCast); + ExprResult ParseMSAsmIdentifier(llvm::SmallVectorImpl &LineToks, + unsigned &NumLineToksConsumed, + void *Info, + bool IsUnevaluated); + private: ExprResult ParseExpressionWithLeadingAt(SourceLocation AtLoc); diff --git a/include/clang/Rewrite/Frontend/FixItRewriter.h b/include/clang/Rewrite/Frontend/FixItRewriter.h index 04c04a25f54..423f0667cde 100644 --- a/include/clang/Rewrite/Frontend/FixItRewriter.h +++ b/include/clang/Rewrite/Frontend/FixItRewriter.h @@ -121,8 +121,6 @@ class FixItRewriter : public DiagnosticConsumer { /// \brief Emit a diagnostic via the adapted diagnostic client. void Diag(SourceLocation Loc, unsigned DiagID); - - DiagnosticConsumer *clone(DiagnosticsEngine &Diags) const; }; } diff --git a/include/clang/Sema/AttributeList.h b/include/clang/Sema/AttributeList.h index 0f0d2185b0c..d5f31779a63 100644 --- a/include/clang/Sema/AttributeList.h +++ b/include/clang/Sema/AttributeList.h @@ -96,6 +96,10 @@ class AttributeList { // TODO: This should really be called ParsedAttribute /// type_tag_for_datatype attribute. unsigned IsTypeTagForDatatype : 1; + /// True if this has extra information associated with a + /// Microsoft __delcspec(property) attribute. + unsigned IsProperty : 1; + unsigned AttrKind : 8; /// \brief The location of the 'unavailable' keyword in an @@ -134,6 +138,11 @@ class AttributeList { // TODO: This should really be called ParsedAttribute unsigned LayoutCompatible : 1; unsigned MustBeNull : 1; }; + struct PropertyData { + IdentifierInfo *GetterId, *SetterId; + PropertyData(IdentifierInfo *getterId, IdentifierInfo *setterId) + : GetterId(getterId), SetterId(setterId) {} + }; private: TypeTagForDatatypeData &getTypeTagForDatatypeDataSlot() { @@ -152,6 +161,16 @@ class AttributeList { // TODO: This should really be called ParsedAttribute return *reinterpret_cast(this + 1); } + PropertyData &getPropertyDataBuffer() { + assert(IsProperty); + return *reinterpret_cast(this + 1); + } + + const PropertyData &getPropertyDataBuffer() const { + assert(IsProperty); + return *reinterpret_cast(this + 1); + } + AttributeList(const AttributeList &) LLVM_DELETED_FUNCTION; void operator=(const AttributeList &) LLVM_DELETED_FUNCTION; void operator delete(void *) LLVM_DELETED_FUNCTION; @@ -169,7 +188,8 @@ class AttributeList { // TODO: This should really be called ParsedAttribute AttrRange(attrRange), ScopeLoc(scopeLoc), ParmLoc(parmLoc), EllipsisLoc(ellipsisLoc), NumArgs(numArgs), SyntaxUsed(syntaxUsed), Invalid(false), UsedAsTypeAttr(false), IsAvailability(false), - IsTypeTagForDatatype(false), NextInPosition(0), NextInPool(0) { + IsTypeTagForDatatype(false), IsProperty(false), NextInPosition(0), + NextInPool(0) { if (numArgs) memcpy(getArgsBuffer(), args, numArgs * sizeof(Expr*)); AttrKind = getKind(getName(), getScopeName(), syntaxUsed); } @@ -188,7 +208,7 @@ class AttributeList { // TODO: This should really be called ParsedAttribute AttrRange(attrRange), ScopeLoc(scopeLoc), ParmLoc(parmLoc), EllipsisLoc(), NumArgs(0), SyntaxUsed(syntaxUsed), Invalid(false), UsedAsTypeAttr(false), IsAvailability(true), - IsTypeTagForDatatype(false), + IsTypeTagForDatatype(false), IsProperty(false), UnavailableLoc(unavailable), MessageExpr(messageExpr), NextInPosition(0), NextInPool(0) { new (&getAvailabilitySlot(IntroducedSlot)) AvailabilityChange(introduced); @@ -208,7 +228,8 @@ class AttributeList { // TODO: This should really be called ParsedAttribute AttrRange(attrRange), ScopeLoc(scopeLoc), ParmLoc(argumentKindLoc), EllipsisLoc(), NumArgs(0), SyntaxUsed(syntaxUsed), Invalid(false), UsedAsTypeAttr(false), IsAvailability(false), - IsTypeTagForDatatype(true), NextInPosition(NULL), NextInPool(NULL) { + IsTypeTagForDatatype(true), IsProperty(false), NextInPosition(NULL), + NextInPool(NULL) { TypeTagForDatatypeData &ExtraData = getTypeTagForDatatypeDataSlot(); new (&ExtraData.MatchingCType) ParsedType(matchingCType); ExtraData.LayoutCompatible = layoutCompatible; @@ -225,11 +246,28 @@ class AttributeList { // TODO: This should really be called ParsedAttribute AttrRange(attrRange), ScopeLoc(scopeLoc), ParmLoc(parmLoc), EllipsisLoc(), NumArgs(1), SyntaxUsed(syntaxUsed), Invalid(false), UsedAsTypeAttr(false), IsAvailability(false), - IsTypeTagForDatatype(false), NextInPosition(0), NextInPool(0) { + IsTypeTagForDatatype(false), IsProperty(false), NextInPosition(0), + NextInPool(0) { new (&getTypeBuffer()) ParsedType(typeArg); AttrKind = getKind(getName(), getScopeName(), syntaxUsed); } + /// Constructor for microsoft __declspec(property) attribute. + AttributeList(IdentifierInfo *attrName, SourceRange attrRange, + IdentifierInfo *scopeName, SourceLocation scopeLoc, + IdentifierInfo *parmName, SourceLocation parmLoc, + IdentifierInfo *getterId, IdentifierInfo *setterId, + Syntax syntaxUsed) + : AttrName(attrName), ScopeName(scopeName), ParmName(parmName), + AttrRange(attrRange), ScopeLoc(scopeLoc), ParmLoc(parmLoc), + SyntaxUsed(syntaxUsed), + Invalid(false), UsedAsTypeAttr(false), IsAvailability(false), + IsTypeTagForDatatype(false), IsProperty(true), NextInPosition(0), + NextInPool(0) { + new (&getPropertyDataBuffer()) PropertyData(getterId, setterId); + AttrKind = getKind(getName(), getScopeName(), syntaxUsed); + } + friend class AttributePool; friend class AttributeFactory; @@ -253,6 +291,11 @@ class AttributeList { // TODO: This should really be called ParsedAttribute IdentifierInfo *getParameterName() const { return ParmName; } SourceLocation getParameterLoc() const { return ParmLoc; } + /// Is this the Microsoft __declspec(property) attribute? + bool isDeclspecPropertyAttribute() const { + return IsProperty; + } + bool isAlignasAttribute() const { // FIXME: Use a better mechanism to determine this. return getKind() == AT_Aligned && SyntaxUsed == AS_Keyword; @@ -379,6 +422,11 @@ class AttributeList { // TODO: This should really be called ParsedAttribute return getTypeBuffer(); } + const PropertyData &getPropertyData() const { + assert(isDeclspecPropertyAttribute() && "Not a __delcspec(property) attribute"); + return getPropertyDataBuffer(); + } + /// \brief Get an index into the attribute spelling list /// defined in Attr.td. This index is used by an attribute /// to pretty print itself. @@ -402,6 +450,10 @@ class AttributeFactory { TypeTagForDatatypeAllocSize = sizeof(AttributeList) + (sizeof(AttributeList::TypeTagForDatatypeData) + sizeof(void *) - 1) + / sizeof(void*) * sizeof(void*), + PropertyAllocSize = + sizeof(AttributeList) + + (sizeof(AttributeList::PropertyData) + sizeof(void *) - 1) / sizeof(void*) * sizeof(void*) }; @@ -548,6 +600,20 @@ class AttributePool { parmName, parmLoc, typeArg, syntaxUsed)); } + + AttributeList *createPropertyAttribute( + IdentifierInfo *attrName, SourceRange attrRange, + IdentifierInfo *scopeName, SourceLocation scopeLoc, + IdentifierInfo *parmName, SourceLocation parmLoc, + IdentifierInfo *getterId, IdentifierInfo *setterId, + AttributeList::Syntax syntaxUsed) { + void *memory = allocate(AttributeFactory::PropertyAllocSize); + return add(new (memory) AttributeList(attrName, attrRange, + scopeName, scopeLoc, + parmName, parmLoc, + getterId, setterId, + syntaxUsed)); + } }; /// addAttributeLists - Add two AttributeLists together @@ -703,6 +769,21 @@ class ParsedAttributes { return attr; } + /// Add microsoft __delspec(property) attribute. + AttributeList * + addNewPropertyAttr(IdentifierInfo *attrName, SourceRange attrRange, + IdentifierInfo *scopeName, SourceLocation scopeLoc, + IdentifierInfo *parmName, SourceLocation parmLoc, + IdentifierInfo *getterId, IdentifierInfo *setterId, + AttributeList::Syntax syntaxUsed) { + AttributeList *attr = + pool.createPropertyAttribute(attrName, attrRange, scopeName, scopeLoc, + parmName, parmLoc, getterId, setterId, + syntaxUsed); + add(attr); + return attr; + } + AttributeList *addNewInteger(ASTContext &C, IdentifierInfo *name, SourceLocation loc, int arg) { AttributeList *attr = diff --git a/include/clang/Sema/DeclSpec.h b/include/clang/Sema/DeclSpec.h index 5b90784dcf0..059919a35bb 100644 --- a/include/clang/Sema/DeclSpec.h +++ b/include/clang/Sema/DeclSpec.h @@ -227,6 +227,14 @@ class DeclSpec { SCS_mutable }; + // Import thread storage class specifier enumeration and constants. + // These can be combined with SCS_extern and SCS_static. + typedef ThreadStorageClassSpecifier TSCS; + static const TSCS TSCS_unspecified = clang::TSCS_unspecified; + static const TSCS TSCS___thread = clang::TSCS___thread; + static const TSCS TSCS_thread_local = clang::TSCS_thread_local; + static const TSCS TSCS__Thread_local = clang::TSCS__Thread_local; + // Import type specifier width enumeration and constants. typedef TypeSpecifierWidth TSW; static const TSW TSW_unspecified = clang::TSW_unspecified; @@ -272,6 +280,7 @@ class DeclSpec { static const TST TST_typeofType = clang::TST_typeofType; static const TST TST_typeofExpr = clang::TST_typeofExpr; static const TST TST_decltype = clang::TST_decltype; + static const TST TST_decltype_auto = clang::TST_decltype_auto; static const TST TST_underlyingType = clang::TST_underlyingType; static const TST TST_auto = clang::TST_auto; static const TST TST_unknown_anytype = clang::TST_unknown_anytype; @@ -310,7 +319,7 @@ class DeclSpec { private: // storage-class-specifier /*SCS*/unsigned StorageClassSpec : 3; - unsigned SCS_thread_specified : 1; + /*TSCS*/unsigned ThreadStorageClassSpec : 2; unsigned SCS_extern_in_linkage_spec : 1; // type-specifier @@ -362,7 +371,7 @@ class DeclSpec { // the setting was synthesized. SourceRange Range; - SourceLocation StorageClassSpecLoc, SCS_threadLoc; + SourceLocation StorageClassSpecLoc, ThreadStorageClassSpecLoc; SourceLocation TSWLoc, TSCLoc, TSSLoc, TSTLoc, AltiVecLoc; /// TSTNameLoc - If TypeSpecType is any of class, enum, struct, union, /// typename, then this is the location of the named type (if present); @@ -398,7 +407,7 @@ class DeclSpec { DeclSpec(AttributeFactory &attrFactory) : StorageClassSpec(SCS_unspecified), - SCS_thread_specified(false), + ThreadStorageClassSpec(TSCS_unspecified), SCS_extern_in_linkage_spec(false), TypeSpecWidth(TSW_unspecified), TypeSpecComplex(TSC_unspecified), @@ -428,21 +437,25 @@ class DeclSpec { } // storage-class-specifier SCS getStorageClassSpec() const { return (SCS)StorageClassSpec; } - bool isThreadSpecified() const { return SCS_thread_specified; } + TSCS getThreadStorageClassSpec() const { + return (TSCS)ThreadStorageClassSpec; + } bool isExternInLinkageSpec() const { return SCS_extern_in_linkage_spec; } void setExternInLinkageSpec(bool Value) { SCS_extern_in_linkage_spec = Value; } SourceLocation getStorageClassSpecLoc() const { return StorageClassSpecLoc; } - SourceLocation getThreadSpecLoc() const { return SCS_threadLoc; } + SourceLocation getThreadStorageClassSpecLoc() const { + return ThreadStorageClassSpecLoc; + } void ClearStorageClassSpecs() { - StorageClassSpec = DeclSpec::SCS_unspecified; - SCS_thread_specified = false; + StorageClassSpec = DeclSpec::SCS_unspecified; + ThreadStorageClassSpec = DeclSpec::TSCS_unspecified; SCS_extern_in_linkage_spec = false; - StorageClassSpecLoc = SourceLocation(); - SCS_threadLoc = SourceLocation(); + StorageClassSpecLoc = SourceLocation(); + ThreadStorageClassSpecLoc = SourceLocation(); } // type-specifier @@ -487,6 +500,10 @@ class DeclSpec { SourceRange getTypeofParensRange() const { return TypeofParensRange; } void setTypeofParensRange(SourceRange range) { TypeofParensRange = range; } + bool containsPlaceholderType() const { + return TypeSpecType == TST_auto || TypeSpecType == TST_decltype_auto; + } + /// \brief Turn a type-specifier-type into a string like "_Bool" or "union". static const char *getSpecifierName(DeclSpec::TST T); static const char *getSpecifierName(DeclSpec::TQ Q); @@ -494,6 +511,7 @@ class DeclSpec { static const char *getSpecifierName(DeclSpec::TSC C); static const char *getSpecifierName(DeclSpec::TSW W); static const char *getSpecifierName(DeclSpec::SCS S); + static const char *getSpecifierName(DeclSpec::TSCS S); // type-qualifiers @@ -570,8 +588,8 @@ class DeclSpec { /// diagnostics to be ignored when desired. bool SetStorageClassSpec(Sema &S, SCS SC, SourceLocation Loc, const char *&PrevSpec, unsigned &DiagID); - bool SetStorageClassSpecThread(SourceLocation Loc, const char *&PrevSpec, - unsigned &DiagID); + bool SetStorageClassSpecThread(TSCS TSC, SourceLocation Loc, + const char *&PrevSpec, unsigned &DiagID); bool SetTypeSpecWidth(TSW W, SourceLocation Loc, const char *&PrevSpec, unsigned &DiagID); bool SetTypeSpecComplex(TSC C, SourceLocation Loc, const char *&PrevSpec, @@ -1480,8 +1498,9 @@ class Declarator { CXXNewContext, // C++ new-expression. CXXCatchContext, // C++ catch exception-declaration ObjCCatchContext, // Objective-C catch exception-declaration - BlockLiteralContext, // Block literal declarator. + BlockLiteralContext, // Block literal declarator. LambdaExprContext, // Lambda-expression declarator. + ConversionIdContext, // C++ conversion-type-id. TrailingReturnContext, // C++11 trailing-type-specifier. TemplateTypeArgContext, // Template type argument. AliasDeclContext, // C++11 alias-declaration. @@ -1657,6 +1676,7 @@ class Declarator { case ObjCCatchContext: case BlockLiteralContext: case LambdaExprContext: + case ConversionIdContext: case TemplateTypeArgContext: case TrailingReturnContext: return true; @@ -1689,6 +1709,7 @@ class Declarator { case ObjCResultContext: case BlockLiteralContext: case LambdaExprContext: + case ConversionIdContext: case TemplateTypeArgContext: case TrailingReturnContext: return false; @@ -1738,6 +1759,7 @@ class Declarator { case AliasTemplateContext: case BlockLiteralContext: case LambdaExprContext: + case ConversionIdContext: case TemplateTypeArgContext: case TrailingReturnContext: return false; @@ -1920,6 +1942,7 @@ class Declarator { case ObjCCatchContext: case BlockLiteralContext: case LambdaExprContext: + case ConversionIdContext: case TemplateTypeArgContext: case TrailingReturnContext: return false; diff --git a/include/clang/Sema/Initialization.h b/include/clang/Sema/Initialization.h index 8459be16f4c..58781ac628c 100644 --- a/include/clang/Sema/Initialization.h +++ b/include/clang/Sema/Initialization.h @@ -75,7 +75,10 @@ class InitializedEntity { EK_ComplexElement, /// \brief The entity being initialized is the field that captures a /// variable in a lambda. - EK_LambdaCapture + EK_LambdaCapture, + /// \brief The entity being initialized is the initializer for a compound + /// literal. + EK_CompoundLiteralInit }; private: @@ -118,8 +121,8 @@ class InitializedEntity { /// low bit indicating whether the parameter is "consumed". uintptr_t Parameter; - /// \brief When Kind == EK_Temporary, the type source information for - /// the temporary. + /// \brief When Kind == EK_Temporary or EK_CompoundLiteralInit, the type + /// source information for the temporary. TypeSourceInfo *TypeInfo; struct LN LocAndNRVO; @@ -287,7 +290,16 @@ class InitializedEntity { SourceLocation Loc) { return InitializedEntity(Var, Field, Loc); } - + + /// \brief Create the entity for a compound literal initializer. + static InitializedEntity InitializeCompoundLiteralInit(TypeSourceInfo *TSI) { + InitializedEntity Result(EK_CompoundLiteralInit, SourceLocation(), + TSI->getType()); + Result.TypeInfo = TSI; + return Result; + } + + /// \brief Determine the kind of initialization. EntityKind getKind() const { return Kind; } @@ -302,7 +314,7 @@ class InitializedEntity { /// \brief Retrieve complete type-source information for the object being /// constructed, if known. TypeSourceInfo *getTypeSourceInfo() const { - if (Kind == EK_Temporary) + if (Kind == EK_Temporary || Kind == EK_CompoundLiteralInit) return TypeInfo; return 0; @@ -594,6 +606,8 @@ class InitializationSequence { SK_QualificationConversionXValue, /// \brief Perform a qualification conversion, producing an lvalue. SK_QualificationConversionLValue, + /// \brief Perform a load from a glvalue, producing an rvalue. + SK_LValueToRValue, /// \brief Perform an implicit conversion sequence. SK_ConversionSequence, /// \brief Perform list-initialization without a constructor @@ -777,13 +791,10 @@ class InitializationSequence { /// \param Kind the kind of initialization being performed. /// /// \param Args the argument(s) provided for initialization. - /// - /// \param NumArgs the number of arguments provided for initialization. InitializationSequence(Sema &S, const InitializedEntity &Entity, const InitializationKind &Kind, - Expr **Args, - unsigned NumArgs); + MultiExprArg Args); ~InitializationSequence(); @@ -821,7 +832,7 @@ class InitializationSequence { bool Diagnose(Sema &S, const InitializedEntity &Entity, const InitializationKind &Kind, - Expr **Args, unsigned NumArgs); + ArrayRef Args); /// \brief Determine the kind of initialization sequence computed. enum SequenceKind getKind() const { return SequenceKind; } @@ -911,6 +922,12 @@ class InitializationSequence { void AddQualificationConversionStep(QualType Ty, ExprValueKind Category); + /// \brief Add a new step that performs a load of the given type. + /// + /// Although the term "LValueToRValue" is conventional, this applies to both + /// lvalues and xvalues. + void AddLValueToRValueStep(QualType Ty); + /// \brief Add a new step that applies an implicit conversion sequence. void AddConversionSequenceStep(const ImplicitConversionSequence &ICS, QualType T); diff --git a/include/clang/Sema/ObjCMethodList.h b/include/clang/Sema/ObjCMethodList.h index 225c13776c5..94e380721de 100644 --- a/include/clang/Sema/ObjCMethodList.h +++ b/include/clang/Sema/ObjCMethodList.h @@ -14,6 +14,8 @@ #ifndef LLVM_CLANG_SEMA_OBJC_METHOD_LIST_H #define LLVM_CLANG_SEMA_OBJC_METHOD_LIST_H +#include "llvm/ADT/PointerIntPair.h" + namespace clang { class ObjCMethodDecl; @@ -21,16 +23,17 @@ class ObjCMethodDecl; /// ObjCMethodList - a linked list of methods with different signatures. struct ObjCMethodList { ObjCMethodDecl *Method; - ObjCMethodList *Next; - - ObjCMethodList() { - Method = 0; - Next = 0; - } - ObjCMethodList(ObjCMethodDecl *M, ObjCMethodList *C) { - Method = M; - Next = C; - } + /// \brief The next list object and 2 bits for extra info. + llvm::PointerIntPair NextAndExtraBits; + + ObjCMethodList() : Method(0) { } + ObjCMethodList(ObjCMethodDecl *M, ObjCMethodList *C) + : Method(M), NextAndExtraBits(C, 0) { } + + ObjCMethodList *getNext() const { return NextAndExtraBits.getPointer(); } + unsigned getBits() const { return NextAndExtraBits.getInt(); } + void setNext(ObjCMethodList *L) { NextAndExtraBits.setPointer(L); } + void setBits(unsigned B) { NextAndExtraBits.setInt(B); } }; } diff --git a/include/clang/Sema/Ownership.h b/include/clang/Sema/Ownership.h index e064b91f78f..c3d1f4e0b7d 100644 --- a/include/clang/Sema/Ownership.h +++ b/include/clang/Sema/Ownership.h @@ -207,6 +207,15 @@ namespace clang { assert((PtrWithInvalid & 0x01) == 0 && "Badly aligned pointer"); return *this; } + + // For types where we can fit a flag in with the pointer, provide + // conversions to/from pointer type. + static ActionResult getFromOpaquePointer(void *P) { + ActionResult Result; + Result.PtrWithInvalid = (uintptr_t)P; + return Result; + } + void *getAsOpaquePointer() const { return (void*)PtrWithInvalid; } }; /// An opaque type for threading parsed type information through the diff --git a/include/clang/Sema/ScopeInfo.h b/include/clang/Sema/ScopeInfo.h index 2295bf437cb..b232b59d3c1 100644 --- a/include/clang/Sema/ScopeInfo.h +++ b/include/clang/Sema/ScopeInfo.h @@ -16,6 +16,7 @@ #define LLVM_CLANG_SEMA_SCOPE_INFO_H #include "clang/AST/Type.h" +#include "clang/Basic/CapturedStmt.h" #include "clang/Basic/PartialDiagnostic.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallVector.h" @@ -24,9 +25,11 @@ namespace clang { class Decl; class BlockDecl; +class CapturedDecl; class CXXMethodDecl; class ObjCPropertyDecl; class IdentifierInfo; +class ImplicitParamDecl; class LabelDecl; class ReturnStmt; class Scope; @@ -73,7 +76,8 @@ class FunctionScopeInfo { enum ScopeKind { SK_Function, SK_Block, - SK_Lambda + SK_Lambda, + SK_CapturedRegion }; public: @@ -319,7 +323,8 @@ class FunctionScopeInfo { class CapturingScopeInfo : public FunctionScopeInfo { public: enum ImplicitCaptureStyle { - ImpCap_None, ImpCap_LambdaByval, ImpCap_LambdaByref, ImpCap_Block + ImpCap_None, ImpCap_LambdaByval, ImpCap_LambdaByref, ImpCap_Block, + ImpCap_CapturedRegion }; ImplicitCaptureStyle ImpCaptureStyle; @@ -461,7 +466,8 @@ class CapturingScopeInfo : public FunctionScopeInfo { } static bool classof(const FunctionScopeInfo *FSI) { - return FSI->Kind == SK_Block || FSI->Kind == SK_Lambda; + return FSI->Kind == SK_Block || FSI->Kind == SK_Lambda + || FSI->Kind == SK_CapturedRegion; } }; @@ -492,6 +498,46 @@ class BlockScopeInfo : public CapturingScopeInfo { } }; +/// \brief Retains information about a captured region. +class CapturedRegionScopeInfo: public CapturingScopeInfo { +public: + /// \brief The CapturedDecl for this statement. + CapturedDecl *TheCapturedDecl; + /// \brief The captured record type. + RecordDecl *TheRecordDecl; + /// \brief This is the enclosing scope of the captured region. + Scope *TheScope; + /// \brief The implicit parameter for the captured variables. + ImplicitParamDecl *ContextParam; + /// \brief The kind of captured region. + CapturedRegionKind CapRegionKind; + + CapturedRegionScopeInfo(DiagnosticsEngine &Diag, Scope *S, CapturedDecl *CD, + RecordDecl *RD, ImplicitParamDecl *Context, + CapturedRegionKind K) + : CapturingScopeInfo(Diag, ImpCap_CapturedRegion), + TheCapturedDecl(CD), TheRecordDecl(RD), TheScope(S), + ContextParam(Context), CapRegionKind(K) + { + Kind = SK_CapturedRegion; + } + + virtual ~CapturedRegionScopeInfo(); + + /// \brief A descriptive name for the kind of captured region this is. + StringRef getRegionName() const { + switch (CapRegionKind) { + case CR_Default: + return "default captured statement"; + } + llvm_unreachable("Invalid captured region kind!"); + } + + static bool classof(const FunctionScopeInfo *FSI) { + return FSI->Kind == SK_CapturedRegion; + } +}; + class LambdaScopeInfo : public CapturingScopeInfo { public: /// \brief The class that describes the lambda. diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 5b93e513eaf..d7c80f2e4f9 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -37,6 +37,7 @@ #include "clang/Sema/LocInfoType.h" #include "clang/Sema/ObjCMethodList.h" #include "clang/Sema/Ownership.h" +#include "clang/Sema/ScopeInfo.h" #include "clang/Sema/TypoCorrection.h" #include "clang/Sema/Weak.h" #include "llvm/ADT/ArrayRef.h" @@ -45,6 +46,7 @@ #include "llvm/ADT/SetVector.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/MC/MCParser/MCAsmParser.h" #include #include @@ -65,6 +67,7 @@ namespace clang { class ArrayType; class AttributeList; class BlockDecl; + class CapturedDecl; class CXXBasePath; class CXXBasePaths; class CXXBindTemporaryExpr; @@ -174,6 +177,7 @@ namespace clang { namespace sema { class AccessedEntity; class BlockScopeInfo; + class CapturedRegionScopeInfo; class CapturingScopeInfo; class CompoundScopeInfo; class DelayedDiagnostic; @@ -203,6 +207,17 @@ class Sema { static bool mightHaveNonExternalLinkage(const DeclaratorDecl *FD); + static bool + shouldLinkPossiblyHiddenDecl(const NamedDecl *Old, const NamedDecl *New) { + // We are about to link these. It is now safe to compute the linkage of + // the new decl. If the new decl has external linkage, we will + // link it with the hidden decl (which also has external linkage) and + // it will keep having external linkage. If it has internal linkage, we + // will not link it. Since it has no previous decls, it will remain + // with internal linkage. + return !Old->isHidden() || New->hasExternalLinkage(); + } + public: typedef OpaquePtr DeclGroupPtrTy; typedef OpaquePtr TemplateTy; @@ -585,6 +600,10 @@ class Sema { /// have been declared. bool GlobalNewDeleteDeclared; + /// A flag to indicate that we're in a context that permits abstract + /// references to fields. This is really a + bool AllowAbstractFieldReference; + /// \brief Describes how the expressions currently being parsed are /// evaluated at run-time, if at all. enum ExpressionEvaluationContext { @@ -595,6 +614,11 @@ class Sema { /// run time. Unevaluated, + /// \brief The current expression occurs within an unevaluated + /// operand that unconditionally permits abstract references to + /// fields, such as a SIZE operator in MS-style inline assembly. + UnevaluatedAbstract, + /// \brief The current context is "potentially evaluated" in C++11 terms, /// but the expression is evaluated at compile-time (like the values of /// cases in a switch statment). @@ -674,6 +698,10 @@ class Sema { LambdaMangle = new LambdaMangleContext; return *LambdaMangle; } + + bool isUnevaluated() const { + return Context == Unevaluated || Context == UnevaluatedAbstract; + } }; /// A stack of expression evaluation contexts. @@ -756,6 +784,8 @@ class Sema { /// We need to maintain a list, since selectors can have differing signatures /// across classes. In Cocoa, this happens to be extremely uncommon (only 1% /// of selectors are "overloaded"). + /// At the head of the list it is recorded whether there were 0, 1, or >= 2 + /// methods inside categories with a particular selector. GlobalMethodPool MethodPool; /// Method selectors used in a \@selector expression. Used for implementation @@ -804,6 +834,9 @@ class Sema { bool OldFPContractState : 1; }; + typedef llvm::MCAsmParserSemaCallback::InlineAsmIdentifierInfo + InlineAsmIdentifierInfo; + public: Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer, TranslationUnitKind TUKind = TU_Complete, @@ -905,6 +938,9 @@ class Sema { void PushFunctionScope(); void PushBlockScope(Scope *BlockScope, BlockDecl *Block); void PushLambdaScope(CXXRecordDecl *Lambda, CXXMethodDecl *CallOperator); + void PushCapturedRegionScope(Scope *RegionScope, CapturedDecl *CD, + RecordDecl *RD, + CapturedRegionKind K); void PopFunctionScopeInfo(const sema::AnalysisBasedWarnings::Policy *WP =0, const Decl *D = 0, const BlockExpr *blkExpr = 0); @@ -925,6 +961,9 @@ class Sema { /// \brief Retrieve the current lambda expression, if any. sema::LambdaScopeInfo *getCurLambda(); + /// \brief Retrieve the current captured region, if any. + sema::CapturedRegionScopeInfo *getCurCapturedRegion(); + /// WeakTopLevelDeclDecls - access to \#pragma weak-generated Decls SmallVector &WeakTopLevelDecls() { return WeakTopLevelDecl; } @@ -1222,7 +1261,7 @@ class Sema { bool isSimpleTypeSpecifier(tok::TokenKind Kind) const; - ParsedType getTypeName(IdentifierInfo &II, SourceLocation NameLoc, + ParsedType getTypeName(const IdentifierInfo &II, SourceLocation NameLoc, Scope *S, CXXScopeSpec *SS = 0, bool isClassName = false, bool HasTrailingDot = false, @@ -1374,6 +1413,7 @@ class Sema { MultiTemplateParamsArg TemplateParamLists); // Returns true if the variable declaration is a redeclaration bool CheckVariableDeclaration(VarDecl *NewVD, LookupResult &Previous); + void CheckVariableDeclarationType(VarDecl *NewVD); void CheckCompleteVariableDeclaration(VarDecl *var); void MaybeSuggestAddingStaticToDecl(const FunctionDecl *D); void ActOnStartFunctionDeclarator(); @@ -1575,6 +1615,12 @@ class Sema { Declarator &D, Expr *BitfieldWidth, InClassInitStyle InitStyle, AccessSpecifier AS); + MSPropertyDecl *HandleMSProperty(Scope *S, RecordDecl *TagD, + SourceLocation DeclStart, + Declarator &D, Expr *BitfieldWidth, + InClassInitStyle InitStyle, + AccessSpecifier AS, + AttributeList *MSPropertyAttr); FieldDecl *CheckFieldDecl(DeclarationName Name, QualType T, TypeSourceInfo *TInfo, @@ -1649,7 +1695,7 @@ class Sema { SourceLocation EqualLoc, Expr *Val); void ActOnEnumBody(SourceLocation EnumLoc, SourceLocation LBraceLoc, SourceLocation RBraceLoc, Decl *EnumDecl, - Decl **Elements, unsigned NumElements, + ArrayRef Elements, Scope *S, AttributeList *Attr); DeclContext *getContainingDC(DeclContext *DC); @@ -1849,7 +1895,7 @@ class Sema { bool IsNoReturnConversion(QualType FromType, QualType ToType, QualType &ResultTy); bool DiagnoseMultipleUserDefinedConversion(Expr *From, QualType ToType); - + bool isSameOrCompatibleFunctionType(CanQualType Param, CanQualType Arg); ExprResult PerformMoveOrCopyInitialization(const InitializedEntity &Entity, const VarDecl *NRVOCandidate, @@ -1979,7 +2025,7 @@ class Sema { void AddMethodCandidate(DeclAccessPair FoundDecl, QualType ObjectType, Expr::Classification ObjectClassification, - Expr **Args, unsigned NumArgs, + ArrayRef Args, OverloadCandidateSet& CandidateSet, bool SuppressUserConversion = false); void AddMethodCandidate(CXXMethodDecl *Method, @@ -2021,18 +2067,16 @@ class Sema { Expr *Object, ArrayRef Args, OverloadCandidateSet& CandidateSet); void AddMemberOperatorCandidates(OverloadedOperatorKind Op, - SourceLocation OpLoc, - Expr **Args, unsigned NumArgs, + SourceLocation OpLoc, ArrayRef Args, OverloadCandidateSet& CandidateSet, SourceRange OpRange = SourceRange()); void AddBuiltinCandidate(QualType ResultTy, QualType *ParamTys, - Expr **Args, unsigned NumArgs, + ArrayRef Args, OverloadCandidateSet& CandidateSet, bool IsAssignmentOperator = false, unsigned NumContextualBoolArguments = 0); void AddBuiltinOperatorCandidates(OverloadedOperatorKind Op, - SourceLocation OpLoc, - Expr **Args, unsigned NumArgs, + SourceLocation OpLoc, ArrayRef Args, OverloadCandidateSet& CandidateSet); void AddArgumentDependentLookupCandidates(DeclarationName Name, bool Operator, SourceLocation Loc, @@ -2443,8 +2487,7 @@ class Sema { /// DiagnoseUnimplementedProperties - This routine warns on those properties /// which must be implemented by this implementation. void DiagnoseUnimplementedProperties(Scope *S, ObjCImplDecl* IMPDecl, - ObjCContainerDecl *CDecl, - const SelectorSet &InsMap); + ObjCContainerDecl *CDecl); /// DefaultSynthesizeProperties - This routine default synthesizes all /// properties which must be synthesized in the class's \@implementation. @@ -2750,6 +2793,13 @@ class Sema { StmtResult ActOnContinueStmt(SourceLocation ContinueLoc, Scope *CurScope); StmtResult ActOnBreakStmt(SourceLocation BreakLoc, Scope *CurScope); + void ActOnCapturedRegionStart(SourceLocation Loc, Scope *CurScope, + CapturedRegionKind Kind, unsigned NumParams); + StmtResult ActOnCapturedRegionEnd(Stmt *S); + void ActOnCapturedRegionError(); + RecordDecl *CreateCapturedStmtRecordDecl(CapturedDecl *&CD, + SourceLocation Loc, + unsigned NumParams); const VarDecl *getCopyElisionCandidate(QualType ReturnType, Expr *E, bool AllowFunctionParameters); @@ -2763,13 +2813,21 @@ class Sema { Expr *AsmString, MultiExprArg Clobbers, SourceLocation RParenLoc); - NamedDecl *LookupInlineAsmIdentifier(StringRef Name, SourceLocation Loc, - unsigned &Length, unsigned &Size, - unsigned &Type, bool &IsVarDecl); + ExprResult LookupInlineAsmIdentifier(CXXScopeSpec &SS, + SourceLocation TemplateKWLoc, + UnqualifiedId &Id, + InlineAsmIdentifierInfo &Info, + bool IsUnevaluatedContext); bool LookupInlineAsmField(StringRef Base, StringRef Member, unsigned &Offset, SourceLocation AsmLoc); StmtResult ActOnMSAsmStmt(SourceLocation AsmLoc, SourceLocation LBraceLoc, - ArrayRef AsmToks, SourceLocation EndLoc); + ArrayRef AsmToks, + StringRef AsmString, + unsigned NumOutputs, unsigned NumInputs, + ArrayRef Constraints, + ArrayRef Clobbers, + ArrayRef Exprs, + SourceLocation EndLoc); VarDecl *BuildObjCExceptionDecl(TypeSourceInfo *TInfo, QualType ExceptionType, SourceLocation StartLoc, @@ -3006,7 +3064,7 @@ class Sema { bool DiagnoseEmptyLookup(Scope *S, CXXScopeSpec &SS, LookupResult &R, CorrectionCandidateCallback &CCC, TemplateArgumentListInfo *ExplicitTemplateArgs = 0, - ArrayRef Args = ArrayRef()); + ArrayRef Args = None); ExprResult LookupInObjCMethod(LookupResult &LookUp, Scope *S, IdentifierInfo *II, @@ -3542,7 +3600,7 @@ class Sema { const QualType *data() const { return Exceptions.data(); } /// \brief Integrate another called method into the collected data. - void CalledDecl(SourceLocation CallLoc, CXXMethodDecl *Method); + void CalledDecl(SourceLocation CallLoc, const CXXMethodDecl *Method); /// \brief Integrate an invoked expression into the collected data. void CalledExpr(Expr *E); @@ -3606,7 +3664,7 @@ class Sema { /// \brief Determine what sort of exception specification an inheriting /// constructor of a class will have. ImplicitExceptionSpecification - ComputeInheritingCtorExceptionSpec(CXXMethodDecl *MD); + ComputeInheritingCtorExceptionSpec(CXXConstructorDecl *CD); /// \brief Evaluate the implicit exception specification for a defaulted /// special member function. @@ -4450,8 +4508,7 @@ class Sema { CXXCtorInitializer *Initializer); bool SetCtorInitializers(CXXConstructorDecl *Constructor, bool AnyErrors, - ArrayRef Initializers = - ArrayRef()); + ArrayRef Initializers = None); void SetIvarInitializers(ObjCImplementationDecl *ObjCImplementation); @@ -5582,7 +5639,8 @@ class Sema { TemplateArgumentListInfo *ExplicitTemplateArgs, QualType ArgFunctionType, FunctionDecl *&Specialization, - sema::TemplateDeductionInfo &Info); + sema::TemplateDeductionInfo &Info, + bool InOverloadResolution = false); TemplateDeductionResult DeduceTemplateArguments(FunctionTemplateDecl *FunctionTemplate, @@ -5594,7 +5652,8 @@ class Sema { DeduceTemplateArguments(FunctionTemplateDecl *FunctionTemplate, TemplateArgumentListInfo *ExplicitTemplateArgs, FunctionDecl *&Specialization, - sema::TemplateDeductionInfo &Info); + sema::TemplateDeductionInfo &Info, + bool InOverloadResolution = false); /// \brief Result type of DeduceAutoType. enum DeduceAutoResult { @@ -5604,8 +5663,17 @@ class Sema { }; DeduceAutoResult DeduceAutoType(TypeSourceInfo *AutoType, Expr *&Initializer, - TypeSourceInfo *&Result); + QualType &Result); + DeduceAutoResult DeduceAutoType(TypeLoc AutoTypeLoc, Expr *&Initializer, + QualType &Result); + QualType SubstAutoType(QualType TypeWithAuto, QualType Replacement); void DiagnoseAutoDeductionFailure(VarDecl *VDecl, Expr *Init); + bool DeduceReturnType(FunctionDecl *FD, SourceLocation Loc, + bool Diagnose = true); + + bool DeduceFunctionTypeFromReturnExpr(FunctionDecl *FD, + SourceLocation ReturnLoc, + Expr *&RetExpr, AutoType *AT); FunctionTemplateDecl *getMoreSpecializedTemplate(FunctionTemplateDecl *FT1, FunctionTemplateDecl *FT2, @@ -5958,7 +6026,7 @@ class Sema { bool isUnevaluatedContext() const { assert(!ExprEvalContexts.empty() && "Must be in an expression evaluation context"); - return ExprEvalContexts.back().Context == Sema::Unevaluated; + return ExprEvalContexts.back().isUnevaluated(); } /// \brief RAII class used to determine whether SFINAE has @@ -7316,7 +7384,7 @@ class Sema { bool CheckFunctionCall(FunctionDecl *FDecl, CallExpr *TheCall, const FunctionProtoType *Proto); bool CheckObjCMethodCall(ObjCMethodDecl *Method, SourceLocation loc, - Expr **Args, unsigned NumArgs); + ArrayRef Args); bool CheckBlockCall(NamedDecl *NDecl, CallExpr *TheCall, const FunctionProtoType *Proto); void CheckConstructorCall(FunctionDecl *FDecl, diff --git a/include/clang/Sema/Template.h b/include/clang/Sema/Template.h index 492e5800bda..f9481c6c0c2 100644 --- a/include/clang/Sema/Template.h +++ b/include/clang/Sema/Template.h @@ -40,10 +40,9 @@ namespace clang { /// list will contain a template argument list (int) at depth 0 and a /// template argument list (17) at depth 1. class MultiLevelTemplateArgumentList { - public: - typedef std::pair ArgList; - - private: + /// \brief The template argument list at a certain template depth + typedef ArrayRef ArgList; + /// \brief The template argument lists, stored from the innermost template /// argument list (first) to the outermost template argument list (last). SmallVector TemplateArgumentLists; @@ -65,8 +64,8 @@ namespace clang { /// \brief Retrieve the template argument at a given depth and index. const TemplateArgument &operator()(unsigned Depth, unsigned Index) const { assert(Depth < TemplateArgumentLists.size()); - assert(Index < TemplateArgumentLists[getNumLevels() - Depth - 1].second); - return TemplateArgumentLists[getNumLevels() - Depth - 1].first[Index]; + assert(Index < TemplateArgumentLists[getNumLevels() - Depth - 1].size()); + return TemplateArgumentLists[getNumLevels() - Depth - 1][Index]; } /// \brief Determine whether there is a non-NULL template argument at the @@ -76,7 +75,7 @@ namespace clang { bool hasTemplateArgument(unsigned Depth, unsigned Index) const { assert(Depth < TemplateArgumentLists.size()); - if (Index >= TemplateArgumentLists[getNumLevels() - Depth - 1].second) + if (Index >= TemplateArgumentLists[getNumLevels() - Depth - 1].size()) return false; return !(*this)(Depth, Index).isNull(); @@ -86,26 +85,32 @@ namespace clang { void setArgument(unsigned Depth, unsigned Index, TemplateArgument Arg) { assert(Depth < TemplateArgumentLists.size()); - assert(Index < TemplateArgumentLists[getNumLevels() - Depth - 1].second); + assert(Index < TemplateArgumentLists[getNumLevels() - Depth - 1].size()); const_cast( - TemplateArgumentLists[getNumLevels() - Depth - 1].first[Index]) + TemplateArgumentLists[getNumLevels() - Depth - 1][Index]) = Arg; } /// \brief Add a new outermost level to the multi-level template argument /// list. void addOuterTemplateArguments(const TemplateArgumentList *TemplateArgs) { - TemplateArgumentLists.push_back(ArgList(TemplateArgs->data(), - TemplateArgs->size())); + addOuterTemplateArguments(ArgList(TemplateArgs->data(), + TemplateArgs->size())); } /// \brief Add a new outmost level to the multi-level template argument /// list. void addOuterTemplateArguments(const TemplateArgument *Args, unsigned NumArgs) { - TemplateArgumentLists.push_back(ArgList(Args, NumArgs)); + addOuterTemplateArguments(ArgList(Args, NumArgs)); } - + + /// \brief Add a new outmost level to the multi-level template argument + /// list. + void addOuterTemplateArguments(ArgList Args) { + TemplateArgumentLists.push_back(Args); + } + /// \brief Retrieve the innermost template argument list. const ArgList &getInnermost() const { return TemplateArgumentLists.front(); @@ -187,10 +192,10 @@ namespace clang { /// this template instantiation. Sema &SemaRef; - typedef llvm::DenseMap > - LocalDeclsMap; - + typedef llvm::SmallDenseMap< + const Decl *, llvm::PointerUnion, 4> + LocalDeclsMap; + /// \brief A mapping from local declarations that occur /// within a template to their instantiations. /// @@ -401,6 +406,7 @@ namespace clang { Decl *VisitVarDecl(VarDecl *D); Decl *VisitAccessSpecDecl(AccessSpecDecl *D); Decl *VisitFieldDecl(FieldDecl *D); + Decl *VisitMSPropertyDecl(MSPropertyDecl *D); Decl *VisitIndirectFieldDecl(IndirectFieldDecl *D); Decl *VisitStaticAssertDecl(StaticAssertDecl *D); Decl *VisitEnumDecl(EnumDecl *D); diff --git a/include/clang/Sema/TemplateDeduction.h b/include/clang/Sema/TemplateDeduction.h index 3abb8f1889e..8292045fdb9 100644 --- a/include/clang/Sema/TemplateDeduction.h +++ b/include/clang/Sema/TemplateDeduction.h @@ -157,8 +157,8 @@ class TemplateDeductionInfo { /// \brief The expression which caused a deduction failure. /// /// TDK_FailedOverloadResolution: this argument is the reference to - // an overloaded function which could not be resolved to a specific - // function. + /// an overloaded function which could not be resolved to a specific + /// function. Expr *Expression; }; diff --git a/include/clang/Serialization/ASTBitCodes.h b/include/clang/Serialization/ASTBitCodes.h index 9b685ba118f..81f89804258 100644 --- a/include/clang/Serialization/ASTBitCodes.h +++ b/include/clang/Serialization/ASTBitCodes.h @@ -951,6 +951,8 @@ namespace clang { DECL_OBJC_PROPERTY_IMPL, /// \brief A FieldDecl record. DECL_FIELD, + /// \brief A MSPropertyDecl record. + DECL_MS_PROPERTY, /// \brief A VarDecl record. DECL_VAR, /// \brief An ImplicitParamDecl record. @@ -961,6 +963,8 @@ namespace clang { DECL_FILE_SCOPE_ASM, /// \brief A BlockDecl record. DECL_BLOCK, + /// \brief A CapturedDecl record. + DECL_CAPTURED, /// \brief A record that stores the set of declarations that are /// lexically stored within a given DeclContext. /// @@ -1101,6 +1105,8 @@ namespace clang { STMT_RETURN, /// \brief A DeclStmt record. STMT_DECL, + /// \brief A CapturedStmt record. + STMT_CAPTURED, /// \brief A GCC-style AsmStmt record. STMT_GCCASM, /// \brief A MS-style AsmStmt record. @@ -1261,6 +1267,7 @@ namespace clang { EXPR_CXX_THIS, // CXXThisExpr EXPR_CXX_THROW, // CXXThrowExpr EXPR_CXX_DEFAULT_ARG, // CXXDefaultArgExpr + EXPR_CXX_DEFAULT_INIT, // CXXDefaultInitExpr EXPR_CXX_BIND_TEMPORARY, // CXXBindTemporaryExpr EXPR_CXX_SCALAR_VALUE_INIT, // CXXScalarValueInitExpr @@ -1300,6 +1307,7 @@ namespace clang { EXPR_ASTYPE, // AsTypeExpr // Microsoft + EXPR_CXX_PROPERTY_REF_EXPR, // MSPropertyRefExpr EXPR_CXX_UUIDOF_EXPR, // CXXUuidofExpr (of expr). EXPR_CXX_UUIDOF_TYPE, // CXXUuidofExpr (of type). STMT_SEH_EXCEPT, // SEHExceptStmt diff --git a/include/clang/Serialization/ASTReader.h b/include/clang/Serialization/ASTReader.h index 925533678c1..2c0102e3410 100644 --- a/include/clang/Serialization/ASTReader.h +++ b/include/clang/Serialization/ASTReader.h @@ -172,6 +172,16 @@ class ASTReaderListener { /// \brief Receives __COUNTER__ value. virtual void ReadCounter(const serialization::ModuleFile &M, unsigned Value) {} + + /// \brief Returns true if this \c ASTReaderListener wants to receive the + /// input files of the AST file via \c visitInputFile, false otherwise. + virtual bool needsInputFileVisitation() { return false; } + + /// \brief if \c needsInputFileVisitation returns true, this is called for each + /// input file of the AST file. + /// + /// \returns true to continue receiving the next input file, false to stop. + virtual bool visitInputFile(StringRef Filename, bool isSystem) { return true;} }; /// \brief ASTReaderListener implementation to validate the information of @@ -1106,6 +1116,8 @@ class ASTReader void finishPendingActions(); + void pushExternalDeclIntoScope(NamedDecl *D, DeclarationName Name); + void addPendingDeclContextInfo(Decl *D, serialization::GlobalDeclID SemaDC, serialization::GlobalDeclID LexicalDC) { @@ -1558,7 +1570,7 @@ class ASTReader /// \brief Retrieve an iterator into the set of all identifiers /// in all loaded AST files. - virtual IdentifierIterator *getIdentifiers() const; + virtual IdentifierIterator *getIdentifiers(); /// \brief Load the contents of the global method pool for a given /// selector. @@ -1800,6 +1812,9 @@ class ASTReader /// \brief Reads a sub-expression operand during statement reading. Expr *ReadSubExpr(); + /// \brief Reads a token out of a record. + Token ReadToken(ModuleFile &M, const RecordData &Record, unsigned &Idx); + /// \brief Reads the macro record located at the given offset. MacroInfo *ReadMacroRecord(ModuleFile &F, uint64_t Offset); diff --git a/include/clang/Serialization/ASTWriter.h b/include/clang/Serialization/ASTWriter.h index 2938dc76de4..8ac8fde8847 100644 --- a/include/clang/Serialization/ASTWriter.h +++ b/include/clang/Serialization/ASTWriter.h @@ -63,6 +63,7 @@ class Sema; class SourceManager; class SwitchCase; class TargetInfo; +class Token; class VersionTuple; class ASTUnresolvedSet; @@ -498,6 +499,9 @@ class ASTWriter : public ASTDeserializationListener, Module *WritingModule, StringRef isysroot, bool hasErrors = false); + /// \brief Emit a token. + void AddToken(const Token &Tok, RecordDataImpl &Record); + /// \brief Emit a source location. void AddSourceLocation(SourceLocation Loc, RecordDataImpl &Record); diff --git a/include/clang/Serialization/GlobalModuleIndex.h b/include/clang/Serialization/GlobalModuleIndex.h index eaf26d1df16..ab91f4009c0 100644 --- a/include/clang/Serialization/GlobalModuleIndex.h +++ b/include/clang/Serialization/GlobalModuleIndex.h @@ -146,6 +146,11 @@ class GlobalModuleIndex { static std::pair readIndex(StringRef Path); + /// \brief Returns an iterator for identifiers stored in the index table. + /// + /// The caller accepts ownership of the returned object. + IdentifierIterator *createIdentifierIterator() const; + /// \brief Retrieve the set of modules that have up-to-date indexes. /// /// \param ModuleFiles Will be populated with the set of module files that diff --git a/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h b/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h index 6dbdbbf89b9..fb35f518ef0 100644 --- a/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h +++ b/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h @@ -232,6 +232,7 @@ class AnalyzerOptions : public RefCountedBase { /// \sa getMaxNodesPerTopLevelFunction Optional MaxNodesPerTopLevelFunction; +public: /// Interprets an option's string value as a boolean. /// /// Accepts the strings "true" and "false". @@ -244,7 +245,6 @@ class AnalyzerOptions : public RefCountedBase { /// Interprets an option's string value as an integer value. int getOptionAsInteger(StringRef Name, int DefaultVal); -public: /// \brief Retrieves and sets the UserMode. This is a high-level option, /// which is used to set other low-level options. It is not accessible /// outside of AnalyzerOptions. diff --git a/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h b/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h index 7a87e47f74c..5c560b2f0ec 100644 --- a/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h +++ b/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h @@ -406,11 +406,6 @@ class BugReporter { /// A vector of BugReports for tracking the allocated pointers and cleanup. std::vector EQClassesVector; - /// A map from PathDiagnosticPiece to the LocationContext of the inlined - /// function call it represents. - llvm::DenseMap LocationContextMap; - protected: BugReporter(BugReporterData& d, Kind k) : BugTypes(F.getEmptySet()), kind(k), D(d) {} @@ -482,10 +477,6 @@ class BugReporter { EmitBasicReport(DeclWithIssue, BugName, Category, BugStr, Loc, &R, 1); } - void addCallPieceLocationContextPair(const PathDiagnosticCallPiece *C, - const LocationContext *LC) { - LocationContextMap[C] = LC; - } private: llvm::StringMap StrBugTypes; diff --git a/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitor.h b/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitor.h index 2e5f207f4b4..2e67180beac 100644 --- a/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitor.h +++ b/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitor.h @@ -134,10 +134,15 @@ class TrackConstraintBRVisitor bool IsSatisfied; bool IsZeroCheck; + /// We should start tracking from the last node along the path in which the + /// value is constrained. + bool IsTrackingTurnedOn; + public: TrackConstraintBRVisitor(DefinedSVal constraint, bool assumption) : Constraint(constraint), Assumption(assumption), IsSatisfied(false), - IsZeroCheck(!Assumption && Constraint.getAs()) {} + IsZeroCheck(!Assumption && Constraint.getAs()), + IsTrackingTurnedOn(false) {} void Profile(llvm::FoldingSetNodeID &ID) const; diff --git a/include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h b/include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h index 3f0a1b1bc14..a80b5a7a248 100644 --- a/include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h +++ b/include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h @@ -21,6 +21,7 @@ #include "llvm/ADT/Optional.h" #include "llvm/ADT/PointerUnion.h" #include +#include #include #include #include @@ -93,7 +94,7 @@ class PathDiagnosticConsumer { void HandlePathDiagnostic(PathDiagnostic *D); - enum PathGenerationScheme { None, Minimal, Extensive }; + enum PathGenerationScheme { None, Minimal, Extensive, AlternateExtensive }; virtual PathGenerationScheme getGenerationScheme() const { return Minimal; } virtual bool supportsLogicalOpControlFlow() const { return false; } virtual bool supportsAllBlockEdges() const { return false; } @@ -283,6 +284,13 @@ class PathDiagnosticLocation { const SourceManager& getManager() const { assert(isValid()); return *SM; } void Profile(llvm::FoldingSetNodeID &ID) const; + + /// \brief Given an exploded node, retrieve the statement that should be used + /// for the diagnostic location. + static const Stmt *getStmt(const ExplodedNode *N); + + /// \brief Retrieve the statement corresponding to the sucessor node. + static const Stmt *getNextStmt(const ExplodedNode *N); }; class PathDiagnosticLocationPair { @@ -296,6 +304,9 @@ class PathDiagnosticLocationPair { const PathDiagnosticLocation &getStart() const { return Start; } const PathDiagnosticLocation &getEnd() const { return End; } + void setStart(const PathDiagnosticLocation &L) { Start = L; } + void setEnd(const PathDiagnosticLocation &L) { End = L; } + void flatten() { Start.flatten(); End.flatten(); @@ -381,7 +392,7 @@ class PathDiagnosticPiece : public RefCountedBaseVPTR { }; -class PathPieces : public std::deque > { +class PathPieces : public std::list > { void flattenTo(PathPieces &Primary, PathPieces &Current, bool ShouldFlattenMacros) const; public: @@ -608,6 +619,14 @@ class PathDiagnosticControlFlowPiece : public PathDiagnosticPiece { return LPairs[0].getEnd(); } + void setStartLocation(const PathDiagnosticLocation &L) { + LPairs[0].setStart(L); + } + + void setEndLocation(const PathDiagnosticLocation &L) { + LPairs[0].setEnd(L); + } + void push_back(const PathDiagnosticLocationPair &X) { LPairs.push_back(X); } virtual PathDiagnosticLocation getLocation() const { diff --git a/include/clang/StaticAnalyzer/Core/CheckerManager.h b/include/clang/StaticAnalyzer/Core/CheckerManager.h index 6f99fc14577..b2411e6e654 100644 --- a/include/clang/StaticAnalyzer/Core/CheckerManager.h +++ b/include/clang/StaticAnalyzer/Core/CheckerManager.h @@ -17,6 +17,7 @@ #include "clang/Analysis/ProgramPoint.h" #include "clang/Basic/LangOptions.h" #include "clang/StaticAnalyzer/Core/PathSensitive/Store.h" +#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/SmallVector.h" @@ -134,9 +135,13 @@ enum PointerEscapeKind { class CheckerManager { const LangOptions LangOpts; - + AnalyzerOptionsRef AOptions; public: - CheckerManager(const LangOptions &langOpts) : LangOpts(langOpts) { } + CheckerManager(const LangOptions &langOpts, + AnalyzerOptionsRef AOptions) + : LangOpts(langOpts), + AOptions(AOptions) {} + ~CheckerManager(); bool hasPathSensitiveCheckers() const; @@ -144,6 +149,7 @@ class CheckerManager { void finishedCheckerRegistration(); const LangOptions &getLangOpts() const { return LangOpts; } + AnalyzerOptions &getAnalyzerOptions() { return *AOptions; } typedef CheckerBase *CheckerRef; typedef const void *CheckerTag; @@ -170,6 +176,20 @@ class CheckerManager { return checker; } + template + CHECKER *registerChecker(AnalyzerOptions &AOpts) { + CheckerTag tag = getTag(); + CheckerRef &ref = CheckerTags[tag]; + if (ref) + return static_cast(ref); // already registered. + + CHECKER *checker = new CHECKER(AOpts); + CheckerDtors.push_back(CheckerDtor(checker, destruct)); + CHECKER::_register(checker, *this); + ref = checker; + return checker; + } + //===----------------------------------------------------------------------===// // Functions for running checkers for AST traversing.. //===----------------------------------------------------------------------===// diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h b/include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h index 1135b511441..2c799c0db44 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h @@ -93,7 +93,7 @@ class BasicValueFactory { /// Returns the type of the APSInt used to store values of the given QualType. APSIntType getAPSIntType(QualType T) const { - assert(T->isIntegerType() || Loc::isLocType(T)); + assert(T->isIntegralOrEnumerationType() || Loc::isLocType(T)); return APSIntType(Ctx.getTypeSize(T), !T->isSignedIntegerOrEnumerationType()); } diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h b/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h index cda1366a439..0b9762ac42e 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h @@ -110,10 +110,6 @@ class CheckerContext { StoreManager &getStoreManager() { return Eng.getStoreManager(); } - - const AnalyzerOptions::ConfigTable &getConfig() const { - return Eng.getAnalysisManager().options.Config; - } /// \brief Returns the previous node in the exploded graph, which includes /// the state of the program before the checker ran. Note, checkers should diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h b/include/clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h index 5211916407c..edcfc8a6c09 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h @@ -196,6 +196,10 @@ class ExplodedNode : public llvm::FoldingSetNode { return const_cast(this)->getFirstPred(); } + const ExplodedNode *getFirstSucc() const { + return succ_empty() ? NULL : *(succ_begin()); + } + // Iterators over successor and predecessor vertices. typedef ExplodedNode* const * succ_iterator; typedef const ExplodedNode* const * const_succ_iterator; diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h b/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h index af2f365ead9..9b4f77dd679 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h @@ -37,11 +37,12 @@ class StackFrameContext; namespace ento { +class CodeTextRegion; class MemRegionManager; class MemSpaceRegion; class SValBuilder; +class SymbolicRegion; class VarRegion; -class CodeTextRegion; /// Represent a region's offset within the top level base region. class RegionOffset { @@ -145,6 +146,10 @@ class MemRegion : public llvm::FoldingSetNode { const MemRegion *StripCasts(bool StripBaseCasts = true) const; + /// \brief If this is a symbolic region, returns the region. Otherwise, + /// goes up the base chain looking for the first symbolic base region. + const SymbolicRegion *getSymbolicBase() const; + bool hasGlobalsOrParametersStorage() const; bool hasStackStorage() const; @@ -169,6 +174,16 @@ class MemRegion : public llvm::FoldingSetNode { /// \brief Print the region for use in diagnostics. virtual void printPretty(raw_ostream &os) const; + /// \brief Returns true if this region's textual representation can be used + /// as part of a larger expression. + virtual bool canPrintPrettyAsExpr() const; + + /// \brief Print the region as expression. + /// + /// When this region represents a subexpression, the method is for printing + /// an expression containing it. + virtual void printPrettyAsExpr(raw_ostream &os) const; + Kind getKind() const { return kind; } template const RegionTy* getAs() const; @@ -874,8 +889,9 @@ class VarRegion : public DeclRegion { return R->getKind() == VarRegionKind; } - bool canPrintPretty() const; - void printPretty(raw_ostream &os) const; + bool canPrintPrettyAsExpr() const; + + void printPrettyAsExpr(raw_ostream &os) const; }; /// CXXThisRegion - Represents the region for the implicit 'this' parameter @@ -937,6 +953,8 @@ class FieldRegion : public DeclRegion { bool canPrintPretty() const; void printPretty(raw_ostream &os) const; + bool canPrintPrettyAsExpr() const; + void printPrettyAsExpr(raw_ostream &os) const; }; class ObjCIvarRegion : public DeclRegion { @@ -952,8 +970,8 @@ class ObjCIvarRegion : public DeclRegion { const ObjCIvarDecl *getDecl() const; QualType getValueType() const; - bool canPrintPretty() const; - void printPretty(raw_ostream &os) const; + bool canPrintPrettyAsExpr() const; + void printPrettyAsExpr(raw_ostream &os) const; void dumpToStream(raw_ostream &os) const; @@ -1082,6 +1100,10 @@ class CXXBaseObjectRegion : public TypedValueRegion { static bool classof(const MemRegion *region) { return region->getKind() == CXXBaseObjectRegionKind; } + + bool canPrintPrettyAsExpr() const; + + void printPrettyAsExpr(raw_ostream &os) const; }; template diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h b/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h index 6ea7211090b..42ef1db4559 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h @@ -201,14 +201,6 @@ class ProgramState : public llvm::FoldingSetNode { // Binding and retrieving values to/from the environment and symbolic store. //==---------------------------------------------------------------------==// - /// \brief Create a new state with the specified CompoundLiteral binding. - /// \param CL the compound literal expression (the binding key) - /// \param LC the LocationContext of the binding - /// \param V the value to bind. - ProgramStateRef bindCompoundLiteral(const CompoundLiteralExpr *CL, - const LocationContext *LC, - SVal V) const; - /// Create a new state by binding the value 'V' to the statement 'S' in the /// state's environment. ProgramStateRef BindExpr(const Stmt *S, const LocationContext *LCtx, @@ -711,7 +703,8 @@ ProgramState::getSValAsScalarOrLoc(const Stmt *S, const LocationContext *LCtx) const { if (const Expr *Ex = dyn_cast(S)) { QualType T = Ex->getType(); - if (Ex->isGLValue() || Loc::isLocType(T) || T->isIntegerType()) + if (Ex->isGLValue() || Loc::isLocType(T) || + T->isIntegralOrEnumerationType()) return getSVal(S, LCtx); } diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h b/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h index f7e49a3c753..bbb56885af0 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h @@ -78,7 +78,8 @@ class SValBuilder { // FIXME: Remove the second disjunct when we support symbolic // truncation/extension. return (Context.getCanonicalType(Ty1) == Context.getCanonicalType(Ty2) || - (Ty1->isIntegerType() && Ty2->isIntegerType())); + (Ty1->isIntegralOrEnumerationType() && + Ty2->isIntegralOrEnumerationType())); } SVal evalCast(SVal val, QualType castTy, QualType originalType); @@ -201,6 +202,12 @@ class SValBuilder { DefinedSVal getBlockPointer(const BlockDecl *block, CanQualType locTy, const LocationContext *locContext); + /// Returns the value of \p E, if it can be determined in a non-path-sensitive + /// manner. + /// + /// If \p E is not a constant or cannot be modeled, returns \c None. + Optional getConstantVal(const Expr *E); + NonLoc makeCompoundVal(QualType type, llvm::ImmutableList vals) { return nonloc::CompoundVal(BasicVals.getCompoundValData(type, vals)); } diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h b/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h index 1c5519e9e78..326e784e83d 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h @@ -144,16 +144,24 @@ class SVal { /// Otherwise return 0. const FunctionDecl *getAsFunctionDecl() const; - /// If this SVal is a location (subclasses Loc) and - /// wraps a symbol, return that SymbolRef. Otherwise return 0. - SymbolRef getAsLocSymbol() const; + /// \brief If this SVal is a location and wraps a symbol, return that + /// SymbolRef. Otherwise return 0. + /// + /// Casts are ignored during lookup. + /// \param IncludeBaseRegions The boolean that controls whether the search + /// should continue to the base regions if the region is not symbolic. + SymbolRef getAsLocSymbol(bool IncludeBaseRegions = false) const; /// Get the symbol in the SVal or its base region. SymbolRef getLocSymbolInBase() const; - /// If this SVal wraps a symbol return that SymbolRef. + /// \brief If this SVal wraps a symbol return that SymbolRef. /// Otherwise, return 0. - SymbolRef getAsSymbol() const; + /// + /// Casts are ignored during lookup. + /// \param IncludeBaseRegions The boolean that controls whether the search + /// should continue to the base regions if the region is not symbolic. + SymbolRef getAsSymbol(bool IncludeBaseRegions = false) const; /// getAsSymbolicExpression - If this Sval wraps a symbolic expression then /// return that expression. Otherwise return NULL. diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/Store.h b/include/clang/StaticAnalyzer/Core/PathSensitive/Store.h index bbfd5797fff..b219495d5f0 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/Store.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/Store.h @@ -76,21 +76,6 @@ class StoreManager { /// \param L the location whose binding should be removed. virtual StoreRef killBinding(Store ST, Loc L) = 0; - /// \brief Create a new store that binds a value to a compound literal. - /// - /// \param ST The original store whose bindings are the basis for the new - /// store. - /// - /// \param CL The compound literal to bind (the binding key). - /// - /// \param LC The LocationContext for the binding. - /// - /// \param V The value to bind to the compound literal. - virtual StoreRef bindCompoundLiteral(Store ST, - const CompoundLiteralExpr *CL, - const LocationContext *LC, - SVal V) = 0; - /// getInitialStore - Returns the initial "empty" store representing the /// value bindings upon entry to an analyzed function. virtual StoreRef getInitialStore(const LocationContext *InitLoc) = 0; diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h b/include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h index 56afca24f6b..914b2bea2d1 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h @@ -49,7 +49,10 @@ class SymExpr : public llvm::FoldingSetNode { MetadataKind, BEGIN_SYMBOLS = RegionValueKind, END_SYMBOLS = MetadataKind, - SymIntKind, IntSymKind, SymSymKind, CastSymbolKind }; + SymIntKind, IntSymKind, SymSymKind, + BEGIN_BINARYSYMEXPRS = SymIntKind, + END_BINARYSYMEXPRS = SymSymKind, + CastSymbolKind }; private: Kind K; @@ -341,24 +344,39 @@ class SymbolCast : public SymExpr { } }; -/// SymIntExpr - Represents symbolic expression like 'x' + 3. -class SymIntExpr : public SymExpr { - const SymExpr *LHS; +/// \brief Represents a symbolic expression involving a binary operator +class BinarySymExpr : public SymExpr { BinaryOperator::Opcode Op; - const llvm::APSInt& RHS; QualType T; -public: - SymIntExpr(const SymExpr *lhs, BinaryOperator::Opcode op, - const llvm::APSInt& rhs, QualType t) - : SymExpr(SymIntKind), LHS(lhs), Op(op), RHS(rhs), T(t) {} +protected: + BinarySymExpr(Kind k, BinaryOperator::Opcode op, QualType t) + : SymExpr(k), Op(op), T(t) {} +public: // FIXME: We probably need to make this out-of-line to avoid redundant // generation of virtual functions. QualType getType() const { return T; } BinaryOperator::Opcode getOpcode() const { return Op; } + // Implement isa support. + static inline bool classof(const SymExpr *SE) { + Kind k = SE->getKind(); + return k >= BEGIN_BINARYSYMEXPRS && k <= END_BINARYSYMEXPRS; + } +}; + +/// \brief Represents a symbolic expression like 'x' + 3. +class SymIntExpr : public BinarySymExpr { + const SymExpr *LHS; + const llvm::APSInt& RHS; + +public: + SymIntExpr(const SymExpr *lhs, BinaryOperator::Opcode op, + const llvm::APSInt& rhs, QualType t) + : BinarySymExpr(SymIntKind, op, t), LHS(lhs), RHS(rhs) {} + virtual void dumpToStream(raw_ostream &os) const; const SymExpr *getLHS() const { return LHS; } @@ -375,7 +393,7 @@ class SymIntExpr : public SymExpr { } void Profile(llvm::FoldingSetNodeID& ID) { - Profile(ID, LHS, Op, RHS, T); + Profile(ID, LHS, getOpcode(), RHS, getType()); } // Implement isa support. @@ -384,21 +402,15 @@ class SymIntExpr : public SymExpr { } }; -/// IntSymExpr - Represents symbolic expression like 3 - 'x'. -class IntSymExpr : public SymExpr { +/// \brief Represents a symbolic expression like 3 - 'x'. +class IntSymExpr : public BinarySymExpr { const llvm::APSInt& LHS; - BinaryOperator::Opcode Op; const SymExpr *RHS; - QualType T; public: IntSymExpr(const llvm::APSInt& lhs, BinaryOperator::Opcode op, const SymExpr *rhs, QualType t) - : SymExpr(IntSymKind), LHS(lhs), Op(op), RHS(rhs), T(t) {} - - QualType getType() const { return T; } - - BinaryOperator::Opcode getOpcode() const { return Op; } + : BinarySymExpr(IntSymKind, op, t), LHS(lhs), RHS(rhs) {} virtual void dumpToStream(raw_ostream &os) const; @@ -416,7 +428,7 @@ class IntSymExpr : public SymExpr { } void Profile(llvm::FoldingSetNodeID& ID) { - Profile(ID, LHS, Op, RHS, T); + Profile(ID, LHS, getOpcode(), RHS, getType()); } // Implement isa support. @@ -425,26 +437,19 @@ class IntSymExpr : public SymExpr { } }; -/// SymSymExpr - Represents symbolic expression like 'x' + 'y'. -class SymSymExpr : public SymExpr { +/// \brief Represents a symbolic expression like 'x' + 'y'. +class SymSymExpr : public BinarySymExpr { const SymExpr *LHS; - BinaryOperator::Opcode Op; const SymExpr *RHS; - QualType T; public: SymSymExpr(const SymExpr *lhs, BinaryOperator::Opcode op, const SymExpr *rhs, QualType t) - : SymExpr(SymSymKind), LHS(lhs), Op(op), RHS(rhs), T(t) {} + : BinarySymExpr(SymSymKind, op, t), LHS(lhs), RHS(rhs) {} - BinaryOperator::Opcode getOpcode() const { return Op; } const SymExpr *getLHS() const { return LHS; } const SymExpr *getRHS() const { return RHS; } - // FIXME: We probably need to make this out-of-line to avoid redundant - // generation of virtual functions. - QualType getType() const { return T; } - virtual void dumpToStream(raw_ostream &os) const; static void Profile(llvm::FoldingSetNodeID& ID, const SymExpr *lhs, @@ -457,7 +462,7 @@ class SymSymExpr : public SymExpr { } void Profile(llvm::FoldingSetNodeID& ID) { - Profile(ID, LHS, Op, RHS, T); + Profile(ID, LHS, getOpcode(), RHS, getType()); } // Implement isa support. diff --git a/include/clang/StaticAnalyzer/Frontend/CheckerRegistration.h b/include/clang/StaticAnalyzer/Frontend/CheckerRegistration.h index 492edd4ccb4..1df8c098d93 100644 --- a/include/clang/StaticAnalyzer/Frontend/CheckerRegistration.h +++ b/include/clang/StaticAnalyzer/Frontend/CheckerRegistration.h @@ -21,7 +21,7 @@ namespace clang { namespace ento { class CheckerManager; -CheckerManager *createCheckerManager(const AnalyzerOptions &opts, +CheckerManager *createCheckerManager(AnalyzerOptions &opts, const LangOptions &langOpts, ArrayRef plugins, DiagnosticsEngine &diags); diff --git a/lib/ARCMigrate/ARCMT.cpp b/lib/ARCMigrate/ARCMT.cpp index 72f35205ca8..a6d48762fe5 100644 --- a/lib/ARCMigrate/ARCMT.cpp +++ b/lib/ARCMigrate/ARCMT.cpp @@ -140,12 +140,6 @@ class CaptureDiagnosticConsumer : public DiagnosticConsumer { // Non-ARC warnings are ignored. Diags.setLastDiagnosticIgnored(); } - - DiagnosticConsumer *clone(DiagnosticsEngine &Diags) const { - // Just drop any diagnostics that come from cloned consumers; they'll - // have different source managers anyway. - return new IgnoringDiagConsumer(); - } }; } // end anonymous namespace @@ -482,7 +476,7 @@ class ARCMTMacroTrackerPPCallbacks : public PPCallbacks { : ARCMTMacroLocs(ARCMTMacroLocs) { } virtual void MacroExpands(const Token &MacroNameTok, const MacroDirective *MD, - SourceRange Range) { + SourceRange Range, const MacroArgs *Args) { if (MacroNameTok.getIdentifierInfo()->getName() == getARCMTMacroName()) ARCMTMacroLocs.push_back(MacroNameTok.getLocation()); } diff --git a/lib/ARCMigrate/TransAPIUses.cpp b/lib/ARCMigrate/TransAPIUses.cpp index 2305b6defd9..a0994a6b459 100644 --- a/lib/ARCMigrate/TransAPIUses.cpp +++ b/lib/ARCMigrate/TransAPIUses.cpp @@ -91,12 +91,12 @@ class APIChecker : public RecursiveASTVisitor { E->getSelector() == zoneSel && Pass.TA.hasDiagnostic(diag::err_unavailable, diag::err_unavailable_message, - E->getInstanceReceiver()->getExprLoc())) { + E->getSelectorLoc(0))) { // Calling -zone is meaningless in ARC, change it to nil. Transaction Trans(Pass.TA); Pass.TA.clearDiagnostic(diag::err_unavailable, diag::err_unavailable_message, - E->getInstanceReceiver()->getExprLoc()); + E->getSelectorLoc(0)); Pass.TA.replace(E->getSourceRange(), getNilString(Pass.Ctx)); } return true; diff --git a/lib/ARCMigrate/TransRetainReleaseDealloc.cpp b/lib/ARCMigrate/TransRetainReleaseDealloc.cpp index 0c8d1554461..446a284a286 100644 --- a/lib/ARCMigrate/TransRetainReleaseDealloc.cpp +++ b/lib/ARCMigrate/TransRetainReleaseDealloc.cpp @@ -118,7 +118,7 @@ class RetainReleaseDeallocRemover : return true; case ObjCMessageExpr::SuperInstance: { Transaction Trans(Pass.TA); - clearDiagnostics(E->getSuperLoc()); + clearDiagnostics(E->getSelectorLoc(0)); if (tryRemoving(E)) return true; Pass.TA.replace(E->getSourceRange(), "self"); @@ -132,7 +132,7 @@ class RetainReleaseDeallocRemover : if (!rec) return true; Transaction Trans(Pass.TA); - clearDiagnostics(rec->getExprLoc()); + clearDiagnostics(E->getSelectorLoc(0)); ObjCMessageExpr *Msg = E; Expr *RecContainer = Msg; diff --git a/lib/ARCMigrate/Transforms.h b/lib/ARCMigrate/Transforms.h index cb7d1535c62..e20fe5927f3 100644 --- a/lib/ARCMigrate/Transforms.h +++ b/lib/ARCMigrate/Transforms.h @@ -169,7 +169,7 @@ bool isPlusOne(const Expr *E); /// source location will be invalid. SourceLocation findLocationAfterSemi(SourceLocation loc, ASTContext &Ctx); -/// \brief \arg Loc is the end of a statement range. This returns the location +/// \brief 'Loc' is the end of a statement range. This returns the location /// of the semicolon following the statement. /// If no semicolon is found or the location is inside a macro, the returned /// source location will be invalid. diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 7245c031608..176aec53a25 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -97,7 +97,12 @@ RawComment *ASTContext::getRawCommentForDeclNoCache(const Decl *D) const { if (ED->getTemplateSpecializationKind() == TSK_ImplicitInstantiation) return NULL; } - + if (const TagDecl *TD = dyn_cast(D)) { + // When tag declaration (but not definition!) is part of the + // decl-specifier-seq of some other declaration, it doesn't get comment + if (TD->isEmbeddedInDeclarator() && !TD->isCompleteDefinition()) + return NULL; + } // TODO: handle comments for function parameters properly. if (isa(D)) return NULL; @@ -141,7 +146,9 @@ RawComment *ASTContext::getRawCommentForDeclNoCache(const Decl *D) const { // 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)); + RawComment CommentAtDeclLoc( + SourceMgr, SourceRange(DeclLoc), false, + LangOpts.CommentOpts.ParseAllComments); BeforeThanCompare Compare(SourceMgr); ArrayRef::iterator MaybeBeforeDecl = RawComments.end() - 1; bool Found = Compare(*MaybeBeforeDecl, &CommentAtDeclLoc); @@ -435,7 +442,7 @@ comments::FullComment *ASTContext::getCommentForDecl( if (comments::FullComment *FC = getCommentForDecl(Overridden[i], PP)) return cloneFullComment(FC, D); } - else if (const TypedefDecl *TD = dyn_cast(D)) { + else if (const TypedefNameDecl *TD = dyn_cast(D)) { // Attach any tag type's documentation to its typedef if latter // does not have one of its own. QualType QT = TD->getUnderlyingType(); @@ -444,6 +451,53 @@ comments::FullComment *ASTContext::getCommentForDecl( if (comments::FullComment *FC = getCommentForDecl(TD, PP)) return cloneFullComment(FC, D); } + else if (const ObjCInterfaceDecl *IC = dyn_cast(D)) { + while (IC->getSuperClass()) { + IC = IC->getSuperClass(); + if (comments::FullComment *FC = getCommentForDecl(IC, PP)) + return cloneFullComment(FC, D); + } + } + else if (const ObjCCategoryDecl *CD = dyn_cast(D)) { + if (const ObjCInterfaceDecl *IC = CD->getClassInterface()) + if (comments::FullComment *FC = getCommentForDecl(IC, PP)) + return cloneFullComment(FC, D); + } + else if (const CXXRecordDecl *RD = dyn_cast(D)) { + if (!(RD = RD->getDefinition())) + return NULL; + // Check non-virtual bases. + for (CXXRecordDecl::base_class_const_iterator I = + RD->bases_begin(), E = RD->bases_end(); I != E; ++I) { + if (I->isVirtual() || (I->getAccessSpecifier() != AS_public)) + continue; + QualType Ty = I->getType(); + if (Ty.isNull()) + continue; + if (const CXXRecordDecl *NonVirtualBase = Ty->getAsCXXRecordDecl()) { + if (!(NonVirtualBase= NonVirtualBase->getDefinition())) + continue; + + if (comments::FullComment *FC = getCommentForDecl((NonVirtualBase), PP)) + return cloneFullComment(FC, D); + } + } + // Check virtual bases. + for (CXXRecordDecl::base_class_const_iterator I = + RD->vbases_begin(), E = RD->vbases_end(); I != E; ++I) { + if (I->getAccessSpecifier() != AS_public) + continue; + QualType Ty = I->getType(); + if (Ty.isNull()) + continue; + if (const CXXRecordDecl *VirtualBase = Ty->getAsCXXRecordDecl()) { + if (!(VirtualBase= VirtualBase->getDefinition())) + continue; + if (comments::FullComment *FC = getCommentForDecl((VirtualBase), PP)) + return cloneFullComment(FC, D); + } + } + } return NULL; } @@ -1125,8 +1179,8 @@ void ASTContext::getOverriddenMethods( assert(D); if (const CXXMethodDecl *CXXMethod = dyn_cast(D)) { - Overridden.append(CXXMethod->begin_overridden_methods(), - CXXMethod->end_overridden_methods()); + Overridden.append(overridden_methods_begin(CXXMethod), + overridden_methods_end(CXXMethod)); return; } @@ -1229,6 +1283,10 @@ CharUnits ASTContext::getDeclAlign(const Decl *D, bool RefAsPointee) const { T = getBaseElementType(arrayType); } Align = std::max(Align, getPreferredTypeAlign(T.getTypePtr())); + if (const VarDecl *VD = dyn_cast(D)) { + if (VD->hasGlobalStorage()) + Align = std::max(Align, getTargetInfo().getMinGlobalAlign()); + } } // Fields can be subject to extra alignment constraints, like if @@ -1543,7 +1601,8 @@ ASTContext::getTypeInfoImpl(const Type *T) const { case Type::Auto: { const AutoType *A = cast(T); - assert(A->isDeduced() && "Cannot request the size of a dependent type"); + assert(!A->getDeducedType().isNull() && + "cannot request the size of an undeduced or dependent auto type"); return getTypeInfo(A->getDeducedType().getTypePtr()); } @@ -1670,6 +1729,18 @@ unsigned ASTContext::getPreferredTypeAlign(const Type *T) const { return ABIAlign; } +/// getAlignOfGlobalVar - Return the alignment in bits that should be given +/// to a global variable of the specified type. +unsigned ASTContext::getAlignOfGlobalVar(QualType T) const { + return std::max(getTypeAlign(T), getTargetInfo().getMinGlobalAlign()); +} + +/// getAlignOfGlobalVarInChars - Return the alignment in characters that +/// should be given to a global variable of the specified type. +CharUnits ASTContext::getAlignOfGlobalVarInChars(QualType T) const { + return toCharUnitsFromBits(getAlignOfGlobalVar(T)); +} + /// DeepCollectObjCIvars - /// This routine first collects all declared, but not synthesized, ivars in /// super class and then collects all ivars, including those synthesized for @@ -1980,6 +2051,16 @@ const FunctionType *ASTContext::adjustFunctionType(const FunctionType *T, return cast(Result.getTypePtr()); } +void ASTContext::adjustDeducedFunctionResultType(FunctionDecl *FD, + QualType ResultType) { + // FIXME: Need to inform serialization code about this! + for (FD = FD->getMostRecentDecl(); FD; FD = FD->getPreviousDecl()) { + const FunctionProtoType *FPT = FD->getType()->castAs(); + FunctionProtoType::ExtProtoInfo EPI = FPT->getExtProtoInfo(); + FD->setType(getFunctionType(ResultType, FPT->getArgTypes(), EPI)); + } +} + /// getComplexType - Return the uniqued reference to the type for a complex /// number with the specified element type. QualType ASTContext::getComplexType(QualType T) const { @@ -3508,18 +3589,24 @@ QualType ASTContext::getUnaryTransformType(QualType BaseType, return QualType(Ty, 0); } -/// getAutoType - We only unique auto types after they've been deduced. -QualType ASTContext::getAutoType(QualType DeducedType) const { +/// getAutoType - Return the uniqued reference to the 'auto' type which has been +/// deduced to the given type, or to the canonical undeduced 'auto' type, or the +/// canonical deduced-but-dependent 'auto' type. +QualType ASTContext::getAutoType(QualType DeducedType, bool IsDecltypeAuto, + bool IsDependent) const { + if (DeducedType.isNull() && !IsDecltypeAuto && !IsDependent) + return getAutoDeductType(); + + // Look in the folding set for an existing type. void *InsertPos = 0; - if (!DeducedType.isNull()) { - // Look in the folding set for an existing type. - llvm::FoldingSetNodeID ID; - AutoType::Profile(ID, DeducedType); - if (AutoType *AT = AutoTypes.FindNodeOrInsertPos(ID, InsertPos)) - return QualType(AT, 0); - } + llvm::FoldingSetNodeID ID; + AutoType::Profile(ID, DeducedType, IsDecltypeAuto, IsDependent); + if (AutoType *AT = AutoTypes.FindNodeOrInsertPos(ID, InsertPos)) + return QualType(AT, 0); - AutoType *AT = new (*this, TypeAlignment) AutoType(DeducedType); + AutoType *AT = new (*this, TypeAlignment) AutoType(DeducedType, + IsDecltypeAuto, + IsDependent); Types.push_back(AT); if (InsertPos) AutoTypes.InsertNode(AT, InsertPos); @@ -3557,8 +3644,10 @@ QualType ASTContext::getAtomicType(QualType T) const { /// getAutoDeductType - Get type pattern for deducing against 'auto'. QualType ASTContext::getAutoDeductType() const { if (AutoDeductTy.isNull()) - AutoDeductTy = getAutoType(QualType()); - assert(!AutoDeductTy.isNull() && "can't build 'auto' pattern"); + AutoDeductTy = QualType( + new (*this, TypeAlignment) AutoType(QualType(), /*decltype(auto)*/false, + /*dependent*/false), + 0); return AutoDeductTy; } @@ -4188,7 +4277,7 @@ QualType ASTContext::isPromotableBitField(Expr *E) const { if (E->isTypeDependent() || E->isValueDependent()) return QualType(); - FieldDecl *Field = E->getBitField(); + FieldDecl *Field = E->getSourceBitField(); // FIXME: conditional bit-fields? if (!Field) return QualType(); @@ -5331,6 +5420,11 @@ void ASTContext::getObjCEncodingForTypeImpl(QualType T, std::string& S, // FIXME. We should do a better job than gcc. return; + case Type::Auto: + // We could see an undeduced auto type here during error recovery. + // Just ignore it. + return; + #define ABSTRACT_TYPE(KIND, BASE) #define TYPE(KIND, BASE) #define DEPENDENT_TYPE(KIND, BASE) \ @@ -5886,6 +5980,80 @@ CreateAAPCSABIBuiltinVaListDecl(const ASTContext *Context) { return VaListTypeDecl; } +static TypedefDecl * +CreateSystemZBuiltinVaListDecl(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]; + + // long __gpr; + FieldTypes[0] = Context->LongTy; + FieldNames[0] = "__gpr"; + + // long __fpr; + FieldTypes[1] = Context->LongTy; + FieldNames[1] = "__fpr"; + + // 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(*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(*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(*Context), + Context->getTranslationUnitDecl(), + SourceLocation(), SourceLocation(), + &Context->Idents.get("__builtin_va_list"), + TInfo); + + return VaListTypedefDecl; +} + static TypedefDecl *CreateVaListDecl(const ASTContext *Context, TargetInfo::BuiltinVaListKind Kind) { switch (Kind) { @@ -5903,6 +6071,8 @@ static TypedefDecl *CreateVaListDecl(const ASTContext *Context, return CreatePNaClABIBuiltinVaListDecl(Context); case TargetInfo::AAPCSABIBuiltinVaList: return CreateAAPCSABIBuiltinVaListDecl(Context); + case TargetInfo::SystemZBuiltinVaList: + return CreateSystemZBuiltinVaListDecl(Context); } llvm_unreachable("Unhandled __builtin_va_list type kind"); @@ -6972,6 +7142,7 @@ QualType ASTContext::mergeTypes(QualType LHS, QualType RHS, #include "clang/AST/TypeNodes.def" llvm_unreachable("Non-canonical and dependent types shouldn't get here"); + case Type::Auto: case Type::LValueReference: case Type::RValueReference: case Type::MemberPointer: diff --git a/lib/AST/ASTDumper.cpp b/lib/AST/ASTDumper.cpp index b1d174b855e..340cc41f7e8 100644 --- a/lib/AST/ASTDumper.cpp +++ b/lib/AST/ASTDumper.cpp @@ -853,8 +853,11 @@ void ASTDumper::VisitVarDecl(const VarDecl *D) { StorageClass SC = D->getStorageClass(); if (SC != SC_None) OS << ' ' << VarDecl::getStorageClassSpecifierString(SC); - if (D->isThreadSpecified()) - OS << " __thread"; + switch (D->getTLSKind()) { + case VarDecl::TLS_None: break; + case VarDecl::TLS_Static: OS << " tls"; break; + case VarDecl::TLS_Dynamic: OS << " tls_dynamic"; break; + } if (D->isModulePrivate()) OS << " __module_private__"; if (D->isNRVOVariable()) diff --git a/lib/AST/ASTImporter.cpp b/lib/AST/ASTImporter.cpp index d2e6d297053..915eb6feb85 100644 --- a/lib/AST/ASTImporter.cpp +++ b/lib/AST/ASTImporter.cpp @@ -839,6 +839,12 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, RecordDecl *D2 = Field2->getType()->castAs()->getDecl(); return IsStructurallyEquivalent(Context, D1, D2); } + + // Check for equivalent field names. + IdentifierInfo *Name1 = Field1->getIdentifier(); + IdentifierInfo *Name2 = Field2->getIdentifier(); + if (!::IsStructurallyEquivalent(Name1, Name2)) + return false; if (!IsStructurallyEquivalent(Context, Field1->getType(), Field2->getType())) { @@ -1680,7 +1686,7 @@ QualType ASTNodeImporter::VisitUnaryTransformType(const UnaryTransformType *T) { } QualType ASTNodeImporter::VisitAutoType(const AutoType *T) { - // FIXME: Make sure that the "to" context supports C++0x! + // FIXME: Make sure that the "to" context supports C++11! QualType FromDeduced = T->getDeducedType(); QualType ToDeduced; if (!FromDeduced.isNull()) { @@ -1689,7 +1695,7 @@ QualType ASTNodeImporter::VisitAutoType(const AutoType *T) { return QualType(); } - return Importer.getToContext().getAutoType(ToDeduced); + return Importer.getToContext().getAutoType(ToDeduced, T->isDecltypeAuto()); } QualType ASTNodeImporter::VisitRecordType(const RecordType *T) { @@ -3644,6 +3650,7 @@ Decl *ASTNodeImporter::VisitObjCImplementationDecl(ObjCImplementationDecl *D) { Iface, Super, Importer.Import(D->getLocation()), Importer.Import(D->getAtStartLoc()), + Importer.Import(D->getSuperClassLoc()), Importer.Import(D->getIvarLBraceLoc()), Importer.Import(D->getIvarRBraceLoc())); diff --git a/lib/AST/Comment.cpp b/lib/AST/Comment.cpp index db55c045449..68c73fd48e3 100644 --- a/lib/AST/Comment.cpp +++ b/lib/AST/Comment.cpp @@ -134,7 +134,7 @@ void DeclInfo::fill() { IsObjCMethod = false; IsInstanceMethod = false; IsClassMethod = false; - ParamVars = ArrayRef(); + ParamVars = None; TemplateParameters = NULL; if (!CommentDecl) { diff --git a/lib/AST/CommentLexer.cpp b/lib/AST/CommentLexer.cpp index 1194520bf36..70410d61085 100644 --- a/lib/AST/CommentLexer.cpp +++ b/lib/AST/CommentLexer.cpp @@ -1,5 +1,6 @@ #include "clang/AST/CommentLexer.h" #include "clang/AST/CommentCommandTraits.h" +#include "clang/AST/CommentDiagnostic.h" #include "clang/Basic/CharInfo.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringSwitch.h" @@ -353,6 +354,7 @@ void Lexer::lexCommentText(Token &T) { if (!Info) { formTokenWithChars(T, TokenPtr, tok::unknown_command); T.setUnknownCommandName(CommandName); + Diag(T.getLocation(), diag::warn_unknown_comment_command_name); return; } if (Info->IsVerbatimBlockCommand) { @@ -685,10 +687,11 @@ void Lexer::lexHTMLEndTag(Token &T) { State = LS_Normal; } -Lexer::Lexer(llvm::BumpPtrAllocator &Allocator, const CommandTraits &Traits, +Lexer::Lexer(llvm::BumpPtrAllocator &Allocator, DiagnosticsEngine &Diags, + const CommandTraits &Traits, SourceLocation FileLoc, const char *BufferStart, const char *BufferEnd): - Allocator(Allocator), Traits(Traits), + Allocator(Allocator), Diags(Diags), Traits(Traits), BufferStart(BufferStart), BufferEnd(BufferEnd), FileLoc(FileLoc), BufferPtr(BufferStart), CommentState(LCS_BeforeComment), State(LS_Normal) { diff --git a/lib/AST/CommentParser.cpp b/lib/AST/CommentParser.cpp index 09912c61886..d89c79b3d93 100644 --- a/lib/AST/CommentParser.cpp +++ b/lib/AST/CommentParser.cpp @@ -302,22 +302,18 @@ void Parser::parseBlockCommandArgs(BlockCommandComment *BC, BlockCommandComment *Parser::parseBlockCommand() { assert(Tok.is(tok::backslash_command) || Tok.is(tok::at_command)); - ParamCommandComment *PC; - TParamCommandComment *TPC; - BlockCommandComment *BC; - bool IsParam = false; - bool IsTParam = false; + ParamCommandComment *PC = 0; + TParamCommandComment *TPC = 0; + BlockCommandComment *BC = 0; const CommandInfo *Info = Traits.getCommandInfo(Tok.getCommandID()); CommandMarkerKind CommandMarker = Tok.is(tok::backslash_command) ? CMK_Backslash : CMK_At; if (Info->IsParamCommand) { - IsParam = true; PC = S.actOnParamCommandStart(Tok.getLocation(), Tok.getEndLocation(), Tok.getCommandID(), CommandMarker); } else if (Info->IsTParamCommand) { - IsTParam = true; TPC = S.actOnTParamCommandStart(Tok.getLocation(), Tok.getEndLocation(), Tok.getCommandID(), @@ -333,12 +329,11 @@ BlockCommandComment *Parser::parseBlockCommand() { if (isTokBlockCommand()) { // Block command ahead. We can't nest block commands, so pretend that this // command has an empty argument. - ParagraphComment *Paragraph = S.actOnParagraphComment( - ArrayRef()); - if (IsParam) { + ParagraphComment *Paragraph = S.actOnParagraphComment(None); + if (PC) { S.actOnParamCommandFinish(PC, Paragraph); return PC; - } else if (IsTParam) { + } else if (TPC) { S.actOnTParamCommandFinish(TPC, Paragraph); return TPC; } else { @@ -347,14 +342,14 @@ BlockCommandComment *Parser::parseBlockCommand() { } } - if (IsParam || IsTParam || Info->NumArgs > 0) { + if (PC || TPC || Info->NumArgs > 0) { // In order to parse command arguments we need to retokenize a few // following text tokens. TextTokenRetokenizer Retokenizer(Allocator, *this); - if (IsParam) + if (PC) parseParamCommandArgs(PC, Retokenizer); - else if (IsTParam) + else if (TPC) parseTParamCommandArgs(TPC, Retokenizer); else parseBlockCommandArgs(BC, Retokenizer, Info->NumArgs); @@ -376,7 +371,7 @@ BlockCommandComment *Parser::parseBlockCommand() { ParagraphComment *Paragraph; if (EmptyParagraph) - Paragraph = S.actOnParagraphComment(ArrayRef()); + Paragraph = S.actOnParagraphComment(None); else { BlockContentComment *Block = parseParagraphOrBlockCommand(); // Since we have checked for a block command, we should have parsed a @@ -384,10 +379,10 @@ BlockCommandComment *Parser::parseBlockCommand() { Paragraph = cast(Block); } - if (IsParam) { + if (PC) { S.actOnParamCommandFinish(PC, Paragraph); return PC; - } else if (IsTParam) { + } else if (TPC) { S.actOnTParamCommandFinish(TPC, Paragraph); return TPC; } else { diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index bf807aeb1d6..ab9d73b9178 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -471,9 +471,16 @@ static bool useInlineVisibilityHidden(const NamedDecl *D) { FD->hasBody(Def) && Def->isInlined() && !Def->hasAttr(); } -template static bool isInExternCContext(T *D) { +template static bool isFirstInExternCContext(T *D) { const T *First = D->getFirstDeclaration(); - return First->getDeclContext()->isExternCContext(); + return First->isInExternCContext(); +} + +static bool isSingleLineExternC(const Decl &D) { + if (const LinkageSpecDecl *SD = dyn_cast(D.getDeclContext())) + if (SD->getLanguage() == LinkageSpecDecl::lang_c && !SD->hasBraces()) + return true; + return false; } static LinkageInfo getLVForNamespaceScopeDecl(const NamedDecl *D, @@ -504,7 +511,8 @@ static LinkageInfo getLVForNamespaceScopeDecl(const NamedDecl *D, return PrevVar->getLinkageAndVisibility(); if (Var->getStorageClass() != SC_Extern && - Var->getStorageClass() != SC_PrivateExtern) + Var->getStorageClass() != SC_PrivateExtern && + !isSingleLineExternC(*Var)) return LinkageInfo::internal(); } @@ -540,8 +548,8 @@ static LinkageInfo getLVForNamespaceScopeDecl(const NamedDecl *D, if (D->isInAnonymousNamespace()) { const VarDecl *Var = dyn_cast(D); const FunctionDecl *Func = dyn_cast(D); - if ((!Var || !isInExternCContext(Var)) && - (!Func || !isInExternCContext(Func))) + if ((!Var || !isFirstInExternCContext(Var)) && + (!Func || !isFirstInExternCContext(Func))) return LinkageInfo::uniqueExternal(); } @@ -618,7 +626,7 @@ static LinkageInfo getLVForNamespaceScopeDecl(const NamedDecl *D, // // Note that we don't want to make the variable non-external // because of this, but unique-external linkage suits us. - if (Context.getLangOpts().CPlusPlus && !isInExternCContext(Var)) { + if (Context.getLangOpts().CPlusPlus && !isFirstInExternCContext(Var)) { LinkageInfo TypeLV = Var->getType()->getLinkageAndVisibility(); if (TypeLV.getLinkage() != ExternalLinkage) return LinkageInfo::uniqueExternal(); @@ -652,7 +660,7 @@ static LinkageInfo getLVForNamespaceScopeDecl(const NamedDecl *D, // this translation unit. However, we should use the C linkage // rules instead for extern "C" declarations. if (Context.getLangOpts().CPlusPlus && - !Function->getDeclContext()->isExternCContext() && + !Function->isInExternCContext() && Function->getType()->getLinkage() == UniqueExternalLinkage) return LinkageInfo::uniqueExternal(); @@ -866,10 +874,6 @@ bool NamedDecl::isLinkageValid() const { Linkage(CachedLinkage); } -bool NamedDecl::hasExternalLinkageUncached() const { - return getLVForDecl(this, LVForExplicitValue).getLinkage() == ExternalLinkage; -} - Linkage NamedDecl::getLinkage() const { if (HasCachedLinkage) return Linkage(CachedLinkage); @@ -993,7 +997,7 @@ static LinkageInfo getLVForLocalDecl(const NamedDecl *D, LVComputationKind computation) { if (const FunctionDecl *Function = dyn_cast(D)) { if (Function->isInAnonymousNamespace() && - !Function->getDeclContext()->isExternCContext()) + !Function->isInExternCContext()) return LinkageInfo::uniqueExternal(); // This is a "void f();" which got merged with a file static. @@ -1016,8 +1020,7 @@ static LinkageInfo getLVForLocalDecl(const NamedDecl *D, if (const VarDecl *Var = dyn_cast(D)) { if (Var->hasExternalStorage()) { - if (Var->isInAnonymousNamespace() && - !Var->getDeclContext()->isExternCContext()) + if (Var->isInAnonymousNamespace() && !Var->isInExternCContext()) return LinkageInfo::uniqueExternal(); LinkageInfo LV; @@ -1297,7 +1300,7 @@ bool NamedDecl::isCXXInstanceMember() const { if (isa(D)) D = cast(D)->getTargetDecl(); - if (isa(D) || isa(D)) + if (isa(D) || isa(D) || isa(D)) return true; if (isa(D)) return cast(D)->isInstance(); @@ -1518,8 +1521,7 @@ static LanguageLinkage getLanguageLinkageTemplate(const T &D) { // If the first decl is in an extern "C" context, any other redeclaration // will have C language linkage. If the first one is not in an extern "C" // context, we would have reported an error for any other decl being in one. - const T *First = D.getFirstDeclaration(); - if (First->getDeclContext()->isExternCContext()) + if (isFirstInExternCContext(&D)) return CLanguageLinkage; return CXXLanguageLinkage; } @@ -1545,6 +1547,29 @@ bool VarDecl::isExternC() const { return isExternCTemplate(*this); } +static bool isLinkageSpecContext(const DeclContext *DC, + LinkageSpecDecl::LanguageIDs ID) { + while (DC->getDeclKind() != Decl::TranslationUnit) { + if (DC->getDeclKind() == Decl::LinkageSpec) + return cast(DC)->getLanguage() == ID; + DC = DC->getParent(); + } + return false; +} + +template +static bool isInLanguageSpecContext(T *D, LinkageSpecDecl::LanguageIDs ID) { + return isLinkageSpecContext(D->getLexicalDeclContext(), ID); +} + +bool VarDecl::isInExternCContext() const { + return isInLanguageSpecContext(this, LinkageSpecDecl::lang_c); +} + +bool VarDecl::isInExternCXXContext() const { + return isInLanguageSpecContext(this, LinkageSpecDecl::lang_cxx); +} + VarDecl *VarDecl::getCanonicalDecl() { return getFirstDeclaration(); } @@ -1576,17 +1601,17 @@ VarDecl::DefinitionKind VarDecl::isThisDeclarationADefinition( // initializer, the declaration is an external definition for the identifier if (hasInit()) return Definition; - // AST for 'extern "C" int foo;' is annotated with 'extern'. + if (hasExternalStorage()) return DeclarationOnly; - if (hasExternalStorage()) { - for (const VarDecl *PrevVar = getPreviousDecl(); - PrevVar; PrevVar = PrevVar->getPreviousDecl()) { - if (PrevVar->getLinkage() == InternalLinkage) - return DeclarationOnly; - } - } + // [dcl.link] p7: + // A declaration directly contained in a linkage-specification is treated + // as if it contains the extern specifier for the purpose of determining + // the linkage of the declared name and whether it is a definition. + if (isSingleLineExternC(*this)) + return DeclarationOnly; + // C99 6.9.2p2: // A declaration of an object that has file scope without an initializer, // and without a storage class specifier or the scs 'static', constitutes @@ -1896,6 +1921,11 @@ SourceRange ParmVarDecl::getSourceRange() const { return SourceRange(getOuterLocStart(), ArgRange.getEnd()); } + // DeclaratorDecl considers the range of postfix types as overlapping with the + // declaration name, but this is not the case with parameters in ObjC methods. + if (isa(getDeclContext())) + return SourceRange(DeclaratorDecl::getLocStart(), getLocation()); + return DeclaratorDecl::getSourceRange(); } @@ -2061,6 +2091,14 @@ bool FunctionDecl::isExternC() const { return isExternCTemplate(*this); } +bool FunctionDecl::isInExternCContext() const { + return isInLanguageSpecContext(this, LinkageSpecDecl::lang_c); +} + +bool FunctionDecl::isInExternCXXContext() const { + return isInLanguageSpecContext(this, LinkageSpecDecl::lang_cxx); +} + bool FunctionDecl::isGlobal() const { if (const CXXMethodDecl *Method = dyn_cast(this)) return Method->isStatic(); @@ -3230,6 +3268,27 @@ BlockDecl *BlockDecl::CreateDeserialized(ASTContext &C, unsigned ID) { return new (Mem) BlockDecl(0, SourceLocation()); } +MSPropertyDecl *MSPropertyDecl::CreateDeserialized(ASTContext &C, + unsigned ID) { + void *Mem = AllocateDeserializedDecl(C, ID, sizeof(MSPropertyDecl)); + return new (Mem) MSPropertyDecl(0, SourceLocation(), DeclarationName(), + QualType(), 0, SourceLocation(), + 0, 0); +} + +CapturedDecl *CapturedDecl::Create(ASTContext &C, DeclContext *DC, + unsigned NumParams) { + unsigned Size = sizeof(CapturedDecl) + NumParams * sizeof(ImplicitParamDecl*); + return new (C.Allocate(Size)) CapturedDecl(DC, NumParams); +} + +CapturedDecl *CapturedDecl::CreateDeserialized(ASTContext &C, unsigned ID, + unsigned NumParams) { + unsigned Size = sizeof(CapturedDecl) + NumParams * sizeof(ImplicitParamDecl*); + void *Mem = AllocateDeserializedDecl(C, ID, Size); + return new (Mem) CapturedDecl(0, NumParams); +} + EnumConstantDecl *EnumConstantDecl::Create(ASTContext &C, EnumDecl *CD, SourceLocation L, IdentifierInfo *Id, QualType T, @@ -3399,7 +3458,7 @@ ImportDecl *ImportDecl::CreateDeserialized(ASTContext &C, unsigned ID, ArrayRef ImportDecl::getIdentifierLocs() const { if (!ImportedAndComplete.getInt()) - return ArrayRef(); + return None; const SourceLocation *StoredLocs = reinterpret_cast(this + 1); diff --git a/lib/AST/DeclBase.cpp b/lib/AST/DeclBase.cpp index bd6d99cd59e..084a4321d8f 100644 --- a/lib/AST/DeclBase.cpp +++ b/lib/AST/DeclBase.cpp @@ -493,6 +493,7 @@ unsigned Decl::getIdentifierNamespaceForKind(Kind DeclKind) { case NonTypeTemplateParm: case ObjCMethod: case ObjCProperty: + case MSProperty: return IDNS_Ordinary; case Label: return IDNS_Label; @@ -552,6 +553,7 @@ unsigned Decl::getIdentifierNamespaceForKind(Kind DeclKind) { case StaticAssert: case ObjCPropertyImpl: case Block: + case Captured: case TranslationUnit: case UsingDirective: @@ -702,21 +704,37 @@ void Decl::CheckAccessDeclContext() const { #endif } -DeclContext *Decl::getNonClosureContext() { - return getDeclContext()->getNonClosureAncestor(); +static Decl::Kind getKind(const Decl *D) { return D->getKind(); } +static Decl::Kind getKind(const DeclContext *DC) { return DC->getDeclKind(); } + +/// Starting at a given context (a Decl or DeclContext), look for a +/// code context that is not a closure (a lambda, block, etc.). +template static Decl *getNonClosureContext(T *D) { + if (getKind(D) == Decl::CXXMethod) { + CXXMethodDecl *MD = cast(D); + if (MD->getOverloadedOperator() == OO_Call && + MD->getParent()->isLambda()) + return getNonClosureContext(MD->getParent()->getParent()); + return MD; + } else if (FunctionDecl *FD = dyn_cast(D)) { + return FD; + } else if (ObjCMethodDecl *MD = dyn_cast(D)) { + return MD; + } else if (BlockDecl *BD = dyn_cast(D)) { + return getNonClosureContext(BD->getParent()); + } else if (CapturedDecl *CD = dyn_cast(D)) { + return getNonClosureContext(CD->getParent()); + } else { + return 0; + } } -DeclContext *DeclContext::getNonClosureAncestor() { - DeclContext *DC = this; - - // This is basically "while (DC->isClosure()) DC = DC->getParent();" - // except that it's significantly more efficient to cast to a known - // decl type and call getDeclContext() than to call getParent(). - while (isa(DC)) - DC = cast(DC)->getDeclContext(); +Decl *Decl::getNonClosureContext() { + return ::getNonClosureContext(this); +} - assert(!DC->isClosure()); - return DC; +Decl *DeclContext::getNonClosureAncestor() { + return ::getNonClosureContext(this); } //===----------------------------------------------------------------------===// @@ -801,28 +819,6 @@ bool DeclContext::isTransparentContext() const { return false; } -bool DeclContext::isExternCContext() const { - const DeclContext *DC = this; - while (DC->DeclKind != Decl::TranslationUnit) { - if (DC->DeclKind == Decl::LinkageSpec) - return cast(DC)->getLanguage() - == LinkageSpecDecl::lang_c; - DC = DC->getParent(); - } - return false; -} - -bool DeclContext::isExternCXXContext() const { - const DeclContext *DC = this; - while (DC->DeclKind != Decl::TranslationUnit) { - if (DC->DeclKind == Decl::LinkageSpec) - return cast(DC)->getLanguage() - == LinkageSpecDecl::lang_cxx; - DC = DC->getParent(); - } - return false; -} - bool DeclContext::Encloses(const DeclContext *DC) const { if (getPrimaryContext() != this) return getPrimaryContext()->Encloses(DC); @@ -838,6 +834,7 @@ DeclContext *DeclContext::getPrimaryContext() { case Decl::TranslationUnit: case Decl::LinkageSpec: case Decl::Block: + case Decl::Captured: // There is only one DeclContext for these entities. return this; @@ -1045,6 +1042,11 @@ bool DeclContext::decls_empty() const { return !FirstDecl; } +bool DeclContext::containsDecl(Decl *D) const { + return (D->getLexicalDeclContext() == this && + (D->NextInContextAndBits.getPointer() || D == LastDecl)); +} + void DeclContext::removeDecl(Decl *D) { assert(D->getLexicalDeclContext() == this && "decl being removed from non-lexical context"); diff --git a/lib/AST/DeclCXX.cpp b/lib/AST/DeclCXX.cpp index ffad9ae93cc..064649904d3 100644 --- a/lib/AST/DeclCXX.cpp +++ b/lib/AST/DeclCXX.cpp @@ -186,7 +186,7 @@ CXXRecordDecl::setBases(CXXBaseSpecifier const * const *Bases, data().IsStandardLayout = false; // Record if this base is the first non-literal field or base. - if (!hasNonLiteralTypeFieldsOrBases() && !BaseType->isLiteralType()) + if (!hasNonLiteralTypeFieldsOrBases() && !BaseType->isLiteralType(C)) data().HasNonLiteralTypeFieldsOrBases = true; // Now go through all virtual bases of this base and add them. @@ -505,7 +505,7 @@ void CXXRecordDecl::addedMember(Decl *D) { // C++ [dcl.init.aggr]p1: // An aggregate is an array or a class with no user-declared // constructors [...]. - // C++0x [dcl.init.aggr]p1: + // C++11 [dcl.init.aggr]p1: // An aggregate is an array or a class with no user-provided // constructors [...]. if (getASTContext().getLangOpts().CPlusPlus11 @@ -542,22 +542,28 @@ void CXXRecordDecl::addedMember(Decl *D) { // Keep the list of conversion functions up-to-date. if (CXXConversionDecl *Conversion = dyn_cast(D)) { - // FIXME: We intentionally don't use the decl's access here because it - // hasn't been set yet. That's really just a misdesign in Sema. + // FIXME: We use the 'unsafe' accessor for the access specifier here, + // because Sema may not have set it yet. That's really just a misdesign + // in Sema. However, LLDB *will* have set the access specifier correctly, + // and adds declarations after the class is technically completed, + // so completeDefinition()'s overriding of the access specifiers doesn't + // work. + AccessSpecifier AS = Conversion->getAccessUnsafe(); + if (Conversion->getPrimaryTemplate()) { // We don't record specializations. } else if (FunTmpl) { if (FunTmpl->getPreviousDecl()) data().Conversions.replace(FunTmpl->getPreviousDecl(), - FunTmpl); + FunTmpl, AS); else - data().Conversions.addDecl(getASTContext(), FunTmpl); + data().Conversions.addDecl(getASTContext(), FunTmpl, AS); } else { if (Conversion->getPreviousDecl()) data().Conversions.replace(Conversion->getPreviousDecl(), - Conversion); + Conversion, AS); else - data().Conversions.addDecl(getASTContext(), Conversion); + data().Conversions.addDecl(getASTContext(), Conversion, AS); } } @@ -670,7 +676,7 @@ void CXXRecordDecl::addedMember(Decl *D) { } // Record if this field is the first non-literal or volatile field or base. - if (!T->isLiteralType() || T.isVolatileQualified()) + if (!T->isLiteralType(Context) || T.isVolatileQualified()) data().HasNonLiteralTypeFieldsOrBases = true; if (Field->hasInClassInitializer()) { @@ -684,7 +690,10 @@ void CXXRecordDecl::addedMember(Decl *D) { // 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; + // + // This rule was removed in C++1y. + if (!getASTContext().getLangOpts().CPlusPlus1y) + data().Aggregate = false; // C++11 [class]p10: // A POD struct is [...] a trivial class. @@ -836,7 +845,7 @@ void CXXRecordDecl::addedMember(Decl *D) { } } else { // Base element type of field is a non-class type. - if (!T->isLiteralType() || + if (!T->isLiteralType(Context) || (!Field->hasInClassInitializer() && !isUnion())) data().DefaultedDefaultConstructorIsConstexpr = false; @@ -1252,20 +1261,7 @@ bool CXXRecordDecl::mayBeAbstract() const { void CXXMethodDecl::anchor() { } bool CXXMethodDecl::isStatic() const { - const CXXMethodDecl *MD = this; - for (;;) { - const CXXMethodDecl *C = MD->getCanonicalDecl(); - if (C != MD) { - MD = C; - continue; - } - - FunctionTemplateSpecializationInfo *Info = - MD->getTemplateSpecializationInfo(); - if (!Info) - break; - MD = cast(Info->getTemplate()->getTemplatedDecl()); - } + const CXXMethodDecl *MD = getCanonicalDecl(); if (MD->getStorageClass() == SC_Static) return true; @@ -1820,14 +1816,14 @@ LinkageSpecDecl *LinkageSpecDecl::Create(ASTContext &C, SourceLocation ExternLoc, SourceLocation LangLoc, LanguageIDs Lang, - SourceLocation RBraceLoc) { - return new (C) LinkageSpecDecl(DC, ExternLoc, LangLoc, Lang, RBraceLoc); + bool HasBraces) { + return new (C) LinkageSpecDecl(DC, ExternLoc, LangLoc, Lang, HasBraces); } LinkageSpecDecl *LinkageSpecDecl::CreateDeserialized(ASTContext &C, unsigned ID) { void *Mem = AllocateDeserializedDecl(C, ID, sizeof(LinkageSpecDecl)); return new (Mem) LinkageSpecDecl(0, SourceLocation(), SourceLocation(), - lang_c, SourceLocation()); + lang_c, false); } void UsingDirectiveDecl::anchor() { } diff --git a/lib/AST/DeclObjC.cpp b/lib/AST/DeclObjC.cpp index 5f5ba52947d..4ddbb22199b 100644 --- a/lib/AST/DeclObjC.cpp +++ b/lib/AST/DeclObjC.cpp @@ -443,9 +443,12 @@ ObjCInterfaceDecl *ObjCInterfaceDecl::lookupInheritedClass( /// lookupMethod - This method returns an instance/class method by looking in /// the class, its categories, and its super classes (using a linear search). +/// When argument category "C" is specified, any implicit method found +/// in this category is ignored. ObjCMethodDecl *ObjCInterfaceDecl::lookupMethod(Selector Sel, bool isInstance, - bool shallowCategoryLookup) const { + bool shallowCategoryLookup, + const ObjCCategoryDecl *C) const { // FIXME: Should make sure no callers ever do this. if (!hasDefinition()) return 0; @@ -469,20 +472,22 @@ ObjCMethodDecl *ObjCInterfaceDecl::lookupMethod(Selector Sel, // Didn't find one yet - now look through categories. for (ObjCInterfaceDecl::visible_categories_iterator - Cat = ClassDecl->visible_categories_begin(), - CatEnd = ClassDecl->visible_categories_end(); + Cat = ClassDecl->visible_categories_begin(), + CatEnd = ClassDecl->visible_categories_end(); Cat != CatEnd; ++Cat) { if ((MethodDecl = Cat->getMethod(Sel, isInstance))) - return MethodDecl; + if (C != (*Cat) || !MethodDecl->isImplicit()) + return MethodDecl; if (!shallowCategoryLookup) { // Didn't find one yet - look through protocols. const ObjCList &Protocols = - Cat->getReferencedProtocols(); + Cat->getReferencedProtocols(); for (ObjCList::iterator I = Protocols.begin(), E = Protocols.end(); I != E; ++I) if ((MethodDecl = (*I)->lookupMethod(Sel, isInstance))) - return MethodDecl; + if (C != (*Cat) || !MethodDecl->isImplicit()) + return MethodDecl; } } @@ -599,12 +604,12 @@ void ObjCMethodDecl::setMethodParams(ASTContext &C, assert((!SelLocs.empty() || isImplicit()) && "No selector locs for non-implicit method"); if (isImplicit()) - return setParamsAndSelLocs(C, Params, ArrayRef()); + return setParamsAndSelLocs(C, Params, llvm::None); SelLocsKind = hasStandardSelectorLocs(getSelector(), SelLocs, Params, DeclEndLoc); if (SelLocsKind != SelLoc_NonStandard) - return setParamsAndSelLocs(C, Params, ArrayRef()); + return setParamsAndSelLocs(C, Params, llvm::None); setParamsAndSelLocs(C, Params, SelLocs); } @@ -959,52 +964,6 @@ static void collectOverriddenMethodsSlow(const ObjCMethodDecl *Method, } } -static void collectOnCategoriesAfterLocation(SourceLocation Loc, - const ObjCInterfaceDecl *Class, - SourceManager &SM, - const ObjCMethodDecl *Method, - SmallVectorImpl &Methods) { - if (!Class) - return; - - for (ObjCInterfaceDecl::known_categories_iterator - Cat = Class->known_categories_begin(), - CatEnd = Class->known_categories_end(); - Cat != CatEnd; ++Cat) { - if (SM.isBeforeInTranslationUnit(Loc, Cat->getLocation())) - CollectOverriddenMethodsRecurse(*Cat, Method, Methods, true); - } - - collectOnCategoriesAfterLocation(Loc, Class->getSuperClass(), SM, - Method, Methods); -} - -/// \brief Faster collection that is enabled when ObjCMethodDecl::isOverriding() -/// returns false. -/// You'd think that in that case there are no overrides but categories can -/// "introduce" new overridden methods that are missed by Sema because the -/// overrides lookup that it does for methods, inside implementations, will -/// stop at the interface level (if there is a method there) and not look -/// further in super classes. -/// Methods in an implementation can overide methods in super class's category -/// but not in current class's category. But, such methods -static void collectOverriddenMethodsFast(SourceManager &SM, - const ObjCMethodDecl *Method, - SmallVectorImpl &Methods) { - assert(!Method->isOverriding()); - - const ObjCContainerDecl * - ContD = cast(Method->getDeclContext()); - if (isa(ContD) || isa(ContD)) - return; - const ObjCInterfaceDecl *Class = Method->getClassInterface(); - if (!Class) - return; - - collectOnCategoriesAfterLocation(Class->getLocation(), Class->getSuperClass(), - SM, Method, Methods); -} - void ObjCMethodDecl::getOverriddenMethods( SmallVectorImpl &Overridden) const { const ObjCMethodDecl *Method = this; @@ -1014,10 +973,7 @@ void ObjCMethodDecl::getOverriddenMethods( getMethod(Method->getSelector(), Method->isInstanceMethod()); } - if (!Method->isOverriding()) { - collectOverriddenMethodsFast(getASTContext().getSourceManager(), - Method, Overridden); - } else { + if (Method->isOverriding()) { collectOverriddenMethodsSlow(Method, Overridden); assert(!Overridden.empty() && "ObjCMethodDecl's overriding bit is not as expected"); @@ -1692,12 +1648,13 @@ ObjCImplementationDecl::Create(ASTContext &C, DeclContext *DC, ObjCInterfaceDecl *SuperDecl, SourceLocation nameLoc, SourceLocation atStartLoc, + SourceLocation superLoc, SourceLocation IvarLBraceLoc, SourceLocation IvarRBraceLoc) { if (ClassInterface && ClassInterface->hasDefinition()) ClassInterface = ClassInterface->getDefinition(); return new (C) ObjCImplementationDecl(DC, ClassInterface, SuperDecl, - nameLoc, atStartLoc, + nameLoc, atStartLoc, superLoc, IvarLBraceLoc, IvarRBraceLoc); } diff --git a/lib/AST/DeclPrinter.cpp b/lib/AST/DeclPrinter.cpp index c3bf8f89b29..d47972bc613 100644 --- a/lib/AST/DeclPrinter.cpp +++ b/lib/AST/DeclPrinter.cpp @@ -487,7 +487,7 @@ void DeclPrinter::VisitFunctionDecl(FunctionDecl *D) { for (CXXConstructorDecl::init_const_iterator B = CDecl->init_begin(), E = CDecl->init_end(); B != E; ++B) { - CXXCtorInitializer * BMInitializer = (*B); + CXXCtorInitializer *BMInitializer = (*B); if (BMInitializer->isInClassMemberInitializer()) continue; @@ -617,7 +617,8 @@ void DeclPrinter::VisitFieldDecl(FieldDecl *D) { if (!Policy.SuppressSpecifiers && D->isModulePrivate()) Out << "__module_private__ "; - Out << D->getType().stream(Policy, D->getName()); + Out << D->getASTContext().getUnqualifiedObjCPointerType(D->getType()). + stream(Policy, D->getName()); if (D->isBitField()) { Out << " : "; @@ -641,16 +642,30 @@ void DeclPrinter::VisitLabelDecl(LabelDecl *D) { void DeclPrinter::VisitVarDecl(VarDecl *D) { - StorageClass SC = D->getStorageClass(); - if (!Policy.SuppressSpecifiers && SC != SC_None) - Out << VarDecl::getStorageClassSpecifierString(SC) << " "; + if (!Policy.SuppressSpecifiers) { + StorageClass SC = D->getStorageClass(); + if (SC != SC_None) + Out << VarDecl::getStorageClassSpecifierString(SC) << " "; - if (!Policy.SuppressSpecifiers && D->isThreadSpecified()) - Out << "__thread "; - if (!Policy.SuppressSpecifiers && D->isModulePrivate()) - Out << "__module_private__ "; + switch (D->getTSCSpec()) { + case TSCS_unspecified: + break; + case TSCS___thread: + Out << "__thread "; + break; + case TSCS__Thread_local: + Out << "_Thread_local "; + break; + case TSCS_thread_local: + Out << "thread_local "; + break; + } + + if (D->isModulePrivate()) + Out << "__module_private__ "; + } - QualType T = D->getType(); + QualType T = D->getASTContext().getUnqualifiedObjCPointerType(D->getType()); if (ParmVarDecl *Parm = dyn_cast(D)) T = Parm->getOriginalType(); T.print(Out, Policy, D->getName()); @@ -899,7 +914,8 @@ void DeclPrinter::VisitObjCMethodDecl(ObjCMethodDecl *OMD) { else Out << "+ "; if (!OMD->getResultType().isNull()) - Out << '(' << OMD->getResultType().getAsString(Policy) << ")"; + Out << '(' << OMD->getASTContext().getUnqualifiedObjCPointerType(OMD->getResultType()). + getAsString(Policy) << ")"; std::string name = OMD->getSelector().getAsString(); std::string::size_type pos, lastPos = 0; @@ -908,7 +924,8 @@ void DeclPrinter::VisitObjCMethodDecl(ObjCMethodDecl *OMD) { // FIXME: selector is missing here! pos = name.find_first_of(':', lastPos); Out << " " << name.substr(lastPos, pos - lastPos); - Out << ":(" << (*PI)->getType().getAsString(Policy) << ')' << **PI; + Out << ":(" << (*PI)->getASTContext().getUnqualifiedObjCPointerType((*PI)->getType()). + getAsString(Policy) << ')' << **PI; lastPos = pos + 1; } @@ -941,7 +958,8 @@ void DeclPrinter::VisitObjCImplementationDecl(ObjCImplementationDecl *OID) { Indentation += Policy.Indentation; for (ObjCImplementationDecl::ivar_iterator I = OID->ivar_begin(), E = OID->ivar_end(); I != E; ++I) { - Indent() << I->getType().getAsString(Policy) << ' ' << **I << ";\n"; + Indent() << I->getASTContext().getUnqualifiedObjCPointerType(I->getType()). + getAsString(Policy) << ' ' << **I << ";\n"; } Indentation -= Policy.Indentation; Out << "}\n"; @@ -979,7 +997,8 @@ 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->getASTContext().getUnqualifiedObjCPointerType(I->getType()). + getAsString(Policy) << ' ' << **I << ";\n"; } Indentation -= Policy.Indentation; Out << "}\n"; @@ -1030,7 +1049,8 @@ void DeclPrinter::VisitObjCCategoryDecl(ObjCCategoryDecl *PID) { Indentation += Policy.Indentation; for (ObjCCategoryDecl::ivar_iterator I = PID->ivar_begin(), E = PID->ivar_end(); I != E; ++I) { - Indent() << I->getType().getAsString(Policy) << ' ' << **I << ";\n"; + Indent() << I->getASTContext().getUnqualifiedObjCPointerType(I->getType()). + getAsString(Policy) << ' ' << **I << ";\n"; } Indentation -= Policy.Indentation; Out << "}\n"; @@ -1116,7 +1136,8 @@ void DeclPrinter::VisitObjCPropertyDecl(ObjCPropertyDecl *PDecl) { (void) first; // Silence dead store warning due to idiomatic code. Out << " )"; } - Out << ' ' << PDecl->getType().getAsString(Policy) << ' ' << *PDecl; + Out << ' ' << PDecl->getASTContext().getUnqualifiedObjCPointerType(PDecl->getType()). + getAsString(Policy) << ' ' << *PDecl; if (Policy.PolishForDeclaration) Out << ';'; } diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index b97f4d1d3a9..9538ddf9416 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -100,11 +100,20 @@ Expr::skipRValueSubobjectAdjustments( const Expr * Expr::findMaterializedTemporary(const MaterializeTemporaryExpr *&MTE) const { const Expr *E = this; + + // This might be a default initializer for a reference member. Walk over the + // wrapper node for that. + if (const CXXDefaultInitExpr *DAE = dyn_cast(E)) + E = DAE->getExpr(); + // Look through single-element init lists that claim to be lvalues. They're // just syntactic wrappers in this case. if (const InitListExpr *ILE = dyn_cast(E)) { - if (ILE->getNumInits() == 1 && ILE->isGLValue()) + if (ILE->getNumInits() == 1 && ILE->isGLValue()) { E = ILE->getInit(0); + if (const CXXDefaultInitExpr *DAE = dyn_cast(E)) + E = DAE->getExpr(); + } } // Look through expressions for materialized temporaries (for now). @@ -280,7 +289,7 @@ static void computeDeclRefDependence(ASTContext &Ctx, NamedDecl *D, QualType T, // expression that is value-dependent [C++11] if (VarDecl *Var = dyn_cast(D)) { if ((Ctx.getLangOpts().CPlusPlus11 ? - Var->getType()->isLiteralType() : + Var->getType()->isLiteralType(Ctx) : Var->getType()->isIntegralOrEnumerationType()) && (Var->getType().isConstQualified() || Var->getType()->isReferenceType())) { @@ -2174,6 +2183,9 @@ bool Expr::isUnusedResultAWarning(const Expr *&WarnE, SourceLocation &Loc, case CXXDefaultArgExprClass: return (cast(this) ->getExpr()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx)); + case CXXDefaultInitExprClass: + return (cast(this) + ->getExpr()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx)); case CXXNewExprClass: // FIXME: In theory, there might be new expressions that don't have side @@ -2789,6 +2801,7 @@ bool Expr::HasSideEffects(const ASTContext &Ctx) const { return false; case CallExprClass: + case MSPropertyRefExprClass: case CompoundAssignOperatorClass: case VAArgExprClass: case AtomicExprClass: @@ -2850,6 +2863,12 @@ bool Expr::HasSideEffects(const ASTContext &Ctx) const { case CXXDefaultArgExprClass: return cast(this)->getExpr()->HasSideEffects(Ctx); + case CXXDefaultInitExprClass: + if (const Expr *E = cast(this)->getExpr()) + return E->HasSideEffects(Ctx); + // If we've not yet parsed the initializer, assume it has side-effects. + return true; + case CXXDynamicCastExprClass: { // A dynamic_cast expression has side-effects if it can throw. const CXXDynamicCastExpr *DCE = cast(this); @@ -3037,8 +3056,12 @@ Expr::isNullPointerConstant(ASTContext &Ctx, return GE->getResultExpr()->isNullPointerConstant(Ctx, NPC); } else if (const CXXDefaultArgExpr *DefaultArg = dyn_cast(this)) { - // See through default argument expressions + // See through default argument expressions. return DefaultArg->getExpr()->isNullPointerConstant(Ctx, NPC); + } else if (const CXXDefaultInitExpr *DefaultInit + = dyn_cast(this)) { + // See through default initializer expressions. + return DefaultInit->getExpr()->isNullPointerConstant(Ctx, NPC); } else if (isa(this)) { // The GNU __null extension is always a null pointer constant. return NPCK_GNUNull; @@ -3126,7 +3149,7 @@ bool Expr::isObjCSelfExpr() const { return M->getSelfDecl() == Param; } -FieldDecl *Expr::getBitField() { +FieldDecl *Expr::getSourceBitField() { Expr *E = this->IgnoreParens(); while (ImplicitCastExpr *ICE = dyn_cast(E)) { @@ -3142,6 +3165,11 @@ FieldDecl *Expr::getBitField() { if (Field->isBitField()) return Field; + if (ObjCIvarRefExpr *IvarRef = dyn_cast(E)) + if (FieldDecl *Ivar = dyn_cast(IvarRef->getDecl())) + if (Ivar->isBitField()) + return Ivar; + if (DeclRefExpr *DeclRef = dyn_cast(E)) if (FieldDecl *Field = dyn_cast(DeclRef->getDecl())) if (Field->isBitField()) @@ -3149,10 +3177,10 @@ FieldDecl *Expr::getBitField() { if (BinaryOperator *BinOp = dyn_cast(E)) { if (BinOp->isAssignmentOp() && BinOp->getLHS()) - return BinOp->getLHS()->getBitField(); + return BinOp->getLHS()->getSourceBitField(); if (BinOp->getOpcode() == BO_Comma && BinOp->getRHS()) - return BinOp->getRHS()->getBitField(); + return BinOp->getRHS()->getSourceBitField(); } return 0; diff --git a/lib/AST/ExprCXX.cpp b/lib/AST/ExprCXX.cpp index 12a47fcd782..402d7b532b2 100644 --- a/lib/AST/ExprCXX.cpp +++ b/lib/AST/ExprCXX.cpp @@ -178,8 +178,7 @@ CXXPseudoDestructorExpr::CXXPseudoDestructorExpr(ASTContext &Context, SourceLocation ColonColonLoc, SourceLocation TildeLoc, PseudoDestructorTypeStorage DestroyedType) : Expr(CXXPseudoDestructorExprClass, - Context.getPointerType(Context.getFunctionType(Context.VoidTy, - ArrayRef(), + Context.getPointerType(Context.getFunctionType(Context.VoidTy, None, FunctionProtoType::ExtProtoInfo())), VK_RValue, OK_Ordinary, /*isTypeDependent=*/(Base->isTypeDependent() || @@ -704,6 +703,17 @@ CXXDefaultArgExpr::Create(ASTContext &C, SourceLocation Loc, SubExpr); } +CXXDefaultInitExpr::CXXDefaultInitExpr(ASTContext &C, SourceLocation Loc, + FieldDecl *Field, QualType T) + : Expr(CXXDefaultInitExprClass, T.getNonLValueExprType(C), + T->isLValueReferenceType() ? VK_LValue : T->isRValueReferenceType() + ? VK_XValue + : VK_RValue, + /*FIXME*/ OK_Ordinary, false, false, false, false), + Field(Field), Loc(Loc) { + assert(Field->hasInClassInitializer()); +} + CXXTemporary *CXXTemporary::Create(ASTContext &C, const CXXDestructorDecl *Destructor) { return new (C) CXXTemporary(Destructor); diff --git a/lib/AST/ExprClassification.cpp b/lib/AST/ExprClassification.cpp index 61bc3e2de5c..bcb6d4e809b 100644 --- a/lib/AST/ExprClassification.cpp +++ b/lib/AST/ExprClassification.cpp @@ -134,6 +134,7 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) { // FIXME: ObjC++0x might have different rules case Expr::ObjCIvarRefExprClass: case Expr::FunctionParmPackExprClass: + case Expr::MSPropertyRefExprClass: return Cl::CL_LValue; // C99 6.5.2.5p5 says that compound literals are lvalues. @@ -297,6 +298,10 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) { case Expr::CXXDefaultArgExprClass: return ClassifyInternal(Ctx, cast(E)->getExpr()); + // Same idea for default initializers. + case Expr::CXXDefaultInitExprClass: + return ClassifyInternal(Ctx, cast(E)->getExpr()); + // Same idea for temporary binding. case Expr::CXXBindTemporaryExprClass: return ClassifyInternal(Ctx, cast(E)->getSubExpr()); diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index ae86150ee2a..8c650290b57 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -286,21 +286,37 @@ namespace { /// ParmBindings - Parameter bindings for this function call, indexed by /// parameters' function scope indices. - const APValue *Arguments; + APValue *Arguments; // Note that we intentionally use std::map here so that references to // values are stable. - typedef std::map MapTy; + typedef std::map MapTy; typedef MapTy::const_iterator temp_iterator; /// Temporaries - Temporary lvalues materialized within this stack frame. MapTy Temporaries; CallStackFrame(EvalInfo &Info, SourceLocation CallLoc, const FunctionDecl *Callee, const LValue *This, - const APValue *Arguments); + APValue *Arguments); ~CallStackFrame(); }; + /// Temporarily override 'this'. + class ThisOverrideRAII { + public: + ThisOverrideRAII(CallStackFrame &Frame, const LValue *NewThis, bool Enable) + : Frame(Frame), OldThis(Frame.This) { + if (Enable) + Frame.This = NewThis; + } + ~ThisOverrideRAII() { + Frame.This = OldThis; + } + private: + CallStackFrame &Frame; + const LValue *OldThis; + }; + /// A partial diagnostic which we might know in advance that we are not going /// to emit. class OptionalDiagnostic { @@ -581,7 +597,7 @@ void SubobjectDesignator::diagnosePointerArithmetic(EvalInfo &Info, CallStackFrame::CallStackFrame(EvalInfo &Info, SourceLocation CallLoc, const FunctionDecl *Callee, const LValue *This, - const APValue *Arguments) + APValue *Arguments) : Info(Info), Caller(Info.CurrentCall), CallLoc(CallLoc), Callee(Callee), Index(Info.NextCallIndex++), This(This), Arguments(Arguments) { Info.CurrentCall = this; @@ -897,6 +913,18 @@ static bool EvaluateComplex(const Expr *E, ComplexValue &Res, EvalInfo &Info); // Misc utilities //===----------------------------------------------------------------------===// +/// Evaluate an expression to see if it had side-effects, and discard its +/// result. +/// \return \c true if the caller should keep evaluating. +static bool EvaluateIgnoredValue(EvalInfo &Info, const Expr *E) { + APValue Scratch; + if (!Evaluate(Scratch, Info, E)) { + Info.EvalStatus.HasSideEffects = true; + return Info.keepEvaluatingAfterFailure(); + } + return true; +} + /// Should this call expression be treated as a string literal? static bool IsStringLiteralCall(const CallExpr *E) { unsigned Builtin = E->isBuiltinCall(); @@ -999,7 +1027,7 @@ static bool CheckLValueConstantExpression(EvalInfo &Info, SourceLocation Loc, // Check if this is a thread-local variable. if (const ValueDecl *VD = Base.dyn_cast()) { if (const VarDecl *Var = dyn_cast(VD)) { - if (Var->isThreadSpecified()) + if (Var->getTLSKind()) return false; } } @@ -1030,7 +1058,7 @@ static bool CheckLValueConstantExpression(EvalInfo &Info, SourceLocation Loc, /// Check that this core constant expression is of literal type, and if not, /// produce an appropriate diagnostic. static bool CheckLiteralType(EvalInfo &Info, const Expr *E) { - if (!E->isRValue() || E->getType()->isLiteralType()) + if (!E->isRValue() || E->getType()->isLiteralType(Info.Ctx)) return true; // Prvalue constant expressions must be of literal types. @@ -1427,9 +1455,16 @@ static bool HandleLValueComplexElement(EvalInfo &Info, const Expr *E, } /// Try to evaluate the initializer for a variable declaration. -static bool EvaluateVarDeclInit(EvalInfo &Info, const Expr *E, - const VarDecl *VD, - CallStackFrame *Frame, APValue &Result) { +/// +/// \param Info Information about the ongoing evaluation. +/// \param E An expression to be used when printing diagnostics. +/// \param VD The variable whose initializer should be obtained. +/// \param Frame The frame in which the variable was created. Must be null +/// if this variable is not local to the evaluation. +/// \param Result Filled in with a pointer to the value of the variable. +static bool evaluateVarDeclInit(EvalInfo &Info, const Expr *E, + const VarDecl *VD, CallStackFrame *Frame, + APValue *&Result) { // If this is a parameter to an active constexpr function call, perform // argument substitution. if (const ParmVarDecl *PVD = dyn_cast(VD)) { @@ -1441,10 +1476,19 @@ static bool EvaluateVarDeclInit(EvalInfo &Info, const Expr *E, Info.Diag(E, diag::note_invalid_subexpr_in_const_expr); return false; } - Result = Frame->Arguments[PVD->getFunctionScopeIndex()]; + Result = &Frame->Arguments[PVD->getFunctionScopeIndex()]; return true; } + // If this is a local variable, dig out its value. + if (Frame) { + Result = &Frame->Temporaries[VD]; + // If we've carried on past an unevaluatable local variable initializer, + // we can't go any further. This can happen during potential constant + // expression checking. + return !Result->isUninit(); + } + // Dig out the initializer, and use the declaration which it's attached to. const Expr *Init = VD->getAnyInitializer(VD); if (!Init || Init->isValueDependent()) { @@ -1458,8 +1502,8 @@ static bool EvaluateVarDeclInit(EvalInfo &Info, const Expr *E, // If we're currently evaluating the initializer of this declaration, use that // in-flight value. if (Info.EvaluatingDecl == VD) { - Result = *Info.EvaluatingDeclValue; - return !Result.isUninit(); + Result = Info.EvaluatingDeclValue; + return !Result->isUninit(); } // Never evaluate the initializer of a weak variable. We can't be sure that @@ -1485,7 +1529,7 @@ static bool EvaluateVarDeclInit(EvalInfo &Info, const Expr *E, Info.addNotes(Notes); } - Result = *VD->getEvaluatedValue(); + Result = VD->getEvaluatedValue(); return true; } @@ -1509,15 +1553,15 @@ static unsigned getBaseIndex(const CXXRecordDecl *Derived, llvm_unreachable("base class missing from derived class's bases list"); } -/// Extract the value of a character from a string literal. CharType is used to -/// determine the expected signedness of the result -- a string literal used to -/// initialize an array of 'signed char' or 'unsigned char' might contain chars -/// of the wrong signedness. -static APSInt ExtractStringLiteralCharacter(EvalInfo &Info, const Expr *Lit, - uint64_t Index, QualType CharType) { +/// Extract the value of a character from a string literal. +static APSInt extractStringLiteralCharacter(EvalInfo &Info, const Expr *Lit, + uint64_t Index) { // FIXME: Support PredefinedExpr, ObjCEncodeExpr, MakeStringConstant - const StringLiteral *S = dyn_cast(Lit); - assert(S && "unexpected string literal expression kind"); + const StringLiteral *S = cast(Lit); + const ConstantArrayType *CAT = + Info.Ctx.getAsConstantArrayType(S->getType()); + assert(CAT && "string literal isn't an array"); + QualType CharType = CAT->getElementType(); assert(CharType->isIntegerType() && "unexpected character type"); APSInt Value(S->getCharByteWidth() * Info.Ctx.getCharWidth(), @@ -1527,26 +1571,99 @@ static APSInt ExtractStringLiteralCharacter(EvalInfo &Info, const Expr *Lit, return Value; } -/// Extract the designated sub-object of an rvalue. -static bool ExtractSubobject(EvalInfo &Info, const Expr *E, - APValue &Obj, QualType ObjType, - const SubobjectDesignator &Sub, QualType SubType) { +// Expand a string literal into an array of characters. +static void expandStringLiteral(EvalInfo &Info, const Expr *Lit, + APValue &Result) { + const StringLiteral *S = cast(Lit); + const ConstantArrayType *CAT = + Info.Ctx.getAsConstantArrayType(S->getType()); + assert(CAT && "string literal isn't an array"); + QualType CharType = CAT->getElementType(); + assert(CharType->isIntegerType() && "unexpected character type"); + + unsigned Elts = CAT->getSize().getZExtValue(); + Result = APValue(APValue::UninitArray(), + std::min(S->getLength(), Elts), Elts); + APSInt Value(S->getCharByteWidth() * Info.Ctx.getCharWidth(), + CharType->isUnsignedIntegerType()); + if (Result.hasArrayFiller()) + Result.getArrayFiller() = APValue(Value); + for (unsigned I = 0, N = Result.getArrayInitializedElts(); I != N; ++I) { + Value = S->getCodeUnit(I); + Result.getArrayInitializedElt(I) = APValue(Value); + } +} + +// Expand an array so that it has more than Index filled elements. +static void expandArray(APValue &Array, unsigned Index) { + unsigned Size = Array.getArraySize(); + assert(Index < Size); + + // Always at least double the number of elements for which we store a value. + unsigned OldElts = Array.getArrayInitializedElts(); + unsigned NewElts = std::max(Index+1, OldElts * 2); + NewElts = std::min(Size, std::max(NewElts, 8u)); + + // Copy the data across. + APValue NewValue(APValue::UninitArray(), NewElts, Size); + for (unsigned I = 0; I != OldElts; ++I) + NewValue.getArrayInitializedElt(I).swap(Array.getArrayInitializedElt(I)); + for (unsigned I = OldElts; I != NewElts; ++I) + NewValue.getArrayInitializedElt(I) = Array.getArrayFiller(); + if (NewValue.hasArrayFiller()) + NewValue.getArrayFiller() = Array.getArrayFiller(); + Array.swap(NewValue); +} + +/// Kinds of access we can perform on an object. +enum AccessKinds { + AK_Read, + AK_Assign, + AK_Increment, + AK_Decrement +}; + +/// A handle to a complete object (an object that is not a subobject of +/// another object). +struct CompleteObject { + /// The value of the complete object. + APValue *Value; + /// The type of the complete object. + QualType Type; + + CompleteObject() : Value(0) {} + CompleteObject(APValue *Value, QualType Type) + : Value(Value), Type(Type) { + assert(Value && "missing value for complete object"); + } + + operator bool() const { return Value; } +}; + +/// Find the designated sub-object of an rvalue. +template +typename SubobjectHandler::result_type +findSubobject(EvalInfo &Info, const Expr *E, const CompleteObject &Obj, + const SubobjectDesignator &Sub, SubobjectHandler &handler) { if (Sub.Invalid) // A diagnostic will have already been produced. - return false; + return handler.failed(); if (Sub.isOnePastTheEnd()) { - Info.Diag(E, Info.getLangOpts().CPlusPlus11 ? - (unsigned)diag::note_constexpr_read_past_end : - (unsigned)diag::note_invalid_subexpr_in_const_expr); - return false; + if (Info.getLangOpts().CPlusPlus11) + Info.Diag(E, diag::note_constexpr_access_past_end) + << handler.AccessKind; + else + Info.Diag(E); + return handler.failed(); } if (Sub.Entries.empty()) - return true; - if (Info.CheckingPotentialConstantExpression && Obj.isUninit()) + return handler.found(*Obj.Value, Obj.Type); + if (Info.CheckingPotentialConstantExpression && Obj.Value->isUninit()) // This object might be initialized later. - return false; + return handler.failed(); - APValue *O = &Obj; + APValue *O = Obj.Value; + QualType ObjType = Obj.Type; // Walk the designator's path to find the subobject. for (unsigned I = 0, N = Sub.Entries.size(); I != N; ++I) { if (ObjType->isArrayType()) { @@ -1557,49 +1674,67 @@ static bool ExtractSubobject(EvalInfo &Info, const Expr *E, if (CAT->getSize().ule(Index)) { // Note, it should not be possible to form a pointer with a valid // designator which points more than one past the end of the array. - Info.Diag(E, Info.getLangOpts().CPlusPlus11 ? - (unsigned)diag::note_constexpr_read_past_end : - (unsigned)diag::note_invalid_subexpr_in_const_expr); - return false; + if (Info.getLangOpts().CPlusPlus11) + Info.Diag(E, diag::note_constexpr_access_past_end) + << handler.AccessKind; + else + Info.Diag(E); + return handler.failed(); } + + ObjType = CAT->getElementType(); + // An array object is represented as either an Array APValue or as an // LValue which refers to a string literal. if (O->isLValue()) { assert(I == N - 1 && "extracting subobject of character?"); assert(!O->hasLValuePath() || O->getLValuePath().empty()); - Obj = APValue(ExtractStringLiteralCharacter( - Info, O->getLValueBase().get(), Index, SubType)); - return true; - } else if (O->getArrayInitializedElts() > Index) + if (handler.AccessKind != AK_Read) + expandStringLiteral(Info, O->getLValueBase().get(), + *O); + else + return handler.foundString(*O, ObjType, Index); + } + + if (O->getArrayInitializedElts() > Index) O = &O->getArrayInitializedElt(Index); - else + else if (handler.AccessKind != AK_Read) { + expandArray(*O, Index); + O = &O->getArrayInitializedElt(Index); + } else O = &O->getArrayFiller(); - ObjType = CAT->getElementType(); } else if (ObjType->isAnyComplexType()) { // Next subobject is a complex number. uint64_t Index = Sub.Entries[I].ArrayIndex; if (Index > 1) { - Info.Diag(E, Info.getLangOpts().CPlusPlus11 ? - (unsigned)diag::note_constexpr_read_past_end : - (unsigned)diag::note_invalid_subexpr_in_const_expr); - return false; + if (Info.getLangOpts().CPlusPlus11) + Info.Diag(E, diag::note_constexpr_access_past_end) + << handler.AccessKind; + else + Info.Diag(E); + return handler.failed(); } + + bool WasConstQualified = ObjType.isConstQualified(); + ObjType = ObjType->castAs()->getElementType(); + if (WasConstQualified) + ObjType.addConst(); + assert(I == N - 1 && "extracting subobject of scalar?"); if (O->isComplexInt()) { - Obj = APValue(Index ? O->getComplexIntImag() - : O->getComplexIntReal()); + return handler.found(Index ? O->getComplexIntImag() + : O->getComplexIntReal(), ObjType); } else { assert(O->isComplexFloat()); - Obj = APValue(Index ? O->getComplexFloatImag() - : O->getComplexFloatReal()); + return handler.found(Index ? O->getComplexFloatImag() + : O->getComplexFloatReal(), ObjType); } - return true; } else if (const FieldDecl *Field = getAsField(Sub.Entries[I])) { - if (Field->isMutable()) { + if (Field->isMutable() && handler.AccessKind == AK_Read) { Info.Diag(E, diag::note_constexpr_ltor_mutable, 1) << Field; Info.Note(Field->getLocation(), diag::note_declared_at); - return false; + return handler.failed(); } // Next subobject is a class, struct or union field. @@ -1608,49 +1743,150 @@ static bool ExtractSubobject(EvalInfo &Info, const Expr *E, const FieldDecl *UnionField = O->getUnionField(); if (!UnionField || UnionField->getCanonicalDecl() != Field->getCanonicalDecl()) { - Info.Diag(E, diag::note_constexpr_read_inactive_union_member) - << Field << !UnionField << UnionField; - return false; + Info.Diag(E, diag::note_constexpr_access_inactive_union_member) + << handler.AccessKind << Field << !UnionField << UnionField; + return handler.failed(); } O = &O->getUnionValue(); } else O = &O->getStructField(Field->getFieldIndex()); + + bool WasConstQualified = ObjType.isConstQualified(); ObjType = Field->getType(); + if (WasConstQualified && !Field->isMutable()) + ObjType.addConst(); if (ObjType.isVolatileQualified()) { if (Info.getLangOpts().CPlusPlus) { // FIXME: Include a description of the path to the volatile subobject. - Info.Diag(E, diag::note_constexpr_ltor_volatile_obj, 1) - << 2 << Field; + Info.Diag(E, diag::note_constexpr_access_volatile_obj, 1) + << handler.AccessKind << 2 << Field; Info.Note(Field->getLocation(), diag::note_declared_at); } else { Info.Diag(E, diag::note_invalid_subexpr_in_const_expr); } - return false; + return handler.failed(); } } else { // Next subobject is a base class. const CXXRecordDecl *Derived = ObjType->getAsCXXRecordDecl(); const CXXRecordDecl *Base = getAsBaseClass(Sub.Entries[I]); O = &O->getStructBase(getBaseIndex(Derived, Base)); + + bool WasConstQualified = ObjType.isConstQualified(); ObjType = Info.Ctx.getRecordType(Base); + if (WasConstQualified) + ObjType.addConst(); } if (O->isUninit()) { if (!Info.CheckingPotentialConstantExpression) - Info.Diag(E, diag::note_constexpr_read_uninit); + Info.Diag(E, diag::note_constexpr_access_uninit) << handler.AccessKind; + return handler.failed(); + } + } + + return handler.found(*O, ObjType); +} + +namespace { +struct ExtractSubobjectHandler { + EvalInfo &Info; + APValue &Result; + + static const AccessKinds AccessKind = AK_Read; + + typedef bool result_type; + bool failed() { return false; } + bool found(APValue &Subobj, QualType SubobjType) { + Result = Subobj; + return true; + } + bool found(APSInt &Value, QualType SubobjType) { + Result = APValue(Value); + return true; + } + bool found(APFloat &Value, QualType SubobjType) { + Result = APValue(Value); + return true; + } + bool foundString(APValue &Subobj, QualType SubobjType, uint64_t Character) { + Result = APValue(extractStringLiteralCharacter( + Info, Subobj.getLValueBase().get(), Character)); + return true; + } +}; +} // end anonymous namespace + +const AccessKinds ExtractSubobjectHandler::AccessKind; + +/// Extract the designated sub-object of an rvalue. +static bool extractSubobject(EvalInfo &Info, const Expr *E, + const CompleteObject &Obj, + const SubobjectDesignator &Sub, + APValue &Result) { + ExtractSubobjectHandler Handler = { Info, Result }; + return findSubobject(Info, E, Obj, Sub, Handler); +} + +namespace { +struct ModifySubobjectHandler { + EvalInfo &Info; + APValue &NewVal; + const Expr *E; + + typedef bool result_type; + static const AccessKinds AccessKind = AK_Assign; + + bool checkConst(QualType QT) { + // Assigning to a const object has undefined behavior. + if (QT.isConstQualified()) { + Info.Diag(E, diag::note_constexpr_modify_const_type) << QT; return false; } + return true; } - // This may look super-stupid, but it serves an important purpose: if we just - // swapped Obj and *O, we'd create an object which had itself as a subobject. - // To avoid the leak, we ensure that Tmp ends up owning the original complete - // object, which is destroyed by Tmp's destructor. - APValue Tmp; - O->swap(Tmp); - Obj.swap(Tmp); - return true; + bool failed() { return false; } + bool found(APValue &Subobj, QualType SubobjType) { + if (!checkConst(SubobjType)) + return false; + // We've been given ownership of NewVal, so just swap it in. + Subobj.swap(NewVal); + return true; + } + bool found(APSInt &Value, QualType SubobjType) { + if (!checkConst(SubobjType)) + return false; + if (!NewVal.isInt()) { + // Maybe trying to write a cast pointer value into a complex? + Info.Diag(E); + return false; + } + Value = NewVal.getInt(); + return true; + } + bool found(APFloat &Value, QualType SubobjType) { + if (!checkConst(SubobjType)) + return false; + Value = NewVal.getFloat(); + return true; + } + bool foundString(APValue &Subobj, QualType SubobjType, uint64_t Character) { + llvm_unreachable("shouldn't encounter string elements with ExpandArrays"); + } +}; +} // end anonymous namespace + +const AccessKinds ModifySubobjectHandler::AccessKind; + +/// Update the designated sub-object of an rvalue to the given value. +static bool modifySubobject(EvalInfo &Info, const Expr *E, + const CompleteObject &Obj, + const SubobjectDesignator &Sub, + APValue &NewVal) { + ModifySubobjectHandler Handler = { Info, NewVal, E }; + return findSubobject(Info, E, Obj, Sub, Handler); } /// Find the position where two subobject designators diverge, or equivalently @@ -1710,59 +1946,52 @@ static bool AreElementsOfSameArray(QualType ObjType, return CommonLength >= A.Entries.size() - IsArray; } -/// HandleLValueToRValueConversion - Perform an lvalue-to-rvalue conversion on -/// the given lvalue. This can also be used for 'lvalue-to-lvalue' conversions -/// for looking up the glvalue referred to by an entity of reference type. -/// -/// \param Info - Information about the ongoing evaluation. -/// \param Conv - The expression for which we are performing the conversion. -/// Used for diagnostics. -/// \param Type - The type we expect this conversion to produce, before -/// stripping cv-qualifiers in the case of a non-clas type. -/// \param LVal - The glvalue on which we are attempting to perform this action. -/// \param RVal - The produced value will be placed here. -static bool HandleLValueToRValueConversion(EvalInfo &Info, const Expr *Conv, - QualType Type, - const LValue &LVal, APValue &RVal) { - if (LVal.Designator.Invalid) - // A diagnostic will have already been produced. - return false; - - const Expr *Base = LVal.Base.dyn_cast(); - +/// Find the complete object to which an LValue refers. +CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E, AccessKinds AK, + const LValue &LVal, QualType LValType) { if (!LVal.Base) { - // FIXME: Indirection through a null pointer deserves a specific diagnostic. - Info.Diag(Conv, diag::note_invalid_subexpr_in_const_expr); - return false; + Info.Diag(E, diag::note_constexpr_access_null) << AK; + return CompleteObject(); } CallStackFrame *Frame = 0; if (LVal.CallIndex) { Frame = Info.getCallFrame(LVal.CallIndex); if (!Frame) { - Info.Diag(Conv, diag::note_constexpr_lifetime_ended, 1) << !Base; + Info.Diag(E, diag::note_constexpr_lifetime_ended, 1) + << AK << LVal.Base.is(); NoteLValueLocation(Info, LVal.Base); - return false; + return CompleteObject(); } + } else if (AK != AK_Read) { + Info.Diag(E, diag::note_constexpr_modify_global); + return CompleteObject(); } // C++11 DR1311: An lvalue-to-rvalue conversion on a volatile-qualified type // is not a constant expression (even if the object is non-volatile). We also // apply this rule to C++98, in order to conform to the expected 'volatile' // semantics. - if (Type.isVolatileQualified()) { + if (LValType.isVolatileQualified()) { if (Info.getLangOpts().CPlusPlus) - Info.Diag(Conv, diag::note_constexpr_ltor_volatile_type) << Type; + Info.Diag(E, diag::note_constexpr_access_volatile_type) + << AK << LValType; else - Info.Diag(Conv); - return false; + Info.Diag(E); + return CompleteObject(); } + // Compute value storage location and type of base object. + APValue *BaseVal = 0; + QualType BaseType; + if (const ValueDecl *D = LVal.Base.dyn_cast()) { // In C++98, const, non-volatile integers initialized with ICEs are ICEs. // In C++11, constexpr, non-volatile variables initialized with constant // expressions are constant expressions too. Inside constexpr functions, // parameters are constant expressions even if they're non-const. + // In C++1y, objects local to a constant expression (those with a Frame) are + // both readable and writable inside constant expressions. // In C, such things can also be folded, although they are not ICEs. const VarDecl *VD = dyn_cast(D); if (VD) { @@ -1770,120 +1999,312 @@ static bool HandleLValueToRValueConversion(EvalInfo &Info, const Expr *Conv, VD = VDef; } if (!VD || VD->isInvalidDecl()) { - Info.Diag(Conv); - return false; + Info.Diag(E); + return CompleteObject(); } - // DR1313: If the object is volatile-qualified but the glvalue was not, - // behavior is undefined so the result is not a constant expression. - QualType VT = VD->getType(); - if (VT.isVolatileQualified()) { + // Accesses of volatile-qualified objects are not allowed. + BaseType = VD->getType(); + if (BaseType.isVolatileQualified()) { if (Info.getLangOpts().CPlusPlus) { - Info.Diag(Conv, diag::note_constexpr_ltor_volatile_obj, 1) << 1 << VD; + Info.Diag(E, diag::note_constexpr_access_volatile_obj, 1) + << AK << 1 << VD; Info.Note(VD->getLocation(), diag::note_declared_at); } else { - Info.Diag(Conv); + Info.Diag(E); } - return false; + return CompleteObject(); } - if (!isa(VD)) { + // Unless we're looking at a local variable or argument in a constexpr call, + // the variable we're reading must be const. + if (!Frame) { + assert(AK == AK_Read && "can't modify non-local"); if (VD->isConstexpr()) { // OK, we can read this variable. - } else if (VT->isIntegralOrEnumerationType()) { - if (!VT.isConstQualified()) { + } else if (BaseType->isIntegralOrEnumerationType()) { + if (!BaseType.isConstQualified()) { if (Info.getLangOpts().CPlusPlus) { - Info.Diag(Conv, diag::note_constexpr_ltor_non_const_int, 1) << VD; + Info.Diag(E, diag::note_constexpr_ltor_non_const_int, 1) << VD; Info.Note(VD->getLocation(), diag::note_declared_at); } else { - Info.Diag(Conv); + Info.Diag(E); } - return false; + return CompleteObject(); } - } else if (VT->isFloatingType() && VT.isConstQualified()) { + } else if (BaseType->isFloatingType() && BaseType.isConstQualified()) { // We support folding of const floating-point types, in order to make // static const data members of such types (supported as an extension) // more useful. if (Info.getLangOpts().CPlusPlus11) { - Info.CCEDiag(Conv, diag::note_constexpr_ltor_non_constexpr, 1) << VD; + Info.CCEDiag(E, diag::note_constexpr_ltor_non_constexpr, 1) << VD; Info.Note(VD->getLocation(), diag::note_declared_at); } else { - Info.CCEDiag(Conv); + Info.CCEDiag(E); } } else { // FIXME: Allow folding of values of any literal type in all languages. if (Info.getLangOpts().CPlusPlus11) { - Info.Diag(Conv, diag::note_constexpr_ltor_non_constexpr, 1) << VD; + Info.Diag(E, diag::note_constexpr_ltor_non_constexpr, 1) << VD; Info.Note(VD->getLocation(), diag::note_declared_at); } else { - Info.Diag(Conv); + Info.Diag(E); } - return false; + return CompleteObject(); } } - if (!EvaluateVarDeclInit(Info, Conv, VD, Frame, RVal)) - return false; + if (!evaluateVarDeclInit(Info, E, VD, Frame, BaseVal)) + return CompleteObject(); + } else { + const Expr *Base = LVal.Base.dyn_cast(); + + if (!Frame) { + Info.Diag(E); + return CompleteObject(); + } - if (isa(VD) || !VD->getAnyInitializer()->isLValue()) - return ExtractSubobject(Info, Conv, RVal, VT, LVal.Designator, Type); - - // The declaration was initialized by an lvalue, with no lvalue-to-rvalue - // conversion. This happens when the declaration and the lvalue should be - // considered synonymous, for instance when initializing an array of char - // from a string literal. Continue as if the initializer lvalue was the - // value we were originally given. - assert(RVal.getLValueOffset().isZero() && - "offset for lvalue init of non-reference"); - Base = RVal.getLValueBase().get(); - - if (unsigned CallIndex = RVal.getLValueCallIndex()) { - Frame = Info.getCallFrame(CallIndex); - if (!Frame) { - Info.Diag(Conv, diag::note_constexpr_lifetime_ended, 1) << !Base; - NoteLValueLocation(Info, RVal.getLValueBase()); + BaseType = Base->getType(); + BaseVal = &Frame->Temporaries[Base]; + + // Volatile temporary objects cannot be accessed in constant expressions. + if (BaseType.isVolatileQualified()) { + if (Info.getLangOpts().CPlusPlus) { + Info.Diag(E, diag::note_constexpr_access_volatile_obj, 1) + << AK << 0; + Info.Note(Base->getExprLoc(), diag::note_constexpr_temporary_here); + } else { + Info.Diag(E); + } + return CompleteObject(); + } + } + + // In C++1y, we can't safely access any mutable state when checking a + // potential constant expression. + if (Frame && Info.getLangOpts().CPlusPlus1y && + Info.CheckingPotentialConstantExpression) + return CompleteObject(); + + return CompleteObject(BaseVal, BaseType); +} + +/// \brief Perform an lvalue-to-rvalue conversion on the given glvalue. This +/// can also be used for 'lvalue-to-lvalue' conversions for looking up the +/// glvalue referred to by an entity of reference type. +/// +/// \param Info - Information about the ongoing evaluation. +/// \param Conv - The expression for which we are performing the conversion. +/// Used for diagnostics. +/// \param Type - The type of the glvalue (before stripping cv-qualifiers in the +/// case of a non-class type). +/// \param LVal - The glvalue on which we are attempting to perform this action. +/// \param RVal - The produced value will be placed here. +static bool handleLValueToRValueConversion(EvalInfo &Info, const Expr *Conv, + QualType Type, + const LValue &LVal, APValue &RVal) { + if (LVal.Designator.Invalid) + return false; + + // Check for special cases where there is no existing APValue to look at. + const Expr *Base = LVal.Base.dyn_cast(); + if (!LVal.Designator.Invalid && Base && !LVal.CallIndex && + !Type.isVolatileQualified()) { + if (const CompoundLiteralExpr *CLE = dyn_cast(Base)) { + // In C99, a CompoundLiteralExpr is an lvalue, and we defer evaluating the + // initializer until now for such expressions. Such an expression can't be + // an ICE in C, so this only matters for fold. + assert(!Info.getLangOpts().CPlusPlus && "lvalue compound literal in c++?"); + if (Type.isVolatileQualified()) { + Info.Diag(Conv); return false; } - } else { - Frame = 0; + APValue Lit; + if (!Evaluate(Lit, Info, CLE->getInitializer())) + return false; + CompleteObject LitObj(&Lit, Base->getType()); + return extractSubobject(Info, Conv, LitObj, LVal.Designator, RVal); + } else if (isa(Base)) { + // We represent a string literal array as an lvalue pointing at the + // corresponding expression, rather than building an array of chars. + // FIXME: Support PredefinedExpr, ObjCEncodeExpr, MakeStringConstant + APValue Str(Base, CharUnits::Zero(), APValue::NoLValuePath(), 0); + CompleteObject StrObj(&Str, Base->getType()); + return extractSubobject(Info, Conv, StrObj, LVal.Designator, RVal); } } - // Volatile temporary objects cannot be read in constant expressions. - if (Base->getType().isVolatileQualified()) { - if (Info.getLangOpts().CPlusPlus) { - Info.Diag(Conv, diag::note_constexpr_ltor_volatile_obj, 1) << 0; - Info.Note(Base->getExprLoc(), diag::note_constexpr_temporary_here); + CompleteObject Obj = findCompleteObject(Info, Conv, AK_Read, LVal, Type); + return Obj && extractSubobject(Info, Conv, Obj, LVal.Designator, RVal); +} + +/// Perform an assignment of Val to LVal. Takes ownership of Val. +static bool handleAssignment(EvalInfo &Info, const Expr *E, const LValue &LVal, + QualType LValType, APValue &Val) { + if (LVal.Designator.Invalid) + return false; + + if (!Info.getLangOpts().CPlusPlus1y) { + Info.Diag(E); + return false; + } + + CompleteObject Obj = findCompleteObject(Info, E, AK_Assign, LVal, LValType); + return Obj && modifySubobject(Info, E, Obj, LVal.Designator, Val); +} + +static bool isOverflowingIntegerType(ASTContext &Ctx, QualType T) { + return T->isSignedIntegerType() && + Ctx.getIntWidth(T) >= Ctx.getIntWidth(Ctx.IntTy); +} + +namespace { +struct IncDecSubobjectHandler { + EvalInfo &Info; + const Expr *E; + AccessKinds AccessKind; + APValue *Old; + + typedef bool result_type; + + bool checkConst(QualType QT) { + // Assigning to a const object has undefined behavior. + if (QT.isConstQualified()) { + Info.Diag(E, diag::note_constexpr_modify_const_type) << QT; + return false; + } + return true; + } + + bool failed() { return false; } + bool found(APValue &Subobj, QualType SubobjType) { + // Stash the old value. Also clear Old, so we don't clobber it later + // if we're post-incrementing a complex. + if (Old) { + *Old = Subobj; + Old = 0; + } + + switch (Subobj.getKind()) { + case APValue::Int: + return found(Subobj.getInt(), SubobjType); + case APValue::Float: + return found(Subobj.getFloat(), SubobjType); + case APValue::ComplexInt: + return found(Subobj.getComplexIntReal(), + SubobjType->castAs()->getElementType() + .withCVRQualifiers(SubobjType.getCVRQualifiers())); + case APValue::ComplexFloat: + return found(Subobj.getComplexFloatReal(), + SubobjType->castAs()->getElementType() + .withCVRQualifiers(SubobjType.getCVRQualifiers())); + case APValue::LValue: + return foundPointer(Subobj, SubobjType); + default: + // FIXME: can this happen? + Info.Diag(E); + return false; + } + } + bool found(APSInt &Value, QualType SubobjType) { + if (!checkConst(SubobjType)) + return false; + + if (!SubobjType->isIntegerType()) { + // We don't support increment / decrement on integer-cast-to-pointer + // values. + Info.Diag(E); + return false; + } + + if (Old) *Old = APValue(Value); + + // bool arithmetic promotes to int, and the conversion back to bool + // doesn't reduce mod 2^n, so special-case it. + if (SubobjType->isBooleanType()) { + if (AccessKind == AK_Increment) + Value = 1; + else + Value = !Value; + return true; + } + + bool WasNegative = Value.isNegative(); + if (AccessKind == AK_Increment) { + ++Value; + + if (!WasNegative && Value.isNegative() && + isOverflowingIntegerType(Info.Ctx, SubobjType)) { + APSInt ActualValue(Value, /*IsUnsigned*/true); + HandleOverflow(Info, E, ActualValue, SubobjType); + } } else { - Info.Diag(Conv); + --Value; + + if (WasNegative && !Value.isNegative() && + isOverflowingIntegerType(Info.Ctx, SubobjType)) { + unsigned BitWidth = Value.getBitWidth(); + APSInt ActualValue(Value.sext(BitWidth + 1), /*IsUnsigned*/false); + ActualValue.setBit(BitWidth); + HandleOverflow(Info, E, ActualValue, SubobjType); + } } - return false; + return true; } + bool found(APFloat &Value, QualType SubobjType) { + if (!checkConst(SubobjType)) + return false; - if (Frame) { - // If this is a temporary expression with a nontrivial initializer, grab the - // value from the relevant stack frame. - RVal = Frame->Temporaries[Base]; - } else if (const CompoundLiteralExpr *CLE - = dyn_cast(Base)) { - // In C99, a CompoundLiteralExpr is an lvalue, and we defer evaluating the - // initializer until now for such expressions. Such an expression can't be - // an ICE in C, so this only matters for fold. - assert(!Info.getLangOpts().CPlusPlus && "lvalue compound literal in c++?"); - if (!Evaluate(RVal, Info, CLE->getInitializer())) + if (Old) *Old = APValue(Value); + + APFloat One(Value.getSemantics(), 1); + if (AccessKind == AK_Increment) + Value.add(One, APFloat::rmNearestTiesToEven); + else + Value.subtract(One, APFloat::rmNearestTiesToEven); + return true; + } + bool foundPointer(APValue &Subobj, QualType SubobjType) { + if (!checkConst(SubobjType)) return false; - } else if (isa(Base)) { - // We represent a string literal array as an lvalue pointing at the - // corresponding expression, rather than building an array of chars. - // FIXME: Support PredefinedExpr, ObjCEncodeExpr, MakeStringConstant - RVal = APValue(Base, CharUnits::Zero(), APValue::NoLValuePath(), 0); - } else { - Info.Diag(Conv, diag::note_invalid_subexpr_in_const_expr); + + QualType PointeeType; + if (const PointerType *PT = SubobjType->getAs()) + PointeeType = PT->getPointeeType(); + else { + Info.Diag(E); + return false; + } + + LValue LVal; + LVal.setFrom(Info.Ctx, Subobj); + if (!HandleLValueArrayAdjustment(Info, E, LVal, PointeeType, + AccessKind == AK_Increment ? 1 : -1)) + return false; + LVal.moveInto(Subobj); + return true; + } + bool foundString(APValue &Subobj, QualType SubobjType, uint64_t Character) { + llvm_unreachable("shouldn't encounter string elements here"); + } +}; +} // end anonymous namespace + +/// Perform an increment or decrement on LVal. +static bool handleIncDec(EvalInfo &Info, const Expr *E, const LValue &LVal, + QualType LValType, bool IsIncrement, APValue *Old) { + if (LVal.Designator.Invalid) + return false; + + if (!Info.getLangOpts().CPlusPlus1y) { + Info.Diag(E); return false; } - return ExtractSubobject(Info, Conv, RVal, Base->getType(), LVal.Designator, - Type); + AccessKinds AK = IsIncrement ? AK_Increment : AK_Decrement; + CompleteObject Obj = findCompleteObject(Info, E, AK, LVal, LValType); + IncDecSubobjectHandler Handler = { Info, E, AK, Old }; + return Obj && findSubobject(Info, E, Obj, LVal.Designator, Handler); } /// Build an lvalue for the object argument of a member function call. @@ -1895,7 +2316,7 @@ static bool EvaluateObjectArgument(EvalInfo &Info, const Expr *Object, if (Object->isGLValue()) return EvaluateLValue(Object, This, Info); - if (Object->getType()->isLiteralType()) + if (Object->getType()->isLiteralType(Info.Ctx)) return EvaluateTemporary(Object, This, Info); return false; @@ -2040,24 +2461,95 @@ enum EvalStmtResult { /// Hit a 'return' statement. ESR_Returned, /// Evaluation succeeded. - ESR_Succeeded + ESR_Succeeded, + /// Hit a 'continue' statement. + ESR_Continue, + /// Hit a 'break' statement. + ESR_Break }; } +static bool EvaluateDecl(EvalInfo &Info, const Decl *D) { + if (const VarDecl *VD = dyn_cast(D)) { + // We don't need to evaluate the initializer for a static local. + if (!VD->hasLocalStorage()) + return true; + + LValue Result; + Result.set(VD, Info.CurrentCall->Index); + APValue &Val = Info.CurrentCall->Temporaries[VD]; + + if (!EvaluateInPlace(Val, Info, Result, VD->getInit())) { + // Wipe out any partially-computed value, to allow tracking that this + // evaluation failed. + Val = APValue(); + return false; + } + } + + return true; +} + +/// Evaluate a condition (either a variable declaration or an expression). +static bool EvaluateCond(EvalInfo &Info, const VarDecl *CondDecl, + const Expr *Cond, bool &Result) { + if (CondDecl && !EvaluateDecl(Info, CondDecl)) + return false; + return EvaluateAsBooleanCondition(Cond, Result, Info); +} + +static EvalStmtResult EvaluateStmt(APValue &Result, EvalInfo &Info, + const Stmt *S); + +/// Evaluate the body of a loop, and translate the result as appropriate. +static EvalStmtResult EvaluateLoopBody(APValue &Result, EvalInfo &Info, + const Stmt *Body) { + switch (EvalStmtResult ESR = EvaluateStmt(Result, Info, Body)) { + case ESR_Break: + return ESR_Succeeded; + case ESR_Succeeded: + case ESR_Continue: + return ESR_Continue; + case ESR_Failed: + case ESR_Returned: + return ESR; + } + llvm_unreachable("Invalid EvalStmtResult!"); +} + // Evaluate a statement. static EvalStmtResult EvaluateStmt(APValue &Result, EvalInfo &Info, const Stmt *S) { + // FIXME: Mark all temporaries in the current frame as destroyed at + // the end of each full-expression. switch (S->getStmtClass()) { default: + if (const Expr *E = dyn_cast(S)) { + // Don't bother evaluating beyond an expression-statement which couldn't + // be evaluated. + if (!EvaluateIgnoredValue(Info, E)) + return ESR_Failed; + return ESR_Succeeded; + } + + Info.Diag(S->getLocStart()); return ESR_Failed; case Stmt::NullStmtClass: - case Stmt::DeclStmtClass: return ESR_Succeeded; + case Stmt::DeclStmtClass: { + const DeclStmt *DS = cast(S); + for (DeclStmt::const_decl_iterator DclIt = DS->decl_begin(), + DclEnd = DS->decl_end(); DclIt != DclEnd; ++DclIt) + if (!EvaluateDecl(Info, *DclIt) && !Info.keepEvaluatingAfterFailure()) + return ESR_Failed; + return ESR_Succeeded; + } + case Stmt::ReturnStmtClass: { const Expr *RetExpr = cast(S)->getRetValue(); - if (!Evaluate(Result, Info, RetExpr)) + if (RetExpr && !Evaluate(Result, Info, RetExpr)) return ESR_Failed; return ESR_Returned; } @@ -2072,6 +2564,123 @@ static EvalStmtResult EvaluateStmt(APValue &Result, EvalInfo &Info, } return ESR_Succeeded; } + + case Stmt::IfStmtClass: { + const IfStmt *IS = cast(S); + + // Evaluate the condition, as either a var decl or as an expression. + bool Cond; + if (!EvaluateCond(Info, IS->getConditionVariable(), IS->getCond(), Cond)) + return ESR_Failed; + + if (const Stmt *SubStmt = Cond ? IS->getThen() : IS->getElse()) { + EvalStmtResult ESR = EvaluateStmt(Result, Info, SubStmt); + if (ESR != ESR_Succeeded) + return ESR; + } + return ESR_Succeeded; + } + + case Stmt::WhileStmtClass: { + const WhileStmt *WS = cast(S); + while (true) { + bool Continue; + if (!EvaluateCond(Info, WS->getConditionVariable(), WS->getCond(), + Continue)) + return ESR_Failed; + if (!Continue) + break; + + EvalStmtResult ESR = EvaluateLoopBody(Result, Info, WS->getBody()); + if (ESR != ESR_Continue) + return ESR; + } + return ESR_Succeeded; + } + + case Stmt::DoStmtClass: { + const DoStmt *DS = cast(S); + bool Continue; + do { + EvalStmtResult ESR = EvaluateLoopBody(Result, Info, DS->getBody()); + if (ESR != ESR_Continue) + return ESR; + + if (!EvaluateAsBooleanCondition(DS->getCond(), Continue, Info)) + return ESR_Failed; + } while (Continue); + return ESR_Succeeded; + } + + case Stmt::ForStmtClass: { + const ForStmt *FS = cast(S); + if (FS->getInit()) { + EvalStmtResult ESR = EvaluateStmt(Result, Info, FS->getInit()); + if (ESR != ESR_Succeeded) + return ESR; + } + while (true) { + bool Continue = true; + if (FS->getCond() && !EvaluateCond(Info, FS->getConditionVariable(), + FS->getCond(), Continue)) + return ESR_Failed; + if (!Continue) + break; + + EvalStmtResult ESR = EvaluateLoopBody(Result, Info, FS->getBody()); + if (ESR != ESR_Continue) + return ESR; + + if (FS->getInc() && !EvaluateIgnoredValue(Info, FS->getInc())) + return ESR_Failed; + } + return ESR_Succeeded; + } + + case Stmt::CXXForRangeStmtClass: { + const CXXForRangeStmt *FS = cast(S); + + // Initialize the __range variable. + EvalStmtResult ESR = EvaluateStmt(Result, Info, FS->getRangeStmt()); + if (ESR != ESR_Succeeded) + return ESR; + + // Create the __begin and __end iterators. + ESR = EvaluateStmt(Result, Info, FS->getBeginEndStmt()); + if (ESR != ESR_Succeeded) + return ESR; + + while (true) { + // Condition: __begin != __end. + bool Continue = true; + if (!EvaluateAsBooleanCondition(FS->getCond(), Continue, Info)) + return ESR_Failed; + if (!Continue) + break; + + // User's variable declaration, initialized by *__begin. + ESR = EvaluateStmt(Result, Info, FS->getLoopVarStmt()); + if (ESR != ESR_Succeeded) + return ESR; + + // Loop body. + ESR = EvaluateLoopBody(Result, Info, FS->getBody()); + if (ESR != ESR_Continue) + return ESR; + + // Increment: ++__begin + if (!EvaluateIgnoredValue(Info, FS->getInc())) + return ESR_Failed; + } + + return ESR_Succeeded; + } + + case Stmt::ContinueStmtClass: + return ESR_Continue; + + case Stmt::BreakStmtClass: + return ESR_Break; } } @@ -2165,7 +2774,13 @@ static bool HandleFunctionCall(SourceLocation CallLoc, return false; CallStackFrame Frame(Info, CallLoc, Callee, This, ArgValues.data()); - return EvaluateStmt(Result, Info, Body) == ESR_Returned; + EvalStmtResult ESR = EvaluateStmt(Result, Info, Body); + if (ESR == ESR_Succeeded) { + if (Callee->getResultType()->isVoidType()) + return true; + Info.Diag(Callee->getLocEnd(), diag::note_constexpr_no_return); + } + return ESR == ESR_Returned; } /// Evaluate a constructor call. @@ -2191,7 +2806,9 @@ static bool HandleConstructorCall(SourceLocation CallLoc, const LValue &This, // If it's a delegating constructor, just delegate. if (Definition->isDelegatingConstructor()) { CXXConstructorDecl::init_const_iterator I = Definition->init_begin(); - return EvaluateInPlace(Result, Info, This, (*I)->getInit()); + if (!EvaluateInPlace(Result, Info, This, (*I)->getInit())) + return false; + return EvaluateStmt(Result, Info, Definition->getBody()) != ESR_Failed; } // For a trivial copy or move constructor, perform an APValue copy. This is @@ -2202,7 +2819,7 @@ static bool HandleConstructorCall(SourceLocation CallLoc, const LValue &This, (Definition->isMoveConstructor() && Definition->isTrivial()))) { LValue RHS; RHS.setFrom(Info.Ctx, ArgValues[0]); - return HandleLValueToRValueConversion(Info, Args[0], Args[0]->getType(), + return handleLValueToRValueConversion(Info, Args[0], Args[0]->getType(), RHS, Result); } @@ -2291,7 +2908,8 @@ static bool HandleConstructorCall(SourceLocation CallLoc, const LValue &This, } } - return Success; + return Success && + EvaluateStmt(Result, Info, Definition->getBody()) != ESR_Failed; } //===----------------------------------------------------------------------===// @@ -2397,6 +3015,8 @@ class ExprEvaluatorBase { return StmtVisitorTy::Visit(E->getReplacement()); } RetTy VisitCXXDefaultArgExpr(const CXXDefaultArgExpr *E) { return StmtVisitorTy::Visit(E->getExpr()); } + RetTy VisitCXXDefaultInitExpr(const CXXDefaultInitExpr *E) + { return StmtVisitorTy::Visit(E->getExpr()); } // We cannot create any objects for which cleanups are required, so there is // nothing to do here; all cleanups must come from unevaluated subexpressions. RetTy VisitExprWithCleanups(const ExprWithCleanups *E) @@ -2426,7 +3046,7 @@ class ExprEvaluatorBase if (!HandleMemberPointerAccess(Info, E, Obj)) return false; APValue Result; - if (!HandleLValueToRValueConversion(Info, E, E->getType(), Obj, Result)) + if (!handleLValueToRValueConversion(Info, E, E->getType(), Obj, Result)) return false; return DerivedSuccess(Result, E); } @@ -2606,11 +3226,13 @@ class ExprEvaluatorBase assert(BaseTy->castAs()->getDecl()->getCanonicalDecl() == FD->getParent()->getCanonicalDecl() && "record / field mismatch"); + CompleteObject Obj(&Val, BaseTy); SubobjectDesignator Designator(BaseTy); Designator.addDeclUnchecked(FD); - return ExtractSubobject(Info, E, Val, BaseTy, Designator, E->getType()) && - DerivedSuccess(Val, E); + APValue Result; + return extractSubobject(Info, E, Obj, Designator, Result) && + DerivedSuccess(Result, E); } RetTy VisitCastExpr(const CastExpr *E) { @@ -2630,7 +3252,7 @@ class ExprEvaluatorBase return false; APValue RVal; // Note, we use the subexpression's type in order to retain cv-qualifiers. - if (!HandleLValueToRValueConversion(Info, E, E->getSubExpr()->getType(), + if (!handleLValueToRValueConversion(Info, E, E->getSubExpr()->getType(), LVal, RVal)) return false; return DerivedSuccess(RVal, E); @@ -2640,11 +3262,29 @@ class ExprEvaluatorBase return Error(E); } + RetTy VisitUnaryPostInc(const UnaryOperator *UO) { + return VisitUnaryPostIncDec(UO); + } + RetTy VisitUnaryPostDec(const UnaryOperator *UO) { + return VisitUnaryPostIncDec(UO); + } + RetTy VisitUnaryPostIncDec(const UnaryOperator *UO) { + if (!Info.getLangOpts().CPlusPlus1y && !Info.keepEvaluatingAfterFailure()) + return Error(UO); + + LValue LVal; + if (!EvaluateLValue(UO->getSubExpr(), LVal, Info)) + return false; + APValue RVal; + if (!handleIncDec(this->Info, UO, LVal, UO->getSubExpr()->getType(), + UO->isIncrementOp(), &RVal)) + return false; + return DerivedSuccess(RVal, UO); + } + /// Visit a value which is evaluated, but whose value is ignored. void VisitIgnoredValue(const Expr *E) { - APValue Scratch; - if (!Evaluate(Scratch, Info, E)) - Info.EvalStatus.HasSideEffects = true; + EvaluateIgnoredValue(Info, E); } }; @@ -2709,7 +3349,7 @@ class LValueExprEvaluatorBase if (MD->getType()->isReferenceType()) { APValue RefValue; - if (!HandleLValueToRValueConversion(this->Info, E, MD->getType(), Result, + if (!handleLValueToRValueConversion(this->Info, E, MD->getType(), Result, RefValue)) return false; return Success(RefValue, E); @@ -2792,6 +3432,7 @@ class LValueExprEvaluator LValueExprEvaluatorBaseTy(Info, Result) {} bool VisitVarDecl(const Expr *E, const VarDecl *VD); + bool VisitUnaryPreIncDec(const UnaryOperator *UO); bool VisitDeclRefExpr(const DeclRefExpr *E); bool VisitPredefinedExpr(const PredefinedExpr *E) { return Success(E); } @@ -2806,6 +3447,14 @@ class LValueExprEvaluator bool VisitUnaryDeref(const UnaryOperator *E); bool VisitUnaryReal(const UnaryOperator *E); bool VisitUnaryImag(const UnaryOperator *E); + bool VisitUnaryPreInc(const UnaryOperator *UO) { + return VisitUnaryPreIncDec(UO); + } + bool VisitUnaryPreDec(const UnaryOperator *UO) { + return VisitUnaryPreIncDec(UO); + } + bool VisitBinAssign(const BinaryOperator *BO); + bool VisitCompoundAssignOperator(const CompoundAssignOperator *CAO); bool VisitCastExpr(const CastExpr *E) { switch (E->getCastKind()) { @@ -2829,14 +3478,12 @@ class LValueExprEvaluator } // end anonymous namespace /// Evaluate an expression as an lvalue. This can be legitimately called on -/// expressions which are not glvalues, in a few cases: -/// * function designators in C, -/// * "extern void" objects, -/// * temporaries, if building with -Wno-address-of-temporary. -static bool EvaluateLValue(const Expr* E, LValue& Result, EvalInfo &Info) { - assert((E->isGLValue() || E->getType()->isFunctionType() || - E->getType()->isVoidType() || isa(E)) && - "can't evaluate expression as an lvalue"); +/// expressions which are not glvalues, in two cases: +/// * function designators in C, and +/// * "extern void" objects +static bool EvaluateLValue(const Expr *E, LValue &Result, EvalInfo &Info) { + assert(E->isGLValue() || E->getType()->isFunctionType() || + E->getType()->isVoidType()); return LValueExprEvaluator(Info, Result).Visit(E); } @@ -2849,41 +3496,32 @@ bool LValueExprEvaluator::VisitDeclRefExpr(const DeclRefExpr *E) { } bool LValueExprEvaluator::VisitVarDecl(const Expr *E, const VarDecl *VD) { + CallStackFrame *Frame = 0; + if (VD->hasLocalStorage() && Info.CurrentCall->Index > 1) + Frame = Info.CurrentCall; + if (!VD->getType()->isReferenceType()) { - if (isa(VD)) { - Result.set(VD, Info.CurrentCall->Index); + if (Frame) { + Result.set(VD, Frame->Index); return true; } return Success(VD); } - APValue V; - if (!EvaluateVarDeclInit(Info, E, VD, Info.CurrentCall, V)) + APValue *V; + if (!evaluateVarDeclInit(Info, E, VD, Frame, V)) return false; - return Success(V, E); + return Success(*V, E); } bool LValueExprEvaluator::VisitMaterializeTemporaryExpr( const MaterializeTemporaryExpr *E) { - if (E->GetTemporaryExpr()->isRValue()) { - if (E->getType()->isRecordType()) - return EvaluateTemporary(E->GetTemporaryExpr(), Result, Info); + if (E->getType()->isRecordType()) + return EvaluateTemporary(E->GetTemporaryExpr(), Result, Info); - Result.set(E, Info.CurrentCall->Index); - return EvaluateInPlace(Info.CurrentCall->Temporaries[E], Info, - Result, E->GetTemporaryExpr()); - } - - // Materialization of an lvalue temporary occurs when we need to force a copy - // (for instance, if it's a bitfield). - // FIXME: The AST should contain an lvalue-to-rvalue node for such cases. - if (!Visit(E->GetTemporaryExpr())) - return false; - if (!HandleLValueToRValueConversion(Info, E, E->getType(), Result, - Info.CurrentCall->Temporaries[E])) - return false; Result.set(E, Info.CurrentCall->Index); - return true; + return EvaluateInPlace(Info.CurrentCall->Temporaries[E], Info, + Result, E->GetTemporaryExpr()); } bool @@ -2906,7 +3544,7 @@ bool LValueExprEvaluator::VisitCXXTypeidExpr(const CXXTypeidExpr *E) { bool LValueExprEvaluator::VisitCXXUuidofExpr(const CXXUuidofExpr *E) { return Success(E); -} +} bool LValueExprEvaluator::VisitMemberExpr(const MemberExpr *E) { // Handle static data members. @@ -2967,6 +3605,64 @@ bool LValueExprEvaluator::VisitUnaryImag(const UnaryOperator *E) { return true; } +bool LValueExprEvaluator::VisitUnaryPreIncDec(const UnaryOperator *UO) { + if (!Info.getLangOpts().CPlusPlus1y && !Info.keepEvaluatingAfterFailure()) + return Error(UO); + + if (!this->Visit(UO->getSubExpr())) + return false; + + return handleIncDec( + this->Info, UO, Result, UO->getSubExpr()->getType(), + UO->isIncrementOp(), 0); +} + +bool LValueExprEvaluator::VisitCompoundAssignOperator( + const CompoundAssignOperator *CAO) { + if (!Info.getLangOpts().CPlusPlus1y && !Info.keepEvaluatingAfterFailure()) + return Error(CAO); + + APValue RHS; + + // The overall lvalue result is the result of evaluating the LHS. + if (!this->Visit(CAO->getLHS())) { + if (Info.keepEvaluatingAfterFailure()) + Evaluate(RHS, this->Info, CAO->getRHS()); + return false; + } + + if (!Evaluate(RHS, this->Info, CAO->getRHS())) + return false; + + // FIXME: + //return handleCompoundAssignment( + // this->Info, CAO, + // Result, CAO->getLHS()->getType(), CAO->getComputationLHSType(), + // RHS, CAO->getRHS()->getType(), + // CAO->getOpForCompoundAssignment(CAO->getOpcode()), + // CAO->getComputationResultType()); + return Error(CAO); +} + +bool LValueExprEvaluator::VisitBinAssign(const BinaryOperator *E) { + if (!Info.getLangOpts().CPlusPlus1y && !Info.keepEvaluatingAfterFailure()) + return Error(E); + + APValue NewVal; + + if (!this->Visit(E->getLHS())) { + if (Info.keepEvaluatingAfterFailure()) + Evaluate(NewVal, this->Info, E->getRHS()); + return false; + } + + if (!Evaluate(NewVal, this->Info, E->getRHS())) + return false; + + return handleAssignment(this->Info, E, Result, E->getLHS()->getType(), + NewVal); +} + //===----------------------------------------------------------------------===// // Pointer Evaluation //===----------------------------------------------------------------------===// @@ -3411,12 +4107,20 @@ bool RecordExprEvaluator::VisitInitListExpr(const InitListExpr *E) { // If the initializer list for a union does not contain any elements, the // first element of the union is value-initialized. + // FIXME: The element should be initialized from an initializer list. + // Is this difference ever observable for initializer lists which + // we don't build? ImplicitValueInitExpr VIE(Field->getType()); const Expr *InitExpr = E->getNumInits() ? E->getInit(0) : &VIE; LValue Subobject = This; if (!HandleLValueMember(Info, InitExpr, Subobject, Field, &Layout)) return false; + + // Temporarily override This, in case there's a CXXDefaultInitExpr in here. + ThisOverrideRAII ThisOverride(*Info.CurrentCall, &This, + isa(InitExpr)); + return EvaluateInPlace(Result.getUnionValue(), Info, Subobject, InitExpr); } @@ -3446,10 +4150,14 @@ bool RecordExprEvaluator::VisitInitListExpr(const InitListExpr *E) { // Perform an implicit value-initialization for members beyond the end of // the initializer list. ImplicitValueInitExpr VIE(HaveInit ? Info.Ctx.IntTy : Field->getType()); + const Expr *Init = HaveInit ? E->getInit(ElementNo++) : &VIE; - if (!EvaluateInPlace( - Result.getStructField(Field->getFieldIndex()), - Info, Subobject, HaveInit ? E->getInit(ElementNo++) : &VIE)) { + // Temporarily override This, in case there's a CXXDefaultInitExpr in here. + ThisOverrideRAII ThisOverride(*Info.CurrentCall, &This, + isa(Init)); + + if (!EvaluateInPlace(Result.getStructField(Field->getFieldIndex()), Info, + Subobject, Init)) { if (!Info.keepEvaluatingAfterFailure()) return false; Success = false; @@ -3777,6 +4485,9 @@ namespace { bool VisitInitListExpr(const InitListExpr *E); bool VisitCXXConstructExpr(const CXXConstructExpr *E); + bool VisitCXXConstructExpr(const CXXConstructExpr *E, + const LValue &Subobject, + APValue *Value, QualType Type); }; } // end anonymous namespace @@ -3810,8 +4521,16 @@ bool ArrayExprEvaluator::VisitInitListExpr(const InitListExpr *E) { if (Result.isArray() && Result.hasArrayFiller()) Filler = Result.getArrayFiller(); - Result = APValue(APValue::UninitArray(), E->getNumInits(), - CAT->getSize().getZExtValue()); + unsigned NumEltsToInit = E->getNumInits(); + unsigned NumElts = CAT->getSize().getZExtValue(); + const Expr *FillerExpr = E->hasArrayFiller() ? E->getArrayFiller() : 0; + + // If the initializer might depend on the array index, run it for each + // array element. For now, just whitelist non-class value-initialization. + if (NumEltsToInit != NumElts && !isa(FillerExpr)) + NumEltsToInit = NumElts; + + Result = APValue(APValue::UninitArray(), NumEltsToInit, NumElts); // If the array was previously zero-initialized, preserve the // zero-initialized values. @@ -3824,12 +4543,12 @@ bool ArrayExprEvaluator::VisitInitListExpr(const InitListExpr *E) { LValue Subobject = This; Subobject.addArray(Info, E, CAT); - unsigned Index = 0; - for (InitListExpr::const_iterator I = E->begin(), End = E->end(); - I != End; ++I, ++Index) { + for (unsigned Index = 0; Index != NumEltsToInit; ++Index) { + const Expr *Init = + Index < E->getNumInits() ? E->getInit(Index) : FillerExpr; if (!EvaluateInPlace(Result.getArrayInitializedElt(Index), - Info, Subobject, cast(*I)) || - !HandleLValueArrayAdjustment(Info, cast(*I), Subobject, + Info, Subobject, Init) || + !HandleLValueArrayAdjustment(Info, Init, Subobject, CAT->getElementType(), 1)) { if (!Info.keepEvaluatingAfterFailure()) return false; @@ -3837,39 +4556,54 @@ bool ArrayExprEvaluator::VisitInitListExpr(const InitListExpr *E) { } } - if (!Result.hasArrayFiller()) return Success; - assert(E->hasArrayFiller() && "no array filler for incomplete init list"); - // 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] = {}; - return EvaluateInPlace(Result.getArrayFiller(), Info, - Subobject, E->getArrayFiller()) && Success; + if (!Result.hasArrayFiller()) + return Success; + + // If we get here, we have a trivial filler, which we can just evaluate + // once and splat over the rest of the array elements. + assert(FillerExpr && "no array filler for incomplete init list"); + return EvaluateInPlace(Result.getArrayFiller(), Info, Subobject, + FillerExpr) && Success; } bool ArrayExprEvaluator::VisitCXXConstructExpr(const CXXConstructExpr *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; + return VisitCXXConstructExpr(E, This, &Result, E->getType()); +} - 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(); +bool ArrayExprEvaluator::VisitCXXConstructExpr(const CXXConstructExpr *E, + const LValue &Subobject, + APValue *Value, + QualType Type) { + bool HadZeroInit = !Value->isUninit(); + + if (const ConstantArrayType *CAT = Info.Ctx.getAsConstantArrayType(Type)) { + unsigned N = CAT->getSize().getZExtValue(); + + // Preserve the array filler if we had prior zero-initialization. + APValue Filler = + HadZeroInit && Value->hasArrayFiller() ? Value->getArrayFiller() + : APValue(); + + *Value = APValue(APValue::UninitArray(), N, N); + + if (HadZeroInit) + for (unsigned I = 0; I != N; ++I) + Value->getArrayInitializedElt(I) = Filler; + + // Initialize the elements. + LValue ArrayElt = Subobject; + ArrayElt.addArray(Info, E, CAT); + for (unsigned I = 0; I != N; ++I) + if (!VisitCXXConstructExpr(E, ArrayElt, &Value->getArrayInitializedElt(I), + CAT->getElementType()) || + !HandleLValueArrayAdjustment(Info, E, ArrayElt, + CAT->getElementType(), 1)) + return false; + + return true; } - if (!ElemTy->isRecordType()) + if (!Type->isRecordType()) return Error(E); const CXXConstructorDecl *FD = E->getConstructor(); @@ -3880,7 +4614,7 @@ bool ArrayExprEvaluator::VisitCXXConstructExpr(const CXXConstructExpr *E) { return true; if (ZeroInit) { - ImplicitValueInitExpr VIE(ElemTy); + ImplicitValueInitExpr VIE(Type); return EvaluateInPlace(*Value, Info, Subobject, &VIE); } @@ -3901,7 +4635,7 @@ bool ArrayExprEvaluator::VisitCXXConstructExpr(const CXXConstructExpr *E) { return false; if (ZeroInit && !HadZeroInit) { - ImplicitValueInitExpr VIE(ElemTy); + ImplicitValueInitExpr VIE(Type); if (!EvaluateInPlace(*Value, Info, Subobject, &VIE)) return false; } @@ -5182,6 +5916,10 @@ CharUnits IntExprEvaluator::GetAlignOfType(QualType T) { CharUnits IntExprEvaluator::GetAlignOfExpr(const Expr *E) { E = E->IgnoreParens(); + // The kinds of expressions that we have special-case logic here for + // should be kept up to date with the special checks for those + // expressions in Sema. + // alignof decl is always accepted, even if it doesn't make sense: we default // to 1 in those cases. if (const DeclRefExpr *DRE = dyn_cast(E)) @@ -6267,7 +7005,7 @@ static bool EvaluateAsRValue(EvalInfo &Info, const Expr *E, APValue &Result) { if (E->isGLValue()) { LValue LV; LV.setFrom(Info.Ctx, Result); - if (!HandleLValueToRValueConversion(Info, E, E->getType(), LV, Result)) + if (!handleLValueToRValueConversion(Info, E, E->getType(), LV, Result)) return false; } @@ -6501,6 +7239,7 @@ static ICEDiag CheckICE(const Expr* E, ASTContext &Ctx) { case Expr::CXXDynamicCastExprClass: case Expr::CXXTypeidExprClass: case Expr::CXXUuidofExprClass: + case Expr::MSPropertyRefExprClass: case Expr::CXXNullPtrLiteralExprClass: case Expr::UserDefinedLiteralClass: case Expr::CXXThisExprClass: @@ -6819,6 +7558,8 @@ static ICEDiag CheckICE(const Expr* E, ASTContext &Ctx) { } case Expr::CXXDefaultArgExprClass: return CheckICE(cast(E)->getExpr(), Ctx); + case Expr::CXXDefaultInitExprClass: + return CheckICE(cast(E)->getExpr(), Ctx); case Expr::ChooseExprClass: { return CheckICE(cast(E)->getChosenSubExpr(Ctx), Ctx); } diff --git a/lib/AST/ItaniumMangle.cpp b/lib/AST/ItaniumMangle.cpp index 21c499317f5..5ad8021fac9 100644 --- a/lib/AST/ItaniumMangle.cpp +++ b/lib/AST/ItaniumMangle.cpp @@ -141,6 +141,8 @@ class ItaniumMangleContext : public MangleContext { raw_ostream &); void mangleItaniumGuardVariable(const VarDecl *D, raw_ostream &); + void mangleItaniumThreadLocalInit(const VarDecl *D, raw_ostream &); + void mangleItaniumThreadLocalWrapper(const VarDecl *D, raw_ostream &); void mangleInitDiscriminator() { Discriminator = 0; @@ -1095,6 +1097,15 @@ void CXXNameMangler::mangleUnqualifiedName(const NamedDecl *ND, mangleSourceName(FD->getIdentifier()); break; } + + // Class extensions have no name as a category, and it's possible + // for them to be the semantic parent of certain declarations + // (primarily, tag decls defined within declarations). Such + // declarations will always have internal linkage, so the name + // doesn't really matter, but we shouldn't crash on them. For + // safety, just handle all ObjC containers here. + if (isa(ND)) + break; // We must have an anonymous struct. const TagDecl *TD = cast(ND); @@ -2264,7 +2275,7 @@ void CXXNameMangler::mangleType(const AutoType *T) { QualType D = T->getDeducedType(); // ::= Da # dependent auto if (D.isNull()) - Out << "Da"; + Out << (T->isDecltypeAuto() ? "Dc" : "Da"); else mangleType(D); } @@ -2385,6 +2396,7 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity) { case Expr::ImplicitValueInitExprClass: case Expr::ParenListExprClass: case Expr::LambdaExprClass: + case Expr::MSPropertyRefExprClass: llvm_unreachable("unexpected statement kind"); // FIXME: invent manglings for all these. @@ -2461,6 +2473,10 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity) { mangleExpression(cast(E)->getExpr(), Arity); break; + case Expr::CXXDefaultInitExprClass: + mangleExpression(cast(E)->getExpr(), Arity); + break; + case Expr::SubstNonTypeTemplateParmExprClass: mangleExpression(cast(E)->getReplacement(), Arity); @@ -3521,6 +3537,22 @@ void ItaniumMangleContext::mangleItaniumGuardVariable(const VarDecl *D, Mangler.mangleName(D); } +void ItaniumMangleContext::mangleItaniumThreadLocalInit(const VarDecl *D, + raw_ostream &Out) { + // ::= TH + CXXNameMangler Mangler(*this, Out); + Mangler.getStream() << "_ZTH"; + Mangler.mangleName(D); +} + +void ItaniumMangleContext::mangleItaniumThreadLocalWrapper(const VarDecl *D, + raw_ostream &Out) { + // ::= TW + CXXNameMangler Mangler(*this, Out); + Mangler.getStream() << "_ZTW"; + Mangler.mangleName(D); +} + void ItaniumMangleContext::mangleReferenceTemporary(const VarDecl *D, raw_ostream &Out) { // We match the GCC mangling here. diff --git a/lib/AST/MicrosoftCXXABI.cpp b/lib/AST/MicrosoftCXXABI.cpp index 6553e9d7494..fd932f7330a 100644 --- a/lib/AST/MicrosoftCXXABI.cpp +++ b/lib/AST/MicrosoftCXXABI.cpp @@ -89,8 +89,8 @@ MSInheritanceModel CXXRecordDecl::getMSInheritanceModel() const { if (this->getNumVBases() > 0) return MSIM_Virtual; if (usesMultipleInheritanceModel(this)) - return MSIM_Multiple; - return MSIM_Single; + return this->isPolymorphic() ? MSIM_MultiplePolymorphic : MSIM_Multiple; + return this->isPolymorphic() ? MSIM_SinglePolymorphic : MSIM_Single; } // Returns the number of pointer and integer slots used to represent a member @@ -119,15 +119,15 @@ MSInheritanceModel CXXRecordDecl::getMSInheritanceModel() const { // // // The offset of the vb-table pointer within the object. Only needed for // // incomplete types. -// int VBTableOffset; +// int VBPtrOffset; // }; -std::pair -MemberPointerType::getMSMemberPointerSlots() const { - const CXXRecordDecl *RD = this->getClass()->getAsCXXRecordDecl(); +static std::pair +getMSMemberPointerSlots(const MemberPointerType *MPT) { + const CXXRecordDecl *RD = MPT->getClass()->getAsCXXRecordDecl(); MSInheritanceModel Inheritance = RD->getMSInheritanceModel(); unsigned Ptrs; unsigned Ints = 0; - if (this->isMemberFunctionPointer()) { + if (MPT->isMemberFunctionPointer()) { // Member function pointers are a struct of a function pointer followed by a // variable number of ints depending on the inheritance model used. The // function pointer is a real function if it is non-virtual and a vftable @@ -137,7 +137,9 @@ MemberPointerType::getMSMemberPointerSlots() const { switch (Inheritance) { case MSIM_Unspecified: ++Ints; // VBTableOffset case MSIM_Virtual: ++Ints; // VirtualBaseAdjustmentOffset + case MSIM_MultiplePolymorphic: case MSIM_Multiple: ++Ints; // NonVirtualBaseAdjustment + case MSIM_SinglePolymorphic: case MSIM_Single: break; // Nothing } } else { @@ -147,7 +149,9 @@ MemberPointerType::getMSMemberPointerSlots() const { switch (Inheritance) { case MSIM_Unspecified: ++Ints; // VBTableOffset case MSIM_Virtual: ++Ints; // VirtualBaseAdjustmentOffset + case MSIM_MultiplePolymorphic: case MSIM_Multiple: // Nothing + case MSIM_SinglePolymorphic: case MSIM_Single: ++Ints; // Field offset } } @@ -160,7 +164,7 @@ std::pair MicrosoftCXXABI::getMemberPointerWidthAndAlign( assert(Target.getTriple().getArch() == llvm::Triple::x86 || Target.getTriple().getArch() == llvm::Triple::x86_64); unsigned Ptrs, Ints; - llvm::tie(Ptrs, Ints) = MPT->getMSMemberPointerSlots(); + llvm::tie(Ptrs, Ints) = getMSMemberPointerSlots(MPT); // The nominal struct is laid out with pointers followed by ints and aligned // to a pointer width if any are present and an int width otherwise. unsigned PtrSize = Target.getPointerWidth(0); diff --git a/lib/AST/MicrosoftMangle.cpp b/lib/AST/MicrosoftMangle.cpp index 40f8730e61a..1785063d7b1 100644 --- a/lib/AST/MicrosoftMangle.cpp +++ b/lib/AST/MicrosoftMangle.cpp @@ -59,6 +59,8 @@ class MicrosoftCXXNameMangler { ASTContext &getASTContext() const { return Context.getASTContext(); } public: + enum QualifierMangleMode { QMM_Drop, QMM_Mangle, QMM_Escape, QMM_Result }; + MicrosoftCXXNameMangler(MangleContext &C, raw_ostream &Out_) : Context(C), Out(Out_), Structor(0), StructorType(-1), @@ -78,7 +80,8 @@ class MicrosoftCXXNameMangler { void mangleVariableEncoding(const VarDecl *VD); void mangleNumber(int64_t Number); void mangleNumber(const llvm::APSInt &Value); - void mangleType(QualType T, SourceRange Range, bool MangleQualifiers = true); + void mangleType(QualType T, SourceRange Range, + QualifierMangleMode QMM = QMM_Mangle); private: void disableBackReferences() { UseNameBackReferences = false; } @@ -112,10 +115,10 @@ class MicrosoftCXXNameMangler { #undef TYPE void mangleType(const TagType*); - void mangleType(const FunctionType *T, const FunctionDecl *D, - bool IsStructor, bool IsInstMethod); - void mangleType(const ArrayType *T, bool IsGlobal); - void mangleExtraDimensions(QualType T); + void mangleFunctionType(const FunctionType *T, const FunctionDecl *D, + bool IsStructor, bool IsInstMethod); + void mangleDecayedArrayType(const ArrayType *T, bool IsGlobal); + void mangleArrayType(const ArrayType *T, Qualifiers Quals); void mangleFunctionClass(const FunctionDecl *FD); void mangleCallingConvention(const FunctionType *T, bool IsInstMethod = false); void mangleIntegerLiteral(const llvm::APSInt &Number, bool IsBoolean); @@ -264,7 +267,7 @@ void MicrosoftCXXNameMangler::mangleFunctionEncoding(const FunctionDecl *FD) { // First, the function class. mangleFunctionClass(FD); - mangleType(FT, FD, InStructor, InInstMethod); + mangleFunctionType(FT, FD, InStructor, InInstMethod); } void MicrosoftCXXNameMangler::mangleVariableEncoding(const VarDecl *VD) { @@ -297,14 +300,17 @@ void MicrosoftCXXNameMangler::mangleVariableEncoding(const VarDecl *VD) { TypeLoc TL = VD->getTypeSourceInfo()->getTypeLoc(); QualType Ty = TL.getType(); if (Ty->isPointerType() || Ty->isReferenceType()) { - mangleType(Ty, TL.getSourceRange()); + mangleType(Ty, TL.getSourceRange(), QMM_Drop); mangleQualifiers(Ty->getPointeeType().getQualifiers(), false); } else if (const ArrayType *AT = getASTContext().getAsArrayType(Ty)) { // Global arrays are funny, too. - mangleType(AT, true); - mangleQualifiers(Ty.getQualifiers(), false); + mangleDecayedArrayType(AT, true); + if (AT->getElementType()->isArrayType()) + Out << 'A'; + else + mangleQualifiers(Ty.getQualifiers(), false); } else { - mangleType(Ty.getLocalUnqualifiedType(), TL.getSourceRange()); + mangleType(Ty, TL.getSourceRange(), QMM_Drop); mangleQualifiers(Ty.getLocalQualifiers(), false); } } @@ -824,9 +830,11 @@ MicrosoftCXXNameMangler::mangleTemplateArgs(const TemplateDecl *TD, switch (TA.getKind()) { case TemplateArgument::Null: llvm_unreachable("Can't mangle null template arguments!"); - case TemplateArgument::Type: - mangleType(TA.getAsType(), SourceRange()); + case TemplateArgument::Type: { + QualType T = TA.getAsType(); + mangleType(T, SourceRange(), QMM_Escape); break; + } case TemplateArgument::Declaration: mangle(cast(TA.getAsDecl()), "$1?"); break; @@ -964,7 +972,14 @@ void MicrosoftCXXNameMangler::mangleArgumentType(QualType T, if (Found == TypeBackReferences.end()) { size_t OutSizeBefore = Out.GetNumBytesInBuffer(); - mangleType(T, Range, false); + if (const ArrayType *AT = getASTContext().getAsArrayType(T)) { + mangleDecayedArrayType(AT, false); + } else if (const FunctionType *FT = T->getAs()) { + Out << "P6"; + mangleFunctionType(FT, 0, false, false); + } else { + mangleType(T, Range, QMM_Drop); + } // See if it's worth creating a back reference. // Only types longer than 1 character are considered @@ -980,28 +995,53 @@ void MicrosoftCXXNameMangler::mangleArgumentType(QualType T, } void MicrosoftCXXNameMangler::mangleType(QualType T, SourceRange Range, - bool MangleQualifiers) { + QualifierMangleMode QMM) { // Only operate on the canonical type! T = getASTContext().getCanonicalType(T); - Qualifiers Quals = T.getLocalQualifiers(); - // We have to mangle these now, while we still have enough information. - if (T->isAnyPointerType() || T->isMemberPointerType() || - T->isBlockPointerType()) { - manglePointerQualifiers(Quals); - } else if (Quals && MangleQualifiers) { - mangleQualifiers(Quals, false); + + if (const ArrayType *AT = dyn_cast(T)) { + if (QMM == QMM_Mangle) + Out << 'A'; + else if (QMM == QMM_Escape || QMM == QMM_Result) + Out << "$$B"; + mangleArrayType(AT, Quals); + return; } - SplitQualType split = T.split(); - const Type *ty = split.Ty; + bool IsPointer = T->isAnyPointerType() || T->isMemberPointerType() || + T->isBlockPointerType(); - // If we're mangling a qualified array type, push the qualifiers to - // the element type. - if (split.Quals && isa(T)) { - ty = Context.getASTContext().getAsArrayType(T); + switch (QMM) { + case QMM_Drop: + break; + case QMM_Mangle: + if (const FunctionType *FT = dyn_cast(T)) { + Out << '6'; + mangleFunctionType(FT, 0, false, false); + return; + } + mangleQualifiers(Quals, false); + break; + case QMM_Escape: + if (!IsPointer && Quals) { + Out << "$$C"; + mangleQualifiers(Quals, false); + } + break; + case QMM_Result: + if ((!IsPointer && Quals) || isa(T)) { + Out << '?'; + mangleQualifiers(Quals, false); + } + break; } + // We have to mangle these now, while we still have enough information. + if (IsPointer) + manglePointerQualifiers(Quals); + const Type *ty = T.getTypePtr(); + switch (ty->getTypeClass()) { #define ABSTRACT_TYPE(CLASS, PARENT) #define NON_CANONICAL_TYPE(CLASS, PARENT) \ @@ -1111,17 +1151,17 @@ void MicrosoftCXXNameMangler::mangleType(const FunctionProtoType *T, // structor type. // FIXME: This may not be lambda-friendly. Out << "$$A6"; - mangleType(T, NULL, false, false); + mangleFunctionType(T, NULL, false, false); } void MicrosoftCXXNameMangler::mangleType(const FunctionNoProtoType *T, SourceRange) { llvm_unreachable("Can't mangle K&R function prototypes"); } -void MicrosoftCXXNameMangler::mangleType(const FunctionType *T, - const FunctionDecl *D, - bool IsStructor, - bool IsInstMethod) { +void MicrosoftCXXNameMangler::mangleFunctionType(const FunctionType *T, + const FunctionDecl *D, + bool IsStructor, + bool IsInstMethod) { // ::= // const FunctionProtoType *Proto = cast(T); @@ -1147,21 +1187,7 @@ void MicrosoftCXXNameMangler::mangleType(const FunctionType *T, } Out << '@'; } 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()); + mangleType(Proto->getResultType(), SourceRange(), QMM_Result); } // ::= X # void @@ -1356,7 +1382,8 @@ void MicrosoftCXXNameMangler::mangleType(const TagType *T) { // It's supposed to be the other way around, but for some strange reason, it // isn't. Today this behavior is retained for the sole purpose of backwards // compatibility. -void MicrosoftCXXNameMangler::mangleType(const ArrayType *T, bool IsGlobal) { +void MicrosoftCXXNameMangler::mangleDecayedArrayType(const ArrayType *T, + bool IsGlobal) { // This isn't a recursive mangling, so now we have to do it all in this // one call. if (IsGlobal) { @@ -1364,25 +1391,27 @@ void MicrosoftCXXNameMangler::mangleType(const ArrayType *T, bool IsGlobal) { } else { Out << 'Q'; } - mangleExtraDimensions(T->getElementType()); + mangleType(T->getElementType(), SourceRange()); } void MicrosoftCXXNameMangler::mangleType(const ConstantArrayType *T, SourceRange) { - mangleType(cast(T), false); + llvm_unreachable("Should have been special cased"); } void MicrosoftCXXNameMangler::mangleType(const VariableArrayType *T, SourceRange) { - mangleType(cast(T), false); + llvm_unreachable("Should have been special cased"); } void MicrosoftCXXNameMangler::mangleType(const DependentSizedArrayType *T, SourceRange) { - mangleType(cast(T), false); + llvm_unreachable("Should have been special cased"); } void MicrosoftCXXNameMangler::mangleType(const IncompleteArrayType *T, SourceRange) { - mangleType(cast(T), false); + llvm_unreachable("Should have been special cased"); } -void MicrosoftCXXNameMangler::mangleExtraDimensions(QualType ElementTy) { +void MicrosoftCXXNameMangler::mangleArrayType(const ArrayType *T, + Qualifiers Quals) { + QualType ElementTy(T, 0); SmallVector Dimensions; for (;;) { if (const ConstantArrayType *CAT = @@ -1408,20 +1437,20 @@ void MicrosoftCXXNameMangler::mangleExtraDimensions(QualType ElementTy) { Diags.Report(DSAT->getSizeExpr()->getExprLoc(), DiagID) << DSAT->getBracketsRange(); return; - } else if (ElementTy->isIncompleteArrayType()) continue; - else break; - } - mangleQualifiers(ElementTy.getQualifiers(), false); - // If there are any additional dimensions, mangle them now. - if (Dimensions.size() > 0) { - Out << 'Y'; - // ::= # number of extra dimensions - mangleNumber(Dimensions.size()); - for (unsigned Dim = 0; Dim < Dimensions.size(); ++Dim) { - mangleNumber(Dimensions[Dim].getLimitedValue()); + } else if (const IncompleteArrayType *IAT = + getASTContext().getAsIncompleteArrayType(ElementTy)) { + Dimensions.push_back(llvm::APInt(32, 0)); + ElementTy = IAT->getElementType(); } + else break; } - mangleType(ElementTy.getLocalUnqualifiedType(), SourceRange()); + Out << 'Y'; + // ::= # number of extra dimensions + mangleNumber(Dimensions.size()); + for (unsigned Dim = 0; Dim < Dimensions.size(); ++Dim) + mangleNumber(Dimensions[Dim].getLimitedValue()); + mangleType(getASTContext().getQualifiedType(ElementTy.getTypePtr(), Quals), + SourceRange(), QMM_Escape); } // ::= @@ -1433,11 +1462,11 @@ void MicrosoftCXXNameMangler::mangleType(const MemberPointerType *T, if (const FunctionProtoType *FPT = PointeeType->getAs()) { Out << '8'; mangleName(T->getClass()->castAs()->getDecl()); - mangleType(FPT, NULL, false, true); + mangleFunctionType(FPT, NULL, false, true); } else { mangleQualifiers(PointeeType.getQualifiers(), true); mangleName(T->getClass()->castAs()->getDecl()); - mangleType(PointeeType.getLocalUnqualifiedType(), Range); + mangleType(PointeeType, Range, QMM_Drop); } } @@ -1465,17 +1494,7 @@ void MicrosoftCXXNameMangler::mangleType( void MicrosoftCXXNameMangler::mangleType(const PointerType *T, SourceRange Range) { QualType PointeeTy = T->getPointeeType(); - if (PointeeTy->isArrayType()) { - // Pointers to arrays are mangled like arrays. - mangleExtraDimensions(PointeeTy); - } else if (const FunctionType *FT = PointeeTy->getAs()) { - // Function pointers are special. - Out << '6'; - mangleType(FT, NULL, false, false); - } else { - mangleQualifiers(PointeeTy.getQualifiers(), false); - mangleType(PointeeTy, Range, false); - } + mangleType(PointeeTy, Range); } void MicrosoftCXXNameMangler::mangleType(const ObjCObjectPointerType *T, SourceRange Range) { @@ -1489,11 +1508,7 @@ void MicrosoftCXXNameMangler::mangleType(const ObjCObjectPointerType *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, Range); + mangleType(T->getPointeeType(), Range); } // ::= @@ -1501,11 +1516,7 @@ void MicrosoftCXXNameMangler::mangleType(const LValueReferenceType *T, 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); + mangleType(T->getPointeeType(), Range); } void MicrosoftCXXNameMangler::mangleType(const ComplexType *T, @@ -1587,7 +1598,7 @@ void MicrosoftCXXNameMangler::mangleType(const BlockPointerType *T, Out << "_E"; QualType pointee = T->getPointeeType(); - mangleType(pointee->castAs(), NULL, false, false); + mangleFunctionType(pointee->castAs(), NULL, false, false); } void MicrosoftCXXNameMangler::mangleType(const InjectedClassNameType *T, diff --git a/lib/AST/RawCommentList.cpp b/lib/AST/RawCommentList.cpp index f2386a56fcc..92b96dc8e5a 100644 --- a/lib/AST/RawCommentList.cpp +++ b/lib/AST/RawCommentList.cpp @@ -21,8 +21,10 @@ using namespace clang; namespace { /// Get comment kind and bool describing if it is a trailing comment. -std::pair getCommentKind(StringRef Comment) { - if (Comment.size() < 3 || Comment[0] != '/') +std::pair getCommentKind(StringRef Comment, + bool ParseAllComments) { + const size_t MinCommentLength = ParseAllComments ? 2 : 3; + if ((Comment.size() < MinCommentLength) || Comment[0] != '/') return std::make_pair(RawComment::RCK_Invalid, false); RawComment::CommentKind K; @@ -63,9 +65,10 @@ bool mergedCommentIsTrailingComment(StringRef Comment) { } // unnamed namespace RawComment::RawComment(const SourceManager &SourceMgr, SourceRange SR, - bool Merged) : + bool Merged, bool ParseAllComments) : Range(SR), RawTextValid(false), BriefTextValid(false), IsAttached(false), IsAlmostTrailingComment(false), + ParseAllComments(ParseAllComments), BeginLineValid(false), EndLineValid(false) { // Extract raw comment text, if possible. if (SR.getBegin() == SR.getEnd() || getRawText(SourceMgr).empty()) { @@ -75,7 +78,7 @@ RawComment::RawComment(const SourceManager &SourceMgr, SourceRange SR, if (!Merged) { // Guess comment kind. - std::pair K = getCommentKind(RawText); + std::pair K = getCommentKind(RawText, ParseAllComments); Kind = K.first; IsTrailingComment = K.second; @@ -143,7 +146,8 @@ const char *RawComment::extractBriefText(const ASTContext &Context) const { // a separate allocator for all temporary stuff. llvm::BumpPtrAllocator Allocator; - comments::Lexer L(Allocator, Context.getCommentCommandTraits(), + comments::Lexer L(Allocator, Context.getDiagnostics(), + Context.getCommentCommandTraits(), Range.getBegin(), RawText.begin(), RawText.end()); comments::BriefParser P(L, Context.getCommentCommandTraits()); @@ -164,7 +168,8 @@ comments::FullComment *RawComment::parse(const ASTContext &Context, // Make sure that RawText is valid. getRawText(Context.getSourceManager()); - comments::Lexer L(Context.getAllocator(), Context.getCommentCommandTraits(), + comments::Lexer L(Context.getAllocator(), Context.getDiagnostics(), + Context.getCommentCommandTraits(), getSourceRange().getBegin(), RawText.begin(), RawText.end()); comments::Sema S(Context.getAllocator(), Context.getSourceManager(), @@ -253,7 +258,8 @@ void RawCommentList::addComment(const RawComment &RC, if (C1EndLine + 1 == C2BeginLine || C1EndLine == C2BeginLine) { SourceRange MergedRange(C1.getSourceRange().getBegin(), C2.getSourceRange().getEnd()); - *Comments.back() = RawComment(SourceMgr, MergedRange, true); + *Comments.back() = RawComment(SourceMgr, MergedRange, true, + RC.isParseAllComments()); Merged = true; } } @@ -262,4 +268,3 @@ void RawCommentList::addComment(const RawComment &RC, OnlyWhitespaceSeen = true; } - diff --git a/lib/AST/Stmt.cpp b/lib/AST/Stmt.cpp index 2ae5a1266c1..5b29c073f92 100644 --- a/lib/AST/Stmt.cpp +++ b/lib/AST/Stmt.cpp @@ -673,19 +673,38 @@ GCCAsmStmt::GCCAsmStmt(ASTContext &C, SourceLocation asmloc, bool issimple, MSAsmStmt::MSAsmStmt(ASTContext &C, SourceLocation asmloc, SourceLocation lbraceloc, bool issimple, bool isvolatile, ArrayRef asmtoks, unsigned numoutputs, - unsigned numinputs, ArrayRef names, + unsigned numinputs, ArrayRef constraints, ArrayRef exprs, StringRef asmstr, ArrayRef clobbers, SourceLocation endloc) : AsmStmt(MSAsmStmtClass, asmloc, issimple, isvolatile, numoutputs, numinputs, clobbers.size()), LBraceLoc(lbraceloc), - EndLoc(endloc), AsmStr(asmstr.str()), NumAsmToks(asmtoks.size()) { + EndLoc(endloc), NumAsmToks(asmtoks.size()) { - unsigned NumExprs = NumOutputs + NumInputs; + initialize(C, asmstr, asmtoks, constraints, exprs, clobbers); +} - Names = new (C) IdentifierInfo*[NumExprs]; - for (unsigned i = 0, e = NumExprs; i != e; ++i) - Names[i] = names[i]; +static StringRef copyIntoContext(ASTContext &C, StringRef str) { + size_t size = str.size(); + char *buffer = new (C) char[size]; + memcpy(buffer, str.data(), size); + return StringRef(buffer, size); +} + +void MSAsmStmt::initialize(ASTContext &C, + StringRef asmstr, + ArrayRef asmtoks, + ArrayRef constraints, + ArrayRef exprs, + ArrayRef clobbers) { + assert(NumAsmToks == asmtoks.size()); + assert(NumClobbers == clobbers.size()); + + unsigned NumExprs = exprs.size(); + assert(NumExprs == NumOutputs + NumInputs); + assert(NumExprs == constraints.size()); + + AsmStr = copyIntoContext(C, asmstr); Exprs = new (C) Stmt*[NumExprs]; for (unsigned i = 0, e = NumExprs; i != e; ++i) @@ -697,19 +716,13 @@ MSAsmStmt::MSAsmStmt(ASTContext &C, SourceLocation asmloc, Constraints = new (C) StringRef[NumExprs]; for (unsigned i = 0, e = NumExprs; i != e; ++i) { - size_t size = constraints[i].size(); - char *dest = new (C) char[size]; - std::strncpy(dest, constraints[i].data(), size); - Constraints[i] = StringRef(dest, size); + Constraints[i] = copyIntoContext(C, constraints[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); + Clobbers[i] = copyIntoContext(C, clobbers[i]); } } @@ -1023,3 +1036,107 @@ SEHFinallyStmt* SEHFinallyStmt::Create(ASTContext &C, Stmt *Block) { return new(C)SEHFinallyStmt(Loc,Block); } + +CapturedStmt::Capture *CapturedStmt::getStoredCaptures() const { + unsigned Size = sizeof(CapturedStmt) + sizeof(Stmt *) * (NumCaptures + 1); + + // Offset of the first Capture object. + unsigned FirstCaptureOffset = + llvm::RoundUpToAlignment(Size, llvm::alignOf()); + + return reinterpret_cast( + reinterpret_cast(const_cast(this)) + + FirstCaptureOffset); +} + +CapturedStmt::CapturedStmt(Stmt *S, CapturedRegionKind Kind, + ArrayRef Captures, + ArrayRef CaptureInits, + CapturedDecl *CD, + RecordDecl *RD) + : Stmt(CapturedStmtClass), NumCaptures(Captures.size()), + CapDeclAndKind(CD, Kind), TheRecordDecl(RD) { + assert( S && "null captured statement"); + assert(CD && "null captured declaration for captured statement"); + assert(RD && "null record declaration for captured statement"); + + // Copy initialization expressions. + Stmt **Stored = getStoredStmts(); + for (unsigned I = 0, N = NumCaptures; I != N; ++I) + *Stored++ = CaptureInits[I]; + + // Copy the statement being captured. + *Stored = S; + + // Copy all Capture objects. + Capture *Buffer = getStoredCaptures(); + std::copy(Captures.begin(), Captures.end(), Buffer); +} + +CapturedStmt::CapturedStmt(EmptyShell Empty, unsigned NumCaptures) + : Stmt(CapturedStmtClass, Empty), NumCaptures(NumCaptures), + CapDeclAndKind(0, CR_Default), TheRecordDecl(0) { + getStoredStmts()[NumCaptures] = 0; +} + +CapturedStmt *CapturedStmt::Create(ASTContext &Context, Stmt *S, + CapturedRegionKind Kind, + ArrayRef Captures, + ArrayRef CaptureInits, + CapturedDecl *CD, + RecordDecl *RD) { + // The layout is + // + // ----------------------------------------------------------- + // | CapturedStmt, Init, ..., Init, S, Capture, ..., Capture | + // ----------------^-------------------^---------------------- + // getStoredStmts() getStoredCaptures() + // + // where S is the statement being captured. + // + assert(CaptureInits.size() == Captures.size() && "wrong number of arguments"); + + unsigned Size = sizeof(CapturedStmt) + sizeof(Stmt *) * (Captures.size() + 1); + if (!Captures.empty()) { + // Realign for the following Capture array. + Size = llvm::RoundUpToAlignment(Size, llvm::alignOf()); + Size += sizeof(Capture) * Captures.size(); + } + + void *Mem = Context.Allocate(Size); + return new (Mem) CapturedStmt(S, Kind, Captures, CaptureInits, CD, RD); +} + +CapturedStmt *CapturedStmt::CreateDeserialized(ASTContext &Context, + unsigned NumCaptures) { + unsigned Size = sizeof(CapturedStmt) + sizeof(Stmt *) * (NumCaptures + 1); + if (NumCaptures > 0) { + // Realign for the following Capture array. + Size = llvm::RoundUpToAlignment(Size, llvm::alignOf()); + Size += sizeof(Capture) * NumCaptures; + } + + void *Mem = Context.Allocate(Size); + return new (Mem) CapturedStmt(EmptyShell(), NumCaptures); +} + +Stmt::child_range CapturedStmt::children() { + // Children are captured field initilizers. + return child_range(getStoredStmts(), getStoredStmts() + NumCaptures); +} + +bool CapturedStmt::capturesVariable(const VarDecl *Var) const { + for (const_capture_iterator I = capture_begin(), + E = capture_end(); I != E; ++I) { + if (I->capturesThis()) + continue; + + // This does not handle variable redeclarations. This should be + // extended to capture variables with redeclarations, for example + // a thread-private variable in OpenMP. + if (I->getCapturedVar() == Var) + return true; + } + + return false; +} diff --git a/lib/AST/StmtPrinter.cpp b/lib/AST/StmtPrinter.cpp index 7df7fdb92bf..9203dc1584b 100644 --- a/lib/AST/StmtPrinter.cpp +++ b/lib/AST/StmtPrinter.cpp @@ -445,11 +445,15 @@ void StmtPrinter::VisitMSAsmStmt(MSAsmStmt *Node) { Indent() << "__asm "; if (Node->hasBraces()) OS << "{\n"; - OS << *(Node->getAsmString()) << "\n"; + OS << Node->getAsmString() << "\n"; if (Node->hasBraces()) Indent() << "}\n"; } +void StmtPrinter::VisitCapturedStmt(CapturedStmt *Node) { + PrintStmt(Node->getCapturedDecl()->getBody()); +} + void StmtPrinter::VisitObjCAtTryStmt(ObjCAtTryStmt *Node) { Indent() << "@try"; if (CompoundStmt *TS = dyn_cast(Node->getTryBody())) { @@ -1109,24 +1113,25 @@ void StmtPrinter::VisitAtomicExpr(AtomicExpr *Node) { // AtomicExpr stores its subexpressions in a permuted order. PrintExpr(Node->getPtr()); - OS << ", "; if (Node->getOp() != AtomicExpr::AO__c11_atomic_load && Node->getOp() != AtomicExpr::AO__atomic_load_n) { - PrintExpr(Node->getVal1()); OS << ", "; + PrintExpr(Node->getVal1()); } if (Node->getOp() == AtomicExpr::AO__atomic_exchange || Node->isCmpXChg()) { - PrintExpr(Node->getVal2()); OS << ", "; + PrintExpr(Node->getVal2()); } if (Node->getOp() == AtomicExpr::AO__atomic_compare_exchange || Node->getOp() == AtomicExpr::AO__atomic_compare_exchange_n) { - PrintExpr(Node->getWeak()); OS << ", "; + PrintExpr(Node->getWeak()); } - if (Node->getOp() != AtomicExpr::AO__c11_atomic_init) + if (Node->getOp() != AtomicExpr::AO__c11_atomic_init) { + OS << ", "; PrintExpr(Node->getOrder()); + } if (Node->isCmpXChg()) { OS << ", "; PrintExpr(Node->getOrderFail()); @@ -1238,6 +1243,18 @@ void StmtPrinter::VisitCXXUuidofExpr(CXXUuidofExpr *Node) { OS << ")"; } +void StmtPrinter::VisitMSPropertyRefExpr(MSPropertyRefExpr *Node) { + PrintExpr(Node->getBaseExpr()); + if (Node->isArrow()) + OS << "->"; + else + OS << "."; + if (NestedNameSpecifier *Qualifier = + Node->getQualifierLoc().getNestedNameSpecifier()) + Qualifier->print(OS, Policy); + OS << Node->getPropertyDecl()->getDeclName(); +} + void StmtPrinter::VisitUserDefinedLiteral(UserDefinedLiteral *Node) { switch (Node->getLiteralOperatorKind()) { case UserDefinedLiteral::LOK_Raw: @@ -1298,7 +1315,11 @@ void StmtPrinter::VisitCXXThrowExpr(CXXThrowExpr *Node) { } void StmtPrinter::VisitCXXDefaultArgExpr(CXXDefaultArgExpr *Node) { - // Nothing to print: we picked up the default argument + // Nothing to print: we picked up the default argument. +} + +void StmtPrinter::VisitCXXDefaultInitExpr(CXXDefaultInitExpr *Node) { + // Nothing to print: we picked up the default initializer. } void StmtPrinter::VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr *Node) { diff --git a/lib/AST/StmtProfile.cpp b/lib/AST/StmtProfile.cpp index bfd3132506e..8ade242d56d 100644 --- a/lib/AST/StmtProfile.cpp +++ b/lib/AST/StmtProfile.cpp @@ -215,6 +215,10 @@ void StmtProfiler::VisitSEHExceptStmt(const SEHExceptStmt *S) { VisitStmt(S); } +void StmtProfiler::VisitCapturedStmt(const CapturedStmt *S) { + VisitStmt(S); +} + void StmtProfiler::VisitObjCForCollectionStmt(const ObjCForCollectionStmt *S) { VisitStmt(S); } @@ -766,6 +770,11 @@ void StmtProfiler::VisitCXXUuidofExpr(const CXXUuidofExpr *S) { VisitType(S->getTypeOperand()); } +void StmtProfiler::VisitMSPropertyRefExpr(const MSPropertyRefExpr *S) { + VisitExpr(S); + VisitDecl(S->getPropertyDecl()); +} + void StmtProfiler::VisitCXXThisExpr(const CXXThisExpr *S) { VisitExpr(S); ID.AddBoolean(S->isImplicit()); @@ -780,6 +789,11 @@ void StmtProfiler::VisitCXXDefaultArgExpr(const CXXDefaultArgExpr *S) { VisitDecl(S->getParam()); } +void StmtProfiler::VisitCXXDefaultInitExpr(const CXXDefaultInitExpr *S) { + VisitExpr(S); + VisitDecl(S->getField()); +} + void StmtProfiler::VisitCXXBindTemporaryExpr(const CXXBindTemporaryExpr *S) { VisitExpr(S); VisitDecl( diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index 0c5636d8406..fa16facb634 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -1142,16 +1142,20 @@ bool QualType::isTriviallyCopyableType(ASTContext &Context) const { -bool Type::isLiteralType() const { +bool Type::isLiteralType(ASTContext &Ctx) const { if (isDependentType()) return false; - // C++0x [basic.types]p10: + // C++1y [basic.types]p10: + // A type is a literal type if it is: + // -- cv void; or + if (Ctx.getLangOpts().CPlusPlus1y && isVoidType()) + return true; + + // C++11 [basic.types]p10: // A type is a literal type if it is: // [...] - // -- an array of literal type. - // Extension: variable arrays cannot be literal types, since they're - // runtime-sized. + // -- an array of literal type other than an array of runtime bound; or if (isVariableArrayType()) return false; const Type *BaseTy = getBaseElementTypeUnsafe(); @@ -1162,7 +1166,7 @@ bool Type::isLiteralType() const { if (BaseTy->isIncompleteType()) return false; - // C++0x [basic.types]p10: + // C++11 [basic.types]p10: // A type is a literal type if it is: // -- a scalar type; or // As an extension, Clang treats vector types and complex types as @@ -2101,6 +2105,11 @@ static CachedProperties computeCachedProperties(const Type *T) { assert(T->isInstantiationDependentType()); return CachedProperties(ExternalLinkage, false); + case Type::Auto: + // Give non-deduced 'auto' types external linkage. We should only see them + // here in error recovery. + return CachedProperties(ExternalLinkage, false); + case Type::Builtin: // C++ [basic.link]p8: // A type is said to have linkage if and only if: @@ -2202,6 +2211,9 @@ static LinkageInfo computeLinkageInfo(const Type *T) { case Type::Builtin: return LinkageInfo::external(); + case Type::Auto: + return LinkageInfo::external(); + case Type::Record: case Type::Enum: return cast(T)->getDecl()->getLinkageAndVisibility(); diff --git a/lib/AST/TypePrinter.cpp b/lib/AST/TypePrinter.cpp index 9d1717a220c..043707622bd 100644 --- a/lib/AST/TypePrinter.cpp +++ b/lib/AST/TypePrinter.cpp @@ -776,16 +776,16 @@ void TypePrinter::printUnaryTransformAfter(const UnaryTransformType *T, void TypePrinter::printAutoBefore(const AutoType *T, raw_ostream &OS) { // If the type has been deduced, do not print 'auto'. - if (T->isDeduced()) { + if (!T->getDeducedType().isNull()) { printBefore(T->getDeducedType(), OS); } else { - OS << "auto"; + OS << (T->isDecltypeAuto() ? "decltype(auto)" : "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()) + if (!T->getDeducedType().isNull()) printAfter(T->getDeducedType(), OS); } diff --git a/lib/Analysis/BodyFarm.cpp b/lib/Analysis/BodyFarm.cpp index dda26bfab89..4d5c2ee236f 100644 --- a/lib/Analysis/BodyFarm.cpp +++ b/lib/Analysis/BodyFarm.cpp @@ -194,8 +194,8 @@ static Stmt *create_dispatch_once(ASTContext &C, const FunctionDecl *D) { // (1) Create the call. DeclRefExpr *DR = M.makeDeclRefExpr(Block); ImplicitCastExpr *ICE = M.makeLvalueToRvalue(DR, Ty); - CallExpr *CE = new (C) CallExpr(C, ICE, ArrayRef(), C.VoidTy, - VK_RValue, SourceLocation()); + CallExpr *CE = new (C) CallExpr(C, ICE, None, C.VoidTy, VK_RValue, + SourceLocation()); // (2) Create the assignment to the predicate. IntegerLiteral *IL = @@ -257,8 +257,8 @@ static Stmt *create_dispatch_sync(ASTContext &C, const FunctionDecl *D) { ASTMaker M(C); DeclRefExpr *DR = M.makeDeclRefExpr(PV); ImplicitCastExpr *ICE = M.makeLvalueToRvalue(DR, Ty); - CallExpr *CE = new (C) CallExpr(C, ICE, ArrayRef(), C.VoidTy, - VK_RValue, SourceLocation()); + CallExpr *CE = new (C) CallExpr(C, ICE, None, C.VoidTy, VK_RValue, + SourceLocation()); return CE; } diff --git a/lib/Analysis/CFG.cpp b/lib/Analysis/CFG.cpp index 1adb8b84e46..096c7a080bf 100644 --- a/lib/Analysis/CFG.cpp +++ b/lib/Analysis/CFG.cpp @@ -1085,11 +1085,16 @@ CFGBlock *CFGBuilder::Visit(Stmt * S, AddStmtChoice asc) { return VisitExprWithCleanups(cast(S), asc); case Stmt::CXXDefaultArgExprClass: + case Stmt::CXXDefaultInitExprClass: // FIXME: The expression inside a CXXDefaultArgExpr is owned by the // called function's declaration, not by the caller. If we simply add // this expression to the CFG, we could end up with the same Expr // appearing multiple times. // PR13385 / + // + // It's likewise possible for multiple CXXDefaultInitExprs for the same + // expression to be used in the same function (through aggregate + // initialization). return VisitStmt(S, asc); case Stmt::CXXBindTemporaryExprClass: diff --git a/lib/Analysis/ThreadSafety.cpp b/lib/Analysis/ThreadSafety.cpp index 4fe342dcc8c..479d9a301f4 100644 --- a/lib/Analysis/ThreadSafety.cpp +++ b/lib/Analysis/ThreadSafety.cpp @@ -2279,6 +2279,10 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) { // Fill in source locations for all CFGBlocks. findBlockLocations(CFGraph, SortedGraph, BlockInfo); + MutexIDList ExclusiveLocksAcquired; + MutexIDList SharedLocksAcquired; + MutexIDList LocksReleased; + // Add locks from exclusive_locks_required and shared_locks_required // to initial lockset. Also turn off checking for lock and unlock functions. // FIXME: is there a more intelligent way to check lock/unlock functions? @@ -2300,15 +2304,30 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) { } else if (SharedLocksRequiredAttr *A = dyn_cast(Attr)) { getMutexIDs(SharedLocksToAdd, A, (Expr*) 0, D); - } else if (isa(Attr)) { - // Don't try to check unlock functions for now - return; - } else if (isa(Attr)) { - // Don't try to check lock functions for now - return; - } else if (isa(Attr)) { - // Don't try to check lock functions for now - return; + } else if (UnlockFunctionAttr *A = dyn_cast(Attr)) { + if (!Handler.issueBetaWarnings()) + return; + // UNLOCK_FUNCTION() is used to hide the underlying lock implementation. + // We must ignore such methods. + if (A->args_size() == 0) + return; + // FIXME -- deal with exclusive vs. shared unlock functions? + getMutexIDs(ExclusiveLocksToAdd, A, (Expr*) 0, D); + getMutexIDs(LocksReleased, A, (Expr*) 0, D); + } else if (ExclusiveLockFunctionAttr *A + = dyn_cast(Attr)) { + if (!Handler.issueBetaWarnings()) + return; + if (A->args_size() == 0) + return; + getMutexIDs(ExclusiveLocksAcquired, A, (Expr*) 0, D); + } else if (SharedLockFunctionAttr *A + = dyn_cast(Attr)) { + if (!Handler.issueBetaWarnings()) + return; + if (A->args_size() == 0) + return; + getMutexIDs(SharedLocksAcquired, A, (Expr*) 0, D); } else if (isa(Attr)) { // Don't try to check trylock functions for now return; @@ -2491,8 +2510,27 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) { if (!Final->Reachable) return; + // By default, we expect all locks held on entry to be held on exit. + FactSet ExpectedExitSet = Initial->EntrySet; + + // Adjust the expected exit set by adding or removing locks, as declared + // by *-LOCK_FUNCTION and UNLOCK_FUNCTION. The intersect below will then + // issue the appropriate warning. + // FIXME: the location here is not quite right. + for (unsigned i=0,n=ExclusiveLocksAcquired.size(); igetLocation(), LK_Exclusive)); + } + for (unsigned i=0,n=SharedLocksAcquired.size(); igetLocation(), LK_Shared)); + } + for (unsigned i=0,n=LocksReleased.size(); iEntrySet, Final->ExitSet, + intersectAndWarn(ExpectedExitSet, Final->ExitSet, Final->ExitLoc, LEK_LockedAtEndOfFunction, LEK_NotLockedAtEndOfFunction, diff --git a/lib/Basic/Diagnostic.cpp b/lib/Basic/Diagnostic.cpp index 842bacb9a5d..45d4b539e8c 100644 --- a/lib/Basic/Diagnostic.cpp +++ b/lib/Basic/Diagnostic.cpp @@ -971,6 +971,23 @@ bool DiagnosticConsumer::IncludeInDiagnosticCounts() const { return true; } void IgnoringDiagConsumer::anchor() { } +ForwardingDiagnosticConsumer::~ForwardingDiagnosticConsumer() {} + +void ForwardingDiagnosticConsumer::HandleDiagnostic( + DiagnosticsEngine::Level DiagLevel, + const Diagnostic &Info) { + Target.HandleDiagnostic(DiagLevel, Info); +} + +void ForwardingDiagnosticConsumer::clear() { + DiagnosticConsumer::clear(); + Target.clear(); +} + +bool ForwardingDiagnosticConsumer::IncludeInDiagnosticCounts() const { + return Target.IncludeInDiagnosticCounts(); +} + PartialDiagnostic::StorageAllocator::StorageAllocator() { for (unsigned I = 0; I != NumCached; ++I) FreeList[I] = Cached + I; diff --git a/lib/Basic/IdentifierTable.cpp b/lib/Basic/IdentifierTable.cpp index 429d9d8cb21..951c718d183 100644 --- a/lib/Basic/IdentifierTable.cpp +++ b/lib/Basic/IdentifierTable.cpp @@ -65,7 +65,7 @@ namespace { }; } -IdentifierIterator *IdentifierInfoLookup::getIdentifiers() const { +IdentifierIterator *IdentifierInfoLookup::getIdentifiers() { return new EmptyLookupIterator(); } diff --git a/lib/Basic/SourceManager.cpp b/lib/Basic/SourceManager.cpp index 1b8383bc426..d6dc6d6328a 100644 --- a/lib/Basic/SourceManager.cpp +++ b/lib/Basic/SourceManager.cpp @@ -1848,23 +1848,42 @@ SourceManager::getMacroArgExpandedLocation(SourceLocation Loc) const { return Loc; } +std::pair +SourceManager::getDecomposedIncludedLoc(FileID FID) const { + // Uses IncludedLocMap to retrieve/cache the decomposed loc. + + typedef std::pair DecompTy; + typedef llvm::DenseMap MapTy; + std::pair + InsertOp = IncludedLocMap.insert(std::make_pair(FID, DecompTy())); + DecompTy &DecompLoc = InsertOp.first->second; + if (!InsertOp.second) + return DecompLoc; // already in map. + + SourceLocation UpperLoc; + const SrcMgr::SLocEntry &Entry = getSLocEntry(FID); + if (Entry.isExpansion()) + UpperLoc = Entry.getExpansion().getExpansionLocStart(); + else + UpperLoc = Entry.getFile().getIncludeLoc(); + + if (UpperLoc.isValid()) + DecompLoc = getDecomposedLoc(UpperLoc); + + return DecompLoc; +} + /// Given a decomposed source location, move it up the include/expansion stack /// to the parent source location. If this is possible, return the decomposed /// version of the parent in Loc and return false. If Loc is the top-level /// entry, return true and don't modify it. static bool MoveUpIncludeHierarchy(std::pair &Loc, const SourceManager &SM) { - SourceLocation UpperLoc; - const SrcMgr::SLocEntry &Entry = SM.getSLocEntry(Loc.first); - if (Entry.isExpansion()) - UpperLoc = Entry.getExpansion().getExpansionLocStart(); - else - UpperLoc = Entry.getFile().getIncludeLoc(); - - if (UpperLoc.isInvalid()) + std::pair UpperLoc = SM.getDecomposedIncludedLoc(Loc.first); + if (UpperLoc.first.isInvalid()) return true; // We reached the top. - - Loc = SM.getDecomposedLoc(UpperLoc); + + Loc = UpperLoc; return false; } @@ -1929,7 +1948,7 @@ bool SourceManager::isBeforeInTranslationUnit(SourceLocation LHS, // of the other looking for a match. // We use a map from FileID to Offset to store the chain. Easier than writing // a custom set hash info that only depends on the first part of a pair. - typedef llvm::DenseMap LocSet; + typedef llvm::SmallDenseMap LocSet; LocSet LChain; do { LChain.insert(LOffs); diff --git a/lib/Basic/TargetInfo.cpp b/lib/Basic/TargetInfo.cpp index 70ea2351ec3..0d44dc010e1 100644 --- a/lib/Basic/TargetInfo.cpp +++ b/lib/Basic/TargetInfo.cpp @@ -37,6 +37,7 @@ TargetInfo::TargetInfo(const std::string &T) : TargetOpts(), Triple(T) LongWidth = LongAlign = 32; LongLongWidth = LongLongAlign = 64; SuitableAlign = 64; + MinGlobalAlign = 0; HalfWidth = 16; HalfAlign = 16; FloatWidth = 32; @@ -373,7 +374,9 @@ bool TargetInfo::validateOutputConstraint(ConstraintInfo &Info) const { Name++; } - return true; + // If a constraint allows neither memory nor register operands it contains + // only modifiers. Reject it. + return Info.allowsMemory() || Info.allowsRegister(); } bool TargetInfo::resolveSymbolicName(const char *&Name, diff --git a/lib/Basic/Targets.cpp b/lib/Basic/Targets.cpp index 3eda9d8c184..a622a11aa5a 100644 --- a/lib/Basic/Targets.cpp +++ b/lib/Basic/Targets.cpp @@ -621,7 +621,7 @@ class NaClTargetInfo : public OSTargetInfo { this->SizeType = TargetInfo::UnsignedInt; this->PtrDiffType = TargetInfo::SignedInt; this->IntPtrType = TargetInfo::SignedInt; - this->RegParmMax = 2; + // RegParmMax is inherited from the underlying architecture this->LongDoubleFormat = &llvm::APFloat::IEEEdouble; this->DescriptionString = "e-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-" "f32:32:32-f64:64:64-p:32:32:32-v128:32:32"; @@ -1835,6 +1835,7 @@ class X86TargetInfo : public TargetInfo { /// Bobcat architecture processors. //@{ CK_BTVER1, + CK_BTVER2, //@} /// \name Bulldozer @@ -1959,6 +1960,7 @@ class X86TargetInfo : public TargetInfo { .Case("opteron-sse3", CK_OpteronSSE3) .Case("amdfam10", CK_AMDFAM10) .Case("btver1", CK_BTVER1) + .Case("btver2", CK_BTVER2) .Case("bdver1", CK_BDVER1) .Case("bdver2", CK_BDVER2) .Case("x86-64", CK_x86_64) @@ -2024,6 +2026,7 @@ class X86TargetInfo : public TargetInfo { case CK_OpteronSSE3: case CK_AMDFAM10: case CK_BTVER1: + case CK_BTVER2: case CK_BDVER1: case CK_BDVER2: case CK_x86_64: @@ -2193,6 +2196,15 @@ void X86TargetInfo::getDefaultFeatures(llvm::StringMap &Features) const { setFeatureEnabled(Features, "lzcnt", true); setFeatureEnabled(Features, "popcnt", true); break; + case CK_BTVER2: + setFeatureEnabled(Features, "avx", true); + setFeatureEnabled(Features, "sse4a", true); + setFeatureEnabled(Features, "lzcnt", true); + setFeatureEnabled(Features, "aes", true); + setFeatureEnabled(Features, "pclmul", true); + setFeatureEnabled(Features, "bmi", true); + setFeatureEnabled(Features, "f16c", true); + break; case CK_BDVER1: setFeatureEnabled(Features, "xop", true); setFeatureEnabled(Features, "lzcnt", true); @@ -2611,6 +2623,9 @@ void X86TargetInfo::getTargetDefines(const LangOptions &Opts, case CK_BTVER1: defineCPUMacros(Builder, "btver1"); break; + case CK_BTVER2: + defineCPUMacros(Builder, "btver2"); + break; case CK_BDVER1: defineCPUMacros(Builder, "bdver1"); break; @@ -3292,6 +3307,8 @@ namespace { class AArch64TargetInfo : public TargetInfo { static const char * const GCCRegNames[]; static const TargetInfo::GCCRegAlias GCCRegAliases[]; + + static const Builtin::Info BuiltinInfo[]; public: AArch64TargetInfo(const std::string& triple) : TargetInfo(triple) { BigEndian = false; @@ -3323,45 +3340,45 @@ class AArch64TargetInfo : public TargetInfo { // FIXME: these were written based on an unreleased version of a 32-bit ACLE // which was intended to be compatible with a 64-bit implementation. They // will need updating when a real 64-bit ACLE exists. Particularly pressing - // instances are: __AARCH_ISA_A32, __AARCH_ISA_T32, __ARCH_PCS. - Builder.defineMacro("__AARCH_ACLE", "101"); - Builder.defineMacro("__AARCH", "8"); - Builder.defineMacro("__AARCH_PROFILE", "'A'"); + // instances are: __ARM_ARCH_ISA_ARM, __ARM_ARCH_ISA_THUMB, __ARM_PCS. + Builder.defineMacro("__ARM_ACLE", "101"); + Builder.defineMacro("__ARM_ARCH", "8"); + Builder.defineMacro("__ARM_ARCH_PROFILE", "'A'"); - Builder.defineMacro("__AARCH_FEATURE_UNALIGNED"); - Builder.defineMacro("__AARCH_FEATURE_CLZ"); - Builder.defineMacro("__AARCH_FEATURE_FMA"); + Builder.defineMacro("__ARM_FEATURE_UNALIGNED"); + Builder.defineMacro("__ARM_FEATURE_CLZ"); + Builder.defineMacro("__ARM_FEATURE_FMA"); // FIXME: ACLE 1.1 reserves bit 4. Will almost certainly come to mean // 128-bit LDXP present, at which point this becomes 0x1f. - Builder.defineMacro("__AARCH_FEATURE_LDREX", "0xf"); + Builder.defineMacro("__ARM_FEATURE_LDREX", "0xf"); // 0xe implies support for half, single and double precision operations. - Builder.defineMacro("__AARCH_FP", "0xe"); + Builder.defineMacro("__ARM_FP", "0xe"); // PCS specifies this for SysV variants, which is all we support. Other ABIs - // may choose __AARCH_FP16_FORMAT_ALTERNATIVE. - Builder.defineMacro("__AARCH_FP16_FORMAT_IEEE"); + // may choose __ARM_FP16_FORMAT_ALTERNATIVE. + Builder.defineMacro("__ARM_FP16_FORMAT_IEEE"); if (Opts.FastMath || Opts.FiniteMathOnly) - Builder.defineMacro("__AARCH_FP_FAST"); + Builder.defineMacro("__ARM_FP_FAST"); if ((Opts.C99 || Opts.C11) && !Opts.Freestanding) - Builder.defineMacro("__AARCH_FP_FENV_ROUNDING"); + Builder.defineMacro("__ARM_FP_FENV_ROUNDING"); - Builder.defineMacro("__AARCH_SIZEOF_WCHAR_T", + Builder.defineMacro("__ARM_SIZEOF_WCHAR_T", Opts.ShortWChar ? "2" : "4"); - Builder.defineMacro("__AARCH_SIZEOF_MINIMAL_ENUM", + Builder.defineMacro("__ARM_SIZEOF_MINIMAL_ENUM", Opts.ShortEnums ? "1" : "4"); if (BigEndian) - Builder.defineMacro("__AARCH_BIG_ENDIAN"); + Builder.defineMacro("__ARM_BIG_ENDIAN"); } virtual void getTargetBuiltins(const Builtin::Info *&Records, unsigned &NumRecords) const { - Records = 0; - NumRecords = 0; + Records = BuiltinInfo; + NumRecords = clang::AArch64::LastTSBuiltin-Builtin::FirstTSBuiltin; } virtual bool hasFeature(StringRef Feature) const { return Feature == "aarch64"; @@ -3470,6 +3487,14 @@ void AArch64TargetInfo::getGCCRegAliases(const GCCRegAlias *&Aliases, NumAliases = llvm::array_lengthof(GCCRegAliases); } + +const Builtin::Info AArch64TargetInfo::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/BuiltinsAArch64.def" +}; + } // end anonymous namespace namespace { @@ -3502,6 +3527,34 @@ class ARMTargetInfo : public TargetInfo { static const Builtin::Info BuiltinInfo[]; + static bool shouldUseInlineAtomic(const llvm::Triple &T) { + // On linux, binaries targeting old cpus call functions in libgcc to + // perform atomic operations. The implementation in libgcc then calls into + // the kernel which on armv6 and newer uses ldrex and strex. The net result + // is that if we assume the kernel is at least as recent as the hardware, + // it is safe to use atomic instructions on armv6 and newer. + if (T.getOS() != llvm::Triple::Linux) + return false; + StringRef ArchName = T.getArchName(); + if (T.getArch() == llvm::Triple::arm) { + if (!ArchName.startswith("armv")) + return false; + StringRef VersionStr = ArchName.substr(4); + unsigned Version; + if (VersionStr.getAsInteger(10, Version)) + return false; + return Version >= 6; + } + assert(T.getArch() == llvm::Triple::thumb); + if (!ArchName.startswith("thumbv")) + return false; + StringRef VersionStr = ArchName.substr(6); + unsigned Version; + if (VersionStr.getAsInteger(10, Version)) + return false; + return Version >= 7; + } + public: ARMTargetInfo(const std::string &TripleStr) : TargetInfo(TripleStr), ABI("aapcs-linux"), CPU("arm1136j-s"), IsAAPCS(true) @@ -3534,8 +3587,9 @@ class ARMTargetInfo : public TargetInfo { TheCXXABI.set(TargetCXXABI::GenericARM); // ARM has atomics up to 8 bytes - // FIXME: Set MaxAtomicInlineWidth if we have the feature v6e MaxAtomicPromoteWidth = 64; + if (shouldUseInlineAtomic(getTriple())) + MaxAtomicInlineWidth = 64; // Do force alignment of members that follow zero length bitfields. If // the alignment of the zero-length bitfield is greater than the member @@ -4111,16 +4165,14 @@ const Builtin::Info HexagonTargetInfo::BuiltinInfo[] = { namespace { -class SparcV8TargetInfo : public TargetInfo { +// Shared base class for SPARC v8 (32-bit) and SPARC v9 (64-bit). +class SparcTargetInfo : public TargetInfo { static const TargetInfo::GCCRegAlias GCCRegAliases[]; static const char * const GCCRegNames[]; bool SoftFloat; public: - SparcV8TargetInfo(const std::string& triple) : TargetInfo(triple) { - // FIXME: Support Sparc quad-precision long double? - 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"; - } + SparcTargetInfo(const std::string &triple) : TargetInfo(triple) {} + virtual bool setFeatureEnabled(llvm::StringMap &Features, StringRef Name, bool Enabled) const { @@ -4140,7 +4192,6 @@ class SparcV8TargetInfo : public TargetInfo { virtual void getTargetDefines(const LangOptions &Opts, MacroBuilder &Builder) const { DefineStd(Builder, "sparc", Opts); - Builder.defineMacro("__sparcv8"); Builder.defineMacro("__REGISTER_PREFIX__", ""); if (SoftFloat) @@ -4176,20 +4227,20 @@ class SparcV8TargetInfo : public TargetInfo { } }; -const char * const SparcV8TargetInfo::GCCRegNames[] = { +const char * const SparcTargetInfo::GCCRegNames[] = { "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23", "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31" }; -void SparcV8TargetInfo::getGCCRegNames(const char * const *&Names, - unsigned &NumNames) const { +void SparcTargetInfo::getGCCRegNames(const char * const *&Names, + unsigned &NumNames) const { Names = GCCRegNames; NumNames = llvm::array_lengthof(GCCRegNames); } -const TargetInfo::GCCRegAlias SparcV8TargetInfo::GCCRegAliases[] = { +const TargetInfo::GCCRegAlias SparcTargetInfo::GCCRegAliases[] = { { { "g0" }, "r0" }, { { "g1" }, "r1" }, { { "g2" }, "r2" }, @@ -4224,11 +4275,53 @@ const TargetInfo::GCCRegAlias SparcV8TargetInfo::GCCRegAliases[] = { { { "i7" }, "r31" }, }; -void SparcV8TargetInfo::getGCCRegAliases(const GCCRegAlias *&Aliases, - unsigned &NumAliases) const { +void SparcTargetInfo::getGCCRegAliases(const GCCRegAlias *&Aliases, + unsigned &NumAliases) const { Aliases = GCCRegAliases; NumAliases = llvm::array_lengthof(GCCRegAliases); } + +// SPARC v8 is the 32-bit mode selected by Triple::sparc. +class SparcV8TargetInfo : public SparcTargetInfo { +public: + SparcV8TargetInfo(const std::string& triple) : SparcTargetInfo(triple) { + // FIXME: Support Sparc quad-precision long double? + 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-S64"; + } + + virtual void getTargetDefines(const LangOptions &Opts, + MacroBuilder &Builder) const { + SparcTargetInfo::getTargetDefines(Opts, Builder); + Builder.defineMacro("__sparcv8"); + } +}; + +// SPARC v9 is the 64-bit mode selected by Triple::sparcv9. +class SparcV9TargetInfo : public SparcTargetInfo { +public: + SparcV9TargetInfo(const std::string& triple) : SparcTargetInfo(triple) { + // FIXME: Support Sparc quad-precision long double? + DescriptionString = "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-v64:64:64-n32:64-S128"; + } + + virtual void getTargetDefines(const LangOptions &Opts, + MacroBuilder &Builder) const { + SparcTargetInfo::getTargetDefines(Opts, Builder); + Builder.defineMacro("__sparcv9"); + Builder.defineMacro("__arch64__"); + // Solaris and its derivative AuroraUX don't need these variants, but the + // BSDs do. + if (getTriple().getOS() != llvm::Triple::Solaris && + getTriple().getOS() != llvm::Triple::AuroraUX) { + Builder.defineMacro("__sparc64__"); + Builder.defineMacro("__sparc_v9__"); + Builder.defineMacro("__sparcv9__"); + } + } +}; + } // end anonymous namespace. namespace { @@ -4250,6 +4343,100 @@ class SolarisSparcV8TargetInfo : public SolarisTargetInfo { }; } // end anonymous namespace. +namespace { + class SystemZTargetInfo : public TargetInfo { + static const char *const GCCRegNames[]; + + public: + SystemZTargetInfo(const std::string& triple) : TargetInfo(triple) { + TLSSupported = true; + IntWidth = IntAlign = 32; + LongWidth = LongLongWidth = LongAlign = LongLongAlign = 64; + PointerWidth = PointerAlign = 64; + LongDoubleWidth = 128; + LongDoubleAlign = 64; + LongDoubleFormat = &llvm::APFloat::IEEEquad; + MinGlobalAlign = 16; + DescriptionString = "E-p:64:64:64-i1:8:16-i8:8:16-i16:16-i32:32-i64:64" + "-f32:32-f64:64-f128:64-a0:8:16-n32:64"; + MaxAtomicPromoteWidth = MaxAtomicInlineWidth = 64; + } + virtual void getTargetDefines(const LangOptions &Opts, + MacroBuilder &Builder) const { + Builder.defineMacro("__s390__"); + Builder.defineMacro("__s390x__"); + Builder.defineMacro("__zarch__"); + Builder.defineMacro("__LONG_DOUBLE_128__"); + } + virtual void getTargetBuiltins(const Builtin::Info *&Records, + unsigned &NumRecords) const { + // FIXME: Implement. + Records = 0; + NumRecords = 0; + } + + virtual void getGCCRegNames(const char *const *&Names, + unsigned &NumNames) const; + virtual void getGCCRegAliases(const GCCRegAlias *&Aliases, + unsigned &NumAliases) const { + // No aliases. + Aliases = 0; + NumAliases = 0; + } + virtual bool validateAsmConstraint(const char *&Name, + TargetInfo::ConstraintInfo &info) const; + virtual const char *getClobbers() const { + // FIXME: Is this really right? + return ""; + } + virtual BuiltinVaListKind getBuiltinVaListKind() const { + return TargetInfo::SystemZBuiltinVaList; + } + }; + + const char *const SystemZTargetInfo::GCCRegNames[] = { + "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", + "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", + "f0", "f2", "f4", "f6", "f1", "f3", "f5", "f7", + "f8", "f10", "f12", "f14", "f9", "f11", "f13", "f15" + }; + + void SystemZTargetInfo::getGCCRegNames(const char *const *&Names, + unsigned &NumNames) const { + Names = GCCRegNames; + NumNames = llvm::array_lengthof(GCCRegNames); + } + + bool SystemZTargetInfo:: + validateAsmConstraint(const char *&Name, + TargetInfo::ConstraintInfo &Info) const { + switch (*Name) { + default: + return false; + + case 'a': // Address register + case 'd': // Data register (equivalent to 'r') + case 'f': // Floating-point register + Info.setAllowsRegister(); + return true; + + case 'I': // Unsigned 8-bit constant + case 'J': // Unsigned 12-bit constant + case 'K': // Signed 16-bit constant + case 'L': // Signed 20-bit displacement (on all targets we support) + case 'M': // 0x7fffffff + return true; + + case 'Q': // Memory with base and unsigned 12-bit displacement + case 'R': // Likewise, plus an index + case 'S': // Memory with base and signed 20-bit displacement + case 'T': // Likewise, plus an index + Info.setAllowsMemory(); + return true; + } + } +} + namespace { class MSP430TargetInfo : public TargetInfo { static const char * const GCCRegNames[]; @@ -4405,8 +4592,10 @@ class MipsTargetInfoBase : public TargetInfo { static const Builtin::Info BuiltinInfo[]; std::string CPU; bool IsMips16; + bool IsMicromips; + bool IsSingleFloat; enum MipsFloatABI { - HardFloat, SingleFloat, SoftFloat + HardFloat, SoftFloat } FloatABI; enum DspRevEnum { NoDSP, DSP1, DSP2 @@ -4422,6 +4611,8 @@ class MipsTargetInfoBase : public TargetInfo { : TargetInfo(triple), CPU(CPUStr), IsMips16(false), + IsMicromips(false), + IsSingleFloat(false), FloatABI(HardFloat), DspRev(NoDSP), ABI(ABIStr) @@ -4448,18 +4639,20 @@ class MipsTargetInfoBase : public TargetInfo { case HardFloat: Builder.defineMacro("__mips_hard_float", Twine(1)); 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 (IsSingleFloat) + Builder.defineMacro("__mips_single_float", Twine(1)); + if (IsMips16) Builder.defineMacro("__mips16", Twine(1)); + if (IsMicromips) + Builder.defineMacro("__mips_micromips", Twine(1)); + switch (DspRev) { default: break; @@ -4549,7 +4742,8 @@ class MipsTargetInfoBase : public TargetInfo { Name == "o32" || Name == "n32" || Name == "n64" || Name == "eabi" || Name == "mips32" || Name == "mips32r2" || Name == "mips64" || Name == "mips64r2" || - Name == "mips16" || Name == "dsp" || Name == "dspr2") { + Name == "mips16" || Name == "micromips" || + Name == "dsp" || Name == "dspr2") { Features[Name] = Enabled; return true; } else if (Name == "32") { @@ -4564,17 +4758,21 @@ class MipsTargetInfoBase : public TargetInfo { virtual void HandleTargetFeatures(std::vector &Features) { IsMips16 = false; + IsMicromips = false; + IsSingleFloat = false; FloatABI = HardFloat; DspRev = NoDSP; for (std::vector::iterator it = Features.begin(), ie = Features.end(); it != ie; ++it) { if (*it == "+single-float") - FloatABI = SingleFloat; + IsSingleFloat = true; else if (*it == "+soft-float") FloatABI = SoftFloat; else if (*it == "+mips16") IsMips16 = true; + else if (*it == "+micromips") + IsMicromips = true; else if (*it == "+dsp") DspRev = std::max(DspRev, DSP1); else if (*it == "+dspr2") @@ -4862,7 +5060,7 @@ class PNaClTargetInfo : public TargetInfo { this->SizeType = TargetInfo::UnsignedInt; this->PtrDiffType = TargetInfo::SignedInt; this->IntPtrType = TargetInfo::SignedInt; - this->RegParmMax = 2; + this->RegParmMax = 0; // Disallow regparm DescriptionString = "e-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-" "f32:32:32-f64:64:64-p:32:32:32-v128:32:32"; } @@ -5188,6 +5386,32 @@ static TargetInfo *AllocateTarget(const std::string &T) { return new SparcV8TargetInfo(T); } + case llvm::Triple::sparcv9: + switch (os) { + case llvm::Triple::Linux: + return new LinuxTargetInfo(T); + case llvm::Triple::AuroraUX: + return new AuroraUXTargetInfo(T); + case llvm::Triple::Solaris: + return new SolarisTargetInfo(T); + case llvm::Triple::NetBSD: + return new NetBSDTargetInfo(T); + case llvm::Triple::OpenBSD: + return new OpenBSDTargetInfo(T); + case llvm::Triple::FreeBSD: + return new FreeBSDTargetInfo(T); + default: + return new SparcV9TargetInfo(T); + } + + case llvm::Triple::systemz: + switch (os) { + case llvm::Triple::Linux: + return new LinuxTargetInfo(T); + default: + return new SystemZTargetInfo(T); + } + case llvm::Triple::tce: return new TCETargetInfo(T); diff --git a/lib/Basic/Version.cpp b/lib/Basic/Version.cpp index 7381e702508..743143d643a 100644 --- a/lib/Basic/Version.cpp +++ b/lib/Basic/Version.cpp @@ -36,7 +36,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/trunk/lib/Basic/Version.cpp $"); + static StringRef SVNRepository("$URL: http://llvm.org/svn/llvm-project/cfe/tags/RELEASE_33/final/lib/Basic/Version.cpp $"); if (URL.empty()) { URL = SVNRepository.slice(SVNRepository.find(':'), SVNRepository.find("/lib/Basic")); diff --git a/lib/CodeGen/ABIInfo.h b/lib/CodeGen/ABIInfo.h index 35780f1556d..df6dc7216a4 100644 --- a/lib/CodeGen/ABIInfo.h +++ b/lib/CodeGen/ABIInfo.h @@ -22,6 +22,7 @@ namespace llvm { namespace clang { class ASTContext; + class TargetInfo; namespace CodeGen { class CGFunctionInfo; @@ -196,6 +197,7 @@ namespace clang { ASTContext &getContext() const; llvm::LLVMContext &getVMContext() const; const llvm::DataLayout &getDataLayout() const; + const TargetInfo &getTarget() const; /// Return the calling convention to use for system runtime /// functions. diff --git a/lib/CodeGen/CGAtomic.cpp b/lib/CodeGen/CGAtomic.cpp index 817d5c4cc68..0b48a5c6643 100644 --- a/lib/CodeGen/CGAtomic.cpp +++ b/lib/CodeGen/CGAtomic.cpp @@ -327,7 +327,7 @@ RValue CodeGenFunction::EmitAtomicExpr(AtomicExpr *E, llvm::Value *Dest) { CharUnits alignChars = getContext().getTypeAlignInChars(AtomicTy); unsigned Align = alignChars.getQuantity(); unsigned MaxInlineWidthInBits = - getContext().getTargetInfo().getMaxAtomicInlineWidth(); + getTarget().getMaxAtomicInlineWidth(); bool UseLibcall = (Size != Align || getContext().toBits(sizeChars) > MaxInlineWidthInBits); @@ -483,15 +483,6 @@ RValue CodeGenFunction::EmitAtomicExpr(AtomicExpr *E, llvm::Value *Dest) { Args.add(RValue::get(EmitCastToVoidPtr(Dest)), getContext().VoidPtrTy); break; -#if 0 - // These are only defined for 1-16 byte integers. It is not clear what - // their semantics would be on anything else... - case AtomicExpr::Add: LibCallName = "__atomic_fetch_add_generic"; break; - case AtomicExpr::Sub: LibCallName = "__atomic_fetch_sub_generic"; break; - case AtomicExpr::And: LibCallName = "__atomic_fetch_and_generic"; break; - case AtomicExpr::Or: LibCallName = "__atomic_fetch_or_generic"; break; - case AtomicExpr::Xor: LibCallName = "__atomic_fetch_xor_generic"; break; -#endif default: return EmitUnsupportedRValue(E, "atomic library call"); } // order is always the last parameter diff --git a/lib/CodeGen/CGBlocks.cpp b/lib/CodeGen/CGBlocks.cpp index 227ee2d024c..ded019e64ae 100644 --- a/lib/CodeGen/CGBlocks.cpp +++ b/lib/CodeGen/CGBlocks.cpp @@ -695,8 +695,8 @@ llvm::Value *CodeGenFunction::EmitBlockLiteral(const CGBlockInfo &blockInfo) { bool isLambdaConv = blockInfo.getBlockDecl()->isConversionFromLambda(); llvm::Constant *blockFn = CodeGenFunction(CGM, true).GenerateBlockFunction(CurGD, blockInfo, - CurFuncDecl, LocalDeclMap, - isLambdaConv); + LocalDeclMap, + isLambdaConv); blockFn = llvm::ConstantExpr::getBitCast(blockFn, VoidPtrTy); // If there is nothing to capture, we can emit this as a global block. @@ -753,6 +753,7 @@ llvm::Value *CodeGenFunction::EmitBlockLiteral(const CGBlockInfo &blockInfo) { if (capture.isConstant()) continue; QualType type = variable->getType(); + CharUnits align = getContext().getDeclAlign(variable); // This will be a [[type]]*, except that a byref entry will just be // an i8**. @@ -796,21 +797,21 @@ llvm::Value *CodeGenFunction::EmitBlockLiteral(const CGBlockInfo &blockInfo) { if (ci->isByRef()) { // Get a void* that points to the byref struct. if (ci->isNested()) - src = Builder.CreateLoad(src, "byref.capture"); + src = Builder.CreateAlignedLoad(src, align.getQuantity(), + "byref.capture"); else src = Builder.CreateBitCast(src, VoidPtrTy); // Write that void* into the capture field. - Builder.CreateStore(src, blockField); + Builder.CreateAlignedStore(src, blockField, align.getQuantity()); // If we have a copy constructor, evaluate that into the block field. } else if (const Expr *copyExpr = ci->getCopyExpr()) { if (blockDecl->isConversionFromLambda()) { // If we have a lambda conversion, emit the expression // directly into the block instead. - CharUnits Align = getContext().getTypeAlignInChars(type); AggValueSlot Slot = - AggValueSlot::forAddr(blockField, Align, Qualifiers(), + AggValueSlot::forAddr(blockField, align, Qualifiers(), AggValueSlot::IsDestructed, AggValueSlot::DoesNotNeedGCBarriers, AggValueSlot::IsNotAliased); @@ -821,7 +822,27 @@ llvm::Value *CodeGenFunction::EmitBlockLiteral(const CGBlockInfo &blockInfo) { // If it's a reference variable, copy the reference into the block field. } else if (type->isReferenceType()) { - Builder.CreateStore(Builder.CreateLoad(src, "ref.val"), blockField); + llvm::Value *ref = + Builder.CreateAlignedLoad(src, align.getQuantity(), "ref.val"); + Builder.CreateAlignedStore(ref, blockField, align.getQuantity()); + + // If this is an ARC __strong block-pointer variable, don't do a + // block copy. + // + // TODO: this can be generalized into the normal initialization logic: + // we should never need to do a block-copy when initializing a local + // variable, because the local variable's lifetime should be strictly + // contained within the stack block's. + } else if (type.getObjCLifetime() == Qualifiers::OCL_Strong && + type->isBlockPointerType()) { + // Load the block and do a simple retain. + LValue srcLV = MakeAddrLValue(src, type, align); + llvm::Value *value = EmitLoadOfScalar(srcLV); + value = EmitARCRetainNonBlock(value); + + // Do a primitive store to the block field. + LValue destLV = MakeAddrLValue(blockField, type, align); + EmitStoreOfScalar(value, destLV, /*init*/ true); // Otherwise, fake up a POD copy into the block field. } else { @@ -839,8 +860,7 @@ llvm::Value *CodeGenFunction::EmitBlockLiteral(const CGBlockInfo &blockInfo) { ImplicitCastExpr l2r(ImplicitCastExpr::OnStack, type, CK_LValueToRValue, &declRef, VK_RValue); EmitExprAsInit(&l2r, &blockFieldPseudoVar, - MakeAddrLValue(blockField, type, - getContext().getDeclAlign(variable)), + MakeAddrLValue(blockField, type, align), /*captured by init*/ false); } @@ -1014,7 +1034,7 @@ CodeGenModule::GetAddrOfGlobalBlock(const BlockExpr *blockExpr, llvm::DenseMap LocalDeclMap; blockFn = CodeGenFunction(*this).GenerateBlockFunction(GlobalDecl(), blockInfo, - 0, LocalDeclMap, + LocalDeclMap, false); } blockFn = llvm::ConstantExpr::getBitCast(blockFn, VoidPtrTy); @@ -1068,7 +1088,6 @@ static llvm::Constant *buildGlobalBlock(CodeGenModule &CGM, llvm::Function * CodeGenFunction::GenerateBlockFunction(GlobalDecl GD, const CGBlockInfo &blockInfo, - const Decl *outerFnDecl, const DeclMapTy &ldm, bool IsLambdaConversionToBlock) { const BlockDecl *blockDecl = blockInfo.getBlockDecl(); @@ -1128,7 +1147,6 @@ CodeGenFunction::GenerateBlockFunction(GlobalDecl GD, // Begin generating the function. StartFunction(blockDecl, fnType->getResultType(), fn, fnInfo, args, blockInfo.getBlockExpr()->getBody()->getLocStart()); - CurFuncDecl = outerFnDecl; // StartFunction sets this to blockDecl // Okay. Undo some of what StartFunction did. @@ -1149,11 +1167,9 @@ CodeGenFunction::GenerateBlockFunction(GlobalDecl GD, Alloca->setAlignment(Align); // Set the DebugLocation to empty, so the store is recognized as a // frame setup instruction by llvm::DwarfDebug::beginFunction(). - llvm::DebugLoc Empty; - llvm::DebugLoc Loc = Builder.getCurrentDebugLocation(); - Builder.SetCurrentDebugLocation(Empty); + Builder.DisableDebugLocations(); Builder.CreateAlignedStore(BlockPointer, Alloca, Align); - Builder.SetCurrentDebugLocation(Loc); + Builder.EnableDebugLocations(); BlockPointerDbgLoc = Alloca; } @@ -1166,23 +1182,6 @@ CodeGenFunction::GenerateBlockFunction(GlobalDecl GD, CXXThisValue = Builder.CreateLoad(addr, "this"); } - // LoadObjCSelf() expects there to be an entry for 'self' in LocalDeclMap; - // appease it. - if (const ObjCMethodDecl *method - = dyn_cast_or_null(CurFuncDecl)) { - const VarDecl *self = method->getSelfDecl(); - - // There might not be a capture for 'self', but if there is... - if (blockInfo.Captures.count(self)) { - const CGBlockInfo::Capture &capture = blockInfo.getCapture(self); - - llvm::Value *selfAddr = Builder.CreateStructGEP(BlockPointer, - capture.getIndex(), - "block.captured-self"); - LocalDeclMap[self] = selfAddr; - } - } - // Also force all the constant captures. for (BlockDecl::capture_const_iterator ci = blockDecl->capture_begin(), ce = blockDecl->capture_end(); ci != ce; ++ci) { @@ -1241,7 +1240,7 @@ CodeGenFunction::GenerateBlockFunction(GlobalDecl GD, } // Recover location if it was changed in the above loop. DI->EmitLocation(Builder, - cast(blockDecl->getBody())->getRBracLoc()); + cast(blockDecl->getBody())->getRBracLoc()); } // And resume where we left off. @@ -2062,7 +2061,8 @@ llvm::Type *CodeGenFunction::BuildByRefType(const VarDecl *D) { bool Packed = false; CharUnits Align = getContext().getDeclAlign(D); - if (Align > getContext().toCharUnitsFromBits(Target.getPointerAlign(0))) { + if (Align > + getContext().toCharUnitsFromBits(getTarget().getPointerAlign(0))) { // We have to insert padding. // The struct above has 2 32-bit integers. diff --git a/lib/CodeGen/CGBuiltin.cpp b/lib/CodeGen/CGBuiltin.cpp index 3c89652b6dd..d18767897f3 100644 --- a/lib/CodeGen/CGBuiltin.cpp +++ b/lib/CodeGen/CGBuiltin.cpp @@ -296,7 +296,7 @@ RValue CodeGenFunction::EmitBuiltinExpr(const FunctionDecl *FD, Value *F = CGM.getIntrinsic(Intrinsic::cttz, ArgType); llvm::Type *ResultType = ConvertType(E->getType()); - Value *ZeroUndef = Builder.getInt1(Target.isCLZForZeroUndef()); + Value *ZeroUndef = Builder.getInt1(getTarget().isCLZForZeroUndef()); Value *Result = Builder.CreateCall2(F, ArgValue, ZeroUndef); if (Result->getType() != ResultType) Result = Builder.CreateIntCast(Result, ResultType, /*isSigned*/true, @@ -313,7 +313,7 @@ RValue CodeGenFunction::EmitBuiltinExpr(const FunctionDecl *FD, Value *F = CGM.getIntrinsic(Intrinsic::ctlz, ArgType); llvm::Type *ResultType = ConvertType(E->getType()); - Value *ZeroUndef = Builder.getInt1(Target.isCLZForZeroUndef()); + Value *ZeroUndef = Builder.getInt1(getTarget().isCLZForZeroUndef()); Value *Result = Builder.CreateCall2(F, ArgValue, ZeroUndef); if (Result->getType() != ResultType) Result = Builder.CreateIntCast(Result, ResultType, /*isSigned*/true, @@ -1430,7 +1430,7 @@ RValue CodeGenFunction::EmitBuiltinExpr(const FunctionDecl *FD, const char *Name = getContext().BuiltinInfo.GetName(BuiltinID); Intrinsic::ID IntrinsicID = Intrinsic::not_intrinsic; if (const char *Prefix = - llvm::Triple::getArchTypePrefix(Target.getTriple().getArch())) + llvm::Triple::getArchTypePrefix(getTarget().getTriple().getArch())) IntrinsicID = Intrinsic::getIntrinsicForGCCBuiltin(Prefix, Name); if (IntrinsicID != Intrinsic::not_intrinsic) { @@ -1501,7 +1501,9 @@ RValue CodeGenFunction::EmitBuiltinExpr(const FunctionDecl *FD, Value *CodeGenFunction::EmitTargetBuiltinExpr(unsigned BuiltinID, const CallExpr *E) { - switch (Target.getTriple().getArch()) { + switch (getTarget().getTriple().getArch()) { + case llvm::Triple::aarch64: + return EmitAArch64BuiltinExpr(BuiltinID, E); case llvm::Triple::arm: case llvm::Triple::thumb: return EmitARMBuiltinExpr(BuiltinID, E); @@ -1621,6 +1623,25 @@ CodeGenFunction::EmitPointerWithAlignment(const Expr *Addr) { return std::make_pair(EmitScalarExpr(Addr), Align); } +Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID, + const CallExpr *E) { + if (BuiltinID == AArch64::BI__clear_cache) { + assert(E->getNumArgs() == 2 && + "Variadic __clear_cache slipped through on AArch64"); + + const FunctionDecl *FD = E->getDirectCallee(); + SmallVector Ops; + for (unsigned i = 0; i < E->getNumArgs(); i++) + Ops.push_back(EmitScalarExpr(E->getArg(i))); + llvm::Type *Ty = CGM.getTypes().ConvertType(FD->getType()); + llvm::FunctionType *FTy = cast(Ty); + StringRef Name = FD->getName(); + return EmitNounwindRuntimeCall(CGM.CreateRuntimeFunction(FTy, Name), Ops); + } + + return 0; +} + Value *CodeGenFunction::EmitARMBuiltinExpr(unsigned BuiltinID, const CallExpr *E) { if (BuiltinID == ARM::BI__clear_cache) { @@ -1852,7 +1873,7 @@ Value *CodeGenFunction::EmitARMBuiltinExpr(unsigned BuiltinID, // 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())); + Ops.push_back(Builder.getInt1(getTarget().isCLZForZeroUndef())); return EmitNeonCall(F, Ops, "vclz"); } case ARM::BI__builtin_neon_vcnt_v: diff --git a/lib/CodeGen/CGCXXABI.cpp b/lib/CodeGen/CGCXXABI.cpp index 0c0a76f346a..68fecb2d0cb 100644 --- a/lib/CodeGen/CGCXXABI.cpp +++ b/lib/CodeGen/CGCXXABI.cpp @@ -220,8 +220,12 @@ void CGCXXABI::EmitGuardedInit(CodeGenFunction &CGF, } void CGCXXABI::registerGlobalDtor(CodeGenFunction &CGF, + const VarDecl &D, llvm::Constant *dtor, llvm::Constant *addr) { + if (D.getTLSKind()) + CGM.ErrorUnsupported(&D, "non-trivial TLS destruction"); + // The default behavior is to use atexit. CGF.registerGlobalDtorWithAtExit(dtor, addr); } @@ -255,3 +259,14 @@ llvm::BasicBlock *CGCXXABI::EmitCtorCompleteObjectHandler( ErrorUnsupportedABI(CGF, "complete object detection in ctor"); return 0; } + +void CGCXXABI::EmitThreadLocalInitFuncs( + llvm::ArrayRef > Decls, + llvm::Function *InitFunc) { +} + +LValue CGCXXABI::EmitThreadLocalDeclRefExpr(CodeGenFunction &CGF, + const DeclRefExpr *DRE) { + ErrorUnsupportedABI(CGF, "odr-use of thread_local global"); + return LValue(); +} diff --git a/lib/CodeGen/CGCXXABI.h b/lib/CodeGen/CGCXXABI.h index 702e59b71a7..1e4da631d6f 100644 --- a/lib/CodeGen/CGCXXABI.h +++ b/lib/CodeGen/CGCXXABI.h @@ -101,6 +101,27 @@ class CGCXXABI { /// kinds that the ABI says returns 'this'. virtual bool HasThisReturn(GlobalDecl GD) const { return false; } + /// Returns true if the given record type should be returned indirectly. + virtual bool isReturnTypeIndirect(const CXXRecordDecl *RD) const = 0; + + /// Specify how one should pass an argument of a record type. + enum RecordArgABI { + /// Pass it using the normal C aggregate rules for the ABI, potentially + /// introducing extra copies and passing some or all of it in registers. + RAA_Default = 0, + + /// Pass it on the stack using its defined layout. The argument must be + /// evaluated directly into the correct stack position in the arguments area, + /// and the call machinery must not move it or introduce extra copies. + RAA_DirectInMemory, + + /// Pass it as a pointer to temporary memory. + RAA_Indirect + }; + + /// Returns how an argument of the given record type should be passed. + virtual RecordArgABI getRecordArgABI(const CXXRecordDecl *RD) const = 0; + /// Find the LLVM type used to represent the given member pointer /// type. virtual llvm::Type * @@ -330,8 +351,27 @@ class CGCXXABI { /// /// \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 void registerGlobalDtor(CodeGenFunction &CGF, const VarDecl &D, + llvm::Constant *dtor, llvm::Constant *addr); + + /*************************** thread_local initialization ********************/ + + /// Emits ABI-required functions necessary to initialize thread_local + /// variables in this translation unit. + /// + /// \param Decls The thread_local declarations in this translation unit. + /// \param InitFunc If this translation unit contains any non-constant + /// initialization or non-trivial destruction for thread_local + /// variables, a function to perform the initialization. Otherwise, 0. + virtual void EmitThreadLocalInitFuncs( + llvm::ArrayRef > Decls, + llvm::Function *InitFunc); + + /// Emit a reference to a non-local thread_local variable (including + /// triggering the initialization of all thread_local variables in its + /// translation unit). + virtual LValue EmitThreadLocalDeclRefExpr(CodeGenFunction &CGF, + const DeclRefExpr *DRE); }; // Create an instance of a C++ ABI class: diff --git a/lib/CodeGen/CGCall.cpp b/lib/CodeGen/CGCall.cpp index faf32e30083..b0f460ec0e7 100644 --- a/lib/CodeGen/CGCall.cpp +++ b/lib/CodeGen/CGCall.cpp @@ -77,9 +77,7 @@ CodeGenTypes::arrangeFreeFunctionType(CanQual FTNP) { // When translating an unprototyped function type, always use a // variadic type. return arrangeLLVMFunctionInfo(FTNP->getResultType().getUnqualifiedType(), - ArrayRef(), - FTNP->getExtInfo(), - RequiredArgs(0)); + None, FTNP->getExtInfo(), RequiredArgs(0)); } /// Arrange the LLVM function layout for a value of the given function @@ -257,10 +255,8 @@ CodeGenTypes::arrangeFunctionDeclaration(const FunctionDecl *FD) { // non-variadic type. if (isa(FTy)) { CanQual noProto = FTy.getAs(); - return arrangeLLVMFunctionInfo(noProto->getResultType(), - ArrayRef(), - noProto->getExtInfo(), - RequiredArgs::All); + return arrangeLLVMFunctionInfo(noProto->getResultType(), None, + noProto->getExtInfo(), RequiredArgs::All); } assert(isa(FTy)); @@ -420,7 +416,7 @@ CodeGenTypes::arrangeFunctionDeclaration(QualType resultType, } const CGFunctionInfo &CodeGenTypes::arrangeNullaryFunction() { - return arrangeLLVMFunctionInfo(getContext().VoidTy, ArrayRef(), + return arrangeLLVMFunctionInfo(getContext().VoidTy, None, FunctionType::ExtInfo(), RequiredArgs::All); } @@ -837,12 +833,11 @@ bool CodeGenModule::ReturnTypeUsesFPRet(QualType ResultType) { default: return false; case BuiltinType::Float: - return getContext().getTargetInfo().useObjCFPRetForRealType(TargetInfo::Float); + return getTarget().useObjCFPRetForRealType(TargetInfo::Float); case BuiltinType::Double: - return getContext().getTargetInfo().useObjCFPRetForRealType(TargetInfo::Double); + return getTarget().useObjCFPRetForRealType(TargetInfo::Double); case BuiltinType::LongDouble: - return getContext().getTargetInfo().useObjCFPRetForRealType( - TargetInfo::LongDouble); + return getTarget().useObjCFPRetForRealType(TargetInfo::LongDouble); } } @@ -853,7 +848,7 @@ bool CodeGenModule::ReturnTypeUsesFP2Ret(QualType ResultType) { if (const ComplexType *CT = ResultType->getAs()) { if (const BuiltinType *BT = CT->getElementType()->getAs()) { if (BT->getKind() == BuiltinType::LongDouble) - return getContext().getTargetInfo().useObjCFP2RetForComplexLongDouble(); + return getTarget().useObjCFP2RetForComplexLongDouble(); } } @@ -1197,7 +1192,7 @@ void CodeGenFunction::EmitFunctionProlog(const CGFunctionInfo &FI, // initialize the return value. TODO: it might be nice to have // a more general mechanism for this that didn't require synthesized // return statements. - if (const FunctionDecl *FD = dyn_cast_or_null(CurFuncDecl)) { + if (const FunctionDecl *FD = dyn_cast_or_null(CurCodeDecl)) { if (FD->hasImplicitReturnZero()) { QualType RetTy = FD->getResultType().getUnqualifiedType(); llvm::Type* LLVMTy = CGM.getTypes().ConvertType(RetTy); @@ -1626,7 +1621,8 @@ static bool checkThisPointer(llvm::Value *ThisArg, llvm::Value *This) { return false; } -void CodeGenFunction::EmitFunctionEpilog(const CGFunctionInfo &FI) { +void CodeGenFunction::EmitFunctionEpilog(const CGFunctionInfo &FI, + bool EmitRetDbgLoc) { // Functions with no result always return void. if (ReturnValue == 0) { Builder.CreateRetVoid(); @@ -1671,8 +1667,10 @@ void CodeGenFunction::EmitFunctionEpilog(const CGFunctionInfo &FI) { // If there is a dominating store to ReturnValue, we can elide // the load, zap the store, and usually zap the alloca. if (llvm::StoreInst *SI = findDominatingStoreToReturnValue(*this)) { + // Reuse the debug location from the store unless we're told not to. + if (EmitRetDbgLoc) + RetDbgLoc = SI->getDebugLoc(); // Get the stored value and nuke the now-dead store. - RetDbgLoc = SI->getDebugLoc(); RV = SI->getValueOperand(); SI->eraseFromParent(); diff --git a/lib/CodeGen/CGClass.cpp b/lib/CodeGen/CGClass.cpp index 2ececb03651..3fd075701d0 100644 --- a/lib/CodeGen/CGClass.cpp +++ b/lib/CodeGen/CGClass.cpp @@ -290,7 +290,7 @@ llvm::Value *CodeGenFunction::GetVTTParameter(GlobalDecl GD, return 0; } - const CXXRecordDecl *RD = cast(CurFuncDecl)->getParent(); + const CXXRecordDecl *RD = cast(CurCodeDecl)->getParent(); const CXXRecordDecl *Base = cast(GD.getDecl())->getParent(); llvm::Value *VTT; @@ -710,7 +710,7 @@ void CodeGenFunction::EmitConstructorBody(FunctionArgList &Args) { // Before we go any further, try the complete->base constructor // delegation optimization. if (CtorType == Ctor_Complete && IsConstructorDelegationValid(Ctor) && - CGM.getContext().getTargetInfo().getCXXABI().hasConstructorVariants()) { + CGM.getTarget().getCXXABI().hasConstructorVariants()) { if (CGDebugInfo *DI = getDebugInfo()) DI->EmitLocation(Builder, Ctor->getLocEnd()); EmitDelegateCXXConstructorCall(Ctor, Ctor_Base, Args); @@ -1139,6 +1139,7 @@ void CodeGenFunction::EmitCtorPrologue(const CXXConstructorDecl *CD, InitializeVTablePointers(ClassDecl); // And finally, initialize class members. + FieldConstructionScope FCS(*this, CXXThisValue); ConstructorMemcpyizer CM(*this, CD, Args); for (; B != E; B++) { CXXCtorInitializer *Member = (*B); @@ -1278,7 +1279,7 @@ void CodeGenFunction::EmitDestructorBody(FunctionArgList &Args) { EnterDtorCleanups(Dtor, Dtor_Complete); if (!isTryBody && - CGM.getContext().getTargetInfo().getCXXABI().hasDestructorVariants()) { + CGM.getTarget().getCXXABI().hasDestructorVariants()) { EmitCXXDestructorCall(Dtor, Dtor_Base, /*ForVirtualBase=*/false, /*Delegating=*/false, LoadCXXThis()); break; @@ -2231,10 +2232,10 @@ void CodeGenFunction::EmitLambdaBlockInvokeBody() { } void CodeGenFunction::EmitLambdaToBlockPointerBody(FunctionArgList &Args) { - if (cast(CurFuncDecl)->isVariadic()) { + if (cast(CurCodeDecl)->isVariadic()) { // FIXME: Making this work correctly is nasty because it requires either // cloning the body of the call operator or making the call operator forward. - CGM.ErrorUnsupported(CurFuncDecl, "lambda conversion to variadic function"); + CGM.ErrorUnsupported(CurCodeDecl, "lambda conversion to variadic function"); return; } diff --git a/lib/CodeGen/CGCleanup.cpp b/lib/CodeGen/CGCleanup.cpp index 861d31fb7fc..ba6b56c2676 100644 --- a/lib/CodeGen/CGCleanup.cpp +++ b/lib/CodeGen/CGCleanup.cpp @@ -371,7 +371,8 @@ void CodeGenFunction::ResolveBranchFixups(llvm::BasicBlock *Block) { } /// Pops cleanup blocks until the given savepoint is reached. -void CodeGenFunction::PopCleanupBlocks(EHScopeStack::stable_iterator Old) { +void CodeGenFunction::PopCleanupBlocks(EHScopeStack::stable_iterator Old, + SourceLocation EHLoc) { assert(Old.isValid()); while (EHStack.stable_begin() != Old) { @@ -383,7 +384,7 @@ void CodeGenFunction::PopCleanupBlocks(EHScopeStack::stable_iterator Old) { bool FallThroughIsBranchThrough = Old.strictlyEncloses(Scope.getEnclosingNormalCleanup()); - PopCleanupBlock(FallThroughIsBranchThrough); + PopCleanupBlock(FallThroughIsBranchThrough, EHLoc); } } @@ -532,7 +533,8 @@ static void destroyOptimisticNormalEntry(CodeGenFunction &CGF, /// Pops a cleanup block. If the block includes a normal cleanup, the /// current insertion point is threaded through the cleanup, as are /// any branch fixups on the cleanup. -void CodeGenFunction::PopCleanupBlock(bool FallthroughIsBranchThrough) { +void CodeGenFunction::PopCleanupBlock(bool FallthroughIsBranchThrough, + SourceLocation EHLoc) { assert(!EHStack.empty() && "cleanup stack is empty!"); assert(isa(*EHStack.begin()) && "top not a cleanup!"); EHCleanupScope &Scope = cast(*EHStack.begin()); @@ -833,6 +835,9 @@ void CodeGenFunction::PopCleanupBlock(bool FallthroughIsBranchThrough) { // Emit the EH cleanup if required. if (RequiresEHCleanup) { + if (CGDebugInfo *DI = getDebugInfo()) + DI->EmitLocation(Builder, EHLoc); + CGBuilderTy::InsertPoint SavedIP = Builder.saveAndClearIP(); EmitBlock(EHEntry); @@ -840,6 +845,7 @@ void CodeGenFunction::PopCleanupBlock(bool FallthroughIsBranchThrough) { // 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); } diff --git a/lib/CodeGen/CGDebugInfo.cpp b/lib/CodeGen/CGDebugInfo.cpp index 711d6861507..ddcb931e46c 100644 --- a/lib/CodeGen/CGDebugInfo.cpp +++ b/lib/CodeGen/CGDebugInfo.cpp @@ -89,7 +89,7 @@ void CGDebugInfo::setLocation(SourceLocation Loc) { } /// getContextDescriptor - Get context info for the decl. -llvm::DIDescriptor CGDebugInfo::getContextDescriptor(const Decl *Context) { +llvm::DIScope CGDebugInfo::getContextDescriptor(const Decl *Context) { if (!Context) return TheCU; @@ -97,20 +97,17 @@ llvm::DIDescriptor CGDebugInfo::getContextDescriptor(const Decl *Context) { I = RegionMap.find(Context); if (I != RegionMap.end()) { llvm::Value *V = I->second; - return llvm::DIDescriptor(dyn_cast_or_null(V)); + return llvm::DIScope(dyn_cast_or_null(V)); } // Check namespace. if (const NamespaceDecl *NSDecl = dyn_cast(Context)) - return llvm::DIDescriptor(getOrCreateNameSpace(NSDecl)); + return getOrCreateNameSpace(NSDecl); - if (const RecordDecl *RDecl = dyn_cast(Context)) { - if (!RDecl->isDependentType()) { - llvm::DIType Ty = getOrCreateType(CGM.getContext().getTypeDeclType(RDecl), + if (const RecordDecl *RDecl = dyn_cast(Context)) + if (!RDecl->isDependentType()) + return getOrCreateType(CGM.getContext().getTypeDeclType(RDecl), getOrCreateMainFile()); - return llvm::DIDescriptor(Ty); - } - } return TheCU; } @@ -642,7 +639,7 @@ llvm::DIType CGDebugInfo::CreatePointerLikeType(unsigned Tag, // Size is always the size of a pointer. We can't use getTypeSize here // because that does not return the correct value for references. unsigned AS = CGM.getContext().getTargetAddressSpace(PointeeTy); - uint64_t Size = CGM.getContext().getTargetInfo().getPointerWidth(AS); + uint64_t Size = CGM.getTarget().getPointerWidth(AS); uint64_t Align = CGM.getContext().getTypeAlign(Ty); return DBuilder.createPointerType(CreatePointeeType(PointeeTy, Unit), @@ -984,7 +981,7 @@ llvm::DIType CGDebugInfo::getOrCreateInstanceMethodType( const PointerType *ThisPtrTy = cast(ThisPtr); QualType PointeeTy = ThisPtrTy->getPointeeType(); unsigned AS = CGM.getContext().getTargetAddressSpace(PointeeTy); - uint64_t Size = CGM.getContext().getTargetInfo().getPointerWidth(AS); + uint64_t Size = CGM.getTarget().getPointerWidth(AS); uint64_t Align = CGM.getContext().getTypeAlign(ThisPtrTy); llvm::DIType PointeeType = getOrCreateType(PointeeTy, Unit); llvm::DIType ThisPtrType = DBuilder.createPointerType(PointeeType, Size, Align); @@ -1699,7 +1696,7 @@ llvm::DIType CGDebugInfo::CreateEnumType(const EnumDecl *ED) { unsigned Line = getLineNumber(ED->getLocation()); llvm::DIDescriptor EnumContext = getContextDescriptor(cast(ED->getDeclContext())); - llvm::DIType ClassTy = ED->isScopedUsingClassTag() ? + llvm::DIType ClassTy = ED->isFixed() ? getOrCreateType(ED->getIntegerType(), DefUnit) : llvm::DIType(); llvm::DIType DbgTy = DBuilder.createEnumerationType(EnumContext, ED->getName(), DefUnit, Line, @@ -2398,7 +2395,7 @@ llvm::DIType CGDebugInfo::EmitTypeForVarWithBlocksAttr(const VarDecl *VD, CharUnits Align = CGM.getContext().getDeclAlign(VD); if (Align > CGM.getContext().toCharUnitsFromBits( - CGM.getContext().getTargetInfo().getPointerAlign(0))) { + CGM.getTarget().getPointerAlign(0))) { CharUnits FieldOffsetInBytes = CGM.getContext().toCharUnitsFromBits(FieldOffset); CharUnits AlignedOffsetInBytes @@ -2494,7 +2491,7 @@ void CGDebugInfo::EmitDeclare(const VarDecl *VD, unsigned Tag, addr.push_back(llvm::ConstantInt::get(Int64Ty, llvm::DIBuilder::OpPlus)); // offset of __forwarding field offset = CGM.getContext().toCharUnitsFromBits( - CGM.getContext().getTargetInfo().getPointerWidth(0)); + CGM.getTarget().getPointerWidth(0)); addr.push_back(llvm::ConstantInt::get(Int64Ty, offset.getQuantity())); addr.push_back(llvm::ConstantInt::get(Int64Ty, llvm::DIBuilder::OpDeref)); addr.push_back(llvm::ConstantInt::get(Int64Ty, llvm::DIBuilder::OpPlus)); @@ -2932,6 +2929,16 @@ void CGDebugInfo::EmitGlobalVariable(const ValueDecl *VD, getStaticDataMemberDeclaration(VD)); } +void CGDebugInfo::EmitUsingDirective(const UsingDirectiveDecl &UD) { + llvm::DIScope Scope = + LexicalBlockStack.empty() + ? getContextDescriptor(cast(UD.getDeclContext())) + : llvm::DIScope(LexicalBlockStack.back()); + DBuilder.createImportedModule( + Scope, getOrCreateNameSpace(UD.getNominatedNamespace()), + getLineNumber(UD.getLocation())); +} + /// getOrCreateNamesSpace - Return namespace descriptor for the given /// namespace decl. llvm::DINameSpace diff --git a/lib/CodeGen/CGDebugInfo.h b/lib/CodeGen/CGDebugInfo.h index 3a0df999b54..4080492a1c6 100644 --- a/lib/CodeGen/CGDebugInfo.h +++ b/lib/CodeGen/CGDebugInfo.h @@ -262,6 +262,9 @@ class CGDebugInfo { /// EmitGlobalVariable - Emit global variable's debug info. void EmitGlobalVariable(const ValueDecl *VD, llvm::Constant *Init); + /// \brief - Emit C++ using directive. + void EmitUsingDirective(const UsingDirectiveDecl &UD); + /// getOrCreateRecordType - Emit record type's standalone debug info. llvm::DIType getOrCreateRecordType(QualType Ty, SourceLocation L); @@ -281,7 +284,7 @@ class CGDebugInfo { uint64_t *OffSet); /// getContextDescriptor - Get context info for the decl. - llvm::DIDescriptor getContextDescriptor(const Decl *Decl); + llvm::DIScope getContextDescriptor(const Decl *Decl); /// createRecordFwdDecl - Create a forward decl for a RecordType in a given /// context. diff --git a/lib/CodeGen/CGDecl.cpp b/lib/CodeGen/CGDecl.cpp index 5375c5e18f2..3ce6dec6a53 100644 --- a/lib/CodeGen/CGDecl.cpp +++ b/lib/CodeGen/CGDecl.cpp @@ -45,6 +45,7 @@ void CodeGenFunction::EmitDecl(const Decl &D) { case Decl::CXXDestructor: case Decl::CXXConversion: case Decl::Field: + case Decl::MSProperty: case Decl::IndirectField: case Decl::ObjCIvar: case Decl::ObjCAtDefsField: @@ -69,6 +70,7 @@ void CodeGenFunction::EmitDecl(const Decl &D) { case Decl::Friend: case Decl::FriendTemplate: case Decl::Block: + case Decl::Captured: case Decl::ClassScopeFunctionSpecialization: llvm_unreachable("Declaration should not be in declstmts!"); case Decl::Function: // void X(); @@ -78,7 +80,6 @@ void CodeGenFunction::EmitDecl(const Decl &D) { case Decl::CXXRecord: // struct/union/class X; [C++] case Decl::Using: // using X; [C++] case Decl::UsingShadow: - case Decl::UsingDirective: // using namespace X; [C++] case Decl::NamespaceAlias: case Decl::StaticAssert: // static_assert(X, ""); [C++0x] case Decl::Label: // __label__ x; @@ -88,6 +89,10 @@ void CodeGenFunction::EmitDecl(const Decl &D) { // None of these decls require codegen support. return; + case Decl::UsingDirective: // using namespace X; [C++] + if (CGDebugInfo *DI = getDebugInfo()) + DI->EmitUsingDirective(cast(D)); + return; case Decl::Var: { const VarDecl &VD = cast(D); assert(VD.isLocalVarDecl() && @@ -198,7 +203,7 @@ CodeGenFunction::CreateStaticVarDecl(const VarDecl &D, if (Linkage != llvm::GlobalValue::InternalLinkage) GV->setVisibility(CurFn->getVisibility()); - if (D.isThreadSpecified()) + if (D.getTLSKind()) CGM.setTLSMode(GV, D); return GV; @@ -898,7 +903,7 @@ CodeGenFunction::EmitAutoVarAlloca(const VarDecl &D) { CharUnits allocaAlignment = alignment; if (isByRef) allocaAlignment = std::max(allocaAlignment, - getContext().toCharUnitsFromBits(Target.getPointerAlign(0))); + getContext().toCharUnitsFromBits(getTarget().getPointerAlign(0))); Alloc->setAlignment(allocaAlignment.getQuantity()); DeclPtr = Alloc; diff --git a/lib/CodeGen/CGDeclCXX.cpp b/lib/CodeGen/CGDeclCXX.cpp index 0448d31f407..9ffcff27662 100644 --- a/lib/CodeGen/CGDeclCXX.cpp +++ b/lib/CodeGen/CGDeclCXX.cpp @@ -39,7 +39,7 @@ static void EmitDeclInit(CodeGenFunction &CGF, const VarDecl &D, CodeGenModule &CGM = CGF.CGM; if (lv.isObjCStrong()) CGM.getObjCRuntime().EmitObjCGlobalAssign(CGF, CGF.EmitScalarExpr(Init), - DeclPtr, D.isThreadSpecified()); + DeclPtr, D.getTLSKind()); else if (lv.isObjCWeak()) CGM.getObjCRuntime().EmitObjCWeakAssign(CGF, CGF.EmitScalarExpr(Init), DeclPtr); @@ -80,6 +80,7 @@ static void EmitDeclDestroy(CodeGenFunction &CGF, const VarDecl &D, case QualType::DK_objc_strong_lifetime: case QualType::DK_objc_weak_lifetime: // We don't care about releasing objects during process teardown. + assert(!D.getTLSKind() && "should have rejected this"); return; } @@ -105,7 +106,7 @@ static void EmitDeclDestroy(CodeGenFunction &CGF, const VarDecl &D, argument = llvm::Constant::getNullValue(CGF.Int8PtrTy); } - CGM.getCXXABI().registerGlobalDtor(CGF, function, argument); + CGM.getCXXABI().registerGlobalDtor(CGF, D, function, argument); } /// Emit code to cause the variable at the given address to be considered as @@ -155,7 +156,8 @@ void CodeGenFunction::EmitCXXGlobalVarDeclInit(const VarDecl &D, static llvm::Function * CreateGlobalInitOrDestructFunction(CodeGenModule &CGM, llvm::FunctionType *ty, - const Twine &name); + const Twine &name, + bool TLS = false); /// Create a stub function, suitable for being passed to atexit, /// which passes the given address to the given destructor function. @@ -224,14 +226,14 @@ void CodeGenFunction::EmitCXXGuardedInit(const VarDecl &D, static llvm::Function * CreateGlobalInitOrDestructFunction(CodeGenModule &CGM, llvm::FunctionType *FTy, - const Twine &Name) { + const Twine &Name, bool TLS) { llvm::Function *Fn = llvm::Function::Create(FTy, llvm::GlobalValue::InternalLinkage, Name, &CGM.getModule()); - if (!CGM.getLangOpts().AppleKext) { + if (!CGM.getLangOpts().AppleKext && !TLS) { // Set the section if needed. if (const char *Section = - CGM.getContext().getTargetInfo().getStaticInitSectionSpecifier()) + CGM.getTarget().getStaticInitSectionSpecifier()) Fn->setSection(Section); } @@ -263,12 +265,20 @@ CodeGenModule::EmitCXXGlobalVarDeclInitFunc(const VarDecl *D, CodeGenFunction(*this).GenerateCXXGlobalVarDeclInitFunc(Fn, D, Addr, PerformInit); - if (D->hasAttr()) { + if (D->getTLSKind()) { + // FIXME: Should we support init_priority for thread_local? + // FIXME: Ideally, initialization of instantiated thread_local static data + // members of class templates should not trigger initialization of other + // entities in the TU. + // FIXME: We only need to register one __cxa_thread_atexit function for the + // entire TU. + CXXThreadLocalInits.push_back(Fn); + } else if (D->hasAttr()) { unsigned int order = D->getAttr()->getPriority(); OrderGlobalInits Key(order, PrioritizedCXXGlobalInits.size()); PrioritizedCXXGlobalInits.push_back(std::make_pair(Key, Fn)); DelayedCXXInitPosition.erase(D); - } else { + } else { llvm::DenseMap::iterator I = DelayedCXXInitPosition.find(D); if (I == DelayedCXXInitPosition.end()) { @@ -281,6 +291,27 @@ CodeGenModule::EmitCXXGlobalVarDeclInitFunc(const VarDecl *D, } } +void CodeGenModule::EmitCXXThreadLocalInitFunc() { + llvm::Function *InitFn = 0; + if (!CXXThreadLocalInits.empty()) { + // Generate a guarded initialization function. + llvm::FunctionType *FTy = llvm::FunctionType::get(VoidTy, false); + InitFn = CreateGlobalInitOrDestructFunction(*this, FTy, "__tls_init", + /*TLS*/ true); + llvm::GlobalVariable *Guard = new llvm::GlobalVariable( + getModule(), Int8Ty, false, llvm::GlobalVariable::InternalLinkage, + llvm::ConstantInt::get(Int8Ty, 0), "__tls_guard"); + Guard->setThreadLocal(true); + CodeGenFunction(*this) + .GenerateCXXGlobalInitFunc(InitFn, CXXThreadLocalInits, Guard); + } + + getCXXABI().EmitThreadLocalInitFuncs(CXXThreadLocals, InitFn); + + CXXThreadLocalInits.clear(); + CXXThreadLocals.clear(); +} + void CodeGenModule::EmitCXXGlobalInitFunc() { while (!CXXGlobalInits.empty() && !CXXGlobalInits.back()) @@ -320,9 +351,7 @@ CodeGenModule::EmitCXXGlobalInitFunc() { for (; I < PrioE; ++I) LocalCXXGlobalInits.push_back(I->second); - CodeGenFunction(*this).GenerateCXXGlobalInitFunc(Fn, - &LocalCXXGlobalInits[0], - LocalCXXGlobalInits.size()); + CodeGenFunction(*this).GenerateCXXGlobalInitFunc(Fn, LocalCXXGlobalInits); AddGlobalCtor(Fn, Priority); } } @@ -330,9 +359,7 @@ CodeGenModule::EmitCXXGlobalInitFunc() { llvm::Function *Fn = CreateGlobalInitOrDestructFunction(*this, FTy, "_GLOBAL__I_a"); - CodeGenFunction(*this).GenerateCXXGlobalInitFunc(Fn, - &CXXGlobalInits[0], - CXXGlobalInits.size()); + CodeGenFunction(*this).GenerateCXXGlobalInitFunc(Fn, CXXGlobalInits); AddGlobalCtor(Fn); CXXGlobalInits.clear(); @@ -379,9 +406,10 @@ void CodeGenFunction::GenerateCXXGlobalVarDeclInitFunc(llvm::Function *Fn, FinishFunction(); } -void CodeGenFunction::GenerateCXXGlobalInitFunc(llvm::Function *Fn, - llvm::Constant **Decls, - unsigned NumDecls) { +void +CodeGenFunction::GenerateCXXGlobalInitFunc(llvm::Function *Fn, + ArrayRef Decls, + llvm::GlobalVariable *Guard) { // Initialize debug info if needed. maybeInitializeDebugInfo(); @@ -389,6 +417,22 @@ void CodeGenFunction::GenerateCXXGlobalInitFunc(llvm::Function *Fn, getTypes().arrangeNullaryFunction(), FunctionArgList(), SourceLocation()); + llvm::BasicBlock *ExitBlock = 0; + if (Guard) { + // If we have a guard variable, check whether we've already performed these + // initializations. This happens for TLS initialization functions. + llvm::Value *GuardVal = Builder.CreateLoad(Guard); + llvm::Value *Uninit = Builder.CreateIsNull(GuardVal, "guard.uninitialized"); + // Mark as initialized before initializing anything else. If the + // initializers use previously-initialized thread_local vars, that's + // probably supposed to be OK, but the standard doesn't say. + Builder.CreateStore(llvm::ConstantInt::get(GuardVal->getType(), 1), Guard); + llvm::BasicBlock *InitBlock = createBasicBlock("init"); + ExitBlock = createBasicBlock("exit"); + Builder.CreateCondBr(Uninit, InitBlock, ExitBlock); + EmitBlock(InitBlock); + } + RunCleanupsScope Scope(*this); // When building in Objective-C++ ARC mode, create an autorelease pool @@ -397,13 +441,18 @@ void CodeGenFunction::GenerateCXXGlobalInitFunc(llvm::Function *Fn, llvm::Value *token = EmitObjCAutoreleasePoolPush(); EmitObjCAutoreleasePoolCleanup(token); } - - for (unsigned i = 0; i != NumDecls; ++i) + + for (unsigned i = 0, e = Decls.size(); i != e; ++i) if (Decls[i]) EmitRuntimeCall(Decls[i]); Scope.ForceCleanup(); - + + if (ExitBlock) { + Builder.CreateBr(ExitBlock); + EmitBlock(ExitBlock); + } + FinishFunction(); } diff --git a/lib/CodeGen/CGException.cpp b/lib/CodeGen/CGException.cpp index 36642bcc48f..a088d78641f 100644 --- a/lib/CodeGen/CGException.cpp +++ b/lib/CodeGen/CGException.cpp @@ -419,14 +419,16 @@ llvm::Value *CodeGenFunction::getSelectorFromSlot() { return Builder.CreateLoad(getEHSelectorSlot(), "sel"); } -void CodeGenFunction::EmitCXXThrowExpr(const CXXThrowExpr *E) { +void CodeGenFunction::EmitCXXThrowExpr(const CXXThrowExpr *E, + bool KeepInsertionPoint) { if (!E->getSubExpr()) { EmitNoreturnRuntimeCallOrInvoke(getReThrowFn(CGM), ArrayRef()); // throw is an expression, and the expression emitters expect us // to leave ourselves at a valid insertion point. - EmitBlock(createBasicBlock("throw.cont")); + if (KeepInsertionPoint) + EmitBlock(createBasicBlock("throw.cont")); return; } @@ -440,7 +442,8 @@ void CodeGenFunction::EmitCXXThrowExpr(const CXXThrowExpr *E) { CGM.getObjCRuntime().EmitThrowStmt(*this, S, false); // This will clear insertion point which was not cleared in // call to EmitThrowStmt. - EmitBlock(createBasicBlock("throw.cont")); + if (KeepInsertionPoint) + EmitBlock(createBasicBlock("throw.cont")); return; } @@ -478,7 +481,8 @@ void CodeGenFunction::EmitCXXThrowExpr(const CXXThrowExpr *E) { // throw is an expression, and the expression emitters expect us // to leave ourselves at a valid insertion point. - EmitBlock(createBasicBlock("throw.cont")); + if (KeepInsertionPoint) + EmitBlock(createBasicBlock("throw.cont")); } void CodeGenFunction::EmitStartEHSpec(const Decl *D) { diff --git a/lib/CodeGen/CGExpr.cpp b/lib/CodeGen/CGExpr.cpp index 2f5186d1f4f..64670c5e81e 100644 --- a/lib/CodeGen/CGExpr.cpp +++ b/lib/CodeGen/CGExpr.cpp @@ -184,12 +184,16 @@ CreateReferenceTemporary(CodeGenFunction &CGF, QualType Type, llvm::Type *RefTempTy = CGF.ConvertTypeForMem(Type); // Create the reference temporary. - llvm::GlobalValue *RefTemp = + llvm::GlobalVariable *RefTemp = new llvm::GlobalVariable(CGF.CGM.getModule(), RefTempTy, /*isConstant=*/false, llvm::GlobalValue::InternalLinkage, llvm::Constant::getNullValue(RefTempTy), Name.str()); + // If we're binding to a thread_local variable, the temporary is also + // thread local. + if (VD->getTLSKind()) + CGF.CGM.setTLSMode(RefTemp, *VD); return RefTemp; } } @@ -201,6 +205,7 @@ static llvm::Value * EmitExprForReferenceBinding(CodeGenFunction &CGF, const Expr *E, llvm::Value *&ReferenceTemporary, const CXXDestructorDecl *&ReferenceTemporaryDtor, + const InitListExpr *&ReferenceInitializerList, QualType &ObjCARCReferenceLifetimeType, const NamedDecl *InitializedDecl) { const MaterializeTemporaryExpr *M = NULL; @@ -222,156 +227,157 @@ EmitExprForReferenceBinding(CodeGenFunction &CGF, const Expr *E, return EmitExprForReferenceBinding(CGF, EWC->getSubExpr(), ReferenceTemporary, ReferenceTemporaryDtor, + ReferenceInitializerList, ObjCARCReferenceLifetimeType, InitializedDecl); } - RValue RV; if (E->isGLValue()) { // Emit the expression as an lvalue. LValue LV = CGF.EmitLValue(E); + assert(LV.isSimple()); + return LV.getAddress(); + } + + if (!ObjCARCReferenceLifetimeType.isNull()) { + ReferenceTemporary = CreateReferenceTemporary(CGF, + ObjCARCReferenceLifetimeType, + InitializedDecl); - if (LV.isSimple()) - return LV.getAddress(); - // We have to load the lvalue. - RV = CGF.EmitLoadOfLValue(LV); - } else { - if (!ObjCARCReferenceLifetimeType.isNull()) { - ReferenceTemporary = CreateReferenceTemporary(CGF, - ObjCARCReferenceLifetimeType, - InitializedDecl); - - - LValue RefTempDst = CGF.MakeAddrLValue(ReferenceTemporary, - ObjCARCReferenceLifetimeType); + LValue RefTempDst = CGF.MakeAddrLValue(ReferenceTemporary, + ObjCARCReferenceLifetimeType); - CGF.EmitScalarInit(E, dyn_cast_or_null(InitializedDecl), - RefTempDst, false); - - bool ExtendsLifeOfTemporary = false; - if (const VarDecl *Var = dyn_cast_or_null(InitializedDecl)) { - if (Var->extendsLifetimeOfTemporary()) - ExtendsLifeOfTemporary = true; - } else if (InitializedDecl && isa(InitializedDecl)) { + CGF.EmitScalarInit(E, dyn_cast_or_null(InitializedDecl), + RefTempDst, false); + + bool ExtendsLifeOfTemporary = false; + if (const VarDecl *Var = dyn_cast_or_null(InitializedDecl)) { + if (Var->extendsLifetimeOfTemporary()) ExtendsLifeOfTemporary = true; - } - - if (!ExtendsLifeOfTemporary) { - // Since the lifetime of this temporary isn't going to be extended, - // we need to clean it up ourselves at the end of the full expression. - switch (ObjCARCReferenceLifetimeType.getObjCLifetime()) { - case Qualifiers::OCL_None: - case Qualifiers::OCL_ExplicitNone: - case Qualifiers::OCL_Autoreleasing: - break; - - case Qualifiers::OCL_Strong: { - assert(!ObjCARCReferenceLifetimeType->isArrayType()); - CleanupKind cleanupKind = CGF.getARCCleanupKind(); - CGF.pushDestroy(cleanupKind, - ReferenceTemporary, - ObjCARCReferenceLifetimeType, - CodeGenFunction::destroyARCStrongImprecise, - cleanupKind & EHCleanup); - break; - } + } else if (InitializedDecl && isa(InitializedDecl)) { + ExtendsLifeOfTemporary = true; + } + + if (!ExtendsLifeOfTemporary) { + // Since the lifetime of this temporary isn't going to be extended, + // we need to clean it up ourselves at the end of the full expression. + switch (ObjCARCReferenceLifetimeType.getObjCLifetime()) { + case Qualifiers::OCL_None: + case Qualifiers::OCL_ExplicitNone: + case Qualifiers::OCL_Autoreleasing: + break; - case Qualifiers::OCL_Weak: - assert(!ObjCARCReferenceLifetimeType->isArrayType()); - CGF.pushDestroy(NormalAndEHCleanup, - ReferenceTemporary, - ObjCARCReferenceLifetimeType, - CodeGenFunction::destroyARCWeak, - /*useEHCleanupForArray*/ true); - break; - } + case Qualifiers::OCL_Strong: { + assert(!ObjCARCReferenceLifetimeType->isArrayType()); + CleanupKind cleanupKind = CGF.getARCCleanupKind(); + CGF.pushDestroy(cleanupKind, + ReferenceTemporary, + ObjCARCReferenceLifetimeType, + CodeGenFunction::destroyARCStrongImprecise, + cleanupKind & EHCleanup); + break; + } - ObjCARCReferenceLifetimeType = QualType(); + case Qualifiers::OCL_Weak: + assert(!ObjCARCReferenceLifetimeType->isArrayType()); + CGF.pushDestroy(NormalAndEHCleanup, + ReferenceTemporary, + ObjCARCReferenceLifetimeType, + CodeGenFunction::destroyARCWeak, + /*useEHCleanupForArray*/ true); + break; } - return ReferenceTemporary; + ObjCARCReferenceLifetimeType = QualType(); } + + return ReferenceTemporary; + } + + SmallVector Adjustments; + E = E->skipRValueSubobjectAdjustments(Adjustments); + if (const OpaqueValueExpr *opaque = dyn_cast(E)) + if (opaque->getType()->isRecordType()) + return CGF.EmitOpaqueValueLValue(opaque).getAddress(); - SmallVector Adjustments; - E = E->skipRValueSubobjectAdjustments(Adjustments); - if (const OpaqueValueExpr *opaque = dyn_cast(E)) - if (opaque->getType()->isRecordType()) - return CGF.EmitOpaqueValueLValue(opaque).getAddress(); - - // Create a reference temporary if necessary. - AggValueSlot AggSlot = AggValueSlot::ignored(); - if (CGF.hasAggregateEvaluationKind(E->getType())) { - ReferenceTemporary = CreateReferenceTemporary(CGF, E->getType(), - InitializedDecl); - CharUnits Alignment = CGF.getContext().getTypeAlignInChars(E->getType()); - AggValueSlot::IsDestructed_t isDestructed - = AggValueSlot::IsDestructed_t(InitializedDecl != 0); - AggSlot = AggValueSlot::forAddr(ReferenceTemporary, Alignment, - Qualifiers(), isDestructed, - AggValueSlot::DoesNotNeedGCBarriers, - AggValueSlot::IsNotAliased); + // Create a reference temporary if necessary. + AggValueSlot AggSlot = AggValueSlot::ignored(); + if (CGF.hasAggregateEvaluationKind(E->getType())) { + ReferenceTemporary = CreateReferenceTemporary(CGF, E->getType(), + InitializedDecl); + CharUnits Alignment = CGF.getContext().getTypeAlignInChars(E->getType()); + AggValueSlot::IsDestructed_t isDestructed + = AggValueSlot::IsDestructed_t(InitializedDecl != 0); + AggSlot = AggValueSlot::forAddr(ReferenceTemporary, Alignment, + Qualifiers(), isDestructed, + AggValueSlot::DoesNotNeedGCBarriers, + AggValueSlot::IsNotAliased); + } + + if (InitializedDecl) { + if (const InitListExpr *ILE = dyn_cast(E)) { + if (ILE->initializesStdInitializerList()) { + ReferenceInitializerList = ILE; + } } - - if (InitializedDecl) { + else if (const RecordType *RT = + E->getType()->getBaseElementTypeUnsafe()->getAs()){ // Get the destructor for the reference temporary. - if (const RecordType *RT = - E->getType()->getBaseElementTypeUnsafe()->getAs()) { - CXXRecordDecl *ClassDecl = cast(RT->getDecl()); - if (!ClassDecl->hasTrivialDestructor()) - ReferenceTemporaryDtor = ClassDecl->getDestructor(); - } + CXXRecordDecl *ClassDecl = cast(RT->getDecl()); + if (!ClassDecl->hasTrivialDestructor()) + ReferenceTemporaryDtor = ClassDecl->getDestructor(); } + } - RV = CGF.EmitAnyExpr(E, AggSlot); - - // Check if need to perform derived-to-base casts and/or field accesses, to - // get from the temporary object we created (and, potentially, for which we - // extended the lifetime) to the subobject we're binding the reference to. - if (!Adjustments.empty()) { - llvm::Value *Object = RV.getAggregateAddr(); - for (unsigned I = Adjustments.size(); I != 0; --I) { - SubobjectAdjustment &Adjustment = Adjustments[I-1]; - switch (Adjustment.Kind) { - case SubobjectAdjustment::DerivedToBaseAdjustment: - Object = - CGF.GetAddressOfBaseClass(Object, - Adjustment.DerivedToBase.DerivedClass, - Adjustment.DerivedToBase.BasePath->path_begin(), - Adjustment.DerivedToBase.BasePath->path_end(), - /*NullCheckValue=*/false); - break; - - case SubobjectAdjustment::FieldAdjustment: { - LValue LV = CGF.MakeAddrLValue(Object, E->getType()); - LV = CGF.EmitLValueForField(LV, Adjustment.Field); - if (LV.isSimple()) { - Object = LV.getAddress(); - break; - } + RValue RV = CGF.EmitAnyExpr(E, AggSlot); + + // Check if need to perform derived-to-base casts and/or field accesses, to + // get from the temporary object we created (and, potentially, for which we + // extended the lifetime) to the subobject we're binding the reference to. + if (!Adjustments.empty()) { + llvm::Value *Object = RV.getAggregateAddr(); + for (unsigned I = Adjustments.size(); I != 0; --I) { + SubobjectAdjustment &Adjustment = Adjustments[I-1]; + switch (Adjustment.Kind) { + case SubobjectAdjustment::DerivedToBaseAdjustment: + Object = + CGF.GetAddressOfBaseClass(Object, + Adjustment.DerivedToBase.DerivedClass, + Adjustment.DerivedToBase.BasePath->path_begin(), + Adjustment.DerivedToBase.BasePath->path_end(), + /*NullCheckValue=*/false); + break; - // For non-simple lvalues, we actually have to create a copy of - // the object we're binding to. - QualType T = Adjustment.Field->getType().getNonReferenceType() - .getUnqualifiedType(); - Object = CreateReferenceTemporary(CGF, T, InitializedDecl); - LValue TempLV = CGF.MakeAddrLValue(Object, - Adjustment.Field->getType()); - CGF.EmitStoreThroughLValue(CGF.EmitLoadOfLValue(LV), TempLV); + case SubobjectAdjustment::FieldAdjustment: { + LValue LV = CGF.MakeAddrLValue(Object, E->getType()); + LV = CGF.EmitLValueForField(LV, Adjustment.Field); + if (LV.isSimple()) { + Object = LV.getAddress(); break; } - - case SubobjectAdjustment::MemberPointerAdjustment: { - llvm::Value *Ptr = CGF.EmitScalarExpr(Adjustment.Ptr.RHS); - Object = CGF.CGM.getCXXABI().EmitMemberDataPointerAddress( - CGF, Object, Ptr, Adjustment.Ptr.MPT); - break; - } - } + + // For non-simple lvalues, we actually have to create a copy of + // the object we're binding to. + QualType T = Adjustment.Field->getType().getNonReferenceType() + .getUnqualifiedType(); + Object = CreateReferenceTemporary(CGF, T, InitializedDecl); + LValue TempLV = CGF.MakeAddrLValue(Object, + Adjustment.Field->getType()); + CGF.EmitStoreThroughLValue(CGF.EmitLoadOfLValue(LV), TempLV); + break; } - return Object; + case SubobjectAdjustment::MemberPointerAdjustment: { + llvm::Value *Ptr = CGF.EmitScalarExpr(Adjustment.Ptr.RHS); + Object = CGF.CGM.getCXXABI().EmitMemberDataPointerAddress( + CGF, Object, Ptr, Adjustment.Ptr.MPT); + break; + } + } } + + return Object; } if (RV.isAggregate()) @@ -396,9 +402,11 @@ CodeGenFunction::EmitReferenceBindingToExpr(const Expr *E, const NamedDecl *InitializedDecl) { llvm::Value *ReferenceTemporary = 0; const CXXDestructorDecl *ReferenceTemporaryDtor = 0; + const InitListExpr *ReferenceInitializerList = 0; QualType ObjCARCReferenceLifetimeType; llvm::Value *Value = EmitExprForReferenceBinding(*this, E, ReferenceTemporary, ReferenceTemporaryDtor, + ReferenceInitializerList, ObjCARCReferenceLifetimeType, InitializedDecl); if (SanitizePerformTypeCheck && !E->getType()->isFunctionType()) { @@ -410,7 +418,8 @@ CodeGenFunction::EmitReferenceBindingToExpr(const Expr *E, QualType Ty = E->getType(); EmitTypeCheck(TCK_ReferenceBinding, E->getExprLoc(), Value, Ty); } - if (!ReferenceTemporaryDtor && ObjCARCReferenceLifetimeType.isNull()) + if (!ReferenceTemporaryDtor && !ReferenceInitializerList && + ObjCARCReferenceLifetimeType.isNull()) return RValue::get(Value); // Make sure to call the destructor for the reference temporary. @@ -429,9 +438,15 @@ CodeGenFunction::EmitReferenceBindingToExpr(const Expr *E, CGM.GetAddrOfCXXDestructor(ReferenceTemporaryDtor, Dtor_Complete); CleanupArg = cast(ReferenceTemporary); } - CGM.getCXXABI().registerGlobalDtor(*this, CleanupFn, CleanupArg); + CGM.getCXXABI().registerGlobalDtor(*this, *VD, CleanupFn, CleanupArg); + } else if (ReferenceInitializerList) { + // FIXME: This is wrong. We need to register a global destructor to clean + // up the initializer_list object, rather than adding it as a local + // cleanup. + EmitStdInitializerListCleanup(ReferenceTemporary, + ReferenceInitializerList); } else { - assert(!ObjCARCReferenceLifetimeType.isNull()); + assert(!ObjCARCReferenceLifetimeType.isNull() && !VD->getTLSKind()); // Note: We intentionally do not register a global "destructor" to // release the object. } @@ -445,6 +460,9 @@ CodeGenFunction::EmitReferenceBindingToExpr(const Expr *E, destroyCXXObject, getLangOpts().Exceptions); else PushDestructorCleanup(ReferenceTemporaryDtor, ReferenceTemporary); + } else if (ReferenceInitializerList) { + EmitStdInitializerListCleanup(ReferenceTemporary, + ReferenceInitializerList); } else { switch (ObjCARCReferenceLifetimeType.getObjCLifetime()) { case Qualifiers::OCL_None: @@ -885,6 +903,10 @@ LValue CodeGenFunction::EmitLValue(const Expr *E) { return EmitNullInitializationLValue(cast(E)); case Expr::CXXDefaultArgExprClass: return EmitLValue(cast(E)->getExpr()); + case Expr::CXXDefaultInitExprClass: { + CXXDefaultInitExprScope Scope(*this); + return EmitLValue(cast(E)->getExpr()); + } case Expr::CXXTypeidExprClass: return EmitCXXTypeidLValue(cast(E)); @@ -1164,7 +1186,7 @@ llvm::Value *CodeGenFunction::EmitLoadOfScalar(llvm::Value *Addr, bool Volatile, if (TBAAInfo) { llvm::MDNode *TBAAPath = CGM.getTBAAStructTagInfo(TBAABaseType, TBAAInfo, TBAAOffset); - CGM.DecorateInstruction(Load, TBAAPath); + CGM.DecorateInstruction(Load, TBAAPath, false/*ConvertTypeToTag*/); } if ((SanOpts->Bool && hasBooleanRepresentation(Ty)) || @@ -1278,7 +1300,7 @@ void CodeGenFunction::EmitStoreOfScalar(llvm::Value *Value, llvm::Value *Addr, if (TBAAInfo) { llvm::MDNode *TBAAPath = CGM.getTBAAStructTagInfo(TBAABaseType, TBAAInfo, TBAAOffset); - CGM.DecorateInstruction(Store, TBAAPath); + CGM.DecorateInstruction(Store, TBAAPath, false/*ConvertTypeToTag*/); } } @@ -1656,7 +1678,7 @@ static void setObjCGCLValueClass(const ASTContext &Ctx, const Expr *E, if (const VarDecl *VD = dyn_cast(Exp->getDecl())) { if (VD->hasGlobalStorage()) { LV.setGlobalObjCRef(true); - LV.setThreadLocalRef(VD->isThreadSpecified()); + LV.setThreadLocalRef(VD->getTLSKind() != VarDecl::TLS_None); } } LV.setObjCArray(E->getType()->isArrayType()); @@ -1806,8 +1828,12 @@ LValue CodeGenFunction::EmitDeclRefLValue(const DeclRefExpr *E) { if (const VarDecl *VD = dyn_cast(ND)) { // Check if this is a global variable. - if (VD->hasLinkage() || VD->isStaticDataMember()) + if (VD->hasLinkage() || VD->isStaticDataMember()) { + // If it's thread_local, emit a call to its wrapper function instead. + if (VD->getTLSKind() == VarDecl::TLS_Dynamic) + return CGM.getCXXABI().EmitThreadLocalDeclRefExpr(*this, E); return EmitGlobalVarDeclLValue(*this, E, VD); + } bool isBlockVariable = VD->hasAttr(); @@ -2469,6 +2495,17 @@ LValue CodeGenFunction::EmitMemberExpr(const MemberExpr *E) { llvm_unreachable("Unhandled member declaration!"); } +/// Given that we are currently emitting a lambda, emit an l-value for +/// one of its members. +LValue CodeGenFunction::EmitLValueForLambdaField(const FieldDecl *Field) { + assert(cast(CurCodeDecl)->getParent()->isLambda()); + assert(cast(CurCodeDecl)->getParent() == Field->getParent()); + QualType LambdaTagType = + getContext().getTagDeclType(Field->getParent()); + LValue LambdaLV = MakeNaturalAlignAddrLValue(CXXABIThisValue, LambdaTagType); + return EmitLValueForField(LambdaLV, Field); +} + LValue CodeGenFunction::EmitLValueForField(LValue base, const FieldDecl *field) { if (field->isBitField()) { @@ -2562,8 +2599,8 @@ LValue CodeGenFunction::EmitLValueForField(LValue base, getContext().getASTRecordLayout(field->getParent()); // Set the base type to be the base type of the base LValue and // update offset to be relative to the base type. - LV.setTBAABaseType(base.getTBAABaseType()); - LV.setTBAAOffset(base.getTBAAOffset() + + LV.setTBAABaseType(mayAlias ? getContext().CharTy : base.getTBAABaseType()); + LV.setTBAAOffset(mayAlias ? 0 : base.getTBAAOffset() + Layout.getFieldOffset(field->getFieldIndex()) / getContext().getCharWidth()); } diff --git a/lib/CodeGen/CGExprAgg.cpp b/lib/CodeGen/CGExprAgg.cpp index 1ac13c01ed4..b974e1dcc68 100644 --- a/lib/CodeGen/CGExprAgg.cpp +++ b/lib/CodeGen/CGExprAgg.cpp @@ -170,6 +170,10 @@ class AggExprEmitter : public StmtVisitor { void VisitCXXDefaultArgExpr(CXXDefaultArgExpr *DAE) { Visit(DAE->getExpr()); } + void VisitCXXDefaultInitExpr(CXXDefaultInitExpr *DIE) { + CodeGenFunction::CXXDefaultInitExprScope Scope(CGF); + Visit(DIE->getExpr()); + } void VisitCXXBindTemporaryExpr(CXXBindTemporaryExpr *E); void VisitCXXConstructExpr(const CXXConstructExpr *E); void VisitLambdaExpr(LambdaExpr *E); @@ -1189,7 +1193,10 @@ void AggExprEmitter::VisitInitListExpr(InitListExpr *E) { // the optimizer, especially with bitfields. unsigned NumInitElements = E->getNumInits(); RecordDecl *record = E->getType()->castAs()->getDecl(); - + + // Prepare a 'this' for CXXDefaultInitExprs. + CodeGenFunction::FieldConstructionScope FCS(CGF, Dest.getAddr()); + if (record->isUnion()) { // Only initialize one field of a union. The field itself is // specified by the initializer list. @@ -1341,7 +1348,7 @@ static CharUnits GetNumNonZeroBytesInInit(const Expr *E, CodeGenFunction &CGF) { // Reference values are always non-null and have the width of a pointer. if (Field->getType()->isReferenceType()) NumNonZeroBytes += CGF.getContext().toCharUnitsFromBits( - CGF.getContext().getTargetInfo().getPointerWidth(0)); + CGF.getTarget().getPointerWidth(0)); else NumNonZeroBytes += GetNumNonZeroBytesInInit(E, CGF); } diff --git a/lib/CodeGen/CGExprComplex.cpp b/lib/CodeGen/CGExprComplex.cpp index 5fc73aa7901..36f974a3132 100644 --- a/lib/CodeGen/CGExprComplex.cpp +++ b/lib/CodeGen/CGExprComplex.cpp @@ -182,6 +182,10 @@ class ComplexExprEmitter ComplexPairTy VisitCXXDefaultArgExpr(CXXDefaultArgExpr *DAE) { return Visit(DAE->getExpr()); } + ComplexPairTy VisitCXXDefaultInitExpr(CXXDefaultInitExpr *DIE) { + CodeGenFunction::CXXDefaultInitExprScope Scope(CGF); + return Visit(DIE->getExpr()); + } ComplexPairTy VisitExprWithCleanups(ExprWithCleanups *E) { CGF.enterFullExpression(E); CodeGenFunction::RunCleanupsScope Scope(CGF); diff --git a/lib/CodeGen/CGExprConstant.cpp b/lib/CodeGen/CGExprConstant.cpp index faaf6468f1e..f5c8187c26f 100644 --- a/lib/CodeGen/CGExprConstant.cpp +++ b/lib/CodeGen/CGExprConstant.cpp @@ -757,6 +757,12 @@ class ConstExprEmitter : return Visit(DAE->getExpr()); } + llvm::Constant *VisitCXXDefaultInitExpr(CXXDefaultInitExpr *DIE) { + // No need for a DefaultInitExprScope: we don't handle 'this' in a + // constant expression. + return Visit(DIE->getExpr()); + } + llvm::Constant *VisitMaterializeTemporaryExpr(MaterializeTemporaryExpr *E) { return Visit(E->GetTemporaryExpr()); } diff --git a/lib/CodeGen/CGExprScalar.cpp b/lib/CodeGen/CGExprScalar.cpp index ffd0eb5572d..c1c252d12b7 100644 --- a/lib/CodeGen/CGExprScalar.cpp +++ b/lib/CodeGen/CGExprScalar.cpp @@ -344,6 +344,10 @@ class ScalarExprEmitter Value *VisitCXXDefaultArgExpr(CXXDefaultArgExpr *DAE) { return Visit(DAE->getExpr()); } + Value *VisitCXXDefaultInitExpr(CXXDefaultInitExpr *DIE) { + CodeGenFunction::CXXDefaultInitExprScope Scope(CGF); + return Visit(DIE->getExpr()); + } Value *VisitCXXThisExpr(CXXThisExpr *TE) { return CGF.LoadCXXThis(); } @@ -1634,8 +1638,8 @@ ScalarExprEmitter::EmitScalarPrePostIncDec(const UnaryOperator *E, LValue LV, else { llvm::APFloat F(static_cast(amount)); bool ignored; - F.convert(CGF.Target.getLongDoubleFormat(), llvm::APFloat::rmTowardZero, - &ignored); + F.convert(CGF.getTarget().getLongDoubleFormat(), + llvm::APFloat::rmTowardZero, &ignored); amt = llvm::ConstantFP::get(VMContext, F); } value = Builder.CreateFAdd(value, amt, isInc ? "inc" : "dec"); diff --git a/lib/CodeGen/CGObjC.cpp b/lib/CodeGen/CGObjC.cpp index 79d97b99b40..713509bf673 100644 --- a/lib/CodeGen/CGObjC.cpp +++ b/lib/CodeGen/CGObjC.cpp @@ -21,6 +21,7 @@ #include "clang/AST/StmtObjC.h" #include "clang/Basic/Diagnostic.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/Support/CallSite.h" #include "llvm/IR/DataLayout.h" #include "llvm/IR/InlineAsm.h" using namespace clang; @@ -713,7 +714,7 @@ PropertyImplStrategy::PropertyImplStrategy(CodeGenModule &CGM, } llvm::Triple::ArchType arch = - CGM.getContext().getTargetInfo().getTriple().getArch(); + CGM.getTarget().getTriple().getArch(); // Most architectures require memory to fit within a single cache // line, so the alignment has to be at least the size of the access. @@ -1400,8 +1401,10 @@ bool CodeGenFunction::IvarTypeWithAggrGCObjects(QualType Ty) { } llvm::Value *CodeGenFunction::LoadObjCSelf() { - const ObjCMethodDecl *OMD = cast(CurFuncDecl); - return Builder.CreateLoad(LocalDeclMap[OMD->getSelfDecl()], "self"); + VarDecl *Self = cast(CurFuncDecl)->getSelfDecl(); + DeclRefExpr DRE(Self, /*is enclosing local*/ (CurFuncDecl != CurCodeDecl), + Self->getType(), VK_LValue, SourceLocation()); + return EmitLoadOfScalar(EmitDeclRefLValue(&DRE)); } QualType CodeGenFunction::TypeOfSelfObject() { @@ -2256,7 +2259,8 @@ void CodeGenFunction::EmitObjCAutoreleasePoolPop(llvm::Value *value) { fn = createARCRuntimeFunction(CGM, fnType, "objc_autoreleasePoolPop"); } - EmitNounwindRuntimeCall(fn, value); + // objc_autoreleasePoolPop can throw. + EmitRuntimeCallOrInvoke(fn, value); } /// Produce the code to do an MRR version objc_autoreleasepool_push. diff --git a/lib/CodeGen/CGObjCMac.cpp b/lib/CodeGen/CGObjCMac.cpp index 6274e1bfe39..e8498b06ad2 100644 --- a/lib/CodeGen/CGObjCMac.cpp +++ b/lib/CodeGen/CGObjCMac.cpp @@ -1949,8 +1949,8 @@ llvm::Constant *CGObjCCommonMac::BuildGCBlockLayout(CodeGenModule &CGM, bool hasUnion = false; SkipIvars.clear(); IvarsInfo.clear(); - unsigned WordSizeInBits = CGM.getContext().getTargetInfo().getPointerWidth(0); - unsigned ByteSizeInBits = CGM.getContext().getTargetInfo().getCharWidth(); + unsigned WordSizeInBits = CGM.getTarget().getPointerWidth(0); + unsigned ByteSizeInBits = CGM.getTarget().getCharWidth(); // __isa is the first field in block descriptor and must assume by runtime's // convention that it is GC'able. @@ -2077,7 +2077,7 @@ void CGObjCCommonMac::BuildRCRecordLayout(const llvm::StructLayout *RecLayout, if (RecFields.empty()) return; - unsigned ByteSizeInBits = CGM.getContext().getTargetInfo().getCharWidth(); + unsigned ByteSizeInBits = CGM.getTarget().getCharWidth(); for (unsigned i = 0, e = RecFields.size(); i != e; ++i) { const FieldDecl *Field = RecFields[i]; @@ -2316,8 +2316,8 @@ llvm::Constant *CGObjCCommonMac::getBitmapBlockLayout(bool ComputeByrefLayout) { llvm::Constant *nullPtr = llvm::Constant::getNullValue(CGM.Int8PtrTy); if (RunSkipBlockVars.empty()) return nullPtr; - unsigned WordSizeInBits = CGM.getContext().getTargetInfo().getPointerWidth(0); - unsigned ByteSizeInBits = CGM.getContext().getTargetInfo().getCharWidth(); + unsigned WordSizeInBits = CGM.getTarget().getPointerWidth(0); + unsigned ByteSizeInBits = CGM.getTarget().getCharWidth(); unsigned WordSizeInBytes = WordSizeInBits/ByteSizeInBits; // Sort on byte position; captures might not be allocated in order, @@ -2393,7 +2393,7 @@ llvm::Constant *CGObjCCommonMac::getBitmapBlockLayout(bool ComputeByrefLayout) { printf("\n Inline instruction for BYREF variable layout: "); else printf("\n Inline instruction for block variable layout: "); - printf("0x0%llx\n", (unsigned long long)Result); + printf("0x0%" PRIx64 "\n", Result); } if (WordSizeInBytes == 8) { const llvm::APInt Instruction(64, Result); @@ -2468,8 +2468,8 @@ llvm::Constant *CGObjCCommonMac::BuildRCBlockLayout(CodeGenModule &CGM, RunSkipBlockVars.clear(); bool hasUnion = false; - unsigned WordSizeInBits = CGM.getContext().getTargetInfo().getPointerWidth(0); - unsigned ByteSizeInBits = CGM.getContext().getTargetInfo().getCharWidth(); + unsigned WordSizeInBits = CGM.getTarget().getPointerWidth(0); + unsigned ByteSizeInBits = CGM.getTarget().getCharWidth(); unsigned WordSizeInBytes = WordSizeInBits/ByteSizeInBits; const BlockDecl *blockDecl = blockInfo.getBlockDecl(); @@ -4537,8 +4537,8 @@ void CGObjCCommonMac::BuildAggrIvarLayout(const ObjCImplementationDecl *OI, if (RecFields.empty()) return; - unsigned WordSizeInBits = CGM.getContext().getTargetInfo().getPointerWidth(0); - unsigned ByteSizeInBits = CGM.getContext().getTargetInfo().getCharWidth(); + unsigned WordSizeInBits = CGM.getTarget().getPointerWidth(0); + unsigned ByteSizeInBits = CGM.getTarget().getCharWidth(); if (!RD && CGM.getLangOpts().ObjCAutoRefCount) { const FieldDecl *FirstField = RecFields[0]; FirstFieldDelta = diff --git a/lib/CodeGen/CGObjCRuntime.cpp b/lib/CodeGen/CGObjCRuntime.cpp index abd10a29c9e..9c0d5189f81 100644 --- a/lib/CodeGen/CGObjCRuntime.cpp +++ b/lib/CodeGen/CGObjCRuntime.cpp @@ -117,7 +117,7 @@ LValue CGObjCRuntime::EmitValueForIvarAtOffset(CodeGen::CodeGenFunction &CGF, // a synthesized ivar can never be a bit-field, so this is safe. uint64_t FieldBitOffset = LookupFieldBitOffset(CGF.CGM, OID, 0, Ivar); uint64_t BitOffset = FieldBitOffset % CGF.CGM.getContext().getCharWidth(); - uint64_t AlignmentBits = CGF.CGM.getContext().getTargetInfo().getCharAlign(); + uint64_t AlignmentBits = CGF.CGM.getTarget().getCharAlign(); uint64_t BitFieldSize = Ivar->getBitWidthValue(CGF.getContext()); CharUnits StorageSize = CGF.CGM.getContext().toCharUnitsFromBits( diff --git a/lib/CodeGen/CGRTTI.cpp b/lib/CodeGen/CGRTTI.cpp index 869843cbd4e..40dc6bfa3b0 100644 --- a/lib/CodeGen/CGRTTI.cpp +++ b/lib/CodeGen/CGRTTI.cpp @@ -412,6 +412,9 @@ void RTTIBuilder::BuildVTablePointer(const Type *Ty) { case Type::RValueReference: llvm_unreachable("References shouldn't get here"); + case Type::Auto: + llvm_unreachable("Undeduced auto type shouldn't get here"); + case Type::Builtin: // GCC treats vector and complex types as fundamental types. case Type::Vector: @@ -619,6 +622,9 @@ llvm::Constant *RTTIBuilder::BuildTypeInfo(QualType Ty, bool Force) { case Type::RValueReference: llvm_unreachable("References shouldn't get here"); + case Type::Auto: + llvm_unreachable("Undeduced auto type shouldn't get here"); + case Type::ConstantArray: case Type::IncompleteArray: case Type::VariableArray: diff --git a/lib/CodeGen/CGRecordLayoutBuilder.cpp b/lib/CodeGen/CGRecordLayoutBuilder.cpp index 2c6438b0b67..30ab528ffbe 100644 --- a/lib/CodeGen/CGRecordLayoutBuilder.cpp +++ b/lib/CodeGen/CGRecordLayoutBuilder.cpp @@ -276,7 +276,7 @@ bool CGRecordLayoutBuilder::LayoutBitfields(const ASTRecordLayout &Layout, uint64_t FirstFieldOffset = Layout.getFieldOffset(FirstFieldNo); uint64_t NextFieldOffsetInBits = Types.getContext().toBits(NextFieldOffset); - unsigned CharAlign = Types.getContext().getTargetInfo().getCharAlign(); + unsigned CharAlign = Types.getTarget().getCharAlign(); assert(FirstFieldOffset % CharAlign == 0 && "First field offset is misaligned"); CharUnits FirstFieldOffsetInBytes @@ -352,7 +352,7 @@ bool CGRecordLayoutBuilder::LayoutBitfields(const ASTRecordLayout &Layout, assert(EndOffset >= (FirstFieldOffset + TotalBits) && "End offset is not past the end of the known storage bits."); uint64_t SpaceBits = EndOffset - FirstFieldOffset; - uint64_t LongBits = Types.getContext().getTargetInfo().getLongWidth(); + uint64_t LongBits = Types.getTarget().getLongWidth(); uint64_t WidenedBits = (StorageBits / LongBits) * LongBits + llvm::NextPowerOf2(StorageBits % LongBits - 1); assert(WidenedBits >= StorageBits && "Widening shrunk the bits!"); @@ -455,7 +455,7 @@ CGRecordLayoutBuilder::LayoutUnionField(const FieldDecl *Field, return 0; unsigned StorageBits = llvm::RoundUpToAlignment( - FieldSize, Types.getContext().getTargetInfo().getCharAlign()); + FieldSize, Types.getTarget().getCharAlign()); CharUnits NumBytesToAppend = Types.getContext().toCharUnitsFromBits(StorageBits); @@ -814,7 +814,7 @@ bool CGRecordLayoutBuilder::LayoutFields(const RecordDecl *D) { // Lay out the virtual bases. The MS ABI uses a different // algorithm here due to the lack of primary virtual bases. - if (Types.getContext().getTargetInfo().getCXXABI().hasPrimaryVBases()) { + if (Types.getTarget().getCXXABI().hasPrimaryVBases()) { RD->getIndirectPrimaryBases(IndirectPrimaryBases); if (Layout.isPrimaryBaseVirtual()) IndirectPrimaryBases.insert(Layout.getPrimaryBase()); diff --git a/lib/CodeGen/CGStmt.cpp b/lib/CodeGen/CGStmt.cpp index 3153ca8ca70..5e2ebe0d9cd 100644 --- a/lib/CodeGen/CGStmt.cpp +++ b/lib/CodeGen/CGStmt.cpp @@ -37,6 +37,9 @@ void CodeGenFunction::EmitStopPoint(const Stmt *S) { else Loc = S->getLocStart(); DI->EmitLocation(Builder, Loc); + + //if (++NumStopPoints == 1) + LastStopPoint = Loc; } } @@ -134,7 +137,9 @@ void CodeGenFunction::EmitStmt(const Stmt *S) { case Stmt::SwitchStmtClass: EmitSwitchStmt(cast(*S)); break; case Stmt::GCCAsmStmtClass: // Intentional fall-through. case Stmt::MSAsmStmtClass: EmitAsmStmt(cast(*S)); break; - + case Stmt::CapturedStmtClass: + EmitCapturedStmt(cast(*S)); + break; case Stmt::ObjCAtTryStmtClass: EmitObjCAtTryStmt(cast(*S)); break; @@ -837,6 +842,10 @@ void CodeGenFunction::EmitReturnStmt(const ReturnStmt &S) { } } + NumReturnExprs += 1; + if (RV == 0 || RV->isEvaluatable(getContext())) + NumSimpleReturnExprs += 1; + cleanupScope.ForceCleanup(); EmitBranchThroughCleanup(ReturnBlock); } @@ -1449,7 +1458,7 @@ static llvm::MDNode *getAsmSrcLocInfo(const StringLiteral *Str, for (unsigned i = 0, e = StrVal.size()-1; i != e; ++i) { if (StrVal[i] != '\n') continue; SourceLocation LineLoc = Str->getLocationOfByte(i+1, SM, LangOpts, - CGF.Target); + CGF.getTarget()); Locs.push_back(llvm::ConstantInt::get(CGF.Int32Ty, LineLoc.getRawEncoding())); } @@ -1467,18 +1476,23 @@ void CodeGenFunction::EmitAsmStmt(const AsmStmt &S) { SmallVector InputConstraintInfos; for (unsigned i = 0, e = S.getNumOutputs(); i != e; i++) { - TargetInfo::ConstraintInfo Info(S.getOutputConstraint(i), - S.getOutputName(i)); - bool IsValid = Target.validateOutputConstraint(Info); (void)IsValid; + StringRef Name; + if (const GCCAsmStmt *GAS = dyn_cast(&S)) + Name = GAS->getOutputName(i); + TargetInfo::ConstraintInfo Info(S.getOutputConstraint(i), Name); + bool IsValid = getTarget().validateOutputConstraint(Info); (void)IsValid; assert(IsValid && "Failed to parse output constraint"); OutputConstraintInfos.push_back(Info); } for (unsigned i = 0, e = S.getNumInputs(); i != e; i++) { - TargetInfo::ConstraintInfo Info(S.getInputConstraint(i), - S.getInputName(i)); - bool IsValid = Target.validateInputConstraint(OutputConstraintInfos.data(), - S.getNumOutputs(), Info); + StringRef Name; + if (const GCCAsmStmt *GAS = dyn_cast(&S)) + Name = GAS->getInputName(i); + TargetInfo::ConstraintInfo Info(S.getInputConstraint(i), Name); + bool IsValid = + getTarget().validateInputConstraint(OutputConstraintInfos.data(), + S.getNumOutputs(), Info); assert(IsValid && "Failed to parse input constraint"); (void)IsValid; InputConstraintInfos.push_back(Info); } @@ -1502,13 +1516,14 @@ void CodeGenFunction::EmitAsmStmt(const AsmStmt &S) { // Simplify the output constraint. std::string OutputConstraint(S.getOutputConstraint(i)); - OutputConstraint = SimplifyConstraint(OutputConstraint.c_str() + 1, Target); + OutputConstraint = SimplifyConstraint(OutputConstraint.c_str() + 1, + getTarget()); const Expr *OutExpr = S.getOutputExpr(i); OutExpr = OutExpr->IgnoreParenNoopCasts(getContext()); OutputConstraint = AddVariableConstraints(OutputConstraint, *OutExpr, - Target, CGM, S); + getTarget(), CGM, S); LValue Dest = EmitLValue(OutExpr); if (!Constraints.empty()) @@ -1589,13 +1604,13 @@ void CodeGenFunction::EmitAsmStmt(const AsmStmt &S) { // Simplify the input constraint. std::string InputConstraint(S.getInputConstraint(i)); - InputConstraint = SimplifyConstraint(InputConstraint.c_str(), Target, + InputConstraint = SimplifyConstraint(InputConstraint.c_str(), getTarget(), &OutputConstraintInfos); InputConstraint = AddVariableConstraints(InputConstraint, *InputExpr->IgnoreParenNoopCasts(getContext()), - Target, CGM, S); + getTarget(), CGM, S); llvm::Value *Arg = EmitAsmInput(Info, InputExpr, Constraints); @@ -1647,7 +1662,7 @@ void CodeGenFunction::EmitAsmStmt(const AsmStmt &S) { StringRef Clobber = S.getClobber(i); if (Clobber != "memory" && Clobber != "cc") - Clobber = Target.getNormalizedGCCRegisterName(Clobber); + Clobber = getTarget().getNormalizedGCCRegisterName(Clobber); if (i != 0 || NumConstraints != 0) Constraints += ','; @@ -1658,7 +1673,7 @@ void CodeGenFunction::EmitAsmStmt(const AsmStmt &S) { } // Add machine specific clobbers - std::string MachineClobbers = Target.getClobbers(); + std::string MachineClobbers = getTarget().getClobbers(); if (!MachineClobbers.empty()) { if (!Constraints.empty()) Constraints += ','; @@ -1735,3 +1750,7 @@ void CodeGenFunction::EmitAsmStmt(const AsmStmt &S) { EmitStoreThroughLValue(RValue::get(Tmp), ResultRegDests[i]); } } + +void CodeGenFunction::EmitCapturedStmt(const CapturedStmt &S) { + llvm_unreachable("not implemented yet"); +} diff --git a/lib/CodeGen/CodeGenFunction.cpp b/lib/CodeGen/CodeGenFunction.cpp index 2c3cabe9851..75c60edbba5 100644 --- a/lib/CodeGen/CodeGenFunction.cpp +++ b/lib/CodeGen/CodeGenFunction.cpp @@ -31,8 +31,7 @@ using namespace clang; using namespace CodeGen; CodeGenFunction::CodeGenFunction(CodeGenModule &cgm, bool suppressNewContext) - : CodeGenTypeCache(cgm), CGM(cgm), - Target(CGM.getContext().getTargetInfo()), + : CodeGenTypeCache(cgm), CGM(cgm), Target(cgm.getTarget()), Builder(cgm.getModule().getContext()), SanitizePerformTypeCheck(CGM.getSanOpts().Null | CGM.getSanOpts().Alignment | @@ -45,7 +44,9 @@ CodeGenFunction::CodeGenFunction(CodeGenModule &cgm, bool suppressNewContext) DebugInfo(0), DisableDebugInfo(false), CalleeWithThisReturn(0), DidCallStackSave(false), IndirectBranch(0), SwitchInsn(0), CaseRangeBlock(0), UnreachableBlock(0), + NumReturnExprs(0), NumSimpleReturnExprs(0), CXXABIThisDecl(0), CXXABIThisValue(0), CXXThisValue(0), + CXXDefaultInitExprThis(0), CXXStructorImplicitParamDecl(0), CXXStructorImplicitParamValue(0), OutermostConditional(0), CurLexicalScope(0), TerminateLandingPad(0), TerminateHandler(0), TrapBB(0) { @@ -91,6 +92,9 @@ TypeEvaluationKind CodeGenFunction::getEvaluationKind(QualType type) { #include "clang/AST/TypeNodes.def" llvm_unreachable("non-canonical or dependent type in IR-generation"); + case Type::Auto: + llvm_unreachable("undeduced auto type in IR-generation"); + // Various scalar types. case Type::Builtin: case Type::Pointer: @@ -184,15 +188,36 @@ void CodeGenFunction::FinishFunction(SourceLocation EndLoc) { assert(BreakContinueStack.empty() && "mismatched push/pop in break/continue stack!"); - if (CGDebugInfo *DI = getDebugInfo()) - DI->EmitLocation(Builder, EndLoc); + bool OnlySimpleReturnStmts = NumSimpleReturnExprs > 0 + && NumSimpleReturnExprs == NumReturnExprs; + // If the function contains only a simple return statement, the + // cleanup code may become the first breakpoint in the function. To + // be safe, set the debug location for it to the location of the + // return statement. Otherwise point it to end of the function's + // lexical scope. + if (CGDebugInfo *DI = getDebugInfo()) { + if (OnlySimpleReturnStmts) + DI->EmitLocation(Builder, LastStopPoint); + else + DI->EmitLocation(Builder, EndLoc); + } // Pop any cleanups that might have been associated with the // parameters. Do this in whatever block we're currently in; it's // important to do this before we enter the return block or return // edges will be *really* confused. - if (EHStack.stable_begin() != PrologueCleanupDepth) - PopCleanupBlocks(PrologueCleanupDepth); + bool EmitRetDbgLoc = true; + if (EHStack.stable_begin() != PrologueCleanupDepth) { + PopCleanupBlocks(PrologueCleanupDepth, EndLoc); + + // Make sure the line table doesn't jump back into the body for + // the ret after it's been at EndLoc. + EmitRetDbgLoc = false; + + if (CGDebugInfo *DI = getDebugInfo()) + if (OnlySimpleReturnStmts) + DI->EmitLocation(Builder, EndLoc); + } // Emit function epilog (to return). EmitReturnBlock(); @@ -205,7 +230,7 @@ void CodeGenFunction::FinishFunction(SourceLocation EndLoc) { DI->EmitFunctionEnd(Builder); } - EmitFunctionEpilog(*CurFnInfo); + EmitFunctionEpilog(*CurFnInfo, EmitRetDbgLoc); EmitEndEHSpec(CurCodeDecl); assert(EHStack.empty() && @@ -279,8 +304,8 @@ void CodeGenFunction::EmitFunctionInstrumentation(const char *Fn) { void CodeGenFunction::EmitMCountInstrumentation() { llvm::FunctionType *FTy = llvm::FunctionType::get(VoidTy, false); - llvm::Constant *MCountFn = CGM.CreateRuntimeFunction(FTy, - Target.getMCountName()); + llvm::Constant *MCountFn = + CGM.CreateRuntimeFunction(FTy, getTarget().getMCountName()); EmitNounwindRuntimeCall(MCountFn); } @@ -448,7 +473,8 @@ void CodeGenFunction::EmitOpenCLKernelMetadata(const FunctionDecl *FD, OpenCLKernelMetadata->addOperand(kernelMDNode); } -void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy, +void CodeGenFunction::StartFunction(GlobalDecl GD, + QualType RetTy, llvm::Function *Fn, const CGFunctionInfo &FnInfo, const FunctionArgList &Args, @@ -456,7 +482,8 @@ void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy, const Decl *D = GD.getDecl(); DidCallStackSave = false; - CurCodeDecl = CurFuncDecl = D; + CurCodeDecl = D; + CurFuncDecl = (D ? D->getNonClosureContext() : 0); FnRetTy = RetTy; CurFn = Fn; CurFnInfo = &FnInfo; @@ -555,12 +582,7 @@ void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy, LambdaThisCaptureField); if (LambdaThisCaptureField) { // If this lambda captures this, load it. - QualType LambdaTagType = - getContext().getTagDeclType(LambdaThisCaptureField->getParent()); - LValue LambdaLV = MakeNaturalAlignAddrLValue(CXXABIThisValue, - LambdaTagType); - LValue ThisLValue = EmitLValueForField(LambdaLV, - LambdaThisCaptureField); + LValue ThisLValue = EmitLValueForLambdaField(LambdaThisCaptureField); CXXThisValue = EmitLoadOfLValue(ThisLValue).getScalarVal(); } } else { @@ -906,6 +928,16 @@ void CodeGenFunction::EmitBranchOnBoolExpr(const Expr *Cond, return; } + if (const CXXThrowExpr *Throw = dyn_cast(Cond)) { + // Conditional operator handling can give us a throw expression as a + // condition for a case like: + // br(c ? throw x : y, t, f) -> br(c, br(throw x, t, f), br(y, t, f) + // Fold this to: + // br(c, throw x, br(y, t, f)) + EmitCXXThrowExpr(Throw, /*KeepInsertionPoint*/false); + return; + } + // Emit the code with the fully general case. llvm::Value *CondV = EvaluateExprAsBool(Cond); Builder.CreateCondBr(CondV, TrueBlock, FalseBlock); diff --git a/lib/CodeGen/CodeGenFunction.h b/lib/CodeGen/CodeGenFunction.h index 645d5ff2378..ff74c15c38c 100644 --- a/lib/CodeGen/CodeGenFunction.h +++ b/lib/CodeGen/CodeGenFunction.h @@ -579,8 +579,8 @@ class CodeGenFunction : public CodeGenTypeCache { typedef std::pair ComplexPairTy; CGBuilderTy Builder; - /// CurFuncDecl - Holds the Decl for the current function or ObjC method. - /// This excludes BlockDecls. + /// CurFuncDecl - Holds the Decl for the current outermost + /// non-closure context. const Decl *CurFuncDecl; /// CurCodeDecl - This is the inner-most code context, which includes blocks. const Decl *CurCodeDecl; @@ -784,7 +784,9 @@ class CodeGenFunction : public CodeGenTypeCache { /// PopCleanupBlock - Will pop the cleanup entry on the stack and /// process all branch fixups. - void PopCleanupBlock(bool FallThroughIsBranchThrough = false); + /// \param EHLoc - Optional debug location for EH code. + void PopCleanupBlock(bool FallThroughIsBranchThrough = false, + SourceLocation EHLoc=SourceLocation()); /// DeactivateCleanupBlock - Deactivates the given cleanup block. /// The block cannot be reactivated. Pops it if it's the top of the @@ -905,7 +907,9 @@ class CodeGenFunction : public CodeGenTypeCache { /// PopCleanupBlocks - Takes the old cleanup stack size and emits /// the cleanup blocks that have been added. - void PopCleanupBlocks(EHScopeStack::stable_iterator OldCleanupStackSize); + /// \param EHLoc - Optional debug location for EH code. + void PopCleanupBlocks(EHScopeStack::stable_iterator OldCleanupStackSize, + SourceLocation EHLoc=SourceLocation()); void ResolveBranchFixups(llvm::BasicBlock *Target); @@ -1206,12 +1210,62 @@ class CodeGenFunction : public CodeGenTypeCache { /// lazily by getUnreachableBlock(). llvm::BasicBlock *UnreachableBlock; + /// Counts of the number return expressions in the function. + unsigned NumReturnExprs; + + /// Count the number of simple (constant) return expressions in the function. + unsigned NumSimpleReturnExprs; + + /// The last regular (non-return) debug location (breakpoint) in the function. + SourceLocation LastStopPoint; + +public: + /// A scope within which we are constructing the fields of an object which + /// might use a CXXDefaultInitExpr. This stashes away a 'this' value to use + /// if we need to evaluate a CXXDefaultInitExpr within the evaluation. + class FieldConstructionScope { + public: + FieldConstructionScope(CodeGenFunction &CGF, llvm::Value *This) + : CGF(CGF), OldCXXDefaultInitExprThis(CGF.CXXDefaultInitExprThis) { + CGF.CXXDefaultInitExprThis = This; + } + ~FieldConstructionScope() { + CGF.CXXDefaultInitExprThis = OldCXXDefaultInitExprThis; + } + + private: + CodeGenFunction &CGF; + llvm::Value *OldCXXDefaultInitExprThis; + }; + + /// The scope of a CXXDefaultInitExpr. Within this scope, the value of 'this' + /// is overridden to be the object under construction. + class CXXDefaultInitExprScope { + public: + CXXDefaultInitExprScope(CodeGenFunction &CGF) + : CGF(CGF), OldCXXThisValue(CGF.CXXThisValue) { + CGF.CXXThisValue = CGF.CXXDefaultInitExprThis; + } + ~CXXDefaultInitExprScope() { + CGF.CXXThisValue = OldCXXThisValue; + } + + public: + CodeGenFunction &CGF; + llvm::Value *OldCXXThisValue; + }; + +private: /// CXXThisDecl - When generating code for a C++ member function, /// this will hold the implicit 'this' declaration. ImplicitParamDecl *CXXABIThisDecl; llvm::Value *CXXABIThisValue; llvm::Value *CXXThisValue; + /// The value of 'this' to use when evaluating CXXDefaultInitExprs within + /// this expression. + llvm::Value *CXXDefaultInitExprThis; + /// CXXStructorImplicitParamDecl - When generating code for a constructor or /// destructor, this will hold the implicit argument (e.g. VTT). ImplicitParamDecl *CXXStructorImplicitParamDecl; @@ -1300,6 +1354,7 @@ class CodeGenFunction : public CodeGenTypeCache { return getInvokeDestImpl(); } + const TargetInfo &getTarget() const { return Target; } llvm::LLVMContext &getLLVMContext() { return CGM.getLLVMContext(); } //===--------------------------------------------------------------------===// @@ -1400,7 +1455,6 @@ class CodeGenFunction : public CodeGenTypeCache { llvm::Function *GenerateBlockFunction(GlobalDecl GD, const CGBlockInfo &Info, - const Decl *OuterFuncDecl, const DeclMapTy &ldm, bool IsLambdaConversionToBlock); @@ -1431,7 +1485,8 @@ class CodeGenFunction : public CodeGenTypeCache { void GenerateCode(GlobalDecl GD, llvm::Function *Fn, const CGFunctionInfo &FnInfo); - void StartFunction(GlobalDecl GD, QualType RetTy, + void StartFunction(GlobalDecl GD, + QualType RetTy, llvm::Function *Fn, const CGFunctionInfo &FnInfo, const FunctionArgList &Args, @@ -1521,7 +1576,7 @@ class CodeGenFunction : public CodeGenTypeCache { /// EmitFunctionEpilog - Emit the target specific LLVM code to return the /// given temporary. - void EmitFunctionEpilog(const CGFunctionInfo &FI); + void EmitFunctionEpilog(const CGFunctionInfo &FI, bool EmitRetDbgLoc); /// EmitStartEHSpec - Emit the start of the exception spec. void EmitStartEHSpec(const Decl *D); @@ -2133,6 +2188,7 @@ class CodeGenFunction : public CodeGenTypeCache { void EmitCaseStmt(const CaseStmt &S); void EmitCaseStmtRange(const CaseStmt &S); void EmitAsmStmt(const AsmStmt &S); + void EmitCapturedStmt(const CapturedStmt &S); void EmitObjCForCollectionStmt(const ObjCForCollectionStmt &S); void EmitObjCAtTryStmt(const ObjCAtTryStmt &S); @@ -2327,6 +2383,7 @@ class CodeGenFunction : public CodeGenTypeCache { llvm::Value *EmitIvarOffset(const ObjCInterfaceDecl *Interface, const ObjCIvarDecl *Ivar); LValue EmitLValueForField(LValue Base, const FieldDecl* Field); + LValue EmitLValueForLambdaField(const FieldDecl *Field); /// EmitLValueForFieldInitialization - Like EmitLValueForField, except that /// if the Field is a reference, this will return the address of the reference @@ -2446,6 +2503,7 @@ class CodeGenFunction : public CodeGenTypeCache { /// is unhandled by the current target. llvm::Value *EmitTargetBuiltinExpr(unsigned BuiltinID, const CallExpr *E); + llvm::Value *EmitAArch64BuiltinExpr(unsigned BuiltinID, const CallExpr *E); llvm::Value *EmitARMBuiltinExpr(unsigned BuiltinID, const CallExpr *E); llvm::Value *EmitNeonCall(llvm::Function *F, SmallVectorImpl &O, @@ -2625,8 +2683,8 @@ class CodeGenFunction : public CodeGenTypeCache { /// GenerateCXXGlobalInitFunc - Generates code for initializing global /// variables. void GenerateCXXGlobalInitFunc(llvm::Function *Fn, - llvm::Constant **Decls, - unsigned NumDecls); + ArrayRef Decls, + llvm::GlobalVariable *Guard = 0); /// GenerateCXXGlobalDtorsFunc - Generates code for destroying global /// variables. @@ -2650,7 +2708,7 @@ class CodeGenFunction : public CodeGenTypeCache { } void enterNonTrivialFullExpression(const ExprWithCleanups *E); - void EmitCXXThrowExpr(const CXXThrowExpr *E); + void EmitCXXThrowExpr(const CXXThrowExpr *E, bool KeepInsertionPoint = true); void EmitLambdaExpr(const LambdaExpr *E, AggValueSlot Dest); diff --git a/lib/CodeGen/CodeGenModule.cpp b/lib/CodeGen/CodeGenModule.cpp index c518a5554e2..0b03a3c4b67 100644 --- a/lib/CodeGen/CodeGenModule.cpp +++ b/lib/CodeGen/CodeGenModule.cpp @@ -35,7 +35,6 @@ #include "clang/Basic/Module.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/TargetInfo.h" -#include "clang/Basic/TargetOptions.h" #include "clang/Frontend/CodeGenOptions.h" #include "llvm/ADT/APSInt.h" #include "llvm/ADT/Triple.h" @@ -55,7 +54,7 @@ using namespace CodeGen; static const char AnnotationSection[] = "llvm.metadata"; static CGCXXABI &createCXXABI(CodeGenModule &CGM) { - switch (CGM.getContext().getTargetInfo().getCXXABI().getKind()) { + switch (CGM.getTarget().getCXXABI().getKind()) { case TargetCXXABI::GenericAArch64: case TargetCXXABI::GenericARM: case TargetCXXABI::iOS: @@ -70,19 +69,16 @@ static CGCXXABI &createCXXABI(CodeGenModule &CGM) { CodeGenModule::CodeGenModule(ASTContext &C, const CodeGenOptions &CGO, - const TargetOptions &TO, llvm::Module &M, - const llvm::DataLayout &TD, + llvm::Module &M, const llvm::DataLayout &TD, DiagnosticsEngine &diags) - : Context(C), LangOpts(C.getLangOpts()), CodeGenOpts(CGO), TargetOpts(TO), - TheModule(M), TheDataLayout(TD), TheTargetCodeGenInfo(0), Diags(diags), - ABI(createCXXABI(*this)), - Types(*this), - TBAA(0), - VTables(*this), ObjCRuntime(0), OpenCLRuntime(0), CUDARuntime(0), + : Context(C), LangOpts(C.getLangOpts()), CodeGenOpts(CGO), TheModule(M), + Diags(diags), TheDataLayout(TD), Target(C.getTargetInfo()), + ABI(createCXXABI(*this)), VMContext(M.getContext()), TBAA(0), + TheTargetCodeGenInfo(0), Types(*this), VTables(*this), + ObjCRuntime(0), OpenCLRuntime(0), CUDARuntime(0), DebugInfo(0), ARCData(0), NoObjCARCExceptionsMetadata(0), RRData(0), CFConstantStringClassRef(0), ConstantStringClassRef(0), NSConstantStringType(0), - VMContext(M.getContext()), NSConcreteGlobalBlock(0), NSConcreteStackBlock(0), BlockObjectAssign(0), BlockObjectDispose(0), BlockDescriptorType(0), GenericBlockLiteralType(0), @@ -180,15 +176,17 @@ void CodeGenModule::Release() { EmitDeferred(); EmitCXXGlobalInitFunc(); EmitCXXGlobalDtorFunc(); + EmitCXXThreadLocalInitFunc(); if (ObjCRuntime) if (llvm::Function *ObjCInitFunction = ObjCRuntime->ModuleInitFunction()) AddGlobalCtor(ObjCInitFunction); EmitCtorList(GlobalCtors, "llvm.global_ctors"); EmitCtorList(GlobalDtors, "llvm.global_dtors"); EmitGlobalAnnotations(); + EmitStaticExternCAliases(); EmitLLVMUsed(); - if (CodeGenOpts.ModulesAutolink) { + if (CodeGenOpts.Autolink && Context.getLangOpts().Modules) { EmitModuleLinkOptions(); } @@ -241,13 +239,18 @@ llvm::MDNode *CodeGenModule::getTBAAStructTagInfo(QualType BaseTy, return TBAA->getTBAAStructTagInfo(BaseTy, AccessN, O); } +/// Decorate the instruction with a TBAA tag. For scalar TBAA, the tag +/// is the same as the type. For struct-path aware TBAA, the tag +/// is different from the type: base type, access type and offset. +/// When ConvertTypeToTag is true, we create a tag based on the scalar type. void CodeGenModule::DecorateInstruction(llvm::Instruction *Inst, - llvm::MDNode *TBAAInfo) { - Inst->setMetadata(llvm::LLVMContext::MD_tbaa, TBAAInfo); -} - -bool CodeGenModule::isTargetDarwin() const { - return getContext().getTargetInfo().getTriple().isOSDarwin(); + llvm::MDNode *TBAAInfo, + bool ConvertTypeToTag) { + if (ConvertTypeToTag && TBAA && CodeGenOpts.StructPathTBAA) + Inst->setMetadata(llvm::LLVMContext::MD_tbaa, + TBAA->getTBAAScalarTagInfo(TBAAInfo)); + else + Inst->setMetadata(llvm::LLVMContext::MD_tbaa, TBAAInfo); } void CodeGenModule::Error(SourceLocation loc, StringRef error) { @@ -323,7 +326,7 @@ static llvm::GlobalVariable::ThreadLocalMode GetLLVMTLSModel( void CodeGenModule::setTLSMode(llvm::GlobalVariable *GV, const VarDecl &D) const { - assert(D.isThreadSpecified() && "setting TLS mode on non-TLS var!"); + assert(D.getTLSKind() && "setting TLS mode on non-TLS var!"); llvm::GlobalVariable::ThreadLocalMode TLM; TLM = GetLLVMTLSModel(CodeGenOpts.getDefaultTLSModel()); @@ -1475,8 +1478,11 @@ CodeGenModule::GetOrCreateLLVMGlobal(StringRef MangledName, GV->setVisibility(GetLLVMVisibility(LV.getVisibility())); } - if (D->isThreadSpecified()) + if (D->getTLSKind()) { + if (D->getTLSKind() == VarDecl::TLS_Dynamic) + CXXThreadLocals.push_back(std::make_pair(D, GV)); setTLSMode(GV, *D); + } } if (AddrSpace != Ty->getAddressSpace()) @@ -1617,6 +1623,7 @@ CodeGenModule::MaybeEmitGlobalStdInitializerListInitializer(const VarDecl *D, D->getLocStart(), D->getLocation(), name, arrayType, sourceInfo, SC_Static); + backingArray->setTSCSpec(D->getTSCSpec()); // Now clone the InitListExpr to initialize the array instead. // Incredible hack: we want to use the existing InitListExpr here, so we need @@ -1707,6 +1714,39 @@ unsigned CodeGenModule::GetGlobalVarAddressSpace(const VarDecl *D, return AddrSpace; } +template +void CodeGenModule::MaybeHandleStaticInExternC(const SomeDecl *D, + llvm::GlobalValue *GV) { + if (!getLangOpts().CPlusPlus) + return; + + // Must have 'used' attribute, or else inline assembly can't rely on + // the name existing. + if (!D->template hasAttr()) + return; + + // Must have internal linkage and an ordinary name. + if (!D->getIdentifier() || D->getLinkage() != InternalLinkage) + return; + + // Must be in an extern "C" context. Entities declared directly within + // a record are not extern "C" even if the record is in such a context. + const SomeDecl *First = D->getFirstDeclaration(); + if (First->getDeclContext()->isRecord() || !First->isInExternCContext()) + return; + + // OK, this is an internal linkage entity inside an extern "C" linkage + // specification. Make a note of that so we can give it the "expected" + // mangled name if nothing else is using that name. + std::pair R = + StaticExternCValues.insert(std::make_pair(D->getIdentifier(), GV)); + + // If we have multiple internal linkage entities with the same name + // in extern "C" regions, none of them gets that name. + if (!R.second) + R.first->second = 0; +} + void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D) { llvm::Constant *Init = 0; QualType ASTTy = D->getType(); @@ -1805,6 +1845,8 @@ void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D) { cast(Entry)->eraseFromParent(); } + MaybeHandleStaticInExternC(D, GV); + if (D->hasAttr()) AddGlobalAnnotations(D, GV); @@ -1870,11 +1912,17 @@ CodeGenModule::GetLLVMLinkageVarDefinition(const VarDecl *D, ((!CodeGenOpts.NoCommon && !D->getAttr()) || D->getAttr()) && !D->hasExternalStorage() && !D->getInit() && - !D->getAttr() && !D->isThreadSpecified() && + !D->getAttr() && !D->getTLSKind() && !D->getAttr()) { // Thread local vars aren't considered common linkage. return llvm::GlobalVariable::CommonLinkage; - } + } else if (D->getTLSKind() == VarDecl::TLS_Dynamic && + getTarget().getTriple().isMacOSX()) + // On Darwin, the backing variable for a C++11 thread_local variable always + // has internal linkage; all accesses should just be calls to the + // Itanium-specified entry point, which has the normal linkage of the + // variable. + return llvm::GlobalValue::InternalLinkage; return llvm::GlobalVariable::ExternalLinkage; } @@ -2083,6 +2131,8 @@ void CodeGenModule::EmitGlobalFunctionDefinition(GlobalDecl GD) { // FIXME: this is redundant with part of SetFunctionDefinitionAttributes setGlobalVisibility(Fn, D); + MaybeHandleStaticInExternC(D, Fn); + CodeGenFunction(*this).GenerateCode(D, Fn, FI); SetFunctionDefinitionAttributes(D, Fn); @@ -2231,7 +2281,8 @@ CodeGenModule::GetAddrOfConstantCFString(const StringLiteral *Literal) { llvm::Constant *Zero = llvm::Constant::getNullValue(Int32Ty); llvm::Constant *Zeros[] = { Zero, Zero }; - + llvm::Value *V; + // If we don't already have it, get __CFConstantStringClassReference. if (!CFConstantStringClassRef) { llvm::Type *Ty = getTypes().ConvertType(getContext().IntTy); @@ -2239,9 +2290,11 @@ CodeGenModule::GetAddrOfConstantCFString(const StringLiteral *Literal) { llvm::Constant *GV = CreateRuntimeVariable(Ty, "__CFConstantStringClassReference"); // Decay array -> ptr - CFConstantStringClassRef = - llvm::ConstantExpr::getGetElementPtr(GV, Zeros); + V = llvm::ConstantExpr::getGetElementPtr(GV, Zeros); + CFConstantStringClassRef = V; } + else + V = CFConstantStringClassRef; QualType CFTy = getContext().getCFConstantStringType(); @@ -2251,7 +2304,7 @@ CodeGenModule::GetAddrOfConstantCFString(const StringLiteral *Literal) { llvm::Constant *Fields[4]; // Class pointer. - Fields[0] = CFConstantStringClassRef; + Fields[0] = cast(V); // Flags. llvm::Type *Ty = getTypes().ConvertType(getContext().UnsignedIntTy); @@ -2286,6 +2339,8 @@ CodeGenModule::GetAddrOfConstantCFString(const StringLiteral *Literal) { new llvm::GlobalVariable(getModule(), C->getType(), /*isConstant=*/true, Linkage, C, ".str"); GV->setUnnamedAddr(true); + // Don't enforce the target's minimum global alignment, since the only use + // of the string is via this class initializer. if (isUTF16) { CharUnits Align = getContext().getTypeAlignInChars(getContext().ShortTy); GV->setAlignment(Align.getQuantity()); @@ -2310,7 +2365,7 @@ CodeGenModule::GetAddrOfConstantCFString(const StringLiteral *Literal) { GV = new llvm::GlobalVariable(getModule(), C->getType(), true, llvm::GlobalVariable::PrivateLinkage, C, "_unnamed_cfstring_"); - if (const char *Sect = getContext().getTargetInfo().getCFStringSection()) + if (const char *Sect = getTarget().getCFStringSection()) GV->setSection(Sect); Entry.setValue(GV); @@ -2338,7 +2393,7 @@ CodeGenModule::GetAddrOfConstantString(const StringLiteral *Literal) { llvm::Constant *Zero = llvm::Constant::getNullValue(Int32Ty); llvm::Constant *Zeros[] = { Zero, Zero }; - + llvm::Value *V; // If we don't already have it, get _NSConstantStringClassReference. if (!ConstantStringClassRef) { std::string StringClass(getLangOpts().ObjCConstantStringClass); @@ -2351,8 +2406,8 @@ CodeGenModule::GetAddrOfConstantString(const StringLiteral *Literal) { GV = getObjCRuntime().GetClassGlobal(str); // Make sure the result is of the correct type. llvm::Type *PTy = llvm::PointerType::getUnqual(Ty); - ConstantStringClassRef = - llvm::ConstantExpr::getBitCast(GV, PTy); + V = llvm::ConstantExpr::getBitCast(GV, PTy); + ConstantStringClassRef = V; } else { std::string str = StringClass.empty() ? "_NSConstantStringClassReference" @@ -2360,10 +2415,12 @@ CodeGenModule::GetAddrOfConstantString(const StringLiteral *Literal) { llvm::Type *PTy = llvm::ArrayType::get(Ty, 0); GV = CreateRuntimeVariable(PTy, str); // Decay array -> ptr - ConstantStringClassRef = - llvm::ConstantExpr::getGetElementPtr(GV, Zeros); + V = llvm::ConstantExpr::getGetElementPtr(GV, Zeros); + ConstantStringClassRef = V; } } + else + V = ConstantStringClassRef; if (!NSConstantStringType) { // Construct the type for a constant NSString. @@ -2402,7 +2459,7 @@ CodeGenModule::GetAddrOfConstantString(const StringLiteral *Literal) { llvm::Constant *Fields[3]; // Class pointer. - Fields[0] = ConstantStringClassRef; + Fields[0] = cast(V); // String pointer. llvm::Constant *C = @@ -2417,6 +2474,8 @@ CodeGenModule::GetAddrOfConstantString(const StringLiteral *Literal) { new llvm::GlobalVariable(getModule(), C->getType(), isConstant, Linkage, C, ".str"); GV->setUnnamedAddr(true); + // Don't enforce the target's minimum global alignment, since the only use + // of the string is via this class initializer. CharUnits Align = getContext().getTypeAlignInChars(getContext().CharTy); GV->setAlignment(Align.getQuantity()); Fields[1] = llvm::ConstantExpr::getGetElementPtr(GV, Zeros); @@ -2433,8 +2492,8 @@ CodeGenModule::GetAddrOfConstantString(const StringLiteral *Literal) { // FIXME. Fix section. if (const char *Sect = LangOpts.ObjCRuntime.isNonFragile() - ? getContext().getTargetInfo().getNSStringNonFragileABISection() - : getContext().getTargetInfo().getNSStringSection()) + ? getTarget().getNSStringNonFragileABISection() + : getTarget().getNSStringSection()) GV->setSection(Sect); Entry.setValue(GV); @@ -2521,7 +2580,7 @@ CodeGenModule::GetConstantArrayFromStringLiteral(const StringLiteral *E) { /// constant array for the given string literal. llvm::Constant * CodeGenModule::GetAddrOfConstantStringFromLiteral(const StringLiteral *S) { - CharUnits Align = getContext().getTypeAlignInChars(S->getType()); + CharUnits Align = getContext().getAlignOfGlobalVarInChars(S->getType()); if (S->isAscii() || S->isUTF8()) { SmallString<64> Str(S->getString()); @@ -2590,6 +2649,10 @@ llvm::Constant *CodeGenModule::GetAddrOfConstantString(StringRef Str, if (!GlobalName) GlobalName = ".str"; + if (Alignment == 0) + Alignment = getContext().getAlignOfGlobalVarInChars(getContext().CharTy) + .getQuantity(); + // Don't share any string literals if strings aren't constant. if (LangOpts.WritableStrings) return GenerateStringLiteral(Str, false, *this, GlobalName, Alignment); @@ -2770,7 +2833,6 @@ void CodeGenModule::EmitTopLevelDecl(Decl *D) { // No code generation needed. case Decl::UsingShadow: case Decl::Using: - case Decl::UsingDirective: case Decl::ClassTemplate: case Decl::FunctionTemplate: case Decl::TypeAliasTemplate: @@ -2778,6 +2840,10 @@ void CodeGenModule::EmitTopLevelDecl(Decl *D) { case Decl::Block: case Decl::Empty: break; + case Decl::UsingDirective: // using namespace X; [C++] + if (CGDebugInfo *DI = getModuleDebugInfo()) + DI->EmitUsingDirective(cast(*D)); + return; case Decl::CXXConstructor: // Skip function templates if (cast(D)->getDescribedFunctionTemplate() || @@ -2903,6 +2969,23 @@ static void EmitGlobalDeclMetadata(CodeGenModule &CGM, GlobalMetadata->addOperand(llvm::MDNode::get(CGM.getLLVMContext(), Ops)); } +/// For each function which is declared within an extern "C" region and marked +/// as 'used', but has internal linkage, create an alias from the unmangled +/// name to the mangled name if possible. People expect to be able to refer +/// to such functions with an unmangled name from inline assembly within the +/// same translation unit. +void CodeGenModule::EmitStaticExternCAliases() { + for (StaticExternCMap::iterator I = StaticExternCValues.begin(), + E = StaticExternCValues.end(); + I != E; ++I) { + IdentifierInfo *Name = I->first; + llvm::GlobalValue *Val = I->second; + if (Val && !getModule().getNamedValue(Name->getName())) + AddUsedGlobal(new llvm::GlobalAlias(Val->getType(), Val->getLinkage(), + Name->getName(), Val, &getModule())); + } +} + /// Emits metadata nodes associating all the global values in the /// current module with the Decls they came from. This is useful for /// projects using IR gen as a subroutine. diff --git a/lib/CodeGen/CodeGenModule.h b/lib/CodeGen/CodeGenModule.h index 5b2153e5ff3..91138c607c3 100644 --- a/lib/CodeGen/CodeGenModule.h +++ b/lib/CodeGen/CodeGenModule.h @@ -67,7 +67,6 @@ namespace clang { class VarDecl; class LangOptions; class CodeGenOptions; - class TargetOptions; class DiagnosticsEngine; class AnnotateAttr; class CXXDestructorDecl; @@ -233,15 +232,22 @@ class CodeGenModule : public CodeGenTypeCache { ASTContext &Context; const LangOptions &LangOpts; const CodeGenOptions &CodeGenOpts; - const TargetOptions &TargetOpts; llvm::Module &TheModule; - const llvm::DataLayout &TheDataLayout; - mutable const TargetCodeGenInfo *TheTargetCodeGenInfo; DiagnosticsEngine &Diags; + const llvm::DataLayout &TheDataLayout; + const TargetInfo &Target; CGCXXABI &ABI; - CodeGenTypes Types; - CodeGenTBAA *TBAA; + llvm::LLVMContext &VMContext; + CodeGenTBAA *TBAA; + + mutable const TargetCodeGenInfo *TheTargetCodeGenInfo; + + // This should not be moved earlier, since its initialization depends on some + // of the previous reference members being already initialized and also checks + // if TheTargetCodeGenInfo is NULL + CodeGenTypes Types; + /// VTables - Holds information about C++ vtables. CodeGenVTables VTables; friend class CodeGenVTables; @@ -255,8 +261,8 @@ class CodeGenModule : public CodeGenTypeCache { RREntrypoints *RRData; // WeakRefReferences - A set of references that have only been seen via - // a weakref so far. This is used to remove the weak of the reference if we ever - // see a direct reference or a definition. + // a weakref so far. This is used to remove the weak of the reference if we + // ever see a direct reference or a definition. llvm::SmallPtrSet WeakRefReferences; /// DeferredDecls - This contains all the decls which have definitions but @@ -305,6 +311,20 @@ class CodeGenModule : public CodeGenTypeCache { llvm::DenseMap AtomicSetterHelperFnMap; llvm::DenseMap AtomicGetterHelperFnMap; + /// Map used to track internal linkage functions declared within + /// extern "C" regions. + typedef llvm::MapVector StaticExternCMap; + StaticExternCMap StaticExternCValues; + + /// \brief thread_local variables defined or used in this TU. + std::vector > + CXXThreadLocals; + + /// \brief thread_local variables with initializers that need to run + /// before any thread_local variable in this TU is odr-used. + std::vector CXXThreadLocalInits; + /// CXXGlobalInits - Global variables with initializers that need to run /// before main. std::vector CXXGlobalInits; @@ -340,11 +360,11 @@ class CodeGenModule : public CodeGenTypeCache { /// CFConstantStringClassRef - Cached reference to the class for constant /// strings. This value has type int * but is actually an Obj-C class pointer. - llvm::Constant *CFConstantStringClassRef; + llvm::WeakVH CFConstantStringClassRef; /// ConstantStringClassRef - Cached reference to the class for constant /// strings. This value has type int * but is actually an Obj-C class pointer. - llvm::Constant *ConstantStringClassRef; + llvm::WeakVH ConstantStringClassRef; /// \brief The LLVM type corresponding to NSConstantString. llvm::StructType *NSConstantStringType; @@ -363,7 +383,6 @@ class CodeGenModule : public CodeGenTypeCache { bool isTriviallyRecursive(const FunctionDecl *F); bool shouldEmitFunction(const FunctionDecl *F); - llvm::LLVMContext &VMContext; /// @name Cache for Blocks Runtime Globals /// @{ @@ -396,8 +415,8 @@ class CodeGenModule : public CodeGenTypeCache { /// @} public: CodeGenModule(ASTContext &C, const CodeGenOptions &CodeGenOpts, - const TargetOptions &TargetOpts, llvm::Module &M, - const llvm::DataLayout &TD, DiagnosticsEngine &Diags); + llvm::Module &M, const llvm::DataLayout &TD, + DiagnosticsEngine &Diags); ~CodeGenModule(); @@ -427,9 +446,6 @@ class CodeGenModule : public CodeGenTypeCache { return *CUDARuntime; } - /// getCXXABI() - Return a reference to the configured C++ ABI. - CGCXXABI &getCXXABI() { return ABI; } - ARCEntrypoints &getARCEntrypoints() const { assert(getLangOpts().ObjCAutoRefCount && ARCData != 0); return *ARCData; @@ -483,21 +499,24 @@ class CodeGenModule : public CodeGenTypeCache { } ASTContext &getContext() const { return Context; } - const CodeGenOptions &getCodeGenOpts() const { return CodeGenOpts; } const LangOptions &getLangOpts() const { return LangOpts; } + const CodeGenOptions &getCodeGenOpts() const { return CodeGenOpts; } llvm::Module &getModule() const { return TheModule; } - CodeGenTypes &getTypes() { return Types; } - CodeGenVTables &getVTables() { return VTables; } - VTableContext &getVTableContext() { return VTables.getVTableContext(); } DiagnosticsEngine &getDiags() const { return Diags; } const llvm::DataLayout &getDataLayout() const { return TheDataLayout; } - const TargetInfo &getTarget() const { return Context.getTargetInfo(); } + const TargetInfo &getTarget() const { return Target; } + CGCXXABI &getCXXABI() { return ABI; } llvm::LLVMContext &getLLVMContext() { return VMContext; } - const TargetCodeGenInfo &getTargetCodeGenInfo(); - bool isTargetDarwin() const; - + bool shouldUseTBAA() const { return TBAA != 0; } + const TargetCodeGenInfo &getTargetCodeGenInfo(); + + CodeGenTypes &getTypes() { return Types; } + + CodeGenVTables &getVTables() { return VTables; } + VTableContext &getVTableContext() { return VTables.getVTableContext(); } + llvm::MDNode *getTBAAInfo(QualType QTy); llvm::MDNode *getTBAAInfoForVTablePtr(); llvm::MDNode *getTBAAStructInfo(QualType QTy); @@ -512,8 +531,13 @@ class CodeGenModule : public CodeGenTypeCache { bool isPaddedAtomicType(QualType type); bool isPaddedAtomicType(const AtomicType *type); - static void DecorateInstruction(llvm::Instruction *Inst, - llvm::MDNode *TBAAInfo); + /// Decorate the instruction with a TBAA tag. For scalar TBAA, the tag + /// is the same as the type. For struct-path aware TBAA, the tag + /// is different from the type: base type, access type and offset. + /// When ConvertTypeToTag is true, we create a tag based on the scalar type. + void DecorateInstruction(llvm::Instruction *Inst, + llvm::MDNode *TBAAInfo, + bool ConvertTypeToTag = true); /// getSize - Emit the given number of characters as a value of type size_t. llvm::ConstantInt *getSize(CharUnits numChars); @@ -563,8 +587,8 @@ class CodeGenModule : public CodeGenTypeCache { return GetAddrOfGlobalVar(cast(GD.getDecl())); } - /// CreateOrReplaceCXXRuntimeVariable - Will return a global variable of the given - /// type. If a variable with a different type already exists then a new + /// CreateOrReplaceCXXRuntimeVariable - Will return a global variable of the + /// given type. If a variable with a different type already exists then a new /// variable with the right type will be created and all uses of the old /// variable will be replaced with a bitcast to the new variable. llvm::GlobalVariable * @@ -689,7 +713,7 @@ class CodeGenModule : public CodeGenTypeCache { /// (if one is created). llvm::Constant *GetAddrOfConstantString(StringRef Str, const char *GlobalName=0, - unsigned Alignment=1); + unsigned Alignment=0); /// GetAddrOfConstantCString - Returns a pointer to a character array /// containing the literal and a terminating '\0' character. The result has @@ -699,7 +723,7 @@ class CodeGenModule : public CodeGenTypeCache { /// created). llvm::Constant *GetAddrOfConstantCString(const std::string &str, const char *GlobalName=0, - unsigned Alignment=1); + unsigned Alignment=0); /// GetAddrOfConstantCompoundLiteral - Returns a pointer to a constant global /// variable for the given file-scope compound literal expression. @@ -726,8 +750,7 @@ class CodeGenModule : public CodeGenTypeCache { llvm::Value *getBuiltinLibFunction(const FunctionDecl *FD, unsigned BuiltinID); - llvm::Function *getIntrinsic(unsigned IID, ArrayRef Tys = - ArrayRef()); + llvm::Function *getIntrinsic(unsigned IID, ArrayRef Tys = None); /// EmitTopLevelDecl - Emit code for a single top level declaration. void EmitTopLevelDecl(Decl *D); @@ -736,6 +759,12 @@ class CodeGenModule : public CodeGenTypeCache { // variable has been instantiated. void HandleCXXStaticMemberVarInstantiation(VarDecl *VD); + /// \brief If the declaration has internal linkage but is inside an + /// extern "C" linkage specification, prepare to emit an alias for it + /// to the expected name. + template + void MaybeHandleStaticInExternC(const SomeDecl *D, llvm::GlobalValue *GV); + /// AddUsedGlobal - Add a global which should be forced to be /// present in the object file; these are emitted to the llvm.used /// metadata global. @@ -1004,6 +1033,9 @@ class CodeGenModule : public CodeGenTypeCache { /// a C++ destructor Decl. void EmitCXXDestructor(const CXXDestructorDecl *D, CXXDtorType Type); + /// \brief Emit the function that initializes C++ thread_local variables. + void EmitCXXThreadLocalInitFunc(); + /// EmitCXXGlobalInitFunc - Emit the function that initializes C++ globals. void EmitCXXGlobalInitFunc(); @@ -1048,6 +1080,10 @@ class CodeGenModule : public CodeGenTypeCache { /// \brief Emit the link options introduced by imported modules. void EmitModuleLinkOptions(); + /// \brief Emit aliases for internal-linkage declarations inside "C" language + /// linkage specifications, giving them the "expected" name where possible. + void EmitStaticExternCAliases(); + void EmitDeclMetadata(); /// EmitCoverageFile - Emit the llvm.gcov metadata used to tell LLVM where diff --git a/lib/CodeGen/CodeGenTBAA.cpp b/lib/CodeGen/CodeGenTBAA.cpp index 7e4d34ab898..5ff1560a488 100644 --- a/lib/CodeGen/CodeGenTBAA.cpp +++ b/lib/CodeGen/CodeGenTBAA.cpp @@ -50,13 +50,25 @@ llvm::MDNode *CodeGenTBAA::getRoot() { return Root; } +// For struct-path aware TBAA, the scalar type has the same format as +// the struct type: name, offset, pointer to another node in the type DAG. +// For scalar TBAA, the scalar type is the same as the scalar tag: +// name and a parent pointer. +llvm::MDNode *CodeGenTBAA::createTBAAScalarType(StringRef Name, + llvm::MDNode *Parent) { + if (CodeGenOpts.StructPathTBAA) + return MDHelper.createTBAAScalarTypeNode(Name, Parent); + else + return MDHelper.createTBAANode(Name, Parent); +} + llvm::MDNode *CodeGenTBAA::getChar() { // Define the root of the tree for user-accessible memory. C and C++ // give special powers to char and certain similar types. However, // these special powers only cover user-accessible memory, and doesn't // include things like vtables. if (!Char) - Char = MDHelper.createTBAANode("omnipotent char", getRoot()); + Char = createTBAAScalarType("omnipotent char", getRoot()); return Char; } @@ -124,7 +136,7 @@ CodeGenTBAA::getTBAAInfo(QualType QTy) { // "underlying types". default: return MetadataCache[Ty] = - MDHelper.createTBAANode(BTy->getName(Features), getChar()); + createTBAAScalarType(BTy->getName(Features), getChar()); } } @@ -132,8 +144,8 @@ CodeGenTBAA::getTBAAInfo(QualType QTy) { // TODO: Implement C++'s type "similarity" and consider dis-"similar" // pointers distinct. if (Ty->isPointerType()) - return MetadataCache[Ty] = MDHelper.createTBAANode("any pointer", - getChar()); + return MetadataCache[Ty] = createTBAAScalarType("any pointer", + getChar()); // Enum types are distinct types. In C++ they have "underlying types", // however they aren't related for TBAA. @@ -160,7 +172,7 @@ CodeGenTBAA::getTBAAInfo(QualType QTy) { llvm::raw_svector_ostream Out(OutName); MContext.mangleCXXRTTIName(QualType(ETy, 0), Out); Out.flush(); - return MetadataCache[Ty] = MDHelper.createTBAANode(OutName, getChar()); + return MetadataCache[Ty] = createTBAAScalarType(OutName, getChar()); } // For now, handle any other kind of type conservatively. @@ -168,7 +180,7 @@ CodeGenTBAA::getTBAAInfo(QualType QTy) { } llvm::MDNode *CodeGenTBAA::getTBAAInfoForVTablePtr() { - return MDHelper.createTBAANode("vtable pointer", getRoot()); + return createTBAAScalarType("vtable pointer", getRoot()); } bool @@ -192,8 +204,18 @@ CodeGenTBAA::CollectFields(uint64_t BaseOffset, const ASTRecordLayout &Layout = Context.getASTRecordLayout(RD); unsigned idx = 0; + const FieldDecl *LastFD = 0; + bool IsMsStruct = RD->isMsStruct(Context); for (RecordDecl::field_iterator i = RD->field_begin(), e = RD->field_end(); i != e; ++i, ++idx) { + if (IsMsStruct) { + // Zero-length bitfields following non-bitfield members are ignored. + if (Context.ZeroBitfieldFollowsNonBitfield(*i, LastFD)) { + --idx; + continue; + } + LastFD = *i; + } uint64_t Offset = BaseOffset + Layout.getFieldOffset(idx) / Context.getCharWidth(); QualType FieldQTy = i->getType(); @@ -208,7 +230,9 @@ CodeGenTBAA::CollectFields(uint64_t BaseOffset, uint64_t Offset = BaseOffset; uint64_t Size = Context.getTypeSizeInChars(QTy).getQuantity(); llvm::MDNode *TBAAInfo = MayAlias ? getChar() : getTBAAInfo(QTy); - Fields.push_back(llvm::MDBuilder::TBAAStructField(Offset, Size, TBAAInfo)); + llvm::MDNode *TBAATag = CodeGenOpts.StructPathTBAA ? + getTBAAScalarTagInfo(TBAAInfo) : TBAAInfo; + Fields.push_back(llvm::MDBuilder::TBAAStructField(Offset, Size, TBAATag)); return true; } @@ -231,9 +255,11 @@ CodeGenTBAA::getTBAAStructInfo(QualType QTy) { static bool isTBAAPathStruct(QualType QTy) { if (const RecordType *TTy = QTy->getAs()) { const RecordDecl *RD = TTy->getDecl()->getDefinition(); + if (RD->hasFlexibleArrayMember()) + return false; // RD can be struct, union, class, interface or enum. - // For now, we only handle struct. - if (RD->isStruct() && !RD->hasFlexibleArrayMember()) + // For now, we only handle struct and class. + if (RD->isStruct() || RD->isClass()) return true; } return false; @@ -251,29 +277,31 @@ CodeGenTBAA::getTBAAStructTypeInfo(QualType QTy) { const RecordDecl *RD = TTy->getDecl()->getDefinition(); const ASTRecordLayout &Layout = Context.getASTRecordLayout(RD); - SmallVector , 4> Fields; - // To reduce the size of MDNode for a given struct type, we only output - // once for all the fields with the same scalar types. - // Offsets for scalar fields in the type DAG are not used. - llvm::SmallSet ScalarFieldTypes; + SmallVector , 4> Fields; unsigned idx = 0; + const FieldDecl *LastFD = 0; + bool IsMsStruct = RD->isMsStruct(Context); for (RecordDecl::field_iterator i = RD->field_begin(), e = RD->field_end(); i != e; ++i, ++idx) { + if (IsMsStruct) { + // Zero-length bitfields following non-bitfield members are ignored. + if (Context.ZeroBitfieldFollowsNonBitfield(*i, LastFD)) { + --idx; + continue; + } + LastFD = *i; + } + QualType FieldQTy = i->getType(); llvm::MDNode *FieldNode; if (isTBAAPathStruct(FieldQTy)) FieldNode = getTBAAStructTypeInfo(FieldQTy); - else { + else FieldNode = getTBAAInfo(FieldQTy); - // Ignore this field if the type already exists. - if (ScalarFieldTypes.count(FieldNode)) - continue; - ScalarFieldTypes.insert(FieldNode); - } if (!FieldNode) return StructTypeMetadataCache[Ty] = NULL; Fields.push_back(std::make_pair( - Layout.getFieldOffset(idx) / Context.getCharWidth(), FieldNode)); + FieldNode, Layout.getFieldOffset(idx) / Context.getCharWidth())); } // TODO: This is using the RTTI name. Is there a better way to get @@ -305,8 +333,18 @@ CodeGenTBAA::getTBAAStructTagInfo(QualType BaseQTy, llvm::MDNode *AccessNode, if (isTBAAPathStruct(BaseQTy)) BNode = getTBAAStructTypeInfo(BaseQTy); if (!BNode) - return StructTagMetadataCache[PathTag] = AccessNode; + return StructTagMetadataCache[PathTag] = + MDHelper.createTBAAStructTagNode(AccessNode, AccessNode, 0); return StructTagMetadataCache[PathTag] = MDHelper.createTBAAStructTagNode(BNode, AccessNode, Offset); } + +llvm::MDNode * +CodeGenTBAA::getTBAAScalarTagInfo(llvm::MDNode *AccessNode) { + if (llvm::MDNode *N = ScalarTagMetadataCache[AccessNode]) + return N; + + return ScalarTagMetadataCache[AccessNode] = + MDHelper.createTBAAStructTagNode(AccessNode, AccessNode, 0); +} diff --git a/lib/CodeGen/CodeGenTBAA.h b/lib/CodeGen/CodeGenTBAA.h index 9ddc3aa9700..f0c9e06f02b 100644 --- a/lib/CodeGen/CodeGenTBAA.h +++ b/lib/CodeGen/CodeGenTBAA.h @@ -61,6 +61,8 @@ class CodeGenTBAA { llvm::DenseMap StructTypeMetadataCache; /// This maps TBAAPathTags to a tag node. llvm::DenseMap StructTagMetadataCache; + /// This maps a scalar type to a scalar tag node. + llvm::DenseMap ScalarTagMetadataCache; /// StructMetadataCache - This maps clang::Types to llvm::MDNodes describing /// them for struct assignments. @@ -84,6 +86,11 @@ class CodeGenTBAA { SmallVectorImpl &Fields, bool MayAlias); + /// A wrapper function to create a scalar type. For struct-path aware TBAA, + /// the scalar type has the same format as the struct type: name, offset, + /// pointer to another node in the type DAG. + llvm::MDNode *createTBAAScalarType(StringRef Name, llvm::MDNode *Parent); + public: CodeGenTBAA(ASTContext &Ctx, llvm::LLVMContext &VMContext, const CodeGenOptions &CGO, @@ -105,10 +112,13 @@ class CodeGenTBAA { /// Get the MDNode in the type DAG for given struct type QType. llvm::MDNode *getTBAAStructTypeInfo(QualType QType); - /// Get the tag MDNode for a given base type, the actual sclar access MDNode + /// Get the tag MDNode for a given base type, the actual scalar access MDNode /// and offset into the base type. llvm::MDNode *getTBAAStructTagInfo(QualType BaseQType, llvm::MDNode *AccessNode, uint64_t Offset); + + /// Get the sclar tag MDNode for a given scalar type. + llvm::MDNode *getTBAAScalarTagInfo(llvm::MDNode *AccessNode); }; } // end namespace CodeGen diff --git a/lib/CodeGen/CodeGenTypes.cpp b/lib/CodeGen/CodeGenTypes.cpp index 8fc78e3de62..4240216b230 100644 --- a/lib/CodeGen/CodeGenTypes.cpp +++ b/lib/CodeGen/CodeGenTypes.cpp @@ -28,12 +28,12 @@ using namespace clang; using namespace CodeGen; -CodeGenTypes::CodeGenTypes(CodeGenModule &CGM) - : Context(CGM.getContext()), Target(Context.getTargetInfo()), - TheModule(CGM.getModule()), TheDataLayout(CGM.getDataLayout()), - TheABIInfo(CGM.getTargetCodeGenInfo().getABIInfo()), - TheCXXABI(CGM.getCXXABI()), - CodeGenOpts(CGM.getCodeGenOpts()), CGM(CGM) { +CodeGenTypes::CodeGenTypes(CodeGenModule &cgm) + : CGM(cgm), Context(cgm.getContext()), TheModule(cgm.getModule()), + TheDataLayout(cgm.getDataLayout()), + Target(cgm.getTarget()), TheCXXABI(cgm.getCXXABI()), + CodeGenOpts(cgm.getCodeGenOpts()), + TheABIInfo(cgm.getTargetCodeGenInfo().getABIInfo()) { SkippedLayout = false; } @@ -392,6 +392,8 @@ llvm::Type *CodeGenTypes::ConvertType(QualType T) { } break; } + case Type::Auto: + llvm_unreachable("Unexpected undeduced auto type!"); case Type::Complex: { llvm::Type *EltTy = ConvertType(cast(Ty)->getElementType()); ResultType = llvm::StructType::get(EltTy, EltTy, NULL); diff --git a/lib/CodeGen/CodeGenTypes.h b/lib/CodeGen/CodeGenTypes.h index 11fd76fb19a..452375f374c 100644 --- a/lib/CodeGen/CodeGenTypes.h +++ b/lib/CodeGen/CodeGenTypes.h @@ -60,14 +60,17 @@ namespace CodeGen { class CodeGenTypes { public: // Some of this stuff should probably be left on the CGM. + CodeGenModule &CGM; ASTContext &Context; - const TargetInfo &Target; llvm::Module &TheModule; const llvm::DataLayout &TheDataLayout; - const ABIInfo &TheABIInfo; + const TargetInfo &Target; CGCXXABI &TheCXXABI; const CodeGenOptions &CodeGenOpts; - CodeGenModule &CGM; + + // This should not be moved earlier, since its initialization depends on some + // of the previous reference members being already initialized + const ABIInfo &TheABIInfo; private: /// The opaque type map for Objective-C interfaces. All direct @@ -107,14 +110,14 @@ class CodeGenTypes { llvm::DenseMap TypeCache; public: - CodeGenTypes(CodeGenModule &CGM); + CodeGenTypes(CodeGenModule &cgm); ~CodeGenTypes(); const llvm::DataLayout &getDataLayout() const { return TheDataLayout; } - const TargetInfo &getTarget() const { return Target; } ASTContext &getContext() const { return Context; } const ABIInfo &getABIInfo() const { return TheABIInfo; } const CodeGenOptions &getCodeGenOpts() const { return CodeGenOpts; } + const TargetInfo &getTarget() const { return Target; } CGCXXABI &getCXXABI() const { return TheCXXABI; } llvm::LLVMContext &getLLVMContext() { return TheModule.getContext(); } diff --git a/lib/CodeGen/ItaniumCXXABI.cpp b/lib/CodeGen/ItaniumCXXABI.cpp index e25d422d236..e117e2867bd 100644 --- a/lib/CodeGen/ItaniumCXXABI.cpp +++ b/lib/CodeGen/ItaniumCXXABI.cpp @@ -41,6 +41,20 @@ class ItaniumCXXABI : public CodeGen::CGCXXABI { ItaniumCXXABI(CodeGen::CodeGenModule &CGM, bool IsARM = false) : CGCXXABI(CGM), IsARM(IsARM) { } + bool isReturnTypeIndirect(const CXXRecordDecl *RD) const { + // Structures with either a non-trivial destructor or a non-trivial + // copy constructor are always indirect. + return !RD->hasTrivialDestructor() || RD->hasNonTrivialCopyConstructor(); + } + + RecordArgABI getRecordArgABI(const CXXRecordDecl *RD) const { + // Structures with either a non-trivial destructor or a non-trivial + // copy constructor are always indirect. + if (!RD->hasTrivialDestructor() || RD->hasNonTrivialCopyConstructor()) + return RAA_Indirect; + return RAA_Default; + } + bool isZeroInitializable(const MemberPointerType *MPT); llvm::Type *ConvertMemberPointerType(const MemberPointerType *MPT); @@ -130,8 +144,16 @@ class ItaniumCXXABI : public CodeGen::CGCXXABI { void EmitGuardedInit(CodeGenFunction &CGF, const VarDecl &D, llvm::GlobalVariable *DeclPtr, bool PerformInit); - void registerGlobalDtor(CodeGenFunction &CGF, llvm::Constant *dtor, - llvm::Constant *addr); + void registerGlobalDtor(CodeGenFunction &CGF, const VarDecl &D, + llvm::Constant *dtor, llvm::Constant *addr); + + llvm::Function *getOrCreateThreadLocalWrapper(const VarDecl *VD, + llvm::GlobalVariable *Var); + void EmitThreadLocalInitFuncs( + llvm::ArrayRef > Decls, + llvm::Function *InitFunc); + LValue EmitThreadLocalDeclRefExpr(CodeGenFunction &CGF, + const DeclRefExpr *DRE); }; class ARMCXXABI : public ItaniumCXXABI { @@ -177,7 +199,7 @@ class ARMCXXABI : public ItaniumCXXABI { } CodeGen::CGCXXABI *CodeGen::CreateItaniumCXXABI(CodeGenModule &CGM) { - switch (CGM.getContext().getTargetInfo().getCXXABI().getKind()) { + switch (CGM.getTarget().getCXXABI().getKind()) { // For IR-generation purposes, there's no significant difference // between the ARM and iOS ABIs. case TargetCXXABI::GenericARM: @@ -1042,10 +1064,10 @@ void ItaniumCXXABI::EmitGuardedInit(CodeGenFunction &CGF, bool shouldPerformInit) { CGBuilderTy &Builder = CGF.Builder; - // We only need to use thread-safe statics for local variables; + // We only need to use thread-safe statics for local non-TLS variables; // global initialization is always single-threaded. - bool threadsafe = - (getContext().getLangOpts().ThreadsafeStatics && D.isLocalVarDecl()); + bool threadsafe = getContext().getLangOpts().ThreadsafeStatics && + D.isLocalVarDecl() && !D.getTLSKind(); // If we have a global variable with internal linkage and thread-safe statics // are disabled, we can just let the guard variable be of type i8. @@ -1080,6 +1102,8 @@ void ItaniumCXXABI::EmitGuardedInit(CodeGenFunction &CGF, llvm::ConstantInt::get(guardTy, 0), guardName.str()); guard->setVisibility(var->getVisibility()); + // If the variable is thread-local, so is its guard variable. + guard->setThreadLocalMode(var->getThreadLocalMode()); CGM.setStaticLocalDeclGuardAddress(&D, guard); } @@ -1180,7 +1204,14 @@ void ItaniumCXXABI::EmitGuardedInit(CodeGenFunction &CGF, /// Register a global destructor using __cxa_atexit. static void emitGlobalDtorWithCXAAtExit(CodeGenFunction &CGF, llvm::Constant *dtor, - llvm::Constant *addr) { + llvm::Constant *addr, + bool TLS) { + const char *Name = "__cxa_atexit"; + if (TLS) { + const llvm::Triple &T = CGF.getTarget().getTriple(); + Name = T.isMacOSX() ? "_tlv_atexit" : "__cxa_thread_atexit"; + } + // 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. @@ -1193,8 +1224,7 @@ static void emitGlobalDtorWithCXAAtExit(CodeGenFunction &CGF, llvm::FunctionType::get(CGF.IntTy, paramTys, false); // Fetch the actual function. - llvm::Constant *atexit = - CGF.CGM.CreateRuntimeFunction(atexitTy, "__cxa_atexit"); + llvm::Constant *atexit = CGF.CGM.CreateRuntimeFunction(atexitTy, Name); if (llvm::Function *fn = dyn_cast(atexit)) fn->setDoesNotThrow(); @@ -1212,12 +1242,15 @@ static void emitGlobalDtorWithCXAAtExit(CodeGenFunction &CGF, /// Register a global destructor as best as we know how. void ItaniumCXXABI::registerGlobalDtor(CodeGenFunction &CGF, + const VarDecl &D, llvm::Constant *dtor, llvm::Constant *addr) { // Use __cxa_atexit if available. - if (CGM.getCodeGenOpts().CXAAtExit) { - return emitGlobalDtorWithCXAAtExit(CGF, dtor, addr); - } + if (CGM.getCodeGenOpts().CXAAtExit) + return emitGlobalDtorWithCXAAtExit(CGF, dtor, addr, D.getTLSKind()); + + if (D.getTLSKind()) + CGM.ErrorUnsupported(&D, "non-trivial TLS destruction"); // In Apple kexts, we want to add a global destructor entry. // FIXME: shouldn't this be guarded by some variable? @@ -1228,3 +1261,138 @@ void ItaniumCXXABI::registerGlobalDtor(CodeGenFunction &CGF, CGF.registerGlobalDtorWithAtExit(dtor, addr); } + +/// Get the appropriate linkage for the wrapper function. This is essentially +/// the weak form of the variable's linkage; every translation unit which wneeds +/// the wrapper emits a copy, and we want the linker to merge them. +static llvm::GlobalValue::LinkageTypes getThreadLocalWrapperLinkage( + llvm::GlobalValue::LinkageTypes VarLinkage) { + if (llvm::GlobalValue::isLinkerPrivateLinkage(VarLinkage)) + return llvm::GlobalValue::LinkerPrivateWeakLinkage; + // For internal linkage variables, we don't need an external or weak wrapper. + if (llvm::GlobalValue::isLocalLinkage(VarLinkage)) + return VarLinkage; + return llvm::GlobalValue::WeakODRLinkage; +} + +llvm::Function * +ItaniumCXXABI::getOrCreateThreadLocalWrapper(const VarDecl *VD, + llvm::GlobalVariable *Var) { + // Mangle the name for the thread_local wrapper function. + SmallString<256> WrapperName; + { + llvm::raw_svector_ostream Out(WrapperName); + getMangleContext().mangleItaniumThreadLocalWrapper(VD, Out); + Out.flush(); + } + + if (llvm::Value *V = Var->getParent()->getNamedValue(WrapperName)) + return cast(V); + + llvm::Type *RetTy = Var->getType(); + if (VD->getType()->isReferenceType()) + RetTy = RetTy->getPointerElementType(); + + llvm::FunctionType *FnTy = llvm::FunctionType::get(RetTy, false); + llvm::Function *Wrapper = llvm::Function::Create( + FnTy, getThreadLocalWrapperLinkage(Var->getLinkage()), WrapperName.str(), + &CGM.getModule()); + // Always resolve references to the wrapper at link time. + Wrapper->setVisibility(llvm::GlobalValue::HiddenVisibility); + return Wrapper; +} + +void ItaniumCXXABI::EmitThreadLocalInitFuncs( + llvm::ArrayRef > Decls, + llvm::Function *InitFunc) { + for (unsigned I = 0, N = Decls.size(); I != N; ++I) { + const VarDecl *VD = Decls[I].first; + llvm::GlobalVariable *Var = Decls[I].second; + + // Mangle the name for the thread_local initialization function. + SmallString<256> InitFnName; + { + llvm::raw_svector_ostream Out(InitFnName); + getMangleContext().mangleItaniumThreadLocalInit(VD, Out); + Out.flush(); + } + + // If we have a definition for the variable, emit the initialization + // function as an alias to the global Init function (if any). Otherwise, + // produce a declaration of the initialization function. + llvm::GlobalValue *Init = 0; + bool InitIsInitFunc = false; + if (VD->hasDefinition()) { + InitIsInitFunc = true; + if (InitFunc) + Init = + new llvm::GlobalAlias(InitFunc->getType(), Var->getLinkage(), + InitFnName.str(), InitFunc, &CGM.getModule()); + } else { + // Emit a weak global function referring to the initialization function. + // This function will not exist if the TU defining the thread_local + // variable in question does not need any dynamic initialization for + // its thread_local variables. + llvm::FunctionType *FnTy = llvm::FunctionType::get(CGM.VoidTy, false); + Init = llvm::Function::Create( + FnTy, llvm::GlobalVariable::ExternalWeakLinkage, InitFnName.str(), + &CGM.getModule()); + } + + if (Init) + Init->setVisibility(Var->getVisibility()); + + llvm::Function *Wrapper = getOrCreateThreadLocalWrapper(VD, Var); + llvm::LLVMContext &Context = CGM.getModule().getContext(); + llvm::BasicBlock *Entry = llvm::BasicBlock::Create(Context, "", Wrapper); + CGBuilderTy Builder(Entry); + if (InitIsInitFunc) { + if (Init) + Builder.CreateCall(Init); + } else { + // Don't know whether we have an init function. Call it if it exists. + llvm::Value *Have = Builder.CreateIsNotNull(Init); + llvm::BasicBlock *InitBB = llvm::BasicBlock::Create(Context, "", Wrapper); + llvm::BasicBlock *ExitBB = llvm::BasicBlock::Create(Context, "", Wrapper); + Builder.CreateCondBr(Have, InitBB, ExitBB); + + Builder.SetInsertPoint(InitBB); + Builder.CreateCall(Init); + Builder.CreateBr(ExitBB); + + Builder.SetInsertPoint(ExitBB); + } + + // For a reference, the result of the wrapper function is a pointer to + // the referenced object. + llvm::Value *Val = Var; + if (VD->getType()->isReferenceType()) { + llvm::LoadInst *LI = Builder.CreateLoad(Val); + LI->setAlignment(CGM.getContext().getDeclAlign(VD).getQuantity()); + Val = LI; + } + + Builder.CreateRet(Val); + } +} + +LValue ItaniumCXXABI::EmitThreadLocalDeclRefExpr(CodeGenFunction &CGF, + const DeclRefExpr *DRE) { + const VarDecl *VD = cast(DRE->getDecl()); + QualType T = VD->getType(); + llvm::Type *Ty = CGF.getTypes().ConvertTypeForMem(T); + llvm::Value *Val = CGF.CGM.GetAddrOfGlobalVar(VD, Ty); + llvm::Function *Wrapper = + getOrCreateThreadLocalWrapper(VD, cast(Val)); + + Val = CGF.Builder.CreateCall(Wrapper); + + LValue LV; + if (VD->getType()->isReferenceType()) + LV = CGF.MakeNaturalAlignAddrLValue(Val, T); + else + LV = CGF.MakeAddrLValue(Val, DRE->getType(), + CGF.getContext().getDeclAlign(VD)); + // FIXME: need setObjCGCLValueClass? + return LV; +} diff --git a/lib/CodeGen/MicrosoftCXXABI.cpp b/lib/CodeGen/MicrosoftCXXABI.cpp index 00b15c9a49c..f5242eaaa2f 100644 --- a/lib/CodeGen/MicrosoftCXXABI.cpp +++ b/lib/CodeGen/MicrosoftCXXABI.cpp @@ -28,6 +28,17 @@ class MicrosoftCXXABI : public CGCXXABI { public: MicrosoftCXXABI(CodeGenModule &CGM) : CGCXXABI(CGM) {} + bool isReturnTypeIndirect(const CXXRecordDecl *RD) const { + // Structures that are not C++03 PODs are always indirect. + return !RD->isPOD(); + } + + RecordArgABI getRecordArgABI(const CXXRecordDecl *RD) const { + if (RD->hasNonTrivialCopyConstructor()) + return RAA_DirectInMemory; + return RAA_Default; + } + StringRef GetPureVirtualCallName() { return "_purecall"; } // No known support for deleted functions in MSVC yet, so this choice is // arbitrary. @@ -111,21 +122,46 @@ class MicrosoftCXXABI : public CGCXXABI { static bool needThisReturn(GlobalDecl GD); private: - llvm::Constant *getSimpleNullMemberPointer(const MemberPointerType *MPT); - - llvm::Constant *getZeroPtrDiff() { - return llvm::ConstantInt::get(CGM.PtrDiffTy, 0); + llvm::Constant *getZeroInt() { + return llvm::ConstantInt::get(CGM.IntTy, 0); } - llvm::Constant *getAllOnesPtrDiff() { - return llvm::Constant::getAllOnesValue(CGM.PtrDiffTy); + llvm::Constant *getAllOnesInt() { + return llvm::Constant::getAllOnesValue(CGM.IntTy); } + void + GetNullMemberPointerFields(const MemberPointerType *MPT, + llvm::SmallVectorImpl &fields); + + llvm::Value *AdjustVirtualBase(CodeGenFunction &CGF, const CXXRecordDecl *RD, + llvm::Value *Base, + llvm::Value *VirtualBaseAdjustmentOffset, + llvm::Value *VBPtrOffset /* optional */); + + /// \brief Emits a full member pointer with the fields common to data and + /// function member pointers. + llvm::Constant *EmitFullMemberPointer(llvm::Constant *FirstField, + bool IsMemberFunction, + const CXXRecordDecl *RD); + public: + virtual llvm::Type *ConvertMemberPointerType(const MemberPointerType *MPT); + + virtual bool isZeroInitializable(const MemberPointerType *MPT); + virtual llvm::Constant *EmitNullMemberPointer(const MemberPointerType *MPT); virtual llvm::Constant *EmitMemberDataPointer(const MemberPointerType *MPT, CharUnits offset); + virtual llvm::Constant *EmitMemberPointer(const CXXMethodDecl *MD); + virtual llvm::Constant *EmitMemberPointer(const APValue &MP, QualType MPT); + + virtual llvm::Value *EmitMemberPointerComparison(CodeGenFunction &CGF, + llvm::Value *L, + llvm::Value *R, + const MemberPointerType *MPT, + bool Inequality); virtual llvm::Value *EmitMemberPointerIsNotNull(CodeGenFunction &CGF, llvm::Value *MemPtr, @@ -136,6 +172,12 @@ class MicrosoftCXXABI : public CGCXXABI { llvm::Value *MemPtr, const MemberPointerType *MPT); + virtual llvm::Value * + EmitLoadOfMemberFunctionPointer(CodeGenFunction &CGF, + llvm::Value *&This, + llvm::Value *MemPtr, + const MemberPointerType *MPT); + }; } @@ -375,49 +417,259 @@ void MicrosoftCXXABI::EmitGuardedInit(CodeGenFunction &CGF, const VarDecl &D, // Not sure whether we want thread-safe static local variables as VS // doesn't make them thread-safe. + if (D.getTLSKind()) + CGM.ErrorUnsupported(&D, "dynamic TLS initialization"); + // Emit the initializer and add a global destructor if appropriate. CGF.EmitCXXGlobalVarDeclInit(D, DeclPtr, PerformInit); } -// Returns true for member pointer types that we know how to represent with a -// simple ptrdiff_t. Currently we only know how to emit, test, and load member -// data pointers for complete single inheritance classes. -static bool isSimpleMemberPointer(const MemberPointerType *MPT) { +// Member pointer helpers. +static bool hasVBPtrOffsetField(MSInheritanceModel Inheritance) { + return Inheritance == MSIM_Unspecified; +} + +static bool hasOnlyOneField(MSInheritanceModel Inheritance) { + return Inheritance <= MSIM_SinglePolymorphic; +} + +// Only member pointers to functions need a this adjustment, since it can be +// combined with the field offset for data pointers. +static bool hasNonVirtualBaseAdjustmentField(bool IsMemberFunction, + MSInheritanceModel Inheritance) { + return (IsMemberFunction && Inheritance >= MSIM_Multiple); +} + +static bool hasVirtualBaseAdjustmentField(MSInheritanceModel Inheritance) { + return Inheritance >= MSIM_Virtual; +} + +// Use zero for the field offset of a null data member pointer if we can +// guarantee that zero is not a valid field offset, or if the member pointer has +// multiple fields. Polymorphic classes have a vfptr at offset zero, so we can +// use zero for null. If there are multiple fields, we can use zero even if it +// is a valid field offset because null-ness testing will check the other +// fields. +static bool nullFieldOffsetIsZero(MSInheritanceModel Inheritance) { + return Inheritance != MSIM_Multiple && Inheritance != MSIM_Single; +} + +bool MicrosoftCXXABI::isZeroInitializable(const MemberPointerType *MPT) { + // Null-ness for function memptrs only depends on the first field, which is + // the function pointer. The rest don't matter, so we can zero initialize. + if (MPT->isMemberFunctionPointer()) + return true; + + // The virtual base adjustment field is always -1 for null, so if we have one + // we can't zero initialize. The field offset is sometimes also -1 if 0 is a + // valid field offset. const CXXRecordDecl *RD = MPT->getClass()->getAsCXXRecordDecl(); - return (MPT->isMemberDataPointer() && - !MPT->getClass()->isIncompleteType() && - RD->getNumVBases() == 0); + MSInheritanceModel Inheritance = RD->getMSInheritanceModel(); + return (!hasVirtualBaseAdjustmentField(Inheritance) && + nullFieldOffsetIsZero(Inheritance)); } -llvm::Constant * -MicrosoftCXXABI::getSimpleNullMemberPointer(const MemberPointerType *MPT) { - if (isSimpleMemberPointer(MPT)) { - const CXXRecordDecl *RD = MPT->getClass()->getAsCXXRecordDecl(); - // A null member data pointer is represented as -1 if the class is not - // polymorphic, and 0 otherwise. - if (RD->isPolymorphic()) - return getZeroPtrDiff(); - return getAllOnesPtrDiff(); +llvm::Type * +MicrosoftCXXABI::ConvertMemberPointerType(const MemberPointerType *MPT) { + const CXXRecordDecl *RD = MPT->getClass()->getAsCXXRecordDecl(); + MSInheritanceModel Inheritance = RD->getMSInheritanceModel(); + llvm::SmallVector fields; + if (MPT->isMemberFunctionPointer()) + fields.push_back(CGM.VoidPtrTy); // FunctionPointerOrVirtualThunk + else + fields.push_back(CGM.IntTy); // FieldOffset + + if (hasNonVirtualBaseAdjustmentField(MPT->isMemberFunctionPointer(), + Inheritance)) + fields.push_back(CGM.IntTy); + if (hasVBPtrOffsetField(Inheritance)) + fields.push_back(CGM.IntTy); + if (hasVirtualBaseAdjustmentField(Inheritance)) + fields.push_back(CGM.IntTy); // VirtualBaseAdjustmentOffset + + if (fields.size() == 1) + return fields[0]; + return llvm::StructType::get(CGM.getLLVMContext(), fields); +} + +void MicrosoftCXXABI:: +GetNullMemberPointerFields(const MemberPointerType *MPT, + llvm::SmallVectorImpl &fields) { + assert(fields.empty()); + const CXXRecordDecl *RD = MPT->getClass()->getAsCXXRecordDecl(); + MSInheritanceModel Inheritance = RD->getMSInheritanceModel(); + if (MPT->isMemberFunctionPointer()) { + // FunctionPointerOrVirtualThunk + fields.push_back(llvm::Constant::getNullValue(CGM.VoidPtrTy)); + } else { + if (nullFieldOffsetIsZero(Inheritance)) + fields.push_back(getZeroInt()); // FieldOffset + else + fields.push_back(getAllOnesInt()); // FieldOffset } - return GetBogusMemberPointer(QualType(MPT, 0)); + + if (hasNonVirtualBaseAdjustmentField(MPT->isMemberFunctionPointer(), + Inheritance)) + fields.push_back(getZeroInt()); + if (hasVBPtrOffsetField(Inheritance)) + fields.push_back(getZeroInt()); + if (hasVirtualBaseAdjustmentField(Inheritance)) + fields.push_back(getAllOnesInt()); } llvm::Constant * MicrosoftCXXABI::EmitNullMemberPointer(const MemberPointerType *MPT) { - if (isSimpleMemberPointer(MPT)) - return getSimpleNullMemberPointer(MPT); - // FIXME: Implement function member pointers. - return GetBogusMemberPointer(QualType(MPT, 0)); + llvm::SmallVector fields; + GetNullMemberPointerFields(MPT, fields); + if (fields.size() == 1) + return fields[0]; + llvm::Constant *Res = llvm::ConstantStruct::getAnon(fields); + assert(Res->getType() == ConvertMemberPointerType(MPT)); + return Res; +} + +llvm::Constant * +MicrosoftCXXABI::EmitFullMemberPointer(llvm::Constant *FirstField, + bool IsMemberFunction, + const CXXRecordDecl *RD) +{ + MSInheritanceModel Inheritance = RD->getMSInheritanceModel(); + + // Single inheritance class member pointer are represented as scalars instead + // of aggregates. + if (hasOnlyOneField(Inheritance)) + return FirstField; + + llvm::SmallVector fields; + fields.push_back(FirstField); + + if (hasNonVirtualBaseAdjustmentField(IsMemberFunction, Inheritance)) + fields.push_back(getZeroInt()); + + if (hasVBPtrOffsetField(Inheritance)) { + int64_t VBPtrOffset = + getContext().getASTRecordLayout(RD).getVBPtrOffset().getQuantity(); + if (VBPtrOffset == -1) + VBPtrOffset = 0; + fields.push_back(llvm::ConstantInt::get(CGM.IntTy, VBPtrOffset)); + } + + // The rest of the fields are adjusted by conversions to a more derived class. + if (hasVirtualBaseAdjustmentField(Inheritance)) + fields.push_back(getZeroInt()); + + return llvm::ConstantStruct::getAnon(fields); } llvm::Constant * MicrosoftCXXABI::EmitMemberDataPointer(const MemberPointerType *MPT, CharUnits offset) { - // Member data pointers are plain offsets when no virtual bases are involved. - if (isSimpleMemberPointer(MPT)) - return llvm::ConstantInt::get(CGM.PtrDiffTy, offset.getQuantity()); - // FIXME: Implement member pointers other inheritance models. - return GetBogusMemberPointer(QualType(MPT, 0)); + const CXXRecordDecl *RD = MPT->getClass()->getAsCXXRecordDecl(); + llvm::Constant *FirstField = + llvm::ConstantInt::get(CGM.IntTy, offset.getQuantity()); + return EmitFullMemberPointer(FirstField, /*IsMemberFunction=*/false, RD); +} + +llvm::Constant * +MicrosoftCXXABI::EmitMemberPointer(const CXXMethodDecl *MD) { + assert(MD->isInstance() && "Member function must not be static!"); + MD = MD->getCanonicalDecl(); + const CXXRecordDecl *RD = MD->getParent(); + CodeGenTypes &Types = CGM.getTypes(); + + llvm::Constant *FirstField; + if (MD->isVirtual()) { + // FIXME: We have to instantiate a thunk that loads the vftable and jumps to + // the right offset. + FirstField = llvm::Constant::getNullValue(CGM.VoidPtrTy); + } else { + const FunctionProtoType *FPT = MD->getType()->castAs(); + llvm::Type *Ty; + // Check whether the function has a computable LLVM signature. + if (Types.isFuncTypeConvertible(FPT)) { + // The function has a computable LLVM signature; use the correct type. + Ty = Types.GetFunctionType(Types.arrangeCXXMethodDeclaration(MD)); + } else { + // Use an arbitrary non-function type to tell GetAddrOfFunction that the + // function type is incomplete. + Ty = CGM.PtrDiffTy; + } + FirstField = CGM.GetAddrOfFunction(MD, Ty); + FirstField = llvm::ConstantExpr::getBitCast(FirstField, CGM.VoidPtrTy); + } + + // The rest of the fields are common with data member pointers. + return EmitFullMemberPointer(FirstField, /*IsMemberFunction=*/true, RD); +} + +llvm::Constant * +MicrosoftCXXABI::EmitMemberPointer(const APValue &MP, QualType MPT) { + // FIXME PR15875: Implement member pointer conversions for Constants. + const CXXRecordDecl *RD = MPT->castAs()->getClass()->getAsCXXRecordDecl(); + return EmitFullMemberPointer(llvm::Constant::getNullValue(CGM.VoidPtrTy), + /*IsMemberFunction=*/true, RD); +} + +/// Member pointers are the same if they're either bitwise identical *or* both +/// null. Null-ness for function members is determined by the first field, +/// while for data member pointers we must compare all fields. +llvm::Value * +MicrosoftCXXABI::EmitMemberPointerComparison(CodeGenFunction &CGF, + llvm::Value *L, + llvm::Value *R, + const MemberPointerType *MPT, + bool Inequality) { + CGBuilderTy &Builder = CGF.Builder; + + // Handle != comparisons by switching the sense of all boolean operations. + llvm::ICmpInst::Predicate Eq; + llvm::Instruction::BinaryOps And, Or; + if (Inequality) { + Eq = llvm::ICmpInst::ICMP_NE; + And = llvm::Instruction::Or; + Or = llvm::Instruction::And; + } else { + Eq = llvm::ICmpInst::ICMP_EQ; + And = llvm::Instruction::And; + Or = llvm::Instruction::Or; + } + + // If this is a single field member pointer (single inheritance), this is a + // single icmp. + const CXXRecordDecl *RD = MPT->getClass()->getAsCXXRecordDecl(); + MSInheritanceModel Inheritance = RD->getMSInheritanceModel(); + if (hasOnlyOneField(Inheritance)) + return Builder.CreateICmp(Eq, L, R); + + // Compare the first field. + llvm::Value *L0 = Builder.CreateExtractValue(L, 0, "lhs.0"); + llvm::Value *R0 = Builder.CreateExtractValue(R, 0, "rhs.0"); + llvm::Value *Cmp0 = Builder.CreateICmp(Eq, L0, R0, "memptr.cmp.first"); + + // Compare everything other than the first field. + llvm::Value *Res = 0; + llvm::StructType *LType = cast(L->getType()); + for (unsigned I = 1, E = LType->getNumElements(); I != E; ++I) { + llvm::Value *LF = Builder.CreateExtractValue(L, I); + llvm::Value *RF = Builder.CreateExtractValue(R, I); + llvm::Value *Cmp = Builder.CreateICmp(Eq, LF, RF, "memptr.cmp.rest"); + if (Res) + Res = Builder.CreateBinOp(And, Res, Cmp); + else + Res = Cmp; + } + + // Check if the first field is 0 if this is a function pointer. + if (MPT->isMemberFunctionPointer()) { + // (l1 == r1 && ...) || l0 == 0 + llvm::Value *Zero = llvm::Constant::getNullValue(L0->getType()); + llvm::Value *IsZero = Builder.CreateICmp(Eq, L0, Zero, "memptr.cmp.iszero"); + Res = Builder.CreateBinOp(Or, Res, IsZero); + } + + // Combine the comparison of the first field, which must always be true for + // this comparison to succeeed. + return Builder.CreateBinOp(And, Res, Cmp0, "memptr.cmp"); } llvm::Value * @@ -425,16 +677,90 @@ MicrosoftCXXABI::EmitMemberPointerIsNotNull(CodeGenFunction &CGF, llvm::Value *MemPtr, const MemberPointerType *MPT) { CGBuilderTy &Builder = CGF.Builder; + llvm::SmallVector fields; + // We only need one field for member functions. + if (MPT->isMemberFunctionPointer()) + fields.push_back(llvm::Constant::getNullValue(CGM.VoidPtrTy)); + else + GetNullMemberPointerFields(MPT, fields); + assert(!fields.empty()); + llvm::Value *FirstField = MemPtr; + if (MemPtr->getType()->isStructTy()) + FirstField = Builder.CreateExtractValue(MemPtr, 0); + llvm::Value *Res = Builder.CreateICmpNE(FirstField, fields[0], "memptr.cmp0"); + + // For function member pointers, we only need to test the function pointer + // field. The other fields if any can be garbage. + if (MPT->isMemberFunctionPointer()) + return Res; + + // Otherwise, emit a series of compares and combine the results. + for (int I = 1, E = fields.size(); I < E; ++I) { + llvm::Value *Field = Builder.CreateExtractValue(MemPtr, I); + llvm::Value *Next = Builder.CreateICmpNE(Field, fields[I], "memptr.cmp"); + Res = Builder.CreateAnd(Res, Next, "memptr.tobool"); + } + return Res; +} - // For member data pointers, this is just a check against -1 or 0. - if (isSimpleMemberPointer(MPT)) { - llvm::Constant *Val = getSimpleNullMemberPointer(MPT); - return Builder.CreateICmpNE(MemPtr, Val, "memptr.tobool"); +// Returns an adjusted base cast to i8*, since we do more address arithmetic on +// it. +llvm::Value * +MicrosoftCXXABI::AdjustVirtualBase(CodeGenFunction &CGF, + const CXXRecordDecl *RD, llvm::Value *Base, + llvm::Value *VirtualBaseAdjustmentOffset, + llvm::Value *VBPtrOffset) { + CGBuilderTy &Builder = CGF.Builder; + Base = Builder.CreateBitCast(Base, CGM.Int8PtrTy); + llvm::BasicBlock *OriginalBB = 0; + llvm::BasicBlock *SkipAdjustBB = 0; + llvm::BasicBlock *VBaseAdjustBB = 0; + + // In the unspecified inheritance model, there might not be a vbtable at all, + // in which case we need to skip the virtual base lookup. If there is a + // vbtable, the first entry is a no-op entry that gives back the original + // base, so look for a virtual base adjustment offset of zero. + if (VBPtrOffset) { + OriginalBB = Builder.GetInsertBlock(); + VBaseAdjustBB = CGF.createBasicBlock("memptr.vadjust"); + SkipAdjustBB = CGF.createBasicBlock("memptr.skip_vadjust"); + llvm::Value *IsVirtual = + Builder.CreateICmpNE(VirtualBaseAdjustmentOffset, getZeroInt(), + "memptr.is_vbase"); + Builder.CreateCondBr(IsVirtual, VBaseAdjustBB, SkipAdjustBB); + CGF.EmitBlock(VBaseAdjustBB); } - // FIXME: Implement member pointers other inheritance models. - ErrorUnsupportedABI(CGF, "function member pointer tests"); - return GetBogusMemberPointer(QualType(MPT, 0)); + // If we weren't given a dynamic vbptr offset, RD should be complete and we'll + // know the vbptr offset. + if (!VBPtrOffset) { + CharUnits offs = getContext().getASTRecordLayout(RD).getVBPtrOffset(); + VBPtrOffset = llvm::ConstantInt::get(CGM.IntTy, offs.getQuantity()); + } + // Load the vbtable pointer from the vbtable offset in the instance. + llvm::Value *VBPtr = + Builder.CreateInBoundsGEP(Base, VBPtrOffset, "memptr.vbptr"); + llvm::Value *VBTable = + Builder.CreateBitCast(VBPtr, CGM.Int8PtrTy->getPointerTo(0)); + VBTable = Builder.CreateLoad(VBTable, "memptr.vbtable"); + // Load an i32 offset from the vb-table. + llvm::Value *VBaseOffs = + Builder.CreateInBoundsGEP(VBTable, VirtualBaseAdjustmentOffset); + VBaseOffs = Builder.CreateBitCast(VBaseOffs, CGM.Int32Ty->getPointerTo(0)); + VBaseOffs = Builder.CreateLoad(VBaseOffs, "memptr.vbase_offs"); + // Add it to VBPtr. GEP will sign extend the i32 value for us. + llvm::Value *AdjustedBase = Builder.CreateInBoundsGEP(VBPtr, VBaseOffs); + + // Merge control flow with the case where we didn't have to adjust. + if (VBaseAdjustBB) { + Builder.CreateBr(SkipAdjustBB); + CGF.EmitBlock(SkipAdjustBB); + llvm::PHINode *Phi = Builder.CreatePHI(CGM.Int8PtrTy, 2, "memptr.base"); + Phi->addIncoming(Base, OriginalBB); + Phi->addIncoming(AdjustedBase, VBaseAdjustBB); + return Phi; + } + return AdjustedBase; } llvm::Value * @@ -442,32 +768,90 @@ MicrosoftCXXABI::EmitMemberDataPointerAddress(CodeGenFunction &CGF, llvm::Value *Base, llvm::Value *MemPtr, const MemberPointerType *MPT) { + assert(MPT->isMemberDataPointer()); unsigned AS = Base->getType()->getPointerAddressSpace(); llvm::Type *PType = CGF.ConvertTypeForMem(MPT->getPointeeType())->getPointerTo(AS); CGBuilderTy &Builder = CGF.Builder; - - if (MPT->isMemberFunctionPointer()) { - ErrorUnsupportedABI(CGF, "function member pointer address"); - return llvm::Constant::getNullValue(PType); + const CXXRecordDecl *RD = MPT->getClass()->getAsCXXRecordDecl(); + MSInheritanceModel Inheritance = RD->getMSInheritanceModel(); + + // Extract the fields we need, regardless of model. We'll apply them if we + // have them. + llvm::Value *FieldOffset = MemPtr; + llvm::Value *VirtualBaseAdjustmentOffset = 0; + llvm::Value *VBPtrOffset = 0; + if (MemPtr->getType()->isStructTy()) { + // We need to extract values. + unsigned I = 0; + FieldOffset = Builder.CreateExtractValue(MemPtr, I++); + if (hasVBPtrOffsetField(Inheritance)) + VBPtrOffset = Builder.CreateExtractValue(MemPtr, I++); + if (hasVirtualBaseAdjustmentField(Inheritance)) + VirtualBaseAdjustmentOffset = Builder.CreateExtractValue(MemPtr, I++); } - llvm::Value *Addr; - if (isSimpleMemberPointer(MPT)) { - // Add the offset with GEP and i8*. - assert(MemPtr->getType() == CGM.PtrDiffTy); - Base = Builder.CreateBitCast(Base, Builder.getInt8Ty()->getPointerTo(AS)); - Addr = Builder.CreateInBoundsGEP(Base, MemPtr, "memptr.offset"); - } else { - ErrorUnsupportedABI(CGF, "non-scalar member pointers"); - return llvm::Constant::getNullValue(PType); + if (VirtualBaseAdjustmentOffset) { + Base = AdjustVirtualBase(CGF, RD, Base, VirtualBaseAdjustmentOffset, + VBPtrOffset); } + llvm::Value *Addr = + Builder.CreateInBoundsGEP(Base, FieldOffset, "memptr.offset"); // Cast the address to the appropriate pointer type, adopting the address // space of the base pointer. return Builder.CreateBitCast(Addr, PType); } +llvm::Value * +MicrosoftCXXABI::EmitLoadOfMemberFunctionPointer(CodeGenFunction &CGF, + llvm::Value *&This, + llvm::Value *MemPtr, + const MemberPointerType *MPT) { + assert(MPT->isMemberFunctionPointer()); + const FunctionProtoType *FPT = + MPT->getPointeeType()->castAs(); + const CXXRecordDecl *RD = MPT->getClass()->getAsCXXRecordDecl(); + llvm::FunctionType *FTy = + CGM.getTypes().GetFunctionType( + CGM.getTypes().arrangeCXXMethodType(RD, FPT)); + CGBuilderTy &Builder = CGF.Builder; + + MSInheritanceModel Inheritance = RD->getMSInheritanceModel(); + + // Extract the fields we need, regardless of model. We'll apply them if we + // have them. + llvm::Value *FunctionPointer = MemPtr; + llvm::Value *NonVirtualBaseAdjustment = NULL; + llvm::Value *VirtualBaseAdjustmentOffset = NULL; + llvm::Value *VBPtrOffset = NULL; + if (MemPtr->getType()->isStructTy()) { + // We need to extract values. + unsigned I = 0; + FunctionPointer = Builder.CreateExtractValue(MemPtr, I++); + if (hasNonVirtualBaseAdjustmentField(MPT, Inheritance)) + NonVirtualBaseAdjustment = Builder.CreateExtractValue(MemPtr, I++); + if (hasVBPtrOffsetField(Inheritance)) + VBPtrOffset = Builder.CreateExtractValue(MemPtr, I++); + if (hasVirtualBaseAdjustmentField(Inheritance)) + VirtualBaseAdjustmentOffset = Builder.CreateExtractValue(MemPtr, I++); + } + + if (VirtualBaseAdjustmentOffset) { + This = AdjustVirtualBase(CGF, RD, This, VirtualBaseAdjustmentOffset, + VBPtrOffset); + } + + if (NonVirtualBaseAdjustment) { + // Apply the adjustment and cast back to the original struct type. + llvm::Value *Ptr = Builder.CreateBitCast(This, Builder.getInt8PtrTy()); + Ptr = Builder.CreateInBoundsGEP(Ptr, NonVirtualBaseAdjustment); + This = Builder.CreateBitCast(Ptr, This->getType(), "this.adjusted"); + } + + return Builder.CreateBitCast(FunctionPointer, FTy->getPointerTo()); +} + CGCXXABI *clang::CodeGen::CreateMicrosoftCXXABI(CodeGenModule &CGM) { return new MicrosoftCXXABI(CGM); } diff --git a/lib/CodeGen/ModuleBuilder.cpp b/lib/CodeGen/ModuleBuilder.cpp index d6e5f0673f8..69e5b323045 100644 --- a/lib/CodeGen/ModuleBuilder.cpp +++ b/lib/CodeGen/ModuleBuilder.cpp @@ -31,15 +31,13 @@ namespace { OwningPtr TD; ASTContext *Ctx; const CodeGenOptions CodeGenOpts; // Intentionally copied in. - const TargetOptions TargetOpts; // Intentionally copied in. protected: OwningPtr M; OwningPtr Builder; public: CodeGeneratorImpl(DiagnosticsEngine &diags, const std::string& ModuleName, - const CodeGenOptions &CGO, const TargetOptions &TO, - llvm::LLVMContext& C) - : Diags(diags), CodeGenOpts(CGO), TargetOpts(TO), + const CodeGenOptions &CGO, llvm::LLVMContext& C) + : Diags(diags), CodeGenOpts(CGO), M(new llvm::Module(ModuleName, C)) {} virtual ~CodeGeneratorImpl() {} @@ -58,8 +56,8 @@ namespace { M->setTargetTriple(Ctx->getTargetInfo().getTriple().getTriple()); M->setDataLayout(Ctx->getTargetInfo().getTargetDescription()); TD.reset(new llvm::DataLayout(Ctx->getTargetInfo().getTargetDescription())); - Builder.reset(new CodeGen::CodeGenModule(Context, CodeGenOpts, TargetOpts, - *M, *TD, Diags)); + Builder.reset(new CodeGen::CodeGenModule(Context, CodeGenOpts, *M, *TD, + Diags)); } virtual void HandleCXXStaticMemberVarInstantiation(VarDecl *VD) { @@ -125,7 +123,7 @@ void CodeGenerator::anchor() { } CodeGenerator *clang::CreateLLVMCodeGen(DiagnosticsEngine &Diags, const std::string& ModuleName, const CodeGenOptions &CGO, - const TargetOptions &TO, + const TargetOptions &/*TO*/, llvm::LLVMContext& C) { - return new CodeGeneratorImpl(Diags, ModuleName, CGO, TO, C); + return new CodeGeneratorImpl(Diags, ModuleName, CGO, C); } diff --git a/lib/CodeGen/TargetInfo.cpp b/lib/CodeGen/TargetInfo.cpp index 7cc63b7db15..32b27b3172a 100644 --- a/lib/CodeGen/TargetInfo.cpp +++ b/lib/CodeGen/TargetInfo.cpp @@ -14,6 +14,7 @@ #include "TargetInfo.h" #include "ABIInfo.h" +#include "CGCXXABI.h" #include "CodeGenFunction.h" #include "clang/AST/RecordLayout.h" #include "clang/Frontend/CodeGenOptions.h" @@ -43,6 +44,37 @@ static bool isAggregateTypeForABI(QualType T) { ABIInfo::~ABIInfo() {} +static bool isRecordReturnIndirect(const RecordType *RT, CodeGen::CodeGenTypes &CGT) { + const CXXRecordDecl *RD = dyn_cast(RT->getDecl()); + if (!RD) + return false; + return CGT.CGM.getCXXABI().isReturnTypeIndirect(RD); +} + + +static bool isRecordReturnIndirect(QualType T, CodeGen::CodeGenTypes &CGT) { + const RecordType *RT = T->getAs(); + if (!RT) + return false; + return isRecordReturnIndirect(RT, CGT); +} + +static CGCXXABI::RecordArgABI getRecordArgABI(const RecordType *RT, + CodeGen::CodeGenTypes &CGT) { + const CXXRecordDecl *RD = dyn_cast(RT->getDecl()); + if (!RD) + return CGCXXABI::RAA_Default; + return CGT.CGM.getCXXABI().getRecordArgABI(RD); +} + +static CGCXXABI::RecordArgABI getRecordArgABI(QualType T, + CodeGen::CodeGenTypes &CGT) { + const RecordType *RT = T->getAs(); + if (!RT) + return CGCXXABI::RAA_Default; + return getRecordArgABI(RT, CGT); +} + ASTContext &ABIInfo::getContext() const { return CGT.getContext(); } @@ -55,6 +87,9 @@ const llvm::DataLayout &ABIInfo::getDataLayout() const { return CGT.getDataLayout(); } +const TargetInfo &ABIInfo::getTarget() const { + return CGT.getTarget(); +} void ABIArgInfo::dump() const { raw_ostream &OS = llvm::errs(); @@ -167,27 +202,6 @@ static bool isEmptyRecord(ASTContext &Context, QualType T, bool AllowArrays) { return true; } -/// hasNonTrivialDestructorOrCopyConstructor - Determine if a type has either -/// a non-trivial destructor or a non-trivial copy constructor. -static bool hasNonTrivialDestructorOrCopyConstructor(const RecordType *RT) { - const CXXRecordDecl *RD = dyn_cast(RT->getDecl()); - if (!RD) - return false; - - return !RD->hasTrivialDestructor() || RD->hasNonTrivialCopyConstructor(); -} - -/// isRecordWithNonTrivialDestructorOrCopyConstructor - Determine if a type is -/// a record type with either a non-trivial destructor or a non-trivial copy -/// constructor. -static bool isRecordWithNonTrivialDestructorOrCopyConstructor(QualType T) { - const RecordType *RT = T->getAs(); - if (!RT) - return false; - - return hasNonTrivialDestructorOrCopyConstructor(RT); -} - /// isSingleElementStruct - Determine if a structure is a "single /// element struct", i.e. it has exactly one non-empty field or /// exactly one field which is itself a single element @@ -367,7 +381,7 @@ ABIArgInfo DefaultABIInfo::classifyArgumentType(QualType Ty) const { if (isAggregateTypeForABI(Ty)) { // Records with non trivial destructors/constructors should not be passed // by value. - if (isRecordWithNonTrivialDestructorOrCopyConstructor(Ty)) + if (isRecordReturnIndirect(Ty, CGT)) return ABIArgInfo::getIndirect(0, /*ByVal=*/false); return ABIArgInfo::getIndirect(0); @@ -398,6 +412,9 @@ ABIArgInfo DefaultABIInfo::classifyReturnType(QualType RetTy) const { //===----------------------------------------------------------------------===// // le32/PNaCl bitcode ABI Implementation +// +// This is a simplified version of the x86_32 ABI. Arguments and return values +// are always passed on the stack. //===----------------------------------------------------------------------===// class PNaClABIInfo : public ABIInfo { @@ -405,7 +422,7 @@ class PNaClABIInfo : public ABIInfo { PNaClABIInfo(CodeGen::CodeGenTypes &CGT) : ABIInfo(CGT) {} ABIArgInfo classifyReturnType(QualType RetTy) const; - ABIArgInfo classifyArgumentType(QualType RetTy, unsigned &FreeRegs) const; + ABIArgInfo classifyArgumentType(QualType RetTy) const; virtual void computeInfo(CGFunctionInfo &FI) const; virtual llvm::Value *EmitVAArg(llvm::Value *VAListAddr, QualType Ty, @@ -421,13 +438,9 @@ class PNaClTargetCodeGenInfo : public TargetCodeGenInfo { void PNaClABIInfo::computeInfo(CGFunctionInfo &FI) const { FI.getReturnInfo() = classifyReturnType(FI.getReturnType()); - // Obtain the initial number of registers available for passing integers - // from the function's regparm attribute. - unsigned FreeRegs = FI.getHasRegParm() ? FI.getRegParm() : 0; - for (CGFunctionInfo::arg_iterator it = FI.arg_begin(), ie = FI.arg_end(); it != ie; ++it) - it->info = classifyArgumentType(it->type, FreeRegs); + it->info = classifyArgumentType(it->type); } llvm::Value *PNaClABIInfo::EmitVAArg(llvm::Value *VAListAddr, QualType Ty, @@ -435,42 +448,22 @@ llvm::Value *PNaClABIInfo::EmitVAArg(llvm::Value *VAListAddr, QualType Ty, return 0; } -// \brief Classify argument of given type \p Ty. \p FreeRegs is the number of -// registers available for passing arguments - it can be updated by this -// method. -ABIArgInfo PNaClABIInfo::classifyArgumentType(QualType Ty, - unsigned &FreeRegs) const { +/// \brief Classify argument of given type \p Ty. +ABIArgInfo PNaClABIInfo::classifyArgumentType(QualType Ty) const { if (isAggregateTypeForABI(Ty)) { - // In the PNaCl ABI we always pass records/structures on the stack. The - // byval attribute can be used if the record doesn't have non-trivial - // constructors/destructors. - FreeRegs = 0; - if (isRecordWithNonTrivialDestructorOrCopyConstructor(Ty)) - return ABIArgInfo::getIndirect(0, /*ByVal=*/false); + if (CGCXXABI::RecordArgABI RAA = getRecordArgABI(Ty, CGT)) + return ABIArgInfo::getIndirect(0, RAA == CGCXXABI::RAA_DirectInMemory); return ABIArgInfo::getIndirect(0); - } - - // Treat an enum type as its underlying type. - if (const EnumType *EnumTy = Ty->getAs()) + } else if (const EnumType *EnumTy = Ty->getAs()) { + // Treat an enum type as its underlying type. Ty = EnumTy->getDecl()->getIntegerType(); + } else if (Ty->isFloatingType()) { + // Floating-point types don't go inreg. + return ABIArgInfo::getDirect(); + } - ABIArgInfo BaseInfo = (Ty->isPromotableIntegerType() ? + return (Ty->isPromotableIntegerType() ? ABIArgInfo::getExtend() : ABIArgInfo::getDirect()); - - // Figure out how many of the free registers can be occupied by this type. - // regparm registers are 32-bit. - unsigned NumRegsRequired = (getContext().getTypeSize(Ty) + 31) / 32; - if (NumRegsRequired == 0) return BaseInfo; - if (NumRegsRequired > FreeRegs) { - // If this type needs more registers than we have available, no more - // passing in-registers can happen. - FreeRegs = 0; - return BaseInfo; - } - FreeRegs -= NumRegsRequired; - return BaseInfo.isDirect() ? - ABIArgInfo::getDirectInReg(BaseInfo.getCoerceToType()) : - ABIArgInfo::getExtendInReg(BaseInfo.getCoerceToType()); } ABIArgInfo PNaClABIInfo::classifyReturnType(QualType RetTy) const { @@ -520,7 +513,7 @@ class X86_32ABIInfo : public ABIInfo { bool IsDarwinVectorABI; bool IsSmallStructInRegABI; - bool IsWin32FloatStructABI; + bool IsWin32StructABI; unsigned DefaultNumRegisterParameters; static bool isRegisterSize(unsigned Size) { @@ -555,7 +548,7 @@ class X86_32ABIInfo : public ABIInfo { X86_32ABIInfo(CodeGen::CodeGenTypes &CGT, bool d, bool p, bool w, unsigned r) : ABIInfo(CGT), IsDarwinVectorABI(d), IsSmallStructInRegABI(p), - IsWin32FloatStructABI(w), DefaultNumRegisterParameters(r) {} + IsWin32StructABI(w), DefaultNumRegisterParameters(r) {} }; class X86_32TargetCodeGenInfo : public TargetCodeGenInfo { @@ -569,8 +562,7 @@ class X86_32TargetCodeGenInfo : public TargetCodeGenInfo { int getDwarfEHStackPointer(CodeGen::CodeGenModule &CGM) const { // Darwin uses different dwarf register numbers for EH. - if (CGM.isTargetDarwin()) return 5; - + if (CGM.getTarget().getTriple().isOSDarwin()) return 5; return 4; } @@ -682,9 +674,7 @@ ABIArgInfo X86_32ABIInfo::classifyReturnType(QualType RetTy, if (isAggregateTypeForABI(RetTy)) { if (const RecordType *RT = RetTy->getAs()) { - // Structures with either a non-trivial destructor or a non-trivial - // copy constructor are always indirect. - if (hasNonTrivialDestructorOrCopyConstructor(RT)) + if (isRecordReturnIndirect(RT, CGT)) return ABIArgInfo::getIndirect(0, /*ByVal=*/false); // Structures with flexible arrays are always indirect. @@ -708,7 +698,7 @@ ABIArgInfo X86_32ABIInfo::classifyReturnType(QualType RetTy, // We apply a similar transformation for pointer types to improve the // quality of the generated IR. if (const Type *SeltTy = isSingleElementStruct(RetTy, getContext())) - if ((!IsWin32FloatStructABI && SeltTy->isRealFloatingType()) + if ((!IsWin32StructABI && SeltTy->isRealFloatingType()) || SeltTy->hasPointerRepresentation()) return ABIArgInfo::getDirect(CGT.ConvertType(QualType(SeltTy, 0))); @@ -865,13 +855,14 @@ ABIArgInfo X86_32ABIInfo::classifyArgumentType(QualType Ty, bool IsFastCall) const { // FIXME: Set alignment on indirect arguments. if (isAggregateTypeForABI(Ty)) { - // Structures with flexible arrays are always indirect. if (const RecordType *RT = Ty->getAs()) { - // Structures with either a non-trivial destructor or a non-trivial - // copy constructor are always indirect. - if (hasNonTrivialDestructorOrCopyConstructor(RT)) - return getIndirectResult(Ty, false, FreeRegs); + if (IsWin32StructABI) + return getIndirectResult(Ty, true, FreeRegs); + + if (CGCXXABI::RecordArgABI RAA = getRecordArgABI(RT, CGT)) + return getIndirectResult(Ty, RAA == CGCXXABI::RAA_DirectInMemory, FreeRegs); + // Structures with flexible arrays are always indirect. if (RT->getDecl()->hasFlexibleArrayMember()) return getIndirectResult(Ty, true, FreeRegs); } @@ -1038,7 +1029,7 @@ bool X86_32TargetCodeGenInfo::initDwarfEHRegSizeTable( // 8 is %eip. AssignToArrayRange(Builder, Address, Four8, 0, 8); - if (CGF.CGM.isTargetDarwin()) { + if (CGF.CGM.getTarget().getTriple().isOSDarwin()) { // 12-16 are st(0..4). Not sure why we stop at 4. // These have size 16, which is sizeof(long double) on // platforms with 8-byte alignment for that type. @@ -1163,7 +1154,7 @@ class X86_64ABIInfo : public ABIInfo { /// required strict binary compatibility with older versions of GCC /// may need to exempt themselves. bool honorsRevision0_98() const { - return !getContext().getTargetInfo().getTriple().isOSDarwin(); + return !getTarget().getTriple().isOSDarwin(); } bool HasAVX; @@ -1198,7 +1189,7 @@ class X86_64ABIInfo : public ABIInfo { /// WinX86_64ABIInfo - The Windows X86_64 ABI information. class WinX86_64ABIInfo : public ABIInfo { - ABIArgInfo classify(QualType Ty) const; + ABIArgInfo classify(QualType Ty, bool IsReturnType) const; public: WinX86_64ABIInfo(CodeGen::CodeGenTypes &CGT) : ABIInfo(CGT) {} @@ -1387,8 +1378,7 @@ void X86_64ABIInfo::classify(QualType Ty, uint64_t OffsetBase, Current = Integer; } else if ((k == BuiltinType::Float || k == BuiltinType::Double) || (k == BuiltinType::LongDouble && - getContext().getTargetInfo().getTriple().getOS() == - llvm::Triple::NaCl)) { + getTarget().getTriple().getOS() == llvm::Triple::NaCl)) { Current = SSE; } else if (k == BuiltinType::LongDouble) { Lo = X87; @@ -1476,8 +1466,7 @@ void X86_64ABIInfo::classify(QualType Ty, uint64_t OffsetBase, Current = SSE; else if (ET == getContext().DoubleTy || (ET == getContext().LongDoubleTy && - getContext().getTargetInfo().getTriple().getOS() == - llvm::Triple::NaCl)) + getTarget().getTriple().getOS() == llvm::Triple::NaCl)) Lo = Hi = SSE; else if (ET == getContext().LongDoubleTy) Current = ComplexX87; @@ -1546,7 +1535,7 @@ void X86_64ABIInfo::classify(QualType Ty, uint64_t OffsetBase, // AMD64-ABI 3.2.3p2: Rule 2. If a C++ object has either a non-trivial // copy constructor or a non-trivial destructor, it is passed by invisible // reference. - if (hasNonTrivialDestructorOrCopyConstructor(RT)) + if (getRecordArgABI(RT, CGT)) return; const RecordDecl *RD = RT->getDecl(); @@ -1696,8 +1685,8 @@ ABIArgInfo X86_64ABIInfo::getIndirectResult(QualType Ty, ABIArgInfo::getExtend() : ABIArgInfo::getDirect()); } - if (isRecordWithNonTrivialDestructorOrCopyConstructor(Ty)) - return ABIArgInfo::getIndirect(0, /*ByVal=*/false); + if (CGCXXABI::RecordArgABI RAA = getRecordArgABI(Ty, CGT)) + return ABIArgInfo::getIndirect(0, RAA == CGCXXABI::RAA_DirectInMemory); // Compute the byval alignment. We specify the alignment of the byval in all // cases so that the mid-level optimizer knows the alignment of the byval. @@ -2185,7 +2174,7 @@ ABIArgInfo X86_64ABIInfo::classifyArgumentType( // COMPLEX_X87, it is passed in memory. case X87: case ComplexX87: - if (isRecordWithNonTrivialDestructorOrCopyConstructor(Ty)) + if (getRecordArgABI(Ty, CGT) == CGCXXABI::RAA_Indirect) ++neededInt; return getIndirectResult(Ty, freeIntRegs); @@ -2516,7 +2505,7 @@ llvm::Value *X86_64ABIInfo::EmitVAArg(llvm::Value *VAListAddr, QualType Ty, return ResAddr; } -ABIArgInfo WinX86_64ABIInfo::classify(QualType Ty) const { +ABIArgInfo WinX86_64ABIInfo::classify(QualType Ty, bool IsReturnType) const { if (Ty->isVoidType()) return ABIArgInfo::getIgnore(); @@ -2527,14 +2516,19 @@ ABIArgInfo WinX86_64ABIInfo::classify(QualType Ty) const { uint64_t Size = getContext().getTypeSize(Ty); if (const RecordType *RT = Ty->getAs()) { - if (hasNonTrivialDestructorOrCopyConstructor(RT) || - RT->getDecl()->hasFlexibleArrayMember()) + if (IsReturnType) { + if (isRecordReturnIndirect(RT, CGT)) + return ABIArgInfo::getIndirect(0, false); + } else { + if (CGCXXABI::RecordArgABI RAA = getRecordArgABI(RT, CGT)) + return ABIArgInfo::getIndirect(0, RAA == CGCXXABI::RAA_DirectInMemory); + } + + if (RT->getDecl()->hasFlexibleArrayMember()) return ABIArgInfo::getIndirect(0, /*ByVal=*/false); // FIXME: mingw-w64-gcc emits 128-bit struct as i128 - if (Size == 128 && - getContext().getTargetInfo().getTriple().getOS() - == llvm::Triple::MinGW32) + if (Size == 128 && getTarget().getTriple().getOS() == llvm::Triple::MinGW32) return ABIArgInfo::getDirect(llvm::IntegerType::get(getVMContext(), Size)); @@ -2557,11 +2551,11 @@ ABIArgInfo WinX86_64ABIInfo::classify(QualType Ty) const { void WinX86_64ABIInfo::computeInfo(CGFunctionInfo &FI) const { QualType RetTy = FI.getReturnType(); - FI.getReturnInfo() = classify(RetTy); + FI.getReturnInfo() = classify(RetTy, true); for (CGFunctionInfo::arg_iterator it = FI.arg_begin(), ie = FI.arg_end(); it != ie; ++it) - it->info = classify(it->type); + it->info = classify(it->type, false); } llvm::Value *WinX86_64ABIInfo::EmitVAArg(llvm::Value *VAListAddr, QualType Ty, @@ -2788,10 +2782,8 @@ PPC64_SVR4_ABIInfo::classifyArgumentType(QualType Ty) const { return ABIArgInfo::getDirect(); if (isAggregateTypeForABI(Ty)) { - // Records with non trivial destructors/constructors should not be passed - // by value. - if (isRecordWithNonTrivialDestructorOrCopyConstructor(Ty)) - return ABIArgInfo::getIndirect(0, /*ByVal=*/false); + if (CGCXXABI::RecordArgABI RAA = getRecordArgABI(Ty, CGT)) + return ABIArgInfo::getIndirect(0, RAA == CGCXXABI::RAA_DirectInMemory); return ABIArgInfo::getIndirect(0); } @@ -2964,8 +2956,7 @@ class ARMABIInfo : public ABIInfo { } bool isEABI() const { - StringRef Env = - getContext().getTargetInfo().getTriple().getEnvironmentName(); + StringRef Env = getTarget().getTriple().getEnvironmentName(); return (Env == "gnueabi" || Env == "eabi" || Env == "android" || Env == "androideabi"); } @@ -3064,7 +3055,7 @@ void ARMABIInfo::computeInfo(CGFunctionInfo &FI) const { /// Return the default calling convention that LLVM will use. llvm::CallingConv::ID ARMABIInfo::getLLVMDefaultCC() const { // The default calling convention that LLVM will infer. - if (getContext().getTargetInfo().getTriple().getEnvironmentName()=="gnueabihf") + if (getTarget().getTriple().getEnvironmentName()=="gnueabihf") return llvm::CallingConv::ARM_AAPCS_VFP; else if (isEABI()) return llvm::CallingConv::ARM_AAPCS; @@ -3256,10 +3247,8 @@ ABIArgInfo ARMABIInfo::classifyArgumentType(QualType Ty, int *VFPRegs, if (isEmptyRecord(getContext(), Ty, true)) return ABIArgInfo::getIgnore(); - // Structures with either a non-trivial destructor or a non-trivial - // copy constructor are always indirect. - if (isRecordWithNonTrivialDestructorOrCopyConstructor(Ty)) - return ABIArgInfo::getIndirect(0, /*ByVal=*/false); + if (CGCXXABI::RecordArgABI RAA = getRecordArgABI(Ty, CGT)) + return ABIArgInfo::getIndirect(0, RAA == CGCXXABI::RAA_DirectInMemory); if (getABIKind() == ARMABIInfo::AAPCS_VFP) { // Homogeneous Aggregates need to be expanded when we can fit the aggregate @@ -3422,7 +3411,7 @@ ABIArgInfo ARMABIInfo::classifyReturnType(QualType RetTy) const { // Structures with either a non-trivial destructor or a non-trivial // copy constructor are always indirect. - if (isRecordWithNonTrivialDestructorOrCopyConstructor(RetTy)) + if (isRecordReturnIndirect(RetTy, CGT)) return ABIArgInfo::getIndirect(0, /*ByVal=*/false); // Are we following APCS? @@ -3746,12 +3735,10 @@ ABIArgInfo AArch64ABIInfo::classifyGenericType(QualType Ty, return tryUseRegs(Ty, FreeIntRegs, RegsNeeded, /*IsInt=*/ true); } - // Structures with either a non-trivial destructor or a non-trivial - // copy constructor are always indirect. - if (isRecordWithNonTrivialDestructorOrCopyConstructor(Ty)) { - if (FreeIntRegs > 0) + if (CGCXXABI::RecordArgABI RAA = getRecordArgABI(Ty, CGT)) { + if (FreeIntRegs > 0 && RAA == CGCXXABI::RAA_Indirect) --FreeIntRegs; - return ABIArgInfo::getIndirect(0, /*ByVal=*/false); + return ABIArgInfo::getIndirect(0, RAA == CGCXXABI::RAA_DirectInMemory); } if (isEmptyRecord(getContext(), Ty, true)) { @@ -4129,6 +4116,293 @@ void NVPTXTargetCodeGenInfo::addKernelMetadata(llvm::Function *F) { } +//===----------------------------------------------------------------------===// +// SystemZ ABI Implementation +//===----------------------------------------------------------------------===// + +namespace { + +class SystemZABIInfo : public ABIInfo { +public: + SystemZABIInfo(CodeGenTypes &CGT) : ABIInfo(CGT) {} + + bool isPromotableIntegerType(QualType Ty) const; + bool isCompoundType(QualType Ty) const; + bool isFPArgumentType(QualType Ty) const; + + ABIArgInfo classifyReturnType(QualType RetTy) const; + ABIArgInfo classifyArgumentType(QualType ArgTy) const; + + virtual void computeInfo(CGFunctionInfo &FI) const { + FI.getReturnInfo() = classifyReturnType(FI.getReturnType()); + for (CGFunctionInfo::arg_iterator it = FI.arg_begin(), ie = FI.arg_end(); + it != ie; ++it) + it->info = classifyArgumentType(it->type); + } + + virtual llvm::Value *EmitVAArg(llvm::Value *VAListAddr, QualType Ty, + CodeGenFunction &CGF) const; +}; + +class SystemZTargetCodeGenInfo : public TargetCodeGenInfo { +public: + SystemZTargetCodeGenInfo(CodeGenTypes &CGT) + : TargetCodeGenInfo(new SystemZABIInfo(CGT)) {} +}; + +} + +bool SystemZABIInfo::isPromotableIntegerType(QualType Ty) const { + // Treat an enum type as its underlying type. + if (const EnumType *EnumTy = Ty->getAs()) + Ty = EnumTy->getDecl()->getIntegerType(); + + // Promotable integer types are required to be promoted by the ABI. + if (Ty->isPromotableIntegerType()) + return true; + + // 32-bit values must also be promoted. + if (const BuiltinType *BT = Ty->getAs()) + switch (BT->getKind()) { + case BuiltinType::Int: + case BuiltinType::UInt: + return true; + default: + return false; + } + return false; +} + +bool SystemZABIInfo::isCompoundType(QualType Ty) const { + return Ty->isAnyComplexType() || isAggregateTypeForABI(Ty); +} + +bool SystemZABIInfo::isFPArgumentType(QualType Ty) const { + if (const BuiltinType *BT = Ty->getAs()) + switch (BT->getKind()) { + case BuiltinType::Float: + case BuiltinType::Double: + return true; + default: + return false; + } + + if (const RecordType *RT = Ty->getAsStructureType()) { + const RecordDecl *RD = RT->getDecl(); + bool Found = false; + + // If this is a C++ record, check the bases first. + if (const CXXRecordDecl *CXXRD = dyn_cast(RD)) + for (CXXRecordDecl::base_class_const_iterator I = CXXRD->bases_begin(), + E = CXXRD->bases_end(); I != E; ++I) { + QualType Base = I->getType(); + + // Empty bases don't affect things either way. + if (isEmptyRecord(getContext(), Base, true)) + continue; + + if (Found) + return false; + Found = isFPArgumentType(Base); + if (!Found) + return false; + } + + // Check the fields. + for (RecordDecl::field_iterator I = RD->field_begin(), + E = RD->field_end(); I != E; ++I) { + const FieldDecl *FD = *I; + + // Empty bitfields don't affect things either way. + // Unlike isSingleElementStruct(), empty structure and array fields + // do count. So do anonymous bitfields that aren't zero-sized. + if (FD->isBitField() && FD->getBitWidthValue(getContext()) == 0) + return true; + + // Unlike isSingleElementStruct(), arrays do not count. + // Nested isFPArgumentType structures still do though. + if (Found) + return false; + Found = isFPArgumentType(FD->getType()); + if (!Found) + return false; + } + + // Unlike isSingleElementStruct(), trailing padding is allowed. + // An 8-byte aligned struct s { float f; } is passed as a double. + return Found; + } + + return false; +} + +llvm::Value *SystemZABIInfo::EmitVAArg(llvm::Value *VAListAddr, QualType Ty, + CodeGenFunction &CGF) const { + // Assume that va_list type is correct; should be pointer to LLVM type: + // struct { + // i64 __gpr; + // i64 __fpr; + // i8 *__overflow_arg_area; + // i8 *__reg_save_area; + // }; + + // Every argument occupies 8 bytes and is passed by preference in either + // GPRs or FPRs. + Ty = CGF.getContext().getCanonicalType(Ty); + ABIArgInfo AI = classifyArgumentType(Ty); + bool InFPRs = isFPArgumentType(Ty); + + llvm::Type *APTy = llvm::PointerType::getUnqual(CGF.ConvertTypeForMem(Ty)); + bool IsIndirect = AI.isIndirect(); + unsigned UnpaddedBitSize; + if (IsIndirect) { + APTy = llvm::PointerType::getUnqual(APTy); + UnpaddedBitSize = 64; + } else + UnpaddedBitSize = getContext().getTypeSize(Ty); + unsigned PaddedBitSize = 64; + assert((UnpaddedBitSize <= PaddedBitSize) && "Invalid argument size."); + + unsigned PaddedSize = PaddedBitSize / 8; + unsigned Padding = (PaddedBitSize - UnpaddedBitSize) / 8; + + unsigned MaxRegs, RegCountField, RegSaveIndex, RegPadding; + if (InFPRs) { + MaxRegs = 4; // Maximum of 4 FPR arguments + RegCountField = 1; // __fpr + RegSaveIndex = 16; // save offset for f0 + RegPadding = 0; // floats are passed in the high bits of an FPR + } else { + MaxRegs = 5; // Maximum of 5 GPR arguments + RegCountField = 0; // __gpr + RegSaveIndex = 2; // save offset for r2 + RegPadding = Padding; // values are passed in the low bits of a GPR + } + + llvm::Value *RegCountPtr = + CGF.Builder.CreateStructGEP(VAListAddr, RegCountField, "reg_count_ptr"); + llvm::Value *RegCount = CGF.Builder.CreateLoad(RegCountPtr, "reg_count"); + llvm::Type *IndexTy = RegCount->getType(); + llvm::Value *MaxRegsV = llvm::ConstantInt::get(IndexTy, MaxRegs); + llvm::Value *InRegs = CGF.Builder.CreateICmpULT(RegCount, MaxRegsV, + "fits_in_regs"); + + llvm::BasicBlock *InRegBlock = CGF.createBasicBlock("vaarg.in_reg"); + llvm::BasicBlock *InMemBlock = CGF.createBasicBlock("vaarg.in_mem"); + llvm::BasicBlock *ContBlock = CGF.createBasicBlock("vaarg.end"); + CGF.Builder.CreateCondBr(InRegs, InRegBlock, InMemBlock); + + // Emit code to load the value if it was passed in registers. + CGF.EmitBlock(InRegBlock); + + // Work out the address of an argument register. + llvm::Value *PaddedSizeV = llvm::ConstantInt::get(IndexTy, PaddedSize); + llvm::Value *ScaledRegCount = + CGF.Builder.CreateMul(RegCount, PaddedSizeV, "scaled_reg_count"); + llvm::Value *RegBase = + llvm::ConstantInt::get(IndexTy, RegSaveIndex * PaddedSize + RegPadding); + llvm::Value *RegOffset = + CGF.Builder.CreateAdd(ScaledRegCount, RegBase, "reg_offset"); + llvm::Value *RegSaveAreaPtr = + CGF.Builder.CreateStructGEP(VAListAddr, 3, "reg_save_area_ptr"); + llvm::Value *RegSaveArea = + CGF.Builder.CreateLoad(RegSaveAreaPtr, "reg_save_area"); + llvm::Value *RawRegAddr = + CGF.Builder.CreateGEP(RegSaveArea, RegOffset, "raw_reg_addr"); + llvm::Value *RegAddr = + CGF.Builder.CreateBitCast(RawRegAddr, APTy, "reg_addr"); + + // Update the register count + llvm::Value *One = llvm::ConstantInt::get(IndexTy, 1); + llvm::Value *NewRegCount = + CGF.Builder.CreateAdd(RegCount, One, "reg_count"); + CGF.Builder.CreateStore(NewRegCount, RegCountPtr); + CGF.EmitBranch(ContBlock); + + // Emit code to load the value if it was passed in memory. + CGF.EmitBlock(InMemBlock); + + // Work out the address of a stack argument. + llvm::Value *OverflowArgAreaPtr = + CGF.Builder.CreateStructGEP(VAListAddr, 2, "overflow_arg_area_ptr"); + llvm::Value *OverflowArgArea = + CGF.Builder.CreateLoad(OverflowArgAreaPtr, "overflow_arg_area"); + llvm::Value *PaddingV = llvm::ConstantInt::get(IndexTy, Padding); + llvm::Value *RawMemAddr = + CGF.Builder.CreateGEP(OverflowArgArea, PaddingV, "raw_mem_addr"); + llvm::Value *MemAddr = + CGF.Builder.CreateBitCast(RawMemAddr, APTy, "mem_addr"); + + // Update overflow_arg_area_ptr pointer + llvm::Value *NewOverflowArgArea = + CGF.Builder.CreateGEP(OverflowArgArea, PaddedSizeV, "overflow_arg_area"); + CGF.Builder.CreateStore(NewOverflowArgArea, OverflowArgAreaPtr); + CGF.EmitBranch(ContBlock); + + // Return the appropriate result. + CGF.EmitBlock(ContBlock); + llvm::PHINode *ResAddr = CGF.Builder.CreatePHI(APTy, 2, "va_arg.addr"); + ResAddr->addIncoming(RegAddr, InRegBlock); + ResAddr->addIncoming(MemAddr, InMemBlock); + + if (IsIndirect) + return CGF.Builder.CreateLoad(ResAddr, "indirect_arg"); + + return ResAddr; +} + + +ABIArgInfo SystemZABIInfo::classifyReturnType(QualType RetTy) const { + if (RetTy->isVoidType()) + return ABIArgInfo::getIgnore(); + if (isCompoundType(RetTy) || getContext().getTypeSize(RetTy) > 64) + return ABIArgInfo::getIndirect(0); + return (isPromotableIntegerType(RetTy) ? + ABIArgInfo::getExtend() : ABIArgInfo::getDirect()); +} + +ABIArgInfo SystemZABIInfo::classifyArgumentType(QualType Ty) const { + // Handle the generic C++ ABI. + if (CGCXXABI::RecordArgABI RAA = getRecordArgABI(Ty, CGT)) + return ABIArgInfo::getIndirect(0, RAA == CGCXXABI::RAA_DirectInMemory); + + // Integers and enums are extended to full register width. + if (isPromotableIntegerType(Ty)) + return ABIArgInfo::getExtend(); + + // Values that are not 1, 2, 4 or 8 bytes in size are passed indirectly. + uint64_t Size = getContext().getTypeSize(Ty); + if (Size != 8 && Size != 16 && Size != 32 && Size != 64) + return ABIArgInfo::getIndirect(0); + + // Handle small structures. + if (const RecordType *RT = Ty->getAs()) { + // Structures with flexible arrays have variable length, so really + // fail the size test above. + const RecordDecl *RD = RT->getDecl(); + if (RD->hasFlexibleArrayMember()) + return ABIArgInfo::getIndirect(0); + + // The structure is passed as an unextended integer, a float, or a double. + llvm::Type *PassTy; + if (isFPArgumentType(Ty)) { + assert(Size == 32 || Size == 64); + if (Size == 32) + PassTy = llvm::Type::getFloatTy(getVMContext()); + else + PassTy = llvm::Type::getDoubleTy(getVMContext()); + } else + PassTy = llvm::IntegerType::get(getVMContext(), Size); + return ABIArgInfo::getDirect(PassTy); + } + + // Non-structure compounds are passed indirectly. + if (isCompoundType(Ty)) + return ABIArgInfo::getIndirect(0); + + return ABIArgInfo::getDirect(0); +} + //===----------------------------------------------------------------------===// // MBlaze ABI Implementation //===----------------------------------------------------------------------===// @@ -4436,11 +4710,9 @@ MipsABIInfo::classifyArgumentType(QualType Ty, uint64_t &Offset) const { if (TySize == 0) return ABIArgInfo::getIgnore(); - // Records with non trivial destructors/constructors should not be passed - // by value. - if (isRecordWithNonTrivialDestructorOrCopyConstructor(Ty)) { + if (CGCXXABI::RecordArgABI RAA = getRecordArgABI(Ty, CGT)) { Offset = OrigOffset + MinABIStackAlignInBytes; - return ABIArgInfo::getIndirect(0, /*ByVal=*/false); + return ABIArgInfo::getIndirect(0, RAA == CGCXXABI::RAA_DirectInMemory); } // If we have reached here, aggregates are passed directly by coercing to @@ -4510,6 +4782,9 @@ ABIArgInfo MipsABIInfo::classifyReturnType(QualType RetTy) const { return ABIArgInfo::getIgnore(); if (isAggregateTypeForABI(RetTy) || RetTy->isVectorType()) { + if (isRecordReturnIndirect(RetTy, CGT)) + return ABIArgInfo::getIndirect(0); + if (Size <= 128) { if (RetTy->isAnyComplexType()) return ABIArgInfo::getDirect(); @@ -4518,7 +4793,7 @@ ABIArgInfo MipsABIInfo::classifyReturnType(QualType RetTy) const { if (IsO32 && RetTy->isVectorType() && !RetTy->hasFloatingRepresentation()) return ABIArgInfo::getDirect(returnAggregateInRegs(RetTy, Size)); - if (!IsO32 && !isRecordWithNonTrivialDestructorOrCopyConstructor(RetTy)) + if (!IsO32) return ABIArgInfo::getDirect(returnAggregateInRegs(RetTy, Size)); } @@ -4556,7 +4831,7 @@ llvm::Value* MipsABIInfo::EmitVAArg(llvm::Value *VAListAddr, QualType Ty, int64_t TypeAlign = getContext().getTypeAlign(Ty) / 8; llvm::Type *PTy = llvm::PointerType::getUnqual(CGF.ConvertType(Ty)); llvm::Value *AddrTyped; - unsigned PtrWidth = getContext().getTargetInfo().getPointerWidth(0); + unsigned PtrWidth = getTarget().getPointerWidth(0); llvm::IntegerType *IntTy = (PtrWidth == 32) ? CGF.Int32Ty : CGF.Int64Ty; if (TypeAlign > MinABIStackAlignInBytes) { @@ -4728,10 +5003,8 @@ ABIArgInfo HexagonABIInfo::classifyArgumentType(QualType Ty) const { if (isEmptyRecord(getContext(), Ty, true)) return ABIArgInfo::getIgnore(); - // Structures with either a non-trivial destructor or a non-trivial - // copy constructor are always indirect. - if (isRecordWithNonTrivialDestructorOrCopyConstructor(Ty)) - return ABIArgInfo::getIndirect(0, /*ByVal=*/false); + if (CGCXXABI::RecordArgABI RAA = getRecordArgABI(Ty, CGT)) + return ABIArgInfo::getIndirect(0, RAA == CGCXXABI::RAA_DirectInMemory); uint64_t Size = getContext().getTypeSize(Ty); if (Size > 64) @@ -4766,7 +5039,7 @@ ABIArgInfo HexagonABIInfo::classifyReturnType(QualType RetTy) const { // Structures with either a non-trivial destructor or a non-trivial // copy constructor are always indirect. - if (isRecordWithNonTrivialDestructorOrCopyConstructor(RetTy)) + if (isRecordReturnIndirect(RetTy, CGT)) return ABIArgInfo::getIndirect(0, /*ByVal=*/false); if (isEmptyRecord(getContext(), RetTy, true)) @@ -4817,7 +5090,7 @@ const TargetCodeGenInfo &CodeGenModule::getTargetCodeGenInfo() { if (TheTargetCodeGenInfo) return *TheTargetCodeGenInfo; - const llvm::Triple &Triple = getContext().getTargetInfo().getTriple(); + const llvm::Triple &Triple = getTarget().getTriple(); switch (Triple.getArch()) { default: return *(TheTargetCodeGenInfo = new DefaultTargetCodeGenInfo(Types)); @@ -4839,10 +5112,11 @@ const TargetCodeGenInfo &CodeGenModule::getTargetCodeGenInfo() { case llvm::Triple::thumb: { ARMABIInfo::ABIKind Kind = ARMABIInfo::AAPCS; - if (strcmp(getContext().getTargetInfo().getABI(), "apcs-gnu") == 0) + if (strcmp(getTarget().getABI(), "apcs-gnu") == 0) Kind = ARMABIInfo::APCS; else if (CodeGenOpts.FloatABI == "hard" || - (CodeGenOpts.FloatABI != "soft" && Triple.getEnvironment()==llvm::Triple::GNUEABIHF)) + (CodeGenOpts.FloatABI != "soft" && + Triple.getEnvironment() == llvm::Triple::GNUEABIHF)) Kind = ARMABIInfo::AAPCS_VFP; switch (Triple.getOS()) { @@ -4873,6 +5147,9 @@ const TargetCodeGenInfo &CodeGenModule::getTargetCodeGenInfo() { case llvm::Triple::msp430: return *(TheTargetCodeGenInfo = new MSP430TargetCodeGenInfo(Types)); + case llvm::Triple::systemz: + return *(TheTargetCodeGenInfo = new SystemZTargetCodeGenInfo(Types)); + case llvm::Triple::tce: return *(TheTargetCodeGenInfo = new TCETargetCodeGenInfo(Types)); @@ -4907,7 +5184,7 @@ const TargetCodeGenInfo &CodeGenModule::getTargetCodeGenInfo() { } case llvm::Triple::x86_64: { - bool HasAVX = strcmp(getContext().getTargetInfo().getABI(), "avx") == 0; + bool HasAVX = strcmp(getTarget().getABI(), "avx") == 0; switch (Triple.getOS()) { case llvm::Triple::Win32: @@ -4915,7 +5192,8 @@ const TargetCodeGenInfo &CodeGenModule::getTargetCodeGenInfo() { case llvm::Triple::Cygwin: return *(TheTargetCodeGenInfo = new WinX86_64TargetCodeGenInfo(Types)); case llvm::Triple::NaCl: - return *(TheTargetCodeGenInfo = new NaClX86_64TargetCodeGenInfo(Types, HasAVX)); + return *(TheTargetCodeGenInfo = new NaClX86_64TargetCodeGenInfo(Types, + HasAVX)); default: return *(TheTargetCodeGenInfo = new X86_64TargetCodeGenInfo(Types, HasAVX)); diff --git a/lib/Driver/ArgList.cpp b/lib/Driver/ArgList.cpp index 6c57b622b8d..4b8d151d195 100644 --- a/lib/Driver/ArgList.cpp +++ b/lib/Driver/ArgList.cpp @@ -206,6 +206,13 @@ bool ArgList::hasFlag(OptSpecifier Pos, OptSpecifier Neg, bool Default) const { return Default; } +bool ArgList::hasFlag(OptSpecifier Pos, OptSpecifier PosAlias, OptSpecifier Neg, + bool Default) const { + if (Arg *A = getLastArg(Pos, PosAlias, Neg)) + return A->getOption().matches(Pos) || A->getOption().matches(PosAlias); + return Default; +} + StringRef ArgList::getLastArgValue(OptSpecifier Id, StringRef Default) const { if (Arg *A = getLastArg(Id)) @@ -241,6 +248,14 @@ void ArgList::AddLastArg(ArgStringList &Output, OptSpecifier Id) const { } } +void ArgList::AddLastArg(ArgStringList &Output, OptSpecifier Id0, + OptSpecifier Id1) const { + if (Arg *A = getLastArg(Id0, Id1)) { + A->claim(); + A->render(*this, Output); + } +} + void ArgList::AddAllArgs(ArgStringList &Output, OptSpecifier Id0, OptSpecifier Id1, OptSpecifier Id2) const { for (arg_iterator it = filtered_begin(Id0, Id1, Id2), diff --git a/lib/Driver/Driver.cpp b/lib/Driver/Driver.cpp index ad1921b838e..1dbbc9a342b 100644 --- a/lib/Driver/Driver.cpp +++ b/lib/Driver/Driver.cpp @@ -491,9 +491,8 @@ void Driver::generateCompilationDiagnostics(Compilation &C, << "\n\n********************"; } else { // Failure, remove preprocessed files. - if (!C.getArgs().hasArg(options::OPT_save_temps)) { + if (!C.getArgs().hasArg(options::OPT_save_temps)) C.CleanupFileList(C.getTempFiles(), true); - } Diag(clang::diag::note_drv_command_failed_diag_msg) << "Error generating preprocessed source(s)."; @@ -825,17 +824,6 @@ void Driver::BuildUniversalActions(const ToolChain &TC, if (!Archs.size()) Archs.push_back(Args.MakeArgString(TC.getDefaultUniversalArchName())); - // 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" - // files they wrote then we could detect the conflict these cause downstream. - if (Archs.size() > 1) { - // No recovery needed, the point of this is just to prevent - // overwriting the same files. - if (const Arg *A = Args.getLastArg(options::OPT_save_temps)) - Diag(clang::diag::err_drv_invalid_opt_with_multiple_archs) - << A->getAsString(Args); - } - ActionList SingleActions; BuildActions(TC, Args, BAInputs, SingleActions); @@ -1221,6 +1209,17 @@ void Driver::BuildJobs(Compilation &C) const { } } + // Collect the list of architectures. + llvm::StringSet<> ArchNames; + if (C.getDefaultToolChain().getTriple().isOSDarwin()) { + for (ArgList::const_iterator it = C.getArgs().begin(), ie = C.getArgs().end(); + it != ie; ++it) { + Arg *A = *it; + if (A->getOption().matches(options::OPT_arch)) + ArchNames.insert(A->getValue()); + } + } + for (ActionList::const_iterator it = C.getActions().begin(), ie = C.getActions().end(); it != ie; ++it) { Action *A = *it; @@ -1243,6 +1242,7 @@ void Driver::BuildJobs(Compilation &C) const { BuildJobsForAction(C, A, &C.getDefaultToolChain(), /*BoundArch*/0, /*AtTopLevel*/ true, + /*MultipleArchs*/ ArchNames.size() > 1, /*LinkingOutput*/ LinkingOutput, II); } @@ -1337,6 +1337,7 @@ void Driver::BuildJobsForAction(Compilation &C, const ToolChain *TC, const char *BoundArch, bool AtTopLevel, + bool MultipleArchs, const char *LinkingOutput, InputInfo &Result) const { llvm::PrettyStackTraceString CrashInfo("Building compilation jobs"); @@ -1364,7 +1365,7 @@ void Driver::BuildJobsForAction(Compilation &C, TC = &C.getDefaultToolChain(); BuildJobsForAction(C, *BAA->begin(), TC, BAA->getArchName(), - AtTopLevel, LinkingOutput, Result); + AtTopLevel, MultipleArchs, LinkingOutput, Result); return; } @@ -1387,8 +1388,8 @@ void Driver::BuildJobsForAction(Compilation &C, SubJobAtTopLevel = true; InputInfo II; - BuildJobsForAction(C, *it, TC, BoundArch, - SubJobAtTopLevel, LinkingOutput, II); + BuildJobsForAction(C, *it, TC, BoundArch, SubJobAtTopLevel, MultipleArchs, + LinkingOutput, II); InputInfos.push_back(II); } @@ -1404,7 +1405,8 @@ void Driver::BuildJobsForAction(Compilation &C, if (JA->getType() == types::TY_Nothing) Result = InputInfo(A->getType(), BaseInput); else - Result = InputInfo(GetNamedOutputPath(C, *JA, BaseInput, AtTopLevel), + Result = InputInfo(GetNamedOutputPath(C, *JA, BaseInput, BoundArch, + AtTopLevel, MultipleArchs), A->getType(), BaseInput); if (CCCPrintBindings && !CCGenDiagnostics) { @@ -1425,7 +1427,9 @@ void Driver::BuildJobsForAction(Compilation &C, const char *Driver::GetNamedOutputPath(Compilation &C, const JobAction &JA, const char *BaseInput, - bool AtTopLevel) const { + const char *BoundArch, + bool AtTopLevel, + bool MultipleArchs) const { llvm::PrettyStackTraceString CrashInfo("Computing output path"); // Output to a user requested destination? if (AtTopLevel && !isa(JA) && @@ -1460,8 +1464,14 @@ const char *Driver::GetNamedOutputPath(Compilation &C, // Determine what the derived output name should be. const char *NamedOutput; - if (JA.getType() == types::TY_Image) { - NamedOutput = DefaultImageName.c_str(); + if (JA.getType() == types::TY_Image) { + if (MultipleArchs && BoundArch) { + SmallString<128> Output(DefaultImageName.c_str()); + Output += "-"; + Output.append(BoundArch); + NamedOutput = C.getArgs().MakeArgString(Output.c_str()); + } else + NamedOutput = DefaultImageName.c_str(); } else { const char *Suffix = types::getTypeTempSuffix(JA.getType()); assert(Suffix && "All types used for output should have a suffix."); @@ -1469,7 +1479,11 @@ const char *Driver::GetNamedOutputPath(Compilation &C, std::string::size_type End = std::string::npos; if (!types::appendSuffixForType(JA.getType())) End = BaseName.rfind('.'); - std::string Suffixed(BaseName.substr(0, End)); + SmallString<128> Suffixed(BaseName.substr(0, End)); + if (MultipleArchs && BoundArch) { + Suffixed += "-"; + Suffixed.append(BoundArch); + } Suffixed += '.'; Suffixed += Suffix; NamedOutput = C.getArgs().MakeArgString(Suffixed.c_str()); diff --git a/lib/Driver/SanitizerArgs.h b/lib/Driver/SanitizerArgs.h index e61f15ad624..326d80db72e 100644 --- a/lib/Driver/SanitizerArgs.h +++ b/lib/Driver/SanitizerArgs.h @@ -38,7 +38,8 @@ class SanitizerArgs { NeedsTsanRt = Thread, NeedsMsanRt = Memory, NeedsUbsanRt = Undefined | Integer, - NotAllowedWithTrap = Vptr + NotAllowedWithTrap = Vptr, + HasZeroBaseShadow = Thread | Memory }; unsigned Kind; std::string BlacklistFile; @@ -50,7 +51,7 @@ class SanitizerArgs { SanitizerArgs() : Kind(0), BlacklistFile(""), MsanTrackOrigins(false), AsanZeroBaseShadow(false), UbsanTrapOnError(false) {} /// Parses the sanitizer arguments from an argument list. - SanitizerArgs(const Driver &D, const ArgList &Args); + SanitizerArgs(const ToolChain &TC, const ArgList &Args); bool needsAsanRt() const { return Kind & NeedsAsanRt; } bool needsTsanRt() const { return Kind & NeedsTsanRt; } @@ -63,6 +64,9 @@ class SanitizerArgs { bool sanitizesVptr() const { return Kind & Vptr; } bool notAllowedWithTrap() const { return Kind & NotAllowedWithTrap; } + bool hasZeroBaseShadow() const { + return (Kind & HasZeroBaseShadow) || AsanZeroBaseShadow; + } void addArgs(const ArgList &Args, ArgStringList &CmdArgs) const { if (!Kind) diff --git a/lib/Driver/ToolChain.cpp b/lib/Driver/ToolChain.cpp index 19270b2aa33..71f53933e2a 100644 --- a/lib/Driver/ToolChain.cpp +++ b/lib/Driver/ToolChain.cpp @@ -19,6 +19,7 @@ #include "clang/Driver/Options.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/FileSystem.h" using namespace clang::driver; using namespace clang; @@ -333,6 +334,13 @@ ToolChain::CXXStdlibType ToolChain::GetCXXStdlibType(const ArgList &Args) const{ CC1Args.push_back(DriverArgs.MakeArgString(Path)); } +void ToolChain::addExternCSystemIncludeIfExists(const ArgList &DriverArgs, + ArgStringList &CC1Args, + const Twine &Path) { + if (llvm::sys::fs::exists(Path)) + addExternCSystemInclude(DriverArgs, CC1Args, Path); +} + /// \brief Utility function to add a list of system include directories to CC1. /*static*/ void ToolChain::addSystemIncludes(const ArgList &DriverArgs, ArgStringList &CC1Args, diff --git a/lib/Driver/ToolChains.cpp b/lib/Driver/ToolChains.cpp index bcfe51ef7bb..fffba0e4e50 100644 --- a/lib/Driver/ToolChains.cpp +++ b/lib/Driver/ToolChains.cpp @@ -294,7 +294,7 @@ void DarwinClang::AddLinkRuntimeLibArgs(const ArgList &Args, } } - SanitizerArgs Sanitize(getDriver(), Args); + SanitizerArgs Sanitize(*this, Args); // Add Ubsan runtime library, if required. if (Sanitize.needsUbsanRt()) { @@ -878,6 +878,10 @@ bool Darwin::isPICDefault() const { return true; } +bool Darwin::isPIEDefault() const { + return false; +} + bool Darwin::isPICDefaultForced() const { return getArch() == llvm::Triple::x86_64; } @@ -1082,6 +1086,7 @@ Generic_GCC::GCCInstallationDetector::GCCInstallationDetector( }; static const char *const ARMHFTriples[] = { "arm-linux-gnueabihf", + "armv7hl-redhat-linux-gnueabi" }; static const char *const X86_64LibDirs[] = { "/lib64", "/lib" }; @@ -1116,7 +1121,8 @@ Generic_GCC::GCCInstallationDetector::GCCInstallationDetector( static const char *const MIPSELLibDirs[] = { "/lib" }; static const char *const MIPSELTriples[] = { "mipsel-linux-gnu", - "mipsel-linux-android" + "mipsel-linux-android", + "mips-linux-gnu" }; static const char *const MIPS64LibDirs[] = { "/lib64", "/lib" }; @@ -1140,6 +1146,15 @@ Generic_GCC::GCCInstallationDetector::GCCInstallationDetector( "ppc64-redhat-linux" }; + static const char *const SystemZLibDirs[] = { "/lib64", "/lib" }; + static const char *const SystemZTriples[] = { + "s390x-linux-gnu", + "s390x-unknown-linux-gnu", + "s390x-ibm-linux-gnu", + "s390x-suse-linux", + "s390x-redhat-linux" + }; + switch (TargetTriple.getArch()) { case llvm::Triple::aarch64: LibDirs.append(AArch64LibDirs, AArch64LibDirs @@ -1240,6 +1255,12 @@ Generic_GCC::GCCInstallationDetector::GCCInstallationDetector( MultiarchTripleAliases.append( PPCTriples, PPCTriples + llvm::array_lengthof(PPCTriples)); break; + case llvm::Triple::systemz: + LibDirs.append( + SystemZLibDirs, SystemZLibDirs + llvm::array_lengthof(SystemZLibDirs)); + TripleAliases.append( + SystemZTriples, SystemZTriples + llvm::array_lengthof(SystemZTriples)); + break; default: // By default, just rely on the standard lib directories and the original @@ -1256,29 +1277,102 @@ Generic_GCC::GCCInstallationDetector::GCCInstallationDetector( MultiarchTripleAliases.push_back(MultiarchTriple.str()); } +static bool isSoftFloatABI(const ArgList &Args) { + Arg *A = Args.getLastArg(options::OPT_msoft_float, + options::OPT_mhard_float, + options::OPT_mfloat_abi_EQ); + if (!A) return false; + + return A->getOption().matches(options::OPT_msoft_float) || + (A->getOption().matches(options::OPT_mfloat_abi_EQ) && + A->getValue() == StringRef("soft")); +} + +static bool isMipsArch(llvm::Triple::ArchType Arch) { + return Arch == llvm::Triple::mips || + Arch == llvm::Triple::mipsel || + Arch == llvm::Triple::mips64 || + Arch == llvm::Triple::mips64el; +} + +static bool isMips16(const ArgList &Args) { + Arg *A = Args.getLastArg(options::OPT_mips16, + options::OPT_mno_mips16); + return A && A->getOption().matches(options::OPT_mips16); +} + +static bool isMicroMips(const ArgList &Args) { + Arg *A = Args.getLastArg(options::OPT_mmicromips, + options::OPT_mno_micromips); + return A && A->getOption().matches(options::OPT_mmicromips); +} + // FIXME: There is the same routine in the Tools.cpp. static bool hasMipsN32ABIArg(const ArgList &Args) { Arg *A = Args.getLastArg(options::OPT_mabi_EQ); return A && (A->getValue() == StringRef("n32")); } -static StringRef getTargetMultiarchSuffix(llvm::Triple::ArchType TargetArch, - const ArgList &Args) { - if (TargetArch == llvm::Triple::x86_64 || - TargetArch == llvm::Triple::ppc64) - return "/64"; +static void appendMipsTargetSuffix(std::string &Path, + llvm::Triple::ArchType TargetArch, + const ArgList &Args) { + if (isMips16(Args)) + Path += "/mips16"; + else if (isMicroMips(Args)) + Path += "/micromips"; + + if (isSoftFloatABI(Args)) + Path += "/soft-float"; + if (TargetArch == llvm::Triple::mipsel || + TargetArch == llvm::Triple::mips64el) + Path += "/el"; +} + +static StringRef getMipsTargetABISuffix(llvm::Triple::ArchType TargetArch, + const ArgList &Args) { if (TargetArch == llvm::Triple::mips64 || - TargetArch == llvm::Triple::mips64el) { - if (hasMipsN32ABIArg(Args)) - return "/n32"; - else - return "/64"; - } + TargetArch == llvm::Triple::mips64el) + return hasMipsN32ABIArg(Args) ? "/n32" : "/64"; return "/32"; } +static bool findTargetMultiarchSuffix(std::string &Suffix, + StringRef Path, + llvm::Triple::ArchType TargetArch, + const ArgList &Args) { + if (isMipsArch(TargetArch)) { + StringRef ABISuffix = getMipsTargetABISuffix(TargetArch, Args); + + // First build and check a complex path to crtbegin.o + // depends on command line options (-mips16, -msoft-float, ...) + // like mips-linux-gnu/4.7/mips16/soft-float/el/crtbegin.o + appendMipsTargetSuffix(Suffix, TargetArch, Args); + + if (TargetArch == llvm::Triple::mips64 || + TargetArch == llvm::Triple::mips64el) + Suffix += ABISuffix; + + if (llvm::sys::fs::exists(Path + Suffix + "/crtbegin.o")) + return true; + + // Then fall back and probe a simple case like + // mips-linux-gnu/4.7/32/crtbegin.o + Suffix = ABISuffix; + return llvm::sys::fs::exists(Path + Suffix + "/crtbegin.o"); + } + + if (TargetArch == llvm::Triple::x86_64 || + TargetArch == llvm::Triple::ppc64 || + TargetArch == llvm::Triple::systemz) + Suffix = "/64"; + else + Suffix = "/32"; + + return llvm::sys::fs::exists(Path + Suffix + "/crtbegin.o"); +} + void Generic_GCC::GCCInstallationDetector::ScanLibDirForGCCTriple( llvm::Triple::ArchType TargetArch, const ArgList &Args, const std::string &LibDir, @@ -1328,9 +1422,11 @@ void Generic_GCC::GCCInstallationDetector::ScanLibDirForGCCTriple( // *if* there is a subdirectory of the right name with crtbegin.o in it, // we use that. If not, and if not a multiarch triple, we look for // crtbegin.o without the subdirectory. - StringRef MultiarchSuffix = getTargetMultiarchSuffix(TargetArch, Args); - if (llvm::sys::fs::exists(LI->path() + MultiarchSuffix + "/crtbegin.o")) { - GCCMultiarchSuffix = MultiarchSuffix.str(); + + std::string MultiarchSuffix; + if (findTargetMultiarchSuffix(MultiarchSuffix, + LI->path(), TargetArch, Args)) { + GCCMultiarchSuffix = MultiarchSuffix; } else { if (NeedsMultiarchSuffix || !llvm::sys::fs::exists(LI->path() + "/crtbegin.o")) @@ -1396,6 +1492,10 @@ bool Generic_GCC::isPICDefault() const { return false; } +bool Generic_GCC::isPIEDefault() const { + return false; +} + bool Generic_GCC::isPICDefaultForced() const { return false; } @@ -1630,6 +1730,10 @@ bool TCEToolChain::isPICDefault() const { return false; } +bool TCEToolChain::isPIEDefault() const { + return false; +} + bool TCEToolChain::isPICDefaultForced() const { return false; } @@ -1773,6 +1877,42 @@ Tool *NetBSD::buildLinker() const { return new tools::netbsd::Link(*this); } +ToolChain::CXXStdlibType +NetBSD::GetCXXStdlibType(const ArgList &Args) const { + if (Arg *A = Args.getLastArg(options::OPT_stdlib_EQ)) { + StringRef Value = A->getValue(); + if (Value == "libstdc++") + return ToolChain::CST_Libstdcxx; + if (Value == "libc++") + return ToolChain::CST_Libcxx; + + getDriver().Diag(diag::err_drv_invalid_stdlib_name) + << A->getAsString(Args); + } + + return ToolChain::CST_Libstdcxx; +} + +void NetBSD::AddClangCXXStdlibIncludeArgs(const ArgList &DriverArgs, + ArgStringList &CC1Args) const { + if (DriverArgs.hasArg(options::OPT_nostdlibinc) || + DriverArgs.hasArg(options::OPT_nostdincxx)) + return; + + switch (GetCXXStdlibType(DriverArgs)) { + case ToolChain::CST_Libcxx: + addSystemInclude(DriverArgs, CC1Args, + getDriver().SysRoot + "/usr/include/c++/"); + break; + case ToolChain::CST_Libstdcxx: + addSystemInclude(DriverArgs, CC1Args, + getDriver().SysRoot + "/usr/include/g++"); + addSystemInclude(DriverArgs, CC1Args, + getDriver().SysRoot + "/usr/include/g++/backward"); + break; + } +} + /// Minix - Minix tool chain which can call as(1) and ld(1) directly. Minix::Minix(const Driver &D, const llvm::Triple& Triple, const ArgList &Args) @@ -2036,13 +2176,6 @@ static void addPathIfExists(Twine Path, ToolChain::path_list &Paths) { if (llvm::sys::fs::exists(Path)) Paths.push_back(Path.str()); } -static bool isMipsArch(llvm::Triple::ArchType Arch) { - return Arch == llvm::Triple::mips || - Arch == llvm::Triple::mipsel || - Arch == llvm::Triple::mips64 || - Arch == llvm::Triple::mips64el; -} - static bool isMipsR2Arch(llvm::Triple::ArchType Arch, const ArgList &Args) { if (Arch != llvm::Triple::mips && @@ -2079,7 +2212,7 @@ static StringRef getMultilibDir(const llvm::Triple &Triple, Linux::Linux(const Driver &D, const llvm::Triple &Triple, const ArgList &Args) : Generic_ELF(D, Triple, Args) { llvm::Triple::ArchType Arch = Triple.getArch(); - const std::string &SysRoot = getDriver().SysRoot; + std::string SysRoot = computeSysRoot(Args); // OpenSuse stores the linker with the compiler, add that to the search // path. @@ -2100,13 +2233,17 @@ Linux::Linux(const Driver &D, const llvm::Triple &Triple, const ArgList &Args) ExtraOpts.push_back("-X"); const bool IsAndroid = Triple.getEnvironment() == llvm::Triple::Android; + const bool IsMips = isMipsArch(Arch); + + if (IsMips && !SysRoot.empty()) + ExtraOpts.push_back("--sysroot=" + SysRoot); // Do not use 'gnu' hash style for Mips targets because .gnu.hash // and the MIPS ABI require .dynsym to be sorted in different ways. // .gnu.hash needs symbols to be grouped by hash code whereas the MIPS // ABI requires a mapping between the GOT and the symbol table. // Android loader does not support .gnu.hash. - if (!isMipsArch(Arch) && !IsAndroid) { + if (!IsMips && !IsAndroid) { if (IsRedhat(Distro) || IsOpenSuse(Distro) || (IsUbuntu(Distro) && Distro >= UbuntuMaverick)) ExtraOpts.push_back("--hash-style=gnu"); @@ -2171,6 +2308,15 @@ Linux::Linux(const Driver &D, const llvm::Triple &Triple, const ArgList &Args) if (IsAndroid) { addPathIfExists(LibPath + "/../" + GCCTriple.str() + "/lib", Paths); } + // Sourcery CodeBench MIPS toolchain holds some libraries under + // the parent prefix of the GCC installation. + if (IsMips) { + std::string Suffix; + appendMipsTargetSuffix(Suffix, Arch, Args); + addPathIfExists(LibPath + "/../" + GCCTriple.str() + "/lib/../" + + Multilib + Suffix, + Paths); + } } addPathIfExists(SysRoot + "/lib/" + MultiarchTriple, Paths); addPathIfExists(SysRoot + "/lib/../" + Multilib, Paths); @@ -2197,6 +2343,8 @@ Linux::Linux(const Driver &D, const llvm::Triple &Triple, const ArgList &Args) } addPathIfExists(SysRoot + "/lib", Paths); addPathIfExists(SysRoot + "/usr/lib", Paths); + + IsPIEDefault = SanitizerArgs(*this, Args).hasZeroBaseShadow(); } bool Linux::HasNativeLLVMSupport() const { @@ -2224,15 +2372,31 @@ void Linux::addClangTargetOptions(const ArgList &DriverArgs, CC1Args.push_back("-fuse-init-array"); } +std::string Linux::computeSysRoot(const ArgList &Args) const { + if (!getDriver().SysRoot.empty()) + return getDriver().SysRoot; + + if (!GCCInstallation.isValid() || !isMipsArch(getTriple().getArch())) + return std::string(); + + std::string Path = + (GCCInstallation.getInstallPath() + + "/../../../../" + GCCInstallation.getTriple().str() + "/libc").str(); + appendMipsTargetSuffix(Path, getTriple().getArch(), Args); + + return llvm::sys::fs::exists(Path) ? Path : ""; +} + void Linux::AddClangSystemIncludeArgs(const ArgList &DriverArgs, ArgStringList &CC1Args) const { const Driver &D = getDriver(); + std::string SysRoot = computeSysRoot(DriverArgs); if (DriverArgs.hasArg(options::OPT_nostdinc)) return; if (!DriverArgs.hasArg(options::OPT_nostdlibinc)) - addSystemInclude(DriverArgs, CC1Args, D.SysRoot + "/usr/local/include"); + addSystemInclude(DriverArgs, CC1Args, SysRoot + "/usr/local/include"); if (!DriverArgs.hasArg(options::OPT_nobuiltininc)) { llvm::sys::Path P(D.ResourceDir); @@ -2250,7 +2414,7 @@ void Linux::AddClangSystemIncludeArgs(const ArgList &DriverArgs, CIncludeDirs.split(dirs, ":"); for (SmallVectorImpl::iterator I = dirs.begin(), E = dirs.end(); I != E; ++I) { - StringRef Prefix = llvm::sys::path::is_absolute(*I) ? D.SysRoot : ""; + StringRef Prefix = llvm::sys::path::is_absolute(*I) ? SysRoot : ""; addExternCSystemInclude(DriverArgs, CC1Args, Prefix + *I); } return; @@ -2259,6 +2423,20 @@ void Linux::AddClangSystemIncludeArgs(const ArgList &DriverArgs, // Lacking those, try to detect the correct set of system includes for the // target triple. + // Sourcery CodeBench and modern FSF Mips toolchains put extern C + // system includes under three additional directories. + if (GCCInstallation.isValid() && isMipsArch(getTriple().getArch())) { + addExternCSystemIncludeIfExists(DriverArgs, CC1Args, + GCCInstallation.getInstallPath() + + "/include"); + + addExternCSystemIncludeIfExists(DriverArgs, CC1Args, + GCCInstallation.getInstallPath() + + "/../../../../" + + GCCInstallation.getTriple().str() + + "/libc/usr/include"); + } + // Implement generic Debian multiarch support. const StringRef X86_64MultiarchIncludeDirs[] = { "/usr/include/x86_64-linux-gnu", @@ -2324,8 +2502,8 @@ void Linux::AddClangSystemIncludeArgs(const ArgList &DriverArgs, for (ArrayRef::iterator I = MultiarchIncludeDirs.begin(), E = MultiarchIncludeDirs.end(); I != E; ++I) { - if (llvm::sys::fs::exists(D.SysRoot + *I)) { - addExternCSystemInclude(DriverArgs, CC1Args, D.SysRoot + *I); + if (llvm::sys::fs::exists(SysRoot + *I)) { + addExternCSystemInclude(DriverArgs, CC1Args, SysRoot + *I); break; } } @@ -2336,9 +2514,9 @@ void Linux::AddClangSystemIncludeArgs(const ArgList &DriverArgs, // Add an include of '/include' directly. This isn't provided by default by // system GCCs, but is often used with cross-compiling GCCs, and harmless to // add even when Clang is acting as-if it were a system compiler. - addExternCSystemInclude(DriverArgs, CC1Args, D.SysRoot + "/include"); + addExternCSystemInclude(DriverArgs, CC1Args, SysRoot + "/include"); - addExternCSystemInclude(DriverArgs, CC1Args, D.SysRoot + "/usr/include"); + addExternCSystemInclude(DriverArgs, CC1Args, SysRoot + "/usr/include"); } /// \brief Helper to add the three variant paths for a libstdc++ installation. @@ -2422,6 +2600,10 @@ void Linux::AddClangCXXStdlibIncludeArgs(const ArgList &DriverArgs, } } +bool Linux::isPIEDefault() const { + return IsPIEDefault; +} + /// DragonFly - DragonFly tool chain which can call as(1) and ld(1) directly. DragonFly::DragonFly(const Driver &D, const llvm::Triple& Triple, const ArgList &Args) @@ -2434,7 +2616,10 @@ DragonFly::DragonFly(const Driver &D, const llvm::Triple& Triple, const ArgList getFilePaths().push_back(getDriver().Dir + "/../lib"); getFilePaths().push_back("/usr/lib"); - getFilePaths().push_back("/usr/lib/gcc41"); + if (llvm::sys::fs::exists("/usr/lib/gcc47")) + getFilePaths().push_back("/usr/lib/gcc47"); + else + getFilePaths().push_back("/usr/lib/gcc44"); } Tool *DragonFly::buildAssembler() const { diff --git a/lib/Driver/ToolChains.h b/lib/Driver/ToolChains.h index 3421c53eb23..3afd8dd228b 100644 --- a/lib/Driver/ToolChains.h +++ b/lib/Driver/ToolChains.h @@ -123,6 +123,7 @@ class LLVM_LIBRARY_VISIBILITY Generic_GCC : public ToolChain { virtual bool IsUnwindTablesDefault() const; virtual bool isPICDefault() const; + virtual bool isPIEDefault() const; virtual bool isPICDefaultForced() const; protected: @@ -332,6 +333,7 @@ class LLVM_LIBRARY_VISIBILITY Darwin : public ToolChain { return ToolChain::RLT_CompilerRT; } virtual bool isPICDefault() const; + virtual bool isPIEDefault() const; virtual bool isPICDefaultForced() const; virtual bool SupportsProfiling() const; @@ -472,6 +474,11 @@ class LLVM_LIBRARY_VISIBILITY NetBSD : public Generic_ELF { virtual bool IsMathErrnoDefault() const { return false; } virtual bool IsObjCNonFragileABIDefault() const { return true; } + virtual CXXStdlibType GetCXXStdlibType(const ArgList &Args) const; + + virtual void AddClangCXXStdlibIncludeArgs(const ArgList &DriverArgs, + ArgStringList &CC1Args) const; + protected: virtual Tool *buildAssembler() const; virtual Tool *buildLinker() const; @@ -509,9 +516,11 @@ class LLVM_LIBRARY_VISIBILITY Linux : public Generic_ELF { ArgStringList &CC1Args) const; virtual void AddClangCXXStdlibIncludeArgs(const ArgList &DriverArgs, ArgStringList &CC1Args) const; + virtual bool isPIEDefault() const; std::string Linker; std::vector ExtraOpts; + bool IsPIEDefault; protected: virtual Tool *buildAssembler() const; @@ -526,6 +535,8 @@ class LLVM_LIBRARY_VISIBILITY Linux : public Generic_ELF { static bool addLibStdCXXIncludePaths(Twine Base, Twine TargetArchDir, const ArgList &DriverArgs, ArgStringList &CC1Args); + + std::string computeSysRoot(const ArgList &Args) const; }; class LLVM_LIBRARY_VISIBILITY Hexagon_TC : public Linux { @@ -562,6 +573,7 @@ class LLVM_LIBRARY_VISIBILITY TCEToolChain : public ToolChain { bool IsMathErrnoDefault() const; bool isPICDefault() const; + bool isPIEDefault() const; bool isPICDefaultForced() const; }; @@ -572,6 +584,7 @@ class LLVM_LIBRARY_VISIBILITY Windows : public ToolChain { virtual bool IsIntegratedAssemblerDefault() const; virtual bool IsUnwindTablesDefault() const; virtual bool isPICDefault() const; + virtual bool isPIEDefault() const; virtual bool isPICDefaultForced() const; virtual void AddClangSystemIncludeArgs(const ArgList &DriverArgs, diff --git a/lib/Driver/Tools.cpp b/lib/Driver/Tools.cpp index 77a72ba33a5..aba1fe4d2d9 100644 --- a/lib/Driver/Tools.cpp +++ b/lib/Driver/Tools.cpp @@ -7,6 +7,7 @@ // //===----------------------------------------------------------------------===// +#include #include "Tools.h" #include "InputInfo.h" #include "SanitizerArgs.h" @@ -544,6 +545,9 @@ static bool isSignedCharDefault(const llvm::Triple &Triple) { if (Triple.isOSDarwin()) return true; return false; + + case llvm::Triple::systemz: + return false; } } @@ -815,7 +819,9 @@ void Clang::AddARMTargetArgs(const ArgList &Args, CmdArgs.push_back("-mno-global-merge"); } - if (Args.hasArg(options::OPT_mno_implicit_float)) + if (!Args.hasFlag(options::OPT_mimplicit_float, + options::OPT_mno_implicit_float, + true)) CmdArgs.push_back("-no-implicit-float"); } @@ -851,8 +857,15 @@ static void getMipsCPUAndABI(const ArgList &Args, CPUName = A->getValue(); } - if (Arg *A = Args.getLastArg(options::OPT_mabi_EQ)) + if (Arg *A = Args.getLastArg(options::OPT_mabi_EQ)) { ABIName = A->getValue(); + // Convert a GNU style Mips ABI name to the name + // accepted by LLVM Mips backend. + ABIName = llvm::StringSwitch(ABIName) + .Case("32", "o32") + .Case("64", "n64") + .Default(ABIName); + } // Setup default CPU and ABI names. if (CPUName.empty() && ABIName.empty()) { @@ -899,8 +912,6 @@ static StringRef getGnuCompatibleMipsABIName(StringRef ABI) { // 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; if (Arg *A = Args.getLastArg(options::OPT_msoft_float, options::OPT_mhard_float, @@ -911,7 +922,7 @@ static StringRef getMipsFloatABI(const Driver &D, const ArgList &Args) { FloatABI = "hard"; else { FloatABI = A->getValue(); - if (FloatABI != "soft" && FloatABI != "single" && FloatABI != "hard") { + if (FloatABI != "soft" && FloatABI != "hard") { D.Diag(diag::err_drv_invalid_mfloat_abi) << A->getAsString(Args); FloatABI = "hard"; } @@ -944,7 +955,7 @@ static void AddTargetFeature(const ArgList &Args, } void Clang::AddMIPSTargetArgs(const ArgList &Args, - ArgStringList &CmdArgs) const { + ArgStringList &CmdArgs) const { const Driver &D = getToolChain().getDriver(); StringRef CPUName; StringRef ABIName; @@ -977,12 +988,6 @@ void Clang::AddMIPSTargetArgs(const ArgList &Args, CmdArgs.push_back("-mips16-hard-float"); } } - else if (FloatABI == "single") { - // Restrict the use of hardware floating-point - // instructions to 32-bit operations. - CmdArgs.push_back("-target-feature"); - CmdArgs.push_back("+single-float"); - } else { // Floating point operations and argument passing are hard. assert(FloatABI == "hard" && "Invalid float abi!"); @@ -990,9 +995,15 @@ void Clang::AddMIPSTargetArgs(const ArgList &Args, CmdArgs.push_back("hard"); } + AddTargetFeature(Args, CmdArgs, + options::OPT_msingle_float, options::OPT_mdouble_float, + "single-float"); AddTargetFeature(Args, CmdArgs, options::OPT_mips16, options::OPT_mno_mips16, "mips16"); + AddTargetFeature(Args, CmdArgs, + options::OPT_mmicromips, options::OPT_mno_micromips, + "micromips"); AddTargetFeature(Args, CmdArgs, options::OPT_mdsp, options::OPT_mno_dsp, "dsp"); @@ -1127,11 +1138,11 @@ static std::string getR600TargetGPU(const ArgList &Args) { if (Arg *A = Args.getLastArg(options::OPT_mcpu_EQ)) { std::string GPUName = A->getValue(); return llvm::StringSwitch(GPUName) - .Cases("rv610", "rv620", "rv630", "r600") - .Cases("rv635", "rs780", "rs880", "r600") + .Cases("rv630", "rv635", "r600") + .Cases("rv610", "rv620", "rs780", "rs880") .Case("rv740", "rv770") .Case("palm", "cedar") - .Cases("sumo", "sumo2", "redwood") + .Cases("sumo", "sumo2", "sumo") .Case("hemlock", "cypress") .Case("aruba", "cayman") .Default(GPUName.c_str()); @@ -1255,6 +1266,7 @@ void Clang::AddX86TargetArgs(const ArgList &Args, Args.hasArg(options::OPT_fapple_kext)); if (Arg *A = Args.getLastArg(options::OPT_msoft_float, options::OPT_mno_soft_float, + options::OPT_mimplicit_float, options::OPT_mno_implicit_float)) { const Option &O = A->getOption(); NoImplicitFloat = (O.matches(options::OPT_mno_implicit_float) || @@ -1452,6 +1464,18 @@ static void addExceptionArgs(const ArgList &Args, types::ID InputType, CmdArgs.push_back("-fexceptions"); } +static bool ShouldDisableAutolink(const ArgList &Args, + const ToolChain &TC) { + bool Default = true; + if (TC.getTriple().isOSDarwin()) { + // The native darwin assembler doesn't support the linker_option directives, + // so we disable them if we think the .s file will be passed to it. + Default = TC.useIntegratedAs(); + } + return !Args.hasFlag(options::OPT_fautolink, options::OPT_fno_autolink, + Default); +} + static bool ShouldDisableCFI(const ArgList &Args, const ToolChain &TC) { bool Default = true; @@ -1508,11 +1532,12 @@ static bool UseRelaxAll(Compilation &C, const ArgList &Args) { RelaxDefault); } -SanitizerArgs::SanitizerArgs(const Driver &D, const ArgList &Args) +SanitizerArgs::SanitizerArgs(const ToolChain &TC, const ArgList &Args) : Kind(0), BlacklistFile(""), MsanTrackOrigins(false), AsanZeroBaseShadow(false) { unsigned AllKinds = 0; // All kinds of sanitizers that were turned on // at least once (possibly, disabled further). + const Driver &D = TC.getDriver(); for (ArgList::const_iterator I = Args.begin(), E = Args.end(); I != E; ++I) { unsigned Add, Remove; if (!parse(D, Args, *I, Add, Remove, true)) @@ -1604,11 +1629,20 @@ SanitizerArgs::SanitizerArgs(const Driver &D, const ArgList &Args) /* Default */false); // Parse -f(no-)sanitize-address-zero-base-shadow options. - if (NeedsAsan) + if (NeedsAsan) { + bool IsAndroid = (TC.getTriple().getEnvironment() == llvm::Triple::Android); + bool ZeroBaseShadowDefault = IsAndroid; AsanZeroBaseShadow = - Args.hasFlag(options::OPT_fsanitize_address_zero_base_shadow, - options::OPT_fno_sanitize_address_zero_base_shadow, - /* Default */false); + Args.hasFlag(options::OPT_fsanitize_address_zero_base_shadow, + options::OPT_fno_sanitize_address_zero_base_shadow, + ZeroBaseShadowDefault); + // Zero-base shadow is a requirement on Android. + if (IsAndroid && !AsanZeroBaseShadow) { + D.Diag(diag::err_drv_argument_not_allowed_with) + << "-fno-sanitize-address-zero-base-shadow" + << lastArgumentForKind(D, Args, Address); + } + } } static void addSanitizerRTLinkFlagsLinux( @@ -1637,6 +1671,7 @@ static void addSanitizerRTLinkFlagsLinux( LibSanitizerArgs.begin(), LibSanitizerArgs.end()); CmdArgs.push_back("-lpthread"); + CmdArgs.push_back("-lrt"); CmdArgs.push_back("-ldl"); // If possible, use a dynamic symbols file to export the symbols from the @@ -1656,11 +1691,6 @@ static void addSanitizerRTLinkFlagsLinux( static void addAsanRTLinux(const ToolChain &TC, const ArgList &Args, ArgStringList &CmdArgs) { if(TC.getTriple().getEnvironment() == llvm::Triple::Android) { - if (!Args.hasArg(options::OPT_shared)) { - if (!Args.hasArg(options::OPT_pie)) - TC.getDriver().Diag(diag::err_drv_asan_android_requires_pie); - } - SmallString<128> LibAsan(TC.getDriver().ResourceDir); llvm::sys::path::append(LibAsan, "lib", "linux", (Twine("libclang_rt.asan-") + @@ -1668,13 +1698,6 @@ static void addAsanRTLinux(const ToolChain &TC, const ArgList &Args, CmdArgs.insert(CmdArgs.begin(), Args.MakeArgString(LibAsan)); } else { if (!Args.hasArg(options::OPT_shared)) { - bool ZeroBaseShadow = Args.hasFlag( - options::OPT_fsanitize_address_zero_base_shadow, - options::OPT_fno_sanitize_address_zero_base_shadow, false); - if (ZeroBaseShadow && !Args.hasArg(options::OPT_pie)) { - TC.getDriver().Diag(diag::err_drv_argument_only_allowed_with) << - "-fsanitize-address-zero-base-shadow" << "-pie"; - } addSanitizerRTLinkFlagsLinux(TC, Args, CmdArgs, "asan", true); } } @@ -1685,9 +1708,6 @@ static void addAsanRTLinux(const ToolChain &TC, const ArgList &Args, static void addTsanRTLinux(const ToolChain &TC, const ArgList &Args, ArgStringList &CmdArgs) { if (!Args.hasArg(options::OPT_shared)) { - if (!Args.hasArg(options::OPT_pie)) - TC.getDriver().Diag(diag::err_drv_argument_only_allowed_with) << - "-fsanitize=thread" << "-pie"; addSanitizerRTLinkFlagsLinux(TC, Args, CmdArgs, "tsan", true); } } @@ -1697,9 +1717,6 @@ static void addTsanRTLinux(const ToolChain &TC, const ArgList &Args, static void addMsanRTLinux(const ToolChain &TC, const ArgList &Args, ArgStringList &CmdArgs) { if (!Args.hasArg(options::OPT_shared)) { - if (!Args.hasArg(options::OPT_pie)) - TC.getDriver().Diag(diag::err_drv_argument_only_allowed_with) << - "-fsanitize=memory" << "-pie"; addSanitizerRTLinkFlagsLinux(TC, Args, CmdArgs, "msan", true); } } @@ -1764,14 +1781,27 @@ static bool shouldUseLeafFramePointer(const ArgList &Args, /// If the PWD environment variable is set, add a CC1 option to specify the /// debug compilation directory. static void addDebugCompDirArg(const ArgList &Args, ArgStringList &CmdArgs) { - if (const char *pwd = ::getenv("PWD")) { - // GCC also verifies that stat(pwd) and stat(".") have the same inode - // number. Not doing those because stats are slow, but we could. - if (llvm::sys::path::is_absolute(pwd)) { - std::string CompDir = pwd; - CmdArgs.push_back("-fdebug-compilation-dir"); - CmdArgs.push_back(Args.MakeArgString(CompDir)); - } + struct stat StatPWDBuf, StatDotBuf; + + const char *pwd = ::getenv("PWD"); + if (!pwd) + return; + + if (llvm::sys::path::is_absolute(pwd) && + stat(pwd, &StatPWDBuf) == 0 && + stat(".", &StatDotBuf) == 0 && + StatPWDBuf.st_ino == StatDotBuf.st_ino && + StatPWDBuf.st_dev == StatDotBuf.st_dev) { + CmdArgs.push_back("-fdebug-compilation-dir"); + CmdArgs.push_back(Args.MakeArgString(pwd)); + return; + } + + // Fall back to using getcwd. + SmallString<128> cwd; + if (!llvm::sys::fs::current_path(cwd)) { + CmdArgs.push_back("-fdebug-compilation-dir"); + CmdArgs.push_back(Args.MakeArgString(cwd)); } } @@ -1817,6 +1847,13 @@ static void SplitDebugInfo(const ToolChain &TC, Compilation &C, C.addCommand(new Command(JA, T, Exec, StripArgs)); } +static bool isOptimizationLevelFast(const ArgList &Args) { + if (Arg *A = Args.getLastArg(options::OPT_O_Group)) + if (A->getOption().matches(options::OPT_Ofast)) + return true; + return false; +} + void Clang::ConstructJob(Compilation &C, const JobAction &JA, const InputInfo &Output, const InputInfoList &Inputs, @@ -1969,6 +2006,9 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, CmdArgs.push_back("-analyzer-checker=deadcode"); + if (types::isCXX(Inputs[0].getType())) + CmdArgs.push_back("-analyzer-checker=cplusplus"); + // Enable the following experimental checkers for testing. CmdArgs.push_back("-analyzer-checker=security.insecureAPI.UncheckedReturn"); CmdArgs.push_back("-analyzer-checker=security.insecureAPI.getpw"); @@ -1997,37 +2037,38 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, CheckCodeGenerationOptions(D, Args); - // For the PIC and PIE flag options, this logic is different from the legacy - // logic in very old versions of GCC, as that logic was just a bug no one had - // ever fixed. This logic is both more rational and consistent with GCC's new - // logic now that the bugs are fixed. The last argument relating to either - // PIC or PIE wins, and no other argument is used. If the last argument is - // any flavor of the '-fno-...' arguments, both PIC and PIE are disabled. Any - // PIE option implicitly enables PIC at the same level. - bool PIE = false; - bool PIC = getToolChain().isPICDefault(); + bool PIE = getToolChain().isPIEDefault(); + bool PIC = PIE || getToolChain().isPICDefault(); bool IsPICLevelTwo = PIC; - if (Arg *A = 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)) { - Option O = A->getOption(); - if (O.matches(options::OPT_fPIC) || O.matches(options::OPT_fpic) || - O.matches(options::OPT_fPIE) || O.matches(options::OPT_fpie)) { - PIE = O.matches(options::OPT_fPIE) || O.matches(options::OPT_fpie); - PIC = PIE || O.matches(options::OPT_fPIC) || O.matches(options::OPT_fpic); - IsPICLevelTwo = O.matches(options::OPT_fPIE) || - O.matches(options::OPT_fPIC); - } else { - PIE = PIC = false; - } - } + + // For the PIC and PIE flag options, this logic is different from the + // legacy logic in very old versions of GCC, as that logic was just + // a bug no one had ever fixed. This logic is both more rational and + // consistent with GCC's new logic now that the bugs are fixed. The last + // argument relating to either PIC or PIE wins, and no other argument is + // used. If the last argument is any flavor of the '-fno-...' arguments, + // both PIC and PIE are disabled. Any PIE option implicitly enables PIC + // at the same level. + 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); // Check whether the tool chain trumps the PIC-ness decision. If the PIC-ness // is forced, then neither PIC nor PIE flags will have no effect. - if (getToolChain().isPICDefaultForced()) { - PIE = false; - PIC = getToolChain().isPICDefault(); - IsPICLevelTwo = PIC; + if (!getToolChain().isPICDefaultForced()) { + if (LastPICArg) { + Option O = LastPICArg->getOption(); + if (O.matches(options::OPT_fPIC) || O.matches(options::OPT_fpic) || + O.matches(options::OPT_fPIE) || O.matches(options::OPT_fpie)) { + PIE = O.matches(options::OPT_fPIE) || O.matches(options::OPT_fpie); + PIC = PIE || O.matches(options::OPT_fPIC) || + O.matches(options::OPT_fpic); + IsPICLevelTwo = O.matches(options::OPT_fPIE) || + O.matches(options::OPT_fPIC); + } else { + PIE = PIC = false; + } + } } // Inroduce a Darwin-specific hack. If the default is PIC but the flags @@ -2101,7 +2142,13 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, if (!Args.hasFlag(options::OPT_fzero_initialized_in_bss, options::OPT_fno_zero_initialized_in_bss)) CmdArgs.push_back("-mno-zero-initialized-in-bss"); - if (!Args.hasFlag(options::OPT_fstrict_aliasing, + + bool OFastEnabled = isOptimizationLevelFast(Args); + // If -Ofast is the optimization level, then -fstrict-aliasing should be + // enabled. This alias option is being used to simplify the hasFlag logic. + OptSpecifier StrictAliasingAliasOption = OFastEnabled ? options::OPT_Ofast : + options::OPT_fstrict_aliasing; + if (!Args.hasFlag(options::OPT_fstrict_aliasing, StrictAliasingAliasOption, options::OPT_fno_strict_aliasing, getToolChain().IsStrictAliasingDefault())) CmdArgs.push_back("-relaxed-aliasing"); @@ -2117,13 +2164,18 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, // Handle segmented stacks. if (Args.hasArg(options::OPT_fsplit_stack)) CmdArgs.push_back("-split-stacks"); + + // If -Ofast is the optimization level, then -ffast-math should be enabled. + // This alias option is being used to simplify the getLastArg logic. + OptSpecifier FastMathAliasOption = OFastEnabled ? options::OPT_Ofast : + options::OPT_ffast_math; // Handle various floating point optimization flags, mapping them to the // appropriate LLVM code generation flags. The pattern for all of these is to // default off the codegen optimizations, and if any flag enables them and no // flag disables them after the flag enabling them, enable the codegen // optimization. This is complicated by several "umbrella" flags. - if (Arg *A = Args.getLastArg(options::OPT_ffast_math, + if (Arg *A = Args.getLastArg(options::OPT_ffast_math, FastMathAliasOption, options::OPT_fno_fast_math, options::OPT_ffinite_math_only, options::OPT_fno_finite_math_only, @@ -2133,7 +2185,7 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, A->getOption().getID() != options::OPT_fno_finite_math_only && A->getOption().getID() != options::OPT_fhonor_infinities) CmdArgs.push_back("-menable-no-infs"); - if (Arg *A = Args.getLastArg(options::OPT_ffast_math, + if (Arg *A = Args.getLastArg(options::OPT_ffast_math, FastMathAliasOption, options::OPT_fno_fast_math, options::OPT_ffinite_math_only, options::OPT_fno_finite_math_only, @@ -2146,7 +2198,7 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, // -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, + if (Arg *A = Args.getLastArg(options::OPT_ffast_math, FastMathAliasOption, options::OPT_fno_fast_math, options::OPT_fmath_errno, options::OPT_fno_math_errno)) @@ -2159,7 +2211,7 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, // entire set of LLVM optimizations, so collect them through all the flag // madness. bool AssociativeMath = false; - if (Arg *A = Args.getLastArg(options::OPT_ffast_math, + if (Arg *A = Args.getLastArg(options::OPT_ffast_math, FastMathAliasOption, options::OPT_fno_fast_math, options::OPT_funsafe_math_optimizations, options::OPT_fno_unsafe_math_optimizations, @@ -2170,7 +2222,7 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, A->getOption().getID() != options::OPT_fno_associative_math) AssociativeMath = true; bool ReciprocalMath = false; - if (Arg *A = Args.getLastArg(options::OPT_ffast_math, + if (Arg *A = Args.getLastArg(options::OPT_ffast_math, FastMathAliasOption, options::OPT_fno_fast_math, options::OPT_funsafe_math_optimizations, options::OPT_fno_unsafe_math_optimizations, @@ -2181,7 +2233,7 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, A->getOption().getID() != options::OPT_fno_reciprocal_math) ReciprocalMath = true; bool SignedZeros = true; - if (Arg *A = Args.getLastArg(options::OPT_ffast_math, + if (Arg *A = Args.getLastArg(options::OPT_ffast_math, FastMathAliasOption, options::OPT_fno_fast_math, options::OPT_funsafe_math_optimizations, options::OPT_fno_unsafe_math_optimizations, @@ -2192,7 +2244,7 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, A->getOption().getID() != options::OPT_fsigned_zeros) SignedZeros = false; bool TrappingMath = true; - if (Arg *A = Args.getLastArg(options::OPT_ffast_math, + if (Arg *A = Args.getLastArg(options::OPT_ffast_math, FastMathAliasOption, options::OPT_fno_fast_math, options::OPT_funsafe_math_optimizations, options::OPT_fno_unsafe_math_optimizations, @@ -2208,7 +2260,7 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, // Validate and pass through -fp-contract option. - if (Arg *A = Args.getLastArg(options::OPT_ffast_math, + if (Arg *A = Args.getLastArg(options::OPT_ffast_math, FastMathAliasOption, options::OPT_fno_fast_math, options::OPT_ffp_contract)) { if (A->getOption().getID() == options::OPT_ffp_contract) { @@ -2219,7 +2271,8 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, D.Diag(diag::err_drv_unsupported_option_argument) << A->getOption().getName() << Val; } - } else if (A->getOption().getID() == options::OPT_ffast_math) { + } else if (A->getOption().matches(options::OPT_ffast_math) || + (OFastEnabled && A->getOption().matches(options::OPT_Ofast))) { // If fast-math is set then set the fp-contract mode to fast. CmdArgs.push_back(Args.MakeArgString("-ffp-contract=fast")); } @@ -2230,9 +2283,10 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, // preprocessor macros. This is distinct from enabling any optimizations as // these options induce language changes which must survive serialization // and deserialization, etc. - if (Arg *A = Args.getLastArg(options::OPT_ffast_math, options::OPT_fno_fast_math)) - if (A->getOption().matches(options::OPT_ffast_math)) - CmdArgs.push_back("-ffast-math"); + if (Arg *A = Args.getLastArg(options::OPT_ffast_math, FastMathAliasOption, + options::OPT_fno_fast_math)) + if (!A->getOption().matches(options::OPT_fno_fast_math)) + CmdArgs.push_back("-ffast-math"); if (Arg *A = Args.getLastArg(options::OPT_ffinite_math_only, options::OPT_fno_fast_math)) if (A->getOption().matches(options::OPT_ffinite_math_only)) CmdArgs.push_back("-ffinite-math-only"); @@ -2597,6 +2651,9 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, if (ShouldDisableDwarfDirectory(Args, getToolChain())) CmdArgs.push_back("-fno-dwarf-directory-asm"); + if (ShouldDisableAutolink(Args, getToolChain())) + CmdArgs.push_back("-fno-autolink"); + // Add in -fdebug-compilation-dir if necessary. addDebugCompDirArg(Args, CmdArgs); @@ -2705,7 +2762,7 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, Args.AddLastArg(CmdArgs, options::OPT_fdiagnostics_show_template_tree); Args.AddLastArg(CmdArgs, options::OPT_fno_elide_type); - SanitizerArgs Sanitize(D, Args); + SanitizerArgs Sanitize(getToolChain(), Args); Sanitize.addArgs(Args, CmdArgs); if (!Args.hasFlag(options::OPT_fsanitize_recover, @@ -2892,16 +2949,6 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, Args.AddLastArg(CmdArgs, options::OPT_fmodules_prune_interval); Args.AddLastArg(CmdArgs, options::OPT_fmodules_prune_after); - // -fmodules-autolink (on by default when modules is enabled) automatically - // links against libraries for imported modules. This requires the - // integrated assembler. - if (HaveModules && getToolChain().useIntegratedAs() && - Args.hasFlag(options::OPT_fmodules_autolink, - options::OPT_fno_modules_autolink, - true)) { - CmdArgs.push_back("-fmodules-autolink"); - } - // -faccess-control is default. if (Args.hasFlag(options::OPT_fno_access_control, options::OPT_faccess_control, @@ -3198,9 +3245,42 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, // Color diagnostics are the default, unless the terminal doesn't support // them. - if (Args.hasFlag(options::OPT_fcolor_diagnostics, - options::OPT_fno_color_diagnostics, - llvm::sys::Process::StandardErrHasColors())) + // Support both clang's -f[no-]color-diagnostics and gcc's + // -f[no-]diagnostics-colors[=never|always|auto]. + enum { Colors_On, Colors_Off, Colors_Auto } ShowColors = Colors_Auto; + for (ArgList::const_iterator it = Args.begin(), ie = Args.end(); + it != ie; ++it) { + const Option &O = (*it)->getOption(); + if (!O.matches(options::OPT_fcolor_diagnostics) && + !O.matches(options::OPT_fdiagnostics_color) && + !O.matches(options::OPT_fno_color_diagnostics) && + !O.matches(options::OPT_fno_diagnostics_color) && + !O.matches(options::OPT_fdiagnostics_color_EQ)) + continue; + + (*it)->claim(); + if (O.matches(options::OPT_fcolor_diagnostics) || + O.matches(options::OPT_fdiagnostics_color)) { + ShowColors = Colors_On; + } else if (O.matches(options::OPT_fno_color_diagnostics) || + O.matches(options::OPT_fno_diagnostics_color)) { + ShowColors = Colors_Off; + } else { + assert(O.matches(options::OPT_fdiagnostics_color_EQ)); + StringRef value((*it)->getValue()); + if (value == "always") + ShowColors = Colors_On; + else if (value == "never") + ShowColors = Colors_Off; + else if (value == "auto") + ShowColors = Colors_Auto; + else + getToolChain().getDriver().Diag(diag::err_drv_clang_unsupported) + << ("-fdiagnostics-color=" + value).str(); + } + } + if (ShowColors == Colors_On || + (ShowColors == Colors_Auto && llvm::sys::Process::StandardErrHasColors())) CmdArgs.push_back("-fcolor-diagnostics"); if (!Args.hasFlag(options::OPT_fshow_source_location, @@ -3222,8 +3302,13 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, false)) CmdArgs.push_back("-fasm-blocks"); + // If -Ofast is the optimization level, then -fvectorize should be enabled. + // This alias option is being used to simplify the hasFlag logic. + OptSpecifier VectorizeAliasOption = OFastEnabled ? options::OPT_Ofast : + options::OPT_fvectorize; + // -fvectorize is default. - if (Args.hasFlag(options::OPT_fvectorize, + if (Args.hasFlag(options::OPT_fvectorize, VectorizeAliasOption, options::OPT_fno_vectorize, true)) { CmdArgs.push_back("-backend-option"); CmdArgs.push_back("-vectorize-loops"); @@ -3233,7 +3318,14 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, if (Args.hasFlag(options::OPT_fslp_vectorize, options::OPT_fno_slp_vectorize, false)) { CmdArgs.push_back("-backend-option"); - CmdArgs.push_back("-vectorize"); + CmdArgs.push_back("-vectorize-slp"); + } + + // -fno-slp-vectorize-aggressive is default. + if (Args.hasFlag(options::OPT_fslp_vectorize_aggressive, + options::OPT_fno_slp_vectorize_aggressive, false)) { + CmdArgs.push_back("-backend-option"); + CmdArgs.push_back("-vectorize-slp-aggressive"); } if (Arg *A = Args.getLastArg(options::OPT_fshow_overloads_EQ)) @@ -3298,6 +3390,8 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, // Forward -fcomment-block-commands to -cc1. Args.AddAllArgs(CmdArgs, options::OPT_fcomment_block_commands); + // Forward -fparse-all-comments to -cc1. + Args.AddAllArgs(CmdArgs, options::OPT_fparse_all_comments); // Forward -Xclang arguments to -cc1, and -mllvm arguments to the LLVM option // parser. @@ -3673,6 +3767,14 @@ void ClangAs::ConstructJob(Compilation &C, const JobAction &JA, const char *Exec = getToolChain().getDriver().getClangProgramPath(); C.addCommand(new Command(JA, *this, Exec, CmdArgs)); + + // Handle the debug info splitting at object creation time if we're + // creating an object. + // TODO: Currently only works on linux with newer objcopy. + if (Args.hasArg(options::OPT_gsplit_dwarf) && + (getToolChain().getTriple().getOS() == llvm::Triple::Linux)) + SplitDebugInfo(getToolChain(), C, *this, JA, Args, Output, + SplitDebugName(Args, Inputs)); } void gcc::Common::ConstructJob(Compilation &C, const JobAction &JA, @@ -3867,7 +3969,6 @@ void hexagon::Assemble::ConstructJob(Compilation &C, const JobAction &JA, CmdArgs.push_back( Args.MakeArgString(std::string("-G") + SmallDataThreshold)); - Args.AddAllArgs(CmdArgs, options::OPT_g_Group); Args.AddAllArgValues(CmdArgs, options::OPT_Wa_COMMA, options::OPT_Xassembler); @@ -4579,7 +4680,7 @@ void darwin::Link::ConstructJob(Compilation &C, const JobAction &JA, Args.AddAllArgs(CmdArgs, options::OPT_L); - SanitizerArgs Sanitize(getToolChain().getDriver(), Args); + SanitizerArgs Sanitize(getToolChain(), Args); // If we're building a dynamic lib with -fsanitize=address, // unresolved symbols may appear. Mark all // of them as dynamic_lookup. Linking executables is handled in @@ -5706,6 +5807,12 @@ void gnutools::Assemble::ConstructJob(Compilation &C, const JobAction &JA, else CmdArgs.push_back("-EL"); + Args.AddLastArg(CmdArgs, options::OPT_mips16, options::OPT_mno_mips16); + Args.AddLastArg(CmdArgs, options::OPT_mmicromips, + options::OPT_mno_micromips); + Args.AddLastArg(CmdArgs, options::OPT_mdsp, options::OPT_mno_dsp); + Args.AddLastArg(CmdArgs, options::OPT_mdspr2, options::OPT_mno_dspr2); + 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, @@ -5717,6 +5824,9 @@ void gnutools::Assemble::ConstructJob(Compilation &C, const JobAction &JA, LastPICArg->getOption().matches(options::OPT_fpie))) { CmdArgs.push_back("-KPIC"); } + } else if (getToolChain().getArch() == llvm::Triple::systemz) { + // At the moment we always produce z10 code. + CmdArgs.push_back("-march=z10"); } Args.AddAllArgValues(CmdArgs, options::OPT_Wa_COMMA, @@ -5784,6 +5894,10 @@ void gnutools::Link::ConstructJob(Compilation &C, const JobAction &JA, const Driver &D = ToolChain.getDriver(); const bool isAndroid = ToolChain.getTriple().getEnvironment() == llvm::Triple::Android; + SanitizerArgs Sanitize(getToolChain(), Args); + const bool IsPIE = + !Args.hasArg(options::OPT_shared) && + (Args.hasArg(options::OPT_pie) || Sanitize.hasZeroBaseShadow()); ArgStringList CmdArgs; @@ -5798,7 +5912,7 @@ void gnutools::Link::ConstructJob(Compilation &C, const JobAction &JA, if (!D.SysRoot.empty()) CmdArgs.push_back(Args.MakeArgString("--sysroot=" + D.SysRoot)); - if (Args.hasArg(options::OPT_pie) && !Args.hasArg(options::OPT_shared)) + if (IsPIE) CmdArgs.push_back("-pie"); if (Args.hasArg(options::OPT_rdynamic)) @@ -5844,6 +5958,8 @@ void gnutools::Link::ConstructJob(Compilation &C, const JobAction &JA, else CmdArgs.push_back("elf64ltsmip"); } + else if (ToolChain.getArch() == llvm::Triple::systemz) + CmdArgs.push_back("elf64_s390"); else CmdArgs.push_back("elf_x86_64"); @@ -5890,7 +6006,8 @@ void gnutools::Link::ConstructJob(Compilation &C, const JobAction &JA, } else if (ToolChain.getArch() == llvm::Triple::ppc) CmdArgs.push_back("/lib/ld.so.1"); - else if (ToolChain.getArch() == llvm::Triple::ppc64) + else if (ToolChain.getArch() == llvm::Triple::ppc64 || + ToolChain.getArch() == llvm::Triple::systemz) CmdArgs.push_back("/lib64/ld64.so.1"); else CmdArgs.push_back("/lib64/ld-linux-x86-64.so.2"); @@ -5904,7 +6021,7 @@ void gnutools::Link::ConstructJob(Compilation &C, const JobAction &JA, if (!isAndroid) { const char *crt1 = NULL; if (!Args.hasArg(options::OPT_shared)){ - if (Args.hasArg(options::OPT_pie)) + if (IsPIE) crt1 = "Scrt1.o"; else crt1 = "crt1.o"; @@ -5920,7 +6037,7 @@ void gnutools::Link::ConstructJob(Compilation &C, const JobAction &JA, crtbegin = isAndroid ? "crtbegin_static.o" : "crtbeginT.o"; else if (Args.hasArg(options::OPT_shared)) crtbegin = isAndroid ? "crtbegin_so.o" : "crtbeginS.o"; - else if (Args.hasArg(options::OPT_pie)) + else if (IsPIE) crtbegin = isAndroid ? "crtbegin_dynamic.o" : "crtbeginS.o"; else crtbegin = isAndroid ? "crtbegin_dynamic.o" : "crtbegin.o"; @@ -5971,8 +6088,6 @@ void gnutools::Link::ConstructJob(Compilation &C, const JobAction &JA, AddLinkerInputs(ToolChain, Inputs, Args, CmdArgs); - SanitizerArgs Sanitize(D, Args); - // Call these before we add the C++ ABI library. if (Sanitize.needsUbsanRt()) addUbsanRTLinux(getToolChain(), Args, CmdArgs, D.CCCIsCXX, @@ -6030,7 +6145,7 @@ void gnutools::Link::ConstructJob(Compilation &C, const JobAction &JA, const char *crtend; if (Args.hasArg(options::OPT_shared)) crtend = isAndroid ? "crtend_so.o" : "crtendS.o"; - else if (Args.hasArg(options::OPT_pie)) + else if (IsPIE) crtend = isAndroid ? "crtend_android.o" : "crtendS.o"; else crtend = isAndroid ? "crtend_android.o" : "crtend.o"; @@ -6162,21 +6277,29 @@ void dragonfly::Link::ConstructJob(Compilation &C, const JobAction &JA, const InputInfoList &Inputs, const ArgList &Args, const char *LinkingOutput) const { + bool UseGCC47 = false; const Driver &D = getToolChain().getDriver(); ArgStringList CmdArgs; + if (llvm::sys::fs::exists("/usr/lib/gcc47", UseGCC47)) + UseGCC47 = false; + if (!D.SysRoot.empty()) CmdArgs.push_back(Args.MakeArgString("--sysroot=" + D.SysRoot)); + CmdArgs.push_back("--eh-frame-hdr"); if (Args.hasArg(options::OPT_static)) { CmdArgs.push_back("-Bstatic"); } else { + if (Args.hasArg(options::OPT_rdynamic)) + CmdArgs.push_back("-export-dynamic"); if (Args.hasArg(options::OPT_shared)) CmdArgs.push_back("-Bshareable"); else { CmdArgs.push_back("-dynamic-linker"); CmdArgs.push_back("/usr/libexec/ld-elf.so.2"); } + CmdArgs.push_back("--hash-style=both"); } // When building 32-bit code on DragonFly/pc64, we have to explicitly @@ -6196,18 +6319,26 @@ void dragonfly::Link::ConstructJob(Compilation &C, const JobAction &JA, if (!Args.hasArg(options::OPT_nostdlib) && !Args.hasArg(options::OPT_nostartfiles)) { if (!Args.hasArg(options::OPT_shared)) { - CmdArgs.push_back( - Args.MakeArgString(getToolChain().GetFilePath("crt1.o"))); - CmdArgs.push_back( - Args.MakeArgString(getToolChain().GetFilePath("crti.o"))); - CmdArgs.push_back( - Args.MakeArgString(getToolChain().GetFilePath("crtbegin.o"))); - } else { - CmdArgs.push_back( - Args.MakeArgString(getToolChain().GetFilePath("crti.o"))); - CmdArgs.push_back( - Args.MakeArgString(getToolChain().GetFilePath("crtbeginS.o"))); + if (Args.hasArg(options::OPT_pg)) + CmdArgs.push_back(Args.MakeArgString( + getToolChain().GetFilePath("gcrt1.o"))); + else { + if (Args.hasArg(options::OPT_pie)) + CmdArgs.push_back(Args.MakeArgString( + getToolChain().GetFilePath("Scrt1.o"))); + else + CmdArgs.push_back(Args.MakeArgString( + getToolChain().GetFilePath("crt1.o"))); + } } + CmdArgs.push_back(Args.MakeArgString( + getToolChain().GetFilePath("crti.o"))); + if (Args.hasArg(options::OPT_shared) || Args.hasArg(options::OPT_pie)) + CmdArgs.push_back(Args.MakeArgString( + getToolChain().GetFilePath("crtbeginS.o"))); + else + CmdArgs.push_back(Args.MakeArgString( + getToolChain().GetFilePath("crtbegin.o"))); } Args.AddAllArgs(CmdArgs, options::OPT_L); @@ -6220,20 +6351,19 @@ void dragonfly::Link::ConstructJob(Compilation &C, const JobAction &JA, !Args.hasArg(options::OPT_nodefaultlibs)) { // FIXME: GCC passes on -lgcc, -lgcc_pic and a whole lot of // rpaths - CmdArgs.push_back("-L/usr/lib/gcc41"); + if (UseGCC47) + CmdArgs.push_back("-L/usr/lib/gcc47"); + else + CmdArgs.push_back("-L/usr/lib/gcc44"); if (!Args.hasArg(options::OPT_static)) { - CmdArgs.push_back("-rpath"); - CmdArgs.push_back("/usr/lib/gcc41"); - - CmdArgs.push_back("-rpath-link"); - CmdArgs.push_back("/usr/lib/gcc41"); - - CmdArgs.push_back("-rpath"); - CmdArgs.push_back("/usr/lib"); - - CmdArgs.push_back("-rpath-link"); - CmdArgs.push_back("/usr/lib"); + if (UseGCC47) { + CmdArgs.push_back("-rpath"); + CmdArgs.push_back("/usr/lib/gcc47"); + } else { + CmdArgs.push_back("-rpath"); + CmdArgs.push_back("/usr/lib/gcc44"); + } } if (D.CCCIsCXX) { @@ -6241,13 +6371,6 @@ void dragonfly::Link::ConstructJob(Compilation &C, const JobAction &JA, CmdArgs.push_back("-lm"); } - if (Args.hasArg(options::OPT_shared)) { - CmdArgs.push_back("-lgcc_pic"); - } else { - CmdArgs.push_back("-lgcc"); - } - - if (Args.hasArg(options::OPT_pthread)) CmdArgs.push_back("-lpthread"); @@ -6255,23 +6378,42 @@ void dragonfly::Link::ConstructJob(Compilation &C, const JobAction &JA, CmdArgs.push_back("-lc"); } - if (Args.hasArg(options::OPT_shared)) { - CmdArgs.push_back("-lgcc_pic"); + if (UseGCC47) { + if (Args.hasArg(options::OPT_static) || + Args.hasArg(options::OPT_static_libgcc)) { + CmdArgs.push_back("-lgcc"); + CmdArgs.push_back("-lgcc_eh"); + } else { + if (Args.hasArg(options::OPT_shared_libgcc)) { + CmdArgs.push_back("-lgcc_pic"); + if (!Args.hasArg(options::OPT_shared)) + CmdArgs.push_back("-lgcc"); + } else { + CmdArgs.push_back("-lgcc"); + CmdArgs.push_back("--as-needed"); + CmdArgs.push_back("-lgcc_pic"); + CmdArgs.push_back("--no-as-needed"); + } + } } else { - CmdArgs.push_back("-lgcc"); + if (Args.hasArg(options::OPT_shared)) { + CmdArgs.push_back("-lgcc_pic"); + } else { + CmdArgs.push_back("-lgcc"); + } } } if (!Args.hasArg(options::OPT_nostdlib) && !Args.hasArg(options::OPT_nostartfiles)) { - if (!Args.hasArg(options::OPT_shared)) + if (Args.hasArg(options::OPT_shared) || Args.hasArg(options::OPT_pie)) CmdArgs.push_back(Args.MakeArgString( - getToolChain().GetFilePath("crtend.o"))); + getToolChain().GetFilePath("crtendS.o"))); else CmdArgs.push_back(Args.MakeArgString( - getToolChain().GetFilePath("crtendS.o"))); + getToolChain().GetFilePath("crtend.o"))); CmdArgs.push_back(Args.MakeArgString( - getToolChain().GetFilePath("crtn.o"))); + getToolChain().GetFilePath("crtn.o"))); } addProfileRT(getToolChain(), Args, CmdArgs, getToolChain().getTriple()); diff --git a/lib/Driver/WindowsToolChain.cpp b/lib/Driver/WindowsToolChain.cpp index dac7e77d608..622c49296de 100644 --- a/lib/Driver/WindowsToolChain.cpp +++ b/lib/Driver/WindowsToolChain.cpp @@ -60,6 +60,10 @@ bool Windows::isPICDefault() const { return getArch() == llvm::Triple::x86_64; } +bool Windows::isPIEDefault() const { + return false; +} + bool Windows::isPICDefaultForced() const { return getArch() == llvm::Triple::x86_64; } diff --git a/lib/Edit/EditedSource.cpp b/lib/Edit/EditedSource.cpp index dd99ca92801..34b5e62333a 100644 --- a/lib/Edit/EditedSource.cpp +++ b/lib/Edit/EditedSource.cpp @@ -188,6 +188,8 @@ void EditedSource::commitRemove(SourceLocation OrigLoc, unsigned diff = EndOffs.getOffset() - TopEnd.getOffset(); TopEnd = EndOffs; TopFA->RemoveLen += diff; + if (B == BeginOffs) + TopFA->Text = StringRef(); ++I; } diff --git a/lib/Format/BreakableToken.cpp b/lib/Format/BreakableToken.cpp new file mode 100644 index 00000000000..3e2e0ce7cf3 --- /dev/null +++ b/lib/Format/BreakableToken.cpp @@ -0,0 +1,179 @@ +//===--- BreakableToken.cpp - Format C++ code -----------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief Contains implementation of BreakableToken class and classes derived +/// from it. +/// +//===----------------------------------------------------------------------===// + +#include "BreakableToken.h" +#include "llvm/ADT/STLExtras.h" +#include + +namespace clang { +namespace format { + +BreakableToken::Split BreakableComment::getSplit(unsigned LineIndex, + unsigned TailOffset, + unsigned ColumnLimit) const { + StringRef Text = getLine(LineIndex).substr(TailOffset); + unsigned ContentStartColumn = getContentStartColumn(LineIndex, TailOffset); + if (ColumnLimit <= ContentStartColumn + 1) + return Split(StringRef::npos, 0); + + unsigned MaxSplit = ColumnLimit - ContentStartColumn + 1; + StringRef::size_type SpaceOffset = Text.rfind(' ', MaxSplit); + if (SpaceOffset == StringRef::npos || + Text.find_last_not_of(' ', SpaceOffset) == StringRef::npos) { + SpaceOffset = Text.find(' ', MaxSplit); + } + if (SpaceOffset != StringRef::npos && SpaceOffset != 0) { + StringRef BeforeCut = Text.substr(0, SpaceOffset).rtrim(); + StringRef AfterCut = Text.substr(SpaceOffset).ltrim(); + return BreakableToken::Split(BeforeCut.size(), + AfterCut.begin() - BeforeCut.end()); + } + return BreakableToken::Split(StringRef::npos, 0); +} + +void BreakableComment::insertBreak(unsigned LineIndex, unsigned TailOffset, + Split Split, bool InPPDirective, + WhitespaceManager &Whitespaces) { + StringRef Text = getLine(LineIndex).substr(TailOffset); + StringRef AdditionalPrefix = Decoration; + if (Text.size() == Split.first + Split.second) { + // For all but the last line handle trailing space in trimLine. + if (LineIndex < Lines.size() - 1) + return; + // For the last line we need to break before "*/", but not to add "* ". + AdditionalPrefix = ""; + } + + unsigned WhitespaceStartColumn = + getContentStartColumn(LineIndex, TailOffset) + Split.first; + unsigned BreakOffset = Text.data() - TokenText.data() + Split.first; + unsigned CharsToRemove = Split.second; + Whitespaces.breakToken(Tok, BreakOffset, CharsToRemove, "", AdditionalPrefix, + InPPDirective, IndentAtLineBreak, + WhitespaceStartColumn); +} + +BreakableBlockComment::BreakableBlockComment(const SourceManager &SourceMgr, + const AnnotatedToken &Token, + unsigned StartColumn) + : BreakableComment(SourceMgr, Token.FormatTok, StartColumn + 2) { + assert(TokenText.startswith("/*") && TokenText.endswith("*/")); + + OriginalStartColumn = + SourceMgr.getSpellingColumnNumber(Tok.getStartOfNonWhitespace()) - 1; + + TokenText.substr(2, TokenText.size() - 4).split(Lines, "\n"); + + bool NeedsStar = true; + CommonPrefixLength = UINT_MAX; + if (Lines.size() == 1) { + if (Token.Parent == 0) { + // Standalone block comments will be aligned and prefixed with *s. + CommonPrefixLength = OriginalStartColumn + 1; + } else { + // Trailing comments can start on arbitrary column, and available + // horizontal space can be too small to align consecutive lines with + // the first one. We could, probably, align them to current + // indentation level, but now we just wrap them without indentation + // and stars. + CommonPrefixLength = 0; + NeedsStar = false; + } + } else { + for (size_t i = 1; i < Lines.size(); ++i) { + size_t FirstNonWhitespace = Lines[i].find_first_not_of(" "); + if (FirstNonWhitespace != StringRef::npos) { + NeedsStar = NeedsStar && (Lines[i][FirstNonWhitespace] == '*'); + CommonPrefixLength = + std::min(CommonPrefixLength, FirstNonWhitespace); + } + } + } + if (CommonPrefixLength == UINT_MAX) + CommonPrefixLength = 0; + + Decoration = NeedsStar ? "* " : ""; + + IndentAtLineBreak = + std::max(StartColumn - OriginalStartColumn + CommonPrefixLength, 0); +} + +void BreakableBlockComment::alignLines(WhitespaceManager &Whitespaces) { + SourceLocation TokenLoc = Tok.getStartOfNonWhitespace(); + int IndentDelta = (StartColumn - 2) - OriginalStartColumn; + if (IndentDelta > 0) { + std::string WhiteSpace(IndentDelta, ' '); + for (size_t i = 1; i < Lines.size(); ++i) { + Whitespaces.addReplacement( + TokenLoc.getLocWithOffset(Lines[i].data() - TokenText.data()), 0, + WhiteSpace); + } + } else if (IndentDelta < 0) { + std::string WhiteSpace(-IndentDelta, ' '); + // Check that the line is indented enough. + for (size_t i = 1; i < Lines.size(); ++i) { + if (!Lines[i].startswith(WhiteSpace)) + return; + } + for (size_t i = 1; i < Lines.size(); ++i) { + Whitespaces.addReplacement( + TokenLoc.getLocWithOffset(Lines[i].data() - TokenText.data()), + -IndentDelta, ""); + } + } + + for (unsigned i = 1; i < Lines.size(); ++i) + Lines[i] = Lines[i].substr(CommonPrefixLength + Decoration.size()); +} + +void BreakableBlockComment::trimLine(unsigned LineIndex, unsigned TailOffset, + unsigned InPPDirective, + WhitespaceManager &Whitespaces) { + if (LineIndex == Lines.size() - 1) + return; + StringRef Text = Lines[LineIndex].substr(TailOffset); + if (!Text.endswith(" ") && !InPPDirective) + return; + + StringRef TrimmedLine = Text.rtrim(); + unsigned WhitespaceStartColumn = + getLineLengthAfterSplit(LineIndex, TailOffset); + unsigned BreakOffset = TrimmedLine.end() - TokenText.data(); + unsigned CharsToRemove = Text.size() - TrimmedLine.size() + 1; + Whitespaces.breakToken(Tok, BreakOffset, CharsToRemove, "", "", InPPDirective, + 0, WhitespaceStartColumn); +} + +BreakableLineComment::BreakableLineComment(const SourceManager &SourceMgr, + const AnnotatedToken &Token, + unsigned StartColumn) + : BreakableComment(SourceMgr, Token.FormatTok, StartColumn) { + assert(TokenText.startswith("//")); + Decoration = getLineCommentPrefix(TokenText); + Lines.push_back(TokenText.substr(Decoration.size())); + IndentAtLineBreak = StartColumn; + this->StartColumn += Decoration.size(); // Start column of the contents. +} + +StringRef BreakableLineComment::getLineCommentPrefix(StringRef Comment) { + const char *KnownPrefixes[] = { "/// ", "///", "// ", "//" }; + for (size_t i = 0; i < llvm::array_lengthof(KnownPrefixes); ++i) + if (Comment.startswith(KnownPrefixes[i])) + return KnownPrefixes[i]; + return ""; +} + +} // namespace format +} // namespace clang diff --git a/lib/Format/BreakableToken.h b/lib/Format/BreakableToken.h new file mode 100644 index 00000000000..c1303183d31 --- /dev/null +++ b/lib/Format/BreakableToken.h @@ -0,0 +1,240 @@ +//===--- BreakableToken.h - Format C++ code -------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief Declares BreakableToken, BreakableStringLiteral, and +/// BreakableBlockComment classes, that contain token type-specific logic to +/// break long lines in tokens. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_FORMAT_BREAKABLETOKEN_H +#define LLVM_CLANG_FORMAT_BREAKABLETOKEN_H + +#include "TokenAnnotator.h" +#include "WhitespaceManager.h" +#include + +namespace clang { +namespace format { + +class BreakableToken { +public: + BreakableToken(const SourceManager &SourceMgr, const FormatToken &Tok, + unsigned StartColumn) + : Tok(Tok), StartColumn(StartColumn), + TokenText(SourceMgr.getCharacterData(Tok.getStartOfNonWhitespace()), + Tok.TokenLength) {} + virtual ~BreakableToken() {} + virtual unsigned getLineCount() const = 0; + virtual unsigned getLineSize(unsigned Index) const = 0; + virtual unsigned getLineLengthAfterSplit(unsigned LineIndex, + unsigned TailOffset) const = 0; + + // Contains starting character index and length of split. + typedef std::pair Split; + virtual Split getSplit(unsigned LineIndex, unsigned TailOffset, + unsigned ColumnLimit) const = 0; + virtual void insertBreak(unsigned LineIndex, unsigned TailOffset, Split Split, + bool InPPDirective, + WhitespaceManager &Whitespaces) = 0; + virtual void trimLine(unsigned LineIndex, unsigned TailOffset, + unsigned InPPDirective, + WhitespaceManager &Whitespaces) {} +protected: + const FormatToken &Tok; + unsigned StartColumn; + StringRef TokenText; +}; + +class BreakableStringLiteral : public BreakableToken { +public: + BreakableStringLiteral(const SourceManager &SourceMgr, const FormatToken &Tok, + unsigned StartColumn) + : BreakableToken(SourceMgr, Tok, StartColumn) { + assert(TokenText.startswith("\"") && TokenText.endswith("\"")); + } + + virtual unsigned getLineCount() const { return 1; } + + virtual unsigned getLineSize(unsigned Index) const { + return Tok.TokenLength - 2; // Should be in sync with getLine + } + + virtual unsigned getLineLengthAfterSplit(unsigned LineIndex, + unsigned TailOffset) const { + return getDecorationLength() + getLine().size() - TailOffset; + } + + virtual Split getSplit(unsigned LineIndex, unsigned TailOffset, + unsigned ColumnLimit) const { + StringRef Text = getLine().substr(TailOffset); + if (ColumnLimit <= getDecorationLength()) + return Split(StringRef::npos, 0); + unsigned MaxSplit = ColumnLimit - getDecorationLength(); + assert(MaxSplit < Text.size()); + StringRef::size_type SpaceOffset = Text.rfind(' ', MaxSplit); + if (SpaceOffset != StringRef::npos && SpaceOffset != 0) + return Split(SpaceOffset + 1, 0); + StringRef::size_type SlashOffset = Text.rfind('/', MaxSplit); + if (SlashOffset != StringRef::npos && SlashOffset != 0) + return Split(SlashOffset + 1, 0); + StringRef::size_type SplitPoint = getStartOfCharacter(Text, MaxSplit); + if (SplitPoint != StringRef::npos && SplitPoint > 1) + // Do not split at 0. + return Split(SplitPoint, 0); + return Split(StringRef::npos, 0); + } + + virtual void insertBreak(unsigned LineIndex, unsigned TailOffset, Split Split, + bool InPPDirective, WhitespaceManager &Whitespaces) { + unsigned WhitespaceStartColumn = StartColumn + Split.first + 2; + Whitespaces.breakToken(Tok, 1 + TailOffset + Split.first, Split.second, + "\"", "\"", InPPDirective, StartColumn, + WhitespaceStartColumn); + } + +private: + StringRef getLine() const { + // Get string without quotes. + // FIXME: Handle string prefixes. + return TokenText.substr(1, TokenText.size() - 2); + } + + unsigned getDecorationLength() const { return StartColumn + 2; } + + static StringRef::size_type getStartOfCharacter(StringRef Text, + StringRef::size_type Offset) { + StringRef::size_type NextEscape = Text.find('\\'); + while (NextEscape != StringRef::npos && NextEscape < Offset) { + StringRef::size_type SequenceLength = + getEscapeSequenceLength(Text.substr(NextEscape)); + if (Offset < NextEscape + SequenceLength) + return NextEscape; + NextEscape = Text.find('\\', NextEscape + SequenceLength); + } + return Offset; + } + + static unsigned getEscapeSequenceLength(StringRef Text) { + assert(Text[0] == '\\'); + if (Text.size() < 2) + return 1; + + switch (Text[1]) { + case 'u': + return 6; + case 'U': + return 10; + case 'x': + return getHexLength(Text); + default: + if (Text[1] >= '0' && Text[1] <= '7') + return getOctalLength(Text); + return 2; + } + } + + static unsigned getHexLength(StringRef Text) { + unsigned I = 2; // Point after '\x'. + while (I < Text.size() && ((Text[I] >= '0' && Text[I] <= '9') || + (Text[I] >= 'a' && Text[I] <= 'f') || + (Text[I] >= 'A' && Text[I] <= 'F'))) { + ++I; + } + return I; + } + + static unsigned getOctalLength(StringRef Text) { + unsigned I = 1; + while (I < Text.size() && I < 4 && (Text[I] >= '0' && Text[I] <= '7')) { + ++I; + } + return I; + } + +}; + +class BreakableComment : public BreakableToken { +public: + virtual unsigned getLineSize(unsigned Index) const { + return getLine(Index).size(); + } + + virtual unsigned getLineCount() const { return Lines.size(); } + + virtual unsigned getLineLengthAfterSplit(unsigned LineIndex, + unsigned TailOffset) const { + return getContentStartColumn(LineIndex, TailOffset) + + getLine(LineIndex).size() - TailOffset; + } + + virtual Split getSplit(unsigned LineIndex, unsigned TailOffset, + unsigned ColumnLimit) const; + virtual void insertBreak(unsigned LineIndex, unsigned TailOffset, Split Split, + bool InPPDirective, WhitespaceManager &Whitespaces); + +protected: + BreakableComment(const SourceManager &SourceMgr, const FormatToken &Tok, + unsigned StartColumn) + : BreakableToken(SourceMgr, Tok, StartColumn) {} + + // Get comment lines without /* */, common prefix and trailing whitespace. + // Last line is not trimmed, as it is terminated by */, so its trailing + // whitespace is not really trailing. + StringRef getLine(unsigned Index) const { + return Index < Lines.size() - 1 ? Lines[Index].rtrim() : Lines[Index]; + } + + unsigned getContentStartColumn(unsigned LineIndex, + unsigned TailOffset) const { + return (TailOffset == 0 && LineIndex == 0) + ? StartColumn + : IndentAtLineBreak + Decoration.size(); + } + + unsigned IndentAtLineBreak; + StringRef Decoration; + SmallVector Lines; +}; + +class BreakableBlockComment : public BreakableComment { +public: + BreakableBlockComment(const SourceManager &SourceMgr, + const AnnotatedToken &Token, unsigned StartColumn); + + void alignLines(WhitespaceManager &Whitespaces); + + virtual unsigned getLineLengthAfterSplit(unsigned LineIndex, + unsigned TailOffset) const { + return BreakableComment::getLineLengthAfterSplit(LineIndex, TailOffset) + + (LineIndex + 1 < Lines.size() ? 0 : 2); + } + + virtual void trimLine(unsigned LineIndex, unsigned TailOffset, + unsigned InPPDirective, WhitespaceManager &Whitespaces); + +private: + unsigned OriginalStartColumn; + unsigned CommonPrefixLength; +}; + +class BreakableLineComment : public BreakableComment { +public: + BreakableLineComment(const SourceManager &SourceMgr, + const AnnotatedToken &Token, unsigned StartColumn); + +private: + static StringRef getLineCommentPrefix(StringRef Comment); +}; + +} // namespace format +} // namespace clang + +#endif // LLVM_CLANG_FORMAT_BREAKABLETOKEN_H diff --git a/lib/Format/CMakeLists.txt b/lib/Format/CMakeLists.txt index d8630eeeead..560e38b4bfa 100644 --- a/lib/Format/CMakeLists.txt +++ b/lib/Format/CMakeLists.txt @@ -1,9 +1,11 @@ set(LLVM_LINK_COMPONENTS support) add_clang_library(clangFormat + BreakableToken.cpp + Format.cpp TokenAnnotator.cpp UnwrappedLineParser.cpp - Format.cpp + WhitespaceManager.cpp ) add_dependencies(clangFormat diff --git a/lib/Format/Format.cpp b/lib/Format/Format.cpp index 101b16f1a18..a0557f78182 100644 --- a/lib/Format/Format.cpp +++ b/lib/Format/Format.cpp @@ -15,8 +15,10 @@ #define DEBUG_TYPE "format-formatter" +#include "BreakableToken.h" #include "TokenAnnotator.h" #include "UnwrappedLineParser.h" +#include "WhitespaceManager.h" #include "clang/Basic/Diagnostic.h" #include "clang/Basic/OperatorPrecedence.h" #include "clang/Basic/SourceManager.h" @@ -34,62 +36,66 @@ namespace format { FormatStyle getLLVMStyle() { FormatStyle LLVMStyle; - LLVMStyle.ColumnLimit = 80; - LLVMStyle.MaxEmptyLinesToKeep = 1; - LLVMStyle.PointerBindsToType = false; - LLVMStyle.DerivePointerBinding = false; LLVMStyle.AccessModifierOffset = -2; - LLVMStyle.Standard = FormatStyle::LS_Cpp03; - LLVMStyle.IndentCaseLabels = false; - LLVMStyle.SpacesBeforeTrailingComments = 1; - LLVMStyle.BinPackParameters = true; + LLVMStyle.AlignEscapedNewlinesLeft = false; LLVMStyle.AllowAllParametersOfDeclarationOnNextLine = true; - LLVMStyle.ConstructorInitializerAllOnOneLineOrOnePerLine = false; LLVMStyle.AllowShortIfStatementsOnASingleLine = false; + LLVMStyle.BinPackParameters = true; + LLVMStyle.ColumnLimit = 80; + LLVMStyle.ConstructorInitializerAllOnOneLineOrOnePerLine = false; + LLVMStyle.DerivePointerBinding = false; + LLVMStyle.IndentCaseLabels = false; + LLVMStyle.MaxEmptyLinesToKeep = 1; LLVMStyle.ObjCSpaceBeforeProtocolList = true; LLVMStyle.PenaltyExcessCharacter = 1000000; - LLVMStyle.PenaltyReturnTypeOnItsOwnLine = 5; + LLVMStyle.PenaltyReturnTypeOnItsOwnLine = 75; + LLVMStyle.PointerBindsToType = false; + LLVMStyle.SpacesBeforeTrailingComments = 1; + LLVMStyle.Standard = FormatStyle::LS_Cpp03; return LLVMStyle; } FormatStyle getGoogleStyle() { FormatStyle GoogleStyle; - GoogleStyle.ColumnLimit = 80; - GoogleStyle.MaxEmptyLinesToKeep = 1; - GoogleStyle.PointerBindsToType = true; - GoogleStyle.DerivePointerBinding = true; GoogleStyle.AccessModifierOffset = -1; - GoogleStyle.Standard = FormatStyle::LS_Auto; - GoogleStyle.IndentCaseLabels = true; - GoogleStyle.SpacesBeforeTrailingComments = 2; - GoogleStyle.BinPackParameters = true; + GoogleStyle.AlignEscapedNewlinesLeft = true; GoogleStyle.AllowAllParametersOfDeclarationOnNextLine = true; + GoogleStyle.AllowShortIfStatementsOnASingleLine = true; + GoogleStyle.BinPackParameters = true; + GoogleStyle.ColumnLimit = 80; GoogleStyle.ConstructorInitializerAllOnOneLineOrOnePerLine = true; - GoogleStyle.AllowShortIfStatementsOnASingleLine = false; + GoogleStyle.DerivePointerBinding = true; + GoogleStyle.IndentCaseLabels = true; + GoogleStyle.MaxEmptyLinesToKeep = 1; GoogleStyle.ObjCSpaceBeforeProtocolList = false; GoogleStyle.PenaltyExcessCharacter = 1000000; - GoogleStyle.PenaltyReturnTypeOnItsOwnLine = 100; + GoogleStyle.PenaltyReturnTypeOnItsOwnLine = 200; + GoogleStyle.PointerBindsToType = true; + GoogleStyle.SpacesBeforeTrailingComments = 2; + GoogleStyle.Standard = FormatStyle::LS_Auto; return GoogleStyle; } FormatStyle getChromiumStyle() { FormatStyle ChromiumStyle = getGoogleStyle(); ChromiumStyle.AllowAllParametersOfDeclarationOnNextLine = false; + ChromiumStyle.AllowShortIfStatementsOnASingleLine = false; ChromiumStyle.BinPackParameters = false; ChromiumStyle.Standard = FormatStyle::LS_Cpp03; ChromiumStyle.DerivePointerBinding = false; return ChromiumStyle; } -static bool isTrailingComment(const AnnotatedToken &Tok) { - return Tok.is(tok::comment) && - (Tok.Children.empty() || Tok.Children[0].MustBreakBefore); -} - -static bool isComparison(const AnnotatedToken &Tok) { - prec::Level Precedence = getPrecedence(Tok); - return Tok.Type == TT_BinaryOperator && - (Precedence == prec::Equality || Precedence == prec::Relational); +FormatStyle getMozillaStyle() { + FormatStyle MozillaStyle = getLLVMStyle(); + MozillaStyle.AllowAllParametersOfDeclarationOnNextLine = false; + MozillaStyle.ConstructorInitializerAllOnOneLineOrOnePerLine = true; + MozillaStyle.DerivePointerBinding = true; + MozillaStyle.IndentCaseLabels = true; + MozillaStyle.ObjCSpaceBeforeProtocolList = false; + MozillaStyle.PenaltyReturnTypeOnItsOwnLine = 200; + MozillaStyle.PointerBindsToType = true; + return MozillaStyle; } // Returns the length of everything up to the first possible line break after @@ -104,373 +110,12 @@ static unsigned getLengthToMatchingParen(const AnnotatedToken &Tok) { return End->TotalLength - Tok.TotalLength + 1; } -static size_t -calculateColumnLimit(const FormatStyle &Style, bool InPPDirective) { - // In preprocessor directives reserve two chars for trailing " \" - return Style.ColumnLimit - (InPPDirective ? 2 : 0); -} - -/// \brief Manages the whitespaces around tokens and their replacements. -/// -/// This includes special handling for certain constructs, e.g. the alignment of -/// trailing line comments. -class WhitespaceManager { -public: - WhitespaceManager(SourceManager &SourceMgr, const FormatStyle &Style) - : SourceMgr(SourceMgr), Style(Style) {} - - /// \brief Replaces the whitespace in front of \p Tok. Only call once for - /// each \c AnnotatedToken. - void replaceWhitespace(const AnnotatedToken &Tok, unsigned NewLines, - unsigned Spaces, unsigned WhitespaceStartColumn) { - // 2+ newlines mean an empty line separating logic scopes. - if (NewLines >= 2) - alignComments(); - - SourceLocation TokenLoc = Tok.FormatTok.Tok.getLocation(); - bool LineExceedsColumnLimit = Spaces + WhitespaceStartColumn + - Tok.FormatTok.TokenLength > Style.ColumnLimit; - - // Align line comments if they are trailing or if they continue other - // trailing comments. - if (isTrailingComment(Tok)) { - // Remove the comment's trailing whitespace. - if (Tok.FormatTok.Tok.getLength() != Tok.FormatTok.TokenLength) - Replaces.insert(tooling::Replacement( - SourceMgr, TokenLoc.getLocWithOffset(Tok.FormatTok.TokenLength), - Tok.FormatTok.Tok.getLength() - Tok.FormatTok.TokenLength, "")); - - // Align comment with other comments. - if ((Tok.Parent != NULL || !Comments.empty()) && - !LineExceedsColumnLimit) { - StoredComment Comment; - Comment.Tok = Tok.FormatTok; - Comment.Spaces = Spaces; - Comment.NewLines = NewLines; - Comment.MinColumn = - NewLines > 0 ? Spaces : WhitespaceStartColumn + Spaces; - Comment.MaxColumn = Style.ColumnLimit - Tok.FormatTok.TokenLength; - Comment.Untouchable = false; - Comments.push_back(Comment); - return; - } - } - - // If this line does not have a trailing comment, align the stored comments. - if (Tok.Children.empty() && !isTrailingComment(Tok)) - alignComments(); - - if (Tok.Type == TT_BlockComment) { - indentBlockComment(Tok, Spaces, WhitespaceStartColumn, NewLines, false); - } else if (Tok.Type == TT_LineComment && LineExceedsColumnLimit) { - StringRef Line(SourceMgr.getCharacterData(TokenLoc), - Tok.FormatTok.TokenLength); - int StartColumn = Spaces + (NewLines == 0 ? WhitespaceStartColumn : 0); - StringRef Prefix = getLineCommentPrefix(Line); - std::string NewPrefix = std::string(StartColumn, ' ') + Prefix.str(); - splitLineInComment(Tok.FormatTok, Line.substr(Prefix.size()), - StartColumn + Prefix.size(), NewPrefix, - /*InPPDirective=*/ false, - /*CommentHasMoreLines=*/ false); - } - - storeReplacement(Tok.FormatTok, getNewLineText(NewLines, Spaces)); - } - - /// \brief Like \c replaceWhitespace, but additionally adds right-aligned - /// backslashes to escape newlines inside a preprocessor directive. - /// - /// This function and \c replaceWhitespace have the same behavior if - /// \c Newlines == 0. - void replacePPWhitespace(const AnnotatedToken &Tok, unsigned NewLines, - unsigned Spaces, unsigned WhitespaceStartColumn) { - if (Tok.Type == TT_BlockComment) - indentBlockComment(Tok, Spaces, WhitespaceStartColumn, NewLines, true); - - storeReplacement(Tok.FormatTok, - getNewLineText(NewLines, Spaces, WhitespaceStartColumn)); - } - - /// \brief Inserts a line break into the middle of a token. - /// - /// Will break at \p Offset inside \p Tok, putting \p Prefix before the line - /// break and \p Postfix before the rest of the token starts in the next line. - /// - /// \p InPPDirective, \p Spaces, \p WhitespaceStartColumn and \p Style are - /// used to generate the correct line break. - void breakToken(const FormatToken &Tok, unsigned Offset, - unsigned ReplaceChars, StringRef Prefix, StringRef Postfix, - bool InPPDirective, unsigned Spaces, - unsigned WhitespaceStartColumn) { - std::string NewLineText; - if (!InPPDirective) - NewLineText = getNewLineText(1, Spaces); - else - NewLineText = getNewLineText(1, Spaces, WhitespaceStartColumn); - std::string ReplacementText = (Prefix + NewLineText + Postfix).str(); - SourceLocation Location = Tok.Tok.getLocation().getLocWithOffset(Offset); - Replaces.insert(tooling::Replacement(SourceMgr, Location, ReplaceChars, - ReplacementText)); - } - - /// \brief Returns all the \c Replacements created during formatting. - const tooling::Replacements &generateReplacements() { - alignComments(); - return Replaces; - } - - void addUntouchableComment(unsigned Column) { - StoredComment Comment; - Comment.MinColumn = Column; - Comment.MaxColumn = Column; - Comment.Untouchable = true; - Comments.push_back(Comment); - } - -private: - static StringRef getLineCommentPrefix(StringRef Comment) { - const char *KnownPrefixes[] = { "/// ", "///", "// ", "//" }; - for (size_t i = 0; i < llvm::array_lengthof(KnownPrefixes); ++i) - if (Comment.startswith(KnownPrefixes[i])) - return KnownPrefixes[i]; - return ""; - } - - /// \brief Finds a common prefix of lines of a block comment to properly - /// indent (and possibly decorate with '*'s) added lines. - /// - /// The first line is ignored (it's special and starts with /*). The number of - /// lines should be more than one. - static StringRef findCommentLinesPrefix(ArrayRef Lines, - const char *PrefixChars = " *") { - assert(Lines.size() > 1); - StringRef Prefix(Lines[1].data(), Lines[1].find_first_not_of(PrefixChars)); - for (size_t i = 2; i < Lines.size(); ++i) { - for (size_t j = 0; j < Prefix.size() && j < Lines[i].size(); ++j) { - if (Prefix[j] != Lines[i][j]) { - Prefix = Prefix.substr(0, j); - break; - } - } - } - return Prefix; - } - - /// \brief Splits one line in a line or block comment, if it doesn't fit to - /// provided column limit. Removes trailing whitespace in each line. - /// - /// \param Line points to the line contents without leading // or /*. - /// - /// \param StartColumn is the column where the first character of Line will be - /// located after formatting. - /// - /// \param LinePrefix is inserted after each line break. - /// - /// When \param InPPDirective is true, each line break will be preceded by a - /// backslash in the last column to make line breaks inside the comment - /// visually consistent with line breaks outside the comment. This only makes - /// sense for block comments. - /// - /// When \param CommentHasMoreLines is false, no line breaks/trailing - /// backslashes will be inserted after it. - void splitLineInComment(const FormatToken &Tok, StringRef Line, - size_t StartColumn, StringRef LinePrefix, - bool InPPDirective, bool CommentHasMoreLines, - const char *WhiteSpaceChars = " ") { - size_t ColumnLimit = calculateColumnLimit(Style, InPPDirective); - const char *TokenStart = SourceMgr.getCharacterData(Tok.Tok.getLocation()); - - StringRef TrimmedLine = Line.rtrim(); - int TrailingSpaceLength = Line.size() - TrimmedLine.size(); - - // Don't touch leading whitespace. - Line = TrimmedLine.ltrim(); - StartColumn += TrimmedLine.size() - Line.size(); - - while (Line.size() + StartColumn > ColumnLimit) { - // Try to break at the last whitespace before the column limit. - size_t SpacePos = - Line.find_last_of(WhiteSpaceChars, ColumnLimit - StartColumn + 1); - if (SpacePos == StringRef::npos) { - // Try to find any whitespace in the line. - SpacePos = Line.find_first_of(WhiteSpaceChars); - if (SpacePos == StringRef::npos) // No whitespace found, give up. - break; - } - - StringRef NextCut = Line.substr(0, SpacePos).rtrim(); - StringRef RemainingLine = Line.substr(SpacePos).ltrim(); - if (RemainingLine.empty()) - break; - - if (RemainingLine == "*/" && LinePrefix.endswith("* ")) - LinePrefix = LinePrefix.substr(0, LinePrefix.size() - 2); - - Line = RemainingLine; - - size_t ReplaceChars = Line.begin() - NextCut.end(); - breakToken(Tok, NextCut.end() - TokenStart, ReplaceChars, "", LinePrefix, - InPPDirective, 0, NextCut.size() + StartColumn); - StartColumn = LinePrefix.size(); - } - - if (TrailingSpaceLength > 0 || (InPPDirective && CommentHasMoreLines)) { - // Remove trailing whitespace/insert backslash. + 1 is for \n - breakToken(Tok, Line.end() - TokenStart, TrailingSpaceLength + 1, "", "", - InPPDirective, 0, Line.size() + StartColumn); - } - } - - /// \brief Changes indentation of all lines in a block comment by Indent, - /// removes trailing whitespace from each line, splits lines that end up - /// exceeding the column limit. - void indentBlockComment(const AnnotatedToken &Tok, int Indent, - int WhitespaceStartColumn, int NewLines, - bool InPPDirective) { - assert(Tok.Type == TT_BlockComment); - int StartColumn = Indent + (NewLines == 0 ? WhitespaceStartColumn : 0); - const SourceLocation TokenLoc = Tok.FormatTok.Tok.getLocation(); - const int CurrentIndent = SourceMgr.getSpellingColumnNumber(TokenLoc) - 1; - const int IndentDelta = Indent - CurrentIndent; - const StringRef Text(SourceMgr.getCharacterData(TokenLoc), - Tok.FormatTok.TokenLength); - assert(Text.startswith("/*") && Text.endswith("*/")); - - SmallVector Lines; - Text.split(Lines, "\n"); - - if (IndentDelta > 0) { - std::string WhiteSpace(IndentDelta, ' '); - for (size_t i = 1; i < Lines.size(); ++i) { - Replaces.insert(tooling::Replacement( - SourceMgr, TokenLoc.getLocWithOffset(Lines[i].data() - Text.data()), - 0, WhiteSpace)); - } - } else if (IndentDelta < 0) { - std::string WhiteSpace(-IndentDelta, ' '); - // Check that the line is indented enough. - for (size_t i = 1; i < Lines.size(); ++i) { - if (!Lines[i].startswith(WhiteSpace)) - return; - } - for (size_t i = 1; i < Lines.size(); ++i) { - Replaces.insert(tooling::Replacement( - SourceMgr, TokenLoc.getLocWithOffset(Lines[i].data() - Text.data()), - -IndentDelta, "")); - } - } - - // Split long lines in comments. - size_t OldPrefixSize = 0; - std::string NewPrefix; - if (Lines.size() > 1) { - StringRef CurrentPrefix = findCommentLinesPrefix(Lines); - OldPrefixSize = CurrentPrefix.size(); - NewPrefix = (IndentDelta < 0) - ? CurrentPrefix.substr(-IndentDelta).str() - : std::string(IndentDelta, ' ') + CurrentPrefix.str(); - if (CurrentPrefix.endswith("*")) { - NewPrefix += " "; - ++OldPrefixSize; - } - } else if (Tok.Parent == 0) { - NewPrefix = std::string(StartColumn, ' ') + " * "; - } - - StartColumn += 2; - for (size_t i = 0; i < Lines.size(); ++i) { - StringRef Line = Lines[i].substr(i == 0 ? 2 : OldPrefixSize); - splitLineInComment(Tok.FormatTok, Line, StartColumn, NewPrefix, - InPPDirective, i != Lines.size() - 1); - StartColumn = NewPrefix.size(); - } - } - - std::string getNewLineText(unsigned NewLines, unsigned Spaces) { - return std::string(NewLines, '\n') + std::string(Spaces, ' '); - } - - std::string getNewLineText(unsigned NewLines, unsigned Spaces, - unsigned WhitespaceStartColumn) { - std::string NewLineText; - if (NewLines > 0) { - unsigned Offset = - std::min(Style.ColumnLimit - 1, WhitespaceStartColumn); - for (unsigned i = 0; i < NewLines; ++i) { - NewLineText += std::string(Style.ColumnLimit - Offset - 1, ' '); - NewLineText += "\\\n"; - Offset = 0; - } - } - return NewLineText + std::string(Spaces, ' '); - } - - /// \brief Structure to store a comment for later layout and alignment. - struct StoredComment { - FormatToken Tok; - unsigned MinColumn; - unsigned MaxColumn; - unsigned NewLines; - unsigned Spaces; - bool Untouchable; - }; - SmallVector Comments; - typedef SmallVector::iterator comment_iterator; - - /// \brief Try to align all stashed comments. - void alignComments() { - unsigned MinColumn = 0; - unsigned MaxColumn = UINT_MAX; - comment_iterator Start = Comments.begin(); - for (comment_iterator I = Start, E = Comments.end(); I != E; ++I) { - if (I->MinColumn > MaxColumn || I->MaxColumn < MinColumn) { - alignComments(Start, I, MinColumn); - MinColumn = I->MinColumn; - MaxColumn = I->MaxColumn; - Start = I; - } else { - MinColumn = std::max(MinColumn, I->MinColumn); - MaxColumn = std::min(MaxColumn, I->MaxColumn); - } - } - alignComments(Start, Comments.end(), MinColumn); - Comments.clear(); - } - - /// \brief Put all the comments between \p I and \p E into \p Column. - void alignComments(comment_iterator I, comment_iterator E, unsigned Column) { - while (I != E) { - if (!I->Untouchable) { - unsigned Spaces = I->Spaces + Column - I->MinColumn; - storeReplacement(I->Tok, getNewLineText(I->NewLines, Spaces)); - } - ++I; - } - } - - /// \brief Stores \p Text as the replacement for the whitespace in front of - /// \p Tok. - void storeReplacement(const FormatToken &Tok, const std::string Text) { - // Don't create a replacement, if it does not change anything. - if (StringRef(SourceMgr.getCharacterData(Tok.WhiteSpaceStart), - Tok.WhiteSpaceLength) == Text) - return; - - Replaces.insert(tooling::Replacement(SourceMgr, Tok.WhiteSpaceStart, - Tok.WhiteSpaceLength, Text)); - } - - SourceManager &SourceMgr; - tooling::Replacements Replaces; - const FormatStyle &Style; -}; - class UnwrappedLineFormatter { public: UnwrappedLineFormatter(const FormatStyle &Style, SourceManager &SourceMgr, const AnnotatedLine &Line, unsigned FirstIndent, const AnnotatedToken &RootToken, - WhitespaceManager &Whitespaces, bool StructuralError) + WhitespaceManager &Whitespaces) : Style(Style), SourceMgr(SourceMgr), Line(Line), FirstIndent(FirstIndent), RootToken(RootToken), Whitespaces(Whitespaces), Count(0) {} @@ -486,16 +131,12 @@ class UnwrappedLineFormatter { State.NextToken = &RootToken; State.Stack.push_back( ParenState(FirstIndent, FirstIndent, !Style.BinPackParameters, - /*HasMultiParameterLine=*/ false)); + /*NoLineBreak=*/ false)); State.LineContainsContinuedForLoopSection = false; State.ParenLevel = 0; State.StartOfStringLiteral = 0; State.StartOfLineLevel = State.ParenLevel; - DEBUG({ - DebugTokenState(*State.NextToken); - }); - // The first token has already been indented and thus consumed. moveStateToNextToken(State, /*DryRun=*/ false); @@ -530,13 +171,13 @@ class UnwrappedLineFormatter { struct ParenState { ParenState(unsigned Indent, unsigned LastSpace, bool AvoidBinPacking, - bool HasMultiParameterLine) + bool NoLineBreak) : Indent(Indent), LastSpace(LastSpace), FirstLessLess(0), BreakBeforeClosingBrace(false), QuestionColumn(0), AvoidBinPacking(AvoidBinPacking), BreakBeforeParameter(false), - HasMultiParameterLine(HasMultiParameterLine), ColonPos(0), - StartOfFunctionCall(0), NestedNameSpecifierContinuation(0), - CallContinuation(0), VariablePos(0) {} + NoLineBreak(NoLineBreak), ColonPos(0), StartOfFunctionCall(0), + NestedNameSpecifierContinuation(0), CallContinuation(0), + VariablePos(0) {} /// \brief The position to which a specific parenthesis level needs to be /// indented. @@ -573,8 +214,8 @@ class UnwrappedLineFormatter { /// \c AvoidBinPacking is \c true). bool BreakBeforeParameter; - /// \brief This context already has a line with more than one parameter. - bool HasMultiParameterLine; + /// \brief Line breaking in this context would break a formatting rule. + bool NoLineBreak; /// \brief The position of the colon in an ObjC method declaration/call. unsigned ColonPos; @@ -610,14 +251,14 @@ class UnwrappedLineFormatter { return AvoidBinPacking; if (BreakBeforeParameter != Other.BreakBeforeParameter) return BreakBeforeParameter; - if (HasMultiParameterLine != Other.HasMultiParameterLine) - return HasMultiParameterLine; + if (NoLineBreak != Other.NoLineBreak) + return NoLineBreak; if (ColonPos != Other.ColonPos) return ColonPos < Other.ColonPos; if (StartOfFunctionCall != Other.StartOfFunctionCall) return StartOfFunctionCall < Other.StartOfFunctionCall; if (NestedNameSpecifierContinuation != - Other.NestedNameSpecifierContinuation) + Other.NestedNameSpecifierContinuation) return NestedNameSpecifierContinuation < Other.NestedNameSpecifierContinuation; if (CallContinuation != Other.CallContinuation) @@ -662,7 +303,7 @@ class UnwrappedLineFormatter { if (Column != Other.Column) return Column < Other.Column; if (LineContainsContinuedForLoopSection != - Other.LineContainsContinuedForLoopSection) + Other.LineContainsContinuedForLoopSection) return LineContainsContinuedForLoopSection; if (ParenLevel != Other.ParenLevel) return ParenLevel < Other.ParenLevel; @@ -730,7 +371,8 @@ class UnwrappedLineFormatter { State.Stack.back().VariablePos != 0) { State.Column = State.Stack.back().VariablePos; } else if (Previous.ClosesTemplateDeclaration || - (Current.Type == TT_StartOfName && State.ParenLevel == 0)) { + (Current.Type == TT_StartOfName && State.ParenLevel == 0 && + Line.StartsDefinition)) { State.Column = State.Stack.back().Indent; } else if (Current.Type == TT_ObjCSelectorName) { if (State.Stack.back().ColonPos > Current.FormatTok.TokenLength) { @@ -741,8 +383,7 @@ class UnwrappedLineFormatter { State.Stack.back().ColonPos = State.Column + Current.FormatTok.TokenLength; } - } else if (Current.Type == TT_StartOfName || Current.is(tok::question) || - Previous.is(tok::equal) || isComparison(Previous) || + } else if (Current.Type == TT_StartOfName || Previous.is(tok::equal) || Previous.Type == TT_ObjCMethodExpr) { State.Column = ContinuationIndent; } else { @@ -781,7 +422,9 @@ class UnwrappedLineFormatter { for (unsigned i = 0, e = State.Stack.size() - 1; i != e; ++i) { State.Stack[i].BreakBeforeParameter = true; } - if (Current.isOneOf(tok::period, tok::arrow)) + const AnnotatedToken *TokenBefore = Current.getPreviousNoneComment(); + if (TokenBefore && !TokenBefore->isOneOf(tok::comma, tok::semi) && + !TokenBefore->opensScope()) State.Stack.back().BreakBeforeParameter = true; // If we break after {, we should also break before the corresponding }. @@ -822,7 +465,7 @@ class UnwrappedLineFormatter { if (Current.Type == TT_ObjCSelectorName && State.Stack.back().ColonPos == 0) { if (State.Stack.back().Indent + Current.LongestObjCSelectorName > - State.Column + Spaces + Current.FormatTok.TokenLength) + State.Column + Spaces + Current.FormatTok.TokenLength) State.Stack.back().ColonPos = State.Stack.back().Indent + Current.LongestObjCSelectorName; else @@ -830,12 +473,12 @@ class UnwrappedLineFormatter { State.Column + Spaces + Current.FormatTok.TokenLength; } - if (Current.Type != TT_LineComment && - (Previous.isOneOf(tok::l_paren, tok::l_brace) || - State.NextToken->Parent->Type == TT_TemplateOpener)) + if (Previous.opensScope() && Previous.Type != TT_ObjCMethodExpr && + Current.Type != TT_LineComment) State.Stack.back().Indent = State.Column + Spaces; - if (Previous.is(tok::comma) && !isTrailingComment(Current)) - State.Stack.back().HasMultiParameterLine = true; + if (Previous.is(tok::comma) && !Current.isTrailingComment() && + State.Stack.back().AvoidBinPacking) + State.Stack.back().NoLineBreak = true; State.Column += Spaces; if (Current.is(tok::l_paren) && Previous.isOneOf(tok::kw_if, tok::kw_for)) @@ -851,9 +494,7 @@ class UnwrappedLineFormatter { State.Stack.back().LastSpace = State.Column; else if (Previous.Type == TT_InheritanceColon) State.Stack.back().Indent = State.Column; - else if (Previous.ParameterCount > 1 && - (Previous.isOneOf(tok::l_paren, tok::l_square, tok::l_brace) || - Previous.Type == TT_TemplateOpener)) + else if (Previous.opensScope() && Previous.ParameterCount > 1) // If this function has multiple parameters, indent nested calls from // the start of the first parameter. State.Stack.back().LastSpace = State.Column; @@ -879,28 +520,55 @@ class UnwrappedLineFormatter { State.Stack.back().StartOfFunctionCall = Current.LastInChainOfCalls ? 0 : State.Column; if (Current.Type == TT_CtorInitializerColon) { + State.Stack.back().Indent = State.Column + 2; if (Style.ConstructorInitializerAllOnOneLineOrOnePerLine) State.Stack.back().AvoidBinPacking = true; State.Stack.back().BreakBeforeParameter = false; } + // If return returns a binary expression, align after it. + if (Current.is(tok::kw_return) && !Current.FakeLParens.empty()) + State.Stack.back().LastSpace = State.Column + 7; + // In ObjC method declaration we align on the ":" of parameters, but we need // to ensure that we indent parameters on subsequent lines by at least 4. if (Current.Type == TT_ObjCMethodSpecifier) State.Stack.back().Indent += 4; // Insert scopes created by fake parenthesis. - for (unsigned i = 0, e = Current.FakeLParens; i != e; ++i) { + const AnnotatedToken *Previous = Current.getPreviousNoneComment(); + // Don't add extra indentation for the first fake parenthesis after + // 'return', assignements or opening <({[. The indentation for these cases + // is special cased. + bool SkipFirstExtraIndent = + Current.is(tok::kw_return) || + (Previous && (Previous->opensScope() || + getPrecedence(*Previous) == prec::Assignment)); + for (SmallVector::const_reverse_iterator + I = Current.FakeLParens.rbegin(), + E = Current.FakeLParens.rend(); + I != E; ++I) { ParenState NewParenState = State.Stack.back(); - NewParenState.Indent = std::max(State.Column, State.Stack.back().Indent); - NewParenState.BreakBeforeParameter = false; + NewParenState.Indent = + std::max(std::max(State.Column, NewParenState.Indent), + State.Stack.back().LastSpace); + + // Always indent conditional expressions. Never indent expression where + // the 'operator' is ',', ';' or an assignment (i.e. *I <= + // prec::Assignment) as those have different indentation rules. Indent + // other expression, unless the indentation needs to be skipped. + if (*I == prec::Conditional || + (!SkipFirstExtraIndent && *I > prec::Assignment)) + NewParenState.Indent += 4; + if (Previous && !Previous->opensScope()) + NewParenState.BreakBeforeParameter = false; State.Stack.push_back(NewParenState); + SkipFirstExtraIndent = false; } // If we encounter an opening (, [, { or <, we add a level to our stacks to // prepare for the following tokens. - if (Current.isOneOf(tok::l_paren, tok::l_square, tok::l_brace) || - State.NextToken->Type == TT_TemplateOpener) { + if (Current.opensScope()) { unsigned NewIndent; bool AvoidBinPacking; if (Current.is(tok::l_brace)) { @@ -909,12 +577,20 @@ class UnwrappedLineFormatter { } else { NewIndent = 4 + std::max(State.Stack.back().LastSpace, State.Stack.back().StartOfFunctionCall); - AvoidBinPacking = - !Style.BinPackParameters || State.Stack.back().AvoidBinPacking; + AvoidBinPacking = !Style.BinPackParameters; } State.Stack.push_back( ParenState(NewIndent, State.Stack.back().LastSpace, AvoidBinPacking, - State.Stack.back().HasMultiParameterLine)); + State.Stack.back().NoLineBreak)); + + if (Current.NoMoreTokensOnLevel && Current.FakeLParens.empty()) { + // This parenthesis was the last token possibly making use of Indent and + // LastSpace of the next higher ParenLevel. Thus, erase them to acieve + // better memoization results. + State.Stack[State.Stack.size() - 2].Indent = 0; + State.Stack[State.Stack.size() - 2].LastSpace = 0; + } + ++State.ParenLevel; } @@ -962,115 +638,74 @@ class UnwrappedLineFormatter { /// it if possible. unsigned breakProtrudingToken(const AnnotatedToken &Current, LineState &State, bool DryRun) { - if (Current.isNot(tok::string_literal)) - return 0; - // Only break up default narrow strings. - const char *LiteralData = Current.FormatTok.Tok.getLiteralData(); - if (!LiteralData || *LiteralData != '"') + llvm::OwningPtr Token; + unsigned StartColumn = State.Column - Current.FormatTok.TokenLength; + if (Current.is(tok::string_literal)) { + // Only break up default narrow strings. + const char *LiteralData = SourceMgr.getCharacterData( + Current.FormatTok.getStartOfNonWhitespace()); + if (!LiteralData || *LiteralData != '"') + return 0; + + Token.reset(new BreakableStringLiteral(SourceMgr, Current.FormatTok, + StartColumn)); + } else if (Current.Type == TT_BlockComment) { + BreakableBlockComment *BBC = + new BreakableBlockComment(SourceMgr, Current, StartColumn); + if (!DryRun) + BBC->alignLines(Whitespaces); + Token.reset(BBC); + } else if (Current.Type == TT_LineComment && + (Current.Parent == NULL || + Current.Parent->Type != TT_ImplicitStringLiteral)) { + Token.reset(new BreakableLineComment(SourceMgr, Current, StartColumn)); + } else { return 0; + } + bool BreakInserted = false; unsigned Penalty = 0; - unsigned TailOffset = 0; - unsigned TailLength = Current.FormatTok.TokenLength; - unsigned StartColumn = State.Column - Current.FormatTok.TokenLength; - unsigned OffsetFromStart = 0; - while (StartColumn + TailLength > getColumnLimit()) { - StringRef Text = StringRef(LiteralData + TailOffset, TailLength); - if (StartColumn + OffsetFromStart + 1 > getColumnLimit()) - break; - StringRef::size_type SplitPoint = getSplitPoint( - Text, getColumnLimit() - StartColumn - OffsetFromStart - 1); - if (SplitPoint == StringRef::npos) - break; - assert(SplitPoint != 0); - // +2, because 'Text' starts after the opening quotes, and does not - // include the closing quote we need to insert. - unsigned WhitespaceStartColumn = - StartColumn + OffsetFromStart + SplitPoint + 2; - State.Stack.back().LastSpace = StartColumn; + for (unsigned LineIndex = 0; LineIndex < Token->getLineCount(); + ++LineIndex) { + unsigned TailOffset = 0; + unsigned RemainingLength = + Token->getLineLengthAfterSplit(LineIndex, TailOffset); + while (RemainingLength > getColumnLimit()) { + BreakableToken::Split Split = + Token->getSplit(LineIndex, TailOffset, getColumnLimit()); + if (Split.first == StringRef::npos) + break; + assert(Split.first != 0); + unsigned NewRemainingLength = Token->getLineLengthAfterSplit( + LineIndex, TailOffset + Split.first + Split.second); + if (NewRemainingLength >= RemainingLength) + break; + if (!DryRun) { + Token->insertBreak(LineIndex, TailOffset, Split, Line.InPPDirective, + Whitespaces); + } + TailOffset += Split.first + Split.second; + RemainingLength = NewRemainingLength; + Penalty += Style.PenaltyExcessCharacter; + BreakInserted = true; + } + State.Column = RemainingLength; if (!DryRun) { - Whitespaces.breakToken(Current.FormatTok, TailOffset + SplitPoint + 1, - 0, "\"", "\"", Line.InPPDirective, StartColumn, - WhitespaceStartColumn); + Token->trimLine(LineIndex, TailOffset, Line.InPPDirective, Whitespaces); } - TailOffset += SplitPoint + 1; - TailLength -= SplitPoint + 1; - OffsetFromStart = 1; - Penalty += Style.PenaltyExcessCharacter; + } + + if (BreakInserted) { for (unsigned i = 0, e = State.Stack.size(); i != e; ++i) State.Stack[i].BreakBeforeParameter = true; + State.Stack.back().LastSpace = StartColumn; } - State.Column = StartColumn + TailLength; return Penalty; } - StringRef::size_type - getSplitPoint(StringRef Text, StringRef::size_type Offset) { - StringRef::size_type SpaceOffset = Text.rfind(' ', Offset); - if (SpaceOffset != StringRef::npos && SpaceOffset != 0) - return SpaceOffset; - StringRef::size_type SlashOffset = Text.rfind('/', Offset); - if (SlashOffset != StringRef::npos && SlashOffset != 0) - return SlashOffset; - StringRef::size_type Split = getStartOfCharacter(Text, Offset); - if (Split != StringRef::npos && Split > 1) - // Do not split at 0. - return Split - 1; - return StringRef::npos; - } - - StringRef::size_type - getStartOfCharacter(StringRef Text, StringRef::size_type Offset) { - StringRef::size_type NextEscape = Text.find('\\'); - while (NextEscape != StringRef::npos && NextEscape < Offset) { - StringRef::size_type SequenceLength = - getEscapeSequenceLength(Text.substr(NextEscape)); - if (Offset < NextEscape + SequenceLength) - return NextEscape; - NextEscape = Text.find('\\', NextEscape + SequenceLength); - } - return Offset; - } - - unsigned getEscapeSequenceLength(StringRef Text) { - assert(Text[0] == '\\'); - if (Text.size() < 2) - return 1; - - switch (Text[1]) { - case 'u': - return 6; - case 'U': - return 10; - case 'x': - return getHexLength(Text); - default: - if (Text[1] >= '0' && Text[1] <= '7') - return getOctalLength(Text); - return 2; - } - } - - unsigned getHexLength(StringRef Text) { - unsigned I = 2; // Point after '\x'. - while (I < Text.size() && ((Text[I] >= '0' && Text[I] <= '9') || - (Text[I] >= 'a' && Text[I] <= 'f') || - (Text[I] >= 'A' && Text[I] <= 'F'))) { - ++I; - } - return I; - } - - unsigned getOctalLength(StringRef Text) { - unsigned I = 1; - while (I < Text.size() && I < 4 && (Text[I] >= '0' && Text[I] <= '7')) { - ++I; - } - return I; - } - unsigned getColumnLimit() { - return calculateColumnLimit(Style, Line.InPPDirective); + // In preprocessor directives reserve two chars for trailing " \" + return Style.ColumnLimit - (Line.InPPDirective ? 2 : 0); } /// \brief An edge in the solution space from \c Previous->State to \c State, @@ -1194,12 +829,7 @@ class UnwrappedLineFormatter { !(State.NextToken->is(tok::r_brace) && State.Stack.back().BreakBeforeClosingBrace)) return false; - // Trying to insert a parameter on a new line if there are already more than - // one parameter on the current line is bin packing. - if (State.Stack.back().HasMultiParameterLine && - State.Stack.back().AvoidBinPacking) - return false; - return true; + return !State.Stack.back().NoLineBreak; } /// \brief Returns \c true, if a line break after \p State is mandatory. @@ -1216,7 +846,7 @@ class UnwrappedLineFormatter { State.NextToken->is(tok::question) || State.NextToken->Type == TT_ConditionalExpr) && State.Stack.back().BreakBeforeParameter && - !isTrailingComment(*State.NextToken) && + !State.NextToken->isTrailingComment() && State.NextToken->isNot(tok::r_paren) && State.NextToken->isNot(tok::r_brace)) return true; @@ -1310,6 +940,11 @@ class LexerBasedFormatTokenSource : public FormatTokenSource { // Now FormatTok is the next non-whitespace token. FormatTok.TokenLength = Text.size(); + if (FormatTok.Tok.is(tok::comment)) { + FormatTok.TrailingWhiteSpaceLength = Text.size() - Text.rtrim().size(); + FormatTok.TokenLength -= FormatTok.TrailingWhiteSpaceLength; + } + // In case the token starts with escaped newlines, we want to // take them into account as whitespace - this pattern is quite frequent // in macro definitions. @@ -1336,11 +971,6 @@ class LexerBasedFormatTokenSource : public FormatTokenSource { GreaterStashed = true; } - // If we reformat comments, we remove trailing whitespace. Update the length - // accordingly. - if (FormatTok.Tok.is(tok::comment)) - FormatTok.TokenLength = Text.rtrim().size(); - return FormatTok; } @@ -1373,7 +1003,7 @@ class Formatter : public UnwrappedLineConsumer { tooling::Replacements format() { LexerBasedFormatTokenSource Tokens(Lex, SourceMgr); UnwrappedLineParser Parser(Diag, Style, Tokens, *this); - StructuralError = Parser.parse(); + bool StructuralError = Parser.parse(); unsigned PreviousEndOfLineColumn = 0; TokenAnnotator Annotator(Style, SourceMgr, Lex, Tokens.getIdentTable().get("in")); @@ -1383,14 +1013,21 @@ class Formatter : public UnwrappedLineConsumer { deriveLocalStyle(); for (unsigned i = 0, e = AnnotatedLines.size(); i != e; ++i) { Annotator.calculateFormattingInformation(AnnotatedLines[i]); + } - // Adapt level to the next line if this is a comment. - // FIXME: Can/should this be done in the UnwrappedLineParser? - if (i + 1 != e && AnnotatedLines[i].First.is(tok::comment) && - AnnotatedLines[i].First.Children.empty() && - AnnotatedLines[i + 1].First.isNot(tok::r_brace)) - AnnotatedLines[i].Level = AnnotatedLines[i + 1].Level; + // Adapt level to the next line if this is a comment. + // FIXME: Can/should this be done in the UnwrappedLineParser? + const AnnotatedLine *NextNoneCommentLine = NULL; + for (unsigned i = AnnotatedLines.size() - 1; i > 0; --i) { + if (NextNoneCommentLine && AnnotatedLines[i].First.is(tok::comment) && + AnnotatedLines[i].First.Children.empty()) + AnnotatedLines[i].Level = NextNoneCommentLine->Level; + else + NextNoneCommentLine = + AnnotatedLines[i].First.isNot(tok::r_brace) ? &AnnotatedLines[i] + : NULL; } + std::vector IndentForLevel; bool PreviousLineWasTouched = false; const AnnotatedToken *PreviousLineLastToken = 0; @@ -1416,17 +1053,19 @@ class Formatter : public UnwrappedLineConsumer { unsigned Indent = LevelIndent; if (static_cast(Indent) + Offset >= 0) Indent += Offset; - if (!FirstTok.WhiteSpaceStart.isValid() || StructuralError) { - Indent = LevelIndent = - SourceMgr.getSpellingColumnNumber(FirstTok.Tok.getLocation()) - 1; - } else { + if (FirstTok.WhiteSpaceStart.isValid() && + // Insert a break even if there is a structural error in case where + // we break apart a line consisting of multiple unwrapped lines. + (FirstTok.NewlinesBefore == 0 || !StructuralError)) { formatFirstToken(TheLine.First, PreviousLineLastToken, Indent, TheLine.InPPDirective, PreviousEndOfLineColumn); + } else { + Indent = LevelIndent = + SourceMgr.getSpellingColumnNumber(FirstTok.Tok.getLocation()) - 1; } tryFitMultipleLinesInOne(Indent, I, E); UnwrappedLineFormatter Formatter(Style, SourceMgr, TheLine, Indent, - TheLine.First, Whitespaces, - StructuralError); + TheLine.First, Whitespaces); PreviousEndOfLineColumn = Formatter.format(I + 1 != E ? &*(I + 1) : NULL); IndentForLevel[TheLine.Level] = LevelIndent; @@ -1457,6 +1096,8 @@ class Formatter : public UnwrappedLineConsumer { if (TheLine.Last->is(tok::comment)) Whitespaces.addUntouchableComment(SourceMgr.getSpellingColumnNumber( TheLine.Last->FormatTok.Tok.getLocation()) - 1); + else + Whitespaces.alignComments(); } PreviousLineLastToken = I->Last; } @@ -1727,12 +1368,12 @@ class Formatter : public UnwrappedLineConsumer { WhitespaceManager Whitespaces; std::vector Ranges; std::vector AnnotatedLines; - bool StructuralError; }; -tooling::Replacements -reformat(const FormatStyle &Style, Lexer &Lex, SourceManager &SourceMgr, - std::vector Ranges, DiagnosticConsumer *DiagClient) { +tooling::Replacements reformat(const FormatStyle &Style, Lexer &Lex, + SourceManager &SourceMgr, + std::vector Ranges, + DiagnosticConsumer *DiagClient) { IntrusiveRefCntPtr DiagOpts = new DiagnosticOptions(); OwningPtr DiagPrinter; if (DiagClient == 0) { diff --git a/lib/Format/TokenAnnotator.cpp b/lib/Format/TokenAnnotator.cpp index 427157e3322..17abb01d181 100644 --- a/lib/Format/TokenAnnotator.cpp +++ b/lib/Format/TokenAnnotator.cpp @@ -16,12 +16,13 @@ #include "TokenAnnotator.h" #include "clang/Basic/SourceManager.h" #include "clang/Lex/Lexer.h" +#include "llvm/Support/Debug.h" namespace clang { namespace format { -static bool isUnaryOperator(const AnnotatedToken &Tok) { - switch (Tok.FormatTok.Tok.getKind()) { +bool AnnotatedToken::isUnaryOperator() const { + switch (FormatTok.Tok.getKind()) { case tok::plus: case tok::plusplus: case tok::minus: @@ -36,49 +37,38 @@ static bool isUnaryOperator(const AnnotatedToken &Tok) { } } -static bool isBinaryOperator(const AnnotatedToken &Tok) { +bool AnnotatedToken::isBinaryOperator() const { // Comma is a binary operator, but does not behave as such wrt. formatting. - return getPrecedence(Tok) > prec::Comma; + return getPrecedence(*this) > prec::Comma; } -// Returns the previous token ignoring comments. -static AnnotatedToken *getPreviousToken(AnnotatedToken &Tok) { - AnnotatedToken *PrevToken = Tok.Parent; - while (PrevToken != NULL && PrevToken->is(tok::comment)) - PrevToken = PrevToken->Parent; - return PrevToken; -} -static const AnnotatedToken *getPreviousToken(const AnnotatedToken &Tok) { - return getPreviousToken(const_cast(Tok)); +bool AnnotatedToken::isTrailingComment() const { + return is(tok::comment) && + (Children.empty() || Children[0].FormatTok.NewlinesBefore > 0); } -static bool isTrailingComment(AnnotatedToken *Tok) { - return Tok != NULL && Tok->is(tok::comment) && - (Tok->Children.empty() || - Tok->Children[0].FormatTok.NewlinesBefore > 0); +AnnotatedToken *AnnotatedToken::getPreviousNoneComment() const { + AnnotatedToken *Tok = Parent; + while (Tok != NULL && Tok->is(tok::comment)) + Tok = Tok->Parent; + return Tok; } -// Returns the next token ignoring comments. -static const AnnotatedToken *getNextToken(const AnnotatedToken &Tok) { - if (Tok.Children.empty()) - return NULL; - const AnnotatedToken *NextToken = &Tok.Children[0]; - while (NextToken->is(tok::comment)) { - if (NextToken->Children.empty()) - return NULL; - NextToken = &NextToken->Children[0]; - } - return NextToken; +const AnnotatedToken *AnnotatedToken::getNextNoneComment() const { + const AnnotatedToken *Tok = Children.empty() ? NULL : &Children[0]; + while (Tok != NULL && Tok->is(tok::comment)) + Tok = Tok->Children.empty() ? NULL : &Tok->Children[0]; + return Tok; } -static bool closesScope(const AnnotatedToken &Tok) { - return Tok.isOneOf(tok::r_paren, tok::r_brace, tok::r_square) || - Tok.Type == TT_TemplateCloser; +bool AnnotatedToken::closesScope() const { + return isOneOf(tok::r_paren, tok::r_brace, tok::r_square) || + Type == TT_TemplateCloser; } -static bool opensScope(const AnnotatedToken &Tok) { - return Tok.isOneOf(tok::l_paren, tok::l_brace, tok::l_square) || - Tok.Type == TT_TemplateOpener; +bool AnnotatedToken::opensScope() const { + return isOneOf(tok::l_paren, tok::l_brace, tok::l_square) || + Type == TT_TemplateOpener; } /// \brief A parser that gathers additional information about tokens. @@ -91,7 +81,7 @@ class AnnotatingParser { AnnotatingParser(SourceManager &SourceMgr, Lexer &Lex, AnnotatedLine &Line, IdentifierInfo &Ident_in) : SourceMgr(SourceMgr), Lex(Lex), Line(Line), CurrentToken(&Line.First), - KeywordVirtualFound(false), Ident_in(Ident_in) { + KeywordVirtualFound(false), NameFound(false), Ident_in(Ident_in) { Contexts.push_back(Context(tok::unknown, 1, /*IsExpression=*/ false)); } @@ -165,6 +155,8 @@ class AnnotatingParser { } if (CurrentToken->is(tok::r_paren)) { + if (CurrentToken->Parent->closesScope()) + CurrentToken->Parent->MatchingParen->NoMoreTokensOnLevel = true; Left->MatchingParen = CurrentToken; CurrentToken->MatchingParen = Left; @@ -196,12 +188,12 @@ class AnnotatingParser { // ')' or ']'), it could be the start of an Objective-C method // expression, or it could the the start of an Objective-C array literal. AnnotatedToken *Left = CurrentToken->Parent; - AnnotatedToken *Parent = getPreviousToken(*Left); + AnnotatedToken *Parent = Left->getPreviousNoneComment(); bool StartsObjCMethodExpr = Contexts.back().CanBeExpression && (!Parent || Parent->isOneOf(tok::colon, tok::l_square, tok::l_paren, tok::kw_return, tok::kw_throw) || - isUnaryOperator(*Parent) || Parent->Type == TT_ObjCForIn || + Parent->isUnaryOperator() || Parent->Type == TT_ObjCForIn || Parent->Type == TT_CastRParen || getBinOpPrecedence(Parent->FormatTok.Tok.getKind(), true, true) > prec::Unknown); @@ -253,24 +245,25 @@ class AnnotatingParser { } bool parseBrace() { - // Lines are fine to end with '{'. - if (CurrentToken == NULL) - return true; - ScopedContextCreator ContextCreator(*this, tok::l_brace, 1); - AnnotatedToken *Left = CurrentToken->Parent; - while (CurrentToken != NULL) { - if (CurrentToken->is(tok::r_brace)) { - Left->MatchingParen = CurrentToken; - CurrentToken->MatchingParen = Left; - next(); - return true; + if (CurrentToken != NULL) { + ScopedContextCreator ContextCreator(*this, tok::l_brace, 1); + AnnotatedToken *Left = CurrentToken->Parent; + while (CurrentToken != NULL) { + if (CurrentToken->is(tok::r_brace)) { + Left->MatchingParen = CurrentToken; + CurrentToken->MatchingParen = Left; + next(); + return true; + } + if (CurrentToken->isOneOf(tok::r_paren, tok::r_square)) + return false; + updateParameterCount(Left, CurrentToken); + if (!consumeToken()) + return false; } - if (CurrentToken->isOneOf(tok::r_paren, tok::r_square)) - return false; - updateParameterCount(Left, CurrentToken); - if (!consumeToken()) - return false; } + // No closing "}" found, this probably starts a definition. + Line.StartsDefinition = true; return true; } @@ -357,7 +350,7 @@ class AnnotatingParser { case tok::l_paren: if (!parseParens()) return false; - if (Line.MustBeDeclaration) + if (Line.MustBeDeclaration && NameFound && !Contexts.back().IsExpression) Line.MightBeFunctionDecl = true; break; case tok::l_square: @@ -467,6 +460,10 @@ class AnnotatingParser { case tok::pp_warning: parseWarningOrError(); break; + case tok::pp_if: + case tok::pp_elif: + parseLine(); + break; default: break; } @@ -573,7 +570,8 @@ class AnnotatingParser { }; void determineTokenType(AnnotatedToken &Current) { - if (getPrecedence(Current) == prec::Assignment) { + if (getPrecedence(Current) == prec::Assignment && + (!Current.Parent || Current.Parent->isNot(tok::kw_operator))) { Contexts.back().IsExpression = true; for (AnnotatedToken *Previous = Current.Parent; Previous && Previous->isNot(tok::comma); @@ -599,6 +597,9 @@ class AnnotatingParser { Contexts.back().IsExpression = true; } else if (Current.is(tok::kw_new)) { Contexts.back().CanBeExpression = false; + } else if (Current.is(tok::semi)) { + // This should be the condition or increment in a for-loop. + Contexts.back().IsExpression = true; } if (Current.Type == TT_Unknown) { @@ -611,6 +612,7 @@ class AnnotatingParser { Current.Parent->Type == TT_TemplateCloser)) { Contexts.back().FirstStartOfName = &Current; Current.Type = TT_StartOfName; + NameFound = true; } else if (Current.isOneOf(tok::star, tok::amp, tok::ampamp)) { Current.Type = determineStarAmpUsage(Current, Contexts.back().IsExpression); @@ -620,7 +622,7 @@ class AnnotatingParser { Current.Type = determineIncrementUsage(Current); } else if (Current.is(tok::exclaim)) { Current.Type = TT_UnaryOperator; - } else if (isBinaryOperator(Current)) { + } else if (Current.isBinaryOperator()) { Current.Type = TT_BinaryOperator; } else if (Current.is(tok::comment)) { std::string Data(Lexer::getSpelling(Current.FormatTok.Tok, SourceMgr, @@ -664,11 +666,11 @@ class AnnotatingParser { /// \brief Return the type of the given token assuming it is * or &. TokenType determineStarAmpUsage(const AnnotatedToken &Tok, bool IsExpression) { - const AnnotatedToken *PrevToken = getPreviousToken(Tok); + const AnnotatedToken *PrevToken = Tok.getPreviousNoneComment(); if (PrevToken == NULL) return TT_UnaryOperator; - const AnnotatedToken *NextToken = getNextToken(Tok); + const AnnotatedToken *NextToken = Tok.getNextNoneComment(); if (NextToken == NULL) return TT_Unknown; @@ -687,7 +689,7 @@ class AnnotatingParser { if (PrevToken->FormatTok.Tok.isLiteral() || PrevToken->isOneOf(tok::r_paren, tok::r_square) || - NextToken->FormatTok.Tok.isLiteral() || isUnaryOperator(*NextToken)) + NextToken->FormatTok.Tok.isLiteral() || NextToken->isUnaryOperator()) return TT_BinaryOperator; // It is very unlikely that we are going to find a pointer or reference type @@ -699,7 +701,7 @@ class AnnotatingParser { } TokenType determinePlusMinusCaretUsage(const AnnotatedToken &Tok) { - const AnnotatedToken *PrevToken = getPreviousToken(Tok); + const AnnotatedToken *PrevToken = Tok.getPreviousNoneComment(); if (PrevToken == NULL) return TT_UnaryOperator; @@ -719,7 +721,7 @@ class AnnotatingParser { /// \brief Determine whether ++/-- are pre- or post-increments/-decrements. TokenType determineIncrementUsage(const AnnotatedToken &Tok) { - const AnnotatedToken *PrevToken = getPreviousToken(Tok); + const AnnotatedToken *PrevToken = Tok.getPreviousNoneComment(); if (PrevToken == NULL) return TT_UnaryOperator; if (PrevToken->isOneOf(tok::r_paren, tok::r_square, tok::identifier)) @@ -768,6 +770,7 @@ class AnnotatingParser { AnnotatedLine &Line; AnnotatedToken *CurrentToken; bool KeywordVirtualFound; + bool NameFound; IdentifierInfo &Ident_in; }; @@ -782,12 +785,8 @@ class ExpressionParser { if (Precedence > prec::PointerToMember || Current == NULL) return; - // Skip over "return" until we can properly parse it. - if (Current->is(tok::kw_return)) - next(); - // Eagerly consume trailing comments. - while (isTrailingComment(Current)) { + while (Current && Current->isTrailingComment()) { next(); } @@ -796,14 +795,13 @@ class ExpressionParser { while (Current) { // Consume operators with higher precedence. - parse(prec::Level(Precedence + 1)); + parse(Precedence + 1); int CurrentPrecedence = 0; if (Current) { if (Current->Type == TT_ConditionalExpr) CurrentPrecedence = 1 + (int) prec::Conditional; - else if (Current->is(tok::semi) || Current->Type == TT_InlineASMColon || - Current->Type == TT_CtorInitializerColon) + else if (Current->is(tok::semi) || Current->Type == TT_InlineASMColon) CurrentPrecedence = 1; else if (Current->Type == TT_BinaryOperator || Current->is(tok::comma)) CurrentPrecedence = 1 + (int) getPrecedence(*Current); @@ -811,10 +809,10 @@ class ExpressionParser { // At the end of the line or when an operator with higher precedence is // found, insert fake parenthesis and return. - if (Current == NULL || closesScope(*Current) || + if (Current == NULL || Current->closesScope() || (CurrentPrecedence != 0 && CurrentPrecedence < Precedence)) { if (OperatorFound) { - ++Start->FakeLParens; + Start->FakeLParens.push_back(prec::Level(Precedence - 1)); if (Current) ++Current->Parent->FakeRParens; } @@ -822,18 +820,11 @@ class ExpressionParser { } // Consume scopes: (), [], <> and {} - if (opensScope(*Current)) { - AnnotatedToken *Left = Current; - while (Current && !closesScope(*Current)) { + if (Current->opensScope()) { + while (Current && !Current->closesScope()) { next(); parse(); } - // Remove fake parens that just duplicate the real parens. - if (Current && Left->Children[0].FakeLParens > 0 && - Current->Parent->FakeRParens > 0) { - --Left->Children[0].FakeLParens; - --Current->Parent->FakeRParens; - } next(); } else { // Operator found. @@ -892,7 +883,7 @@ void TokenAnnotator::calculateFormattingInformation(AnnotatedLine &Line) { Current->MustBreakBefore = true; } else if (Current->Type == TT_LineComment) { Current->MustBreakBefore = Current->FormatTok.NewlinesBefore > 0; - } else if (isTrailingComment(Current->Parent) || + } else if (Current->Parent->isTrailingComment() || (Current->is(tok::string_literal) && Current->Parent->is(tok::string_literal))) { Current->MustBreakBefore = true; @@ -919,6 +910,10 @@ void TokenAnnotator::calculateFormattingInformation(AnnotatedLine &Line) { Current = Current->Children.empty() ? NULL : &Current->Children[0]; } + + DEBUG({ + printDebugInfo(Line); + }); } unsigned TokenAnnotator::splitPenalty(const AnnotatedLine &Line, @@ -933,12 +928,14 @@ unsigned TokenAnnotator::splitPenalty(const AnnotatedLine &Line, // FIXME: Clean up hack of using BindingStrength to find top-level names. return Style.PenaltyReturnTypeOnItsOwnLine; else - return 100; + return 200; } if (Left.is(tok::equal) && Right.is(tok::l_brace)) return 150; if (Left.is(tok::coloncolon)) return 500; + if (Left.isOneOf(tok::kw_class, tok::kw_struct)) + return 5000; if (Left.Type == TT_RangeBasedForLoopColon || Left.Type == TT_InheritanceColon) @@ -969,7 +966,9 @@ unsigned TokenAnnotator::splitPenalty(const AnnotatedLine &Line, if (Left.is(tok::colon) && Left.Type == TT_ObjCMethodExpr) return 20; - if (opensScope(Left)) + if (Left.is(tok::l_paren) && Line.MightBeFunctionDecl) + return 100; + if (Left.opensScope()) return Left.ParameterCount > 1 ? prec::Comma : 20; if (Right.is(tok::lessless)) { @@ -1050,13 +1049,15 @@ bool TokenAnnotator::spaceRequiredBetween(const AnnotatedLine &Line, return Line.Type == LT_ObjCDecl || Left.isOneOf(tok::kw_if, tok::kw_for, tok::kw_while, tok::kw_switch, tok::kw_return, tok::kw_catch, tok::kw_new, - tok::kw_delete); + tok::kw_delete, tok::semi); } if (Left.is(tok::at) && Right.FormatTok.Tok.getObjCKeywordID() != tok::objc_not_keyword) return false; if (Left.is(tok::l_brace) && Right.is(tok::r_brace)) return false; + if (Right.is(tok::ellipsis)) + return false; return true; } @@ -1088,7 +1089,7 @@ bool TokenAnnotator::spaceRequiredBefore(const AnnotatedLine &Line, return false; if (Tok.is(tok::colon)) return !Line.First.isOneOf(tok::kw_case, tok::kw_default) && - !Tok.Children.empty() && Tok.Type != TT_ObjCMethodExpr; + Tok.getNextNoneComment() != NULL && Tok.Type != TT_ObjCMethodExpr; if (Tok.is(tok::l_paren) && !Tok.Children.empty() && Tok.Children[0].Type == TT_PointerOrReference && !Tok.Children[0].Children.empty() && @@ -1137,10 +1138,9 @@ bool TokenAnnotator::canBreakBefore(const AnnotatedLine &Line, if (Right.Type == TT_ConditionalExpr || Right.is(tok::question)) return true; if (Right.Type == TT_RangeBasedForLoopColon || - Right.Type == TT_InheritanceColon) + Right.Type == TT_OverloadedOperatorLParen) return false; - if (Left.Type == TT_RangeBasedForLoopColon || - Left.Type == TT_InheritanceColon) + if (Left.Type == TT_RangeBasedForLoopColon) return true; if (Right.Type == TT_RangeBasedForLoopColon) return false; @@ -1174,8 +1174,9 @@ bool TokenAnnotator::canBreakBefore(const AnnotatedLine &Line, return false; if (Left.is(tok::identifier) && Right.is(tok::string_literal)) return true; - return (isBinaryOperator(Left) && Left.isNot(tok::lessless)) || - Left.isOneOf(tok::comma, tok::coloncolon, tok::semi, tok::l_brace) || + return (Left.isBinaryOperator() && Left.isNot(tok::lessless)) || + Left.isOneOf(tok::comma, tok::coloncolon, tok::semi, tok::l_brace, + tok::kw_class, tok::kw_struct) || Right.isOneOf(tok::lessless, tok::arrow, tok::period, tok::colon) || (Left.is(tok::r_paren) && Left.Type != TT_CastRParen && Right.isOneOf(tok::identifier, tok::kw___attribute)) || @@ -1183,5 +1184,22 @@ bool TokenAnnotator::canBreakBefore(const AnnotatedLine &Line, (Left.is(tok::l_square) && !Right.is(tok::r_square)); } +void TokenAnnotator::printDebugInfo(const AnnotatedLine &Line) { + llvm::errs() << "AnnotatedTokens:\n"; + const AnnotatedToken *Tok = &Line.First; + while (Tok) { + llvm::errs() << " M=" << Tok->MustBreakBefore + << " C=" << Tok->CanBreakBefore << " T=" << Tok->Type + << " S=" << Tok->SpacesRequiredBefore + << " P=" << Tok->SplitPenalty + << " Name=" << Tok->FormatTok.Tok.getName() << " FakeLParens="; + for (unsigned i = 0, e = Tok->FakeLParens.size(); i != e; ++i) + llvm::errs() << Tok->FakeLParens[i] << "/"; + llvm::errs() << " FakeRParens=" << Tok->FakeRParens << "\n"; + Tok = Tok->Children.empty() ? NULL : &Tok->Children[0]; + } + llvm::errs() << "----\n"; +} + } // namespace format } // namespace clang diff --git a/lib/Format/TokenAnnotator.h b/lib/Format/TokenAnnotator.h index c41ee33c439..b364082391f 100644 --- a/lib/Format/TokenAnnotator.h +++ b/lib/Format/TokenAnnotator.h @@ -75,9 +75,9 @@ class AnnotatedToken { CanBreakBefore(false), MustBreakBefore(false), ClosesTemplateDeclaration(false), MatchingParen(NULL), ParameterCount(0), BindingStrength(0), SplitPenalty(0), - LongestObjCSelectorName(0), Parent(NULL), FakeLParens(0), + LongestObjCSelectorName(0), Parent(NULL), FakeRParens(0), LastInChainOfCalls(false), - PartOfMultiVariableDeclStmt(false) {} + PartOfMultiVariableDeclStmt(false), NoMoreTokensOnLevel(false) {} bool is(tok::TokenKind Kind) const { return FormatTok.Tok.is(Kind); } @@ -121,6 +121,15 @@ class AnnotatedToken { Children[0].isObjCAtKeyword(tok::objc_private)); } + /// \brief Returns whether \p Tok is ([{ or a template opening <. + bool opensScope() const; + /// \brief Returns whether \p Tok is )]} or a template opening >. + bool closesScope() const; + + bool isUnaryOperator() const; + bool isBinaryOperator() const; + bool isTrailingComment() const; + FormatToken FormatTok; TokenType Type; @@ -158,8 +167,12 @@ class AnnotatedToken { std::vector Children; AnnotatedToken *Parent; - /// \brief Insert this many fake ( before this token for correct indentation. - unsigned FakeLParens; + /// \brief Stores the number of required fake parentheses and the + /// corresponding operator precedence. + /// + /// If multiple fake parentheses start at a token, this vector stores them in + /// reverse order, i.e. inner fake parenthesis first. + SmallVector FakeLParens; /// \brief Insert this many fake ) after this token for correct indentation. unsigned FakeRParens; @@ -171,12 +184,24 @@ class AnnotatedToken { /// Only set if \c Type == \c TT_StartOfName. bool PartOfMultiVariableDeclStmt; - const AnnotatedToken *getPreviousNoneComment() const { - AnnotatedToken *Tok = Parent; - while (Tok != NULL && Tok->is(tok::comment)) - Tok = Tok->Parent; - return Tok; - } + /// \brief Set to \c true for "("-tokens if this is the last token other than + /// ")" in the next higher parenthesis level. + /// + /// If this is \c true, no more formatting decisions have to be made on the + /// next higher parenthesis level, enabling optimizations. + /// + /// Example: + /// \code + /// aaaaaa(aaaaaa()); + /// ^ // Set to true for this parenthesis. + /// \endcode + bool NoMoreTokensOnLevel; + + /// \brief Returns the previous token ignoring comments. + AnnotatedToken *getPreviousNoneComment() const; + + /// \brief Returns the next token ignoring comments. + const AnnotatedToken *getNextNoneComment() const; }; class AnnotatedLine { @@ -184,8 +209,8 @@ class AnnotatedLine { AnnotatedLine(const UnwrappedLine &Line) : First(Line.Tokens.front()), Level(Line.Level), InPPDirective(Line.InPPDirective), - MustBeDeclaration(Line.MustBeDeclaration), - MightBeFunctionDecl(false) { + MustBeDeclaration(Line.MustBeDeclaration), MightBeFunctionDecl(false), + StartsDefinition(false) { assert(!Line.Tokens.empty()); AnnotatedToken *Current = &First; for (std::list::const_iterator I = ++Line.Tokens.begin(), @@ -201,7 +226,8 @@ class AnnotatedLine { : First(Other.First), Type(Other.Type), Level(Other.Level), InPPDirective(Other.InPPDirective), MustBeDeclaration(Other.MustBeDeclaration), - MightBeFunctionDecl(Other.MightBeFunctionDecl) { + MightBeFunctionDecl(Other.MightBeFunctionDecl), + StartsDefinition(Other.StartsDefinition) { Last = &First; while (!Last->Children.empty()) { Last->Children[0].Parent = Last; @@ -217,6 +243,7 @@ class AnnotatedLine { bool InPPDirective; bool MustBeDeclaration; bool MightBeFunctionDecl; + bool StartsDefinition; }; inline prec::Level getPrecedence(const AnnotatedToken &Tok) { @@ -248,6 +275,8 @@ class TokenAnnotator { bool canBreakBefore(const AnnotatedLine &Line, const AnnotatedToken &Right); + void printDebugInfo(const AnnotatedLine &Line); + const FormatStyle &Style; SourceManager &SourceMgr; Lexer &Lex; diff --git a/lib/Format/UnwrappedLineParser.cpp b/lib/Format/UnwrappedLineParser.cpp index 89a391bd192..722af5d2b76 100644 --- a/lib/Format/UnwrappedLineParser.cpp +++ b/lib/Format/UnwrappedLineParser.cpp @@ -45,9 +45,11 @@ class ScopedDeclarationState { class ScopedMacroState : public FormatTokenSource { public: ScopedMacroState(UnwrappedLine &Line, FormatTokenSource *&TokenSource, - FormatToken &ResetToken) + FormatToken &ResetToken, bool &StructuralError) : Line(Line), TokenSource(TokenSource), ResetToken(ResetToken), - PreviousLineLevel(Line.Level), PreviousTokenSource(TokenSource) { + PreviousLineLevel(Line.Level), PreviousTokenSource(TokenSource), + StructuralError(StructuralError), + PreviousStructuralError(StructuralError) { TokenSource = this; Line.Level = 0; Line.InPPDirective = true; @@ -58,6 +60,7 @@ class ScopedMacroState : public FormatTokenSource { ResetToken = Token; Line.InPPDirective = false; Line.Level = PreviousLineLevel; + StructuralError = PreviousStructuralError; } virtual FormatToken getNextToken() { @@ -71,7 +74,7 @@ class ScopedMacroState : public FormatTokenSource { } private: - bool eof() { return Token.NewlinesBefore > 0 && Token.HasUnescapedNewline; } + bool eof() { return Token.HasUnescapedNewline; } FormatToken createEOF() { FormatToken FormatTok; @@ -85,6 +88,8 @@ class ScopedMacroState : public FormatTokenSource { FormatToken &ResetToken; unsigned PreviousLineLevel; FormatTokenSource *PreviousTokenSource; + bool &StructuralError; + bool PreviousStructuralError; FormatToken Token; }; @@ -124,13 +129,13 @@ UnwrappedLineParser::UnwrappedLineParser( clang::DiagnosticsEngine &Diag, const FormatStyle &Style, FormatTokenSource &Tokens, UnwrappedLineConsumer &Callback) : Line(new UnwrappedLine), MustBreakBeforeNextToken(false), - CurrentLines(&Lines), Diag(Diag), Style(Style), Tokens(&Tokens), - Callback(Callback) {} + CurrentLines(&Lines), StructuralError(false), Diag(Diag), Style(Style), + Tokens(&Tokens), Callback(Callback) {} bool UnwrappedLineParser::parse() { DEBUG(llvm::dbgs() << "----\n"); readToken(); - bool Error = parseFile(); + parseFile(); for (std::vector::iterator I = Lines.begin(), E = Lines.end(); I != E; ++I) { Callback.consumeUnwrappedLine(*I); @@ -139,23 +144,20 @@ bool UnwrappedLineParser::parse() { // Create line with eof token. pushToken(FormatTok); Callback.consumeUnwrappedLine(*Line); - - return Error; + return StructuralError; } -bool UnwrappedLineParser::parseFile() { +void UnwrappedLineParser::parseFile() { ScopedDeclarationState DeclarationState( *Line, DeclarationScopeStack, /*MustBeDeclaration=*/ !Line->InPPDirective); - bool Error = parseLevel(/*HasOpeningBrace=*/ false); + parseLevel(/*HasOpeningBrace=*/ false); // Make sure to format the remaining tokens. flushComments(true); addUnwrappedLine(); - return Error; } -bool UnwrappedLineParser::parseLevel(bool HasOpeningBrace) { - bool Error = false; +void UnwrappedLineParser::parseLevel(bool HasOpeningBrace) { do { switch (FormatTok.Tok.getKind()) { case tok::comment: @@ -165,30 +167,27 @@ bool UnwrappedLineParser::parseLevel(bool HasOpeningBrace) { case tok::l_brace: // FIXME: Add parameter whether this can happen - if this happens, we must // be in a non-declaration context. - Error |= parseBlock(/*MustBeDeclaration=*/ false); + parseBlock(/*MustBeDeclaration=*/ false); addUnwrappedLine(); break; case tok::r_brace: - if (HasOpeningBrace) { - return false; - } else { - Diag.Report(FormatTok.Tok.getLocation(), - Diag.getCustomDiagID(clang::DiagnosticsEngine::Error, - "unexpected '}'")); - Error = true; - nextToken(); - addUnwrappedLine(); - } + if (HasOpeningBrace) + return; + Diag.Report(FormatTok.Tok.getLocation(), + Diag.getCustomDiagID(clang::DiagnosticsEngine::Error, + "unexpected '}'")); + StructuralError = true; + nextToken(); + addUnwrappedLine(); break; default: parseStructuralElement(); break; } } while (!eof()); - return Error; } -bool UnwrappedLineParser::parseBlock(bool MustBeDeclaration, +void UnwrappedLineParser::parseBlock(bool MustBeDeclaration, unsigned AddLevels) { assert(FormatTok.Tok.is(tok::l_brace) && "'{' expected"); nextToken(); @@ -202,17 +201,17 @@ bool UnwrappedLineParser::parseBlock(bool MustBeDeclaration, if (!FormatTok.Tok.is(tok::r_brace)) { Line->Level -= AddLevels; - return true; + StructuralError = true; + return; } nextToken(); // Munch the closing brace. Line->Level -= AddLevels; - return false; } void UnwrappedLineParser::parsePPDirective() { assert(FormatTok.Tok.is(tok::hash) && "'#' expected"); - ScopedMacroState MacroState(*Line, Tokens, FormatTok); + ScopedMacroState MacroState(*Line, Tokens, FormatTok, StructuralError); nextToken(); if (FormatTok.Tok.getIdentifierInfo() == NULL) { @@ -260,9 +259,35 @@ void UnwrappedLineParser::parsePPUnknown() { addUnwrappedLine(); } +// Here we blacklist certain tokens that are not usually the first token in an +// unwrapped line. This is used in attempt to distinguish macro calls without +// trailing semicolons from other constructs split to several lines. +bool tokenCanStartNewLine(clang::Token Tok) { + // Semicolon can be a null-statement, l_square can be a start of a macro or + // a C++11 attribute, but this doesn't seem to be common. + return Tok.isNot(tok::semi) && Tok.isNot(tok::l_brace) && + Tok.isNot(tok::l_square) && + // Tokens that can only be used as binary operators and a part of + // overloaded operator names. + Tok.isNot(tok::period) && Tok.isNot(tok::periodstar) && + Tok.isNot(tok::arrow) && Tok.isNot(tok::arrowstar) && + Tok.isNot(tok::less) && Tok.isNot(tok::greater) && + Tok.isNot(tok::slash) && Tok.isNot(tok::percent) && + Tok.isNot(tok::lessless) && Tok.isNot(tok::greatergreater) && + Tok.isNot(tok::equal) && Tok.isNot(tok::plusequal) && + Tok.isNot(tok::minusequal) && Tok.isNot(tok::starequal) && + Tok.isNot(tok::slashequal) && Tok.isNot(tok::percentequal) && + Tok.isNot(tok::ampequal) && Tok.isNot(tok::pipeequal) && + Tok.isNot(tok::caretequal) && Tok.isNot(tok::greatergreaterequal) && + Tok.isNot(tok::lesslessequal) && + // Colon is used in labels, base class lists, initializer lists, + // range-based for loops, ternary operator, but should never be the + // first token in an unwrapped line. + Tok.isNot(tok::colon); +} + void UnwrappedLineParser::parseStructuralElement() { assert(!FormatTok.Tok.is(tok::l_brace)); - int TokenNumber = 0; switch (FormatTok.Tok.getKind()) { case tok::at: nextToken(); @@ -297,7 +322,6 @@ void UnwrappedLineParser::parseStructuralElement() { return; case tok::kw_inline: nextToken(); - TokenNumber++; if (FormatTok.Tok.is(tok::kw_namespace)) { parseNamespace(); return; @@ -347,7 +371,6 @@ void UnwrappedLineParser::parseStructuralElement() { break; } do { - ++TokenNumber; switch (FormatTok.Tok.getKind()) { case tok::at: nextToken(); @@ -384,9 +407,20 @@ void UnwrappedLineParser::parseStructuralElement() { return; case tok::identifier: nextToken(); - if (TokenNumber == 1 && FormatTok.Tok.is(tok::colon)) { - parseLabel(); - return; + if (Line->Tokens.size() == 1) { + if (FormatTok.Tok.is(tok::colon)) { + parseLabel(); + return; + } + // Recognize function-like macro usages without trailing semicolon. + if (FormatTok.Tok.is(tok::l_paren)) { + parseParens(); + if (FormatTok.HasUnescapedNewline && + tokenCanStartNewLine(FormatTok.Tok)) { + addUnwrappedLine(); + return; + } + } } break; case tok::equal: @@ -405,16 +439,36 @@ void UnwrappedLineParser::parseStructuralElement() { void UnwrappedLineParser::parseBracedList() { nextToken(); + // FIXME: Once we have an expression parser in the UnwrappedLineParser, + // replace this by using parseAssigmentExpression() inside. + bool StartOfExpression = true; do { + // FIXME: When we start to support lambdas, we'll want to parse them away + // here, otherwise our bail-out scenarios below break. The better solution + // might be to just implement a more or less complete expression parser. switch (FormatTok.Tok.getKind()) { case tok::l_brace: + if (!StartOfExpression) { + // Probably a missing closing brace. Bail out. + addUnwrappedLine(); + return; + } parseBracedList(); + StartOfExpression = false; break; case tok::r_brace: nextToken(); return; + case tok::semi: + // Probably a missing closing brace. Bail out. + return; + case tok::comma: + nextToken(); + StartOfExpression = true; + break; default: nextToken(); + StartOfExpression = false; break; } } while (!eof()); @@ -427,6 +481,11 @@ void UnwrappedLineParser::parseReturn() { switch (FormatTok.Tok.getKind()) { case tok::l_brace: parseBracedList(); + if (FormatTok.Tok.isNot(tok::semi)) { + // Assume missing ';'. + addUnwrappedLine(); + return; + } break; case tok::l_paren: parseParens(); @@ -820,8 +879,7 @@ void UnwrappedLineParser::readToken() { do { FormatTok = Tokens->getNextToken(); while (!Line->InPPDirective && FormatTok.Tok.is(tok::hash) && - ((FormatTok.NewlinesBefore > 0 && FormatTok.HasUnescapedNewline) || - FormatTok.IsFirst)) { + (FormatTok.HasUnescapedNewline || FormatTok.IsFirst)) { // If there is an unfinished unwrapped line, we flush the preprocessor // directives only after that unwrapped line was finished later. bool SwitchToPreprocessorLines = diff --git a/lib/Format/UnwrappedLineParser.h b/lib/Format/UnwrappedLineParser.h index f4fecc5ef0a..0c618e24d44 100644 --- a/lib/Format/UnwrappedLineParser.h +++ b/lib/Format/UnwrappedLineParser.h @@ -34,7 +34,7 @@ struct FormatToken { FormatToken() : NewlinesBefore(0), HasUnescapedNewline(false), WhiteSpaceLength(0), LastNewlineOffset(0), TokenLength(0), IsFirst(false), - MustBreakBefore(false) {} + MustBreakBefore(false), TrailingWhiteSpaceLength(0) {} /// \brief The \c Token. Token Tok; @@ -76,6 +76,18 @@ struct FormatToken { /// This happens for example when a preprocessor directive ended directly /// before the token. bool MustBreakBefore; + + /// \brief Number of characters of trailing whitespace. + unsigned TrailingWhiteSpaceLength; + + /// \brief Returns actual token start location without leading escaped + /// newlines and whitespace. + /// + /// This can be different to Tok.getLocation(), which includes leading escaped + /// newlines. + SourceLocation getStartOfNonWhitespace() const { + return WhiteSpaceStart.getLocWithOffset(WhiteSpaceLength); + } }; /// \brief An unwrapped line is a sequence of \c Token, that we would like to @@ -125,9 +137,9 @@ class UnwrappedLineParser { bool parse(); private: - bool parseFile(); - bool parseLevel(bool HasOpeningBrace); - bool parseBlock(bool MustBeDeclaration, unsigned AddLevels = 1); + void parseFile(); + void parseLevel(bool HasOpeningBrace); + void parseBlock(bool MustBeDeclaration, unsigned AddLevels = 1); void parsePPDirective(); void parsePPDefine(); void parsePPUnknown(); @@ -187,6 +199,10 @@ class UnwrappedLineParser { // whether we are in a compound statement or not. std::vector DeclarationScopeStack; + // Will be true if we encounter an error that leads to possibily incorrect + // indentation levels. + bool StructuralError; + clang::DiagnosticsEngine &Diag; const FormatStyle &Style; FormatTokenSource *Tokens; diff --git a/lib/Format/WhitespaceManager.cpp b/lib/Format/WhitespaceManager.cpp new file mode 100644 index 00000000000..a75c592bfed --- /dev/null +++ b/lib/Format/WhitespaceManager.cpp @@ -0,0 +1,211 @@ +//===--- WhitespaceManager.cpp - Format C++ code --------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief This file implements WhitespaceManager class. +/// +//===----------------------------------------------------------------------===// + +#include "WhitespaceManager.h" +#include "llvm/ADT/STLExtras.h" + +namespace clang { +namespace format { + +void WhitespaceManager::replaceWhitespace(const AnnotatedToken &Tok, + unsigned NewLines, unsigned Spaces, + unsigned WhitespaceStartColumn) { + if (NewLines > 0) + alignEscapedNewlines(); + + // 2+ newlines mean an empty line separating logic scopes. + if (NewLines >= 2) + alignComments(); + + // Align line comments if they are trailing or if they continue other + // trailing comments. + if (Tok.isTrailingComment()) { + SourceLocation TokenEndLoc = Tok.FormatTok.getStartOfNonWhitespace() + .getLocWithOffset(Tok.FormatTok.TokenLength); + // Remove the comment's trailing whitespace. + if (Tok.FormatTok.TrailingWhiteSpaceLength != 0) + Replaces.insert(tooling::Replacement( + SourceMgr, TokenEndLoc, Tok.FormatTok.TrailingWhiteSpaceLength, "")); + + bool LineExceedsColumnLimit = + Spaces + WhitespaceStartColumn + Tok.FormatTok.TokenLength > + Style.ColumnLimit; + // Align comment with other comments. + if ((Tok.Parent != NULL || !Comments.empty()) && + !LineExceedsColumnLimit) { + unsigned MinColumn = + NewLines > 0 ? Spaces : WhitespaceStartColumn + Spaces; + unsigned MaxColumn = Style.ColumnLimit - Tok.FormatTok.TokenLength; + Comments.push_back(StoredToken( + Tok.FormatTok.WhiteSpaceStart, Tok.FormatTok.WhiteSpaceLength, + MinColumn, MaxColumn, NewLines, Spaces)); + return; + } + } + + // If this line does not have a trailing comment, align the stored comments. + if (Tok.Children.empty() && !Tok.isTrailingComment()) + alignComments(); + + storeReplacement(Tok.FormatTok.WhiteSpaceStart, + Tok.FormatTok.WhiteSpaceLength, + getNewLineText(NewLines, Spaces)); +} + +void WhitespaceManager::replacePPWhitespace(const AnnotatedToken &Tok, + unsigned NewLines, unsigned Spaces, + unsigned WhitespaceStartColumn) { + if (NewLines == 0) { + replaceWhitespace(Tok, NewLines, Spaces, WhitespaceStartColumn); + } else { + // The earliest position for "\" is 2 after the last token. + unsigned MinColumn = WhitespaceStartColumn + 2; + unsigned MaxColumn = Style.ColumnLimit; + EscapedNewlines.push_back(StoredToken( + Tok.FormatTok.WhiteSpaceStart, Tok.FormatTok.WhiteSpaceLength, + MinColumn, MaxColumn, NewLines, Spaces)); + } +} + +void WhitespaceManager::breakToken(const FormatToken &Tok, unsigned Offset, + unsigned ReplaceChars, StringRef Prefix, + StringRef Postfix, bool InPPDirective, + unsigned Spaces, + unsigned WhitespaceStartColumn) { + SourceLocation Location = + Tok.getStartOfNonWhitespace().getLocWithOffset(Offset); + if (InPPDirective) { + // The earliest position for "\" is 2 after the last token. + unsigned MinColumn = WhitespaceStartColumn + 2; + unsigned MaxColumn = Style.ColumnLimit; + StoredToken StoredTok = StoredToken(Location, ReplaceChars, MinColumn, + MaxColumn, /*NewLines=*/ 1, Spaces); + StoredTok.Prefix = Prefix; + StoredTok.Postfix = Postfix; + EscapedNewlines.push_back(StoredTok); + } else { + std::string ReplacementText = + (Prefix + getNewLineText(1, Spaces) + Postfix).str(); + Replaces.insert(tooling::Replacement(SourceMgr, Location, ReplaceChars, + ReplacementText)); + } +} + +const tooling::Replacements &WhitespaceManager::generateReplacements() { + alignComments(); + alignEscapedNewlines(); + return Replaces; +} + +void WhitespaceManager::addReplacement(const SourceLocation &SourceLoc, + unsigned ReplaceChars, StringRef Text) { + Replaces.insert( + tooling::Replacement(SourceMgr, SourceLoc, ReplaceChars, Text)); +} + +void WhitespaceManager::addUntouchableComment(unsigned Column) { + StoredToken Tok = StoredToken(SourceLocation(), 0, Column, Column, 0, 0); + Tok.Untouchable = true; + Comments.push_back(Tok); +} + +std::string WhitespaceManager::getNewLineText(unsigned NewLines, + unsigned Spaces) { + return std::string(NewLines, '\n') + std::string(Spaces, ' '); +} + +std::string WhitespaceManager::getNewLineText(unsigned NewLines, + unsigned Spaces, + unsigned WhitespaceStartColumn, + unsigned EscapedNewlineColumn) { + std::string NewLineText; + if (NewLines > 0) { + unsigned Offset = + std::min(EscapedNewlineColumn - 1, WhitespaceStartColumn); + for (unsigned i = 0; i < NewLines; ++i) { + NewLineText += std::string(EscapedNewlineColumn - Offset - 1, ' '); + NewLineText += "\\\n"; + Offset = 0; + } + } + return NewLineText + std::string(Spaces, ' '); +} + +void WhitespaceManager::alignComments() { + unsigned MinColumn = 0; + unsigned MaxColumn = UINT_MAX; + token_iterator Start = Comments.begin(); + for (token_iterator I = Start, E = Comments.end(); I != E; ++I) { + if (I->MinColumn > MaxColumn || I->MaxColumn < MinColumn) { + alignComments(Start, I, MinColumn); + MinColumn = I->MinColumn; + MaxColumn = I->MaxColumn; + Start = I; + } else { + MinColumn = std::max(MinColumn, I->MinColumn); + MaxColumn = std::min(MaxColumn, I->MaxColumn); + } + } + alignComments(Start, Comments.end(), MinColumn); + Comments.clear(); +} + +void WhitespaceManager::alignComments(token_iterator I, token_iterator E, + unsigned Column) { + while (I != E) { + if (!I->Untouchable) { + unsigned Spaces = I->Spaces + Column - I->MinColumn; + storeReplacement(I->ReplacementLoc, I->ReplacementLength, + getNewLineText(I->NewLines, Spaces)); + } + ++I; + } +} + +void WhitespaceManager::alignEscapedNewlines() { + unsigned MinColumn; + if (Style.AlignEscapedNewlinesLeft) { + MinColumn = 0; + for (token_iterator I = EscapedNewlines.begin(), E = EscapedNewlines.end(); + I != E; ++I) { + if (I->MinColumn > MinColumn) + MinColumn = I->MinColumn; + } + } else { + MinColumn = Style.ColumnLimit; + } + + for (token_iterator I = EscapedNewlines.begin(), E = EscapedNewlines.end(); + I != E; ++I) { + // I->MinColumn - 2 is the end of the previous token (i.e. the + // WhitespaceStartColumn). + storeReplacement( + I->ReplacementLoc, I->ReplacementLength, + I->Prefix + getNewLineText(I->NewLines, I->Spaces, I->MinColumn - 2, + MinColumn) + I->Postfix); + + } + EscapedNewlines.clear(); +} + +void WhitespaceManager::storeReplacement(SourceLocation Loc, unsigned Length, + const std::string Text) { + // Don't create a replacement, if it does not change anything. + if (StringRef(SourceMgr.getCharacterData(Loc), Length) == Text) + return; + Replaces.insert(tooling::Replacement(SourceMgr, Loc, Length, Text)); +} + +} // namespace format +} // namespace clang diff --git a/lib/Format/WhitespaceManager.h b/lib/Format/WhitespaceManager.h new file mode 100644 index 00000000000..5f3dc55edac --- /dev/null +++ b/lib/Format/WhitespaceManager.h @@ -0,0 +1,119 @@ +//===--- WhitespaceManager.h - Format C++ code ------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief WhitespaceManager class manages whitespace around tokens and their +/// replacements. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_FORMAT_WHITESPACEMANAGER_H +#define LLVM_CLANG_FORMAT_WHITESPACEMANAGER_H + +#include "TokenAnnotator.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Format/Format.h" +#include + +namespace clang { +namespace format { + +/// \brief Manages the whitespaces around tokens and their replacements. +/// +/// This includes special handling for certain constructs, e.g. the alignment of +/// trailing line comments. +class WhitespaceManager { +public: + WhitespaceManager(SourceManager &SourceMgr, const FormatStyle &Style) + : SourceMgr(SourceMgr), Style(Style) {} + + /// \brief Replaces the whitespace in front of \p Tok. Only call once for + /// each \c AnnotatedToken. + void replaceWhitespace(const AnnotatedToken &Tok, unsigned NewLines, + unsigned Spaces, unsigned WhitespaceStartColumn); + + /// \brief Like \c replaceWhitespace, but additionally adds right-aligned + /// backslashes to escape newlines inside a preprocessor directive. + /// + /// This function and \c replaceWhitespace have the same behavior if + /// \c Newlines == 0. + void replacePPWhitespace(const AnnotatedToken &Tok, unsigned NewLines, + unsigned Spaces, unsigned WhitespaceStartColumn); + + /// \brief Inserts a line break into the middle of a token. + /// + /// Will break at \p Offset inside \p Tok, putting \p Prefix before the line + /// break and \p Postfix before the rest of the token starts in the next line. + /// + /// \p InPPDirective, \p Spaces, \p WhitespaceStartColumn and \p Style are + /// used to generate the correct line break. + void breakToken(const FormatToken &Tok, unsigned Offset, + unsigned ReplaceChars, StringRef Prefix, StringRef Postfix, + bool InPPDirective, unsigned Spaces, + unsigned WhitespaceStartColumn); + + /// \brief Returns all the \c Replacements created during formatting. + const tooling::Replacements &generateReplacements(); + + void addReplacement(const SourceLocation &SourceLoc, unsigned ReplaceChars, + StringRef Text); + + void addUntouchableComment(unsigned Column); + + /// \brief Try to align all stashed comments. + void alignComments(); + /// \brief Try to align all stashed escaped newlines. + void alignEscapedNewlines(); + +private: + std::string getNewLineText(unsigned NewLines, unsigned Spaces); + + std::string getNewLineText(unsigned NewLines, unsigned Spaces, + unsigned WhitespaceStartColumn, + unsigned EscapedNewlineColumn); + + /// \brief Structure to store tokens for later layout and alignment. + struct StoredToken { + StoredToken(SourceLocation ReplacementLoc, unsigned ReplacementLength, + unsigned MinColumn, unsigned MaxColumn, unsigned NewLines, + unsigned Spaces) + : ReplacementLoc(ReplacementLoc), ReplacementLength(ReplacementLength), + MinColumn(MinColumn), MaxColumn(MaxColumn), NewLines(NewLines), + Spaces(Spaces), Untouchable(false) {} + SourceLocation ReplacementLoc; + unsigned ReplacementLength; + unsigned MinColumn; + unsigned MaxColumn; + unsigned NewLines; + unsigned Spaces; + bool Untouchable; + std::string Prefix; + std::string Postfix; + }; + SmallVector Comments; + SmallVector EscapedNewlines; + typedef SmallVector::iterator token_iterator; + + /// \brief Put all the comments between \p I and \p E into \p Column. + void alignComments(token_iterator I, token_iterator E, unsigned Column); + + /// \brief Stores \p Text as the replacement for the whitespace in front of + /// \p Tok. + void storeReplacement(SourceLocation Loc, unsigned Length, + const std::string Text); + + SourceManager &SourceMgr; + tooling::Replacements Replaces; + const FormatStyle &Style; +}; + +} // namespace format +} // namespace clang + +#endif // LLVM_CLANG_FORMAT_WHITESPACEMANAGER_H diff --git a/lib/Frontend/ASTMerge.cpp b/lib/Frontend/ASTMerge.cpp index bfb30836d81..b6c644eba78 100644 --- a/lib/Frontend/ASTMerge.cpp +++ b/lib/Frontend/ASTMerge.cpp @@ -34,7 +34,7 @@ bool ASTMergeAction::BeginSourceFileAction(CompilerInstance &CI, void ASTMergeAction::ExecuteAction() { CompilerInstance &CI = getCompilerInstance(); CI.getDiagnostics().getClient()->BeginSourceFile( - CI.getASTContext().getLangOpts()); + CI.getASTContext().getLangOpts()); CI.getDiagnostics().SetArgToStringFn(&FormatASTNodeDiagnosticArgument, &CI.getASTContext()); IntrusiveRefCntPtr @@ -42,8 +42,9 @@ void ASTMergeAction::ExecuteAction() { for (unsigned I = 0, N = ASTFiles.size(); I != N; ++I) { IntrusiveRefCntPtr Diags(new DiagnosticsEngine(DiagIDs, &CI.getDiagnosticOpts(), - CI.getDiagnostics().getClient(), - /*ShouldOwnClient=*/false)); + new ForwardingDiagnosticConsumer( + *CI.getDiagnostics().getClient()), + /*ShouldOwnClient=*/true)); ASTUnit *Unit = ASTUnit::LoadFromASTFile(ASTFiles[I], Diags, CI.getFileSystemOpts(), false); if (!Unit) diff --git a/lib/Frontend/ASTUnit.cpp b/lib/Frontend/ASTUnit.cpp index c1115aedbf8..8bd5172454c 100644 --- a/lib/Frontend/ASTUnit.cpp +++ b/lib/Frontend/ASTUnit.cpp @@ -236,6 +236,11 @@ ASTUnit::ASTUnit(bool _MainFileIsAST) } ASTUnit::~ASTUnit() { + // If we loaded from an AST file, balance out the BeginSourceFile call. + if (MainFileIsAST && getDiagnostics().getClient()) { + getDiagnostics().getClient()->EndSourceFile(); + } + clearFileLevelDecls(); // Clean up the temporary files and the preamble file. @@ -581,25 +586,24 @@ class ASTInfoCollector : public ASTReaderListener { } }; + /// \brief Diagnostic consumer that saves each diagnostic it is given. class StoredDiagnosticConsumer : public DiagnosticConsumer { SmallVectorImpl &StoredDiags; - + SourceManager *SourceMgr; + public: explicit StoredDiagnosticConsumer( SmallVectorImpl &StoredDiags) - : StoredDiags(StoredDiags) { } - + : StoredDiags(StoredDiags), SourceMgr(0) { } + + virtual void BeginSourceFile(const LangOptions &LangOpts, + const Preprocessor *PP = 0) { + if (PP) + SourceMgr = &PP->getSourceManager(); + } + virtual void HandleDiagnostic(DiagnosticsEngine::Level Level, const Diagnostic &Info); - - DiagnosticConsumer *clone(DiagnosticsEngine &Diags) const { - // Just drop any diagnostics that come from cloned consumers; they'll - // have different source managers anyway. - // FIXME: We'd like to be able to capture these somehow, even if it's just - // file/line/column, because they could occur when parsing module maps or - // building modules on-demand. - return new IgnoringDiagConsumer(); - } }; /// \brief RAII object that optionally captures diagnostics, if @@ -635,7 +639,11 @@ void StoredDiagnosticConsumer::HandleDiagnostic(DiagnosticsEngine::Level Level, // Default implementation (Warnings/errors count). DiagnosticConsumer::HandleDiagnostic(Level, Info); - StoredDiags.push_back(StoredDiagnostic(Level, Info)); + // Only record the diagnostic if it's part of the source manager we know + // about. This effectively drops diagnostics from modules we're building. + // FIXME: In the long run, ee don't want to drop source managers from modules. + if (!Info.hasSourceManager() || &Info.getSourceManager() == SourceMgr) + StoredDiags.push_back(StoredDiagnostic(Level, Info)); } ASTDeserializationListener *ASTUnit::getDeserializationListener() { @@ -662,8 +670,7 @@ void ASTUnit::ConfigureDiags(IntrusiveRefCntPtr &Diags, Client = new StoredDiagnosticConsumer(AST.StoredDiagnostics); Diags = CompilerInstance::createDiagnostics(new DiagnosticOptions(), Client, - /*ShouldOwnClient=*/true, - /*ShouldCloneClient=*/false); + /*ShouldOwnClient=*/true); } else if (CaptureDiagnostics) { Diags->setClient(new StoredDiagnosticConsumer(AST.StoredDiagnostics)); } @@ -835,6 +842,9 @@ ASTUnit *ASTUnit::LoadFromASTFile(const std::string &Filename, ReaderPtr->InitializeSema(*AST->TheSema); AST->Reader = ReaderPtr; + // Tell the diagnostic client that we have started a source file. + AST->getDiagnostics().getClient()->BeginSourceFile(Context.getLangOpts(),&PP); + return AST.take(); } @@ -1027,16 +1037,17 @@ class PrecompilePreambleAction : public ASTFrontendAction { } -static void checkAndRemoveNonDriverDiags(SmallVectorImpl & - StoredDiagnostics) { +static bool isNonDriverDiag(const StoredDiagnostic &StoredDiag) { + return StoredDiag.getLocation().isValid(); +} + +static void +checkAndRemoveNonDriverDiags(SmallVectorImpl &StoredDiags) { // Get rid of stored diagnostics except the ones from the driver which do not // have a source location. - for (unsigned I = 0; I < StoredDiagnostics.size(); ++I) { - if (StoredDiagnostics[I].getLocation().isValid()) { - StoredDiagnostics.erase(StoredDiagnostics.begin()+I); - --I; - } - } + StoredDiags.erase( + std::remove_if(StoredDiags.begin(), StoredDiags.end(), isNonDriverDiag), + StoredDiags.end()); } static void checkAndSanitizeDiags(SmallVectorImpl & diff --git a/lib/Frontend/ChainedIncludesSource.cpp b/lib/Frontend/ChainedIncludesSource.cpp index a17def0b37b..cde84caade5 100644 --- a/lib/Frontend/ChainedIncludesSource.cpp +++ b/lib/Frontend/ChainedIncludesSource.cpp @@ -143,6 +143,7 @@ ChainedIncludesSource *ChainedIncludesSource::create(CompilerInstance &CI) { Clang->getASTConsumer().GetASTDeserializationListener())); if (!Reader) return 0; + Clang->setModuleManager(static_cast(Reader.get())); Clang->getASTContext().setExternalSource(Reader); } diff --git a/lib/Frontend/CompilerInstance.cpp b/lib/Frontend/CompilerInstance.cpp index df06a816e84..cf856fc2ab6 100644 --- a/lib/Frontend/CompilerInstance.cpp +++ b/lib/Frontend/CompilerInstance.cpp @@ -155,18 +155,15 @@ static void SetupSerializedDiagnostics(DiagnosticOptions *DiagOpts, } void CompilerInstance::createDiagnostics(DiagnosticConsumer *Client, - bool ShouldOwnClient, - bool ShouldCloneClient) { + bool ShouldOwnClient) { Diagnostics = createDiagnostics(&getDiagnosticOpts(), Client, - ShouldOwnClient, ShouldCloneClient, - &getCodeGenOpts()); + ShouldOwnClient, &getCodeGenOpts()); } IntrusiveRefCntPtr CompilerInstance::createDiagnostics(DiagnosticOptions *Opts, DiagnosticConsumer *Client, bool ShouldOwnClient, - bool ShouldCloneClient, const CodeGenOptions *CodeGenOpts) { IntrusiveRefCntPtr DiagID(new DiagnosticIDs()); IntrusiveRefCntPtr @@ -175,10 +172,7 @@ CompilerInstance::createDiagnostics(DiagnosticOptions *Opts, // Create the diagnostic client for reporting errors or for // implementing -verify. if (Client) { - if (ShouldCloneClient) - Diags->setClient(Client->clone(*Diags), ShouldOwnClient); - else - Diags->setClient(Client, ShouldOwnClient); + Diags->setClient(Client, ShouldOwnClient); } else Diags->setClient(new TextDiagnosticPrinter(llvm::errs(), Opts)); @@ -868,9 +862,10 @@ static void compileModule(CompilerInstance &ImportingInstance, // module. CompilerInstance Instance; Instance.setInvocation(&*Invocation); - Instance.createDiagnostics(&ImportingInstance.getDiagnosticClient(), - /*ShouldOwnClient=*/true, - /*ShouldCloneClient=*/true); + + Instance.createDiagnostics(new ForwardingDiagnosticConsumer( + ImportingInstance.getDiagnosticClient()), + /*ShouldOwnClient=*/true); // Note that this module is part of the module build stack, so that we // can detect cycles in the module graph. @@ -892,6 +887,7 @@ static void compileModule(CompilerInstance &ImportingInstance, llvm::CrashRecoveryContext CRC; CompileModuleMapData Data = { Instance, CreateModuleAction }; CRC.RunSafelyOnThread(&doCompileMapModule, &Data, ThreadStackSize); + // Delete the temporary module map file. // FIXME: Even though we're executing under crash protection, it would still diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 41f941729a6..42ea96f0f2a 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -29,6 +29,7 @@ #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/Host.h" #include "llvm/Support/Path.h" +#include "llvm/Support/system_error.h" using namespace clang; //===----------------------------------------------------------------------===// @@ -68,6 +69,9 @@ static unsigned getOptimizationLevel(ArgList &Args, InputKind IK, if (A->getOption().matches(options::OPT_O0)) return 0; + if (A->getOption().matches(options::OPT_Ofast)) + return 3; + assert (A->getOption().matches(options::OPT_O)); StringRef S(A->getValue()); @@ -80,8 +84,7 @@ static unsigned getOptimizationLevel(ArgList &Args, InputKind IK, return DefaultOpt; } -static unsigned getOptimizationLevelSize(ArgList &Args, InputKind IK, - DiagnosticsEngine &Diags) { +static unsigned getOptimizationLevelSize(ArgList &Args) { if (Arg *A = Args.getLastArg(options::OPT_O_Group)) { if (A->getOption().matches(options::OPT_O)) { switch (A->getValue()[0]) { @@ -281,6 +284,7 @@ static bool ParseMigratorArgs(MigratorOptions &Opts, ArgList &Args) { static void ParseCommentArgs(CommentOptions &Opts, ArgList &Args) { Opts.BlockCommandNames = Args.getAllArgValues(OPT_fcomment_block_commands); + Opts.ParseAllComments = Args.hasArg(OPT_fparse_all_comments); } static bool ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, InputKind IK, @@ -317,7 +321,6 @@ static bool ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, InputKind IK, Opts.DebugColumnInfo = Args.hasArg(OPT_dwarf_column_info); Opts.SplitDwarfFile = Args.getLastArgValue(OPT_split_dwarf_file); - Opts.ModulesAutolink = Args.hasArg(OPT_fmodules_autolink); Opts.DisableLLVMOpts = Args.hasArg(OPT_disable_llvm_optzns); Opts.DisableRedZone = Args.hasArg(OPT_disable_red_zone); Opts.ForbidGuardVariables = Args.hasArg(OPT_fforbid_guard_variables); @@ -329,12 +332,13 @@ 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 = getOptimizationLevelSize(Args, IK, Diags); + Opts.OptimizeSize = getOptimizationLevelSize(Args); Opts.SimplifyLibCalls = !(Args.hasArg(OPT_fno_builtin) || Args.hasArg(OPT_ffreestanding)); Opts.UnrollLoops = Args.hasArg(OPT_funroll_loops) || (Opts.OptimizationLevel > 1 && !Opts.OptimizeSize); + Opts.Autolink = !Args.hasArg(OPT_fno_autolink); Opts.AsmVerbose = Args.hasArg(OPT_masm_verbose); Opts.ObjCAutoRefCountExceptions = Args.hasArg(OPT_fobjc_arc_exceptions); Opts.CUDAIsDevice = Args.hasArg(OPT_fcuda_is_device); @@ -564,7 +568,6 @@ bool clang::ParseDiagnosticArgs(DiagnosticOptions &Opts, ArgList &Args, Opts.VerifyDiagnostics = Args.hasArg(OPT_verify); Opts.ElideType = !Args.hasArg(OPT_fno_elide_type); Opts.ShowTemplateTree = Args.hasArg(OPT_fdiagnostics_show_template_tree); - Opts.WarnOnSpellCheck = Args.hasArg(OPT_fwarn_on_spellcheck); Opts.ErrorLimit = Args.getLastArgIntValue(OPT_ferror_limit, 0, Diags); Opts.MacroBacktraceLimit = Args.getLastArgIntValue(OPT_fmacro_backtrace_limit, @@ -1244,7 +1247,7 @@ 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, 512, + Opts.InstantiationDepth = Args.getLastArgIntValue(OPT_ftemplate_depth, 256, Diags); Opts.ConstexprCallDepth = Args.getLastArgIntValue(OPT_fconstexpr_depth, 512, Diags); @@ -1292,7 +1295,7 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK, // FIXME: Eliminate this dependency. unsigned Opt = getOptimizationLevel(Args, IK, Diags), - OptSize = getOptimizationLevelSize(Args, IK, Diags); + OptSize = getOptimizationLevelSize(Args); Opts.Optimize = Opt != 0; Opts.OptimizeSize = OptSize != 0; @@ -1684,5 +1687,21 @@ std::string CompilerInvocation::getModuleHash() const { hsOpts.UseStandardCXXIncludes, hsOpts.UseLibcxx); + // Darwin-specific hack: if we have a sysroot, use the contents of + // $sysroot/System/Library/CoreServices/SystemVersion.plist + // as part of the module hash. + if (!hsOpts.Sysroot.empty()) { + llvm::OwningPtr buffer; + SmallString<128> systemVersionFile; + systemVersionFile += hsOpts.Sysroot; + llvm::sys::path::append(systemVersionFile, "System"); + llvm::sys::path::append(systemVersionFile, "Library"); + llvm::sys::path::append(systemVersionFile, "CoreServices"); + llvm::sys::path::append(systemVersionFile, "SystemVersion.plist"); + if (!llvm::MemoryBuffer::getFile(systemVersionFile, buffer)) { + code = hash_combine(code, buffer.get()->getBuffer()); + } + } + return llvm::APInt(64, code).toString(36, /*Signed=*/false); } diff --git a/lib/Frontend/DiagnosticRenderer.cpp b/lib/Frontend/DiagnosticRenderer.cpp index 3b4f55c6c41..4eee59548c8 100644 --- a/lib/Frontend/DiagnosticRenderer.cpp +++ b/lib/Frontend/DiagnosticRenderer.cpp @@ -462,9 +462,8 @@ void DiagnosticRenderer::emitMacroExpansions(SourceLocation Loc, Message << "expanded from here"; else Message << "expanded from macro '" << MacroName << "'"; - emitDiagnostic(SpellingLoc, DiagnosticsEngine::Note, - Message.str(), - SpellingRanges, ArrayRef(), &SM); + emitDiagnostic(SpellingLoc, DiagnosticsEngine::Note, Message.str(), + SpellingRanges, None, &SM); } DiagnosticNoteRenderer::~DiagnosticNoteRenderer() {} diff --git a/lib/Frontend/FrontendAction.cpp b/lib/Frontend/FrontendAction.cpp index 6031ad2b361..ece51a35707 100644 --- a/lib/Frontend/FrontendAction.cpp +++ b/lib/Frontend/FrontendAction.cpp @@ -294,6 +294,8 @@ bool FrontendAction::BeginSourceFile(CompilerInstance &CI, source.reset(ChainedIncludesSource::create(CI)); if (!source) goto failure; + CI.setModuleManager(static_cast( + &static_cast(source.get())->getFinalReader())); CI.getASTContext().setExternalSource(source); } else if (!CI.getPreprocessorOpts().ImplicitPCHInclude.empty()) { diff --git a/lib/Frontend/InitHeaderSearch.cpp b/lib/Frontend/InitHeaderSearch.cpp index 35eec565f7a..f4ca4d498ae 100644 --- a/lib/Frontend/InitHeaderSearch.cpp +++ b/lib/Frontend/InitHeaderSearch.cpp @@ -24,6 +24,7 @@ #include "llvm/ADT/Triple.h" #include "llvm/ADT/Twine.h" #include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" #include "llvm/Support/raw_ostream.h" @@ -410,16 +411,16 @@ AddDefaultCPlusPlusIncludePaths(const llvm::Triple &triple, const HeaderSearchOp #endif break; case llvm::Triple::DragonFly: - AddPath("/usr/include/c++/4.1", CXXSystem, false); + if (llvm::sys::fs::exists("/usr/lib/gcc47")) + AddPath("/usr/include/c++/4.7", CXXSystem, false); + else + AddPath("/usr/include/c++/4.4", CXXSystem, false); break; case llvm::Triple::FreeBSD: // FreeBSD 8.0 // FreeBSD 7.3 AddGnuCPlusPlusIncludePaths("/usr/include/c++/4.2", "", "", "", triple); break; - case llvm::Triple::NetBSD: - AddGnuCPlusPlusIncludePaths("/usr/include/g++", "", "", "", triple); - break; case llvm::Triple::OpenBSD: { std::string t = triple.getTriple(); if (t.substr(0, 6) == "x86_64") diff --git a/lib/Frontend/InitPreprocessor.cpp b/lib/Frontend/InitPreprocessor.cpp index 25cfac63233..dc3ab53eda0 100644 --- a/lib/Frontend/InitPreprocessor.cpp +++ b/lib/Frontend/InitPreprocessor.cpp @@ -302,12 +302,13 @@ static void InitializeStandardPredefinedMacros(const TargetInfo &TI, else if (!LangOpts.GNUMode && LangOpts.Digraphs) Builder.defineMacro("__STDC_VERSION__", "199409L"); } else { - // FIXME: LangOpts.CPlusPlus1y - + // FIXME: Use the right value for __cplusplus for C++1y once one is chosen. + if (LangOpts.CPlusPlus1y) + Builder.defineMacro("__cplusplus", "201305L"); // C++11 [cpp.predefined]p1: // The name __cplusplus is defined to the value 201103L when compiling a // C++ translation unit. - if (LangOpts.CPlusPlus11) + else if (LangOpts.CPlusPlus11) Builder.defineMacro("__cplusplus", "201103L"); // C++03 [cpp.predefined]p1: // The name __cplusplus is defined to the value 199711L when compiling a diff --git a/lib/Frontend/LogDiagnosticPrinter.cpp b/lib/Frontend/LogDiagnosticPrinter.cpp index 0a22481cb63..2189b8658ed 100644 --- a/lib/Frontend/LogDiagnosticPrinter.cpp +++ b/lib/Frontend/LogDiagnosticPrinter.cpp @@ -171,8 +171,3 @@ void LogDiagnosticPrinter::HandleDiagnostic(DiagnosticsEngine::Level Level, Entries.push_back(DE); } -DiagnosticConsumer * -LogDiagnosticPrinter::clone(DiagnosticsEngine &Diags) const { - return new LogDiagnosticPrinter(OS, &*DiagOpts, /*OwnsOutputStream=*/false); -} - diff --git a/lib/Frontend/PrintPreprocessedOutput.cpp b/lib/Frontend/PrintPreprocessedOutput.cpp index f70bd7c93e5..9fd36494358 100644 --- a/lib/Frontend/PrintPreprocessedOutput.cpp +++ b/lib/Frontend/PrintPreprocessedOutput.cpp @@ -127,10 +127,22 @@ class PrintPPOutputPPCallbacks : public PPCallbacks { virtual void FileChanged(SourceLocation Loc, FileChangeReason Reason, SrcMgr::CharacteristicKind FileType, FileID PrevFID); + virtual void InclusionDirective(SourceLocation HashLoc, + const Token &IncludeTok, + StringRef FileName, + bool IsAngled, + CharSourceRange FilenameRange, + const FileEntry *File, + StringRef SearchPath, + StringRef RelativePath, + const Module *Imported); virtual void Ident(SourceLocation Loc, const std::string &str); + virtual void PragmaCaptured(SourceLocation Loc, StringRef Str); virtual void PragmaComment(SourceLocation Loc, const IdentifierInfo *Kind, const std::string &Str); - virtual void PragmaMessage(SourceLocation Loc, StringRef Str); + virtual void PragmaMessage(SourceLocation Loc, StringRef Namespace, + PragmaMessageKind Kind, StringRef Str); + virtual void PragmaDebug(SourceLocation Loc, StringRef DebugType); virtual void PragmaDiagnosticPush(SourceLocation Loc, StringRef Namespace); virtual void PragmaDiagnosticPop(SourceLocation Loc, @@ -258,11 +270,12 @@ void PrintPPOutputPPCallbacks::FileChanged(SourceLocation Loc, if (IncludeLoc.isValid()) MoveToLine(IncludeLoc); } else if (Reason == PPCallbacks::SystemHeaderPragma) { - MoveToLine(NewLine); - - // TODO GCC emits the # directive for this directive on the line AFTER the - // directive and emits a bunch of spaces that aren't needed. Emulate this - // strange behavior. + // GCC emits the # directive for this directive on the line AFTER the + // directive and emits a bunch of spaces that aren't needed. This is because + // otherwise we will emit a line marker for THIS line, which requires an + // extra blank line after the directive to avoid making all following lines + // off by one. We can do better by simply incrementing NewLine here. + NewLine += 1; } CurLine = NewLine; @@ -305,6 +318,27 @@ void PrintPPOutputPPCallbacks::FileChanged(SourceLocation Loc, } } +void PrintPPOutputPPCallbacks::InclusionDirective(SourceLocation HashLoc, + const Token &IncludeTok, + StringRef FileName, + bool IsAngled, + CharSourceRange FilenameRange, + const FileEntry *File, + StringRef SearchPath, + StringRef RelativePath, + const Module *Imported) { + // When preprocessing, turn implicit imports into @imports. + // FIXME: This is a stop-gap until a more comprehensive "preprocessing with + // modules" solution is introduced. + if (Imported) { + startNewLineIfNeeded(); + MoveToLine(HashLoc); + OS << "@import " << Imported->getFullModuleName() << ";" + << " /* clang -E: implicit import for \"" << File->getName() << "\" */"; + EmittedTokensOnThisLine = true; + } +} + /// Ident - Handle #ident directives when read by the preprocessor. /// void PrintPPOutputPPCallbacks::Ident(SourceLocation Loc, const std::string &S) { @@ -315,6 +349,15 @@ void PrintPPOutputPPCallbacks::Ident(SourceLocation Loc, const std::string &S) { EmittedTokensOnThisLine = true; } +void PrintPPOutputPPCallbacks::PragmaCaptured(SourceLocation Loc, + StringRef Str) { + startNewLineIfNeeded(); + MoveToLine(Loc); + OS << "#pragma captured"; + + setEmittedDirectiveOnThisLine(); +} + /// MacroDefined - This hook is called whenever a macro definition is seen. void PrintPPOutputPPCallbacks::MacroDefined(const Token &MacroNameTok, const MacroDirective *MD) { @@ -367,12 +410,25 @@ void PrintPPOutputPPCallbacks::PragmaComment(SourceLocation Loc, } void PrintPPOutputPPCallbacks::PragmaMessage(SourceLocation Loc, + StringRef Namespace, + PragmaMessageKind Kind, StringRef Str) { startNewLineIfNeeded(); MoveToLine(Loc); - OS << "#pragma message("; - - OS << '"'; + OS << "#pragma "; + if (!Namespace.empty()) + OS << Namespace << ' '; + switch (Kind) { + case PMK_Message: + OS << "message(\""; + break; + case PMK_Warning: + OS << "warning \""; + break; + case PMK_Error: + OS << "error \""; + break; + } for (unsigned i = 0, e = Str.size(); i != e; ++i) { unsigned char Char = Str[i]; @@ -385,8 +441,19 @@ void PrintPPOutputPPCallbacks::PragmaMessage(SourceLocation Loc, << (char)('0'+ ((Char >> 0) & 7)); } OS << '"'; + if (Kind == PMK_Message) + OS << ')'; + setEmittedDirectiveOnThisLine(); +} + +void PrintPPOutputPPCallbacks::PragmaDebug(SourceLocation Loc, + StringRef DebugType) { + startNewLineIfNeeded(); + MoveToLine(Loc); + + OS << "#pragma clang __debug "; + OS << DebugType; - OS << ')'; setEmittedDirectiveOnThisLine(); } diff --git a/lib/Frontend/SerializedDiagnosticPrinter.cpp b/lib/Frontend/SerializedDiagnosticPrinter.cpp index 4bb662bb265..6514321f228 100644 --- a/lib/Frontend/SerializedDiagnosticPrinter.cpp +++ b/lib/Frontend/SerializedDiagnosticPrinter.cpp @@ -114,10 +114,6 @@ class SDiagsWriter : public DiagnosticConsumer { virtual void finish(); - DiagnosticConsumer *clone(DiagnosticsEngine &Diags) const { - return new SDiagsWriter(State); - } - private: /// \brief Emit the preamble for the serialized diagnostics. void EmitPreamble(); diff --git a/lib/Frontend/TextDiagnostic.cpp b/lib/Frontend/TextDiagnostic.cpp index ca4ad60c524..1572d0f1d07 100644 --- a/lib/Frontend/TextDiagnostic.cpp +++ b/lib/Frontend/TextDiagnostic.cpp @@ -1095,7 +1095,7 @@ void TextDiagnostic::emitSnippetAndCaret( unsigned ColNo = SM.getColumnNumber(FID, FileOffset); // Arbitrarily stop showing snippets when the line is too long. - static const ptrdiff_t MaxLineLengthToPrint = 4096; + static const size_t MaxLineLengthToPrint = 4096; if (ColNo > MaxLineLengthToPrint) return; @@ -1110,7 +1110,7 @@ void TextDiagnostic::emitSnippetAndCaret( ++LineEnd; // Arbitrarily stop showing snippets when the line is too long. - if (LineEnd - LineStart > MaxLineLengthToPrint) + if (size_t(LineEnd - LineStart) > MaxLineLengthToPrint) return; // Copy the line of code into an std::string for ease of manipulation. diff --git a/lib/Frontend/TextDiagnosticBuffer.cpp b/lib/Frontend/TextDiagnosticBuffer.cpp index 039475a2e04..5821436a306 100644 --- a/lib/Frontend/TextDiagnosticBuffer.cpp +++ b/lib/Frontend/TextDiagnosticBuffer.cpp @@ -75,6 +75,3 @@ void TextDiagnosticBuffer::FlushDiagnostics(DiagnosticsEngine &Diags) const { escapeDiag(it->second, Buf))); } -DiagnosticConsumer *TextDiagnosticBuffer::clone(DiagnosticsEngine &) const { - return new TextDiagnosticBuffer(); -} diff --git a/lib/Frontend/TextDiagnosticPrinter.cpp b/lib/Frontend/TextDiagnosticPrinter.cpp index 010f649e6b7..c22798af60a 100644 --- a/lib/Frontend/TextDiagnosticPrinter.cpp +++ b/lib/Frontend/TextDiagnosticPrinter.cpp @@ -155,8 +155,3 @@ void TextDiagnosticPrinter::HandleDiagnostic(DiagnosticsEngine::Level Level, OS.flush(); } - -DiagnosticConsumer * -TextDiagnosticPrinter::clone(DiagnosticsEngine &Diags) const { - return new TextDiagnosticPrinter(OS, &*DiagOpts, /*OwnsOutputStream=*/false); -} diff --git a/lib/Frontend/VerifyDiagnosticConsumer.cpp b/lib/Frontend/VerifyDiagnosticConsumer.cpp index 82f6e916e58..46745b6b9a4 100644 --- a/lib/Frontend/VerifyDiagnosticConsumer.cpp +++ b/lib/Frontend/VerifyDiagnosticConsumer.cpp @@ -111,8 +111,13 @@ void VerifyDiagnosticConsumer::EndSourceFile() { void VerifyDiagnosticConsumer::HandleDiagnostic( DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info) { - if (Info.hasSourceManager()) + if (Info.hasSourceManager()) { + // If this diagnostic is for a different source manager, ignore it. + if (SrcManager && &Info.getSourceManager() != SrcManager) + return; + setSourceManager(Info.getSourceManager()); + } #ifndef NDEBUG // Debug build tracks unparsed files for possible @@ -278,8 +283,10 @@ class ParseHelper /// /// Returns true if any valid directives were found. static bool ParseDirective(StringRef S, ExpectedData *ED, SourceManager &SM, - SourceLocation Pos, DiagnosticsEngine &Diags, + Preprocessor *PP, SourceLocation Pos, VerifyDiagnosticConsumer::DirectiveStatus &Status) { + DiagnosticsEngine &Diags = PP ? PP->getDiagnostics() : SM.getDiagnostics(); + // A single comment may contain multiple directives. bool FoundDirective = false; for (ParseHelper PH(S); !PH.Done();) { @@ -353,10 +360,30 @@ static bool ParseDirective(StringRef S, ExpectedData *ED, SourceManager &SM, else ExpectedLine -= Line; ExpectedLoc = SM.translateLineCol(SM.getFileID(Pos), ExpectedLine, 1); } - } else { + } else if (PH.Next(Line)) { // Absolute line number. - if (PH.Next(Line) && Line > 0) + if (Line > 0) ExpectedLoc = SM.translateLineCol(SM.getFileID(Pos), Line, 1); + } else if (PP && PH.Search(":")) { + // Specific source file. + StringRef Filename(PH.C, PH.P-PH.C); + PH.Advance(); + + // Lookup file via Preprocessor, like a #include. + const DirectoryLookup *CurDir; + const FileEntry *FE = PP->LookupFile(Filename, false, NULL, CurDir, + NULL, NULL, 0); + if (!FE) { + Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin), + diag::err_verify_missing_file) << Filename << KindStr; + continue; + } + + if (SM.translateFile(FE).isInvalid()) + SM.createFileID(FE, Pos, SrcMgr::C_User); + + if (PH.Next(Line) && Line > 0) + ExpectedLoc = SM.translateFileLineCol(FE, Line, 1); } if (ExpectedLoc.isInvalid()) { @@ -454,6 +481,11 @@ static bool ParseDirective(StringRef S, ExpectedData *ED, SourceManager &SM, bool VerifyDiagnosticConsumer::HandleComment(Preprocessor &PP, SourceRange Comment) { SourceManager &SM = PP.getSourceManager(); + + // If this comment is for a different source manager, ignore it. + if (SrcManager && &SM != SrcManager) + return false; + SourceLocation CommentBegin = Comment.getBegin(); const char *CommentRaw = SM.getCharacterData(CommentBegin); @@ -465,7 +497,7 @@ bool VerifyDiagnosticConsumer::HandleComment(Preprocessor &PP, // Fold any "\" sequences size_t loc = C.find('\\'); if (loc == StringRef::npos) { - ParseDirective(C, &ED, SM, CommentBegin, PP.getDiagnostics(), Status); + ParseDirective(C, &ED, SM, &PP, CommentBegin, Status); return false; } @@ -495,7 +527,7 @@ bool VerifyDiagnosticConsumer::HandleComment(Preprocessor &PP, } if (!C2.empty()) - ParseDirective(C2, &ED, SM, CommentBegin, PP.getDiagnostics(), Status); + ParseDirective(C2, &ED, SM, &PP, CommentBegin, Status); return false; } @@ -530,8 +562,7 @@ static bool findDirectives(SourceManager &SM, FileID FID, if (Comment.empty()) continue; // Find first directive. - if (ParseDirective(Comment, 0, SM, Tok.getLocation(), - SM.getDiagnostics(), Status)) + if (ParseDirective(Comment, 0, SM, 0, Tok.getLocation(), Status)) return true; } return false; @@ -551,8 +582,13 @@ static unsigned PrintUnexpected(DiagnosticsEngine &Diags, SourceManager *SourceM for (const_diag_iterator I = diag_begin, E = diag_end; I != E; ++I) { if (I->first.isInvalid() || !SourceMgr) OS << "\n (frontend)"; - else - OS << "\n Line " << SourceMgr->getPresumedLineNumber(I->first); + else { + OS << "\n "; + if (const FileEntry *File = SourceMgr->getFileEntryForID( + SourceMgr->getFileID(I->first))) + OS << " File " << File->getName(); + OS << " Line " << SourceMgr->getPresumedLineNumber(I->first); + } OS << ": " << I->second; } @@ -572,11 +608,12 @@ static unsigned PrintExpected(DiagnosticsEngine &Diags, SourceManager &SourceMgr llvm::raw_svector_ostream OS(Fmt); for (DirectiveList::iterator I = DL.begin(), E = DL.end(); I != E; ++I) { Directive &D = **I; - OS << "\n Line " << SourceMgr.getPresumedLineNumber(D.DiagnosticLoc); + OS << "\n File " << SourceMgr.getFilename(D.DiagnosticLoc) + << " Line " << SourceMgr.getPresumedLineNumber(D.DiagnosticLoc); if (D.DirectiveLoc != D.DiagnosticLoc) OS << " (directive at " - << SourceMgr.getFilename(D.DirectiveLoc) << ":" - << SourceMgr.getPresumedLineNumber(D.DirectiveLoc) << ")"; + << SourceMgr.getFilename(D.DirectiveLoc) << ':' + << SourceMgr.getPresumedLineNumber(D.DirectiveLoc) << ')'; OS << ": " << D.Text; } @@ -585,6 +622,22 @@ static unsigned PrintExpected(DiagnosticsEngine &Diags, SourceManager &SourceMgr return DL.size(); } +/// \brief Determine whether two source locations come from the same file. +static bool IsFromSameFile(SourceManager &SM, SourceLocation DirectiveLoc, + SourceLocation DiagnosticLoc) { + while (DiagnosticLoc.isMacroID()) + DiagnosticLoc = SM.getImmediateMacroCallerLoc(DiagnosticLoc); + + if (SM.isFromSameFile(DirectiveLoc, DiagnosticLoc)) + return true; + + const FileEntry *DiagFile = SM.getFileEntryForID(SM.getFileID(DiagnosticLoc)); + if (!DiagFile && SM.isFromMainFile(DirectiveLoc)) + return true; + + return (DiagFile == SM.getFileEntryForID(SM.getFileID(DirectiveLoc))); +} + /// CheckLists - Compare expected to seen diagnostic lists and return the /// the difference between them. /// @@ -607,6 +660,9 @@ static unsigned CheckLists(DiagnosticsEngine &Diags, SourceManager &SourceMgr, if (LineNo1 != LineNo2) continue; + if (!IsFromSameFile(SourceMgr, D.DiagnosticLoc, II->first)) + continue; + const std::string &RightText = II->second; if (D.match(RightText)) break; @@ -764,14 +820,6 @@ void VerifyDiagnosticConsumer::CheckDiagnostics() { ED.Notes.clear(); } -DiagnosticConsumer * -VerifyDiagnosticConsumer::clone(DiagnosticsEngine &Diags) const { - if (!Diags.getClient()) - Diags.setClient(PrimaryClient->clone(Diags)); - - return new VerifyDiagnosticConsumer(Diags); -} - Directive *Directive::create(bool RegexKind, SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc, StringRef Text, unsigned Min, unsigned Max) { diff --git a/lib/Frontend/Warnings.cpp b/lib/Frontend/Warnings.cpp index b7547b9998e..767096a1c99 100644 --- a/lib/Frontend/Warnings.cpp +++ b/lib/Frontend/Warnings.cpp @@ -56,7 +56,6 @@ void clang::ProcessWarningOptions(DiagnosticsEngine &Diags, Diags.setElideType(Opts.ElideType); Diags.setPrintTemplateTree(Opts.ShowTemplateTree); - Diags.setWarnOnSpellCheck(Opts.WarnOnSpellCheck); Diags.setShowColors(Opts.ShowColors); // Handle -ferror-limit diff --git a/lib/Headers/CMakeLists.txt b/lib/Headers/CMakeLists.txt index 5e727a7b2e2..8be33b7b4a1 100644 --- a/lib/Headers/CMakeLists.txt +++ b/lib/Headers/CMakeLists.txt @@ -98,6 +98,7 @@ set_target_properties(clang-headers PROPERTIES FOLDER "Misc") if (other_output_dir) if(UNIX) add_custom_command(TARGET clang-headers POST_BUILD + COMMAND ${CMAKE_COMMAND} -E make_directory "${LLVM_BINARY_DIR}/lib/${CMAKE_CFG_INTDIR}" COMMAND ${CMAKE_COMMAND} -E create_symlink "${LLVM_BINARY_DIR}/bin/lib/clang" "${LLVM_BINARY_DIR}/lib/${CMAKE_CFG_INTDIR}/clang") endif() endif () diff --git a/lib/Headers/avxintrin.h b/lib/Headers/avxintrin.h index 412d284f002..0683a65facc 100644 --- a/lib/Headers/avxintrin.h +++ b/lib/Headers/avxintrin.h @@ -1078,78 +1078,78 @@ _mm256_setzero_si256(void) /* Cast between vector types */ static __inline __m256 __attribute__((__always_inline__, __nodebug__)) -_mm256_castpd_ps(__m256d __in) +_mm256_castpd_ps(__m256d __a) { - return (__m256)__in; + return (__m256)__a; } static __inline __m256i __attribute__((__always_inline__, __nodebug__)) -_mm256_castpd_si256(__m256d __in) +_mm256_castpd_si256(__m256d __a) { - return (__m256i)__in; + return (__m256i)__a; } static __inline __m256d __attribute__((__always_inline__, __nodebug__)) -_mm256_castps_pd(__m256 __in) +_mm256_castps_pd(__m256 __a) { - return (__m256d)__in; + return (__m256d)__a; } static __inline __m256i __attribute__((__always_inline__, __nodebug__)) -_mm256_castps_si256(__m256 __in) +_mm256_castps_si256(__m256 __a) { - return (__m256i)__in; + return (__m256i)__a; } static __inline __m256 __attribute__((__always_inline__, __nodebug__)) -_mm256_castsi256_ps(__m256i __in) +_mm256_castsi256_ps(__m256i __a) { - return (__m256)__in; + return (__m256)__a; } static __inline __m256d __attribute__((__always_inline__, __nodebug__)) -_mm256_castsi256_pd(__m256i __in) +_mm256_castsi256_pd(__m256i __a) { - return (__m256d)__in; + return (__m256d)__a; } static __inline __m128d __attribute__((__always_inline__, __nodebug__)) -_mm256_castpd256_pd128(__m256d __in) +_mm256_castpd256_pd128(__m256d __a) { - return __builtin_shufflevector(__in, __in, 0, 1); + return __builtin_shufflevector(__a, __a, 0, 1); } static __inline __m128 __attribute__((__always_inline__, __nodebug__)) -_mm256_castps256_ps128(__m256 __in) +_mm256_castps256_ps128(__m256 __a) { - return __builtin_shufflevector(__in, __in, 0, 1, 2, 3); + return __builtin_shufflevector(__a, __a, 0, 1, 2, 3); } static __inline __m128i __attribute__((__always_inline__, __nodebug__)) -_mm256_castsi256_si128(__m256i __in) +_mm256_castsi256_si128(__m256i __a) { - return __builtin_shufflevector(__in, __in, 0, 1); + return __builtin_shufflevector(__a, __a, 0, 1); } static __inline __m256d __attribute__((__always_inline__, __nodebug__)) -_mm256_castpd128_pd256(__m128d __in) +_mm256_castpd128_pd256(__m128d __a) { __m128d __zero = _mm_setzero_pd(); - return __builtin_shufflevector(__in, __zero, 0, 1, 2, 2); + return __builtin_shufflevector(__a, __zero, 0, 1, 2, 2); } static __inline __m256 __attribute__((__always_inline__, __nodebug__)) -_mm256_castps128_ps256(__m128 __in) +_mm256_castps128_ps256(__m128 __a) { __m128 __zero = _mm_setzero_ps(); - return __builtin_shufflevector(__in, __zero, 0, 1, 2, 3, 4, 4, 4, 4); + return __builtin_shufflevector(__a, __zero, 0, 1, 2, 3, 4, 4, 4, 4); } static __inline __m256i __attribute__((__always_inline__, __nodebug__)) -_mm256_castsi128_si256(__m128i __in) +_mm256_castsi128_si256(__m128i __a) { __m128i __zero = _mm_setzero_si128(); - return __builtin_shufflevector(__in, __zero, 0, 1, 2, 2); + return __builtin_shufflevector(__a, __zero, 0, 1, 2, 2); } /* SIMD load ops (unaligned) */ diff --git a/lib/Headers/emmintrin.h b/lib/Headers/emmintrin.h index e18fae40ece..56c6c228554 100644 --- a/lib/Headers/emmintrin.h +++ b/lib/Headers/emmintrin.h @@ -1379,39 +1379,39 @@ _mm_movemask_pd(__m128d __a) __builtin_shufflevector(__a, __b, (i) & 1, (((i) & 2) >> 1) + 2); }) static __inline__ __m128 __attribute__((__always_inline__, __nodebug__)) -_mm_castpd_ps(__m128d __in) +_mm_castpd_ps(__m128d __a) { - return (__m128)__in; + return (__m128)__a; } static __inline__ __m128i __attribute__((__always_inline__, __nodebug__)) -_mm_castpd_si128(__m128d __in) +_mm_castpd_si128(__m128d __a) { - return (__m128i)__in; + return (__m128i)__a; } static __inline__ __m128d __attribute__((__always_inline__, __nodebug__)) -_mm_castps_pd(__m128 __in) +_mm_castps_pd(__m128 __a) { - return (__m128d)__in; + return (__m128d)__a; } static __inline__ __m128i __attribute__((__always_inline__, __nodebug__)) -_mm_castps_si128(__m128 __in) +_mm_castps_si128(__m128 __a) { - return (__m128i)__in; + return (__m128i)__a; } static __inline__ __m128 __attribute__((__always_inline__, __nodebug__)) -_mm_castsi128_ps(__m128i __in) +_mm_castsi128_ps(__m128i __a) { - return (__m128)__in; + return (__m128)__a; } static __inline__ __m128d __attribute__((__always_inline__, __nodebug__)) -_mm_castsi128_pd(__m128i __in) +_mm_castsi128_pd(__m128i __a) { - return (__m128d)__in; + return (__m128d)__a; } static __inline__ void __attribute__((__always_inline__, __nodebug__)) diff --git a/lib/Headers/stddef.h b/lib/Headers/stddef.h index 52962248f67..6a64d6d32a8 100644 --- a/lib/Headers/stddef.h +++ b/lib/Headers/stddef.h @@ -42,11 +42,25 @@ typedef __PTRDIFF_TYPE__ ptrdiff_t; typedef __SIZE_TYPE__ size_t; #endif +/* ISO9899:2011 7.20 (C11 Annex K): Define rsize_t if __STDC_WANT_LIB_EXT1__ is + * enabled. */ +#if (defined(__STDC_WANT_LIB_EXT1__) && __STDC_WANT_LIB_EXT1__ >= 1 && \ + !defined(_RSIZE_T)) || __has_feature(modules) +/* Always define rsize_t when modules are available. */ +#if !__has_feature(modules) +#define _RSIZE_T +#endif +typedef __SIZE_TYPE__ rsize_t; +#endif + #ifndef __cplusplus /* Always define wchar_t when modules are available. */ #if !defined(_WCHAR_T) || __has_feature(modules) #if !__has_feature(modules) #define _WCHAR_T +#if defined(_MSC_EXTENSIONS) +#define _WCHAR_T_DEFINED +#endif #endif typedef __WCHAR_TYPE__ wchar_t; #endif diff --git a/lib/Headers/stdint.h b/lib/Headers/stdint.h index 6f1a8761e1c..11529c0c674 100644 --- a/lib/Headers/stdint.h +++ b/lib/Headers/stdint.h @@ -30,7 +30,48 @@ */ #if __STDC_HOSTED__ && \ defined(__has_include_next) && __has_include_next() + +// C99 7.18.3 Limits of other integer types +// +// Footnote 219, 220: C++ implementations should define these macros only when +// __STDC_LIMIT_MACROS is defined before is included. +// +// Footnote 222: C++ implementations should define these macros only when +// __STDC_CONSTANT_MACROS is defined before is included. +// +// C++11 [cstdint.syn]p2: +// +// The macros defined by are provided unconditionally. In particular, +// the symbols __STDC_LIMIT_MACROS and __STDC_CONSTANT_MACROS (mentioned in +// footnotes 219, 220, and 222 in the C standard) play no role in C++. +// +// C11 removed the problematic footnotes. +// +// Work around this inconsistency by always defining those macros in C++ mode, +// so that a C library implementation which follows the C99 standard can be +// used in C++. +# ifdef __cplusplus +# if !defined(__STDC_LIMIT_MACROS) +# define __STDC_LIMIT_MACROS +# define __STDC_LIMIT_MACROS_DEFINED_BY_CLANG +# endif +# if !defined(__STDC_CONSTANT_MACROS) +# define __STDC_CONSTANT_MACROS +# define __STDC_CONSTANT_MACROS_DEFINED_BY_CLANG +# endif +# endif + # include_next + +# ifdef __STDC_LIMIT_MACROS_DEFINED_BY_CLANG +# undef __STDC_LIMIT_MACROS +# undef __STDC_LIMIT_MACROS_DEFINED_BY_CLANG +# endif +# ifdef __STDC_CONSTANT_MACROS_DEFINED_BY_CLANG +# undef __STDC_CONSTANT_MACROS +# undef __STDC_CONSTANT_MACROS_DEFINED_BY_CLANG +# endif + #else /* C99 7.18.1.1 Exact-width integer types. @@ -626,6 +667,12 @@ typedef __UINTMAX_TYPE__ uintmax_t; #define PTRDIFF_MAX __INTN_MAX(__PTRDIFF_WIDTH__) #define SIZE_MAX __UINTN_MAX(__SIZE_WIDTH__) +/* ISO9899:2011 7.20 (C11 Annex K): Define RSIZE_MAX if __STDC_WANT_LIB_EXT1__ + * is enabled. */ +#if defined(__STDC_WANT_LIB_EXT1__) && __STDC_WANT_LIB_EXT1__ >= 1 +#define RSIZE_MAX (SIZE_MAX >> 1) +#endif + /* C99 7.18.2.5 Limits of greatest-width integer types. */ #define INTMAX_MIN __INTN_MIN(__INTMAX_WIDTH__) #define INTMAX_MAX __INTN_MAX(__INTMAX_WIDTH__) diff --git a/lib/Headers/xopintrin.h b/lib/Headers/xopintrin.h index d107be4a426..9a5824c9710 100644 --- a/lib/Headers/xopintrin.h +++ b/lib/Headers/xopintrin.h @@ -1,4 +1,4 @@ -/*===---- xopintrin.h - FMA4 intrinsics ------------------------------------=== +/*===---- xopintrin.h - XOP 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 @@ -22,7 +22,7 @@ */ #ifndef __X86INTRIN_H -#error "Never use directly; include instead." +#error "Never use directly; include instead." #endif #ifndef __XOPINTRIN_H diff --git a/lib/Lex/Lexer.cpp b/lib/Lex/Lexer.cpp index ed4666aa211..9958287ba47 100644 --- a/lib/Lex/Lexer.cpp +++ b/lib/Lex/Lexer.cpp @@ -557,6 +557,7 @@ Lexer::ComputePreamble(const llvm::MemoryBuffer *Buffer, SourceLocation FileLoc = SourceLocation::getFromRawEncoding(StartOffset); Lexer TheLexer(FileLoc, LangOpts, Buffer->getBufferStart(), Buffer->getBufferStart(), Buffer->getBufferEnd()); + TheLexer.SetCommentRetentionState(true); // StartLoc will differ from FileLoc if there is a BOM that was skipped. SourceLocation StartLoc = TheLexer.getSourceLocation(); @@ -565,6 +566,7 @@ Lexer::ComputePreamble(const llvm::MemoryBuffer *Buffer, Token TheTok; Token IfStartTok; unsigned IfCount = 0; + SourceLocation ActiveCommentLoc; unsigned MaxLineOffset = 0; if (MaxLines) { @@ -612,13 +614,17 @@ Lexer::ComputePreamble(const llvm::MemoryBuffer *Buffer, } // Comments are okay; skip over them. - if (TheTok.getKind() == tok::comment) + if (TheTok.getKind() == tok::comment) { + if (ActiveCommentLoc.isInvalid()) + ActiveCommentLoc = TheTok.getLocation(); continue; + } if (TheTok.isAtStartOfLine() && TheTok.getKind() == tok::hash) { // This is the start of a preprocessor directive. Token HashTok = TheTok; InPreprocessorDirective = true; + ActiveCommentLoc = SourceLocation(); // Figure out which directive this is. Since we're lexing raw tokens, // we don't have an identifier table available. Instead, just look at @@ -689,7 +695,14 @@ Lexer::ComputePreamble(const llvm::MemoryBuffer *Buffer, break; } while (true); - SourceLocation End = IfCount? IfStartTok.getLocation() : TheTok.getLocation(); + SourceLocation End; + if (IfCount) + End = IfStartTok.getLocation(); + else if (ActiveCommentLoc.isValid()) + End = ActiveCommentLoc; // don't truncate a decl comment. + else + End = TheTok.getLocation(); + return std::make_pair(End.getRawEncoding() - StartLoc.getRawEncoding(), IfCount? IfStartTok.isAtStartOfLine() : TheTok.isAtStartOfLine()); diff --git a/lib/Lex/LiteralSupport.cpp b/lib/Lex/LiteralSupport.cpp index 91da8223c18..09f4a682f05 100644 --- a/lib/Lex/LiteralSupport.cpp +++ b/lib/Lex/LiteralSupport.cpp @@ -686,8 +686,13 @@ void NumericLiteralParser::ParseNumberStartingWithZero(SourceLocation TokLoc) { // Handle simple binary numbers 0b01010 if (*s == 'b' || *s == 'B') { - // 0b101010 is a GCC extension. - PP.Diag(TokLoc, diag::ext_binary_literal); + // 0b101010 is a C++1y / GCC extension. + PP.Diag(TokLoc, + PP.getLangOpts().CPlusPlus1y + ? diag::warn_cxx11_compat_binary_literal + : PP.getLangOpts().CPlusPlus + ? diag::ext_binary_literal_cxx1y + : diag::ext_binary_literal); ++s; radix = 2; DigitsBegin = s; diff --git a/lib/Lex/MacroArgs.cpp b/lib/Lex/MacroArgs.cpp index f6e781a936d..d2dc04b36cf 100644 --- a/lib/Lex/MacroArgs.cpp +++ b/lib/Lex/MacroArgs.cpp @@ -11,7 +11,7 @@ // //===----------------------------------------------------------------------===// -#include "MacroArgs.h" +#include "clang/Lex/MacroArgs.h" #include "clang/Lex/LexDiagnostic.h" #include "clang/Lex/MacroInfo.h" #include "clang/Lex/Preprocessor.h" diff --git a/lib/Lex/ModuleMap.cpp b/lib/Lex/ModuleMap.cpp index 0c03201aa6d..3e7a44c0e35 100644 --- a/lib/Lex/ModuleMap.cpp +++ b/lib/Lex/ModuleMap.cpp @@ -83,7 +83,7 @@ Module *ModuleMap::resolveModuleId(const ModuleId &Id, Module *Mod, return Context; } -ModuleMap::ModuleMap(FileManager &FileMgr, const DiagnosticConsumer &DC, +ModuleMap::ModuleMap(FileManager &FileMgr, DiagnosticConsumer &DC, const LangOptions &LangOpts, const TargetInfo *Target, HeaderSearch &HeaderInfo) : LangOpts(LangOpts), Target(Target), HeaderInfo(HeaderInfo), @@ -92,7 +92,8 @@ ModuleMap::ModuleMap(FileManager &FileMgr, const DiagnosticConsumer &DC, IntrusiveRefCntPtr DiagIDs(new DiagnosticIDs); Diags = IntrusiveRefCntPtr( new DiagnosticsEngine(DiagIDs, new DiagnosticOptions)); - Diags->setClient(DC.clone(*Diags), /*ShouldOwnClient=*/true); + Diags->setClient(new ForwardingDiagnosticConsumer(DC), + /*ShouldOwnClient=*/true); SourceMgr = new SourceManager(*Diags, FileMgr); } @@ -149,6 +150,24 @@ static StringRef sanitizeFilenameAsIdentifier(StringRef Name, return Name; } +/// \brief Determine whether the given file name is the name of a builtin +/// header, supplied by Clang to replace, override, or augment existing system +/// headers. +static bool isBuiltinHeader(StringRef FileName) { + return llvm::StringSwitch(FileName) + .Case("float.h", true) + .Case("iso646.h", true) + .Case("limits.h", true) + .Case("stdalign.h", true) + .Case("stdarg.h", true) + .Case("stdbool.h", true) + .Case("stddef.h", true) + .Case("stdint.h", true) + .Case("tgmath.h", true) + .Case("unwind.h", true) + .Default(false); +} + Module *ModuleMap::findModuleForHeader(const FileEntry *File) { HeadersMap::iterator Known = Headers.find(File); if (Known != Headers.end()) { @@ -158,6 +177,25 @@ Module *ModuleMap::findModuleForHeader(const FileEntry *File) { return Known->second.getModule(); } + + // If we've found a builtin header within Clang's builtin include directory, + // load all of the module maps to see if it will get associated with a + // specific module (e.g., in /usr/include). + if (File->getDir() == BuiltinIncludeDir && + isBuiltinHeader(llvm::sys::path::filename(File->getName()))) { + SmallVector AllModules; + HeaderInfo.collectAllModules(AllModules); + + // Check again. + Known = Headers.find(File); + if (Known != Headers.end()) { + // If a header is not available, don't report that it maps to anything. + if (!Known->second.isAvailable()) + return 0; + + return Known->second.getModule(); + } + } const DirectoryEntry *Dir = File->getDir(); SmallVector SkippedDirs; @@ -1266,24 +1304,6 @@ static void appendSubframeworkPaths(Module *Mod, } } -/// \brief Determine whether the given file name is the name of a builtin -/// header, supplied by Clang to replace, override, or augment existing system -/// headers. -static bool isBuiltinHeader(StringRef FileName) { - return llvm::StringSwitch(FileName) - .Case("float.h", true) - .Case("iso646.h", true) - .Case("limits.h", true) - .Case("stdalign.h", true) - .Case("stdarg.h", true) - .Case("stdbool.h", true) - .Case("stddef.h", true) - .Case("stdint.h", true) - .Case("tgmath.h", true) - .Case("unwind.h", true) - .Default(false); -} - /// \brief Parse a header declaration. /// /// header-declaration: diff --git a/lib/Lex/PPDirectives.cpp b/lib/Lex/PPDirectives.cpp index 07c18670100..50a0cb55f73 100644 --- a/lib/Lex/PPDirectives.cpp +++ b/lib/Lex/PPDirectives.cpp @@ -61,9 +61,12 @@ MacroInfo *Preprocessor::AllocateDeserializedMacroInfo(SourceLocation L, unsigned SubModuleID) { LLVM_STATIC_ASSERT(llvm::AlignOf::Alignment >= sizeof(SubModuleID), "alignment for MacroInfo is less than the ID"); - MacroInfo *MI = - (MacroInfo*)BP.Allocate(sizeof(MacroInfo) + sizeof(SubModuleID), - llvm::AlignOf::Alignment); + DeserializedMacroInfoChain *MIChain = + BP.Allocate(); + MIChain->Next = DeserialMIChainHead; + DeserialMIChainHead = MIChain; + + MacroInfo *MI = &MIChain->MI; new (MI) MacroInfo(L); MI->FromASTFile = true; MI->setOwningModuleID(SubModuleID); @@ -793,7 +796,8 @@ void Preprocessor::HandleDirective(Token &Result) { /// GetLineValue - Convert a numeric token into an unsigned value, emitting /// Diagnostic DiagID if it is invalid, and returning the value in Val. static bool GetLineValue(Token &DigitTok, unsigned &Val, - unsigned DiagID, Preprocessor &PP) { + unsigned DiagID, Preprocessor &PP, + bool IsGNULineDirective=false) { if (DigitTok.isNot(tok::numeric_constant)) { PP.Diag(DigitTok, DiagID); @@ -817,7 +821,7 @@ static bool GetLineValue(Token &DigitTok, unsigned &Val, for (unsigned i = 0; i != ActualLength; ++i) { if (!isDigit(DigitTokBegin[i])) { PP.Diag(PP.AdvanceToTokenCharacter(DigitTok.getLocation(), i), - diag::err_pp_line_digit_sequence); + diag::err_pp_line_digit_sequence) << IsGNULineDirective; PP.DiscardUntilEndOfDirective(); return true; } @@ -832,7 +836,8 @@ static bool GetLineValue(Token &DigitTok, unsigned &Val, } if (DigitTokBegin[0] == '0' && Val) - PP.Diag(DigitTok.getLocation(), diag::warn_pp_line_decimal); + PP.Diag(DigitTok.getLocation(), diag::warn_pp_line_decimal) + << IsGNULineDirective; return false; } @@ -998,7 +1003,7 @@ void Preprocessor::HandleDigitDirective(Token &DigitTok) { // line # limit other than it fit in 32-bits. unsigned LineNo; if (GetLineValue(DigitTok, LineNo, diag::err_pp_linemarker_requires_integer, - *this)) + *this, true)) return; Token StrTok; diff --git a/lib/Lex/PPMacroExpansion.cpp b/lib/Lex/PPMacroExpansion.cpp index 21451f581f3..24c6217ced2 100644 --- a/lib/Lex/PPMacroExpansion.cpp +++ b/lib/Lex/PPMacroExpansion.cpp @@ -13,7 +13,7 @@ //===----------------------------------------------------------------------===// #include "clang/Lex/Preprocessor.h" -#include "MacroArgs.h" +#include "clang/Lex/MacroArgs.h" #include "clang/Basic/FileManager.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/TargetInfo.h" @@ -223,7 +223,7 @@ bool Preprocessor::HandleMacroExpandedIdentifier(Token &Identifier, // If this is a builtin macro, like __LINE__ or _Pragma, handle it specially. if (MI->isBuiltinMacro()) { if (Callbacks) Callbacks->MacroExpands(Identifier, MD, - Identifier.getLocation()); + Identifier.getLocation(),/*Args=*/0); ExpandBuiltinMacro(Identifier); return false; } @@ -277,11 +277,12 @@ bool Preprocessor::HandleMacroExpandedIdentifier(Token &Identifier, DelayedMacroExpandsCallbacks.push_back( MacroExpandsInfo(Identifier, MD, ExpansionRange)); } else { - Callbacks->MacroExpands(Identifier, MD, ExpansionRange); + Callbacks->MacroExpands(Identifier, MD, ExpansionRange, Args); if (!DelayedMacroExpandsCallbacks.empty()) { for (unsigned i=0, e = DelayedMacroExpandsCallbacks.size(); i!=e; ++i) { MacroExpandsInfo &Info = DelayedMacroExpandsCallbacks[i]; - Callbacks->MacroExpands(Info.Tok, Info.MD, Info.Range); + // FIXME: We lose macro args info with delayed callback. + Callbacks->MacroExpands(Info.Tok, Info.MD, Info.Range, /*Args=*/0); } DelayedMacroExpandsCallbacks.clear(); } @@ -751,6 +752,8 @@ static bool HasFeature(const Preprocessor &PP, const IdentifierInfo *II) { .Case("c_atomic", LangOpts.C11) .Case("c_generic_selections", LangOpts.C11) .Case("c_static_assert", LangOpts.C11) + .Case("c_thread_local", + LangOpts.C11 && PP.getTargetInfo().isTLSSupported()) // C++11 features .Case("cxx_access_control_sfinae", LangOpts.CPlusPlus11) .Case("cxx_alias_templates", LangOpts.CPlusPlus11) @@ -768,7 +771,7 @@ static bool HasFeature(const Preprocessor &PP, const IdentifierInfo *II) { .Case("cxx_explicit_conversions", LangOpts.CPlusPlus11) .Case("cxx_generalized_initializers", LangOpts.CPlusPlus11) .Case("cxx_implicit_moves", LangOpts.CPlusPlus11) - //.Case("cxx_inheriting_constructors", false) + .Case("cxx_inheriting_constructors", LangOpts.CPlusPlus11) .Case("cxx_inline_namespaces", LangOpts.CPlusPlus11) .Case("cxx_lambdas", LangOpts.CPlusPlus11) .Case("cxx_local_type_template_args", LangOpts.CPlusPlus11) @@ -782,11 +785,23 @@ static bool HasFeature(const Preprocessor &PP, const IdentifierInfo *II) { .Case("cxx_rvalue_references", LangOpts.CPlusPlus11) .Case("cxx_strong_enums", LangOpts.CPlusPlus11) .Case("cxx_static_assert", LangOpts.CPlusPlus11) + .Case("cxx_thread_local", + LangOpts.CPlusPlus11 && PP.getTargetInfo().isTLSSupported()) .Case("cxx_trailing_return", LangOpts.CPlusPlus11) .Case("cxx_unicode_literals", LangOpts.CPlusPlus11) .Case("cxx_unrestricted_unions", LangOpts.CPlusPlus11) .Case("cxx_user_literals", LangOpts.CPlusPlus11) .Case("cxx_variadic_templates", LangOpts.CPlusPlus11) + // C++1y features + .Case("cxx_binary_literals", LangOpts.CPlusPlus1y) + //.Case("cxx_contextual_conversions", LangOpts.CPlusPlus1y) + //.Case("cxx_generalized_capture", LangOpts.CPlusPlus1y) + //.Case("cxx_generic_lambda", LangOpts.CPlusPlus1y) + //.Case("cxx_relaxed_constexpr", LangOpts.CPlusPlus1y) + //.Case("cxx_return_type_deduction", LangOpts.CPlusPlus1y) + //.Case("cxx_runtime_array", LangOpts.CPlusPlus1y) + .Case("cxx_aggregate_nsdmi", LangOpts.CPlusPlus1y) + //.Case("cxx_variable_templates", LangOpts.CPlusPlus1y) // Type traits .Case("has_nothrow_assign", LangOpts.CPlusPlus) .Case("has_nothrow_copy", LangOpts.CPlusPlus) @@ -847,7 +862,7 @@ static bool HasExtension(const Preprocessor &PP, const IdentifierInfo *II) { .Case("c_atomic", true) .Case("c_generic_selections", true) .Case("c_static_assert", true) - // C++0x features supported by other languages as extensions. + // C++11 features supported by other languages as extensions. .Case("cxx_atomic", LangOpts.CPlusPlus) .Case("cxx_deleted_functions", LangOpts.CPlusPlus) .Case("cxx_explicit_conversions", LangOpts.CPlusPlus) @@ -858,6 +873,8 @@ static bool HasExtension(const Preprocessor &PP, const IdentifierInfo *II) { .Case("cxx_range_for", LangOpts.CPlusPlus) .Case("cxx_reference_qualified_functions", LangOpts.CPlusPlus) .Case("cxx_rvalue_references", LangOpts.CPlusPlus) + // C++1y features supported by other languages as extensions. + .Case("cxx_binary_literals", true) .Default(false); } diff --git a/lib/Lex/Pragma.cpp b/lib/Lex/Pragma.cpp index 95e8a8ca8fc..b2ae4c9c443 100644 --- a/lib/Lex/Pragma.cpp +++ b/lib/Lex/Pragma.cpp @@ -254,14 +254,15 @@ void Preprocessor::Handle_Pragma(Token &Tok) { "Invalid string token!"); // Remove escaped quotes and escapes. - for (unsigned i = 1, e = StrVal.size(); i < e-2; ++i) { - if (StrVal[i] == '\\' && - (StrVal[i+1] == '\\' || StrVal[i+1] == '"')) { + unsigned ResultPos = 1; + for (unsigned i = 1, e = StrVal.size() - 2; i != e; ++i) { + if (StrVal[i] != '\\' || + (StrVal[i + 1] != '\\' && StrVal[i + 1] != '"')) { // \\ -> '\' and \" -> '"'. - StrVal.erase(StrVal.begin()+i); - --e; + StrVal[ResultPos++] = StrVal[i]; } } + StrVal.erase(StrVal.begin() + ResultPos, StrVal.end() - 2); } // Remove the front quote, replacing it with a space, so that the pragma @@ -434,8 +435,9 @@ void Preprocessor::HandlePragmaSystemHeader(Token &SysHeaderTok) { // Emit a line marker. This will change any source locations from this point // forward to realize they are in a system header. // Create a line note with this information. - SourceMgr.AddLineNote(SysHeaderTok.getLocation(), PLoc.getLine(), FilenameID, - false, false, true, false); + SourceMgr.AddLineNote(SysHeaderTok.getLocation(), PLoc.getLine()+1, + FilenameID, /*IsEntry=*/false, /*IsExit=*/false, + /*IsSystem=*/true, /*IsExternC=*/false); } /// HandlePragmaDependency - Handle \#pragma GCC dependency "foo" blah. @@ -491,126 +493,7 @@ void Preprocessor::HandlePragmaDependency(Token &DependencyTok) { } } -/// \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. -void Preprocessor::HandlePragmaComment(Token &Tok) { - SourceLocation CommentLoc = Tok.getLocation(); - Lex(Tok); - if (Tok.isNot(tok::l_paren)) { - Diag(CommentLoc, diag::err_pragma_comment_malformed); - return; - } - - // Read the identifier. - Lex(Tok); - if (Tok.isNot(tok::identifier)) { - Diag(CommentLoc, diag::err_pragma_comment_malformed); - return; - } - - // Verify that this is one of the 5 whitelisted options. - // FIXME: warn that 'exestr' is deprecated. - const IdentifierInfo *II = Tok.getIdentifierInfo(); - if (!II->isStr("compiler") && !II->isStr("exestr") && !II->isStr("lib") && - !II->isStr("linker") && !II->isStr("user")) { - Diag(Tok.getLocation(), diag::err_pragma_comment_unknown_kind); - return; - } - - // Read the optional string if present. - Lex(Tok); - std::string ArgumentString; - if (Tok.is(tok::comma) && !LexStringLiteral(Tok, ArgumentString, - "pragma comment", - /*MacroExpansion=*/true)) - return; - - // FIXME: If the kind is "compiler" warn if the string is present (it is - // ignored). - // FIXME: 'lib' requires a comment string. - // FIXME: 'linker' requires a comment string, and has a specific list of - // things that are allowable. - - if (Tok.isNot(tok::r_paren)) { - Diag(Tok.getLocation(), diag::err_pragma_comment_malformed); - return; - } - Lex(Tok); // eat the r_paren. - - if (Tok.isNot(tok::eod)) { - Diag(Tok.getLocation(), diag::err_pragma_comment_malformed); - return; - } - - // If the pragma is lexically sound, notify any interested PPCallbacks. - if (Callbacks) - Callbacks->PragmaComment(CommentLoc, II, ArgumentString); -} - -/// HandlePragmaMessage - Handle the microsoft and gcc \#pragma message -/// extension. The syntax is: -/// \code -/// #pragma message(string) -/// \endcode -/// OR, in GCC mode: -/// \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) { - SourceLocation MessageLoc = Tok.getLocation(); - Lex(Tok); - bool ExpectClosingParen = false; - switch (Tok.getKind()) { - case tok::l_paren: - // We have a MSVC style pragma message. - ExpectClosingParen = true; - // Read the string. - Lex(Tok); - break; - case tok::string_literal: - // We have a GCC style pragma message, and we just read the string. - break; - default: - Diag(MessageLoc, diag::err_pragma_message_malformed); - return; - } - - std::string MessageString; - if (!FinishLexStringLiteral(Tok, MessageString, "pragma message", - /*MacroExpansion=*/true)) - return; - - if (ExpectClosingParen) { - if (Tok.isNot(tok::r_paren)) { - Diag(Tok.getLocation(), diag::err_pragma_message_malformed); - return; - } - Lex(Tok); // eat the r_paren. - } - - if (Tok.isNot(tok::eod)) { - Diag(Tok.getLocation(), diag::err_pragma_message_malformed); - return; - } - - // Output the message. - Diag(MessageLoc, diag::warn_pragma_message) << MessageString; - - // If the pragma is lexically sound, notify any interested PPCallbacks. - if (Callbacks) - Callbacks->PragmaMessage(MessageLoc, MessageString); -} - -/// ParsePragmaPushOrPopMacro - Handle parsing of pragma push_macro/pop_macro. +/// ParsePragmaPushOrPopMacro - Handle parsing of pragma push_macro/pop_macro. /// Return the IdentifierInfo* associated with the macro to push or pop. IdentifierInfo *Preprocessor::ParsePragmaPushOrPopMacro(Token &Tok) { // Remember the pragma token location. @@ -995,10 +878,40 @@ struct PragmaDebugHandler : public PragmaHandler { llvm::CrashRecoveryContext *CRC =llvm::CrashRecoveryContext::GetCurrent(); if (CRC) CRC->HandleCrash(); + } else if (II->isStr("captured")) { + HandleCaptured(PP); } else { PP.Diag(Tok, diag::warn_pragma_debug_unexpected_command) << II->getName(); } + + PPCallbacks *Callbacks = PP.getPPCallbacks(); + if (Callbacks) + Callbacks->PragmaDebug(Tok.getLocation(), II->getName()); + } + + void HandleCaptured(Preprocessor &PP) { + // Skip if emitting preprocessed output. + if (PP.isPreprocessedOutput()) + return; + + Token Tok; + PP.LexUnexpandedToken(Tok); + + if (Tok.isNot(tok::eod)) { + PP.Diag(Tok, diag::ext_pp_extra_tokens_at_eol) + << "pragma clang __debug captured"; + return; + } + + SourceLocation NameLoc = Tok.getLocation(); + Token *Toks = PP.getPreprocessorAllocator().Allocate(1); + Toks->startToken(); + Toks->setKind(tok::annot_pragma_captured); + Toks->setLocation(NameLoc); + + PP.EnterTokenStream(Toks, 1, /*DisableMacroExpansion=*/true, + /*OwnsTokens=*/false); } // Disable MSVC warning about runtime stack overflow. @@ -1086,15 +999,6 @@ struct PragmaDiagnosticHandler : public PragmaHandler { } }; -/// PragmaCommentHandler - "\#pragma comment ...". -struct PragmaCommentHandler : public PragmaHandler { - PragmaCommentHandler() : PragmaHandler("comment") {} - virtual void HandlePragma(Preprocessor &PP, PragmaIntroducerKind Introducer, - Token &CommentTok) { - PP.HandlePragmaComment(CommentTok); - } -}; - /// PragmaIncludeAliasHandler - "\#pragma include_alias("...")". struct PragmaIncludeAliasHandler : public PragmaHandler { PragmaIncludeAliasHandler() : PragmaHandler("include_alias") {} @@ -1104,12 +1008,88 @@ struct PragmaIncludeAliasHandler : public PragmaHandler { } }; -/// PragmaMessageHandler - "\#pragma message("...")". +/// PragmaMessageHandler - Handle the microsoft and gcc \#pragma message +/// extension. The syntax is: +/// \code +/// #pragma message(string) +/// \endcode +/// OR, in GCC mode: +/// \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. +/// Also handles \#pragma GCC warning and \#pragma GCC error which take the same +/// form as \#pragma message. struct PragmaMessageHandler : public PragmaHandler { - PragmaMessageHandler() : PragmaHandler("message") {} +private: + const PPCallbacks::PragmaMessageKind Kind; + const StringRef Namespace; + + static const char* PragmaKind(PPCallbacks::PragmaMessageKind Kind, + bool PragmaNameOnly = false) { + switch (Kind) { + case PPCallbacks::PMK_Message: + return PragmaNameOnly ? "message" : "pragma message"; + case PPCallbacks::PMK_Warning: + return PragmaNameOnly ? "warning" : "pragma warning"; + case PPCallbacks::PMK_Error: + return PragmaNameOnly ? "error" : "pragma error"; + } + llvm_unreachable("Unknown PragmaMessageKind!"); + } + +public: + PragmaMessageHandler(PPCallbacks::PragmaMessageKind Kind, + StringRef Namespace = StringRef()) + : PragmaHandler(PragmaKind(Kind, true)), Kind(Kind), Namespace(Namespace) {} + virtual void HandlePragma(Preprocessor &PP, PragmaIntroducerKind Introducer, - Token &CommentTok) { - PP.HandlePragmaMessage(CommentTok); + Token &Tok) { + SourceLocation MessageLoc = Tok.getLocation(); + PP.Lex(Tok); + bool ExpectClosingParen = false; + switch (Tok.getKind()) { + case tok::l_paren: + // We have a MSVC style pragma message. + ExpectClosingParen = true; + // Read the string. + PP.Lex(Tok); + break; + case tok::string_literal: + // We have a GCC style pragma message, and we just read the string. + break; + default: + PP.Diag(MessageLoc, diag::err_pragma_message_malformed) << Kind; + return; + } + + std::string MessageString; + if (!PP.FinishLexStringLiteral(Tok, MessageString, PragmaKind(Kind), + /*MacroExpansion=*/true)) + return; + + if (ExpectClosingParen) { + if (Tok.isNot(tok::r_paren)) { + PP.Diag(Tok.getLocation(), diag::err_pragma_message_malformed) << Kind; + return; + } + PP.Lex(Tok); // eat the r_paren. + } + + if (Tok.isNot(tok::eod)) { + PP.Diag(Tok.getLocation(), diag::err_pragma_message_malformed) << Kind; + return; + } + + // Output the message. + PP.Diag(MessageLoc, (Kind == PPCallbacks::PMK_Error) + ? diag::err_pragma_message + : diag::warn_pragma_message) << MessageString; + + // If the pragma is lexically sound, notify any interested PPCallbacks. + if (PPCallbacks *Callbacks = PP.getPPCallbacks()) + Callbacks->PragmaMessage(MessageLoc, Namespace, Kind, MessageString); } }; @@ -1257,13 +1237,17 @@ void Preprocessor::RegisterBuiltinPragmas() { AddPragmaHandler(new PragmaMarkHandler()); AddPragmaHandler(new PragmaPushMacroHandler()); AddPragmaHandler(new PragmaPopMacroHandler()); - AddPragmaHandler(new PragmaMessageHandler()); + AddPragmaHandler(new PragmaMessageHandler(PPCallbacks::PMK_Message)); // #pragma GCC ... AddPragmaHandler("GCC", new PragmaPoisonHandler()); AddPragmaHandler("GCC", new PragmaSystemHeaderHandler()); AddPragmaHandler("GCC", new PragmaDependencyHandler()); AddPragmaHandler("GCC", new PragmaDiagnosticHandler("GCC")); + AddPragmaHandler("GCC", new PragmaMessageHandler(PPCallbacks::PMK_Warning, + "GCC")); + AddPragmaHandler("GCC", new PragmaMessageHandler(PPCallbacks::PMK_Error, + "GCC")); // #pragma clang ... AddPragmaHandler("clang", new PragmaPoisonHandler()); AddPragmaHandler("clang", new PragmaSystemHeaderHandler()); @@ -1278,7 +1262,6 @@ void Preprocessor::RegisterBuiltinPragmas() { // MS extensions. if (LangOpts.MicrosoftExt) { - AddPragmaHandler(new PragmaCommentHandler()); AddPragmaHandler(new PragmaIncludeAliasHandler()); AddPragmaHandler(new PragmaRegionHandler("region")); AddPragmaHandler(new PragmaRegionHandler("endregion")); diff --git a/lib/Lex/PreprocessingRecord.cpp b/lib/Lex/PreprocessingRecord.cpp index b10e7f7beee..426b9225626 100644 --- a/lib/Lex/PreprocessingRecord.cpp +++ b/lib/Lex/PreprocessingRecord.cpp @@ -406,7 +406,8 @@ void PreprocessingRecord::Defined(const Token &MacroNameTok, } void PreprocessingRecord::MacroExpands(const Token &Id,const MacroDirective *MD, - SourceRange Range) { + SourceRange Range, + const MacroArgs *Args) { addMacroExpansion(Id, MD->getMacroInfo(), Range); } diff --git a/lib/Lex/Preprocessor.cpp b/lib/Lex/Preprocessor.cpp index 53c45dca01f..66f23f10188 100644 --- a/lib/Lex/Preprocessor.cpp +++ b/lib/Lex/Preprocessor.cpp @@ -26,7 +26,7 @@ //===----------------------------------------------------------------------===// #include "clang/Lex/Preprocessor.h" -#include "MacroArgs.h" +#include "clang/Lex/MacroArgs.h" #include "clang/Basic/FileManager.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/TargetInfo.h" @@ -67,7 +67,8 @@ Preprocessor::Preprocessor(IntrusiveRefCntPtr PPOpts, CodeComplete(0), CodeCompletionFile(0), CodeCompletionOffset(0), CodeCompletionReached(0), SkipMainFilePreamble(0, true), CurPPLexer(0), CurDirLookup(0), CurLexerKind(CLK_Lexer), Callbacks(0), - MacroArgCache(0), Record(0), MIChainHead(0), MICache(0) { + MacroArgCache(0), Record(0), MIChainHead(0), MICache(0), + DeserialMIChainHead(0) { OwnsHeaderSearch = OwnsHeaders; ScratchBuf = new ScratchBuffer(SourceMgr); @@ -153,6 +154,9 @@ Preprocessor::~Preprocessor() { for (unsigned i = 0, e = NumCachedTokenLexers; i != e; ++i) delete TokenLexerCache[i]; + for (DeserializedMacroInfoChain *I = DeserialMIChainHead ; I ; I = I->Next) + I->MI.Destroy(); + // Free any cached MacroArgs. for (MacroArgs *ArgList = MacroArgCache; ArgList; ) ArgList = ArgList->deallocate(); diff --git a/lib/Lex/TokenLexer.cpp b/lib/Lex/TokenLexer.cpp index 5b41fe9b8d3..07753c7d7c3 100644 --- a/lib/Lex/TokenLexer.cpp +++ b/lib/Lex/TokenLexer.cpp @@ -12,7 +12,7 @@ //===----------------------------------------------------------------------===// #include "clang/Lex/TokenLexer.h" -#include "MacroArgs.h" +#include "clang/Lex/MacroArgs.h" #include "clang/Basic/SourceManager.h" #include "clang/Lex/LexDiagnostic.h" #include "clang/Lex/MacroInfo.h" diff --git a/lib/Parse/CMakeLists.txt b/lib/Parse/CMakeLists.txt index 939998ecb1a..01c0694d035 100644 --- a/lib/Parse/CMakeLists.txt +++ b/lib/Parse/CMakeLists.txt @@ -17,6 +17,7 @@ add_clang_library(clangParse add_dependencies(clangParse ClangAttrClasses + ClangAttrExprArgs ClangAttrLateParsed ClangAttrList ClangAttrParsedAttrList diff --git a/lib/Parse/ParseCXXInlineMethods.cpp b/lib/Parse/ParseCXXInlineMethods.cpp index bc634b57d9c..5fc4189742c 100644 --- a/lib/Parse/ParseCXXInlineMethods.cpp +++ b/lib/Parse/ParseCXXInlineMethods.cpp @@ -57,8 +57,7 @@ NamedDecl *Parser::ParseCXXInlineMethodDef(AccessSpecifier AS, if (FnD) { Actions.ProcessDeclAttributeList(getCurScope(), FnD, AccessAttrs, false, true); - bool TypeSpecContainsAuto - = D.getDeclSpec().getTypeSpecType() == DeclSpec::TST_auto; + bool TypeSpecContainsAuto = D.getDeclSpec().containsPlaceholderType(); if (Init.isUsable()) Actions.AddInitializerToDecl(FnD, Init.get(), false, TypeSpecContainsAuto); @@ -279,8 +278,11 @@ void Parser::LateParsedMemberInitializer::ParseLexedMemberInitializers() { void Parser::ParseLexedMethodDeclarations(ParsingClass &Class) { bool HasTemplateScope = !Class.TopLevelClass && Class.TemplateScope; ParseScope ClassTemplateScope(this, Scope::TemplateParamScope, HasTemplateScope); - if (HasTemplateScope) + TemplateParameterDepthRAII CurTemplateDepthTracker(TemplateParameterDepth); + if (HasTemplateScope) { Actions.ActOnReenterTemplateScope(getCurScope(), Class.TagOrTemplate); + ++CurTemplateDepthTracker; + } // The current scope is still active if we're the top-level class. // Otherwise we'll need to push and enter a new scope. @@ -301,9 +303,11 @@ void Parser::ParseLexedMethodDeclarations(ParsingClass &Class) { void Parser::ParseLexedMethodDeclaration(LateParsedMethodDeclaration &LM) { // If this is a member template, introduce the template parameter scope. ParseScope TemplateScope(this, Scope::TemplateParamScope, LM.TemplateScope); - if (LM.TemplateScope) + TemplateParameterDepthRAII CurTemplateDepthTracker(TemplateParameterDepth); + if (LM.TemplateScope) { Actions.ActOnReenterTemplateScope(getCurScope(), LM.Method); - + ++CurTemplateDepthTracker; + } // Start the delayed C++ method declaration Actions.ActOnStartDelayedCXXMethodDeclaration(getCurScope(), LM.Method); @@ -379,9 +383,11 @@ void Parser::ParseLexedMethodDeclaration(LateParsedMethodDeclaration &LM) { void Parser::ParseLexedMethodDefs(ParsingClass &Class) { bool HasTemplateScope = !Class.TopLevelClass && Class.TemplateScope; ParseScope ClassTemplateScope(this, Scope::TemplateParamScope, HasTemplateScope); - if (HasTemplateScope) + TemplateParameterDepthRAII CurTemplateDepthTracker(TemplateParameterDepth); + if (HasTemplateScope) { Actions.ActOnReenterTemplateScope(getCurScope(), Class.TagOrTemplate); - + ++CurTemplateDepthTracker; + } bool HasClassScope = !Class.TopLevelClass; ParseScope ClassScope(this, Scope::ClassScope|Scope::DeclScope, HasClassScope); @@ -394,9 +400,11 @@ void Parser::ParseLexedMethodDefs(ParsingClass &Class) { void Parser::ParseLexedMethodDef(LexedMethod &LM) { // If this is a member template, introduce the template parameter scope. ParseScope TemplateScope(this, Scope::TemplateParamScope, LM.TemplateScope); - if (LM.TemplateScope) + TemplateParameterDepthRAII CurTemplateDepthTracker(TemplateParameterDepth); + if (LM.TemplateScope) { Actions.ActOnReenterTemplateScope(getCurScope(), LM.D); - + ++CurTemplateDepthTracker; + } // Save the current token position. SourceLocation origLoc = Tok.getLocation(); @@ -441,6 +449,13 @@ void Parser::ParseLexedMethodDef(LexedMethod &LM) { } else Actions.ActOnDefaultCtorInitializers(LM.D); + assert((Actions.getDiagnostics().hasErrorOccurred() || + !isa(LM.D) || + cast(LM.D)->getTemplateParameters()->getDepth() + < TemplateParameterDepth) && + "TemplateParameterDepth should be greater than the depth of " + "current template being instantiated!"); + ParseFunctionStatementBody(LM.D, FnScope); // Clear the late-template-parsed bit if we set it before. @@ -466,9 +481,11 @@ void Parser::ParseLexedMemberInitializers(ParsingClass &Class) { bool HasTemplateScope = !Class.TopLevelClass && Class.TemplateScope; ParseScope ClassTemplateScope(this, Scope::TemplateParamScope, HasTemplateScope); - if (HasTemplateScope) + TemplateParameterDepthRAII CurTemplateDepthTracker(TemplateParameterDepth); + if (HasTemplateScope) { Actions.ActOnReenterTemplateScope(getCurScope(), Class.TagOrTemplate); - + ++CurTemplateDepthTracker; + } // Set or update the scope flags. bool AlreadyHasClassScope = Class.TopLevelClass; unsigned ScopeFlags = Scope::ClassScope|Scope::DeclScope; diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 990a9097acf..6a87b78879e 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -178,6 +178,12 @@ void Parser::ParseGNUAttributes(ParsedAttributes &attrs, } } +/// \brief Determine whether the given attribute has all expression arguments. +static bool attributeHasExprArgs(const IdentifierInfo &II) { + return llvm::StringSwitch(II.getName()) +#include "clang/Parse/AttrExprArgs.inc" + .Default(false); +} /// Parse the arguments to a parameterized GNU attribute or /// a C++11 attribute in "gnu" namespace. @@ -247,6 +253,10 @@ void Parser::ParseGNUAttributeArgs(IdentifierInfo *AttrName, TypeParsed = true; break; } + // If the attribute has all expression arguments, and not a "parameter", + // break out to handle it below. + if (attributeHasExprArgs(*AttrName)) + break; ParmName = Tok.getIdentifierInfo(); ParmLoc = ConsumeToken(); break; @@ -364,6 +374,7 @@ bool Parser::IsSimpleMicrosoftDeclSpec(IdentifierInfo *Ident) { .Case("novtable", true) .Case("selectany", true) .Case("thread", true) + .Case("safebuffers", true ) .Default(false); } @@ -394,17 +405,119 @@ void Parser::ParseComplexMicrosoftDeclSpec(IdentifierInfo *Ident, // 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)) + if (Tok.isNot(tok::l_paren)) { + Diag(Tok.getLocation(), diag::err_expected_lparen_after) + << Ident->getNameStart(); return; + } + BalancedDelimiterTracker T(*this, tok::l_paren); + T.expectAndConsume(diag::err_expected_lparen_after, + Ident->getNameStart(), tok::r_paren); + + enum AccessorKind { + AK_Invalid = -1, + AK_Put = 0, AK_Get = 1 // indices into AccessorNames + }; + IdentifierInfo *AccessorNames[] = { 0, 0 }; + bool HasInvalidAccessor = false; + + // Parse the accessor specifications. + while (true) { + // Stop if this doesn't look like an accessor spec. + if (!Tok.is(tok::identifier)) { + // If the user wrote a completely empty list, use a special diagnostic. + if (Tok.is(tok::r_paren) && !HasInvalidAccessor && + AccessorNames[AK_Put] == 0 && AccessorNames[AK_Get] == 0) { + Diag(Loc, diag::err_ms_property_no_getter_or_putter); + break; + } + + Diag(Tok.getLocation(), diag::err_ms_property_unknown_accessor); + break; + } + + AccessorKind Kind; + SourceLocation KindLoc = Tok.getLocation(); + StringRef KindStr = Tok.getIdentifierInfo()->getName(); + if (KindStr == "get") { + Kind = AK_Get; + } else if (KindStr == "put") { + Kind = AK_Put; + + // Recover from the common mistake of using 'set' instead of 'put'. + } else if (KindStr == "set") { + Diag(KindLoc, diag::err_ms_property_has_set_accessor) + << FixItHint::CreateReplacement(KindLoc, "put"); + Kind = AK_Put; + + // Handle the mistake of forgetting the accessor kind by skipping + // this accessor. + } else if (NextToken().is(tok::comma) || NextToken().is(tok::r_paren)) { + Diag(KindLoc, diag::err_ms_property_missing_accessor_kind); + ConsumeToken(); + HasInvalidAccessor = true; + goto next_property_accessor; + + // Otherwise, complain about the unknown accessor kind. + } else { + Diag(KindLoc, diag::err_ms_property_unknown_accessor); + HasInvalidAccessor = true; + Kind = AK_Invalid; + + // Try to keep parsing unless it doesn't look like an accessor spec. + if (!NextToken().is(tok::equal)) break; + } + + // Consume the identifier. + ConsumeToken(); + + // Consume the '='. + if (Tok.is(tok::equal)) { + ConsumeToken(); + } else { + Diag(Tok.getLocation(), diag::err_ms_property_expected_equal) + << KindStr; + break; + } + + // Expect the method name. + if (!Tok.is(tok::identifier)) { + Diag(Tok.getLocation(), diag::err_ms_property_expected_accessor_name); + break; + } + + if (Kind == AK_Invalid) { + // Just drop invalid accessors. + } else if (AccessorNames[Kind] != NULL) { + // Complain about the repeated accessor, ignore it, and keep parsing. + Diag(KindLoc, diag::err_ms_property_duplicate_accessor) << KindStr; + } else { + AccessorNames[Kind] = Tok.getIdentifierInfo(); + } + ConsumeToken(); + + next_property_accessor: + // Keep processing accessors until we run out. + if (Tok.is(tok::comma)) { + ConsumeAnyToken(); + continue; + + // If we run into the ')', stop without consuming it. + } else if (Tok.is(tok::r_paren)) { + break; + } else { + Diag(Tok.getLocation(), diag::err_ms_property_expected_comma_or_rparen); + break; + } + } + + // Only add the property attribute if it was well-formed. + if (!HasInvalidAccessor) { + Attrs.addNewPropertyAttr(Ident, Loc, 0, SourceLocation(), 0, + SourceLocation(), + AccessorNames[AK_Get], AccessorNames[AK_Put], + AttributeList::AS_Declspec); + } T.skipToEnd(); } else { // We don't recognize this as a valid declspec, but instead of creating the @@ -1489,35 +1602,42 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS, MaybeParseGNUAttributes(D, &LateParsedAttrs); // Check to see if we have a function *definition* which must have a body. - if (AllowFunctionDefinitions && D.isFunctionDeclarator() && + if (D.isFunctionDeclarator() && // Look at the next token to make sure that this isn't a function // 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); + if (AllowFunctionDefinitions) { + if (isStartOfFunctionDefinition(D)) { + if (DS.getStorageClassSpec() == DeclSpec::SCS_typedef) { + Diag(Tok, diag::err_function_declared_typedef); - // Recover by treating the 'typedef' as spurious. - DS.ClearStorageClassSpecs(); - } + // Recover by treating the 'typedef' as spurious. + DS.ClearStorageClassSpecs(); + } - Decl *TheDecl = - ParseFunctionDefinition(D, ParsedTemplateInfo(), &LateParsedAttrs); - return Actions.ConvertDeclToDeclGroup(TheDecl); - } + Decl *TheDecl = + 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 - // actually a body. Just fall through into the code that handles it as a - // prototype, and let the top-level code handle the erroneous declspec - // where it would otherwise expect a comma or semicolon. + 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 + // actually a body. Just fall through into the code that handles it as a + // prototype, and let the top-level code handle the erroneous declspec + // where it would otherwise expect a comma or semicolon. + } else { + Diag(Tok, diag::err_expected_fn_body); + SkipUntil(tok::semi); + return DeclGroupPtrTy(); + } } else { - Diag(Tok, diag::err_expected_fn_body); - SkipUntil(tok::semi); - return DeclGroupPtrTy(); + if (Tok.is(tok::l_brace)) { + Diag(Tok, diag::err_function_definition_not_allowed); + SkipUntil(tok::r_brace, true, true); + } } } @@ -1527,14 +1647,23 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS, // C++0x [stmt.iter]p1: Check if we have a for-range-declarator. If so, we // must parse and analyze the for-range-initializer before the declaration is // analyzed. - if (FRI && Tok.is(tok::colon)) { - FRI->ColonLoc = ConsumeToken(); - if (Tok.is(tok::l_brace)) - FRI->RangeExpr = ParseBraceInitializer(); - else - FRI->RangeExpr = ParseExpression(); + // + // Handle the Objective-C for-in loop variable similarly, although we + // don't need to parse the container in advance. + if (FRI && (Tok.is(tok::colon) || isTokIdentifier_in())) { + bool IsForRangeLoop = false; + if (Tok.is(tok::colon)) { + IsForRangeLoop = true; + FRI->ColonLoc = ConsumeToken(); + if (Tok.is(tok::l_brace)) + FRI->RangeExpr = ParseBraceInitializer(); + else + FRI->RangeExpr = ParseExpression(); + } + Decl *ThisDecl = Actions.ActOnDeclarator(getCurScope(), D); - Actions.ActOnCXXForRangeDecl(ThisDecl); + if (IsForRangeLoop) + Actions.ActOnCXXForRangeDecl(ThisDecl); Actions.FinalizeDeclaration(ThisDecl); D.complete(ThisDecl); return Actions.FinalizeDeclaratorGroup(getCurScope(), DS, &ThisDecl, 1); @@ -1691,8 +1820,7 @@ Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes(Declarator &D, } } - bool TypeContainsAuto = - D.getDeclSpec().getTypeSpecType() == DeclSpec::TST_auto; + bool TypeContainsAuto = D.getDeclSpec().containsPlaceholderType(); // Parse declarator '=' initializer. // If a '==' or '+=' is found, suggest a fixit to '='. @@ -1839,7 +1967,8 @@ void Parser::ParseSpecifierQualifierList(DeclSpec &DS, AccessSpecifier AS, if (DS.getStorageClassSpecLoc().isValid()) Diag(DS.getStorageClassSpecLoc(),diag::err_typename_invalid_storageclass); else - Diag(DS.getThreadSpecLoc(), diag::err_typename_invalid_storageclass); + Diag(DS.getThreadStorageClassSpecLoc(), + diag::err_typename_invalid_storageclass); DS.ClearStorageClassSpecs(); } @@ -1920,10 +2049,9 @@ bool Parser::ParseImplicitInt(DeclSpec &DS, CXXScopeSpec *SS, // 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. + // implicit int as an extension in C99 and C11. if (DSC != DSC_type_specifier && DSC != DSC_trailing && - (!getLangOpts().CPlusPlus || getLangOpts().MicrosoftExt) && + !getLangOpts().CPlusPlus && 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 @@ -2172,6 +2300,8 @@ void Parser::ParseAlignmentSpecifier(ParsedAttributes &Attrs, /// 'auto' /// 'register' /// [C++] 'mutable' +/// [C++11] 'thread_local' +/// [C11] '_Thread_local' /// [GNU] '__thread' /// function-specifier: [C99 6.7.4] /// [C99] 'inline' @@ -2608,7 +2738,7 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, PrevSpec, DiagID); break; case tok::kw_extern: - if (DS.isThreadSpecified()) + if (DS.getThreadStorageClassSpec() == DeclSpec::TSCS___thread) Diag(Tok, diag::ext_thread_before) << "extern"; isInvalid = DS.SetStorageClassSpec(Actions, DeclSpec::SCS_extern, Loc, PrevSpec, DiagID); @@ -2618,7 +2748,7 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, Loc, PrevSpec, DiagID); break; case tok::kw_static: - if (DS.isThreadSpecified()) + if (DS.getThreadStorageClassSpec() == DeclSpec::TSCS___thread) Diag(Tok, diag::ext_thread_before) << "static"; isInvalid = DS.SetStorageClassSpec(Actions, DeclSpec::SCS_static, Loc, PrevSpec, DiagID); @@ -2647,7 +2777,16 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, PrevSpec, DiagID); break; case tok::kw___thread: - isInvalid = DS.SetStorageClassSpecThread(Loc, PrevSpec, DiagID); + isInvalid = DS.SetStorageClassSpecThread(DeclSpec::TSCS___thread, Loc, + PrevSpec, DiagID); + break; + case tok::kw_thread_local: + isInvalid = DS.SetStorageClassSpecThread(DeclSpec::TSCS_thread_local, Loc, + PrevSpec, DiagID); + break; + case tok::kw__Thread_local: + isInvalid = DS.SetStorageClassSpecThread(DeclSpec::TSCS__Thread_local, + Loc, PrevSpec, DiagID); break; // function-specifier @@ -3100,6 +3239,16 @@ void Parser::ParseStructUnionBody(SourceLocation RecordLoc, continue; } + if (Tok.is(tok::annot_pragma_pack)) { + HandlePragmaPack(); + continue; + } + + if (Tok.is(tok::annot_pragma_align)) { + HandlePragmaAlign(); + continue; + } + if (!Tok.is(tok::at)) { struct CFieldCallback : FieldCallback { Parser &P; @@ -3227,9 +3376,9 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS, bool IsScopedUsingClassTag = false; // In C++11, recognize 'enum class' and 'enum struct'. - if (getLangOpts().CPlusPlus11 && - (Tok.is(tok::kw_class) || Tok.is(tok::kw_struct))) { - Diag(Tok, diag::warn_cxx98_compat_scoped_enum); + if (Tok.is(tok::kw_class) || Tok.is(tok::kw_struct)) { + Diag(Tok, getLangOpts().CPlusPlus11 ? diag::warn_cxx98_compat_scoped_enum + : diag::ext_scoped_enum); IsScopedUsingClassTag = Tok.is(tok::kw_class); ScopedEnumKWLoc = ConsumeToken(); @@ -3614,8 +3763,8 @@ void Parser::ParseEnumBody(SourceLocation StartLoc, Decl *EnumDecl) { MaybeParseGNUAttributes(attrs); Actions.ActOnEnumBody(StartLoc, T.getOpenLocation(), T.getCloseLocation(), - EnumDecl, EnumConstantDecls.data(), - EnumConstantDecls.size(), getCurScope(), + EnumDecl, EnumConstantDecls, + getCurScope(), attrs.getList()); EnumScope.Exit(); @@ -3894,6 +4043,8 @@ bool Parser::isDeclarationSpecifier(bool DisambiguatingWithExpression) { case tok::kw_auto: case tok::kw_register: case tok::kw___thread: + case tok::kw_thread_local: + case tok::kw__Thread_local: // Modules case tok::kw___module_private__: @@ -4873,7 +5024,8 @@ void Parser::ParseFunctionDeclarator(Declarator &D, Sema::CXXThisScopeRAII ThisScope(Actions, dyn_cast(Actions.CurContext), DS.getTypeQualifiers() | - (D.getDeclSpec().isConstexprSpecified() + (D.getDeclSpec().isConstexprSpecified() && + !getLangOpts().CPlusPlus1y ? Qualifiers::Const : 0), IsCXX11MemberFunction); diff --git a/lib/Parse/ParseDeclCXX.cpp b/lib/Parse/ParseDeclCXX.cpp index d7f8e982aa5..f1fbbb15fed 100644 --- a/lib/Parse/ParseDeclCXX.cpp +++ b/lib/Parse/ParseDeclCXX.cpp @@ -674,15 +674,15 @@ Decl *Parser::ParseStaticAssertDeclaration(SourceLocation &DeclEnd){ T.getCloseLocation()); } -/// ParseDecltypeSpecifier - Parse a C++0x decltype specifier. +/// ParseDecltypeSpecifier - Parse a C++11 decltype specifier. /// /// 'decltype' ( expression ) +/// 'decltype' ( 'auto' ) [C++1y] /// SourceLocation Parser::ParseDecltypeSpecifier(DeclSpec &DS) { assert((Tok.is(tok::kw_decltype) || Tok.is(tok::annot_decltype)) && "Not a decltype specifier"); - ExprResult Result; SourceLocation StartLoc = Tok.getLocation(); SourceLocation EndLoc; @@ -709,29 +709,44 @@ SourceLocation Parser::ParseDecltypeSpecifier(DeclSpec &DS) { StartLoc : T.getOpenLocation(); } - // Parse the expression - - // C++0x [dcl.type.simple]p4: - // The operand of the decltype specifier is an unevaluated operand. - EnterExpressionEvaluationContext Unevaluated(Actions, Sema::Unevaluated, - 0, /*IsDecltype=*/true); - Result = ParseExpression(); - if (Result.isInvalid()) { - DS.SetTypeSpecError(); - if (SkipUntil(tok::r_paren, /*StopAtSemi=*/true, /*DontConsume=*/true)) { - EndLoc = ConsumeParen(); - } else { - if (PP.isBacktrackEnabled() && Tok.is(tok::semi)) { - // Backtrack to get the location of the last token before the semi. - PP.RevertCachedTokens(2); - ConsumeToken(); // the semi. - EndLoc = ConsumeAnyToken(); - assert(Tok.is(tok::semi)); + // Check for C++1y 'decltype(auto)'. + if (Tok.is(tok::kw_auto)) { + // No need to disambiguate here: an expression can't start with 'auto', + // because the typename-specifier in a function-style cast operation can't + // be 'auto'. + Diag(Tok.getLocation(), + getLangOpts().CPlusPlus1y + ? diag::warn_cxx11_compat_decltype_auto_type_specifier + : diag::ext_decltype_auto_type_specifier); + ConsumeToken(); + } else { + // Parse the expression + + // C++11 [dcl.type.simple]p4: + // The operand of the decltype specifier is an unevaluated operand. + EnterExpressionEvaluationContext Unevaluated(Actions, Sema::Unevaluated, + 0, /*IsDecltype=*/true); + Result = ParseExpression(); + if (Result.isInvalid()) { + DS.SetTypeSpecError(); + if (SkipUntil(tok::r_paren, /*StopAtSemi=*/true, + /*DontConsume=*/true)) { + EndLoc = ConsumeParen(); } else { - EndLoc = Tok.getLocation(); + if (PP.isBacktrackEnabled() && Tok.is(tok::semi)) { + // Backtrack to get the location of the last token before the semi. + PP.RevertCachedTokens(2); + ConsumeToken(); // the semi. + EndLoc = ConsumeAnyToken(); + assert(Tok.is(tok::semi)); + } else { + EndLoc = Tok.getLocation(); + } } + return EndLoc; } - return EndLoc; + + Result = Actions.ActOnDecltypeExpression(Result.take()); } // Match the ')' @@ -743,7 +758,6 @@ SourceLocation Parser::ParseDecltypeSpecifier(DeclSpec &DS) { return T.getCloseLocation(); } - Result = Actions.ActOnDecltypeExpression(Result.take()); if (Result.isInvalid()) { DS.SetTypeSpecError(); return T.getCloseLocation(); @@ -751,12 +765,16 @@ SourceLocation Parser::ParseDecltypeSpecifier(DeclSpec &DS) { EndLoc = T.getCloseLocation(); } + assert(!Result.isInvalid()); const char *PrevSpec = 0; unsigned DiagID; // Check for duplicate type specifiers (e.g. "int decltype(a)"). - if (DS.SetTypeSpecType(DeclSpec::TST_decltype, StartLoc, PrevSpec, - DiagID, Result.release())) { + if (Result.get() + ? DS.SetTypeSpecType(DeclSpec::TST_decltype, StartLoc, PrevSpec, + DiagID, Result.release()) + : DS.SetTypeSpecType(DeclSpec::TST_decltype_auto, StartLoc, PrevSpec, + DiagID)) { Diag(StartLoc, DiagID) << PrevSpec; DS.SetTypeSpecError(); } @@ -773,8 +791,10 @@ void Parser::AnnotateExistingDecltypeSpecifier(const DeclSpec& DS, PP.EnterToken(Tok); Tok.setKind(tok::annot_decltype); - setExprAnnotation(Tok, DS.getTypeSpecType() == TST_decltype ? - DS.getRepAsExpr() : ExprResult()); + setExprAnnotation(Tok, + DS.getTypeSpecType() == TST_decltype ? DS.getRepAsExpr() : + DS.getTypeSpecType() == TST_decltype_auto ? ExprResult() : + ExprError()); Tok.setAnnotationEndLoc(EndLoc); Tok.setLocation(StartLoc); PP.AnnotateCachedTokens(Tok); @@ -2267,11 +2287,10 @@ void Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS, SkipUntil(tok::comma, true, true); else if (ThisDecl) Actions.AddInitializerToDecl(ThisDecl, Init.get(), EqualLoc.isInvalid(), - DS.getTypeSpecType() == DeclSpec::TST_auto); + DS.containsPlaceholderType()); } else if (ThisDecl && DS.getStorageClassSpec() == DeclSpec::SCS_static) { // No initializer. - Actions.ActOnUninitializedDecl(ThisDecl, - DS.getTypeSpecType() == DeclSpec::TST_auto); + Actions.ActOnUninitializedDecl(ThisDecl, DS.containsPlaceholderType()); } if (ThisDecl) { diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index 956ba36d3c8..9521ffbc0e3 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -691,8 +691,14 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression, Res = getExprAnnotation(Tok); ConsumeToken(); break; - + case tok::kw_decltype: + // Annotate the token and tail recurse. + if (TryAnnotateTypeOrScopeToken()) + return ExprError(); + assert(Tok.isNot(tok::kw_decltype)); + return ParseCastExpression(isUnaryExpression, isAddressOfOperand); + case tok::identifier: { // primary-expression: identifier // unqualified-id: identifier // constant: enumeration-constant @@ -1408,8 +1414,7 @@ Parser::ParsePostfixExpressionSuffix(ExprResult LHS) { CommaLocsTy CommaLocs; if (Tok.is(tok::code_completion)) { - Actions.CodeCompleteCall(getCurScope(), LHS.get(), - ArrayRef()); + Actions.CodeCompleteCall(getCurScope(), LHS.get(), None); cutOffParsing(); return ExprError(); } diff --git a/lib/Parse/ParseExprCXX.cpp b/lib/Parse/ParseExprCXX.cpp index 17c4adf7d7e..f259d5f59b4 100644 --- a/lib/Parse/ParseExprCXX.cpp +++ b/lib/Parse/ParseExprCXX.cpp @@ -1455,7 +1455,9 @@ bool Parser::ParseCXXCondition(ExprResult &ExprOut, if (!InitExpr.isInvalid()) Actions.AddInitializerToDecl(DeclOut, InitExpr.take(), !CopyInitialization, - DS.getTypeSpecType() == DeclSpec::TST_auto); + DS.containsPlaceholderType()); + else + Actions.ActOnInitializerError(DeclOut); // FIXME: Build a reference to this declaration? Convert it to bool? // (This is currently handled by Sema). @@ -2033,7 +2035,7 @@ bool Parser::ParseUnqualifiedIdOperator(CXXScopeSpec &SS, bool EnteringContext, // Parse the conversion-declarator, which is merely a sequence of // ptr-operators. - Declarator D(DS, Declarator::TypeNameContext); + Declarator D(DS, Declarator::ConversionIdContext); ParseDeclaratorInternal(D, /*DirectDeclParser=*/0); // Finish up the type. diff --git a/lib/Parse/ParseInit.cpp b/lib/Parse/ParseInit.cpp index 3b967174bc5..8311aa22075 100644 --- a/lib/Parse/ParseInit.cpp +++ b/lib/Parse/ParseInit.cpp @@ -412,7 +412,7 @@ ExprResult Parser::ParseBraceInitializer() { if (!getLangOpts().CPlusPlus) Diag(LBraceLoc, diag::ext_gnu_empty_initializer); // Match the '}'. - return Actions.ActOnInitList(LBraceLoc, MultiExprArg(), ConsumeBrace()); + return Actions.ActOnInitList(LBraceLoc, None, ConsumeBrace()); } bool InitExprsOk = true; diff --git a/lib/Parse/ParseObjc.cpp b/lib/Parse/ParseObjc.cpp index ad95dd5821c..4a572f19932 100644 --- a/lib/Parse/ParseObjc.cpp +++ b/lib/Parse/ParseObjc.cpp @@ -1565,6 +1565,13 @@ Parser::ParseObjCAtImplementationDeclaration(SourceLocation AtLoc) { if (Tok.is(tok::l_brace)) // we have ivars ParseObjCClassInstanceVariables(ObjCImpDecl, tok::objc_private, AtLoc); + else if (Tok.is(tok::less)) { // we have illegal '<' try to recover + Diag(Tok, diag::err_unexpected_protocol_qualifier); + // try to recover. + AttributeFactory attr; + DeclSpec DS(attr); + (void)ParseObjCProtocolQualifiers(DS); + } } assert(ObjCImpDecl); @@ -1675,7 +1682,7 @@ Decl *Parser::ParseObjCAtAliasDeclaration(SourceLocation atLoc) { /// Decl *Parser::ParseObjCPropertySynthesize(SourceLocation atLoc) { assert(Tok.isObjCAtKeyword(tok::objc_synthesize) && - "ParseObjCPropertyDynamic(): Expected '@synthesize'"); + "ParseObjCPropertySynthesize(): Expected '@synthesize'"); ConsumeToken(); // consume synthesize while (true) { @@ -2501,7 +2508,14 @@ Parser::ParseObjCMessageExpressionBody(SourceLocation LBracLoc, return ExprError(); } - ExprResult Res(ParseAssignmentExpression()); + ExprResult Expr; + if (getLangOpts().CPlusPlus11 && Tok.is(tok::l_brace)) { + Diag(Tok, diag::warn_cxx98_compat_generalized_initializer_lists); + Expr = ParseBraceInitializer(); + } else + Expr = ParseAssignmentExpression(); + + ExprResult Res(Expr); if (Res.isInvalid()) { // 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 @@ -2747,7 +2761,9 @@ ExprResult Parser::ParseObjCDictionaryLiteral(SourceLocation AtLoc) { if (Tok.is(tok::colon)) { ConsumeToken(); } else { - return ExprError(Diag(Tok, diag::err_expected_colon)); + Diag(Tok, diag::err_expected_colon); + SkipUntil(tok::r_brace); + return ExprError(); } ExprResult ValueExpr(ParseAssignmentExpression()); diff --git a/lib/Parse/ParsePragma.cpp b/lib/Parse/ParsePragma.cpp index dc6b3ed4fa9..3d1249aa684 100644 --- a/lib/Parse/ParsePragma.cpp +++ b/lib/Parse/ParsePragma.cpp @@ -15,6 +15,8 @@ #include "clang/Lex/Preprocessor.h" #include "clang/Parse/ParseDiagnostic.h" #include "clang/Parse/Parser.h" +#include "clang/Sema/Scope.h" +#include "llvm/ADT/StringSwitch.h" using namespace clang; /// \brief Handle the annotation token produced for #pragma unused(...) @@ -122,6 +124,33 @@ void Parser::HandlePragmaFPContract() { ConsumeToken(); // The annotation token. } +StmtResult Parser::HandlePragmaCaptured() +{ + assert(Tok.is(tok::annot_pragma_captured)); + ConsumeToken(); + + if (Tok.isNot(tok::l_brace)) { + PP.Diag(Tok, diag::err_expected_lbrace); + return StmtError(); + } + + SourceLocation Loc = Tok.getLocation(); + + ParseScope CapturedRegionScope(this, Scope::FnScope | Scope::DeclScope); + Actions.ActOnCapturedRegionStart(Loc, getCurScope(), CR_Default, + /*NumParams=*/1); + + StmtResult R = ParseCompoundStatement(); + CapturedRegionScope.Exit(); + + if (R.isInvalid()) { + Actions.ActOnCapturedRegionError(); + return StmtError(); + } + + return Actions.ActOnCapturedRegionEnd(R.get()); +} + namespace { typedef llvm::PointerIntPair OpenCLExtData; } @@ -151,6 +180,8 @@ void Parser::HandlePragmaOpenCLExtension() { } } + + // #pragma GCC visibility comes in two variants: // 'push' '(' [visibility] ')' // 'pop' @@ -762,3 +793,68 @@ PragmaOpenMPHandler::HandlePragma(Preprocessor &PP, PP.EnterTokenStream(Toks, Pragma.size(), /*DisableMacroExpansion=*/true, /*OwnsTokens=*/true); } + +/// \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. +void PragmaCommentHandler::HandlePragma(Preprocessor &PP, + PragmaIntroducerKind Introducer, + Token &Tok) { + SourceLocation CommentLoc = Tok.getLocation(); + PP.Lex(Tok); + if (Tok.isNot(tok::l_paren)) { + PP.Diag(CommentLoc, diag::err_pragma_comment_malformed); + return; + } + + // Read the identifier. + PP.Lex(Tok); + if (Tok.isNot(tok::identifier)) { + PP.Diag(CommentLoc, diag::err_pragma_comment_malformed); + return; + } + + // Verify that this is one of the 5 whitelisted options. + // FIXME: warn that 'exestr' is deprecated. + const IdentifierInfo *II = Tok.getIdentifierInfo(); + if (!II->isStr("compiler") && !II->isStr("exestr") && !II->isStr("lib") && + !II->isStr("linker") && !II->isStr("user")) { + PP.Diag(Tok.getLocation(), diag::err_pragma_comment_unknown_kind); + return; + } + + // Read the optional string if present. + PP.Lex(Tok); + std::string ArgumentString; + if (Tok.is(tok::comma) && !PP.LexStringLiteral(Tok, ArgumentString, + "pragma comment", + /*MacroExpansion=*/true)) + return; + + // FIXME: If the kind is "compiler" warn if the string is present (it is + // ignored). + // FIXME: 'lib' requires a comment string. + // FIXME: 'linker' requires a comment string, and has a specific list of + // things that are allowable. + + if (Tok.isNot(tok::r_paren)) { + PP.Diag(Tok.getLocation(), diag::err_pragma_comment_malformed); + return; + } + PP.Lex(Tok); // eat the r_paren. + + if (Tok.isNot(tok::eod)) { + PP.Diag(Tok.getLocation(), diag::err_pragma_comment_malformed); + return; + } + + // If the pragma is lexically sound, notify any interested PPCallbacks. + if (PP.getPPCallbacks()) + PP.getPPCallbacks()->PragmaComment(CommentLoc, II, ArgumentString); +} diff --git a/lib/Parse/ParsePragma.h b/lib/Parse/ParsePragma.h index 841a60be7ba..d9560f3181d 100644 --- a/lib/Parse/ParsePragma.h +++ b/lib/Parse/ParsePragma.h @@ -113,6 +113,14 @@ class PragmaOpenMPHandler : public PragmaHandler { Token &FirstToken); }; +/// PragmaCommentHandler - "\#pragma comment ...". +class PragmaCommentHandler : public PragmaHandler { +public: + PragmaCommentHandler() : PragmaHandler("comment") {} + virtual void HandlePragma(Preprocessor &PP, PragmaIntroducerKind Introducer, + Token &FirstToken); +}; + } // end namespace clang #endif diff --git a/lib/Parse/ParseStmt.cpp b/lib/Parse/ParseStmt.cpp index 355f3694bb6..43b6965d314 100644 --- a/lib/Parse/ParseStmt.cpp +++ b/lib/Parse/ParseStmt.cpp @@ -14,13 +14,26 @@ #include "clang/Parse/Parser.h" #include "RAIIObjectsForParser.h" +#include "clang/AST/ASTContext.h" #include "clang/Basic/Diagnostic.h" #include "clang/Basic/PrettyStackTrace.h" #include "clang/Basic/SourceManager.h" +#include "clang/Basic/TargetInfo.h" #include "clang/Sema/DeclSpec.h" #include "clang/Sema/PrettyDeclStackTrace.h" #include "clang/Sema/Scope.h" #include "clang/Sema/TypoCorrection.h" +#include "llvm/MC/MCAsmInfo.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCObjectFileInfo.h" +#include "llvm/MC/MCParser/MCAsmParser.h" +#include "llvm/MC/MCRegisterInfo.h" +#include "llvm/MC/MCStreamer.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/MC/MCTargetAsmParser.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Support/TargetSelect.h" #include "llvm/ADT/SmallString.h" using namespace clang; @@ -289,6 +302,9 @@ Parser::ParseStatementOrDeclarationAfterAttributes(StmtVector &Stmts, HandlePragmaOpenCLExtension(); return StmtEmpty(); + case tok::annot_pragma_captured: + return HandlePragmaCaptured(); + case tok::annot_pragma_openmp: SourceLocation DeclStart = Tok.getLocation(); DeclGroupPtrTy Res = ParseOpenMPDeclarativeDirective(); @@ -1660,6 +1676,281 @@ StmtResult Parser::ParseReturnStatement() { return Actions.ActOnReturnStmt(ReturnLoc, R.take()); } +namespace { + class ClangAsmParserCallback : public llvm::MCAsmParserSemaCallback { + Parser &TheParser; + SourceLocation AsmLoc; + StringRef AsmString; + + /// The tokens we streamed into AsmString and handed off to MC. + ArrayRef AsmToks; + + /// The offset of each token in AsmToks within AsmString. + ArrayRef AsmTokOffsets; + + public: + ClangAsmParserCallback(Parser &P, SourceLocation Loc, + StringRef AsmString, + ArrayRef Toks, + ArrayRef Offsets) + : TheParser(P), AsmLoc(Loc), AsmString(AsmString), + AsmToks(Toks), AsmTokOffsets(Offsets) { + assert(AsmToks.size() == AsmTokOffsets.size()); + } + + void *LookupInlineAsmIdentifier(StringRef &LineBuf, + InlineAsmIdentifierInfo &Info, + bool IsUnevaluatedContext) { + // Collect the desired tokens. + SmallVector LineToks; + const Token *FirstOrigToken = 0; + findTokensForString(LineBuf, LineToks, FirstOrigToken); + + unsigned NumConsumedToks; + ExprResult Result = + TheParser.ParseMSAsmIdentifier(LineToks, NumConsumedToks, &Info, + IsUnevaluatedContext); + + // If we consumed the entire line, tell MC that. + // Also do this if we consumed nothing as a way of reporting failure. + if (NumConsumedToks == 0 || NumConsumedToks == LineToks.size()) { + // By not modifying LineBuf, we're implicitly consuming it all. + + // Otherwise, consume up to the original tokens. + } else { + assert(FirstOrigToken && "not using original tokens?"); + + // Since we're using original tokens, apply that offset. + assert(FirstOrigToken[NumConsumedToks].getLocation() + == LineToks[NumConsumedToks].getLocation()); + unsigned FirstIndex = FirstOrigToken - AsmToks.begin(); + unsigned LastIndex = FirstIndex + NumConsumedToks - 1; + + // The total length we've consumed is the relative offset + // of the last token we consumed plus its length. + unsigned TotalOffset = (AsmTokOffsets[LastIndex] + + AsmToks[LastIndex].getLength() + - AsmTokOffsets[FirstIndex]); + LineBuf = LineBuf.substr(0, TotalOffset); + } + + // Initialize the "decl" with the lookup result. + Info.OpDecl = static_cast(Result.take()); + return Info.OpDecl; + } + + bool LookupInlineAsmField(StringRef Base, StringRef Member, + unsigned &Offset) { + return TheParser.getActions().LookupInlineAsmField(Base, Member, + Offset, AsmLoc); + } + + static void DiagHandlerCallback(const llvm::SMDiagnostic &D, + void *Context) { + ((ClangAsmParserCallback*) Context)->handleDiagnostic(D); + } + + private: + /// Collect the appropriate tokens for the given string. + void findTokensForString(StringRef Str, SmallVectorImpl &TempToks, + const Token *&FirstOrigToken) const { + // For now, assert that the string we're working with is a substring + // of what we gave to MC. This lets us use the original tokens. + assert(!std::less()(Str.begin(), AsmString.begin()) && + !std::less()(AsmString.end(), Str.end())); + + // Try to find a token whose offset matches the first token. + unsigned FirstCharOffset = Str.begin() - AsmString.begin(); + const unsigned *FirstTokOffset + = std::lower_bound(AsmTokOffsets.begin(), AsmTokOffsets.end(), + FirstCharOffset); + + // For now, assert that the start of the string exactly + // corresponds to the start of a token. + assert(*FirstTokOffset == FirstCharOffset); + + // Use all the original tokens for this line. (We assume the + // end of the line corresponds cleanly to a token break.) + unsigned FirstTokIndex = FirstTokOffset - AsmTokOffsets.begin(); + FirstOrigToken = &AsmToks[FirstTokIndex]; + unsigned LastCharOffset = Str.end() - AsmString.begin(); + for (unsigned i = FirstTokIndex, e = AsmTokOffsets.size(); i != e; ++i) { + if (AsmTokOffsets[i] >= LastCharOffset) break; + TempToks.push_back(AsmToks[i]); + } + } + + void handleDiagnostic(const llvm::SMDiagnostic &D) { + // Compute an offset into the inline asm buffer. + // FIXME: This isn't right if .macro is involved (but hopefully, no + // real-world code does that). + const llvm::SourceMgr &LSM = *D.getSourceMgr(); + const llvm::MemoryBuffer *LBuf = + LSM.getMemoryBuffer(LSM.FindBufferContainingLoc(D.getLoc())); + unsigned Offset = D.getLoc().getPointer() - LBuf->getBufferStart(); + + // Figure out which token that offset points into. + const unsigned *TokOffsetPtr = + std::lower_bound(AsmTokOffsets.begin(), AsmTokOffsets.end(), Offset); + unsigned TokIndex = TokOffsetPtr - AsmTokOffsets.begin(); + unsigned TokOffset = *TokOffsetPtr; + + // If we come up with an answer which seems sane, use it; otherwise, + // just point at the __asm keyword. + // FIXME: Assert the answer is sane once we handle .macro correctly. + SourceLocation Loc = AsmLoc; + if (TokIndex < AsmToks.size()) { + const Token &Tok = AsmToks[TokIndex]; + Loc = Tok.getLocation(); + Loc = Loc.getLocWithOffset(Offset - TokOffset); + } + TheParser.Diag(Loc, diag::err_inline_ms_asm_parsing) + << D.getMessage(); + } + }; +} + +/// Parse an identifier in an MS-style inline assembly block. +/// +/// \param CastInfo - a void* so that we don't have to teach Parser.h +/// about the actual type. +ExprResult Parser::ParseMSAsmIdentifier(llvm::SmallVectorImpl &LineToks, + unsigned &NumLineToksConsumed, + void *CastInfo, + bool IsUnevaluatedContext) { + llvm::InlineAsmIdentifierInfo &Info = + *(llvm::InlineAsmIdentifierInfo *) CastInfo; + + // Push a fake token on the end so that we don't overrun the token + // stream. We use ';' because it expression-parsing should never + // overrun it. + const tok::TokenKind EndOfStream = tok::semi; + Token EndOfStreamTok; + EndOfStreamTok.startToken(); + EndOfStreamTok.setKind(EndOfStream); + LineToks.push_back(EndOfStreamTok); + + // Also copy the current token over. + LineToks.push_back(Tok); + + PP.EnterTokenStream(LineToks.begin(), + LineToks.size(), + /*disable macros*/ true, + /*owns tokens*/ false); + + // Clear the current token and advance to the first token in LineToks. + ConsumeAnyToken(); + + // Parse an optional scope-specifier if we're in C++. + CXXScopeSpec SS; + if (getLangOpts().CPlusPlus) { + ParseOptionalCXXScopeSpecifier(SS, ParsedType(), /*EnteringContext=*/false); + } + + // Require an identifier here. + SourceLocation TemplateKWLoc; + UnqualifiedId Id; + bool Invalid = ParseUnqualifiedId(SS, + /*EnteringContext=*/false, + /*AllowDestructorName=*/false, + /*AllowConstructorName=*/false, + /*ObjectType=*/ ParsedType(), + TemplateKWLoc, + Id); + + // If we've run into the poison token we inserted before, or there + // was a parsing error, then claim the entire line. + if (Invalid || Tok.is(EndOfStream)) { + NumLineToksConsumed = LineToks.size() - 2; + + // Otherwise, claim up to the start of the next token. + } else { + // Figure out how many tokens we are into LineToks. + unsigned LineIndex = 0; + while (LineToks[LineIndex].getLocation() != Tok.getLocation()) { + LineIndex++; + assert(LineIndex < LineToks.size() - 2); // we added two extra tokens + } + + NumLineToksConsumed = LineIndex; + } + + // Finally, restore the old parsing state by consuming all the + // tokens we staged before, implicitly killing off the + // token-lexer we pushed. + for (unsigned n = LineToks.size() - 2 - NumLineToksConsumed; n != 0; --n) { + ConsumeAnyToken(); + } + ConsumeToken(EndOfStream); + + // Leave LineToks in its original state. + LineToks.pop_back(); + LineToks.pop_back(); + + // Perform the lookup. + return Actions.LookupInlineAsmIdentifier(SS, TemplateKWLoc, Id, Info, + IsUnevaluatedContext); +} + +/// Turn a sequence of our tokens back into a string that we can hand +/// to the MC asm parser. +static bool buildMSAsmString(Preprocessor &PP, + SourceLocation AsmLoc, + ArrayRef AsmToks, + SmallVectorImpl &TokOffsets, + SmallString<512> &Asm) { + assert (!AsmToks.empty() && "Didn't expect an empty AsmToks!"); + + // Is this the start of a new assembly statement? + bool isNewStatement = true; + + for (unsigned i = 0, e = AsmToks.size(); i < e; ++i) { + const Token &Tok = AsmToks[i]; + + // Start each new statement with a newline and a tab. + if (!isNewStatement && + (Tok.is(tok::kw_asm) || Tok.isAtStartOfLine())) { + Asm += "\n\t"; + isNewStatement = true; + } + + // Preserve the existence of leading whitespace except at the + // start of a statement. + if (!isNewStatement && Tok.hasLeadingSpace()) + Asm += ' '; + + // Remember the offset of this token. + TokOffsets.push_back(Asm.size()); + + // Don't actually write '__asm' into the assembly stream. + if (Tok.is(tok::kw_asm)) { + // Complain about __asm at the end of the stream. + if (i + 1 == e) { + PP.Diag(AsmLoc, diag::err_asm_empty); + return true; + } + + continue; + } + + // Append the spelling of the token. + SmallString<32> SpellingBuffer; + bool SpellingInvalid = false; + Asm += PP.getSpelling(Tok, SpellingBuffer, &SpellingInvalid); + assert(!SpellingInvalid && "spelling was invalid after correct parse?"); + + // We are no longer at the start of a statement. + isNewStatement = false; + } + + // Ensure that the buffer is null-terminated. + Asm.push_back('\0'); + Asm.pop_back(); + + assert(TokOffsets.size() == AsmToks.size()); + return false; +} + /// ParseMicrosoftAsmStatement. When -fms-extensions/-fasm-blocks is enabled, /// this routine is called to collect the tokens for an MS asm statement. /// @@ -1768,9 +2059,114 @@ StmtResult Parser::ParseMicrosoftAsmStatement(SourceLocation AsmLoc) { return StmtError(); } + // Okay, prepare to use MC to parse the assembly. + SmallVector ConstraintRefs; + SmallVector Exprs; + SmallVector ClobberRefs; + + // We need an actual supported target. + llvm::Triple TheTriple = Actions.Context.getTargetInfo().getTriple(); + llvm::Triple::ArchType ArchTy = TheTriple.getArch(); + bool UnsupportedArch = (ArchTy != llvm::Triple::x86 && + ArchTy != llvm::Triple::x86_64); + if (UnsupportedArch) + Diag(AsmLoc, diag::err_msasm_unsupported_arch) << TheTriple.getArchName(); + + // If we don't support assembly, or the assembly is empty, we don't + // need to instantiate the AsmParser, etc. + if (UnsupportedArch || AsmToks.empty()) { + return Actions.ActOnMSAsmStmt(AsmLoc, LBraceLoc, AsmToks, StringRef(), + /*NumOutputs*/ 0, /*NumInputs*/ 0, + ConstraintRefs, ClobberRefs, Exprs, EndLoc); + } + + // Expand the tokens into a string buffer. + SmallString<512> AsmString; + SmallVector TokOffsets; + if (buildMSAsmString(PP, AsmLoc, AsmToks, TokOffsets, AsmString)) + return StmtError(); + + // Find the target and create the target specific parser. + std::string Error; + const std::string &TT = TheTriple.getTriple(); + const llvm::Target *TheTarget = llvm::TargetRegistry::lookupTarget(TT, Error); + + OwningPtr MAI(TheTarget->createMCAsmInfo(TT)); + OwningPtr MRI(TheTarget->createMCRegInfo(TT)); + OwningPtr MOFI(new llvm::MCObjectFileInfo()); + OwningPtr + STI(TheTarget->createMCSubtargetInfo(TT, "", "")); + + llvm::SourceMgr TempSrcMgr; + llvm::MCContext Ctx(*MAI, *MRI, MOFI.get(), &TempSrcMgr); + llvm::MemoryBuffer *Buffer = + llvm::MemoryBuffer::getMemBuffer(AsmString, ""); + + // Tell SrcMgr about this buffer, which is what the parser will pick up. + TempSrcMgr.AddNewSourceBuffer(Buffer, llvm::SMLoc()); + + OwningPtr Str(createNullStreamer(Ctx)); + OwningPtr + Parser(createMCAsmParser(TempSrcMgr, Ctx, *Str.get(), *MAI)); + OwningPtr + TargetParser(TheTarget->createMCAsmParser(*STI, *Parser)); + + // Get the instruction descriptor. + const llvm::MCInstrInfo *MII = TheTarget->createMCInstrInfo(); + llvm::MCInstPrinter *IP = + TheTarget->createMCInstPrinter(1, *MAI, *MII, *MRI, *STI); + + // Change to the Intel dialect. + Parser->setAssemblerDialect(1); + Parser->setTargetParser(*TargetParser.get()); + Parser->setParsingInlineAsm(true); + TargetParser->setParsingInlineAsm(true); + + ClangAsmParserCallback Callback(*this, AsmLoc, AsmString, + AsmToks, TokOffsets); + TargetParser->setSemaCallback(&Callback); + TempSrcMgr.setDiagHandler(ClangAsmParserCallback::DiagHandlerCallback, + &Callback); + + unsigned NumOutputs; + unsigned NumInputs; + std::string AsmStringIR; + SmallVector, 4> OpExprs; + SmallVector Constraints; + SmallVector Clobbers; + if (Parser->parseMSInlineAsm(AsmLoc.getPtrEncoding(), AsmStringIR, + NumOutputs, NumInputs, OpExprs, Constraints, + Clobbers, MII, IP, Callback)) + return StmtError(); + + // Build the vector of clobber StringRefs. + unsigned NumClobbers = Clobbers.size(); + ClobberRefs.resize(NumClobbers); + for (unsigned i = 0; i != NumClobbers; ++i) + ClobberRefs[i] = StringRef(Clobbers[i]); + + // Recast the void pointers and build the vector of constraint StringRefs. + unsigned NumExprs = NumOutputs + NumInputs; + ConstraintRefs.resize(NumExprs); + Exprs.resize(NumExprs); + for (unsigned i = 0, e = NumExprs; i != e; ++i) { + Expr *OpExpr = static_cast(OpExprs[i].first); + if (!OpExpr) + return StmtError(); + + // Need address of variable. + if (OpExprs[i].second) + OpExpr = Actions.BuildUnaryOp(getCurScope(), AsmLoc, UO_AddrOf, OpExpr) + .take(); + + ConstraintRefs[i] = StringRef(Constraints[i]); + Exprs[i] = OpExpr; + } + // FIXME: We should be passing source locations for better diagnostics. - return Actions.ActOnMSAsmStmt(AsmLoc, LBraceLoc, - llvm::makeArrayRef(AsmToks), EndLoc); + return Actions.ActOnMSAsmStmt(AsmLoc, LBraceLoc, AsmToks, AsmStringIR, + NumOutputs, NumInputs, + ConstraintRefs, ClobberRefs, Exprs, EndLoc); } /// ParseAsmStatement - Parse a GNU extended asm statement. diff --git a/lib/Parse/ParseTemplate.cpp b/lib/Parse/ParseTemplate.cpp index f14666922b9..84b7df7295f 100644 --- a/lib/Parse/ParseTemplate.cpp +++ b/lib/Parse/ParseTemplate.cpp @@ -39,28 +39,7 @@ Parser::ParseDeclarationStartingWithTemplate(unsigned Context, AccessAttrs); } -/// \brief RAII class that manages the template parameter depth. -namespace { - class TemplateParameterDepthCounter { - unsigned &Depth; - unsigned AddedLevels; - - public: - explicit TemplateParameterDepthCounter(unsigned &Depth) - : Depth(Depth), AddedLevels(0) { } - - ~TemplateParameterDepthCounter() { - Depth -= AddedLevels; - } - void operator++() { - ++Depth; - ++AddedLevels; - } - - operator unsigned() const { return Depth; } - }; -} /// \brief Parse a template declaration or an explicit specialization. /// @@ -117,7 +96,8 @@ Parser::ParseTemplateDeclarationOrSpecialization(unsigned Context, bool isSpecialization = true; bool LastParamListWasEmpty = false; TemplateParameterLists ParamLists; - TemplateParameterDepthCounter Depth(TemplateParameterDepth); + TemplateParameterDepthRAII CurTemplateDepthTracker(TemplateParameterDepth); + do { // Consume the 'export', if any. SourceLocation ExportLoc; @@ -137,8 +117,8 @@ Parser::ParseTemplateDeclarationOrSpecialization(unsigned Context, // Parse the '<' template-parameter-list '>' SourceLocation LAngleLoc, RAngleLoc; SmallVector TemplateParams; - if (ParseTemplateParameters(Depth, TemplateParams, LAngleLoc, - RAngleLoc)) { + if (ParseTemplateParameters(CurTemplateDepthTracker.getDepth(), + TemplateParams, LAngleLoc, RAngleLoc)) { // Skip until the semi-colon or a }. SkipUntil(tok::r_brace, true, true); if (Tok.is(tok::semi)) @@ -147,14 +127,15 @@ Parser::ParseTemplateDeclarationOrSpecialization(unsigned Context, } ParamLists.push_back( - Actions.ActOnTemplateParameterList(Depth, ExportLoc, + Actions.ActOnTemplateParameterList(CurTemplateDepthTracker.getDepth(), + ExportLoc, TemplateLoc, LAngleLoc, TemplateParams.data(), TemplateParams.size(), RAngleLoc)); if (!TemplateParams.empty()) { isSpecialization = false; - ++Depth; + ++CurTemplateDepthTracker; } else { LastParamListWasEmpty = true; } @@ -1168,6 +1149,9 @@ bool Parser::IsTemplateArgumentList(unsigned Skip) { /// template-argument-list ',' template-argument bool Parser::ParseTemplateArgumentList(TemplateArgList &TemplateArgs) { + // Template argument lists are constant-evaluation contexts. + EnterExpressionEvaluationContext EvalContext(Actions,Sema::ConstantEvaluated); + while (true) { ParsedTemplateArgument Arg = ParseTemplateArgument(); if (Tok.is(tok::ellipsis)) { @@ -1249,53 +1233,56 @@ void Parser::ParseLateTemplatedFuncDef(LateParsedTemplatedFunction &LMT) { return; // Get the FunctionDecl. - FunctionDecl *FD = 0; - if (FunctionTemplateDecl *FunTmpl = dyn_cast(LMT.D)) - FD = FunTmpl->getTemplatedDecl(); - else - FD = cast(LMT.D); + FunctionTemplateDecl *FunTmplD = dyn_cast(LMT.D); + FunctionDecl *FunD = + FunTmplD ? FunTmplD->getTemplatedDecl() : cast(LMT.D); + // Track template parameter depth. + TemplateParameterDepthRAII CurTemplateDepthTracker(TemplateParameterDepth); // To restore the context after late parsing. Sema::ContextRAII GlobalSavedContext(Actions, Actions.CurContext); SmallVector TemplateParamScopeStack; - DeclaratorDecl* Declarator = dyn_cast(FD); - if (Declarator && Declarator->getNumTemplateParameterLists() != 0) { - TemplateParamScopeStack.push_back(new ParseScope(this, Scope::TemplateParamScope)); - Actions.ActOnReenterDeclaratorTemplateScope(getCurScope(), Declarator); - Actions.ActOnReenterTemplateScope(getCurScope(), LMT.D); - } else { - // Get the list of DeclContext to reenter. - SmallVector DeclContextToReenter; - DeclContext *DD = FD->getLexicalParent(); - while (DD && !DD->isTranslationUnit()) { - DeclContextToReenter.push_back(DD); - DD = DD->getLexicalParent(); - } - // Reenter template scopes from outmost to innermost. - SmallVector::reverse_iterator II = - DeclContextToReenter.rbegin(); - for (; II != DeclContextToReenter.rend(); ++II) { - if (ClassTemplatePartialSpecializationDecl* MD = - dyn_cast_or_null(*II)) { - TemplateParamScopeStack.push_back(new ParseScope(this, - Scope::TemplateParamScope)); - Actions.ActOnReenterTemplateScope(getCurScope(), MD); - } else if (CXXRecordDecl* MD = dyn_cast_or_null(*II)) { - TemplateParamScopeStack.push_back(new ParseScope(this, - Scope::TemplateParamScope, - MD->getDescribedClassTemplate() != 0 )); - Actions.ActOnReenterTemplateScope(getCurScope(), - MD->getDescribedClassTemplate()); - } - TemplateParamScopeStack.push_back(new ParseScope(this, Scope::DeclScope)); - Actions.PushDeclContext(Actions.getCurScope(), *II); + // Get the list of DeclContexts to reenter. + SmallVector DeclContextsToReenter; + DeclContext *DD = FunD->getLexicalParent(); + while (DD && !DD->isTranslationUnit()) { + DeclContextsToReenter.push_back(DD); + DD = DD->getLexicalParent(); + } + + // Reenter template scopes from outermost to innermost. + SmallVector::reverse_iterator II = + DeclContextsToReenter.rbegin(); + for (; II != DeclContextsToReenter.rend(); ++II) { + if (ClassTemplatePartialSpecializationDecl *MD = + dyn_cast_or_null(*II)) { + TemplateParamScopeStack.push_back( + new ParseScope(this, Scope::TemplateParamScope)); + Actions.ActOnReenterTemplateScope(getCurScope(), MD); + ++CurTemplateDepthTracker; + } else if (CXXRecordDecl *MD = dyn_cast_or_null(*II)) { + bool ManageScope = MD->getDescribedClassTemplate() != 0; + TemplateParamScopeStack.push_back( + new ParseScope(this, Scope::TemplateParamScope, ManageScope)); + Actions.ActOnReenterTemplateScope(getCurScope(), + MD->getDescribedClassTemplate()); + ++CurTemplateDepthTracker; } - TemplateParamScopeStack.push_back(new ParseScope(this, - Scope::TemplateParamScope)); - Actions.ActOnReenterTemplateScope(getCurScope(), LMT.D); + TemplateParamScopeStack.push_back(new ParseScope(this, Scope::DeclScope)); + Actions.PushDeclContext(Actions.getCurScope(), *II); } + TemplateParamScopeStack.push_back( + new ParseScope(this, Scope::TemplateParamScope)); + + DeclaratorDecl *Declarator = dyn_cast(FunD); + if (Declarator && Declarator->getNumTemplateParameterLists() != 0) { + Actions.ActOnReenterDeclaratorTemplateScope(getCurScope(), Declarator); + ++CurTemplateDepthTracker; + } + Actions.ActOnReenterTemplateScope(getCurScope(), LMT.D); + ++CurTemplateDepthTracker; assert(!LMT.Toks.empty() && "Empty body!"); @@ -1314,15 +1301,9 @@ void Parser::ParseLateTemplatedFuncDef(LateParsedTemplatedFunction &LMT) { ParseScope FnScope(this, Scope::FnScope|Scope::DeclScope); // Recreate the containing function DeclContext. - Sema::ContextRAII FunctionSavedContext(Actions, Actions.getContainingDC(FD)); - - if (FunctionTemplateDecl *FunctionTemplate - = dyn_cast_or_null(LMT.D)) - Actions.ActOnStartOfFunctionDef(getCurScope(), - FunctionTemplate->getTemplatedDecl()); - if (FunctionDecl *Function = dyn_cast_or_null(LMT.D)) - Actions.ActOnStartOfFunctionDef(getCurScope(), Function); + Sema::ContextRAII FunctionSavedContext(Actions, Actions.getContainingDC(FunD)); + Actions.ActOnStartOfFunctionDef(getCurScope(), FunD); if (Tok.is(tok::kw_try)) { ParseFunctionTryBlock(LMT.D, FnScope); @@ -1333,8 +1314,12 @@ void Parser::ParseLateTemplatedFuncDef(LateParsedTemplatedFunction &LMT) { Actions.ActOnDefaultCtorInitializers(LMT.D); if (Tok.is(tok::l_brace)) { + assert((!FunTmplD || FunTmplD->getTemplateParameters()->getDepth() < + TemplateParameterDepth) && + "TemplateParameterDepth should be greater than the depth of " + "current template being instantiated!"); ParseFunctionStatementBody(LMT.D, FnScope); - Actions.MarkAsLateParsedTemplate(FD, false); + Actions.MarkAsLateParsedTemplate(FunD, false); } else Actions.ActOnFinishFunctionBody(LMT.D, 0); } diff --git a/lib/Parse/ParseTentative.cpp b/lib/Parse/ParseTentative.cpp index 5e0ef2b83f6..dff3b64c5b3 100644 --- a/lib/Parse/ParseTentative.cpp +++ b/lib/Parse/ParseTentative.cpp @@ -837,11 +837,12 @@ Parser::isExpressionOrTypeSpecifierSimple(tok::TokenKind Kind) { case tok::kw_char16_t: case tok::kw_char32_t: case tok::kw___underlying_type: - case tok::kw_thread_local: case tok::kw__Decimal32: case tok::kw__Decimal64: case tok::kw__Decimal128: case tok::kw___thread: + case tok::kw_thread_local: + case tok::kw__Thread_local: case tok::kw_typeof: case tok::kw___cdecl: case tok::kw___stdcall: @@ -893,7 +894,7 @@ bool Parser::isTentativelyDeclared(IdentifierInfo *II) { /// function-specifier /// 'friend' /// 'typedef' -/// [C++0x] 'constexpr' +/// [C++11] 'constexpr' /// [GNU] attributes declaration-specifiers[opt] /// /// storage-class-specifier: @@ -903,6 +904,8 @@ bool Parser::isTentativelyDeclared(IdentifierInfo *II) { /// 'mutable' /// 'auto' /// [GNU] '__thread' +/// [C++11] 'thread_local' +/// [C11] '_Thread_local' /// /// function-specifier: /// 'inline' @@ -937,8 +940,9 @@ bool Parser::isTentativelyDeclared(IdentifierInfo *II) { /// 'void' /// [GNU] typeof-specifier /// [GNU] '_Complex' -/// [C++0x] 'auto' [TODO] -/// [C++0x] 'decltype' ( expression ) +/// [C++11] 'auto' +/// [C++11] 'decltype' ( expression ) +/// [C++1y] 'decltype' ( 'auto' ) /// /// type-name: /// class-name @@ -1073,6 +1077,8 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult, case tok::kw_mutable: case tok::kw_auto: case tok::kw___thread: + case tok::kw_thread_local: + case tok::kw__Thread_local: // function-specifier case tok::kw_inline: case tok::kw_virtual: diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp index 1ebba3e67a8..455139b881a 100644 --- a/lib/Parse/Parser.cpp +++ b/lib/Parse/Parser.cpp @@ -102,6 +102,11 @@ Parser::Parser(Preprocessor &pp, Sema &actions, bool skipFunctionBodies) OpenMPHandler.reset(new PragmaNoOpenMPHandler()); PP.AddPragmaHandler(OpenMPHandler.get()); + if (getLangOpts().MicrosoftExt) { + MSCommentHandler.reset(new PragmaCommentHandler()); + PP.AddPragmaHandler(MSCommentHandler.get()); + } + CommentSemaHandler.reset(new ActionCommentHandler(actions)); PP.addCommentHandler(CommentSemaHandler.get()); @@ -436,6 +441,11 @@ Parser::~Parser() { PP.RemovePragmaHandler(OpenMPHandler.get()); OpenMPHandler.reset(); + if (getLangOpts().MicrosoftExt) { + PP.RemovePragmaHandler(MSCommentHandler.get()); + MSCommentHandler.reset(); + } + PP.RemovePragmaHandler("STDC", FPContractHandler.get()); FPContractHandler.reset(); @@ -1141,8 +1151,8 @@ void Parser::ParseKNRParamDeclarations(Declarator &D) { diag::err_invalid_storage_class_in_func_decl); DS.ClearStorageClassSpecs(); } - if (DS.isThreadSpecified()) { - Diag(DS.getThreadSpecLoc(), + if (DS.getThreadStorageClassSpec() != DeclSpec::TSCS_unspecified) { + Diag(DS.getThreadStorageClassSpecLoc(), diag::err_invalid_storage_class_in_func_decl); DS.ClearStorageClassSpecs(); } diff --git a/lib/Rewrite/Frontend/FixItRewriter.cpp b/lib/Rewrite/Frontend/FixItRewriter.cpp index a3bbdcf6ebd..166c607d020 100644 --- a/lib/Rewrite/Frontend/FixItRewriter.cpp +++ b/lib/Rewrite/Frontend/FixItRewriter.cpp @@ -197,9 +197,4 @@ void FixItRewriter::Diag(SourceLocation Loc, unsigned DiagID) { Diags.setClient(this); } -DiagnosticConsumer *FixItRewriter::clone(DiagnosticsEngine &Diags) const { - return new FixItRewriter(Diags, Diags.getSourceManager(), - Rewrite.getLangOpts(), FixItOpts); -} - FixItOptions::~FixItOptions() {} diff --git a/lib/Rewrite/Frontend/InclusionRewriter.cpp b/lib/Rewrite/Frontend/InclusionRewriter.cpp index d95fb073b1e..878be84224a 100644 --- a/lib/Rewrite/Frontend/InclusionRewriter.cpp +++ b/lib/Rewrite/Frontend/InclusionRewriter.cpp @@ -15,7 +15,9 @@ #include "clang/Rewrite/Frontend/Rewriters.h" #include "clang/Basic/SourceManager.h" #include "clang/Frontend/PreprocessorOutputOptions.h" +#include "clang/Lex/HeaderSearch.h" #include "clang/Lex/Preprocessor.h" +#include "llvm/ADT/SmallString.h" #include "llvm/Support/raw_ostream.h" using namespace clang; @@ -27,10 +29,11 @@ class InclusionRewriter : public PPCallbacks { /// Information about which #includes were actually performed, /// created by preprocessor callbacks. struct FileChange { + const Module *Mod; SourceLocation From; FileID Id; SrcMgr::CharacteristicKind FileType; - FileChange(SourceLocation From) : From(From) { + FileChange(SourceLocation From, const Module *Mod) : Mod(Mod), From(From) { } }; Preprocessor &PP; ///< Used to find inclusion directives. @@ -65,6 +68,7 @@ class InclusionRewriter : public PPCallbacks { void WriteLineInfo(const char *Filename, int Line, SrcMgr::CharacteristicKind FileType, StringRef EOL, StringRef Extra = StringRef()); + void WriteImplicitModuleImport(const Module *Mod, StringRef EOL); void OutputContentUpTo(const MemoryBuffer &FromFile, unsigned &WriteFrom, unsigned WriteTo, StringRef EOL, int &lines, @@ -72,6 +76,9 @@ class InclusionRewriter : public PPCallbacks { void CommentOutDirective(Lexer &DirectivesLex, const Token &StartToken, const MemoryBuffer &FromFile, StringRef EOL, unsigned &NextToWrite, int &Lines); + bool HandleHasInclude(FileID FileId, Lexer &RawLex, + const DirectoryLookup *Lookup, Token &Tok, + bool &FileExists); const FileChange *FindFileChangeLocation(SourceLocation Loc) const; StringRef NextIdentifierName(Lexer &RawLex, Token &RawToken); }; @@ -117,6 +124,12 @@ void InclusionRewriter::WriteLineInfo(const char *Filename, int Line, OS << EOL; } +void InclusionRewriter::WriteImplicitModuleImport(const Module *Mod, + StringRef EOL) { + OS << "@import " << Mod->getFullModuleName() << ";" + << " /* clang -frewrite-includes: implicit import */" << EOL; +} + /// FileChanged - Whenever the preprocessor enters or exits a #include file /// it invokes this handler. void InclusionRewriter::FileChanged(SourceLocation Loc, @@ -157,13 +170,14 @@ void InclusionRewriter::InclusionDirective(SourceLocation HashLoc, const FileEntry * /*File*/, StringRef /*SearchPath*/, StringRef /*RelativePath*/, - const Module * /*Imported*/) { + const Module *Imported) { assert(LastInsertedFileChange == FileChanges.end() && "Another inclusion " "directive was found before the previous one was processed"); std::pair p = FileChanges.insert( - std::make_pair(HashLoc.getRawEncoding(), FileChange(HashLoc))); + std::make_pair(HashLoc.getRawEncoding(), FileChange(HashLoc, Imported))); assert(p.second && "Unexpected revisitation of the same include directive"); - LastInsertedFileChange = p.first; + if (!Imported) + LastInsertedFileChange = p.first; } /// Simple lookup for a SourceLocation (specifically one denoting the hash in @@ -245,6 +259,75 @@ StringRef InclusionRewriter::NextIdentifierName(Lexer &RawLex, return StringRef(); } +// Expand __has_include and __has_include_next if possible. If there's no +// definitive answer return false. +bool InclusionRewriter::HandleHasInclude( + FileID FileId, Lexer &RawLex, const DirectoryLookup *Lookup, Token &Tok, + bool &FileExists) { + // Lex the opening paren. + RawLex.LexFromRawLexer(Tok); + if (Tok.isNot(tok::l_paren)) + return false; + + RawLex.LexFromRawLexer(Tok); + + SmallString<128> FilenameBuffer; + StringRef Filename; + // Since the raw lexer doesn't give us angle_literals we have to parse them + // ourselves. + // FIXME: What to do if the file name is a macro? + if (Tok.is(tok::less)) { + RawLex.LexFromRawLexer(Tok); + + FilenameBuffer += '<'; + do { + if (Tok.is(tok::eod)) // Sanity check. + return false; + + if (Tok.is(tok::raw_identifier)) + PP.LookUpIdentifierInfo(Tok); + + // Get the string piece. + SmallVector TmpBuffer; + bool Invalid = false; + StringRef TmpName = PP.getSpelling(Tok, TmpBuffer, &Invalid); + if (Invalid) + return false; + + FilenameBuffer += TmpName; + + RawLex.LexFromRawLexer(Tok); + } while (Tok.isNot(tok::greater)); + + FilenameBuffer += '>'; + Filename = FilenameBuffer; + } else { + if (Tok.isNot(tok::string_literal)) + return false; + + bool Invalid = false; + Filename = PP.getSpelling(Tok, FilenameBuffer, &Invalid); + if (Invalid) + return false; + } + + // Lex the closing paren. + RawLex.LexFromRawLexer(Tok); + if (Tok.isNot(tok::r_paren)) + return false; + + // Now ask HeaderInfo if it knows about the header. + // FIXME: Subframeworks aren't handled here. Do we care? + bool isAngled = PP.GetIncludeFilenameSpelling(Tok.getLocation(), Filename); + const DirectoryLookup *CurDir; + const FileEntry *File = PP.getHeaderSearchInfo().LookupFile( + Filename, isAngled, 0, CurDir, + PP.getSourceManager().getFileEntryForID(FileId), 0, 0, 0, false); + + FileExists = File != 0; + return true; +} + /// 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, @@ -253,7 +336,7 @@ bool InclusionRewriter::Process(FileID FileId, bool Invalid; const MemoryBuffer &FromFile = *SM.getBuffer(FileId, &Invalid); if (Invalid) // invalid inclusion - return true; + return false; const char *FileName = FromFile.getBufferIdentifier(); Lexer RawLex(FileId, &FromFile, PP.getSourceManager(), PP.getLangOpts()); RawLex.SetCommentRetentionState(false); @@ -264,7 +347,7 @@ bool InclusionRewriter::Process(FileID FileId, WriteLineInfo(FileName, 1, FileType, EOL, " 1"); if (SM.getFileIDSize(FileId) == 0) - return true; + return false; // The next byte to be copied from the source file unsigned NextToWrite = 0; @@ -282,26 +365,31 @@ bool InclusionRewriter::Process(FileID FileId, RawLex.LexFromRawLexer(RawToken); if (RawToken.is(tok::raw_identifier)) PP.LookUpIdentifierInfo(RawToken); - if (RawToken.is(tok::identifier)) { + if (RawToken.is(tok::identifier) || RawToken.is(tok::kw_if)) { switch (RawToken.getIdentifierInfo()->getPPKeywordID()) { case tok::pp_include: case tok::pp_include_next: case tok::pp_import: { CommentOutDirective(RawLex, HashToken, FromFile, EOL, NextToWrite, Line); + StringRef LineInfoExtra; if (const FileChange *Change = FindFileChangeLocation( HashToken.getLocation())) { - // now include and recursively process the file - if (Process(Change->Id, Change->FileType)) + if (Change->Mod) { + WriteImplicitModuleImport(Change->Mod, EOL); + + // else now include and recursively process the file + } else 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); + LineInfoExtra = " 2"; + } + } + // fix up lineinfo (since commented out directive changed line + // numbers) for inclusions that were skipped due to header guards + WriteLineInfo(FileName, Line, FileType, EOL, LineInfoExtra); break; } case tok::pp_pragma: { @@ -323,6 +411,50 @@ bool InclusionRewriter::Process(FileID FileId, } break; } + case tok::pp_if: + case tok::pp_elif: + // Rewrite special builtin macros to avoid pulling in host details. + do { + // Walk over the directive. + RawLex.LexFromRawLexer(RawToken); + if (RawToken.is(tok::raw_identifier)) + PP.LookUpIdentifierInfo(RawToken); + + if (RawToken.is(tok::identifier)) { + bool HasFile; + SourceLocation Loc = RawToken.getLocation(); + + // Rewrite __has_include(x) + if (RawToken.getIdentifierInfo()->isStr("__has_include")) { + if (!HandleHasInclude(FileId, RawLex, 0, RawToken, HasFile)) + continue; + // Rewrite __has_include_next(x) + } else if (RawToken.getIdentifierInfo()->isStr( + "__has_include_next")) { + const DirectoryLookup *Lookup = PP.GetCurDirLookup(); + if (Lookup) + ++Lookup; + + if (!HandleHasInclude(FileId, RawLex, Lookup, RawToken, + HasFile)) + continue; + } else { + continue; + } + // Replace the macro with (0) or (1), followed by the commented + // out macro for reference. + OutputContentUpTo(FromFile, NextToWrite, SM.getFileOffset(Loc), + EOL, Line); + OS << '(' << (int) HasFile << ")/*"; + OutputContentUpTo(FromFile, NextToWrite, + SM.getFileOffset(RawToken.getLocation()) + + RawToken.getLength(), + EOL, Line); + OS << "*/"; + } + } while (RawToken.isNot(tok::eod)); + + break; default: break; } diff --git a/lib/Sema/AnalysisBasedWarnings.cpp b/lib/Sema/AnalysisBasedWarnings.cpp index 00d3c47525d..1295339aa33 100644 --- a/lib/Sema/AnalysisBasedWarnings.cpp +++ b/lib/Sema/AnalysisBasedWarnings.cpp @@ -1333,8 +1333,12 @@ class ThreadSafetyReporter : public clang::thread_safety::ThreadSafetyHandler { LocEndOfScope = FunEndLocation; PartialDiagnosticAt Warning(LocEndOfScope, S.PDiag(DiagID) << LockName); - PartialDiagnosticAt Note(LocLocked, S.PDiag(diag::note_locked_here)); - Warnings.push_back(DelayedDiag(Warning, OptionalNotes(1, Note))); + if (LocLocked.isValid()) { + PartialDiagnosticAt Note(LocLocked, S.PDiag(diag::note_locked_here)); + Warnings.push_back(DelayedDiag(Warning, OptionalNotes(1, Note))); + return; + } + Warnings.push_back(DelayedDiag(Warning, OptionalNotes())); } diff --git a/lib/Sema/AttributeList.cpp b/lib/Sema/AttributeList.cpp index e227d4e840b..9ac4c63e191 100644 --- a/lib/Sema/AttributeList.cpp +++ b/lib/Sema/AttributeList.cpp @@ -23,6 +23,8 @@ size_t AttributeList::allocated_size() const { if (IsAvailability) return AttributeFactory::AvailabilityAllocSize; else if (IsTypeTagForDatatype) return AttributeFactory::TypeTagForDatatypeAllocSize; + else if (IsProperty) + return AttributeFactory::PropertyAllocSize; return (sizeof(AttributeList) + NumArgs * sizeof(Expr*)); } diff --git a/lib/Sema/DeclSpec.cpp b/lib/Sema/DeclSpec.cpp index e1d55dbddcc..3b3ab2c27b4 100644 --- a/lib/Sema/DeclSpec.cpp +++ b/lib/Sema/DeclSpec.cpp @@ -24,6 +24,7 @@ #include "clang/Sema/Sema.h" #include "clang/Sema/SemaDiagnostic.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallString.h" #include "llvm/Support/ErrorHandling.h" #include using namespace clang; @@ -293,6 +294,11 @@ bool Declarator::isDeclarationOfFunction() const { case TST_event_t: return false; + case TST_decltype_auto: + // This must have an initializer, so can't be a function declaration, + // even if the initializer has function type. + return false; + case TST_decltype: case TST_typeofExpr: if (Expr *E = DS.getRepAsExpr()) @@ -325,7 +331,7 @@ bool Declarator::isDeclarationOfFunction() const { unsigned DeclSpec::getParsedSpecifiers() const { unsigned Res = 0; if (StorageClassSpec != SCS_unspecified || - SCS_thread_specified) + ThreadStorageClassSpec != TSCS_unspecified) Res |= PQ_StorageClassSpecifier; if (TypeQualifiers != TQ_unspecified) @@ -367,6 +373,16 @@ const char *DeclSpec::getSpecifierName(DeclSpec::SCS S) { llvm_unreachable("Unknown typespec!"); } +const char *DeclSpec::getSpecifierName(DeclSpec::TSCS S) { + switch (S) { + case DeclSpec::TSCS_unspecified: return "unspecified"; + case DeclSpec::TSCS___thread: return "__thread"; + case DeclSpec::TSCS_thread_local: return "thread_local"; + case DeclSpec::TSCS__Thread_local: return "_Thread_local"; + } + llvm_unreachable("Unknown typespec!"); +} + const char *DeclSpec::getSpecifierName(TSW W) { switch (W) { case TSW_unspecified: return "unspecified"; @@ -423,6 +439,7 @@ const char *DeclSpec::getSpecifierName(DeclSpec::TST T) { case DeclSpec::TST_typeofExpr: return "typeof"; case DeclSpec::TST_auto: return "auto"; case DeclSpec::TST_decltype: return "(decltype)"; + case DeclSpec::TST_decltype_auto: return "decltype(auto)"; case DeclSpec::TST_underlyingType: return "__underlying_type"; case DeclSpec::TST_unknown_anytype: return "__unknown_anytype"; case DeclSpec::TST_atomic: return "_Atomic"; @@ -483,7 +500,7 @@ bool DeclSpec::SetStorageClassSpec(Sema &S, SCS SC, SourceLocation Loc, } if (StorageClassSpec != SCS_unspecified) { - // Maybe this is an attempt to use C++0x 'auto' outside of C++0x mode. + // Maybe this is an attempt to use C++11 'auto' outside of C++11 mode. bool isInvalid = true; if (TypeSpecType == TST_unspecified && S.getLangOpts().CPlusPlus) { if (SC == SCS_auto) @@ -510,16 +527,14 @@ bool DeclSpec::SetStorageClassSpec(Sema &S, SCS SC, SourceLocation Loc, return false; } -bool DeclSpec::SetStorageClassSpecThread(SourceLocation Loc, +bool DeclSpec::SetStorageClassSpecThread(TSCS TSC, SourceLocation Loc, const char *&PrevSpec, unsigned &DiagID) { - if (SCS_thread_specified) { - PrevSpec = "__thread"; - DiagID = diag::ext_duplicate_declspec; - return true; - } - SCS_thread_specified = true; - SCS_threadLoc = Loc; + if (ThreadStorageClassSpec != TSCS_unspecified) + return BadSpecifier(TSC, (TSCS)ThreadStorageClassSpec, PrevSpec, DiagID); + + ThreadStorageClassSpec = TSC; + ThreadStorageClassSpecLoc = Loc; return false; } @@ -825,6 +840,39 @@ void DeclSpec::Finish(DiagnosticsEngine &D, Preprocessor &PP) { // Check the type specifier components first. + // If decltype(auto) is used, no other type specifiers are permitted. + if (TypeSpecType == TST_decltype_auto && + (TypeSpecWidth != TSW_unspecified || + TypeSpecComplex != TSC_unspecified || + TypeSpecSign != TSS_unspecified || + TypeAltiVecVector || TypeAltiVecPixel || TypeAltiVecBool || + TypeQualifiers)) { + const unsigned NumLocs = 8; + SourceLocation ExtraLocs[NumLocs] = { + TSWLoc, TSCLoc, TSSLoc, AltiVecLoc, + TQ_constLoc, TQ_restrictLoc, TQ_volatileLoc, TQ_atomicLoc + }; + FixItHint Hints[NumLocs]; + SourceLocation FirstLoc; + for (unsigned I = 0; I != NumLocs; ++I) { + if (!ExtraLocs[I].isInvalid()) { + if (FirstLoc.isInvalid() || + PP.getSourceManager().isBeforeInTranslationUnit(ExtraLocs[I], + FirstLoc)) + FirstLoc = ExtraLocs[I]; + Hints[I] = FixItHint::CreateRemoval(ExtraLocs[I]); + } + } + TypeSpecWidth = TSW_unspecified; + TypeSpecComplex = TSC_unspecified; + TypeSpecSign = TSS_unspecified; + TypeAltiVecVector = TypeAltiVecPixel = TypeAltiVecBool = false; + TypeQualifiers = 0; + Diag(D, TSTLoc, diag::err_decltype_auto_cannot_be_combined) + << Hints[0] << Hints[1] << Hints[2] << Hints[3] + << Hints[4] << Hints[5] << Hints[6] << Hints[7]; + } + // Validate and finalize AltiVec vector declspec. if (TypeAltiVecVector) { if (TypeAltiVecBool) { @@ -923,12 +971,39 @@ void DeclSpec::Finish(DiagnosticsEngine &D, Preprocessor &PP) { } } + // C11 6.7.1/3, C++11 [dcl.stc]p1, GNU TLS: __thread, thread_local and + // _Thread_local can only appear with the 'static' and 'extern' storage class + // specifiers. We also allow __private_extern__ as an extension. + if (ThreadStorageClassSpec != TSCS_unspecified) { + switch (StorageClassSpec) { + case SCS_unspecified: + case SCS_extern: + case SCS_private_extern: + case SCS_static: + break; + default: + if (PP.getSourceManager().isBeforeInTranslationUnit( + getThreadStorageClassSpecLoc(), getStorageClassSpecLoc())) + Diag(D, getStorageClassSpecLoc(), + diag::err_invalid_decl_spec_combination) + << DeclSpec::getSpecifierName(getThreadStorageClassSpec()) + << SourceRange(getThreadStorageClassSpecLoc()); + else + Diag(D, getThreadStorageClassSpecLoc(), + diag::err_invalid_decl_spec_combination) + << DeclSpec::getSpecifierName(getStorageClassSpec()) + << SourceRange(getStorageClassSpecLoc()); + // Discard the thread storage class specifier to recover. + ThreadStorageClassSpec = TSCS_unspecified; + ThreadStorageClassSpecLoc = SourceLocation(); + } + } + // If no type specifier was provided and we're parsing a language where // the type specifier is not optional, but we got 'auto' as a storage // class specifier, then assume this is an attempt to use C++0x's 'auto' // type specifier. - // FIXME: Does Microsoft really support implicit int in C++? - if (PP.getLangOpts().CPlusPlus && !PP.getLangOpts().MicrosoftExt && + if (PP.getLangOpts().CPlusPlus && TypeSpecType == TST_unspecified && StorageClassSpec == SCS_auto) { TypeSpecType = TST_auto; StorageClassSpec = SCS_unspecified; @@ -936,7 +1011,7 @@ void DeclSpec::Finish(DiagnosticsEngine &D, Preprocessor &PP) { StorageClassSpecLoc = SourceLocation(); } // Diagnose if we've recovered from an ill-formed 'auto' storage class - // specifier in a pre-C++0x dialect of C++. + // specifier in a pre-C++11 dialect of C++. if (!PP.getLangOpts().CPlusPlus11 && TypeSpecType == TST_auto) Diag(D, TSTLoc, diag::ext_auto_type_specifier); if (PP.getLangOpts().CPlusPlus && !PP.getLangOpts().CPlusPlus11 && @@ -952,16 +1027,27 @@ void DeclSpec::Finish(DiagnosticsEngine &D, Preprocessor &PP) { // C++ [class.friend]p6: // No storage-class-specifier shall appear in the decl-specifier-seq // of a friend declaration. - if (isFriendSpecified() && getStorageClassSpec()) { - DeclSpec::SCS SC = getStorageClassSpec(); - const char *SpecName = getSpecifierName(SC); + if (isFriendSpecified() && + (getStorageClassSpec() || getThreadStorageClassSpec())) { + SmallString<32> SpecName; + SourceLocation SCLoc; + FixItHint StorageHint, ThreadHint; + + if (DeclSpec::SCS SC = getStorageClassSpec()) { + SpecName = getSpecifierName(SC); + SCLoc = getStorageClassSpecLoc(); + StorageHint = FixItHint::CreateRemoval(SCLoc); + } - SourceLocation SCLoc = getStorageClassSpecLoc(); - SourceLocation SCEndLoc = SCLoc.getLocWithOffset(strlen(SpecName)); + if (DeclSpec::TSCS TSC = getThreadStorageClassSpec()) { + if (!SpecName.empty()) SpecName += " "; + SpecName += getSpecifierName(TSC); + SCLoc = getThreadStorageClassSpecLoc(); + ThreadHint = FixItHint::CreateRemoval(SCLoc); + } Diag(D, SCLoc, diag::err_friend_storage_spec) - << SpecName - << FixItHint::CreateRemoval(SourceRange(SCLoc, SCEndLoc)); + << SpecName << StorageHint << ThreadHint; ClearStorageClassSpecs(); } diff --git a/lib/Sema/ScopeInfo.cpp b/lib/Sema/ScopeInfo.cpp index 4d29a34a73e..2f48bec123b 100644 --- a/lib/Sema/ScopeInfo.cpp +++ b/lib/Sema/ScopeInfo.cpp @@ -187,3 +187,4 @@ void FunctionScopeInfo::markSafeWeakUse(const Expr *E) { FunctionScopeInfo::~FunctionScopeInfo() { } BlockScopeInfo::~BlockScopeInfo() { } LambdaScopeInfo::~LambdaScopeInfo() { } +CapturedRegionScopeInfo::~CapturedRegionScopeInfo() { } diff --git a/lib/Sema/Sema.cpp b/lib/Sema/Sema.cpp index 6bab9e80cbf..e718be2f8bd 100644 --- a/lib/Sema/Sema.cpp +++ b/lib/Sema/Sema.cpp @@ -90,7 +90,7 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer, AccessCheckingSFINAE(false), InNonInstantiationSFINAEContext(false), NonInstantiationEntries(0), ArgumentPackSubstitutionIndex(-1), CurrentInstantiationScope(0), TyposCorrected(0), - AnalysisWarnings(*this), Ident_super(0) + AnalysisWarnings(*this), CurScope(0), Ident_super(0) { TUScope = 0; @@ -590,12 +590,11 @@ void Sema::ActOnEndOfTranslationUnit() { } // Remove file scoped decls that turned out to be used. - UnusedFileScopedDecls.erase(std::remove_if(UnusedFileScopedDecls.begin(0, - true), - UnusedFileScopedDecls.end(), - std::bind1st(std::ptr_fun(ShouldRemoveFromUnused), - this)), - UnusedFileScopedDecls.end()); + UnusedFileScopedDecls.erase( + std::remove_if(UnusedFileScopedDecls.begin(0, true), + UnusedFileScopedDecls.end(), + std::bind1st(std::ptr_fun(ShouldRemoveFromUnused), this)), + UnusedFileScopedDecls.end()); if (TUKind == TU_Prefix) { // Translation unit prefixes don't need any of the checking below. @@ -751,9 +750,13 @@ void Sema::ActOnEndOfTranslationUnit() { if (DiagD->isReferenced()) { Diag(DiagD->getLocation(), diag::warn_unneeded_internal_decl) << /*variable*/1 << DiagD->getDeclName(); - } else { + } else if (getSourceManager().isFromMainFile(DiagD->getLocation())) { + // If the declaration is in a header which is included into multiple + // TUs, it will declare one variable per TU, and one of the other + // variables may be used. So, only warn if the declaration is in the + // main file. Diag(DiagD->getLocation(), diag::warn_unused_variable) - << DiagD->getDeclName(); + << DiagD->getDeclName(); } } } @@ -798,7 +801,7 @@ DeclContext *Sema::getFunctionLevelDeclContext() { DeclContext *DC = CurContext; while (true) { - if (isa(DC) || isa(DC)) { + if (isa(DC) || isa(DC) || isa(DC)) { DC = DC->getParent(); } else if (isa(DC) && cast(DC)->getOverloadedOperator() == OO_Call && @@ -1073,7 +1076,8 @@ void Sema::ActOnComment(SourceRange Comment) { if (!LangOpts.RetainCommentsFromSystemHeaders && SourceMgr.isInSystemHeader(Comment.getBegin())) return; - RawComment RC(SourceMgr, Comment); + RawComment RC(SourceMgr, Comment, false, + LangOpts.CommentOpts.ParseAllComments); if (RC.isAlmostTrailingComment()) { SourceRange MagicMarkerRange(Comment.getBegin(), Comment.getBegin().getLocWithOffset(3)); @@ -1292,7 +1296,7 @@ bool Sema::tryToRecoverWithCall(ExprResult &E, const PartialDiagnostic &PD, // FIXME: Try this before emitting the fixit, and suppress diagnostics // while doing so. E = ActOnCallExpr(0, E.take(), ParenInsertionLoc, - MultiExprArg(), ParenInsertionLoc.getLocWithOffset(1)); + None, ParenInsertionLoc.getLocWithOffset(1)); return true; } @@ -1309,3 +1313,18 @@ IdentifierInfo *Sema::getSuperIdentifier() const { Ident_super = &Context.Idents.get("super"); return Ident_super; } + +void Sema::PushCapturedRegionScope(Scope *S, CapturedDecl *CD, RecordDecl *RD, + CapturedRegionKind K) { + CapturingScopeInfo *CSI = new CapturedRegionScopeInfo(getDiagnostics(), S, CD, RD, + CD->getContextParam(), K); + CSI->ReturnType = Context.VoidTy; + FunctionScopes.push_back(CSI); +} + +CapturedRegionScopeInfo *Sema::getCurCapturedRegion() { + if (FunctionScopes.empty()) + return 0; + + return dyn_cast(FunctionScopes.back()); +} diff --git a/lib/Sema/SemaAccess.cpp b/lib/Sema/SemaAccess.cpp index 79a9d3c9fdc..3ef1fdebaac 100644 --- a/lib/Sema/SemaAccess.cpp +++ b/lib/Sema/SemaAccess.cpp @@ -84,10 +84,10 @@ struct EffectiveContext { : Inner(DC), Dependent(DC->isDependentContext()) { - // C++ [class.access.nest]p1: + // C++11 [class.access.nest]p1: // A nested class is a member and as such has the same access // rights as any other member. - // C++ [class.access]p2: + // C++11 [class.access]p2: // A member of a class can also access all the names to which // the class has access. A local class of a member function // may access the same names that the member function itself @@ -1476,18 +1476,18 @@ static Sema::AccessResult CheckAccess(Sema &S, SourceLocation Loc, llvm_unreachable("falling off end"); } -void Sema::HandleDelayedAccessCheck(DelayedDiagnostic &DD, Decl *decl) { +void Sema::HandleDelayedAccessCheck(DelayedDiagnostic &DD, Decl *D) { // Access control for names used in the declarations of functions // and function templates should normally be evaluated in the context // of the declaration, just in case it's a friend of something. // However, this does not apply to local extern declarations. - DeclContext *DC = decl->getDeclContext(); - if (FunctionDecl *fn = dyn_cast(decl)) { - if (!DC->isFunctionOrMethod()) DC = fn; - } else if (FunctionTemplateDecl *fnt = dyn_cast(decl)) { - // Never a local declaration. - DC = fnt->getTemplatedDecl(); + DeclContext *DC = D->getDeclContext(); + if (FunctionDecl *FN = dyn_cast(D)) { + if (!DC->isFunctionOrMethod()) + DC = FN; + } else if (TemplateDecl *TD = dyn_cast(D)) { + DC = cast(TD->getTemplatedDecl()); } EffectiveContext EC(DC); diff --git a/lib/Sema/SemaCast.cpp b/lib/Sema/SemaCast.cpp index e2a40847663..eb11a577cb0 100644 --- a/lib/Sema/SemaCast.cpp +++ b/lib/Sema/SemaCast.cpp @@ -333,7 +333,7 @@ static bool tryDiagnoseOverloadedCast(Sema &S, CastType CT, : (CT == CT_Functional)? InitializationKind::CreateFunctionalCast(range, listInitialization) : InitializationKind::CreateCast(/*type range?*/ range); - InitializationSequence sequence(S, entity, initKind, &src, 1); + InitializationSequence sequence(S, entity, initKind, src); assert(sequence.Failed() && "initialization succeeded on second try?"); switch (sequence.getFailureKind()) { @@ -1418,7 +1418,7 @@ TryStaticImplicitCast(Sema &Self, ExprResult &SrcExpr, QualType DestType, ? InitializationKind::CreateFunctionalCast(OpRange, ListInitialization) : InitializationKind::CreateCast(OpRange); Expr *SrcExprRaw = SrcExpr.get(); - InitializationSequence InitSeq(Self, Entity, InitKind, &SrcExprRaw, 1); + InitializationSequence InitSeq(Self, Entity, InitKind, SrcExprRaw); // At this point of CheckStaticCast, if the destination is a reference, // or the expression is an overload expression this has to work. @@ -1452,7 +1452,7 @@ static TryCastResult TryConstCast(Sema &Self, Expr *SrcExpr, QualType DestType, DestType = Self.Context.getCanonicalType(DestType); QualType SrcType = SrcExpr->getType(); if (const ReferenceType *DestTypeTmp =DestType->getAs()) { - if (DestTypeTmp->isLValueReferenceType() && !SrcExpr->isLValue()) { + if (isa(DestTypeTmp) && !SrcExpr->isLValue()) { // Cannot const_cast non-lvalue to lvalue reference type. But if this // is C-style, static_cast might find a way, so we simply suggest a // message and tell the parent to keep searching. @@ -1460,6 +1460,16 @@ static TryCastResult TryConstCast(Sema &Self, Expr *SrcExpr, QualType DestType, return TC_NotApplicable; } + // It's not completely clear under the standard whether we can + // const_cast bit-field gl-values. Doing so would not be + // intrinsically complicated, but for now, we say no for + // consistency with other compilers and await the word of the + // committee. + if (SrcExpr->refersToBitField()) { + msg = diag::err_bad_cxx_cast_bitfield; + return TC_NotApplicable; + } + // C++ 5.2.11p4: An lvalue of type T1 can be [cast] to an lvalue of type T2 // [...] if a pointer to T1 can be [cast] to the type pointer to T2. DestType = Self.Context.getPointerType(DestTypeTmp->getPointeeType()); diff --git a/lib/Sema/SemaChecking.cpp b/lib/Sema/SemaChecking.cpp index 4e11b3aa792..a0998a46c66 100644 --- a/lib/Sema/SemaChecking.cpp +++ b/lib/Sema/SemaChecking.cpp @@ -586,12 +586,11 @@ bool Sema::CheckFunctionCall(FunctionDecl *FDecl, CallExpr *TheCall, } bool Sema::CheckObjCMethodCall(ObjCMethodDecl *Method, SourceLocation lbrac, - Expr **Args, unsigned NumArgs) { + ArrayRef Args) { VariadicCallType CallType = Method->isVariadic() ? VariadicMethod : VariadicDoesNotApply; - checkCall(Method, llvm::makeArrayRef(Args, NumArgs), - Method->param_size(), + checkCall(Method, Args, Method->param_size(), /*IsMemberFunction=*/false, lbrac, Method->getSourceRange(), CallType); @@ -2009,7 +2008,7 @@ class CheckFormatHandler : public analyze_format_string::FormatStringHandler { PartialDiagnostic PDiag, SourceLocation StringLoc, bool IsStringLocation, Range StringRange, - ArrayRef Fixit = ArrayRef()); + ArrayRef Fixit = None); protected: bool HandleInvalidConversionSpecifier(unsigned argIndex, SourceLocation Loc, @@ -2036,7 +2035,7 @@ class CheckFormatHandler : public analyze_format_string::FormatStringHandler { template void EmitFormatDiagnostic(PartialDiagnostic PDiag, SourceLocation StringLoc, bool IsStringLocation, Range StringRange, - ArrayRef Fixit = ArrayRef()); + ArrayRef Fixit = None); void CheckPositionalAndNonpositionalArgs( const analyze_format_string::FormatSpecifier *FS); @@ -2744,6 +2743,10 @@ CheckPrintfHandler::checkFormatExpr(const analyze_printf::PrintfSpecifier &FS, return true; QualType ExprTy = E->getType(); + while (const TypeOfExprType *TET = dyn_cast(ExprTy)) { + ExprTy = TET->getUnderlyingExpr()->getType(); + } + if (AT.matchesType(S.Context, ExprTy)) return true; @@ -4308,7 +4311,7 @@ static IntRange GetExprRange(ASTContext &C, Expr *E, unsigned MaxWidth) { IntRange::forValueOfType(C, E->getType()); } - if (FieldDecl *BitField = E->getBitField()) + if (FieldDecl *BitField = E->getSourceBitField()) return IntRange(BitField->getBitWidthValue(C), BitField->getType()->isUnsignedIntegerOrEnumerationType()); @@ -4685,7 +4688,7 @@ static void AnalyzeAssignment(Sema &S, BinaryOperator *E) { // We want to recurse on the RHS as normal unless we're assigning to // a bitfield. - if (FieldDecl *Bitfield = E->getLHS()->getBitField()) { + if (FieldDecl *Bitfield = E->getLHS()->getSourceBitField()) { if (AnalyzeBitFieldAssignment(S, Bitfield, E->getRHS(), E->getOperatorLoc())) { // Recurse, ignoring any implicit conversions on the RHS. @@ -5699,12 +5702,14 @@ bool Sema::CheckParmsForFunctionDef(ParmVarDecl **P, ParmVarDecl **PEnd, // notation in their sequences of declarator specifiers to specify // variable length array types. QualType PType = Param->getOriginalType(); - if (const ArrayType *AT = Context.getAsArrayType(PType)) { + while (const ArrayType *AT = Context.getAsArrayType(PType)) { if (AT->getSizeModifier() == ArrayType::Star) { // FIXME: This diagnostic should point the '[*]' if source-location // information is added for it. Diag(Param->getLocation(), diag::err_array_star_in_function_definition); + break; } + PType= AT->getElementType(); } } diff --git a/lib/Sema/SemaCodeComplete.cpp b/lib/Sema/SemaCodeComplete.cpp index 2db1e2afa26..fd2ce1749fe 100644 --- a/lib/Sema/SemaCodeComplete.cpp +++ b/lib/Sema/SemaCodeComplete.cpp @@ -3348,13 +3348,12 @@ void Sema::CodeCompleteDeclSpec(Scope *S, DeclSpec &DS, // be a receiver of a class message, this may be a class message send with // the initial opening bracket '[' missing. Add appropriate completions. if (AllowNonIdentifiers && !AllowNestedNameSpecifiers && + DS.getParsedSpecifiers() == DeclSpec::PQ_TypeSpecifier && DS.getTypeSpecType() == DeclSpec::TST_typename && - DS.getStorageClassSpec() == DeclSpec::SCS_unspecified && - !DS.isThreadSpecified() && !DS.isExternInLinkageSpec() && DS.getTypeSpecComplex() == DeclSpec::TSC_unspecified && DS.getTypeSpecSign() == DeclSpec::TSS_unspecified && - DS.getTypeQualifiers() == 0 && - S && + !DS.isTypeAltiVecVector() && + S && (S->getFlags() & Scope::DeclScope) != 0 && (S->getFlags() & (Scope::ClassScope | Scope::TemplateParamScope | Scope::FunctionPrototypeScope | @@ -5472,7 +5471,7 @@ static void AddClassMessageCompletions(Sema &SemaRef, Scope *S, M != MEnd; ++M) { for (ObjCMethodList *MethList = &M->second.second; MethList && MethList->Method; - MethList = MethList->Next) { + MethList = MethList->getNext()) { if (!isAcceptableObjCMethod(MethList->Method, MK_Any, SelIdents, NumSelIdents)) continue; @@ -5645,7 +5644,7 @@ void Sema::CodeCompleteObjCInstanceMessage(Scope *S, Expr *Receiver, M != MEnd; ++M) { for (ObjCMethodList *MethList = &M->second.first; MethList && MethList->Method; - MethList = MethList->Next) { + MethList = MethList->getNext()) { if (!isAcceptableObjCMethod(MethList->Method, MK_Any, SelIdents, NumSelIdents)) continue; @@ -7086,7 +7085,7 @@ void Sema::CodeCompleteObjCMethodDeclSelector(Scope *S, for (ObjCMethodList *MethList = IsInstanceMethod ? &M->second.first : &M->second.second; MethList && MethList->Method; - MethList = MethList->Next) { + MethList = MethList->getNext()) { if (!isAcceptableObjCMethod(MethList->Method, MK_Any, SelIdents, NumSelIdents)) continue; diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index adf3505633b..e0e8bd646b3 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -129,7 +129,7 @@ bool Sema::isSimpleTypeSpecifier(tok::TokenKind Kind) const { /// /// If name lookup results in an ambiguity, this routine will complain /// and then return NULL. -ParsedType Sema::getTypeName(IdentifierInfo &II, SourceLocation NameLoc, +ParsedType Sema::getTypeName(const IdentifierInfo &II, SourceLocation NameLoc, Scope *S, CXXScopeSpec *SS, bool isClassName, bool HasTrailingDot, ParsedType ObjectTypePtr, @@ -1219,7 +1219,10 @@ bool Sema::ShouldWarnIfUnusedFileScopedDecl(const DeclaratorDecl *D) const { return false; } else { // 'static inline' functions are used in headers; don't warn. - if (FD->getStorageClass() == SC_Static && + // Make sure we get the storage class from the canonical declaration, + // since otherwise we will get spurious warnings on specialized + // static template functions. + if (FD->getCanonicalDecl()->getStorageClass() == SC_Static && FD->isInlineSpecified()) return false; } @@ -1613,20 +1616,7 @@ static void filterNonConflictingPreviousDecls(ASTContext &context, if (!old->isHidden()) continue; - // If either has no-external linkage, ignore the old declaration. - // If this declaration would have external linkage if it were the first - // declaration of this name, then it may in fact be a redeclaration of - // some hidden declaration, so include those too. We don't need to worry - // about some previous visible declaration giving this declaration external - // linkage, because in that case, we'll mark this declaration as a redecl - // of the visible decl, and that decl will already be a redecl of the - // hidden declaration if that's appropriate. - // - // Don't cache this linkage computation, because it's not yet correct: we - // may later give this declaration a previous declaration which changes - // its linkage. - if (old->getLinkage() != ExternalLinkage || - !decl->hasExternalLinkageUncached()) + if (old->getLinkage() != ExternalLinkage) filter.erase(); } @@ -2256,11 +2246,9 @@ static bool haveIncompatibleLanguageLinkages(const T *Old, const T *New) { return false; LanguageLinkage OldLinkage = Old->getLanguageLinkage(); - if (OldLinkage == CXXLanguageLinkage && - New->getDeclContext()->isExternCContext()) + if (OldLinkage == CXXLanguageLinkage && New->isInExternCContext()) return true; - if (OldLinkage == CLanguageLinkage && - New->getDeclContext()->isExternCXXContext()) + if (OldLinkage == CLanguageLinkage && New->isInExternCXXContext()) return true; return false; } @@ -2451,12 +2439,22 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, Decl *OldD, Scope *S) { // Certain function declarations cannot be overloaded: // -- Function declarations that differ only in the return type // cannot be overloaded. - QualType OldReturnType = OldType->getResultType(); - QualType NewReturnType = cast(NewQType)->getResultType(); + + // Go back to the type source info to compare the declared return types, + // per C++1y [dcl.type.auto]p??: + // Redeclarations or specializations of a function or function template + // with a declared return type that uses a placeholder type shall also + // use that placeholder, not a deduced type. + QualType OldDeclaredReturnType = (Old->getTypeSourceInfo() + ? Old->getTypeSourceInfo()->getType()->castAs() + : OldType)->getResultType(); + QualType NewDeclaredReturnType = (New->getTypeSourceInfo() + ? New->getTypeSourceInfo()->getType()->castAs() + : NewType)->getResultType(); QualType ResQT; - if (OldReturnType != NewReturnType) { - if (NewReturnType->isObjCObjectPointerType() - && OldReturnType->isObjCObjectPointerType()) + if (!Context.hasSameType(OldDeclaredReturnType, NewDeclaredReturnType)) { + if (NewDeclaredReturnType->isObjCObjectPointerType() && + OldDeclaredReturnType->isObjCObjectPointerType()) ResQT = Context.mergeObjCGCQualifiers(NewQType, OldQType); if (ResQT.isNull()) { if (New->isCXXClassMember() && New->isOutOfLine()) @@ -2471,8 +2469,21 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, Decl *OldD, Scope *S) { NewQType = ResQT; } - const CXXMethodDecl* OldMethod = dyn_cast(Old); - CXXMethodDecl* NewMethod = dyn_cast(New); + QualType OldReturnType = OldType->getResultType(); + QualType NewReturnType = cast(NewQType)->getResultType(); + if (OldReturnType != NewReturnType) { + // If this function has a deduced return type and has already been + // defined, copy the deduced value from the old declaration. + AutoType *OldAT = Old->getResultType()->getContainedAutoType(); + if (OldAT && OldAT->isDeduced()) { + New->setType(SubstAutoType(New->getType(), OldAT->getDeducedType())); + NewQType = Context.getCanonicalType( + SubstAutoType(NewQType, OldAT->getDeducedType())); + } + } + + const CXXMethodDecl *OldMethod = dyn_cast(Old); + CXXMethodDecl *NewMethod = dyn_cast(New); if (OldMethod && NewMethod) { // Preserve triviality. NewMethod->setTrivial(OldMethod->isTrivial()); @@ -2697,21 +2708,31 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, Decl *OldD, Scope *S) { // Fall through to diagnose conflicting types. } - // A function that has already been declared has been redeclared or defined - // with a different type- show appropriate diagnostic - if (unsigned BuiltinID = Old->getBuiltinID()) { - // The user has declared a builtin function with an incompatible - // signature. + // A function that has already been declared has been redeclared or + // defined with a different type; show an appropriate diagnostic. + + // If the previous declaration was an implicitly-generated builtin + // declaration, then at the very least we should use a specialized note. + unsigned BuiltinID; + if (Old->isImplicit() && (BuiltinID = Old->getBuiltinID())) { + // If it's actually a library-defined builtin function like 'malloc' + // or 'printf', just warn about the incompatible redeclaration. if (Context.BuiltinInfo.isPredefinedLibFunction(BuiltinID)) { - // The function the user is redeclaring is a library-defined - // function like 'malloc' or 'printf'. Warn about the - // redeclaration, then pretend that we don't know about this - // library built-in. Diag(New->getLocation(), diag::warn_redecl_library_builtin) << New; Diag(Old->getLocation(), diag::note_previous_builtin_declaration) << Old << Old->getType(); - New->getIdentifier()->setBuiltinID(Builtin::NotBuiltin); - Old->setInvalidDecl(); + + // If this is a global redeclaration, just forget hereafter + // about the "builtin-ness" of the function. + // + // Doing this for local extern declarations is problematic. If + // the builtin declaration remains visible, a second invalid + // local declaration will produce a hard error; if it doesn't + // remain visible, a single bogus local redeclaration (which is + // actually only a warning) could break all the downstream code. + if (!New->getDeclContext()->isFunctionOrMethod()) + New->getIdentifier()->setBuiltinID(Builtin::NotBuiltin); + return false; } @@ -2769,7 +2790,10 @@ void Sema::mergeObjCMethodDecls(ObjCMethodDecl *newMethod, ObjCMethodDecl *oldMethod) { // Merge the attributes, including deprecated/unavailable - mergeDeclAttributes(newMethod, oldMethod, AMK_Override); + AvailabilityMergeKind MergeKind = + isa(newMethod->getDeclContext()) ? AMK_Redeclaration + : AMK_Override; + mergeDeclAttributes(newMethod, oldMethod, MergeKind); // Merge attributes from the parameters. ObjCMethodDecl::param_const_iterator oi = oldMethod->param_begin(), @@ -2795,8 +2819,7 @@ void Sema::MergeVarDeclTypes(VarDecl *New, VarDecl *Old, bool OldWasHidden) { QualType MergedT; if (getLangOpts().CPlusPlus) { - AutoType *AT = New->getType()->getContainedAutoType(); - if (AT && !AT->isDeduced()) { + if (New->getType()->isUndeducedType()) { // We don't know what the new type is until the initializer is attached. return; } else if (Context.hasSameType(New->getType(), Old->getType())) { @@ -2868,6 +2891,9 @@ void Sema::MergeVarDecl(VarDecl *New, LookupResult &Previous, return New->setInvalidDecl(); } + if (!shouldLinkPossiblyHiddenDecl(Old, New)) + return; + // C++ [class.mem]p1: // A member shall not be declared twice in the member-specification [...] // @@ -2950,12 +2976,22 @@ void Sema::MergeVarDecl(VarDecl *New, LookupResult &Previous, return New->setInvalidDecl(); } - if (New->isThreadSpecified() && !Old->isThreadSpecified()) { - Diag(New->getLocation(), diag::err_thread_non_thread) << New->getDeclName(); - Diag(Old->getLocation(), diag::note_previous_definition); - } else if (!New->isThreadSpecified() && Old->isThreadSpecified()) { - Diag(New->getLocation(), diag::err_non_thread_thread) << New->getDeclName(); - Diag(Old->getLocation(), diag::note_previous_definition); + if (New->getTLSKind() != Old->getTLSKind()) { + if (!Old->getTLSKind()) { + Diag(New->getLocation(), diag::err_thread_non_thread) << New->getDeclName(); + Diag(Old->getLocation(), diag::note_previous_declaration); + } else if (!New->getTLSKind()) { + Diag(New->getLocation(), diag::err_non_thread_thread) << New->getDeclName(); + Diag(Old->getLocation(), diag::note_previous_declaration); + } else { + // Do not allow redeclaration to change the variable between requiring + // static and dynamic initialization. + // FIXME: GCC allows this, but uses the TLS keyword on the first + // declaration to determine the kind. Do we need to be compatible here? + Diag(New->getLocation(), diag::err_thread_thread_different_kind) + << New->getDeclName() << (New->getTLSKind() == VarDecl::TLS_Dynamic); + Diag(Old->getLocation(), diag::note_previous_declaration); + } } // C++ doesn't have tentative definitions, so go right ahead and check here. @@ -3179,8 +3215,9 @@ Decl *Sema::ParsedFreeStandingDeclSpec(Scope *S, AccessSpecifier AS, Diag(DS.getStorageClassSpecLoc(), DiagID) << DeclSpec::getSpecifierName(SCS); - if (DS.isThreadSpecified()) - Diag(DS.getThreadSpecLoc(), DiagID) << "__thread"; + if (DeclSpec::TSCS TSCS = DS.getThreadStorageClassSpec()) + Diag(DS.getThreadStorageClassSpecLoc(), DiagID) + << DeclSpec::getSpecifierName(TSCS); if (DS.getTypeQualifiers()) { if (DS.getTypeQualifiers() & DeclSpec::TQ_const) Diag(DS.getConstSpecLoc(), DiagID) << "const"; @@ -3333,10 +3370,16 @@ static bool InjectAnonymousStructOrUnionMembers(Sema &SemaRef, Scope *S, /// a VarDecl::StorageClass. Any error reporting is up to the caller: /// illegal input values are mapped to SC_None. static StorageClass -StorageClassSpecToVarDeclStorageClass(DeclSpec::SCS StorageClassSpec) { +StorageClassSpecToVarDeclStorageClass(const DeclSpec &DS) { + DeclSpec::SCS StorageClassSpec = DS.getStorageClassSpec(); + assert(StorageClassSpec != DeclSpec::SCS_typedef && + "Parser allowed 'typedef' as storage class VarDecl."); switch (StorageClassSpec) { case DeclSpec::SCS_unspecified: return SC_None; - case DeclSpec::SCS_extern: return SC_Extern; + case DeclSpec::SCS_extern: + if (DS.isExternInLinkageSpec()) + return SC_None; + return SC_Extern; case DeclSpec::SCS_static: return SC_Static; case DeclSpec::SCS_auto: return SC_Auto; case DeclSpec::SCS_register: return SC_Register; @@ -3534,9 +3577,7 @@ Decl *Sema::BuildAnonymousStructOrUnion(Scope *S, DeclSpec &DS, FieldCollector->Add(cast(Anon)); } else { DeclSpec::SCS SCSpec = DS.getStorageClassSpec(); - assert(SCSpec != DeclSpec::SCS_typedef && - "Parser allowed 'typedef' as storage class VarDecl."); - VarDecl::StorageClass SC = StorageClassSpecToVarDeclStorageClass(SCSpec); + VarDecl::StorageClass SC = StorageClassSpecToVarDeclStorageClass(DS); if (SCSpec == DeclSpec::SCS_mutable) { // mutable can only appear on non-static class members, so it's always // an error here @@ -4324,34 +4365,6 @@ Sema::RegisterLocallyScopedExternCDecl(NamedDecl *ND, "Decl is not a locally-scoped decl!"); // Note that we have a locally-scoped external with this name. LocallyScopedExternCDecls[ND->getDeclName()] = ND; - - if (!Previous.isSingleResult()) - return; - - NamedDecl *PrevDecl = Previous.getFoundDecl(); - - // If there was a previous declaration of this entity, it may be in - // our identifier chain. Update the identifier chain with the new - // declaration. - if (S && IdResolver.ReplaceDecl(PrevDecl, ND)) { - // The previous declaration was found on the identifer resolver - // chain, so remove it from its scope. - - if (S->isDeclScope(PrevDecl)) { - // Special case for redeclarations in the SAME scope. - // Because this declaration is going to be added to the identifier chain - // later, we should temporarily take it OFF the chain. - IdResolver.RemoveDecl(ND); - - } else { - // Find the scope for the original declaration. - while (S && !S->isDeclScope(PrevDecl)) - S = S->getParent(); - } - - if (S) - S->RemoveDecl(PrevDecl); - } } llvm::DenseMap::iterator @@ -4408,8 +4421,6 @@ Sema::ActOnTypedefDeclarator(Scope* S, Declarator& D, DeclContext* DC, DiagnoseFunctionSpecifiers(D.getDeclSpec()); - if (D.getDeclSpec().isThreadSpecified()) - Diag(D.getDeclSpec().getThreadSpecLoc(), diag::err_invalid_thread); if (D.getDeclSpec().isConstexprSpecified()) Diag(D.getDeclSpec().getConstexprSpecLoc(), diag::err_invalid_constexpr) << 1; @@ -4603,7 +4614,7 @@ bool Sema::inferObjCARCLifetime(ValueDecl *decl) { if (VarDecl *var = dyn_cast(decl)) { // Thread-local variables cannot have lifetime. if (lifetime && lifetime != Qualifiers::OCL_ExplicitNone && - var->isThreadSpecified()) { + var->getTLSKind()) { Diag(var->getLocation(), diag::err_arc_thread_ownership) << var->getType(); return true; @@ -4689,12 +4700,10 @@ Sema::ActOnVariableDeclarator(Scope *S, Declarator &D, DeclContext *DC, DeclarationName Name = GetNameForDeclarator(D).getName(); DeclSpec::SCS SCSpec = D.getDeclSpec().getStorageClassSpec(); - assert(SCSpec != DeclSpec::SCS_typedef && - "Parser allowed 'typedef' as storage class VarDecl."); - VarDecl::StorageClass SC = StorageClassSpecToVarDeclStorageClass(SCSpec); + VarDecl::StorageClass SC = + StorageClassSpecToVarDeclStorageClass(D.getDeclSpec()); - if (getLangOpts().OpenCL && !getOpenCLOptions().cl_khr_fp16) - { + if (getLangOpts().OpenCL && !getOpenCLOptions().cl_khr_fp16) { // OpenCL v1.2 s6.1.1.1: reject declaring variables of the half and // half array type (unless the cl_khr_fp16 extension is enabled). if (Context.getBaseElementType(R)->isHalfType()) { @@ -4711,6 +4720,16 @@ Sema::ActOnVariableDeclarator(Scope *S, Declarator &D, DeclContext *DC, SC = SC_None; } + // C++11 [dcl.stc]p4: + // When thread_local is applied to a variable of block scope the + // storage-class-specifier static is implied if it does not appear + // explicitly. + // Core issue: 'static' is not implied if the variable is declared 'extern'. + if (SCSpec == DeclSpec::SCS_unspecified && + D.getDeclSpec().getThreadStorageClassSpec() == + DeclSpec::TSCS_thread_local && DC->isFunctionOrMethod()) + SC = SC_Static; + IdentifierInfo *II = Name.getAsIdentifierInfo(); if (!II) { Diag(D.getIdentifierLoc(), diag::err_bad_variable_name) @@ -4845,8 +4864,7 @@ Sema::ActOnVariableDeclarator(Scope *S, Declarator &D, DeclContext *DC, // If this decl has an auto type in need of deduction, make a note of the // Decl so we can diagnose uses of it in its own initializer. - if (D.getDeclSpec().getTypeSpecType() == DeclSpec::TST_auto && - R->getContainedAutoType()) + if (D.getDeclSpec().containsPlaceholderType() && R->getContainedAutoType()) ParsingInitForAutoVars.insert(NewVD); if (D.isInvalidType() || Invalid) @@ -4868,13 +4886,16 @@ Sema::ActOnVariableDeclarator(Scope *S, Declarator &D, DeclContext *DC, // lexical context will be different from the semantic context. NewVD->setLexicalDeclContext(CurContext); - if (D.getDeclSpec().isThreadSpecified()) { + if (DeclSpec::TSCS TSCS = D.getDeclSpec().getThreadStorageClassSpec()) { if (NewVD->hasLocalStorage()) - Diag(D.getDeclSpec().getThreadSpecLoc(), diag::err_thread_non_global); + Diag(D.getDeclSpec().getThreadStorageClassSpecLoc(), + diag::err_thread_non_global) + << DeclSpec::getSpecifierName(TSCS); else if (!Context.getTargetInfo().isTLSSupported()) - Diag(D.getDeclSpec().getThreadSpecLoc(), diag::err_thread_unsupported); + Diag(D.getDeclSpec().getThreadStorageClassSpecLoc(), + diag::err_thread_unsupported); else - NewVD->setThreadSpecified(true); + NewVD->setTSCSpec(TSCS); } // C99 6.7.4p3 @@ -5130,7 +5151,7 @@ static bool mayConflictWithNonVisibleExternC(const T *ND) { // This code runs before the init of foo is set, and therefore before // the type of foo is known. Not knowing the type we cannot know its linkage // unless it is in an extern C block. - if (!DC->isExternCContext()) { + if (!ND->isInExternCContext()) { const ASTContext &Context = ND->getASTContext(); if (Context.getLangOpts().CPlusPlus) return false; @@ -5139,27 +5160,18 @@ static bool mayConflictWithNonVisibleExternC(const T *ND) { return ND->isExternC(); } -/// \brief Perform semantic checking on a newly-created variable -/// declaration. -/// -/// This routine performs all of the type-checking required for a -/// variable declaration once it has been built. It is used both to -/// check variables after they have been parsed and their declarators -/// have been translated into a declaration, and to check variables -/// that have been instantiated from a template. -/// -/// Sets NewVD->isInvalidDecl() if an error was encountered. -/// -/// Returns true if the variable declaration is a redeclaration. -bool Sema::CheckVariableDeclaration(VarDecl *NewVD, - LookupResult &Previous) { +void Sema::CheckVariableDeclarationType(VarDecl *NewVD) { // If the decl is already known invalid, don't check it. if (NewVD->isInvalidDecl()) - return false; + return; TypeSourceInfo *TInfo = NewVD->getTypeSourceInfo(); QualType T = TInfo->getType(); + // Defer checking an 'auto' type until its initializer is attached. + if (T->isUndeducedType()) + return; + if (T->isObjCObjectType()) { Diag(NewVD->getLocation(), diag::err_statically_allocated_object) << FixItHint::CreateInsertion(NewVD->getLocation(), "*"); @@ -5174,16 +5186,26 @@ bool Sema::CheckVariableDeclaration(VarDecl *NewVD, if (NewVD->hasLocalStorage() && T.getAddressSpace() != 0) { Diag(NewVD->getLocation(), diag::err_as_qualified_auto_decl); NewVD->setInvalidDecl(); - return false; + return; } + // OpenCL v1.2 s6.5 - All program scope variables must be declared in the + // __constant address space. + if (getLangOpts().OpenCL && NewVD->isFileVarDecl() + && T.getAddressSpace() != LangAS::opencl_constant + && !T->isSamplerT()){ + Diag(NewVD->getLocation(), diag::err_opencl_global_invalid_addr_space); + NewVD->setInvalidDecl(); + return; + } + // 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; + return; } if (NewVD->hasLocalStorage() && T.isObjCGCWeak() @@ -5224,7 +5246,7 @@ bool Sema::CheckVariableDeclaration(VarDecl *NewVD, Diag(NewVD->getLocation(), diag::err_vla_decl_has_extern_linkage) << SizeRange; NewVD->setInvalidDecl(); - return false; + return; } if (FixedTInfo == 0) { @@ -5233,7 +5255,7 @@ bool Sema::CheckVariableDeclaration(VarDecl *NewVD, else Diag(NewVD->getLocation(), diag::err_vm_decl_has_extern_linkage); NewVD->setInvalidDecl(); - return false; + return; } Diag(NewVD->getLocation(), diag::warn_illegal_constant_array_size); @@ -5241,6 +5263,54 @@ bool Sema::CheckVariableDeclaration(VarDecl *NewVD, NewVD->setTypeSourceInfo(FixedTInfo); } + if (T->isVoidType() && NewVD->isThisDeclarationADefinition()) { + Diag(NewVD->getLocation(), diag::err_typecheck_decl_incomplete_type) + << T; + NewVD->setInvalidDecl(); + return; + } + + if (!NewVD->hasLocalStorage() && NewVD->hasAttr()) { + Diag(NewVD->getLocation(), diag::err_block_on_nonlocal); + NewVD->setInvalidDecl(); + return; + } + + if (isVM && NewVD->hasAttr()) { + Diag(NewVD->getLocation(), diag::err_block_on_vm); + NewVD->setInvalidDecl(); + return; + } + + if (NewVD->isConstexpr() && !T->isDependentType() && + RequireLiteralType(NewVD->getLocation(), T, + diag::err_constexpr_var_non_literal)) { + // Can't perform this check until the type is deduced. + NewVD->setInvalidDecl(); + return; + } +} + +/// \brief Perform semantic checking on a newly-created variable +/// declaration. +/// +/// This routine performs all of the type-checking required for a +/// variable declaration once it has been built. It is used both to +/// check variables after they have been parsed and their declarators +/// have been translated into a declaration, and to check variables +/// that have been instantiated from a template. +/// +/// Sets NewVD->isInvalidDecl() if an error was encountered. +/// +/// Returns true if the variable declaration is a redeclaration. +bool Sema::CheckVariableDeclaration(VarDecl *NewVD, + LookupResult &Previous) { + CheckVariableDeclarationType(NewVD); + + // If the decl is already known invalid, don't check it. + if (NewVD->isInvalidDecl()) + return false; + // If we did not find anything by this name, look for a non-visible // extern "C" declaration with the same name. // @@ -5279,32 +5349,6 @@ bool Sema::CheckVariableDeclaration(VarDecl *NewVD, // Filter out any non-conflicting previous declarations. filterNonConflictingPreviousDecls(Context, NewVD, Previous); - if (T->isVoidType() && !NewVD->hasExternalStorage()) { - Diag(NewVD->getLocation(), diag::err_typecheck_decl_incomplete_type) - << T; - NewVD->setInvalidDecl(); - return false; - } - - if (!NewVD->hasLocalStorage() && NewVD->hasAttr()) { - Diag(NewVD->getLocation(), diag::err_block_on_nonlocal); - NewVD->setInvalidDecl(); - return false; - } - - if (isVM && NewVD->hasAttr()) { - Diag(NewVD->getLocation(), diag::err_block_on_vm); - NewVD->setInvalidDecl(); - return false; - } - - if (NewVD->isConstexpr() && !T->isDependentType() && - RequireLiteralType(NewVD->getLocation(), T, - diag::err_constexpr_var_non_literal)) { - NewVD->setInvalidDecl(); - return false; - } - if (!Previous.empty()) { MergeVarDecl(NewVD, Previous, PreviousWasHidden); return true; @@ -5631,7 +5675,10 @@ static FunctionDecl::StorageClass getFunctionStorageClass(Sema &SemaRef, D.setInvalidType(); break; case DeclSpec::SCS_unspecified: break; - case DeclSpec::SCS_extern: return SC_Extern; + case DeclSpec::SCS_extern: + if (D.getDeclSpec().isExternInLinkageSpec()) + return SC_None; + return SC_Extern; case DeclSpec::SCS_static: { if (SemaRef.CurContext->getRedeclContext()->isFunctionOrMethod()) { // C99 6.7.1p5: @@ -5823,8 +5870,10 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, DeclarationName Name = NameInfo.getName(); FunctionDecl::StorageClass SC = getFunctionStorageClass(*this, D); - if (D.getDeclSpec().isThreadSpecified()) - Diag(D.getDeclSpec().getThreadSpecLoc(), diag::err_invalid_thread); + if (DeclSpec::TSCS TSCS = D.getDeclSpec().getThreadStorageClassSpec()) + Diag(D.getDeclSpec().getThreadStorageClassSpecLoc(), + diag::err_invalid_thread) + << DeclSpec::getSpecifierName(TSCS); // Do not allow returning a objc interface by-value. if (R->getAs()->getResultType()->isObjCObjectType()) { @@ -6017,6 +6066,10 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, // Okay: Add virtual to the method. NewFD->setVirtualAsWritten(true); } + + if (getLangOpts().CPlusPlus1y && + NewFD->getResultType()->isUndeducedType()) + Diag(D.getDeclSpec().getVirtualSpecLoc(), diag::err_auto_fn_virtual); } // C++ [dcl.fct.spec]p3: @@ -6521,9 +6574,7 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, EPI.Variadic = true; EPI.ExtInfo = FT->getExtInfo(); - QualType R = Context.getFunctionType(FT->getResultType(), - ArrayRef(), - EPI); + QualType R = Context.getFunctionType(FT->getResultType(), None, EPI); NewFD->setType(R); } @@ -6667,8 +6718,11 @@ bool Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD, // there's no more work to do here; we'll just add the new // function to the scope. if (!AllowOverloadingOfFunction(Previous, Context)) { - Redeclaration = true; - OldDecl = Previous.getFoundDecl(); + NamedDecl *Candidate = Previous.getFoundDecl(); + if (shouldLinkPossiblyHiddenDecl(Candidate, NewFD)) { + Redeclaration = true; + OldDecl = Candidate; + } } else { switch (CheckOverload(S, NewFD, Previous, OldDecl, /*NewIsUsingDecl*/ false)) { @@ -6710,9 +6764,12 @@ bool Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD, // // This needs to be delayed until we know whether this is an out-of-line // definition of a static member function. + // + // This rule is not present in C++1y, so we produce a backwards + // compatibility warning whenever it happens in C++11. CXXMethodDecl *MD = dyn_cast(NewFD); - if (MD && MD->isConstexpr() && !MD->isStatic() && - !isa(MD) && + if (!getLangOpts().CPlusPlus1y && MD && MD->isConstexpr() && + !MD->isStatic() && !isa(MD) && (MD->getTypeQualifiers() & Qualifiers::Const) == 0) { CXXMethodDecl *OldMD = dyn_cast_or_null(OldDecl); if (FunctionTemplateDecl *OldTD = @@ -6727,6 +6784,18 @@ bool Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD, ArrayRef(FPT->arg_type_begin(), FPT->getNumArgs()), EPI)); + + // Warn that we did this, if we're not performing template instantiation. + // In that case, we'll have warned already when the template was defined. + if (ActiveTemplateInstantiations.empty()) { + SourceLocation AddConstLoc; + if (FunctionTypeLoc FTL = MD->getTypeSourceInfo()->getTypeLoc() + .IgnoreParens().getAs()) + AddConstLoc = PP.getLocForEndOfToken(FTL.getRParenLoc()); + + Diag(MD->getLocation(), diag::warn_cxx1y_compat_constexpr_not_const) + << FixItHint::CreateInsertion(AddConstLoc, " const"); + } } } @@ -7164,7 +7233,7 @@ namespace { return; } Inherited::VisitUnaryOperator(E); - } + } void VisitObjCMessageExpr(ObjCMessageExpr *E) { return; } @@ -7248,10 +7317,7 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init, ParenListExpr *CXXDirectInit = dyn_cast(Init); // C++11 [decl.spec.auto]p6. Deduce the type which 'auto' stands in for. - AutoType *Auto = 0; - if (TypeMayContainAuto && - (Auto = VDecl->getType()->getContainedAutoType()) && - !Auto->isDeduced()) { + if (TypeMayContainAuto && VDecl->getType()->isUndeducedType()) { Expr *DeduceInit = Init; // Initializer could be a C++ direct-initializer. Deduction only works if it // contains exactly one expression. @@ -7289,17 +7355,16 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init, Init = Result.take(); DefaultedToAuto = true; } - - TypeSourceInfo *DeducedType = 0; + + QualType DeducedType; if (DeduceAutoType(VDecl->getTypeSourceInfo(), DeduceInit, DeducedType) == DAR_Failed) DiagnoseAutoDeductionFailure(VDecl, DeduceInit); - if (!DeducedType) { + if (DeducedType.isNull()) { RealDecl->setInvalidDecl(); return; } - VDecl->setTypeSourceInfo(DeducedType); - VDecl->setType(DeducedType->getType()); + VDecl->setType(DeducedType); assert(VDecl->isLinkageValid()); // In ARC, infer lifetime. @@ -7311,8 +7376,9 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init, // We only want to warn outside of template instantiations, though: // inside a template, the 'id' could have come from a parameter. if (ActiveTemplateInstantiations.empty() && !DefaultedToAuto && - DeducedType->getType()->isObjCIdType()) { - SourceLocation Loc = DeducedType->getTypeLoc().getBeginLoc(); + DeducedType->isObjCIdType()) { + SourceLocation Loc = + VDecl->getTypeSourceInfo()->getTypeLoc().getBeginLoc(); Diag(Loc, diag::warn_auto_var_is_id) << VDecl->getDeclName() << DeduceInit->getSourceRange(); } @@ -7321,6 +7387,11 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init, // the previously declared type. if (VarDecl *Old = VDecl->getPreviousDecl()) MergeVarDeclTypes(VDecl, Old, /*OldWasHidden*/ false); + + // Check the deduced type is valid for a variable declaration. + CheckVariableDeclarationType(VDecl); + if (VDecl->isInvalidDecl()) + return; } if (VDecl->isLocalVarDecl() && VDecl->hasExternalStorage()) { @@ -7426,15 +7497,13 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init, : InitializationKind::CreateCopy(VDecl->getLocation(), Init->getLocStart()); - Expr **Args = &Init; - unsigned NumArgs = 1; - if (CXXDirectInit) { - Args = CXXDirectInit->getExprs(); - NumArgs = CXXDirectInit->getNumExprs(); - } - InitializationSequence InitSeq(*this, Entity, Kind, Args, NumArgs); - ExprResult Result = InitSeq.Perform(*this, Entity, Kind, - MultiExprArg(Args, NumArgs), &DclT); + MultiExprArg Args = Init; + if (CXXDirectInit) + Args = MultiExprArg(CXXDirectInit->getExprs(), + CXXDirectInit->getNumExprs()); + + InitializationSequence InitSeq(*this, Entity, Kind, Args); + ExprResult Result = InitSeq.Perform(*this, Entity, Kind, Args, &DclT); if (Result.isInvalid()) { VDecl->setInvalidDecl(); return; @@ -7595,7 +7664,7 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init, } // Suggest adding 'constexpr' in C++11 for literal types. - } else if (getLangOpts().CPlusPlus11 && DclT->isLiteralType()) { + } else if (getLangOpts().CPlusPlus11 && DclT->isLiteralType(Context)) { Diag(VDecl->getLocation(), diag::err_in_class_initializer_literal_type) << DclT << Init->getSourceRange() << FixItHint::CreateInsertion(VDecl->getLocStart(), "constexpr "); @@ -7616,6 +7685,19 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init, // C99 6.7.8p4. All file scoped initializers need to be constant. if (!getLangOpts().CPlusPlus && !VDecl->isInvalidDecl()) CheckForConstantInitializer(Init, DclT); + else if (VDecl->getTLSKind() == VarDecl::TLS_Static && + !VDecl->isInvalidDecl() && !DclT->isDependentType() && + !Init->isValueDependent() && !VDecl->isConstexpr() && + !Init->isConstantInitializer( + Context, VDecl->getType()->isReferenceType())) { + // GNU C++98 edits for __thread, [basic.start.init]p4: + // An object of thread storage duration shall not require dynamic + // initialization. + // FIXME: Need strict checking here. + Diag(VDecl->getLocation(), diag::err_thread_dynamic_init); + if (getLangOpts().CPlusPlus11) + Diag(VDecl->getLocation(), diag::note_use_thread_local); + } } // We will represent direct-initialization similarly to copy-initialization: @@ -7870,9 +7952,9 @@ void Sema::ActOnUninitializedDecl(Decl *RealDecl, InitializedEntity Entity = InitializedEntity::InitializeVariable(Var); InitializationKind Kind = InitializationKind::CreateDefault(Var->getLocation()); - - InitializationSequence InitSeq(*this, Entity, Kind, 0, 0); - ExprResult Init = InitSeq.Perform(*this, Entity, Kind, MultiExprArg()); + + InitializationSequence InitSeq(*this, Entity, Kind, None); + ExprResult Init = InitSeq.Perform(*this, Entity, Kind, None); if (Init.isInvalid()) Var->setInvalidDecl(); else if (Init.get()) { @@ -7961,6 +8043,16 @@ void Sema::CheckCompleteVariableDeclaration(VarDecl *var) { Diag(var->getLocation(), diag::warn_missing_variable_declarations) << var; } + if (var->getTLSKind() == VarDecl::TLS_Static && + var->getType().isDestructedType()) { + // GNU C++98 edits for __thread, [basic.start.term]p3: + // The type of an object with thread storage duration shall not + // have a non-trivial destructor. + Diag(var->getLocation(), diag::err_thread_nontrivial_dtor); + if (getLangOpts().CPlusPlus11) + Diag(var->getLocation(), diag::note_use_thread_local); + } + // All the following checks are C++ only. if (!getLangOpts().CPlusPlus) return; @@ -8106,7 +8198,7 @@ Sema::FinalizeDeclaratorGroup(Scope *S, const DeclSpec &DS, getASTContext().addUnnamedTag(Tag); return BuildDeclaratorGroup(Decls.data(), Decls.size(), - DS.getTypeSpecType() == DeclSpec::TST_auto); + DS.containsPlaceholderType()); } /// BuildDeclaratorGroup - convert a list of declarations into a declaration @@ -8131,8 +8223,8 @@ Sema::BuildDeclaratorGroup(Decl **Group, unsigned NumDecls, // Don't reissue diagnostics when instantiating a template. if (AT && D->isInvalidDecl()) break; - if (AT && AT->isDeduced()) { - QualType U = AT->getDeducedType(); + QualType U = AT ? AT->getDeducedType() : QualType(); + if (!U.isNull()) { CanQualType UCanon = Context.getCanonicalType(U); if (Deduced.isNull()) { Deduced = U; @@ -8141,6 +8233,7 @@ Sema::BuildDeclaratorGroup(Decl **Group, unsigned NumDecls, } else if (DeducedCanon != UCanon) { Diag(D->getTypeSourceInfo()->getTypeLoc().getBeginLoc(), diag::err_auto_different_deductions) + << (AT->isDecltypeAuto() ? 1 : 0) << Deduced << DeducedDecl->getDeclName() << U << D->getDeclName() << DeducedDecl->getInit()->getSourceRange() @@ -8222,13 +8315,14 @@ Decl *Sema::ActOnParamDeclarator(Scope *S, Declarator &D) { D.getMutableDeclSpec().ClearStorageClassSpecs(); } - if (D.getDeclSpec().isThreadSpecified()) - Diag(D.getDeclSpec().getThreadSpecLoc(), diag::err_invalid_thread); - if (D.getDeclSpec().isConstexprSpecified()) - Diag(D.getDeclSpec().getConstexprSpecLoc(), diag::err_invalid_constexpr) + if (DeclSpec::TSCS TSCS = DS.getThreadStorageClassSpec()) + Diag(DS.getThreadStorageClassSpecLoc(), diag::err_invalid_thread) + << DeclSpec::getSpecifierName(TSCS); + if (DS.isConstexprSpecified()) + Diag(DS.getConstexprSpecLoc(), diag::err_invalid_constexpr) << 0; - DiagnoseFunctionSpecifiers(D.getDeclSpec()); + DiagnoseFunctionSpecifiers(DS); TypeSourceInfo *TInfo = GetTypeForDeclarator(D, S); QualType parmDeclType = TInfo->getType(); @@ -8787,6 +8881,21 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body, if (FD) { FD->setBody(Body); + if (getLangOpts().CPlusPlus1y && !FD->isInvalidDecl() && + !FD->isDependentContext()) { + if (FD->getResultType()->isUndeducedType()) { + // If the function has a deduced result type but contains no 'return' + // statements, the result type as written must be exactly 'auto', and + // the deduced result type is 'void'. + if (!FD->getResultType()->getAs()) { + Diag(dcl->getLocation(), diag::err_auto_fn_no_return_but_not_auto) + << FD->getResultType(); + FD->setInvalidDecl(); + } + Context.adjustDeducedFunctionResultType(FD, Context.VoidTy); + } + } + // The only way to be included in UndefinedButUsed is if there is an // ODR use before the definition. Avoid the expensive map lookup if this // is the first declaration. @@ -10348,8 +10457,10 @@ FieldDecl *Sema::HandleField(Scope *S, RecordDecl *Record, DiagnoseFunctionSpecifiers(D.getDeclSpec()); - if (D.getDeclSpec().isThreadSpecified()) - Diag(D.getDeclSpec().getThreadSpecLoc(), diag::err_invalid_thread); + if (DeclSpec::TSCS TSCS = D.getDeclSpec().getThreadStorageClassSpec()) + Diag(D.getDeclSpec().getThreadStorageClassSpecLoc(), + diag::err_invalid_thread) + << DeclSpec::getSpecifierName(TSCS); // Check to see if this name was declared as a member previously NamedDecl *PrevDecl = 0; @@ -10557,8 +10668,8 @@ FieldDecl *Sema::CheckFieldDecl(DeclarationName Name, QualType T, // FIXME: We need to pass in the attributes given an AST // representation, not a parser representation. if (D) { - // FIXME: What to pass instead of TUScope? - ProcessDeclAttributes(TUScope, NewFD, *D); + // FIXME: The current scope is almost... but not entirely... correct here. + ProcessDeclAttributes(getCurScope(), NewFD, *D); if (NewFD->hasAttrs()) CheckAlignasUnderalignment(NewFD); @@ -11497,8 +11608,8 @@ struct DenseMapInfoDupKey { // Emits a warning when an element is implicitly set a value that // a previous element has already been set to. -static void CheckForDuplicateEnumValues(Sema &S, Decl **Elements, - unsigned NumElements, EnumDecl *Enum, +static void CheckForDuplicateEnumValues(Sema &S, ArrayRef Elements, + EnumDecl *Enum, QualType EnumType) { if (S.Diags.getDiagnosticLevel(diag::warn_duplicate_enum_values, Enum->getLocation()) == @@ -11524,8 +11635,8 @@ static void CheckForDuplicateEnumValues(Sema &S, Decl **Elements, // Populate the EnumMap with all values represented by enum constants without // an initialier. - for (unsigned i = 0; i < NumElements; ++i) { - EnumConstantDecl *ECD = cast(Elements[i]); + for (unsigned i = 0, e = Elements.size(); i != e; ++i) { + EnumConstantDecl *ECD = cast_or_null(Elements[i]); // Null EnumConstantDecl means a previous diagnostic has been emitted for // this constant. Skip this enum since it may be ill-formed. @@ -11545,7 +11656,7 @@ static void CheckForDuplicateEnumValues(Sema &S, Decl **Elements, } // Create vectors for any values that has duplicates. - for (unsigned i = 0; i < NumElements; ++i) { + for (unsigned i = 0, e = Elements.size(); i != e; ++i) { EnumConstantDecl *ECD = cast(Elements[i]); if (!ValidDuplicateEnum(ECD, Enum)) continue; @@ -11609,7 +11720,7 @@ static void CheckForDuplicateEnumValues(Sema &S, Decl **Elements, void Sema::ActOnEnumBody(SourceLocation EnumLoc, SourceLocation LBraceLoc, SourceLocation RBraceLoc, Decl *EnumDeclX, - Decl **Elements, unsigned NumElements, + ArrayRef Elements, Scope *S, AttributeList *Attr) { EnumDecl *Enum = cast(EnumDeclX); QualType EnumType = Context.getTypeDeclType(Enum); @@ -11618,7 +11729,7 @@ void Sema::ActOnEnumBody(SourceLocation EnumLoc, SourceLocation LBraceLoc, ProcessDeclAttributeList(S, Enum, Attr); if (Enum->isDependentType()) { - for (unsigned i = 0; i != NumElements; ++i) { + for (unsigned i = 0, e = Elements.size(); i != e; ++i) { EnumConstantDecl *ECD = cast_or_null(Elements[i]); if (!ECD) continue; @@ -11645,7 +11756,7 @@ void Sema::ActOnEnumBody(SourceLocation EnumLoc, SourceLocation LBraceLoc, // Keep track of whether all elements have type int. bool AllElementsInt = true; - for (unsigned i = 0; i != NumElements; ++i) { + for (unsigned i = 0, e = Elements.size(); i != e; ++i) { EnumConstantDecl *ECD = cast_or_null(Elements[i]); if (!ECD) continue; // Already issued a diagnostic. @@ -11762,7 +11873,7 @@ void Sema::ActOnEnumBody(SourceLocation EnumLoc, SourceLocation LBraceLoc, // Loop over all of the enumerator constants, changing their types to match // the type of the enum if needed. - for (unsigned i = 0; i != NumElements; ++i) { + for (unsigned i = 0, e = Elements.size(); i != e; ++i) { EnumConstantDecl *ECD = cast_or_null(Elements[i]); if (!ECD) continue; // Already issued a diagnostic. @@ -11830,7 +11941,7 @@ void Sema::ActOnEnumBody(SourceLocation EnumLoc, SourceLocation LBraceLoc, if (InFunctionDeclarator) DeclsInPrototypeScope.push_back(Enum); - CheckForDuplicateEnumValues(*this, Elements, NumElements, Enum, EnumType); + CheckForDuplicateEnumValues(*this, Elements, Enum, EnumType); // Now that the enum type is defined, ensure it's not been underaligned. if (Enum->hasAttrs()) diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp index 982e7a5dd81..7b3345a3321 100644 --- a/lib/Sema/SemaDeclAttr.cpp +++ b/lib/Sema/SemaDeclAttr.cpp @@ -284,7 +284,7 @@ static bool mayBeSharedVariable(const Decl *D) { if (isa(D)) return true; if (const VarDecl *vd = dyn_cast(D)) - return (vd->hasGlobalStorage() && !(vd->isThreadSpecified())); + return vd->hasGlobalStorage() && !vd->getTLSKind(); return false; } @@ -709,7 +709,7 @@ static bool checkAcquireOrderAttrCommon(Sema &S, Decl *D, // Check that all arguments are lockable objects. checkAttrArgsAreLockableObjs(S, D, Attr, Args); - if (Args.size() == 0) + if (Args.empty()) return false; return true; @@ -858,7 +858,7 @@ static bool checkLocksRequiredCommon(Sema &S, Decl *D, // check that all arguments are lockable objects checkAttrArgsAreLockableObjs(S, D, Attr, Args); - if (Args.size() == 0) + if (Args.empty()) return false; return true; @@ -1656,7 +1656,7 @@ static void handleTLSModelAttr(Sema &S, Decl *D, return; } - if (!isa(D) || !cast(D)->isThreadSpecified()) { + if (!isa(D) || !cast(D)->getTLSKind()) { S.Diag(Attr.getLoc(), diag::err_attribute_wrong_decl_type) << Attr.getName() << ExpectedTLSVar; return; @@ -2246,8 +2246,11 @@ AvailabilityAttr *Sema::mergeAvailabilityAttr(NamedDecl *D, SourceRange Range, MergedObsoleted == Obsoleted) return NULL; + // Only create a new attribute if !Override, but we want to do + // the checking. if (!checkAvailabilityAttr(*this, Range, Platform, MergedIntroduced, - MergedDeprecated, MergedObsoleted)) { + MergedDeprecated, MergedObsoleted) && + !Override) { return ::new (Context) AvailabilityAttr(Range, Context, Platform, Introduced, Deprecated, Obsoleted, IsUnavailable, Message, @@ -4886,6 +4889,7 @@ static void ProcessInheritableDeclAttr(Sema &S, Scope *scope, Decl *D, break; // Microsoft attributes: + case AttributeList::AT_MsProperty: break; case AttributeList::AT_MsStruct: handleMsStructAttr(S, D, Attr); break; diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 35890e6deb4..e6a131a0761 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -65,6 +65,7 @@ namespace { bool VisitDeclRefExpr(DeclRefExpr *DRE); bool VisitCXXThisExpr(CXXThisExpr *ThisE); bool VisitLambdaExpr(LambdaExpr *Lambda); + bool VisitPseudoObjectExpr(PseudoObjectExpr *POE); }; /// VisitExpr - Visit all of the children of this expression. @@ -115,6 +116,23 @@ namespace { << ThisE->getSourceRange(); } + bool CheckDefaultArgumentVisitor::VisitPseudoObjectExpr(PseudoObjectExpr *POE) { + bool Invalid = false; + for (PseudoObjectExpr::semantics_iterator + i = POE->semantics_begin(), e = POE->semantics_end(); i != e; ++i) { + Expr *E = *i; + + // Look through bindings. + if (OpaqueValueExpr *OVE = dyn_cast(E)) { + E = OVE->getSourceExpr(); + assert(E && "pseudo-object binding without source expression?"); + } + + Invalid |= Visit(E); + } + return Invalid; + } + bool CheckDefaultArgumentVisitor::VisitLambdaExpr(LambdaExpr *Lambda) { // C++11 [expr.lambda.prim]p13: // A lambda-expression appearing in a default argument shall not @@ -127,8 +145,9 @@ namespace { } } -void Sema::ImplicitExceptionSpecification::CalledDecl(SourceLocation CallLoc, - CXXMethodDecl *Method) { +void +Sema::ImplicitExceptionSpecification::CalledDecl(SourceLocation CallLoc, + const CXXMethodDecl *Method) { // If we have an MSAny spec already, don't bother. if (!Method || ComputedEST == EST_MSAny) return; @@ -246,7 +265,7 @@ Sema::SetParamDefaultArgument(ParmVarDecl *Param, Expr *Arg, Param); InitializationKind Kind = InitializationKind::CreateCopy(Param->getLocation(), EqualLoc); - InitializationSequence InitSeq(*this, Entity, Kind, &Arg, 1); + InitializationSequence InitSeq(*this, Entity, Kind, Arg); ExprResult Result = InitSeq.Perform(*this, Entity, Kind, Arg); if (Result.isInvalid()) return true; @@ -614,25 +633,11 @@ void Sema::CheckCXXDefaultArguments(FunctionDecl *FD) { unsigned NumParams = FD->getNumParams(); unsigned p; - bool IsLambda = FD->getOverloadedOperator() == OO_Call && - isa(FD) && - cast(FD)->getParent()->isLambda(); - // Find first parameter with a default argument for (p = 0; p < NumParams; ++p) { ParmVarDecl *Param = FD->getParamDecl(p); - if (Param->hasDefaultArg()) { - // C++11 [expr.prim.lambda]p5: - // [...] Default arguments (8.3.6) shall not be specified in the - // parameter-declaration-clause of a lambda-declarator. - // - // FIXME: Core issue 974 strikes this sentence, we only provide an - // extension warning. - if (IsLambda) - Diag(Param->getLocation(), diag::ext_lambda_default_arguments) - << Param->getDefaultArgRange(); + if (Param->hasDefaultArg()) break; - } } // C++ [dcl.fct.default]p4: @@ -770,12 +775,13 @@ bool Sema::CheckConstexprFunctionDecl(const FunctionDecl *NewFD) { } /// Check the given declaration statement is legal within a constexpr function -/// body. C++0x [dcl.constexpr]p3,p4. +/// body. C++11 [dcl.constexpr]p3,p4, and C++1y [dcl.constexpr]p3. /// -/// \return true if the body is OK, false if we have diagnosed a problem. +/// \return true if the body is OK (maybe only as an extension), false if we +/// have diagnosed a problem. static bool CheckConstexprDeclStmt(Sema &SemaRef, const FunctionDecl *Dcl, - DeclStmt *DS) { - // C++0x [dcl.constexpr]p3 and p4: + DeclStmt *DS, SourceLocation &Cxx1yLoc) { + // C++11 [dcl.constexpr]p3 and p4: // The definition of a constexpr function(p3) or constructor(p4) [...] shall // contain only for (DeclStmt::decl_iterator DclIt = DS->decl_begin(), @@ -786,6 +792,7 @@ static bool CheckConstexprDeclStmt(Sema &SemaRef, const FunctionDecl *Dcl, case Decl::UsingShadow: case Decl::UsingDirective: case Decl::UnresolvedUsingTypename: + case Decl::UnresolvedUsingValue: // - static_assert-declarations // - using-declarations, // - using-directives, @@ -809,20 +816,63 @@ static bool CheckConstexprDeclStmt(Sema &SemaRef, const FunctionDecl *Dcl, case Decl::Enum: case Decl::CXXRecord: - // As an extension, we allow the declaration (but not the definition) of - // classes and enumerations in all declarations, not just in typedef and - // alias declarations. - if (cast(*DclIt)->isThisDeclarationADefinition()) { - SemaRef.Diag(DS->getLocStart(), diag::err_constexpr_type_definition) + // C++1y allows types to be defined, not just declared. + if (cast(*DclIt)->isThisDeclarationADefinition()) + SemaRef.Diag(DS->getLocStart(), + SemaRef.getLangOpts().CPlusPlus1y + ? diag::warn_cxx11_compat_constexpr_type_definition + : diag::ext_constexpr_type_definition) << isa(Dcl); - return false; - } continue; - case Decl::Var: - SemaRef.Diag(DS->getLocStart(), diag::err_constexpr_var_declaration) + case Decl::EnumConstant: + case Decl::IndirectField: + case Decl::ParmVar: + // These can only appear with other declarations which are banned in + // C++11 and permitted in C++1y, so ignore them. + continue; + + case Decl::Var: { + // C++1y [dcl.constexpr]p3 allows anything except: + // a definition of a variable of non-literal type or of static or + // thread storage duration or for which no initialization is performed. + VarDecl *VD = cast(*DclIt); + if (VD->isThisDeclarationADefinition()) { + if (VD->isStaticLocal()) { + SemaRef.Diag(VD->getLocation(), + diag::err_constexpr_local_var_static) + << isa(Dcl) + << (VD->getTLSKind() == VarDecl::TLS_Dynamic); + return false; + } + if (!VD->getType()->isDependentType() && + SemaRef.RequireLiteralType( + VD->getLocation(), VD->getType(), + diag::err_constexpr_local_var_non_literal_type, + isa(Dcl))) + return false; + if (!VD->hasInit()) { + SemaRef.Diag(VD->getLocation(), + diag::err_constexpr_local_var_no_init) + << isa(Dcl); + return false; + } + } + SemaRef.Diag(VD->getLocation(), + SemaRef.getLangOpts().CPlusPlus1y + ? diag::warn_cxx11_compat_constexpr_local_var + : diag::ext_constexpr_local_var) << isa(Dcl); - return false; + continue; + } + + case Decl::NamespaceAlias: + case Decl::Function: + // These are disallowed in C++11 and permitted in C++1y. Allow them + // everywhere as an extension. + if (!Cxx1yLoc.isValid()) + Cxx1yLoc = DS->getLocStart(); + continue; default: SemaRef.Diag(DS->getLocStart(), diag::err_constexpr_body_invalid_stmt) @@ -871,6 +921,124 @@ static void CheckConstexprCtorInitializer(Sema &SemaRef, } } +/// Check the provided statement is allowed in a constexpr function +/// definition. +static bool +CheckConstexprFunctionStmt(Sema &SemaRef, const FunctionDecl *Dcl, Stmt *S, + llvm::SmallVectorImpl &ReturnStmts, + SourceLocation &Cxx1yLoc) { + // - its function-body shall be [...] a compound-statement that contains only + switch (S->getStmtClass()) { + case Stmt::NullStmtClass: + // - null statements, + return true; + + case Stmt::DeclStmtClass: + // - static_assert-declarations + // - using-declarations, + // - using-directives, + // - typedef declarations and alias-declarations that do not define + // classes or enumerations, + if (!CheckConstexprDeclStmt(SemaRef, Dcl, cast(S), Cxx1yLoc)) + return false; + return true; + + case Stmt::ReturnStmtClass: + // - and exactly one return statement; + if (isa(Dcl)) { + // C++1y allows return statements in constexpr constructors. + if (!Cxx1yLoc.isValid()) + Cxx1yLoc = S->getLocStart(); + return true; + } + + ReturnStmts.push_back(S->getLocStart()); + return true; + + case Stmt::CompoundStmtClass: { + // C++1y allows compound-statements. + if (!Cxx1yLoc.isValid()) + Cxx1yLoc = S->getLocStart(); + + CompoundStmt *CompStmt = cast(S); + for (CompoundStmt::body_iterator BodyIt = CompStmt->body_begin(), + BodyEnd = CompStmt->body_end(); BodyIt != BodyEnd; ++BodyIt) { + if (!CheckConstexprFunctionStmt(SemaRef, Dcl, *BodyIt, ReturnStmts, + Cxx1yLoc)) + return false; + } + return true; + } + + case Stmt::AttributedStmtClass: + if (!Cxx1yLoc.isValid()) + Cxx1yLoc = S->getLocStart(); + return true; + + case Stmt::IfStmtClass: { + // C++1y allows if-statements. + if (!Cxx1yLoc.isValid()) + Cxx1yLoc = S->getLocStart(); + + IfStmt *If = cast(S); + if (!CheckConstexprFunctionStmt(SemaRef, Dcl, If->getThen(), ReturnStmts, + Cxx1yLoc)) + return false; + if (If->getElse() && + !CheckConstexprFunctionStmt(SemaRef, Dcl, If->getElse(), ReturnStmts, + Cxx1yLoc)) + return false; + return true; + } + + case Stmt::WhileStmtClass: + case Stmt::DoStmtClass: + case Stmt::ForStmtClass: + case Stmt::CXXForRangeStmtClass: + case Stmt::ContinueStmtClass: + // C++1y allows all of these. We don't allow them as extensions in C++11, + // because they don't make sense without variable mutation. + if (!SemaRef.getLangOpts().CPlusPlus1y) + break; + if (!Cxx1yLoc.isValid()) + Cxx1yLoc = S->getLocStart(); + for (Stmt::child_range Children = S->children(); Children; ++Children) + if (*Children && + !CheckConstexprFunctionStmt(SemaRef, Dcl, *Children, ReturnStmts, + Cxx1yLoc)) + return false; + return true; + + case Stmt::SwitchStmtClass: + case Stmt::CaseStmtClass: + case Stmt::DefaultStmtClass: + case Stmt::BreakStmtClass: + // C++1y allows switch-statements, and since they don't need variable + // mutation, we can reasonably allow them in C++11 as an extension. + if (!Cxx1yLoc.isValid()) + Cxx1yLoc = S->getLocStart(); + for (Stmt::child_range Children = S->children(); Children; ++Children) + if (*Children && + !CheckConstexprFunctionStmt(SemaRef, Dcl, *Children, ReturnStmts, + Cxx1yLoc)) + return false; + return true; + + default: + if (!isa(S)) + break; + + // C++1y allows expression-statements. + if (!Cxx1yLoc.isValid()) + Cxx1yLoc = S->getLocStart(); + return true; + } + + SemaRef.Diag(S->getLocStart(), diag::err_constexpr_body_invalid_stmt) + << isa(Dcl); + return false; +} + /// Check the body for the given constexpr function declaration only contains /// the permitted types of statement. C++11 [dcl.constexpr]p3,p4. /// @@ -891,43 +1059,24 @@ bool Sema::CheckConstexprFunctionBody(const FunctionDecl *Dcl, Stmt *Body) { return false; } + SmallVector ReturnStmts; + // - its function-body shall be [...] a compound-statement that contains only + // [... list of cases ...] CompoundStmt *CompBody = cast(Body); - - SmallVector ReturnStmts; + SourceLocation Cxx1yLoc; for (CompoundStmt::body_iterator BodyIt = CompBody->body_begin(), BodyEnd = CompBody->body_end(); BodyIt != BodyEnd; ++BodyIt) { - switch ((*BodyIt)->getStmtClass()) { - case Stmt::NullStmtClass: - // - null statements, - continue; - - case Stmt::DeclStmtClass: - // - static_assert-declarations - // - using-declarations, - // - using-directives, - // - typedef declarations and alias-declarations that do not define - // classes or enumerations, - if (!CheckConstexprDeclStmt(*this, Dcl, cast(*BodyIt))) - return false; - continue; - - case Stmt::ReturnStmtClass: - // - and exactly one return statement; - if (isa(Dcl)) - break; - - ReturnStmts.push_back((*BodyIt)->getLocStart()); - continue; - - default: - break; - } + if (!CheckConstexprFunctionStmt(*this, Dcl, *BodyIt, ReturnStmts, Cxx1yLoc)) + return false; + } - Diag((*BodyIt)->getLocStart(), diag::err_constexpr_body_invalid_stmt) + if (Cxx1yLoc.isValid()) + Diag(Cxx1yLoc, + getLangOpts().CPlusPlus1y + ? diag::warn_cxx11_compat_constexpr_body_invalid_stmt + : diag::ext_constexpr_body_invalid_stmt) << isa(Dcl); - return false; - } if (const CXXConstructorDecl *Constructor = dyn_cast(Dcl)) { @@ -983,14 +1132,23 @@ bool Sema::CheckConstexprFunctionBody(const FunctionDecl *Dcl, Stmt *Body) { } } else { if (ReturnStmts.empty()) { - Diag(Dcl->getLocation(), diag::err_constexpr_body_no_return); - return false; + // C++1y doesn't require constexpr functions to contain a 'return' + // statement. We still do, unless the return type is void, because + // otherwise if there's no return statement, the function cannot + // be used in a core constant expression. + bool OK = getLangOpts().CPlusPlus1y && Dcl->getResultType()->isVoidType(); + Diag(Dcl->getLocation(), + OK ? diag::warn_cxx11_compat_constexpr_body_no_return + : diag::err_constexpr_body_no_return); + return OK; } if (ReturnStmts.size() > 1) { - Diag(ReturnStmts.back(), diag::err_constexpr_body_multiple_return); + Diag(ReturnStmts.back(), + getLangOpts().CPlusPlus1y + ? diag::warn_cxx11_compat_constexpr_body_multiple_return + : diag::ext_constexpr_body_multiple_return); for (unsigned I = 0; I < ReturnStmts.size() - 1; ++I) Diag(ReturnStmts[I], diag::note_constexpr_body_previous_return); - return false; } } @@ -1584,6 +1742,13 @@ static bool InitializationHasSideEffects(const FieldDecl &FD) { return false; } +static AttributeList *getMSPropertyAttr(AttributeList *list) { + for (AttributeList* it = list; it != 0; it = it->getNext()) + if (it->isDeclspecPropertyAttribute()) + return it; + return 0; +} + /// 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 @@ -1660,30 +1825,24 @@ Sema::ActOnCXXMemberDeclarator(Scope *S, AccessSpecifier AS, Declarator &D, // data members and cannot be applied to names declared const or static, // and cannot be applied to reference members. switch (DS.getStorageClassSpec()) { - case DeclSpec::SCS_unspecified: - case DeclSpec::SCS_typedef: - case DeclSpec::SCS_static: - // FALL THROUGH. - break; - case DeclSpec::SCS_mutable: - if (isFunc) { - if (DS.getStorageClassSpecLoc().isValid()) - Diag(DS.getStorageClassSpecLoc(), diag::err_mutable_function); - else - Diag(DS.getThreadSpecLoc(), diag::err_mutable_function); + case DeclSpec::SCS_unspecified: + case DeclSpec::SCS_typedef: + case DeclSpec::SCS_static: + break; + case DeclSpec::SCS_mutable: + if (isFunc) { + Diag(DS.getStorageClassSpecLoc(), diag::err_mutable_function); - // FIXME: It would be nicer if the keyword was ignored only for this - // declarator. Otherwise we could get follow-up errors. - D.getMutableDeclSpec().ClearStorageClassSpecs(); - } - break; - default: - if (DS.getStorageClassSpecLoc().isValid()) - Diag(DS.getStorageClassSpecLoc(), - diag::err_storageclass_invalid_for_member); - else - Diag(DS.getThreadSpecLoc(), diag::err_storageclass_invalid_for_member); + // FIXME: It would be nicer if the keyword was ignored only for this + // declarator. Otherwise we could get follow-up errors. D.getMutableDeclSpec().ClearStorageClassSpecs(); + } + break; + default: + Diag(DS.getStorageClassSpecLoc(), + diag::err_storageclass_invalid_for_member); + D.getMutableDeclSpec().ClearStorageClassSpecs(); + break; } bool isInstField = ((DS.getStorageClassSpec() == DeclSpec::SCS_unspecified || @@ -1769,8 +1928,16 @@ Sema::ActOnCXXMemberDeclarator(Scope *S, AccessSpecifier AS, Declarator &D, SS.clear(); } - Member = HandleField(S, cast(CurContext), Loc, D, BitWidth, - InitStyle, AS); + AttributeList *MSPropertyAttr = + getMSPropertyAttr(D.getDeclSpec().getAttributes().getList()); + if (MSPropertyAttr) { + Member = HandleMSProperty(S, cast(CurContext), Loc, D, + BitWidth, InitStyle, AS, MSPropertyAttr); + isInstField = false; + } else { + Member = HandleField(S, cast(CurContext), Loc, D, + BitWidth, InitStyle, AS); + } assert(Member && "HandleField never returns null"); } else { assert(InitStyle == ICIS_NoInit || D.getDeclSpec().getStorageClassSpec() == DeclSpec::SCS_static); @@ -1990,14 +2157,12 @@ Sema::ActOnCXXInClassMemberInitializer(Decl *D, SourceLocation InitLoc, Diag(FD->getLocation(), diag::warn_dangling_std_initializer_list) << /*at end of ctor*/1 << InitExpr->getSourceRange(); } - Expr **Inits = &InitExpr; - unsigned NumInits = 1; InitializedEntity Entity = InitializedEntity::InitializeMember(FD); InitializationKind Kind = FD->getInClassInitStyle() == ICIS_ListInit ? InitializationKind::CreateDirectList(InitExpr->getLocStart()) : InitializationKind::CreateCopy(InitExpr->getLocStart(), InitLoc); - InitializationSequence Seq(*this, Entity, Kind, Inits, NumInits); - Init = Seq.Perform(*this, Entity, Kind, MultiExprArg(Inits, NumInits)); + InitializationSequence Seq(*this, Entity, Kind, InitExpr); + Init = Seq.Perform(*this, Entity, Kind, InitExpr); if (Init.isInvalid()) { FD->setInvalidDecl(); return; @@ -2360,23 +2525,19 @@ Sema::BuildMemberInitializer(ValueDecl *Member, Expr *Init, // foo(foo) // where foo is not also a parameter to the constructor. // TODO: implement -Wuninitialized and fold this into that framework. - Expr **Args; - unsigned NumArgs; + MultiExprArg Args; if (ParenListExpr *ParenList = dyn_cast(Init)) { - Args = ParenList->getExprs(); - NumArgs = ParenList->getNumExprs(); + Args = MultiExprArg(ParenList->getExprs(), ParenList->getNumExprs()); } else if (InitListExpr *InitList = dyn_cast(Init)) { - Args = InitList->getInits(); - NumArgs = InitList->getNumInits(); + Args = MultiExprArg(InitList->getInits(), InitList->getNumInits()); } else { // Template instantiation doesn't reconstruct ParenListExprs for us. - Args = &Init; - NumArgs = 1; + Args = Init; } if (getDiagnostics().getDiagnosticLevel(diag::warn_field_is_uninit, IdLoc) != DiagnosticsEngine::Ignored) - for (unsigned i = 0; i < NumArgs; ++i) + for (unsigned i = 0, e = Args.size(); i != e; ++i) // FIXME: Warn about the case when other fields are used before being // initialized. 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 @@ -2397,8 +2558,7 @@ Sema::BuildMemberInitializer(ValueDecl *Member, Expr *Init, bool InitList = false; if (isa(Init)) { InitList = true; - Args = &Init; - NumArgs = 1; + Args = Init; if (isStdInitializerList(Member->getType(), 0)) { Diag(IdLoc, diag::warn_dangling_std_initializer_list) @@ -2415,10 +2575,8 @@ Sema::BuildMemberInitializer(ValueDecl *Member, Expr *Init, : InitializationKind::CreateDirect(IdLoc, InitRange.getBegin(), InitRange.getEnd()); - InitializationSequence InitSeq(*this, MemberEntity, Kind, Args, NumArgs); - ExprResult MemberInit = InitSeq.Perform(*this, MemberEntity, Kind, - MultiExprArg(Args, NumArgs), - 0); + InitializationSequence InitSeq(*this, MemberEntity, Kind, Args); + ExprResult MemberInit = InitSeq.Perform(*this, MemberEntity, Kind, Args, 0); if (MemberInit.isInvalid()) return true; @@ -2454,12 +2612,10 @@ Sema::BuildDelegatingInitializer(TypeSourceInfo *TInfo, Expr *Init, Diag(NameLoc, diag::warn_cxx98_compat_delegating_ctor); bool InitList = true; - Expr **Args = &Init; - unsigned NumArgs = 1; + MultiExprArg Args = Init; if (ParenListExpr *ParenList = dyn_cast(Init)) { InitList = false; - Args = ParenList->getExprs(); - NumArgs = ParenList->getNumExprs(); + Args = MultiExprArg(ParenList->getExprs(), ParenList->getNumExprs()); } SourceRange InitRange = Init->getSourceRange(); @@ -2470,10 +2626,9 @@ Sema::BuildDelegatingInitializer(TypeSourceInfo *TInfo, Expr *Init, InitList ? InitializationKind::CreateDirectList(NameLoc) : InitializationKind::CreateDirect(NameLoc, InitRange.getBegin(), InitRange.getEnd()); - InitializationSequence InitSeq(*this, DelegationEntity, Kind, Args, NumArgs); + InitializationSequence InitSeq(*this, DelegationEntity, Kind, Args); ExprResult DelegationInit = InitSeq.Perform(*this, DelegationEntity, Kind, - MultiExprArg(Args, NumArgs), - 0); + Args, 0); if (DelegationInit.isInvalid()) return true; @@ -2593,12 +2748,10 @@ Sema::BuildBaseInitializer(QualType BaseType, TypeSourceInfo *BaseTInfo, // Initialize the base. bool InitList = true; - Expr **Args = &Init; - unsigned NumArgs = 1; + MultiExprArg Args = Init; if (ParenListExpr *ParenList = dyn_cast(Init)) { InitList = false; - Args = ParenList->getExprs(); - NumArgs = ParenList->getNumExprs(); + Args = MultiExprArg(ParenList->getExprs(), ParenList->getNumExprs()); } InitializedEntity BaseEntity = @@ -2607,9 +2760,8 @@ Sema::BuildBaseInitializer(QualType BaseType, TypeSourceInfo *BaseTInfo, InitList ? InitializationKind::CreateDirectList(BaseLoc) : InitializationKind::CreateDirect(BaseLoc, InitRange.getBegin(), InitRange.getEnd()); - InitializationSequence InitSeq(*this, BaseEntity, Kind, Args, NumArgs); - ExprResult BaseInit = InitSeq.Perform(*this, BaseEntity, Kind, - MultiExprArg(Args, NumArgs), 0); + InitializationSequence InitSeq(*this, BaseEntity, Kind, Args); + ExprResult BaseInit = InitSeq.Perform(*this, BaseEntity, Kind, Args, 0); if (BaseInit.isInvalid()) return true; @@ -2695,8 +2847,7 @@ BuildImplicitBaseInitializer(Sema &SemaRef, CXXConstructorDecl *Constructor, InitializationKind InitKind = InitializationKind::CreateDirect( Constructor->getLocation(), SourceLocation(), SourceLocation()); - InitializationSequence InitSeq(SemaRef, InitEntity, InitKind, - Args.data(), Args.size()); + InitializationSequence InitSeq(SemaRef, InitEntity, InitKind, Args); BaseInit = InitSeq.Perform(SemaRef, InitEntity, InitKind, Args); break; } @@ -2705,8 +2856,8 @@ BuildImplicitBaseInitializer(Sema &SemaRef, CXXConstructorDecl *Constructor, case IIK_Default: { InitializationKind InitKind = InitializationKind::CreateDefault(Constructor->getLocation()); - InitializationSequence InitSeq(SemaRef, InitEntity, InitKind, 0, 0); - BaseInit = InitSeq.Perform(SemaRef, InitEntity, InitKind, MultiExprArg()); + InitializationSequence InitSeq(SemaRef, InitEntity, InitKind, None); + BaseInit = InitSeq.Perform(SemaRef, InitEntity, InitKind, None); break; } @@ -2743,10 +2894,8 @@ BuildImplicitBaseInitializer(Sema &SemaRef, CXXConstructorDecl *Constructor, InitializationKind InitKind = InitializationKind::CreateDirect(Constructor->getLocation(), SourceLocation(), SourceLocation()); - InitializationSequence InitSeq(SemaRef, InitEntity, InitKind, - &CopyCtorArg, 1); - BaseInit = InitSeq.Perform(SemaRef, InitEntity, InitKind, - MultiExprArg(&CopyCtorArg, 1)); + InitializationSequence InitSeq(SemaRef, InitEntity, InitKind, CopyCtorArg); + BaseInit = InitSeq.Perform(SemaRef, InitEntity, InitKind, CopyCtorArg); break; } } @@ -2897,8 +3046,7 @@ BuildImplicitMemberInitializer(Sema &SemaRef, CXXConstructorDecl *Constructor, InitializationKind::CreateDirect(Loc, SourceLocation(), SourceLocation()); Expr *CtorArgE = CtorArg.takeAs(); - InitializationSequence InitSeq(SemaRef, Entities.back(), InitKind, - &CtorArgE, 1); + InitializationSequence InitSeq(SemaRef, Entities.back(), InitKind, CtorArgE); ExprResult MemberInit = InitSeq.Perform(SemaRef, Entities.back(), InitKind, @@ -2936,10 +3084,10 @@ BuildImplicitMemberInitializer(Sema &SemaRef, CXXConstructorDecl *Constructor, : InitializedEntity::InitializeMember(Field); InitializationKind InitKind = InitializationKind::CreateDefault(Loc); - - InitializationSequence InitSeq(SemaRef, InitEntity, InitKind, 0, 0); - ExprResult MemberInit = - InitSeq.Perform(SemaRef, InitEntity, InitKind, MultiExprArg()); + + InitializationSequence InitSeq(SemaRef, InitEntity, InitKind, None); + ExprResult MemberInit = + InitSeq.Perform(SemaRef, InitEntity, InitKind, None); MemberInit = SemaRef.MaybeCreateExprWithCleanups(MemberInit); if (MemberInit.isInvalid()) @@ -3040,7 +3188,7 @@ struct BaseAndFieldInfo { AllToInit.push_back(Init); // Check whether this initializer makes the field "used". - if (Init->getInit() && Init->getInit()->HasSideEffects(S.Context)) + if (Init->getInit()->HasSideEffects(S.Context)) S.UnusedPrivateFields.remove(Init->getAnyMember()); return false; @@ -3089,16 +3237,18 @@ static bool CollectFieldInitializer(Sema &SemaRef, BaseAndFieldInfo &Info, // has a brace-or-equal-initializer, the entity is initialized as specified // in [dcl.init]. if (Field->hasInClassInitializer() && !Info.isImplicitCopyOrMove()) { + Expr *DIE = CXXDefaultInitExpr::Create(SemaRef.Context, + Info.Ctor->getLocation(), Field); CXXCtorInitializer *Init; if (Indirect) Init = new (SemaRef.Context) CXXCtorInitializer(SemaRef.Context, Indirect, SourceLocation(), - SourceLocation(), 0, + SourceLocation(), DIE, SourceLocation()); else Init = new (SemaRef.Context) CXXCtorInitializer(SemaRef.Context, Field, SourceLocation(), - SourceLocation(), 0, + SourceLocation(), DIE, SourceLocation()); return Info.addFieldInitializer(Init); } @@ -4226,9 +4376,8 @@ updateExceptionSpec(Sema &S, FunctionDecl *FD, const FunctionProtoType *FPT, const Sema::ImplicitExceptionSpecification &ExceptSpec) { FunctionProtoType::ExtProtoInfo EPI = FPT->getExtProtoInfo(); ExceptSpec.getEPI(EPI); - const FunctionProtoType *NewFPT = cast( - S.Context.getFunctionType(FPT->getResultType(), FPT->getArgTypes(), EPI)); - FD->setType(QualType(NewFPT, 0)); + FD->setType(S.Context.getFunctionType(FPT->getResultType(), + FPT->getArgTypes(), EPI)); } void Sema::EvaluateImplicitExceptionSpec(SourceLocation Loc, CXXMethodDecl *MD) { @@ -4427,7 +4576,7 @@ void Sema::CheckExplicitlyDefaultedMemberExceptionSpec( FunctionProtoType::ExtProtoInfo EPI; computeImplicitExceptionSpec(*this, MD->getLocation(), MD).getEPI(EPI); const FunctionProtoType *ImplicitType = cast( - Context.getFunctionType(Context.VoidTy, ArrayRef(), EPI)); + Context.getFunctionType(Context.VoidTy, None, EPI)); // Ensure that it matches. CheckEquivalentExceptionSpec( @@ -5409,8 +5558,10 @@ void Sema::DiagnoseHiddenVirtualMethods(CXXRecordDecl *DC, CXXMethodDecl *MD) { for (unsigned i = 0, e = Data.OverloadedMethods.size(); i != e; ++i) { CXXMethodDecl *overloadedMD = Data.OverloadedMethods[i]; - Diag(overloadedMD->getLocation(), + PartialDiagnostic PD = PDiag( diag::note_hidden_overloaded_virtual_declared_here) << overloadedMD; + HandleFunctionTypeMismatch(PD, MD->getType(), overloadedMD->getType()); + Diag(overloadedMD->getLocation(), PD); } } } @@ -5860,7 +6011,7 @@ QualType Sema::CheckDestructorDeclarator(Declarator &D, QualType R, EPI.Variadic = false; EPI.TypeQuals = 0; EPI.RefQualifier = RQ_None; - return Context.getFunctionType(Context.VoidTy, ArrayRef(), EPI); + return Context.getFunctionType(Context.VoidTy, None, EPI); } /// CheckConversionDeclarator - Called by ActOnDeclarator to check the @@ -5941,8 +6092,7 @@ void Sema::CheckConversionDeclarator(Declarator &D, QualType &R, // of the errors above fired) and with the conversion type as the // return type. if (D.isInvalidType()) - R = Context.getFunctionType(ConvType, ArrayRef(), - Proto->getExtProtoInfo()); + R = Context.getFunctionType(ConvType, None, Proto->getExtProtoInfo()); // C++0x explicit conversion operators. if (D.getDeclSpec().isExplicitSpecified()) @@ -7503,9 +7653,73 @@ Sema::ComputeDefaultedDefaultCtorExceptionSpec(SourceLocation Loc, } Sema::ImplicitExceptionSpecification -Sema::ComputeInheritingCtorExceptionSpec(CXXMethodDecl *MD) { +Sema::ComputeInheritingCtorExceptionSpec(CXXConstructorDecl *CD) { + CXXRecordDecl *ClassDecl = CD->getParent(); + + // C++ [except.spec]p14: + // An inheriting constructor [...] shall have an exception-specification. [...] ImplicitExceptionSpecification ExceptSpec(*this); - // FIXME: Compute the exception spec. + if (ClassDecl->isInvalidDecl()) + return ExceptSpec; + + // Inherited constructor. + const CXXConstructorDecl *InheritedCD = CD->getInheritedConstructor(); + const CXXRecordDecl *InheritedDecl = InheritedCD->getParent(); + // FIXME: Copying or moving the parameters could add extra exceptions to the + // set, as could the default arguments for the inherited constructor. This + // will be addressed when we implement the resolution of core issue 1351. + ExceptSpec.CalledDecl(CD->getLocStart(), InheritedCD); + + // Direct base-class constructors. + for (CXXRecordDecl::base_class_iterator B = ClassDecl->bases_begin(), + BEnd = ClassDecl->bases_end(); + B != BEnd; ++B) { + if (B->isVirtual()) // Handled below. + continue; + + if (const RecordType *BaseType = B->getType()->getAs()) { + CXXRecordDecl *BaseClassDecl = cast(BaseType->getDecl()); + if (BaseClassDecl == InheritedDecl) + continue; + CXXConstructorDecl *Constructor = LookupDefaultConstructor(BaseClassDecl); + if (Constructor) + ExceptSpec.CalledDecl(B->getLocStart(), Constructor); + } + } + + // Virtual base-class constructors. + for (CXXRecordDecl::base_class_iterator B = ClassDecl->vbases_begin(), + BEnd = ClassDecl->vbases_end(); + B != BEnd; ++B) { + if (const RecordType *BaseType = B->getType()->getAs()) { + CXXRecordDecl *BaseClassDecl = cast(BaseType->getDecl()); + if (BaseClassDecl == InheritedDecl) + continue; + CXXConstructorDecl *Constructor = LookupDefaultConstructor(BaseClassDecl); + if (Constructor) + ExceptSpec.CalledDecl(B->getLocStart(), Constructor); + } + } + + // Field constructors. + for (RecordDecl::field_iterator F = ClassDecl->field_begin(), + FEnd = ClassDecl->field_end(); + F != FEnd; ++F) { + if (F->hasInClassInitializer()) { + if (Expr *E = F->getInClassInitializer()) + ExceptSpec.CalledExpr(E); + else if (!F->isInvalidDecl()) + Diag(CD->getLocation(), + diag::err_in_class_initializer_references_def_ctor) << CD; + } else if (const RecordType *RecordTy + = Context.getBaseElementType(F->getType())->getAs()) { + CXXRecordDecl *FieldRecDecl = cast(RecordTy->getDecl()); + CXXConstructorDecl *Constructor = LookupDefaultConstructor(FieldRecDecl); + if (Constructor) + ExceptSpec.CalledDecl(F->getLocation(), Constructor); + } + } + return ExceptSpec; } @@ -7577,9 +7791,7 @@ CXXConstructorDecl *Sema::DeclareImplicitDefaultConstructor( FunctionProtoType::ExtProtoInfo EPI; EPI.ExceptionSpecType = EST_Unevaluated; EPI.ExceptionSpecDecl = DefaultCon; - DefaultCon->setType(Context.getFunctionType(Context.VoidTy, - ArrayRef(), - EPI)); + DefaultCon->setType(Context.getFunctionType(Context.VoidTy, None, EPI)); // We don't need to use SpecialMemberIsTrivial here; triviality for default // constructors is easy to compute. @@ -7635,208 +7847,308 @@ void Sema::ActOnFinishDelayedMemberInitializers(Decl *D) { CheckDelayedExplicitlyDefaultedMemberExceptionSpecs(); } -void Sema::DeclareInheritingConstructors(CXXRecordDecl *ClassDecl) { - // We start with an initial pass over the base classes to collect those that - // inherit constructors from. If there are none, we can forgo all further - // processing. - typedef SmallVector BasesVector; - BasesVector BasesToInheritFrom; - for (CXXRecordDecl::base_class_iterator BaseIt = ClassDecl->bases_begin(), - BaseE = ClassDecl->bases_end(); - BaseIt != BaseE; ++BaseIt) { - if (BaseIt->getInheritConstructors()) { - QualType Base = BaseIt->getType(); - if (Base->isDependentType()) { - // If we inherit constructors from anything that is dependent, just - // abort processing altogether. We'll get another chance for the - // instantiations. - // FIXME: We need to ensure that any call to a constructor of this class - // is considered instantiation-dependent in this case. - return; +namespace { +/// Information on inheriting constructors to declare. +class InheritingConstructorInfo { +public: + InheritingConstructorInfo(Sema &SemaRef, CXXRecordDecl *Derived) + : SemaRef(SemaRef), Derived(Derived) { + // Mark the constructors that we already have in the derived class. + // + // C++11 [class.inhctor]p3: [...] a constructor is implicitly declared [...] + // unless there is a user-declared constructor with the same signature in + // the class where the using-declaration appears. + visitAll(Derived, &InheritingConstructorInfo::noteDeclaredInDerived); + } + + void inheritAll(CXXRecordDecl *RD) { + visitAll(RD, &InheritingConstructorInfo::inherit); + } + +private: + /// Information about an inheriting constructor. + struct InheritingConstructor { + InheritingConstructor() + : DeclaredInDerived(false), BaseCtor(0), DerivedCtor(0) {} + + /// If \c true, a constructor with this signature is already declared + /// in the derived class. + bool DeclaredInDerived; + + /// The constructor which is inherited. + const CXXConstructorDecl *BaseCtor; + + /// The derived constructor we declared. + CXXConstructorDecl *DerivedCtor; + }; + + /// Inheriting constructors with a given canonical type. There can be at + /// most one such non-template constructor, and any number of templated + /// constructors. + struct InheritingConstructorsForType { + InheritingConstructor NonTemplate; + llvm::SmallVector< + std::pair, 4> Templates; + + InheritingConstructor &getEntry(Sema &S, const CXXConstructorDecl *Ctor) { + if (FunctionTemplateDecl *FTD = Ctor->getDescribedFunctionTemplate()) { + TemplateParameterList *ParamList = FTD->getTemplateParameters(); + for (unsigned I = 0, N = Templates.size(); I != N; ++I) + if (S.TemplateParameterListsAreEqual(ParamList, Templates[I].first, + false, S.TPL_TemplateMatch)) + return Templates[I].second; + Templates.push_back(std::make_pair(ParamList, InheritingConstructor())); + return Templates.back().second; } - BasesToInheritFrom.push_back(Base->castAs()); + + return NonTemplate; } + }; + + /// Get or create the inheriting constructor record for a constructor. + InheritingConstructor &getEntry(const CXXConstructorDecl *Ctor, + QualType CtorType) { + return Map[CtorType.getCanonicalType()->castAs()] + .getEntry(SemaRef, Ctor); } - if (BasesToInheritFrom.empty()) - return; - // FIXME: Constructor templates. - - // Now collect the constructors that we already have in the current class. - // Those take precedence over inherited constructors. - // C++11 [class.inhctor]p3: [...] a constructor is implicitly declared [...] - // unless there is a user-declared constructor with the same signature in - // the class where the using-declaration appears. - llvm::SmallSet ExistingConstructors; - for (CXXRecordDecl::ctor_iterator CtorIt = ClassDecl->ctor_begin(), - CtorE = ClassDecl->ctor_end(); - CtorIt != CtorE; ++CtorIt) - ExistingConstructors.insert( - Context.getCanonicalType(CtorIt->getType()).getTypePtr()); - - DeclarationName CreatedCtorName = - Context.DeclarationNames.getCXXConstructorName( - ClassDecl->getTypeForDecl()->getCanonicalTypeUnqualified()); - - // Now comes the true work. - // First, we keep a map from constructor types to the base that introduced - // them. Needed for finding conflicting constructors. We also keep the - // actually inserted declarations in there, for pretty diagnostics. - typedef std::pair ConstructorInfo; - typedef llvm::DenseMap ConstructorToSourceMap; - ConstructorToSourceMap InheritedConstructors; - for (BasesVector::iterator BaseIt = BasesToInheritFrom.begin(), - BaseE = BasesToInheritFrom.end(); - BaseIt != BaseE; ++BaseIt) { - const RecordType *Base = *BaseIt; - CanQualType CanonicalBase = Base->getCanonicalTypeUnqualified(); - CXXRecordDecl *BaseDecl = cast(Base->getDecl()); - for (CXXRecordDecl::ctor_iterator CtorIt = BaseDecl->ctor_begin(), - CtorE = BaseDecl->ctor_end(); - CtorIt != CtorE; ++CtorIt) { - // Find the using declaration for inheriting this base's constructors. - // FIXME: Don't perform name lookup just to obtain a source location! - DeclarationName Name = - Context.DeclarationNames.getCXXConstructorName(CanonicalBase); - LookupResult Result(*this, Name, SourceLocation(), LookupUsingDeclName); - LookupQualifiedName(Result, CurContext); - UsingDecl *UD = Result.getAsSingle(); - SourceLocation UsingLoc = UD ? UD->getLocation() : - ClassDecl->getLocation(); - - // C++11 [class.inhctor]p1: - // The candidate set of inherited constructors from the class X named in - // the using-declaration consists of actual constructors and notional - // constructors that result from the transformation of defaulted - // parameters as follows: - // - all non-template constructors of X, and - // - for each non-template constructor of X that has at least one - // parameter with a default argument, the set of constructors that - // results from omitting any ellipsis parameter specification and - // successively omitting parameters with a default argument from the - // end of the parameter-type-list, and - // FIXME: ...also constructor templates. - CXXConstructorDecl *BaseCtor = *CtorIt; - bool CanBeCopyOrMove = BaseCtor->isCopyOrMoveConstructor(); - const FunctionProtoType *BaseCtorType = - BaseCtor->getType()->getAs(); - - // Determine whether this would be a copy or move constructor for the - // derived class. - if (BaseCtorType->getNumArgs() >= 1 && - BaseCtorType->getArgType(0)->isReferenceType() && - Context.hasSameUnqualifiedType( - BaseCtorType->getArgType(0)->getPointeeType(), - Context.getTagDeclType(ClassDecl))) - CanBeCopyOrMove = true; - - ArrayRef ArgTypes(BaseCtorType->getArgTypes()); - FunctionProtoType::ExtProtoInfo EPI = BaseCtorType->getExtProtoInfo(); - // Core issue (no number yet): the ellipsis is always discarded. - if (EPI.Variadic) { - Diag(UsingLoc, diag::warn_using_decl_constructor_ellipsis); - Diag(BaseCtor->getLocation(), - diag::note_using_decl_constructor_ellipsis); - EPI.Variadic = false; - } + typedef void (InheritingConstructorInfo::*VisitFn)(const CXXConstructorDecl*); - for (unsigned Params = BaseCtor->getMinRequiredArguments(), - MaxParams = BaseCtor->getNumParams(); - Params <= MaxParams; ++Params) { - // Skip default constructors. They're never inherited. - if (Params == 0) - continue; + /// Process all constructors for a class. + void visitAll(const CXXRecordDecl *RD, VisitFn Callback) { + for (CXXRecordDecl::ctor_iterator CtorIt = RD->ctor_begin(), + CtorE = RD->ctor_end(); + CtorIt != CtorE; ++CtorIt) + (this->*Callback)(*CtorIt); + for (CXXRecordDecl::specific_decl_iterator + I(RD->decls_begin()), E(RD->decls_end()); + I != E; ++I) { + const FunctionDecl *FD = (*I)->getTemplatedDecl(); + if (const CXXConstructorDecl *CD = dyn_cast(FD)) + (this->*Callback)(CD); + } + } - // Skip copy and move constructors for both base and derived class - // for the same reason. - if (CanBeCopyOrMove && Params == 1) - continue; + /// Note that a constructor (or constructor template) was declared in Derived. + void noteDeclaredInDerived(const CXXConstructorDecl *Ctor) { + getEntry(Ctor, Ctor->getType()).DeclaredInDerived = true; + } - // Build up a function type for this particular constructor. - QualType NewCtorType = - Context.getFunctionType(Context.VoidTy, ArgTypes.slice(0, Params), - EPI); - const Type *CanonicalNewCtorType = - Context.getCanonicalType(NewCtorType).getTypePtr(); - - // C++11 [class.inhctor]p3: - // ... a constructor is implicitly declared with the same constructor - // characteristics unless there is a user-declared constructor with - // the same signature in the class where the using-declaration appears - if (ExistingConstructors.count(CanonicalNewCtorType)) - continue; + /// Inherit a single constructor. + void inherit(const CXXConstructorDecl *Ctor) { + const FunctionProtoType *CtorType = + Ctor->getType()->castAs(); + ArrayRef ArgTypes(CtorType->getArgTypes()); + FunctionProtoType::ExtProtoInfo EPI = CtorType->getExtProtoInfo(); - // C++11 [class.inhctor]p7: - // If two using-declarations declare inheriting constructors with the - // same signature, the program is ill-formed - std::pair result = - InheritedConstructors.insert(std::make_pair( - CanonicalNewCtorType, - std::make_pair(CanonicalBase, (CXXConstructorDecl*)0))); - if (!result.second) { - // Already in the map. If it came from a different class, that's an - // error. Not if it's from the same. - CanQualType PreviousBase = result.first->second.first; - if (CanonicalBase != PreviousBase) { - const CXXConstructorDecl *PrevCtor = result.first->second.second; - const CXXConstructorDecl *PrevBaseCtor = - PrevCtor->getInheritedConstructor(); - assert(PrevBaseCtor && "Conflicting constructor was not inherited"); - - Diag(UsingLoc, diag::err_using_decl_constructor_conflict); - Diag(BaseCtor->getLocation(), - diag::note_using_decl_constructor_conflict_current_ctor); - Diag(PrevBaseCtor->getLocation(), - diag::note_using_decl_constructor_conflict_previous_ctor); - Diag(PrevCtor->getLocation(), - diag::note_using_decl_constructor_conflict_previous_using); - } else { - // Core issue (no number): if the same inheriting constructor is - // produced by multiple base class constructors from the same base - // class, the inheriting constructor is defined as deleted. - SetDeclDeleted(result.first->second.second, UsingLoc); - } - continue; - } + SourceLocation UsingLoc = getUsingLoc(Ctor->getParent()); - // OK, we're there, now add the constructor. - DeclarationNameInfo DNI(CreatedCtorName, UsingLoc); - CXXConstructorDecl *NewCtor = CXXConstructorDecl::Create( - Context, ClassDecl, UsingLoc, DNI, NewCtorType, - /*TInfo=*/0, BaseCtor->isExplicit(), /*Inline=*/true, - /*ImplicitlyDeclared=*/true, /*Constexpr=*/BaseCtor->isConstexpr()); - NewCtor->setAccess(BaseCtor->getAccess()); - - // Build an unevaluated exception specification for this constructor. - EPI.ExceptionSpecType = EST_Unevaluated; - EPI.ExceptionSpecDecl = NewCtor; - NewCtor->setType(Context.getFunctionType(Context.VoidTy, - ArgTypes.slice(0, Params), - EPI)); - - // Build up the parameter decls and add them. - SmallVector ParamDecls; - for (unsigned i = 0; i < Params; ++i) { - ParmVarDecl *PD = ParmVarDecl::Create(Context, NewCtor, - UsingLoc, UsingLoc, - /*IdentifierInfo=*/0, - BaseCtorType->getArgType(i), - /*TInfo=*/0, SC_None, - /*DefaultArg=*/0); - PD->setScopeInfo(0, i); - PD->setImplicit(); - ParamDecls.push_back(PD); - } - NewCtor->setParams(ParamDecls); - NewCtor->setInheritedConstructor(BaseCtor); - if (BaseCtor->isDeleted()) - SetDeclDeleted(NewCtor, UsingLoc); + // Core issue (no number yet): the ellipsis is always discarded. + if (EPI.Variadic) { + SemaRef.Diag(UsingLoc, diag::warn_using_decl_constructor_ellipsis); + SemaRef.Diag(Ctor->getLocation(), + diag::note_using_decl_constructor_ellipsis); + EPI.Variadic = false; + } - ClassDecl->addDecl(NewCtor); - result.first->second.second = NewCtor; + // Declare a constructor for each number of parameters. + // + // C++11 [class.inhctor]p1: + // The candidate set of inherited constructors from the class X named in + // the using-declaration consists of [... modulo defects ...] for each + // constructor or constructor template of X, the set of constructors or + // constructor templates that results from omitting any ellipsis parameter + // specification and successively omitting parameters with a default + // argument from the end of the parameter-type-list + unsigned MinParams = minParamsToInherit(Ctor); + unsigned Params = Ctor->getNumParams(); + if (Params >= MinParams) { + do + declareCtor(UsingLoc, Ctor, + SemaRef.Context.getFunctionType( + Ctor->getResultType(), ArgTypes.slice(0, Params), EPI)); + while (Params > MinParams && + Ctor->getParamDecl(--Params)->hasDefaultArg()); + } + } + + /// Find the using-declaration which specified that we should inherit the + /// constructors of \p Base. + SourceLocation getUsingLoc(const CXXRecordDecl *Base) { + // No fancy lookup required; just look for the base constructor name + // directly within the derived class. + ASTContext &Context = SemaRef.Context; + DeclarationName Name = Context.DeclarationNames.getCXXConstructorName( + Context.getCanonicalType(Context.getRecordType(Base))); + DeclContext::lookup_const_result Decls = Derived->lookup(Name); + return Decls.empty() ? Derived->getLocation() : Decls[0]->getLocation(); + } + + unsigned minParamsToInherit(const CXXConstructorDecl *Ctor) { + // C++11 [class.inhctor]p3: + // [F]or each constructor template in the candidate set of inherited + // constructors, a constructor template is implicitly declared + if (Ctor->getDescribedFunctionTemplate()) + return 0; + + // For each non-template constructor in the candidate set of inherited + // constructors other than a constructor having no parameters or a + // copy/move constructor having a single parameter, a constructor is + // implicitly declared [...] + if (Ctor->getNumParams() == 0) + return 1; + if (Ctor->isCopyOrMoveConstructor()) + return 2; + + // Per discussion on core reflector, never inherit a constructor which + // would become a default, copy, or move constructor of Derived either. + const ParmVarDecl *PD = Ctor->getParamDecl(0); + const ReferenceType *RT = PD->getType()->getAs(); + return (RT && RT->getPointeeCXXRecordDecl() == Derived) ? 2 : 1; + } + + /// Declare a single inheriting constructor, inheriting the specified + /// constructor, with the given type. + void declareCtor(SourceLocation UsingLoc, const CXXConstructorDecl *BaseCtor, + QualType DerivedType) { + InheritingConstructor &Entry = getEntry(BaseCtor, DerivedType); + + // C++11 [class.inhctor]p3: + // ... a constructor is implicitly declared with the same constructor + // characteristics unless there is a user-declared constructor with + // the same signature in the class where the using-declaration appears + if (Entry.DeclaredInDerived) + return; + + // C++11 [class.inhctor]p7: + // If two using-declarations declare inheriting constructors with the + // same signature, the program is ill-formed + if (Entry.DerivedCtor) { + if (BaseCtor->getParent() != Entry.BaseCtor->getParent()) { + // Only diagnose this once per constructor. + if (Entry.DerivedCtor->isInvalidDecl()) + return; + Entry.DerivedCtor->setInvalidDecl(); + + SemaRef.Diag(UsingLoc, diag::err_using_decl_constructor_conflict); + SemaRef.Diag(BaseCtor->getLocation(), + diag::note_using_decl_constructor_conflict_current_ctor); + SemaRef.Diag(Entry.BaseCtor->getLocation(), + diag::note_using_decl_constructor_conflict_previous_ctor); + SemaRef.Diag(Entry.DerivedCtor->getLocation(), + diag::note_using_decl_constructor_conflict_previous_using); + } else { + // Core issue (no number): if the same inheriting constructor is + // produced by multiple base class constructors from the same base + // class, the inheriting constructor is defined as deleted. + SemaRef.SetDeclDeleted(Entry.DerivedCtor, UsingLoc); } + + return; } + + ASTContext &Context = SemaRef.Context; + DeclarationName Name = Context.DeclarationNames.getCXXConstructorName( + Context.getCanonicalType(Context.getRecordType(Derived))); + DeclarationNameInfo NameInfo(Name, UsingLoc); + + TemplateParameterList *TemplateParams = 0; + if (const FunctionTemplateDecl *FTD = + BaseCtor->getDescribedFunctionTemplate()) { + TemplateParams = FTD->getTemplateParameters(); + // We're reusing template parameters from a different DeclContext. This + // is questionable at best, but works out because the template depth in + // both places is guaranteed to be 0. + // FIXME: Rebuild the template parameters in the new context, and + // transform the function type to refer to them. + } + + // Build type source info pointing at the using-declaration. This is + // required by template instantiation. + TypeSourceInfo *TInfo = + Context.getTrivialTypeSourceInfo(DerivedType, UsingLoc); + FunctionProtoTypeLoc ProtoLoc = + TInfo->getTypeLoc().IgnoreParens().castAs(); + + CXXConstructorDecl *DerivedCtor = CXXConstructorDecl::Create( + Context, Derived, UsingLoc, NameInfo, DerivedType, + TInfo, BaseCtor->isExplicit(), /*Inline=*/true, + /*ImplicitlyDeclared=*/true, /*Constexpr=*/BaseCtor->isConstexpr()); + + // Build an unevaluated exception specification for this constructor. + const FunctionProtoType *FPT = DerivedType->castAs(); + FunctionProtoType::ExtProtoInfo EPI = FPT->getExtProtoInfo(); + EPI.ExceptionSpecType = EST_Unevaluated; + EPI.ExceptionSpecDecl = DerivedCtor; + DerivedCtor->setType(Context.getFunctionType(FPT->getResultType(), + FPT->getArgTypes(), EPI)); + + // Build the parameter declarations. + SmallVector ParamDecls; + for (unsigned I = 0, N = FPT->getNumArgs(); I != N; ++I) { + TypeSourceInfo *TInfo = + Context.getTrivialTypeSourceInfo(FPT->getArgType(I), UsingLoc); + ParmVarDecl *PD = ParmVarDecl::Create( + Context, DerivedCtor, UsingLoc, UsingLoc, /*IdentifierInfo=*/0, + FPT->getArgType(I), TInfo, SC_None, /*DefaultArg=*/0); + PD->setScopeInfo(0, I); + PD->setImplicit(); + ParamDecls.push_back(PD); + ProtoLoc.setArg(I, PD); + } + + // Set up the new constructor. + DerivedCtor->setAccess(BaseCtor->getAccess()); + DerivedCtor->setParams(ParamDecls); + DerivedCtor->setInheritedConstructor(BaseCtor); + if (BaseCtor->isDeleted()) + SemaRef.SetDeclDeleted(DerivedCtor, UsingLoc); + + // If this is a constructor template, build the template declaration. + if (TemplateParams) { + FunctionTemplateDecl *DerivedTemplate = + FunctionTemplateDecl::Create(SemaRef.Context, Derived, UsingLoc, Name, + TemplateParams, DerivedCtor); + DerivedTemplate->setAccess(BaseCtor->getAccess()); + DerivedCtor->setDescribedFunctionTemplate(DerivedTemplate); + Derived->addDecl(DerivedTemplate); + } else { + Derived->addDecl(DerivedCtor); + } + + Entry.BaseCtor = BaseCtor; + Entry.DerivedCtor = DerivedCtor; } + + Sema &SemaRef; + CXXRecordDecl *Derived; + typedef llvm::DenseMap MapType; + MapType Map; +}; +} + +void Sema::DeclareInheritingConstructors(CXXRecordDecl *ClassDecl) { + // Defer declaring the inheriting constructors until the class is + // instantiated. + if (ClassDecl->isDependentContext()) + return; + + // Find base classes from which we might inherit constructors. + SmallVector InheritedBases; + for (CXXRecordDecl::base_class_iterator BaseIt = ClassDecl->bases_begin(), + BaseE = ClassDecl->bases_end(); + BaseIt != BaseE; ++BaseIt) + if (BaseIt->getInheritConstructors()) + InheritedBases.push_back(BaseIt->getType()->getAsCXXRecordDecl()); + + // Go no further if we're not inheriting any constructors. + if (InheritedBases.empty()) + return; + + // Declare the inherited constructors. + InheritingConstructorInfo ICI(*this, ClassDecl); + for (unsigned I = 0, N = InheritedBases.size(); I != N; ++I) + ICI.inheritAll(InheritedBases[I]); } void Sema::DefineInheritingConstructor(SourceLocation CurrentLocation, @@ -7943,9 +8255,7 @@ CXXDestructorDecl *Sema::DeclareImplicitDestructor(CXXRecordDecl *ClassDecl) { FunctionProtoType::ExtProtoInfo EPI; EPI.ExceptionSpecType = EST_Unevaluated; EPI.ExceptionSpecDecl = Destructor; - Destructor->setType(Context.getFunctionType(Context.VoidTy, - ArrayRef(), - EPI)); + Destructor->setType(Context.getFunctionType(Context.VoidTy, None, EPI)); AddOverriddenMethods(ClassDecl, Destructor); @@ -8049,9 +8359,7 @@ void Sema::AdjustDestructorExceptionSpec(CXXRecordDecl *ClassDecl, FunctionProtoType::ExtProtoInfo EPI = DtorType->getExtProtoInfo(); EPI.ExceptionSpecType = EST_Unevaluated; EPI.ExceptionSpecDecl = Destructor; - Destructor->setType(Context.getFunctionType(Context.VoidTy, - ArrayRef(), - EPI)); + Destructor->setType(Context.getFunctionType(Context.VoidTy, None, 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 @@ -10174,7 +10482,8 @@ Decl *Sema::ActOnStartLinkageSpecification(Scope *S, SourceLocation ExternLoc, // FIXME: Add all the various semantics of linkage specifications LinkageSpecDecl *D = LinkageSpecDecl::Create(Context, CurContext, - ExternLoc, LangLoc, Language); + ExternLoc, LangLoc, Language, + LBraceLoc.isValid()); CurContext->addDecl(D); PushDeclContext(S, D); return D; @@ -10307,9 +10616,8 @@ VarDecl *Sema::BuildExceptionDeclaration(Scope *S, Expr *opaqueValue = new (Context) OpaqueValueExpr(Loc, initType, VK_LValue, OK_Ordinary); - InitializationSequence sequence(*this, entity, initKind, &opaqueValue, 1); - ExprResult result = sequence.Perform(*this, entity, initKind, - MultiExprArg(&opaqueValue, 1)); + InitializationSequence sequence(*this, entity, initKind, opaqueValue); + ExprResult result = sequence.Perform(*this, entity, initKind, opaqueValue); if (result.isInvalid()) Invalid = true; else { @@ -10798,29 +11106,39 @@ NamedDecl *Sema::ActOnFriendFunctionDecl(Scope *S, Declarator &D, // Find the appropriate context according to the above. DC = CurContext; + + // Skip class contexts. If someone can cite chapter and verse + // for this behavior, that would be nice --- it's what GCC and + // EDG do, and it seems like a reasonable intent, but the spec + // really only says that checks for unqualified existing + // declarations should stop at the nearest enclosing namespace, + // not that they should only consider the nearest enclosing + // namespace. + while (DC->isRecord()) + DC = DC->getParent(); + + DeclContext *LookupDC = DC; + while (LookupDC->isTransparentContext()) + LookupDC = LookupDC->getParent(); + while (true) { - // Skip class contexts. If someone can cite chapter and verse - // for this behavior, that would be nice --- it's what GCC and - // EDG do, and it seems like a reasonable intent, but the spec - // really only says that checks for unqualified existing - // declarations should stop at the nearest enclosing namespace, - // not that they should only consider the nearest enclosing - // namespace. - while (DC->isRecord() || DC->isTransparentContext()) - DC = DC->getParent(); - - LookupQualifiedName(Previous, DC); + LookupQualifiedName(Previous, LookupDC); // TODO: decide what we think about using declarations. - if (isLocal || !Previous.empty()) + if (isLocal) + break; + + if (!Previous.empty()) { + DC = LookupDC; break; + } if (isTemplateId) { - if (isa(DC)) break; + if (isa(LookupDC)) break; } else { - if (DC->isFileContext()) break; + if (LookupDC->isFileContext()) break; } - DC = DC->getParent(); + LookupDC = LookupDC->getParent(); } DCScope = getScopeForDeclContext(S, DC); @@ -11373,8 +11691,7 @@ void Sema::MarkVTableUsed(SourceLocation Loc, CXXRecordDecl *Class, // Ignore any vtable uses in unevaluated operands or for classes that do // not have a vtable. if (!Class->isDynamicClass() || Class->isDependentContext() || - CurContext->isDependentContext() || - ExprEvalContexts.back().Context == Unevaluated) + CurContext->isDependentContext() || isUnevaluatedContext()) return; // Try to insert this class into the map. @@ -11562,10 +11879,10 @@ void Sema::SetIvarInitializers(ObjCImplementationDecl *ObjCImplementation) { InitializedEntity InitEntity = InitializedEntity::InitializeMember(Field); InitializationKind InitKind = InitializationKind::CreateDefault(ObjCImplementation->getLocation()); - - InitializationSequence InitSeq(*this, InitEntity, InitKind, 0, 0); - ExprResult MemberInit = - InitSeq.Perform(*this, InitEntity, InitKind, MultiExprArg()); + + InitializationSequence InitSeq(*this, InitEntity, InitKind, None); + ExprResult MemberInit = + InitSeq.Perform(*this, InitEntity, InitKind, None); MemberInit = MaybeCreateExprWithCleanups(MemberInit); // Note, MemberInit could actually come back empty if no initialization // is required (e.g., because it would call a trivial default constructor) @@ -11921,3 +12238,94 @@ bool Sema::CheckCUDATarget(CUDAFunctionTarget CallerTarget, return false; } + +/// HandleMSProperty - Analyze a __delcspec(property) field of a C++ class. +/// +MSPropertyDecl *Sema::HandleMSProperty(Scope *S, RecordDecl *Record, + SourceLocation DeclStart, + Declarator &D, Expr *BitWidth, + InClassInitStyle InitStyle, + AccessSpecifier AS, + AttributeList *MSPropertyAttr) { + IdentifierInfo *II = D.getIdentifier(); + if (!II) { + Diag(DeclStart, diag::err_anonymous_property); + return NULL; + } + SourceLocation Loc = D.getIdentifierLoc(); + + TypeSourceInfo *TInfo = GetTypeForDeclarator(D, S); + QualType T = TInfo->getType(); + if (getLangOpts().CPlusPlus) { + CheckExtraCXXDefaultArguments(D); + + if (DiagnoseUnexpandedParameterPack(D.getIdentifierLoc(), TInfo, + UPPC_DataMemberType)) { + D.setInvalidType(); + T = Context.IntTy; + TInfo = Context.getTrivialTypeSourceInfo(T, Loc); + } + } + + DiagnoseFunctionSpecifiers(D.getDeclSpec()); + + if (DeclSpec::TSCS TSCS = D.getDeclSpec().getThreadStorageClassSpec()) + Diag(D.getDeclSpec().getThreadStorageClassSpecLoc(), + diag::err_invalid_thread) + << DeclSpec::getSpecifierName(TSCS); + + // Check to see if this name was declared as a member previously + NamedDecl *PrevDecl = 0; + LookupResult Previous(*this, II, Loc, LookupMemberName, ForRedeclaration); + LookupName(Previous, S); + switch (Previous.getResultKind()) { + case LookupResult::Found: + case LookupResult::FoundUnresolvedValue: + PrevDecl = Previous.getAsSingle(); + break; + + case LookupResult::FoundOverloaded: + PrevDecl = Previous.getRepresentativeDecl(); + break; + + case LookupResult::NotFound: + case LookupResult::NotFoundInCurrentInstantiation: + case LookupResult::Ambiguous: + break; + } + + if (PrevDecl && PrevDecl->isTemplateParameter()) { + // Maybe we will complain about the shadowed template parameter. + DiagnoseTemplateParameterShadow(D.getIdentifierLoc(), PrevDecl); + // Just pretend that we didn't see the previous declaration. + PrevDecl = 0; + } + + if (PrevDecl && !isDeclInScope(PrevDecl, Record, S)) + PrevDecl = 0; + + SourceLocation TSSL = D.getLocStart(); + MSPropertyDecl *NewPD; + const AttributeList::PropertyData &Data = MSPropertyAttr->getPropertyData(); + NewPD = new (Context) MSPropertyDecl(Record, Loc, + II, T, TInfo, TSSL, + Data.GetterId, Data.SetterId); + ProcessDeclAttributes(TUScope, NewPD, D); + NewPD->setAccess(AS); + + if (NewPD->isInvalidDecl()) + Record->setInvalidDecl(); + + if (D.getDeclSpec().isModulePrivateSpecified()) + NewPD->setModulePrivate(); + + if (NewPD->isInvalidDecl() && PrevDecl) { + // Don't introduce NewFD into scope; there's already something + // with the same name in the same scope. + } else if (II) { + PushOnScopeChains(NewPD, S); + } else + Record->addDecl(NewPD); + + return NewPD; +} diff --git a/lib/Sema/SemaDeclObjC.cpp b/lib/Sema/SemaDeclObjC.cpp index 5c26d7ff8e0..f33e7bcb167 100644 --- a/lib/Sema/SemaDeclObjC.cpp +++ b/lib/Sema/SemaDeclObjC.cpp @@ -745,7 +745,10 @@ Sema::FindProtocolDeclaration(bool WarnOnDeclarations, << ProtocolId[i].first; continue; } - + // If this is a forward protocol declaration, get its definition. + if (!PDecl->isThisDeclarationADefinition() && PDecl->getDefinition()) + PDecl = PDecl->getDefinition(); + (void)DiagnoseUseOfDecl(PDecl, ProtocolId[i].second); // If this is a forward declaration and we are supposed to warn in this @@ -1042,7 +1045,7 @@ Decl *Sema::ActOnStartClassImplementation( ObjCImplementationDecl* IMPDecl = ObjCImplementationDecl::Create(Context, CurContext, IDecl, SDecl, - ClassLoc, AtClassImplLoc); + ClassLoc, AtClassImplLoc, SuperClassLoc); if (CheckObjCDeclScope(IMPDecl)) return ActOnObjCContainerStartDefinition(IMPDecl); @@ -1833,7 +1836,7 @@ void Sema::ImplMethodsVsClassMethods(Scope *S, ObjCImplDecl* IMPDecl, if (!(LangOpts.ObjCDefaultSynthProperties && LangOpts.ObjCRuntime.isNonFragile()) || IDecl->isObjCRequiresPropertyDefs()) - DiagnoseUnimplementedProperties(S, IMPDecl, CDecl, InsMap); + DiagnoseUnimplementedProperties(S, IMPDecl, CDecl); SelectorSet ClsMap; for (ObjCImplementationDecl::classmeth_iterator @@ -1880,17 +1883,7 @@ void Sema::ImplMethodsVsClassMethods(Scope *S, ObjCImplDecl* IMPDecl, E = C->protocol_end(); PI != E; ++PI) CheckProtocolMethodDefs(IMPDecl->getLocation(), *PI, IncompleteImpl, InsMap, ClsMap, CDecl); - // Report unimplemented properties in the category as well. - // When reporting on missing setter/getters, do not report when - // setter/getter is implemented in category's primary class - // implementation. - if (ObjCInterfaceDecl *ID = C->getClassInterface()) - if (ObjCImplDecl *IMP = ID->getImplementation()) { - for (ObjCImplementationDecl::instmeth_iterator - I = IMP->instmeth_begin(), E = IMP->instmeth_end(); I!=E; ++I) - InsMap.insert((*I)->getSelector()); - } - DiagnoseUnimplementedProperties(S, IMPDecl, CDecl, InsMap); + DiagnoseUnimplementedProperties(S, IMPDecl, CDecl); } } else llvm_unreachable("invalid ObjCContainerDecl type."); @@ -2082,17 +2075,24 @@ bool Sema::MatchTwoMethodDeclarations(const ObjCMethodDecl *left, } void Sema::addMethodToGlobalList(ObjCMethodList *List, ObjCMethodDecl *Method) { + // Record at the head of the list whether there were 0, 1, or >= 2 methods + // inside categories. + if (ObjCCategoryDecl * + CD = dyn_cast(Method->getDeclContext())) + if (!CD->IsClassExtension() && List->getBits() < 2) + List->setBits(List->getBits()+1); + // If the list is empty, make it a singleton list. if (List->Method == 0) { List->Method = Method; - List->Next = 0; + List->setNext(0); return; } // We've seen a method with this name, see if we have already seen this type // signature. ObjCMethodList *Previous = List; - for (; List; Previous = List, List = List->Next) { + for (; List; Previous = List, List = List->getNext()) { if (!MatchTwoMethodDeclarations(Method, List->Method)) continue; @@ -2121,7 +2121,7 @@ void Sema::addMethodToGlobalList(ObjCMethodList *List, ObjCMethodDecl *Method) { // We have a new signature for an existing method - add it. // This is extremely rare. Only 1% of Cocoa selectors are "overloaded". ObjCMethodList *Mem = BumpAlloc.Allocate(); - Previous->Next = new (Mem) ObjCMethodList(Method, 0); + Previous->setNext(new (Mem) ObjCMethodList(Method, 0)); } /// \brief Read the contents of the method pool for a given selector from @@ -2183,7 +2183,7 @@ ObjCMethodDecl *Sema::LookupMethodInGlobalPool(Selector Sel, SourceRange R, // Gather the non-hidden methods. ObjCMethodList &MethList = instance ? Pos->second.first : Pos->second.second; llvm::SmallVector Methods; - for (ObjCMethodList *M = &MethList; M; M = M->Next) { + for (ObjCMethodList *M = &MethList; M; M = M->getNext()) { if (M->Method && !M->Method->isHidden()) { // If we're not supposed to warn about mismatches, we're done. if (!warn) @@ -2800,10 +2800,47 @@ void Sema::CheckObjCMethodOverrides(ObjCMethodDecl *ObjCMethod, i = overrides.begin(), e = overrides.end(); i != e; ++i) { ObjCMethodDecl *overridden = *i; - if (isa(overridden->getDeclContext()) || - CurrentClass != overridden->getClassInterface() || - overridden->isOverriding()) - hasOverriddenMethodsInBaseOrProtocol = true; + if (!hasOverriddenMethodsInBaseOrProtocol) { + if (isa(overridden->getDeclContext()) || + CurrentClass != overridden->getClassInterface() || + overridden->isOverriding()) { + hasOverriddenMethodsInBaseOrProtocol = true; + + } else if (isa(ObjCMethod->getDeclContext())) { + // OverrideSearch will return as "overridden" the same method in the + // interface. For hasOverriddenMethodsInBaseOrProtocol, we need to + // check whether a category of a base class introduced a method with the + // same selector, after the interface method declaration. + // To avoid unnecessary lookups in the majority of cases, we use the + // extra info bits in GlobalMethodPool to check whether there were any + // category methods with this selector. + GlobalMethodPool::iterator It = + MethodPool.find(ObjCMethod->getSelector()); + if (It != MethodPool.end()) { + ObjCMethodList &List = + ObjCMethod->isInstanceMethod()? It->second.first: It->second.second; + unsigned CategCount = List.getBits(); + if (CategCount > 0) { + // If the method is in a category we'll do lookup if there were at + // least 2 category methods recorded, otherwise only one will do. + if (CategCount > 1 || + !isa(overridden->getDeclContext())) { + OverrideSearch overrides(*this, overridden); + for (OverrideSearch::iterator + OI= overrides.begin(), OE= overrides.end(); OI!=OE; ++OI) { + ObjCMethodDecl *SuperOverridden = *OI; + if (isa(SuperOverridden->getDeclContext()) || + CurrentClass != SuperOverridden->getClassInterface()) { + hasOverriddenMethodsInBaseOrProtocol = true; + overridden->setOverriding(true); + break; + } + } + } + } + } + } + } // Propagate down the 'related result type' bit from overridden methods. if (RTC != Sema::RTC_Incompatible && overridden->hasRelatedResultType()) @@ -3187,12 +3224,14 @@ Decl *Sema::ActOnObjCExceptionDecl(Scope *S, Declarator &D) { if (DS.getStorageClassSpec() == DeclSpec::SCS_register) { Diag(DS.getStorageClassSpecLoc(), diag::warn_register_objc_catch_parm) << FixItHint::CreateRemoval(SourceRange(DS.getStorageClassSpecLoc())); - } else if (DS.getStorageClassSpec() != DeclSpec::SCS_unspecified) { + } else if (DeclSpec::SCS SCS = DS.getStorageClassSpec()) { Diag(DS.getStorageClassSpecLoc(), diag::err_storage_spec_on_catch_parm) - << DS.getStorageClassSpec(); - } - if (D.getDeclSpec().isThreadSpecified()) - Diag(D.getDeclSpec().getThreadSpecLoc(), diag::err_invalid_thread); + << DeclSpec::getSpecifierName(SCS); + } + if (DeclSpec::TSCS TSCS = D.getDeclSpec().getThreadStorageClassSpec()) + Diag(D.getDeclSpec().getThreadStorageClassSpecLoc(), + diag::err_invalid_thread) + << DeclSpec::getSpecifierName(TSCS); D.getMutableDeclSpec().ClearStorageClassSpecs(); DiagnoseFunctionSpecifiers(D.getDeclSpec()); diff --git a/lib/Sema/SemaExceptionSpec.cpp b/lib/Sema/SemaExceptionSpec.cpp index 26c3d354c7a..1a5f4824d09 100644 --- a/lib/Sema/SemaExceptionSpec.cpp +++ b/lib/Sema/SemaExceptionSpec.cpp @@ -1011,7 +1011,6 @@ CanThrowResult Sema::canThrow(const Expr *E) { case Expr::ConditionalOperatorClass: case Expr::CompoundLiteralExprClass: case Expr::CXXConstCastExprClass: - case Expr::CXXDefaultArgExprClass: case Expr::CXXReinterpretCastExprClass: case Expr::DesignatedInitExprClass: case Expr::ExprWithCleanupsClass: @@ -1044,6 +1043,12 @@ CanThrowResult Sema::canThrow(const Expr *E) { case Expr::StmtExprClass: return CT_Can; + case Expr::CXXDefaultArgExprClass: + return canThrow(cast(E)->getExpr()); + + case Expr::CXXDefaultInitExprClass: + return canThrow(cast(E)->getExpr()); + case Expr::ChooseExprClass: if (E->isTypeDependent() || E->isValueDependent()) return CT_Dependent; @@ -1111,6 +1116,9 @@ CanThrowResult Sema::canThrow(const Expr *E) { // These expressions can never throw. return CT_Cannot; + case Expr::MSPropertyRefExprClass: + llvm_unreachable("Invalid class for expression"); + #define STMT(CLASS, PARENT) case Expr::CLASS##Class: #define STMT_RANGE(Base, First, Last) #define LAST_STMT_RANGE(BASE, FIRST, LAST) diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index 76330f5cdbd..dd05b822364 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -55,6 +55,12 @@ bool Sema::CanUseDecl(NamedDecl *D) { if (FunctionDecl *FD = dyn_cast(D)) { if (FD->isDeleted()) return false; + + // If the function has a deduced return type, and we can't deduce it, + // then we can't use it either. + if (getLangOpts().CPlusPlus1y && FD->getResultType()->isUndeducedType() && + DeduceReturnType(FD, SourceLocation(), /*Diagnose*/false)) + return false; } // See if this function is unavailable. @@ -278,6 +284,12 @@ bool Sema::DiagnoseUseOfDecl(NamedDecl *D, SourceLocation Loc, NoteDeletedFunction(FD); return true; } + + // If the function has a deduced return type, and we can't deduce it, + // then we can't use it either. + if (getLangOpts().CPlusPlus1y && FD->getResultType()->isUndeducedType() && + DeduceReturnType(FD, Loc)) + return true; } DiagnoseAvailabilityOfDecl(*this, D, Loc, UnknownObjCClass); @@ -806,7 +818,7 @@ ExprResult Sema::DefaultVariadicArgumentPromotion(Expr *E, VariadicCallType CT, return ExprError(); ExprResult Call = ActOnCallExpr(TUScope, TrapFn.get(), - E->getLocStart(), MultiExprArg(), + E->getLocStart(), None, E->getLocEnd()); if (Call.isInvalid()) return ExprError(); @@ -1916,15 +1928,17 @@ ExprResult Sema::ActOnIdExpression(Scope *S, // If this name wasn't predeclared and if this is not a function // call, diagnose the problem. if (R.empty()) { - // In Microsoft mode, if we are inside a template class member function - // and we can't resolve an identifier then assume the identifier is type - // dependent. The goal is to postpone name lookup to instantiation time - // to be able to search into type dependent base classes. - if (getLangOpts().MicrosoftMode && CurContext->isDependentContext() && - isa(CurContext)) - return ActOnDependentIdExpression(SS, TemplateKWLoc, NameInfo, - IsAddressOfOperand, TemplateArgs); + // whose parent class has dependent base classes, and we can't resolve + // an identifier, then assume the identifier is type dependent. The + // goal is to postpone name lookup to instantiation time to be able to + // search into the type dependent base classes. + if (getLangOpts().MicrosoftMode) { + CXXMethodDecl *MD = dyn_cast(CurContext); + if (MD && MD->getParent()->hasAnyDependentBases()) + return ActOnDependentIdExpression(SS, TemplateKWLoc, NameInfo, + IsAddressOfOperand, TemplateArgs); + } CorrectionCandidateCallback DefaultValidator; if (DiagnoseEmptyLookup(S, SS, R, CCC ? *CCC : DefaultValidator)) @@ -2636,6 +2650,10 @@ Sema::BuildDeclarationNameExpr(const CXXScopeSpec &SS, break; } + case Decl::MSProperty: + valueKind = VK_LValue; + break; + case Decl::CXXMethod: // If we're referring to a method with an __unknown_anytype // result type, make the entire expression __unknown_anytype. @@ -2760,8 +2778,7 @@ ExprResult Sema::ActOnCharacterConstant(const Token &Tok, Scope *UDLScope) { // C++11 [lex.ext]p6: The literal L is treated as a call of the form // operator "" X (ch) return BuildCookedLiteralOperatorCall(*this, UDLScope, UDSuffix, UDSuffixLoc, - llvm::makeArrayRef(&Lit, 1), - Tok.getLocation()); + Lit, Tok.getLocation()); } ExprResult Sema::ActOnIntegerConstant(SourceLocation Loc, uint64_t Val) { @@ -2858,7 +2875,7 @@ ExprResult Sema::ActOnNumericConstant(const Token &Tok, Scope *UDLScope) { // Perform literal operator lookup to determine if we're building a raw // literal or a cooked one. LookupResult R(*this, OpName, UDSuffixLoc, LookupOrdinaryName); - switch (LookupLiteralOperator(UDLScope, R, llvm::makeArrayRef(&CookedTy, 1), + switch (LookupLiteralOperator(UDLScope, R, CookedTy, /*AllowRawAndTemplate*/true)) { case LOLR_Error: return ExprError(); @@ -2874,8 +2891,7 @@ ExprResult Sema::ActOnNumericConstant(const Token &Tok, Scope *UDLScope) { Lit = IntegerLiteral::Create(Context, ResultVal, CookedTy, Tok.getLocation()); } - return BuildLiteralOperatorCall(R, OpNameInfo, - llvm::makeArrayRef(&Lit, 1), + return BuildLiteralOperatorCall(R, OpNameInfo, Lit, Tok.getLocation()); } @@ -2891,8 +2907,7 @@ ExprResult Sema::ActOnNumericConstant(const Token &Tok, Scope *UDLScope) { Expr *Lit = StringLiteral::Create( Context, StringRef(TokSpelling.data(), Length), StringLiteral::Ascii, /*Pascal*/false, StrTy, &TokLoc, 1); - return BuildLiteralOperatorCall(R, OpNameInfo, - llvm::makeArrayRef(&Lit, 1), TokLoc); + return BuildLiteralOperatorCall(R, OpNameInfo, Lit, TokLoc); } case LOLR_Template: @@ -2910,8 +2925,8 @@ ExprResult Sema::ActOnNumericConstant(const Token &Tok, Scope *UDLScope) { TemplateArgumentLocInfo ArgInfo; ExplicitArgs.addArgument(TemplateArgumentLoc(Arg, ArgInfo)); } - return BuildLiteralOperatorCall(R, OpNameInfo, ArrayRef(), - Tok.getLocation(), &ExplicitArgs); + return BuildLiteralOperatorCall(R, OpNameInfo, None, Tok.getLocation(), + &ExplicitArgs); } llvm_unreachable("unexpected literal operator lookup result"); @@ -3150,13 +3165,7 @@ static void warnOnSizeofOnArrayDecay(Sema &S, SourceLocation Loc, QualType T, bool Sema::CheckUnaryExprOrTypeTraitOperand(Expr *E, UnaryExprOrTypeTrait ExprKind) { QualType ExprTy = E->getType(); - - // C++ [expr.sizeof]p2: "When applied to a reference or a reference type, - // the result is the size of the referenced type." - // C++ [expr.alignof]p3: "When alignof is applied to a reference type, the - // result shall be the alignment of the referenced type." - if (const ReferenceType *Ref = ExprTy->getAs()) - ExprTy = Ref->getPointeeType(); + assert(!ExprTy->isReferenceType()); if (ExprKind == UETT_VecStep) return CheckVecStepTraitOperandType(*this, ExprTy, E->getExprLoc(), @@ -3172,10 +3181,9 @@ bool Sema::CheckUnaryExprOrTypeTraitOperand(Expr *E, ExprKind, E->getSourceRange())) return true; - // Completeing the expression's type may have changed it. + // Completing the expression's type may have changed it. ExprTy = E->getType(); - if (const ReferenceType *Ref = ExprTy->getAs()) - ExprTy = Ref->getPointeeType(); + assert(!ExprTy->isReferenceType()); if (CheckObjCTraitOperandConstraints(*this, ExprTy, E->getExprLoc(), E->getSourceRange(), ExprKind)) @@ -3260,25 +3268,67 @@ bool Sema::CheckUnaryExprOrTypeTraitOperand(QualType ExprType, static bool CheckAlignOfExpr(Sema &S, Expr *E) { E = E->IgnoreParens(); - // alignof decl is always ok. - if (isa(E)) - return false; - // Cannot know anything else if the expression is dependent. if (E->isTypeDependent()) return false; - if (E->getBitField()) { + if (E->getObjectKind() == OK_BitField) { S.Diag(E->getExprLoc(), diag::err_sizeof_alignof_bitfield) << 1 << E->getSourceRange(); return true; } - // Alignment of a field access is always okay, so long as it isn't a - // bit-field. - if (MemberExpr *ME = dyn_cast(E)) - if (isa(ME->getMemberDecl())) + ValueDecl *D = 0; + if (DeclRefExpr *DRE = dyn_cast(E)) { + D = DRE->getDecl(); + } else if (MemberExpr *ME = dyn_cast(E)) { + D = ME->getMemberDecl(); + } + + // If it's a field, require the containing struct to have a + // complete definition so that we can compute the layout. + // + // This requires a very particular set of circumstances. For a + // field to be contained within an incomplete type, we must in the + // process of parsing that type. To have an expression refer to a + // field, it must be an id-expression or a member-expression, but + // the latter are always ill-formed when the base type is + // incomplete, including only being partially complete. An + // id-expression can never refer to a field in C because fields + // are not in the ordinary namespace. In C++, an id-expression + // can implicitly be a member access, but only if there's an + // implicit 'this' value, and all such contexts are subject to + // delayed parsing --- except for trailing return types in C++11. + // And if an id-expression referring to a field occurs in a + // context that lacks a 'this' value, it's ill-formed --- except, + // agian, in C++11, where such references are allowed in an + // unevaluated context. So C++11 introduces some new complexity. + // + // For the record, since __alignof__ on expressions is a GCC + // extension, GCC seems to permit this but always gives the + // nonsensical answer 0. + // + // We don't really need the layout here --- we could instead just + // directly check for all the appropriate alignment-lowing + // attributes --- but that would require duplicating a lot of + // logic that just isn't worth duplicating for such a marginal + // use-case. + if (FieldDecl *FD = dyn_cast_or_null(D)) { + // Fast path this check, since we at least know the record has a + // definition if we can find a member of it. + if (!FD->getParent()->isCompleteDefinition()) { + S.Diag(E->getExprLoc(), diag::err_alignof_member_of_incomplete_type) + << E->getSourceRange(); + return true; + } + + // Otherwise, if it's a field, and the field doesn't have + // reference type, then it must have a complete type (or be a + // flexible array member, which we explicitly want to + // white-list anyway), which makes the following checks trivial. + if (!FD->getType()->isReferenceType()) return false; + } return S.CheckUnaryExprOrTypeTraitOperand(E, UETT_AlignOf); } @@ -3333,7 +3383,7 @@ Sema::CreateUnaryExprOrTypeTraitExpr(Expr *E, SourceLocation OpLoc, isInvalid = CheckAlignOfExpr(*this, E); } else if (ExprKind == UETT_VecStep) { isInvalid = CheckVecStepExpr(E); - } else if (E->getBitField()) { // C99 6.5.3.4p1. + } else if (E->refersToBitField()) { // C99 6.5.3.4p1. Diag(E->getExprLoc(), diag::err_sizeof_alignof_bitfield) << 0; isInvalid = true; } else { @@ -3665,14 +3715,11 @@ ExprResult Sema::BuildCXXDefaultArgExpr(SourceLocation CallLoc, Param); // Instantiate the expression. - MultiLevelTemplateArgumentList ArgList + MultiLevelTemplateArgumentList MutiLevelArgList = getTemplateInstantiationArgs(FD, 0, /*RelativeToPrimary=*/true); - std::pair Innermost - = ArgList.getInnermost(); InstantiatingTemplate Inst(*this, CallLoc, Param, - ArrayRef(Innermost.first, - Innermost.second)); + MutiLevelArgList.getInnermost()); if (Inst) return ExprError(); @@ -3684,7 +3731,7 @@ ExprResult Sema::BuildCXXDefaultArgExpr(SourceLocation CallLoc, // default argument expression appears. ContextRAII SavedContext(*this, FD); LocalInstantiationScope Local(*this); - Result = SubstExpr(UninstExpr, ArgList); + Result = SubstExpr(UninstExpr, MutiLevelArgList); } if (Result.isInvalid()) return ExprError(); @@ -3697,7 +3744,7 @@ ExprResult Sema::BuildCXXDefaultArgExpr(SourceLocation CallLoc, /*FIXME:EqualLoc*/UninstExpr->getLocStart()); Expr *ResultE = Result.takeAs(); - InitializationSequence InitSeq(*this, Entity, Kind, &ResultE, 1); + InitializationSequence InitSeq(*this, Entity, Kind, ResultE); Result = InitSeq.Perform(*this, Entity, Kind, ResultE); if (Result.isInvalid()) return ExprError(); @@ -4023,6 +4070,63 @@ Sema::CheckStaticArrayArgument(SourceLocation CallLoc, /// to have a function type. static ExprResult rebuildUnknownAnyFunction(Sema &S, Expr *fn); +/// Is the given type a placeholder that we need to lower out +/// immediately during argument processing? +static bool isPlaceholderToRemoveAsArg(QualType type) { + // Placeholders are never sugared. + const BuiltinType *placeholder = dyn_cast(type); + if (!placeholder) return false; + + switch (placeholder->getKind()) { + // Ignore all the non-placeholder types. +#define PLACEHOLDER_TYPE(ID, SINGLETON_ID) +#define BUILTIN_TYPE(ID, SINGLETON_ID) case BuiltinType::ID: +#include "clang/AST/BuiltinTypes.def" + return false; + + // We cannot lower out overload sets; they might validly be resolved + // by the call machinery. + case BuiltinType::Overload: + return false; + + // Unbridged casts in ARC can be handled in some call positions and + // should be left in place. + case BuiltinType::ARCUnbridgedCast: + return false; + + // Pseudo-objects should be converted as soon as possible. + case BuiltinType::PseudoObject: + return true; + + // The debugger mode could theoretically but currently does not try + // to resolve unknown-typed arguments based on known parameter types. + case BuiltinType::UnknownAny: + return true; + + // These are always invalid as call arguments and should be reported. + case BuiltinType::BoundMember: + case BuiltinType::BuiltinFn: + return true; + } + llvm_unreachable("bad builtin type kind"); +} + +/// Check an argument list for placeholders that we won't try to +/// handle later. +static bool checkArgsForPlaceholders(Sema &S, MultiExprArg args) { + // Apply this processing to all the arguments at once instead of + // dying at the first failure. + bool hasInvalid = false; + for (size_t i = 0, e = args.size(); i != e; i++) { + if (isPlaceholderToRemoveAsArg(args[i]->getType())) { + ExprResult result = S.CheckPlaceholderExpr(args[i]); + if (result.isInvalid()) hasInvalid = true; + else args[i] = result.take(); + } + } + return hasInvalid; +} + /// ActOnCallExpr - Handle a call to Fn with the specified array of arguments. /// This provides the location of the left/right parens and a list of comma /// locations. @@ -4035,6 +4139,9 @@ Sema::ActOnCallExpr(Scope *S, Expr *Fn, SourceLocation LParenLoc, if (Result.isInvalid()) return ExprError(); Fn = Result.take(); + if (checkArgsForPlaceholders(*this, ArgExprs)) + return ExprError(); + if (getLangOpts().CPlusPlus) { // If this is a pseudo-destructor expression, build the call immediately. if (isa(Fn)) { @@ -4046,10 +4153,15 @@ Sema::ActOnCallExpr(Scope *S, Expr *Fn, SourceLocation LParenLoc, ArgExprs.back()->getLocEnd())); } - return Owned(new (Context) CallExpr(Context, Fn, MultiExprArg(), + return Owned(new (Context) CallExpr(Context, Fn, None, Context.VoidTy, VK_RValue, RParenLoc)); } + if (Fn->getType() == Context.PseudoObjectTy) { + ExprResult result = CheckPlaceholderExpr(Fn); + if (result.isInvalid()) return ExprError(); + Fn = result.take(); + } // Determine whether this is a dependent call inside a C++ template, // in which case we won't do any semantic analysis now. @@ -4397,12 +4509,12 @@ Sema::BuildCompoundLiteralExpr(SourceLocation LParenLoc, TypeSourceInfo *TInfo, return ExprError(); InitializedEntity Entity - = InitializedEntity::InitializeTemporary(literalType); + = InitializedEntity::InitializeCompoundLiteralInit(TInfo); InitializationKind Kind = InitializationKind::CreateCStyleCast(LParenLoc, SourceRange(LParenLoc, RParenLoc), /*InitList=*/true); - InitializationSequence InitSeq(*this, Entity, Kind, &LiteralExpr, 1); + InitializationSequence InitSeq(*this, Entity, Kind, LiteralExpr); ExprResult Result = InitSeq.Perform(*this, Entity, Kind, LiteralExpr, &literalType); if (Result.isInvalid()) @@ -6823,18 +6935,6 @@ QualType Sema::CheckShiftOperands(ExprResult &LHS, ExprResult &RHS, bool IsCompAssign) { checkArithmeticNull(*this, LHS, RHS, Loc, /*isCompare=*/false); - // C99 6.5.7p2: Each of the operands shall have integer type. - if (!LHS.get()->getType()->hasIntegerRepresentation() || - !RHS.get()->getType()->hasIntegerRepresentation()) - return InvalidOperands(Loc, LHS, RHS); - - // C++0x: Don't allow scoped enums. FIXME: Use something better than - // hasIntegerRepresentation() above instead of this. - if (isScopedEnumerationType(LHS.get()->getType()) || - isScopedEnumerationType(RHS.get()->getType())) { - return InvalidOperands(Loc, LHS, RHS); - } - // Vector shifts promote their scalar inputs to vector type. if (LHS.get()->getType()->isVectorType() || RHS.get()->getType()->isVectorType()) @@ -6856,7 +6956,19 @@ QualType Sema::CheckShiftOperands(ExprResult &LHS, ExprResult &RHS, RHS = UsualUnaryConversions(RHS.take()); if (RHS.isInvalid()) return QualType(); + QualType RHSType = RHS.get()->getType(); + + // C99 6.5.7p2: Each of the operands shall have integer type. + if (!LHSType->hasIntegerRepresentation() || + !RHSType->hasIntegerRepresentation()) + return InvalidOperands(Loc, LHS, RHS); + // C++0x: Don't allow scoped enums. FIXME: Use something better than + // hasIntegerRepresentation() above instead of this. + if (isScopedEnumerationType(LHSType) || + isScopedEnumerationType(RHSType)) { + return InvalidOperands(Loc, LHS, RHS); + } // Sanity-check shift operands DiagnoseBadShiftValues(*this, LHS, RHS, Loc, Opc, LHSType); @@ -7213,17 +7325,6 @@ QualType Sema::CheckCompareOperands(ExprResult &LHS, ExprResult &RHS, } if (literalString) { - std::string resultComparison; - switch (Opc) { - case BO_LT: resultComparison = ") < 0"; break; - case BO_GT: resultComparison = ") > 0"; break; - case BO_LE: resultComparison = ") <= 0"; break; - case BO_GE: resultComparison = ") >= 0"; break; - case BO_EQ: resultComparison = ") == 0"; break; - case BO_NE: resultComparison = ") != 0"; break; - default: llvm_unreachable("Invalid comparison operator"); - } - DiagRuntimeBehavior(Loc, 0, PDiag(diag::warn_stringcompare) << isa(literalStringStripped) @@ -8246,6 +8347,9 @@ static QualType CheckAddressOfOperand(Sema &S, ExprResult &OrigOp, << op->getType() << op->getSourceRange(); if (sfinae) return QualType(); + // Materialize the temporary as an lvalue so that we can take its address. + OrigOp = op = new (S.Context) + MaterializeTemporaryExpr(op->getType(), OrigOp.take(), true); } else if (isa(op)) { return S.Context.getPointerType(op->getType()); } else if (lval == Expr::LV_MemberFunction) { @@ -8501,6 +8605,36 @@ static void DiagnoseSelfAssignment(Sema &S, Expr *LHSExpr, Expr *RHSExpr, << LHSExpr->getSourceRange() << RHSExpr->getSourceRange(); } +/// Check if a bitwise-& is performed on an Objective-C pointer. This +/// is usually indicative of introspection within the Objective-C pointer. +static void checkObjCPointerIntrospection(Sema &S, ExprResult &L, ExprResult &R, + SourceLocation OpLoc) { + if (!S.getLangOpts().ObjC1) + return; + + const Expr *ObjCPointerExpr = 0, *OtherExpr = 0; + const Expr *LHS = L.get(); + const Expr *RHS = R.get(); + + if (LHS->IgnoreParenCasts()->getType()->isObjCObjectPointerType()) { + ObjCPointerExpr = LHS; + OtherExpr = RHS; + } + else if (RHS->IgnoreParenCasts()->getType()->isObjCObjectPointerType()) { + ObjCPointerExpr = RHS; + OtherExpr = LHS; + } + + // This warning is deliberately made very specific to reduce false + // positives with logic that uses '&' for hashing. This logic mainly + // looks for code trying to introspect into tagged pointers, which + // code should generally never do. + if (ObjCPointerExpr && isa(OtherExpr->IgnoreParenCasts())) { + S.Diag(OpLoc, diag::warn_objc_pointer_masking) + << ObjCPointerExpr->getSourceRange(); + } +} + /// CreateBuiltinBinOp - Creates a new built-in binary operation with /// operator @p Opc at location @c TokLoc. This routine only supports /// built-in operations; ActOnBinOp handles overloaded operators. @@ -8518,7 +8652,7 @@ ExprResult Sema::CreateBuiltinBinOp(SourceLocation OpLoc, InitializationKind::CreateDirectList(RHSExpr->getLocStart()); InitializedEntity Entity = InitializedEntity::InitializeTemporary(LHSExpr->getType()); - InitializationSequence InitSeq(*this, Entity, Kind, &RHSExpr, 1); + InitializationSequence InitSeq(*this, Entity, Kind, RHSExpr); ExprResult Init = InitSeq.Perform(*this, Entity, Kind, RHSExpr); if (Init.isInvalid()) return Init; @@ -8578,6 +8712,7 @@ ExprResult Sema::CreateBuiltinBinOp(SourceLocation OpLoc, ResultTy = CheckCompareOperands(LHS, RHS, OpLoc, Opc, false); break; case BO_And: + checkObjCPointerIntrospection(*this, LHS, RHS, OpLoc); case BO_Xor: case BO_Or: ResultTy = CheckBitwiseOperands(LHS, RHS, OpLoc); @@ -8819,6 +8954,33 @@ static void DiagnoseAdditionInShift(Sema &S, SourceLocation OpLoc, } } +static void DiagnoseShiftCompare(Sema &S, SourceLocation OpLoc, + Expr *LHSExpr, Expr *RHSExpr) { + CXXOperatorCallExpr *OCE = dyn_cast(LHSExpr); + if (!OCE) + return; + + FunctionDecl *FD = OCE->getDirectCallee(); + if (!FD || !FD->isOverloadedOperator()) + return; + + OverloadedOperatorKind Kind = FD->getOverloadedOperator(); + if (Kind != OO_LessLess && Kind != OO_GreaterGreater) + return; + + S.Diag(OpLoc, diag::warn_overloaded_shift_in_comparison) + << LHSExpr->getSourceRange() << RHSExpr->getSourceRange() + << (Kind == OO_LessLess); + SuggestParentheses(S, OCE->getOperatorLoc(), + S.PDiag(diag::note_precedence_silence) + << (Kind == OO_LessLess ? "<<" : ">>"), + OCE->getSourceRange()); + SuggestParentheses(S, OpLoc, + S.PDiag(diag::note_evaluate_comparison_first), + SourceRange(OCE->getArg(1)->getLocStart(), + RHSExpr->getLocEnd())); +} + /// DiagnoseBinOpPrecedence - Emit warnings for expressions with tricky /// precedence. static void DiagnoseBinOpPrecedence(Sema &Self, BinaryOperatorKind Opc, @@ -8847,6 +9009,11 @@ static void DiagnoseBinOpPrecedence(Sema &Self, BinaryOperatorKind Opc, DiagnoseAdditionInShift(Self, OpLoc, LHSExpr, Shift); DiagnoseAdditionInShift(Self, OpLoc, RHSExpr, Shift); } + + // Warn on overloaded shift operators and comparisons, such as: + // cout << 5 == 4; + if (BinaryOperator::isComparisonOp(Opc)) + DiagnoseShiftCompare(Self, OpLoc, LHSExpr, RHSExpr); } // Binary Operators. 'Tok' is the token for the operator. @@ -9611,7 +9778,7 @@ void Sema::ActOnBlockArguments(SourceLocation CaretLoc, Declarator &ParamInfo, FunctionProtoType::ExtProtoInfo EPI; EPI.HasTrailingReturn = false; EPI.TypeQuals |= DeclSpec::TQ_const; - T = Context.getFunctionType(Context.DependentTy, ArrayRef(), EPI); + T = Context.getFunctionType(Context.DependentTy, None, EPI); Sig = Context.getTrivialTypeSourceInfo(T); } @@ -9790,7 +9957,7 @@ ExprResult Sema::ActOnBlockStmtExpr(SourceLocation CaretLoc, if (isa(FTy)) { FunctionProtoType::ExtProtoInfo EPI; EPI.ExtInfo = Ext; - BlockTy = Context.getFunctionType(RetTy, ArrayRef(), EPI); + BlockTy = Context.getFunctionType(RetTy, None, EPI); // Otherwise, if we don't need to change anything about the function type, // preserve its sugar structure. @@ -9815,7 +9982,7 @@ ExprResult Sema::ActOnBlockStmtExpr(SourceLocation CaretLoc, } else { FunctionProtoType::ExtProtoInfo EPI; EPI.ExtInfo = FunctionType::ExtInfo().withNoReturn(NoReturn); - BlockTy = Context.getFunctionType(RetTy, ArrayRef(), EPI); + BlockTy = Context.getFunctionType(RetTy, None, EPI); } DiagnoseUnusedParameters(BSI->TheDecl->param_begin(), @@ -10047,6 +10214,10 @@ bool Sema::DiagnoseAssignmentResult(AssignConvertType ConvTy, if (Hint.isNull() && !CheckInferredResultType) { ConvHints.tryToFixConversion(SrcExpr, SrcType, DstType, *this); } + else if (CheckInferredResultType) { + SrcType = SrcType.getUnqualifiedType(); + DstType = DstType.getUnqualifiedType(); + } MayHaveConvFixit = true; break; case IncompatiblePointerSign: @@ -10446,11 +10617,11 @@ namespace { } ExprResult Sema::TransformToPotentiallyEvaluated(Expr *E) { - assert(ExprEvalContexts.back().Context == Unevaluated && + assert(isUnevaluatedContext() && "Should only transform unevaluated expressions"); ExprEvalContexts.back().Context = ExprEvalContexts[ExprEvalContexts.size()-2].Context; - if (ExprEvalContexts.back().Context == Unevaluated) + if (isUnevaluatedContext()) return E; return TransformToPE(*this).TransformExpr(E); } @@ -10482,7 +10653,7 @@ void Sema::PopExpressionEvaluationContext() { ExpressionEvaluationContextRecord& Rec = ExprEvalContexts.back(); if (!Rec.Lambdas.empty()) { - if (Rec.Context == Unevaluated) { + if (Rec.isUnevaluated()) { // C++11 [expr.prim.lambda]p2: // A lambda-expression shall not appear in an unevaluated operand // (Clause 5). @@ -10508,7 +10679,7 @@ void Sema::PopExpressionEvaluationContext() { // temporaries that we may have created as part of the evaluation of // the expression in that context: they aren't relevant because they // will never be constructed. - if (Rec.Context == Unevaluated || Rec.Context == ConstantEvaluated) { + if (Rec.isUnevaluated() || Rec.Context == ConstantEvaluated) { ExprCleanupObjects.erase(ExprCleanupObjects.begin() + Rec.NumCleanupObjects, ExprCleanupObjects.end()); ExprNeedsCleanups = Rec.ParentNeedsCleanups; @@ -10547,6 +10718,7 @@ static bool IsPotentiallyEvaluatedContext(Sema &SemaRef) { switch (SemaRef.ExprEvalContexts.back().Context) { case Sema::Unevaluated: + case Sema::UnevaluatedAbstract: // We are in an expression that is not potentially evaluated; do nothing. // (Depending on how you read the standard, we actually do need to do // something here for null pointer constants, but the standard's @@ -10793,6 +10965,34 @@ diagnoseUncapturableValueReference(Sema &S, SourceLocation loc, // capture. } +/// \brief Capture the given variable in the captured region. +static ExprResult captureInCapturedRegion(Sema &S, CapturedRegionScopeInfo *RSI, + VarDecl *Var, QualType FieldType, + QualType DeclRefType, + SourceLocation Loc, + bool RefersToEnclosingLocal) { + // The current implemention assumes that all variables are captured + // by references. Since there is no capture by copy, no expression evaluation + // will be needed. + // + RecordDecl *RD = RSI->TheRecordDecl; + + FieldDecl *Field + = FieldDecl::Create(S.Context, RD, Loc, Loc, 0, FieldType, + S.Context.getTrivialTypeSourceInfo(FieldType, Loc), + 0, false, ICIS_NoInit); + Field->setImplicit(true); + Field->setAccess(AS_private); + RD->addDecl(Field); + + Expr *Ref = new (S.Context) DeclRefExpr(Var, RefersToEnclosingLocal, + DeclRefType, VK_LValue, Loc); + Var->setReferenced(true); + Var->setUsed(true); + + return Ref; +} + /// \brief Capture the given variable in the given lambda expression. static ExprResult captureInLambda(Sema &S, LambdaScopeInfo *LSI, VarDecl *Var, QualType FieldType, @@ -10894,9 +11094,9 @@ static ExprResult captureInLambda(Sema &S, LambdaScopeInfo *LSI, InitializationKind InitKind = InitializationKind::CreateDirect(Loc, Loc, Loc); - InitializationSequence Init(S, Entities.back(), InitKind, &Ref, 1); + InitializationSequence Init(S, Entities.back(), InitKind, Ref); ExprResult Result(true); - if (!Init.Diagnose(S, Entities.back(), InitKind, &Ref, 1)) + if (!Init.Diagnose(S, Entities.back(), InitKind, Ref)) Result = Init.Perform(S, Entities.back(), InitKind, Ref); // If this initialization requires any cleanups (e.g., due to a @@ -10933,10 +11133,10 @@ bool Sema::tryCaptureVariable(VarDecl *Var, SourceLocation Loc, bool Explicit = (Kind != TryCapture_Implicit); unsigned FunctionScopesIndex = FunctionScopes.size() - 1; do { - // Only block literals and lambda expressions can capture; other - // scopes don't work. + // Only block literals, captured statements, and lambda expressions can + // capture; other scopes don't work. DeclContext *ParentDC; - if (isa(DC)) + if (isa(DC) || isa(DC)) ParentDC = DC->getParent(); else if (isa(DC) && cast(DC)->getOverloadedOperator() == OO_Call && @@ -10970,7 +11170,7 @@ bool Sema::tryCaptureVariable(VarDecl *Var, SourceLocation Loc, } bool IsBlock = isa(CSI); - bool IsLambda = !IsBlock; + bool IsLambda = isa(CSI); // Lambdas are not allowed to capture unnamed variables // (e.g. anonymous unions). @@ -11130,8 +11330,31 @@ bool Sema::tryCaptureVariable(VarDecl *Var, SourceLocation Loc, SourceLocation(), CaptureType, CopyExpr); Nested = true; continue; - } - + } + + if (CapturedRegionScopeInfo *RSI = dyn_cast(CSI)) { + // By default, capture variables by reference. + bool ByRef = true; + // Using an LValue reference type is consistent with Lambdas (see below). + CaptureType = Context.getLValueReferenceType(DeclRefType); + + Expr *CopyExpr = 0; + if (BuildAndDiagnose) { + ExprResult Result = captureInCapturedRegion(*this, RSI, Var, + CaptureType, DeclRefType, + Loc, Nested); + if (!Result.isInvalid()) + CopyExpr = Result.take(); + } + + // Actually capture the variable. + if (BuildAndDiagnose) + CSI->addCapture(Var, /*isBlock*/false, ByRef, Nested, Loc, + SourceLocation(), CaptureType, CopyExpr); + Nested = true; + continue; + } + LambdaScopeInfo *LSI = cast(CSI); // Determine whether we are capturing by reference or by value. @@ -11580,6 +11803,7 @@ bool Sema::DiagRuntimeBehavior(SourceLocation Loc, const Stmt *Statement, const PartialDiagnostic &PD) { switch (ExprEvalContexts.back().Context) { case Unevaluated: + case UnevaluatedAbstract: // The argument will never be evaluated, so don't complain. break; diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index 3f2cb026730..27032a91114 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -684,7 +684,8 @@ ExprResult Sema::CheckCXXThrowOperand(SourceLocation ThrowLoc, Expr *E, MarkFunctionReferenced(E->getExprLoc(), Destructor); CheckDestructorAccess(E->getExprLoc(), Destructor, PDiag(diag::err_access_dtor_exception) << Ty); - DiagnoseUseOfDecl(Destructor, E->getExprLoc()); + if (DiagnoseUseOfDecl(Destructor, E->getExprLoc())) + return ExprError(); return Owned(E); } @@ -728,9 +729,21 @@ Sema::CXXThisScopeRAII::~CXXThisScopeRAII() { } } +static Expr *captureThis(ASTContext &Context, RecordDecl *RD, + QualType ThisTy, SourceLocation Loc) { + FieldDecl *Field + = FieldDecl::Create(Context, RD, Loc, Loc, 0, ThisTy, + Context.getTrivialTypeSourceInfo(ThisTy, Loc), + 0, false, ICIS_NoInit); + Field->setImplicit(true); + Field->setAccess(AS_private); + RD->addDecl(Field); + return new (Context) CXXThisExpr(Loc, ThisTy, /*isImplicit*/true); +} + void Sema::CheckCXXThisCapture(SourceLocation Loc, bool Explicit) { // We don't need to capture this in an unevaluated context. - if (ExprEvalContexts.back().Context == Unevaluated && !Explicit) + if (isUnevaluatedContext() && !Explicit) return; // Otherwise, check that we can capture 'this'. @@ -746,6 +759,7 @@ void Sema::CheckCXXThisCapture(SourceLocation Loc, bool Explicit) { if (CSI->ImpCaptureStyle == CapturingScopeInfo::ImpCap_LambdaByref || CSI->ImpCaptureStyle == CapturingScopeInfo::ImpCap_LambdaByval || CSI->ImpCaptureStyle == CapturingScopeInfo::ImpCap_Block || + CSI->ImpCaptureStyle == CapturingScopeInfo::ImpCap_CapturedRegion || Explicit) { // This closure can capture 'this'; continue looking upwards. NumClosures++; @@ -767,18 +781,13 @@ void Sema::CheckCXXThisCapture(SourceLocation Loc, bool Explicit) { CapturingScopeInfo *CSI = cast(FunctionScopes[idx]); Expr *ThisExpr = 0; QualType ThisTy = getCurrentThisType(); - if (LambdaScopeInfo *LSI = dyn_cast(CSI)) { + if (LambdaScopeInfo *LSI = dyn_cast(CSI)) // For lambda expressions, build a field and an initializing expression. - CXXRecordDecl *Lambda = LSI->Lambda; - FieldDecl *Field - = FieldDecl::Create(Context, Lambda, Loc, Loc, 0, ThisTy, - Context.getTrivialTypeSourceInfo(ThisTy, Loc), - 0, false, ICIS_NoInit); - Field->setImplicit(true); - Field->setAccess(AS_private); - Lambda->addDecl(Field); - ThisExpr = new (Context) CXXThisExpr(Loc, ThisTy, /*isImplicit=*/true); - } + ThisExpr = captureThis(Context, LSI->Lambda, ThisTy, Loc); + else if (CapturedRegionScopeInfo *RSI + = dyn_cast(FunctionScopes[idx])) + ThisExpr = captureThis(Context, RSI->TheRecordDecl, ThisTy, Loc); + bool isNested = NumClosures > 1; CSI->addThisCapture(isNested, Loc, ThisTy, ThisExpr); } @@ -831,23 +840,20 @@ Sema::ActOnCXXTypeConstructExpr(ParsedType TypeRep, ExprResult Sema::BuildCXXTypeConstructExpr(TypeSourceInfo *TInfo, SourceLocation LParenLoc, - MultiExprArg exprs, + MultiExprArg Exprs, SourceLocation RParenLoc) { QualType Ty = TInfo->getType(); SourceLocation TyBeginLoc = TInfo->getTypeLoc().getBeginLoc(); - if (Ty->isDependentType() || CallExpr::hasAnyTypeDependentArguments(exprs)) { + if (Ty->isDependentType() || CallExpr::hasAnyTypeDependentArguments(Exprs)) { return Owned(CXXUnresolvedConstructExpr::Create(Context, TInfo, LParenLoc, - exprs, + Exprs, RParenLoc)); } - unsigned NumExprs = exprs.size(); - Expr **Exprs = exprs.data(); - bool ListInitialization = LParenLoc.isInvalid(); - assert((!ListInitialization || (NumExprs == 1 && isa(Exprs[0]))) + assert((!ListInitialization || (Exprs.size() == 1 && isa(Exprs[0]))) && "List initialization must have initializer list as expression."); SourceRange FullRange = SourceRange(TyBeginLoc, ListInitialization ? Exprs[0]->getSourceRange().getEnd() : RParenLoc); @@ -856,7 +862,7 @@ Sema::BuildCXXTypeConstructExpr(TypeSourceInfo *TInfo, // If the expression list is a single expression, the type conversion // expression is equivalent (in definedness, and if defined in meaning) to the // corresponding cast expression. - if (NumExprs == 1 && !ListInitialization) { + if (Exprs.size() == 1 && !ListInitialization) { Expr *Arg = Exprs[0]; return BuildCXXFunctionalCastExpr(TInfo, LParenLoc, Arg, RParenLoc); } @@ -879,15 +885,13 @@ Sema::BuildCXXTypeConstructExpr(TypeSourceInfo *TInfo, return ExprError(); InitializedEntity Entity = InitializedEntity::InitializeTemporary(TInfo); - InitializationKind Kind - = NumExprs ? ListInitialization - ? InitializationKind::CreateDirectList(TyBeginLoc) - : InitializationKind::CreateDirect(TyBeginLoc, - LParenLoc, RParenLoc) - : InitializationKind::CreateValue(TyBeginLoc, - LParenLoc, RParenLoc); - InitializationSequence InitSeq(*this, Entity, Kind, Exprs, NumExprs); - ExprResult Result = InitSeq.Perform(*this, Entity, Kind, exprs); + InitializationKind Kind = + Exprs.size() ? ListInitialization + ? InitializationKind::CreateDirectList(TyBeginLoc) + : InitializationKind::CreateDirect(TyBeginLoc, LParenLoc, RParenLoc) + : InitializationKind::CreateValue(TyBeginLoc, LParenLoc, RParenLoc); + InitializationSequence InitSeq(*this, Entity, Kind, Exprs); + ExprResult Result = InitSeq.Perform(*this, Entity, Kind, Exprs); if (!Result.isInvalid() && ListInitialization && isa(Result.get())) { @@ -983,7 +987,7 @@ Sema::ActOnCXXNew(SourceLocation StartLoc, bool UseGlobal, SourceLocation PlacementLParen, MultiExprArg PlacementArgs, SourceLocation PlacementRParen, SourceRange TypeIdParens, Declarator &D, Expr *Initializer) { - bool TypeContainsAuto = D.getDeclSpec().getTypeSpecType() == DeclSpec::TST_auto; + bool TypeContainsAuto = D.getDeclSpec().containsPlaceholderType(); Expr *ArraySize = 0; // If the specified type is an array, unwrap it and save the expression. @@ -1110,9 +1114,7 @@ Sema::BuildCXXNew(SourceRange Range, bool UseGlobal, HaveCompleteInit = true; // 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 (TypeMayContainAuto && AllocType->isUndeducedType()) { if (initStyle == CXXNewExpr::NoInit || NumInits == 0) return ExprError(Diag(StartLoc, diag::err_auto_new_requires_ctor_arg) << AllocType << TypeRange); @@ -1127,16 +1129,14 @@ Sema::BuildCXXNew(SourceRange Range, bool UseGlobal, << AllocType << TypeRange); } Expr *Deduce = Inits[0]; - TypeSourceInfo *DeducedType = 0; + QualType DeducedType; if (DeduceAutoType(AllocTypeInfo, Deduce, DeducedType) == DAR_Failed) return ExprError(Diag(StartLoc, diag::err_auto_new_deduction_failure) << AllocType << Deduce->getType() << TypeRange << Deduce->getSourceRange()); - if (!DeducedType) + if (DeducedType.isNull()) return ExprError(); - - AllocTypeInfo = DeducedType; - AllocType = AllocTypeInfo->getType(); + AllocType = DeducedType; } // Per C++0x [expr.new]p5, the type being constructed may be a @@ -1170,6 +1170,11 @@ Sema::BuildCXXNew(SourceRange Range, bool UseGlobal, QualType ResultType = Context.getPointerType(AllocType); + if (ArraySize && ArraySize->getType()->isNonOverloadPlaceholderType()) { + ExprResult result = CheckPlaceholderExpr(ArraySize); + if (result.isInvalid()) return ExprError(); + ArraySize = result.take(); + } // C++98 5.3.4p6: "The expression in a direct-new-declarator shall have // integral or enumeration type with a non-negative value." // C++11 [expr.new]p6: The expression [...] shall be of integral or unscoped @@ -1405,7 +1410,7 @@ Sema::BuildCXXNew(SourceRange Range, bool UseGlobal, InitializedEntity Entity = InitializedEntity::InitializeNew(StartLoc, InitType); - InitializationSequence InitSeq(*this, Entity, Kind, Inits, NumInits); + InitializationSequence InitSeq(*this, Entity, Kind, MultiExprArg(Inits, NumInits)); ExprResult FullInit = InitSeq.Perform(*this, Entity, Kind, MultiExprArg(Inits, NumInits)); if (FullInit.isInvalid()) @@ -1422,11 +1427,13 @@ Sema::BuildCXXNew(SourceRange Range, bool UseGlobal, // Mark the new and delete operators as referenced. if (OperatorNew) { - DiagnoseUseOfDecl(OperatorNew, StartLoc); + if (DiagnoseUseOfDecl(OperatorNew, StartLoc)) + return ExprError(); MarkFunctionReferenced(StartLoc, OperatorNew); } if (OperatorDelete) { - DiagnoseUseOfDecl(OperatorDelete, StartLoc); + if (DiagnoseUseOfDecl(OperatorDelete, StartLoc)) + return ExprError(); MarkFunctionReferenced(StartLoc, OperatorDelete); } @@ -1442,7 +1449,8 @@ Sema::BuildCXXNew(SourceRange Range, bool UseGlobal, CheckDestructorAccess(StartLoc, dtor, PDiag(diag::err_access_dtor) << BaseAllocType); - DiagnoseUseOfDecl(dtor, StartLoc); + if (DiagnoseUseOfDecl(dtor, StartLoc)) + return ExprError(); } } } @@ -2200,7 +2208,8 @@ Sema::ActOnCXXDelete(SourceLocation StartLoc, bool UseGlobal, if (CXXDestructorDecl *Dtor = LookupDestructor(PointeeRD)) { MarkFunctionReferenced(StartLoc, const_cast(Dtor)); - DiagnoseUseOfDecl(Dtor, StartLoc); + if (DiagnoseUseOfDecl(Dtor, StartLoc)) + return ExprError(); } // C++ [expr.delete]p3: @@ -2266,6 +2275,9 @@ Sema::ActOnCXXDelete(SourceLocation StartLoc, bool UseGlobal, ExprResult Sema::CheckConditionVariable(VarDecl *ConditionVar, SourceLocation StmtLoc, bool ConvertToBoolean) { + if (ConditionVar->isInvalidDecl()) + return ExprError(); + QualType T = ConditionVar->getType(); // C++ [stmt.select]p2: @@ -3119,7 +3131,7 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, UnaryTypeTrait UTT, case UTT_IsPOD: return T.isPODType(Self.Context); case UTT_IsLiteral: - return T->isLiteralType(); + return T->isLiteralType(Self.Context); case UTT_IsEmpty: if (const CXXRecordDecl *RD = T->getAsCXXRecordDecl()) return !RD->isUnion() && RD->isEmpty(); @@ -3486,8 +3498,7 @@ static bool evaluateTypeTrait(Sema &S, TypeTrait Kind, SourceLocation KWLoc, InitializedEntity To(InitializedEntity::InitializeTemporary(Args[0])); InitializationKind InitKind(InitializationKind::CreateDirect(KWLoc, KWLoc, RParenLoc)); - InitializationSequence Init(S, To, InitKind, - ArgExprs.begin(), ArgExprs.size()); + InitializationSequence Init(S, To, InitKind, ArgExprs); if (Init.Failed()) return false; @@ -3645,7 +3656,7 @@ static bool EvaluateBinaryTypeTrait(Sema &Self, BinaryTypeTrait BTT, EnterExpressionEvaluationContext Unevaluated(Self, Sema::Unevaluated); Sema::SFINAETrap SFINAE(Self, /*AccessCheckingSFINAE=*/true); Sema::ContextRAII TUContext(Self, Self.Context.getTranslationUnitDecl()); - InitializationSequence Init(Self, To, Kind, &FromPtr, 1); + InitializationSequence Init(Self, To, Kind, FromPtr); if (Init.Failed()) return false; @@ -4045,7 +4056,7 @@ static bool TryClassUnification(Sema &Self, Expr *From, Expr *To, QualType T = Self.Context.getLValueReferenceType(ToType); InitializedEntity Entity = InitializedEntity::InitializeTemporary(T); - InitializationSequence InitSeq(Self, Entity, Kind, &From, 1); + InitializationSequence InitSeq(Self, Entity, Kind, From); if (InitSeq.isDirectReferenceBinding()) { ToType = T; HaveConversion = true; @@ -4053,7 +4064,7 @@ static bool TryClassUnification(Sema &Self, Expr *From, Expr *To, } if (InitSeq.isAmbiguous()) - return InitSeq.Diagnose(Self, Entity, Kind, &From, 1); + return InitSeq.Diagnose(Self, Entity, Kind, From); } // -- If E2 is an rvalue, or if the conversion above cannot be done: @@ -4073,14 +4084,14 @@ static bool TryClassUnification(Sema &Self, Expr *From, Expr *To, if (FRec == TRec || FDerivedFromT) { if (TTy.isAtLeastAsQualifiedAs(FTy)) { InitializedEntity Entity = InitializedEntity::InitializeTemporary(TTy); - InitializationSequence InitSeq(Self, Entity, Kind, &From, 1); + InitializationSequence InitSeq(Self, Entity, Kind, From); if (InitSeq) { HaveConversion = true; return false; } if (InitSeq.isAmbiguous()) - return InitSeq.Diagnose(Self, Entity, Kind, &From, 1); + return InitSeq.Diagnose(Self, Entity, Kind, From); } } @@ -4098,11 +4109,11 @@ static bool TryClassUnification(Sema &Self, Expr *From, Expr *To, TTy = TTy.getUnqualifiedType(); InitializedEntity Entity = InitializedEntity::InitializeTemporary(TTy); - InitializationSequence InitSeq(Self, Entity, Kind, &From, 1); + InitializationSequence InitSeq(Self, Entity, Kind, From); HaveConversion = !InitSeq.Failed(); ToType = TTy; if (InitSeq.isAmbiguous()) - return InitSeq.Diagnose(Self, Entity, Kind, &From, 1); + return InitSeq.Diagnose(Self, Entity, Kind, From); return false; } @@ -4116,7 +4127,7 @@ static bool FindConditionalOverload(Sema &Self, ExprResult &LHS, ExprResult &RHS SourceLocation QuestionLoc) { Expr *Args[2] = { LHS.get(), RHS.get() }; OverloadCandidateSet CandidateSet(QuestionLoc); - Self.AddBuiltinOperatorCandidates(OO_Conditional, QuestionLoc, Args, 2, + Self.AddBuiltinOperatorCandidates(OO_Conditional, QuestionLoc, Args, CandidateSet); OverloadCandidateSet::iterator Best; @@ -4175,7 +4186,7 @@ static bool ConvertForConditional(Sema &Self, ExprResult &E, QualType T) { InitializationKind Kind = InitializationKind::CreateCopy(E.get()->getLocStart(), SourceLocation()); Expr *Arg = E.take(); - InitializationSequence InitSeq(Self, Entity, Kind, &Arg, 1); + InitializationSequence InitSeq(Self, Entity, Kind, Arg); ExprResult Result = InitSeq.Perform(Self, Entity, Kind, Arg); if (Result.isInvalid()) return true; @@ -4624,8 +4635,8 @@ QualType Sema::FindCompositePointerType(SourceLocation Loc, = InitializedEntity::InitializeTemporary(Composite1); InitializationKind Kind = InitializationKind::CreateCopy(Loc, SourceLocation()); - InitializationSequence E1ToC1(*this, Entity1, Kind, &E1, 1); - InitializationSequence E2ToC1(*this, Entity1, Kind, &E2, 1); + InitializationSequence E1ToC1(*this, Entity1, Kind, E1); + InitializationSequence E2ToC1(*this, Entity1, Kind, E2); if (E1ToC1 && E2ToC1) { // Conversion to Composite1 is viable. @@ -4634,8 +4645,8 @@ QualType Sema::FindCompositePointerType(SourceLocation Loc, // Composite2 is also viable. InitializedEntity Entity2 = InitializedEntity::InitializeTemporary(Composite2); - InitializationSequence E1ToC2(*this, Entity2, Kind, &E1, 1); - InitializationSequence E2ToC2(*this, Entity2, Kind, &E2, 1); + InitializationSequence E1ToC2(*this, Entity2, Kind, E1); + InitializationSequence E2ToC2(*this, Entity2, Kind, E2); if (E1ToC2 && E2ToC2) { // Both Composite1 and Composite2 are viable and are different; // this is an ambiguity. @@ -4663,8 +4674,8 @@ QualType Sema::FindCompositePointerType(SourceLocation Loc, // Check whether Composite2 is viable. InitializedEntity Entity2 = InitializedEntity::InitializeTemporary(Composite2); - InitializationSequence E1ToC2(*this, Entity2, Kind, &E1, 1); - InitializationSequence E2ToC2(*this, Entity2, Kind, &E2, 1); + InitializationSequence E1ToC2(*this, Entity2, Kind, E1); + InitializationSequence E2ToC2(*this, Entity2, Kind, E2); if (!E1ToC2 || !E2ToC2) return QualType(); @@ -4813,7 +4824,8 @@ ExprResult Sema::MaybeBindToTemporary(Expr *E) { CheckDestructorAccess(E->getExprLoc(), Destructor, PDiag(diag::err_access_dtor_temp) << E->getType()); - DiagnoseUseOfDecl(Destructor, E->getExprLoc()); + if (DiagnoseUseOfDecl(Destructor, E->getExprLoc())) + return ExprError(); // If destructor is trivial, we can avoid the extra copy. if (Destructor->isTrivial()) @@ -4968,7 +4980,8 @@ ExprResult Sema::ActOnDecltypeExpression(Expr *E) { CheckDestructorAccess(Bind->getExprLoc(), Destructor, PDiag(diag::err_access_dtor_temp) << Bind->getType()); - DiagnoseUseOfDecl(Destructor, Bind->getExprLoc()); + if (DiagnoseUseOfDecl(Destructor, Bind->getExprLoc())) + return ExprError(); // We need a cleanup, but we don't need to remember the temporary. ExprNeedsCleanups = true; @@ -5086,7 +5099,7 @@ ExprResult Sema::DiagnoseDtorReference(SourceLocation NameLoc, return ActOnCallExpr(/*Scope*/ 0, MemExpr, /*LPLoc*/ ExpectedLParenLoc, - MultiExprArg(), + None, /*RPLoc*/ ExpectedLParenLoc); } @@ -5434,7 +5447,7 @@ ExprResult Sema::BuildCXXMemberCallExpr(Expr *E, NamedDecl *FoundDecl, ResultType = ResultType.getNonLValueExprType(Context); CXXMemberCallExpr *CE = - new (Context) CXXMemberCallExpr(Context, ME, MultiExprArg(), ResultType, VK, + new (Context) CXXMemberCallExpr(Context, ME, None, ResultType, VK, Exp.get()->getLocEnd()); return CE; } diff --git a/lib/Sema/SemaExprMember.cpp b/lib/Sema/SemaExprMember.cpp index 847db24632b..545ac2746d6 100644 --- a/lib/Sema/SemaExprMember.cpp +++ b/lib/Sema/SemaExprMember.cpp @@ -60,6 +60,9 @@ enum IMAKind { /// The reference may be to an unresolved using declaration. IMA_Unresolved, + /// The reference is a contextually-permitted abstract member reference. + IMA_Abstract, + /// The reference may be to an unresolved using declaration and the /// context is not an instance method. IMA_Unresolved_StaticContext, @@ -105,7 +108,8 @@ static IMAKind ClassifyImplicitMemberAccess(Sema &SemaRef, NamedDecl *D = *I; if (D->isCXXInstanceMember()) { - if (dyn_cast(D) || dyn_cast(D)) + if (dyn_cast(D) || dyn_cast(D) + || dyn_cast(D)) isField = true; CXXRecordDecl *R = cast(D->getDeclContext()); @@ -119,19 +123,32 @@ static IMAKind ClassifyImplicitMemberAccess(Sema &SemaRef, // member reference. if (Classes.empty()) return IMA_Static; - - bool IsCXX11UnevaluatedField = false; - if (SemaRef.getLangOpts().CPlusPlus11 && isField) { - // C++11 [expr.prim.general]p12: - // An id-expression that denotes a non-static data member or non-static - // member function of a class can only be used: - // (...) - // - if that id-expression denotes a non-static data member and it - // appears in an unevaluated operand. - const Sema::ExpressionEvaluationContextRecord& record - = SemaRef.ExprEvalContexts.back(); - if (record.Context == Sema::Unevaluated) - IsCXX11UnevaluatedField = true; + + // C++11 [expr.prim.general]p12: + // An id-expression that denotes a non-static data member or non-static + // member function of a class can only be used: + // (...) + // - if that id-expression denotes a non-static data member and it + // appears in an unevaluated operand. + // + // This rule is specific to C++11. However, we also permit this form + // in unevaluated inline assembly operands, like the operand to a SIZE. + IMAKind AbstractInstanceResult = IMA_Static; // happens to be 'false' + assert(!AbstractInstanceResult); + switch (SemaRef.ExprEvalContexts.back().Context) { + case Sema::Unevaluated: + if (isField && SemaRef.getLangOpts().CPlusPlus11) + AbstractInstanceResult = IMA_Field_Uneval_Context; + break; + + case Sema::UnevaluatedAbstract: + AbstractInstanceResult = IMA_Abstract; + break; + + case Sema::ConstantEvaluated: + case Sema::PotentiallyEvaluated: + case Sema::PotentiallyEvaluatedIfUsed: + break; } // If the current context is not an instance method, it can't be @@ -140,8 +157,8 @@ static IMAKind ClassifyImplicitMemberAccess(Sema &SemaRef, if (hasNonInstance) return IMA_Mixed_StaticContext; - return IsCXX11UnevaluatedField ? IMA_Field_Uneval_Context - : IMA_Error_StaticContext; + return AbstractInstanceResult ? AbstractInstanceResult + : IMA_Error_StaticContext; } CXXRecordDecl *contextClass; @@ -171,8 +188,8 @@ static IMAKind ClassifyImplicitMemberAccess(Sema &SemaRef, // which case it's an error if any of those members are selected). if (isProvablyNotDerivedFrom(SemaRef, contextClass, Classes)) return hasNonInstance ? IMA_Mixed_Unrelated : - IsCXX11UnevaluatedField ? IMA_Field_Uneval_Context : - IMA_Error_Unrelated; + AbstractInstanceResult ? AbstractInstanceResult : + IMA_Error_Unrelated; return (hasNonInstance ? IMA_Mixed : IMA_Instance); } @@ -232,6 +249,7 @@ Sema::BuildPossibleImplicitMemberExpr(const CXXScopeSpec &SS, << R.getLookupNameInfo().getName(); // Fall through. case IMA_Static: + case IMA_Abstract: case IMA_Mixed_StaticContext: case IMA_Unresolved_StaticContext: if (TemplateArgs || TemplateKWLoc.isValid()) @@ -778,6 +796,19 @@ Sema::BuildAnonymousStructUnionMemberReference(const CXXScopeSpec &SS, return Owned(result); } +static ExprResult +BuildMSPropertyRefExpr(Sema &S, Expr *BaseExpr, bool IsArrow, + const CXXScopeSpec &SS, + MSPropertyDecl *PD, + const DeclarationNameInfo &NameInfo) { + // Property names are always simple identifiers and therefore never + // require any interesting additional storage. + return new (S.Context) MSPropertyRefExpr(BaseExpr, PD, IsArrow, + S.Context.PseudoObjectTy, VK_LValue, + SS.getWithLocInContext(S.Context), + NameInfo.getLoc()); +} + /// \brief Build a MemberExpr AST node. static MemberExpr *BuildMemberExpr(Sema &SemaRef, ASTContext &C, Expr *Base, bool isArrow, @@ -935,6 +966,10 @@ Sema::BuildMemberReferenceExpr(Expr *BaseExpr, QualType BaseExprType, return BuildFieldReferenceExpr(*this, BaseExpr, IsArrow, SS, FD, FoundDecl, MemberNameInfo); + if (MSPropertyDecl *PD = dyn_cast(MemberDecl)) + return BuildMSPropertyRefExpr(*this, BaseExpr, IsArrow, SS, PD, + MemberNameInfo); + if (IndirectFieldDecl *FD = dyn_cast(MemberDecl)) // We may have found a field within an anonymous union or struct // (C++ [class.union]). diff --git a/lib/Sema/SemaExprObjC.cpp b/lib/Sema/SemaExprObjC.cpp index e7b5ec9b016..cf77896cb8c 100644 --- a/lib/Sema/SemaExprObjC.cpp +++ b/lib/Sema/SemaExprObjC.cpp @@ -239,7 +239,7 @@ static ObjCMethodDecl *getNSNumberFactoryMethod(Sema &S, SourceLocation Loc, &CX.Idents.get("value"), NumberType, /*TInfo=*/0, SC_None, 0); - Method->setMethodParams(S.Context, value, ArrayRef()); + Method->setMethodParams(S.Context, value, None); } if (!validateBoxingMethod(S, Loc, S.NSNumberDecl, Sel, Method)) @@ -343,7 +343,7 @@ static ExprResult CheckObjCCollectionLiteralElement(Sema &S, Expr *Element, InitializationKind Kind = InitializationKind::CreateCopy(Element->getLocStart(), SourceLocation()); - InitializationSequence Seq(S, Entity, Kind, &Element, 1); + InitializationSequence Seq(S, Entity, Kind, Element); if (!Seq.Failed()) return Seq.Perform(S, Entity, Kind, Element); } @@ -490,7 +490,7 @@ ExprResult Sema::BuildObjCBoxedExpr(SourceRange SR, Expr *ValueExpr) { Context.getPointerType(ConstCharType), /*TInfo=*/0, SC_None, 0); - M->setMethodParams(Context, value, ArrayRef()); + M->setMethodParams(Context, value, None); BoxingMethod = M; } @@ -665,7 +665,7 @@ ExprResult Sema::BuildObjCArrayLiteral(SourceRange SR, MultiExprArg Elements) { Context.UnsignedLongTy, /*TInfo=*/0, SC_None, 0); Params.push_back(cnt); - Method->setMethodParams(Context, Params, ArrayRef()); + Method->setMethodParams(Context, Params, None); } if (!validateBoxingMethod(*this, SR.getBegin(), NSArrayDecl, Sel, Method)) @@ -788,7 +788,7 @@ ExprResult Sema::BuildObjCDictionaryLiteral(SourceRange SR, Context.UnsignedLongTy, /*TInfo=*/0, SC_None, 0); Params.push_back(cnt); - Method->setMethodParams(Context, Params, ArrayRef()); + Method->setMethodParams(Context, Params, None); } if (!validateBoxingMethod(*this, SR.getBegin(), NSDictionaryDecl, Sel, @@ -1191,6 +1191,12 @@ bool Sema::CheckMessageArgumentTypes(QualType ReceiverType, bool isClassMessage, bool isSuperMessage, SourceLocation lbrac, SourceLocation rbrac, QualType &ReturnType, ExprValueKind &VK) { + SourceLocation SelLoc; + if (!SelectorLocs.empty() && SelectorLocs.front().isValid()) + SelLoc = SelectorLocs.front(); + else + SelLoc = lbrac; + if (!Method) { // Apply default argument promotion as for (C99 6.5.2.2p6). for (unsigned i = 0; i != NumArgs; i++) { @@ -1200,7 +1206,7 @@ bool Sema::CheckMessageArgumentTypes(QualType ReceiverType, ExprResult result; if (getLangOpts().DebuggerSupport) { QualType paramTy; // ignored - result = checkUnknownAnyArg(lbrac, Args[i], paramTy); + result = checkUnknownAnyArg(SelLoc, Args[i], paramTy); } else { result = DefaultArgumentPromotion(Args[i]); } @@ -1216,7 +1222,7 @@ bool Sema::CheckMessageArgumentTypes(QualType ReceiverType, DiagID = isClassMessage ? diag::warn_class_method_not_found : diag::warn_inst_method_not_found; if (!getLangOpts().DebuggerSupport) - Diag(lbrac, DiagID) + Diag(SelLoc, DiagID) << Sel << isClassMessage << SourceRange(SelectorLocs.front(), SelectorLocs.back()); @@ -1242,7 +1248,7 @@ bool Sema::CheckMessageArgumentTypes(QualType ReceiverType, NumNamedArgs = Method->param_size(); // FIXME. This need be cleaned up. if (NumArgs < NumNamedArgs) { - Diag(lbrac, diag::err_typecheck_call_too_few_args) + Diag(SelLoc, diag::err_typecheck_call_too_few_args) << 2 << NumNamedArgs << NumArgs; return false; } @@ -1268,7 +1274,7 @@ bool Sema::CheckMessageArgumentTypes(QualType ReceiverType, // from the argument. if (param->getType() == Context.UnknownAnyTy) { QualType paramType; - ExprResult argE = checkUnknownAnyArg(lbrac, argExpr, paramType); + ExprResult argE = checkUnknownAnyArg(SelLoc, argExpr, paramType); if (argE.isInvalid()) { IsError = true; } else { @@ -1287,7 +1293,7 @@ bool Sema::CheckMessageArgumentTypes(QualType ReceiverType, InitializedEntity Entity = InitializedEntity::InitializeParameter(Context, param); - ExprResult ArgE = PerformCopyInitialization(Entity, lbrac, Owned(argExpr)); + ExprResult ArgE = PerformCopyInitialization(Entity, SelLoc, Owned(argExpr)); if (ArgE.isInvalid()) IsError = true; else @@ -1317,10 +1323,11 @@ bool Sema::CheckMessageArgumentTypes(QualType ReceiverType, } } - DiagnoseSentinelCalls(Method, lbrac, Args, NumArgs); + DiagnoseSentinelCalls(Method, SelLoc, Args, NumArgs); // Do additional checkings on method. - IsError |= CheckObjCMethodCall(Method, lbrac, Args, NumArgs); + IsError |= CheckObjCMethodCall(Method, SelLoc, + llvm::makeArrayRef(Args, NumArgs)); return IsError; } @@ -1328,7 +1335,7 @@ bool Sema::CheckMessageArgumentTypes(QualType ReceiverType, bool Sema::isSelfExpr(Expr *receiver) { // 'self' is objc 'self' in an objc method only. ObjCMethodDecl *method = - dyn_cast(CurContext->getNonClosureAncestor()); + dyn_cast_or_null(CurContext->getNonClosureAncestor()); if (!method) return false; receiver = receiver->IgnoreParenLValueCasts(); @@ -1993,7 +2000,12 @@ ExprResult Sema::BuildClassMessage(TypeSourceInfo *ReceiverTypeInfo, << FixItHint::CreateInsertion(Loc, "["); LBracLoc = Loc; } - + SourceLocation SelLoc; + if (!SelectorLocs.empty() && SelectorLocs.front().isValid()) + SelLoc = SelectorLocs.front(); + else + SelLoc = Loc; + if (ReceiverType->isDependentType()) { // If the receiver type is dependent, we can't type-check anything // at this point. Build a dependent expression. @@ -2018,7 +2030,7 @@ ExprResult Sema::BuildClassMessage(TypeSourceInfo *ReceiverTypeInfo, assert(Class && "We don't know which class we're messaging?"); // objc++ diagnoses during typename annotation. if (!getLangOpts().CPlusPlus) - (void)DiagnoseUseOfDecl(Class, Loc); + (void)DiagnoseUseOfDecl(Class, SelLoc); // Find the method we are messaging. if (!Method) { SourceRange TypeRange @@ -2043,7 +2055,7 @@ ExprResult Sema::BuildClassMessage(TypeSourceInfo *ReceiverTypeInfo, if (!Method) Method = Class->lookupPrivateClassMethod(Sel); - if (Method && DiagnoseUseOfDecl(Method, Loc)) + if (Method && DiagnoseUseOfDecl(Method, SelLoc)) return ExprError(); } @@ -2159,7 +2171,14 @@ ExprResult Sema::BuildInstanceMessage(Expr *Receiver, bool isImplicit) { // The location of the receiver. SourceLocation Loc = SuperLoc.isValid()? SuperLoc : Receiver->getLocStart(); - + SourceRange RecRange = + SuperLoc.isValid()? SuperLoc : Receiver->getSourceRange(); + SourceLocation SelLoc; + if (!SelectorLocs.empty() && SelectorLocs.front().isValid()) + SelLoc = SelectorLocs.front(); + else + SelLoc = Loc; + if (LBracLoc.isInvalid()) { Diag(Loc, diag::err_missing_open_square_message_send) << FixItHint::CreateInsertion(Loc, "["); @@ -2264,7 +2283,7 @@ ExprResult Sema::BuildInstanceMessage(Expr *Receiver, Method = LookupMethodInQualifiedType(Sel, QClassTy, true); // warn if instance method found for a Class message. if (Method) { - Diag(Loc, diag::warn_instance_method_on_class_found) + Diag(SelLoc, diag::warn_instance_method_on_class_found) << Method->getSelector() << Sel; Diag(Method->getLocation(), diag::note_method_declared_at) << Method->getDeclName(); @@ -2279,7 +2298,7 @@ ExprResult Sema::BuildInstanceMessage(Expr *Receiver, if (!Method) Method = ClassDecl->lookupPrivateClassMethod(Sel); } - if (Method && DiagnoseUseOfDecl(Method, Loc)) + if (Method && DiagnoseUseOfDecl(Method, SelLoc)) return ExprError(); } if (!Method) { @@ -2298,7 +2317,7 @@ ExprResult Sema::BuildInstanceMessage(Expr *Receiver, if (const ObjCInterfaceDecl *ID = dyn_cast(Method->getDeclContext())) { if (ID->getSuperClass()) - Diag(Loc, diag::warn_root_inst_method_not_found) + Diag(SelLoc, diag::warn_root_inst_method_not_found) << Sel << SourceRange(LBracLoc, RBracLoc); } } @@ -2317,7 +2336,7 @@ ExprResult Sema::BuildInstanceMessage(Expr *Receiver, Method = LookupMethodInQualifiedType(Sel, QIdTy, true); if (!Method) Method = LookupMethodInQualifiedType(Sel, QIdTy, false); - if (Method && DiagnoseUseOfDecl(Method, Loc)) + if (Method && DiagnoseUseOfDecl(Method, SelLoc)) return ExprError(); } else if (const ObjCObjectPointerType *OCIType = ReceiverType->getAsObjCInterfacePointerType()) { @@ -2353,8 +2372,8 @@ ExprResult Sema::BuildInstanceMessage(Expr *Receiver, Method = ClassDecl->lookupPrivateMethod(Sel); if (!Method && getLangOpts().ObjCAutoRefCount) { - Diag(Loc, diag::err_arc_may_not_respond) - << OCIType->getPointeeType() << Sel + Diag(SelLoc, diag::err_arc_may_not_respond) + << OCIType->getPointeeType() << Sel << RecRange << SourceRange(SelectorLocs.front(), SelectorLocs.back()); return ExprError(); } @@ -2367,12 +2386,13 @@ ExprResult Sema::BuildInstanceMessage(Expr *Receiver, Method = LookupInstanceMethodInGlobalPool(Sel, SourceRange(LBracLoc, RBracLoc)); if (Method && !forwardClass) - Diag(Loc, diag::warn_maynot_respond) - << OCIType->getInterfaceDecl()->getIdentifier() << Sel; + Diag(SelLoc, diag::warn_maynot_respond) + << OCIType->getInterfaceDecl()->getIdentifier() + << Sel << RecRange; } } } - if (Method && DiagnoseUseOfDecl(Method, Loc, forwardClass)) + if (Method && DiagnoseUseOfDecl(Method, SelLoc, forwardClass)) return ExprError(); } else { // Reject other random receiver types (e.g. structs). @@ -2401,8 +2421,6 @@ ExprResult Sema::BuildInstanceMessage(Expr *Receiver, diag::err_illegal_message_expr_incomplete_type)) return ExprError(); - SourceLocation SelLoc = SelectorLocs.front(); - // In ARC, forbid the user from sending messages to // retain/release/autorelease/dealloc/retainCount explicitly. if (getLangOpts().ObjCAutoRefCount) { @@ -2427,8 +2445,8 @@ ExprResult Sema::BuildInstanceMessage(Expr *Receiver, case OMF_release: case OMF_autorelease: case OMF_retainCount: - Diag(Loc, diag::err_arc_illegal_explicit_message) - << Sel << SelLoc; + Diag(SelLoc, diag::err_arc_illegal_explicit_message) + << Sel << RecRange; break; case OMF_performSelector: diff --git a/lib/Sema/SemaInit.cpp b/lib/Sema/SemaInit.cpp index 63309e376ea..9e8936e17b8 100644 --- a/lib/Sema/SemaInit.cpp +++ b/lib/Sema/SemaInit.cpp @@ -82,6 +82,24 @@ static Expr *IsStringInit(Expr *init, QualType declType, ASTContext &Context) { return IsStringInit(init, arrayType, Context); } +/// Update the type of a string literal, including any surrounding parentheses, +/// to match the type of the object which it is initializing. +static void updateStringLiteralType(Expr *E, QualType Ty) { + while (true) { + E->setType(Ty); + if (isa(E) || isa(E)) + break; + else if (ParenExpr *PE = dyn_cast(E)) + E = PE->getSubExpr(); + else if (UnaryOperator *UO = dyn_cast(E)) + E = UO->getSubExpr(); + else if (GenericSelectionExpr *GSE = dyn_cast(E)) + E = GSE->getResultExpr(); + else + llvm_unreachable("unexpected expr in string literal init"); + } +} + static void CheckStringInit(Expr *Str, QualType &DeclT, const ArrayType *AT, Sema &S) { // Get the length of the string as parsed. @@ -97,6 +115,7 @@ static void CheckStringInit(Expr *Str, QualType &DeclT, const ArrayType *AT, DeclT = S.Context.getConstantArrayType(IAT->getElementType(), ConstVal, ArrayType::Normal, 0); + updateStringLiteralType(Str, DeclT); return; } @@ -106,7 +125,7 @@ static void CheckStringInit(Expr *Str, QualType &DeclT, const ArrayType *AT, // the size may be smaller or larger than the string we are initializing. // FIXME: Avoid truncation for 64-bit length strings. if (S.getLangOpts().CPlusPlus) { - if (StringLiteral *SL = dyn_cast(Str)) { + if (StringLiteral *SL = dyn_cast(Str->IgnoreParens())) { // For Pascal strings it's OK to strip off the terminating null character, // so the example below is valid: // @@ -132,7 +151,7 @@ static void CheckStringInit(Expr *Str, QualType &DeclT, const ArrayType *AT, // something like: // char x[1] = "foo"; // then this will set the string literal's type to char[1]. - Str->setType(DeclT); + updateStringLiteralType(Str, DeclT); } //===----------------------------------------------------------------------===// @@ -279,7 +298,7 @@ void InitListChecker::CheckValueInitializable(const InitializedEntity &Entity) { SourceLocation Loc; InitializationKind Kind = InitializationKind::CreateValue(Loc, Loc, Loc, true); - InitializationSequence InitSeq(SemaRef, Entity, Kind, 0, 0); + InitializationSequence InitSeq(SemaRef, Entity, Kind, None); if (InitSeq.Failed()) hadError = true; } @@ -293,6 +312,21 @@ void InitListChecker::FillInValueInitForField(unsigned Init, FieldDecl *Field, InitializedEntity MemberEntity = InitializedEntity::InitializeMember(Field, &ParentEntity); if (Init >= NumInits || !ILE->getInit(Init)) { + // If there's no explicit initializer but we have a default initializer, use + // that. This only happens in C++1y, since classes with default + // initializers are not aggregates in C++11. + if (Field->hasInClassInitializer()) { + Expr *DIE = CXXDefaultInitExpr::Create(SemaRef.Context, + ILE->getRBraceLoc(), Field); + if (Init < NumInits) + ILE->setInit(Init, DIE); + else { + ILE->updateInit(SemaRef.Context, Init, DIE); + RequiresSecondPass = true; + } + return; + } + // FIXME: We probably don't need to handle references // specially here, since value-initialization of references is // handled in InitializationSequence. @@ -312,15 +346,15 @@ void InitListChecker::FillInValueInitForField(unsigned Init, FieldDecl *Field, InitializationKind Kind = InitializationKind::CreateValue(Loc, Loc, Loc, true); - InitializationSequence InitSeq(SemaRef, MemberEntity, Kind, 0, 0); + InitializationSequence InitSeq(SemaRef, MemberEntity, Kind, None); if (!InitSeq) { - InitSeq.Diagnose(SemaRef, MemberEntity, Kind, 0, 0); + InitSeq.Diagnose(SemaRef, MemberEntity, Kind, None); hadError = true; return; } ExprResult MemberInit - = InitSeq.Perform(SemaRef, MemberEntity, Kind, MultiExprArg()); + = InitSeq.Perform(SemaRef, MemberEntity, Kind, None); if (MemberInit.isInvalid()) { hadError = true; return; @@ -358,15 +392,24 @@ InitListChecker::FillInValueInitializations(const InitializedEntity &Entity, Loc = ILE->getSyntacticForm()->getLocStart(); if (const RecordType *RType = ILE->getType()->getAs()) { - if (RType->getDecl()->isUnion() && - ILE->getInitializedFieldInUnion()) + const RecordDecl *RDecl = RType->getDecl(); + if (RDecl->isUnion() && ILE->getInitializedFieldInUnion()) FillInValueInitForField(0, ILE->getInitializedFieldInUnion(), Entity, ILE, RequiresSecondPass); - else { + else if (RDecl->isUnion() && isa(RDecl) && + cast(RDecl)->hasInClassInitializer()) { + for (RecordDecl::field_iterator Field = RDecl->field_begin(), + FieldEnd = RDecl->field_end(); + Field != FieldEnd; ++Field) { + if (Field->hasInClassInitializer()) { + FillInValueInitForField(0, *Field, Entity, ILE, RequiresSecondPass); + break; + } + } + } else { unsigned Init = 0; - for (RecordDecl::field_iterator - Field = RType->getDecl()->field_begin(), - FieldEnd = RType->getDecl()->field_end(); + for (RecordDecl::field_iterator Field = RDecl->field_begin(), + FieldEnd = RDecl->field_end(); Field != FieldEnd; ++Field) { if (Field->isUnnamedBitfield()) continue; @@ -381,7 +424,7 @@ InitListChecker::FillInValueInitializations(const InitializedEntity &Entity, ++Init; // Only look at the first initialization of a union. - if (RType->getDecl()->isUnion()) + if (RDecl->isUnion()) break; } } @@ -421,15 +464,15 @@ InitListChecker::FillInValueInitializations(const InitializedEntity &Entity, if (!InitExpr && !ILE->hasArrayFiller()) { InitializationKind Kind = InitializationKind::CreateValue(Loc, Loc, Loc, true); - InitializationSequence InitSeq(SemaRef, ElementEntity, Kind, 0, 0); + InitializationSequence InitSeq(SemaRef, ElementEntity, Kind, None); if (!InitSeq) { - InitSeq.Diagnose(SemaRef, ElementEntity, Kind, 0, 0); + InitSeq.Diagnose(SemaRef, ElementEntity, Kind, None); hadError = true; return; } ExprResult ElementInit - = InitSeq.Perform(SemaRef, ElementEntity, Kind, MultiExprArg()); + = InitSeq.Perform(SemaRef, ElementEntity, Kind, None); if (ElementInit.isInvalid()) { hadError = true; return; @@ -784,12 +827,12 @@ void InitListChecker::CheckSubElementType(const InitializedEntity &Entity, // FIXME: Better EqualLoc? InitializationKind Kind = InitializationKind::CreateCopy(expr->getLocStart(), SourceLocation()); - InitializationSequence Seq(SemaRef, Entity, Kind, &expr, 1); + InitializationSequence Seq(SemaRef, Entity, Kind, expr); if (Seq) { if (!VerifyOnly) { ExprResult Result = - Seq.Perform(SemaRef, Entity, Kind, MultiExprArg(&expr, 1)); + Seq.Perform(SemaRef, Entity, Kind, expr); if (Result.isInvalid()) hadError = true; @@ -819,8 +862,8 @@ void InitListChecker::CheckSubElementType(const InitializedEntity &Entity, hadError = true; else { ExprRes = SemaRef.DefaultFunctionArrayLvalueConversion(ExprRes.take()); - if (ExprRes.isInvalid()) - hadError = true; + if (ExprRes.isInvalid()) + hadError = true; } UpdateStructuredListElement(StructuredList, StructuredIndex, ExprRes.takeAs()); @@ -1323,8 +1366,23 @@ void InitListChecker::CheckStructUnionTypes(const InitializedEntity &Entity, } if (DeclType->isUnionType() && IList->getNumInits() == 0) { - // Value-initialize the first named member of the union. RecordDecl *RD = DeclType->getAs()->getDecl(); + + // If there's a default initializer, use it. + if (isa(RD) && cast(RD)->hasInClassInitializer()) { + if (VerifyOnly) + return; + for (RecordDecl::field_iterator FieldEnd = RD->field_end(); + Field != FieldEnd; ++Field) { + if (Field->hasInClassInitializer()) { + StructuredList->setInitializedFieldInUnion(*Field); + // FIXME: Actually build a CXXDefaultInitExpr? + return; + } + } + } + + // Value-initialize the first named member of the union. for (RecordDecl::field_iterator FieldEnd = RD->field_end(); Field != FieldEnd; ++Field) { if (Field->getDeclName()) { @@ -1428,7 +1486,7 @@ void InitListChecker::CheckStructUnionTypes(const InitializedEntity &Entity, // Find first (if any) named field and emit warning. for (RecordDecl::field_iterator it = Field, end = RD->field_end(); it != end; ++it) { - if (!it->isUnnamedBitfield()) { + if (!it->isUnnamedBitfield() && !it->hasInClassInitializer()) { SemaRef.Diag(IList->getSourceRange().getEnd(), diag::warn_missing_field_initializers) << it->getName(); break; @@ -1441,7 +1499,7 @@ void InitListChecker::CheckStructUnionTypes(const InitializedEntity &Entity, !Field->getType()->isIncompleteArrayType()) { // FIXME: Should check for holes left by designated initializers too. for (; Field != FieldEnd && !hadError; ++Field) { - if (!Field->isUnnamedBitfield()) + if (!Field->isUnnamedBitfield() && !Field->hasInClassInitializer()) CheckValueInitializable( InitializedEntity::InitializeMember(*Field, &Entity)); } @@ -2073,7 +2131,7 @@ InitListChecker::getStructuredSubobjectInit(InitListExpr *IList, unsigned Index, InitListExpr *Result = new (SemaRef.Context) InitListExpr(SemaRef.Context, - InitRange.getBegin(), MultiExprArg(), + InitRange.getBegin(), None, InitRange.getEnd()); QualType ResultType = CurrentObjectType; @@ -2336,6 +2394,7 @@ DeclarationName InitializedEntity::getName() const { case EK_VectorElement: case EK_ComplexElement: case EK_BlockElement: + case EK_CompoundLiteralInit: return DeclarationName(); } @@ -2362,6 +2421,7 @@ DeclaratorDecl *InitializedEntity::getDecl() const { case EK_ComplexElement: case EK_BlockElement: case EK_LambdaCapture: + case EK_CompoundLiteralInit: return 0; } @@ -2379,6 +2439,7 @@ bool InitializedEntity::allowsNRVO() const { case EK_Member: case EK_New: case EK_Temporary: + case EK_CompoundLiteralInit: case EK_Base: case EK_Delegating: case EK_ArrayElement: @@ -2409,6 +2470,7 @@ void InitializationSequence::Step::Destroy() { case SK_QualificationConversionRValue: case SK_QualificationConversionXValue: case SK_QualificationConversionLValue: + case SK_LValueToRValue: case SK_ListInitialization: case SK_ListConstructorCall: case SK_UnwrapInitList: @@ -2555,6 +2617,15 @@ void InitializationSequence::AddQualificationConversionStep(QualType Ty, Steps.push_back(S); } +void InitializationSequence::AddLValueToRValueStep(QualType Ty) { + assert(!Ty.hasQualifiers() && "rvalues may not have qualifiers"); + + Step S; + S.Kind = SK_LValueToRValue; + S.Type = Ty; + Steps.push_back(S); +} + void InitializationSequence::AddConversionSequenceStep( const ImplicitConversionSequence &ICS, QualType T) { @@ -2758,7 +2829,7 @@ static bool TryInitializerListConstruction(Sema &S, static OverloadingResult ResolveConstructorOverload(Sema &S, SourceLocation DeclLoc, - Expr **Args, unsigned NumArgs, + MultiExprArg Args, OverloadCandidateSet &CandidateSet, ArrayRef Ctors, OverloadCandidateSet::iterator &Best, @@ -2784,7 +2855,7 @@ ResolveConstructorOverload(Sema &S, SourceLocation DeclLoc, // If we're performing copy initialization using a copy constructor, we // suppress user-defined conversions on the arguments. We do the same for // move constructors. - if ((CopyInitializing || (InitListSyntax && NumArgs == 1)) && + if ((CopyInitializing || (InitListSyntax && Args.size() == 1)) && Constructor->isCopyOrMoveConstructor()) SuppressUserConversions = true; } @@ -2794,8 +2865,7 @@ ResolveConstructorOverload(Sema &S, SourceLocation DeclLoc, (!OnlyListConstructors || S.isInitListConstructor(Constructor))) { if (ConstructorTmpl) S.AddTemplateOverloadCandidate(ConstructorTmpl, FoundDecl, - /*ExplicitArgs*/ 0, - llvm::makeArrayRef(Args, NumArgs), + /*ExplicitArgs*/ 0, Args, CandidateSet, SuppressUserConversions); else { // C++ [over.match.copy]p1: @@ -2805,10 +2875,9 @@ ResolveConstructorOverload(Sema &S, SourceLocation DeclLoc, // context of direct-initialization, explicit conversion functions // are also considered. bool AllowExplicitConv = AllowExplicit && !CopyInitializing && - NumArgs == 1 && + Args.size() == 1 && Constructor->isCopyOrMoveConstructor(); - S.AddOverloadCandidate(Constructor, FoundDecl, - llvm::makeArrayRef(Args, NumArgs), CandidateSet, + S.AddOverloadCandidate(Constructor, FoundDecl, Args, CandidateSet, SuppressUserConversions, /*PartialOverloading=*/false, /*AllowExplicit=*/AllowExplicitConv); @@ -2828,11 +2897,10 @@ ResolveConstructorOverload(Sema &S, SourceLocation DeclLoc, static void TryConstructorInitialization(Sema &S, const InitializedEntity &Entity, const InitializationKind &Kind, - Expr **Args, unsigned NumArgs, - QualType DestType, + MultiExprArg Args, QualType DestType, InitializationSequence &Sequence, bool InitListSyntax = false) { - assert((!InitListSyntax || (NumArgs == 1 && isa(Args[0]))) && + assert((!InitListSyntax || (Args.size() == 1 && isa(Args[0]))) && "InitListSyntax must come with a single initializer list argument."); // The type we're constructing needs to be complete. @@ -2881,15 +2949,14 @@ static void TryConstructorInitialization(Sema &S, // If the initializer list has no elements and T has a default constructor, // the first phase is omitted. if (ILE->getNumInits() != 0 || !DestRecordDecl->hasDefaultConstructor()) - Result = ResolveConstructorOverload(S, Kind.getLocation(), Args, NumArgs, + Result = ResolveConstructorOverload(S, Kind.getLocation(), Args, CandidateSet, Ctors, Best, CopyInitialization, AllowExplicit, /*OnlyListConstructor=*/true, InitListSyntax); // Time to unwrap the init list. - Args = ILE->getInits(); - NumArgs = ILE->getNumInits(); + Args = MultiExprArg(ILE->getInits(), ILE->getNumInits()); } // C++11 [over.match.list]p1: @@ -2899,7 +2966,7 @@ static void TryConstructorInitialization(Sema &S, // elements of the initializer list. if (Result == OR_No_Viable_Function) { AsInitializerList = false; - Result = ResolveConstructorOverload(S, Kind.getLocation(), Args, NumArgs, + Result = ResolveConstructorOverload(S, Kind.getLocation(), Args, CandidateSet, Ctors, Best, CopyInitialization, AllowExplicit, /*OnlyListConstructors=*/false, @@ -3106,8 +3173,8 @@ static void TryListInitialization(Sema &S, return; // - Otherwise, if T is a class type, constructors are considered. - Expr *Arg = InitList; - TryConstructorInitialization(S, Entity, Kind, &Arg, 1, DestType, + Expr *InitListAsExpr = InitList; + TryConstructorInitialization(S, Entity, Kind, InitListAsExpr, DestType, Sequence, /*InitListSyntax*/true); } else Sequence.SetFailed( @@ -3351,6 +3418,57 @@ static void TryReferenceInitialization(Sema &S, T1Quals, cv2T2, T2, T2Quals, Sequence); } +/// Converts the target of reference initialization so that it has the +/// appropriate qualifiers and value kind. +/// +/// In this case, 'x' is an 'int' lvalue, but it needs to be 'const int'. +/// \code +/// int x; +/// const int &r = x; +/// \endcode +/// +/// In this case the reference is binding to a bitfield lvalue, which isn't +/// valid. Perform a load to create a lifetime-extended temporary instead. +/// \code +/// const int &r = someStruct.bitfield; +/// \endcode +static ExprValueKind +convertQualifiersAndValueKindIfNecessary(Sema &S, + InitializationSequence &Sequence, + Expr *Initializer, + QualType cv1T1, + Qualifiers T1Quals, + Qualifiers T2Quals, + bool IsLValueRef) { + bool IsNonAddressableType = Initializer->refersToBitField() || + Initializer->refersToVectorElement(); + + if (IsNonAddressableType) { + // C++11 [dcl.init.ref]p5: [...] Otherwise, the reference shall be an + // lvalue reference to a non-volatile const type, or the reference shall be + // an rvalue reference. + // + // If not, we can't make a temporary and bind to that. Give up and allow the + // error to be diagnosed later. + if (IsLValueRef && (!T1Quals.hasConst() || T1Quals.hasVolatile())) { + assert(Initializer->isGLValue()); + return Initializer->getValueKind(); + } + + // Force a load so we can materialize a temporary. + Sequence.AddLValueToRValueStep(cv1T1.getUnqualifiedType()); + return VK_RValue; + } + + if (T1Quals != T2Quals) { + Sequence.AddQualificationConversionStep(cv1T1, + Initializer->getValueKind()); + } + + return Initializer->getValueKind(); +} + + /// \brief Reference initialization without resolving overloaded functions. static void TryReferenceInitializationCore(Sema &S, const InitializedEntity &Entity, @@ -3406,11 +3524,11 @@ static void TryReferenceInitializationCore(Sema &S, Sequence.AddObjCObjectConversionStep( S.Context.getQualifiedType(T1, T2Quals)); - if (T1Quals != T2Quals) - Sequence.AddQualificationConversionStep(cv1T1, VK_LValue); - bool BindingTemporary = T1Quals.hasConst() && !T1Quals.hasVolatile() && - (Initializer->getBitField() || Initializer->refersToVectorElement()); - Sequence.AddReferenceBindingStep(cv1T1, BindingTemporary); + ExprValueKind ValueKind = + convertQualifiersAndValueKindIfNecessary(S, Sequence, Initializer, + cv1T1, T1Quals, T2Quals, + isLValueRef); + Sequence.AddReferenceBindingStep(cv1T1, ValueKind == VK_RValue); return; } @@ -3493,10 +3611,12 @@ static void TryReferenceInitializationCore(Sema &S, Sequence.AddObjCObjectConversionStep( S.Context.getQualifiedType(T1, T2Quals)); - if (T1Quals != T2Quals) - Sequence.AddQualificationConversionStep(cv1T1, ValueKind); - Sequence.AddReferenceBindingStep(cv1T1, - /*bindingTemporary=*/InitCategory.isPRValue()); + ValueKind = convertQualifiersAndValueKindIfNecessary(S, Sequence, + Initializer, cv1T1, + T1Quals, T2Quals, + isLValueRef); + + Sequence.AddReferenceBindingStep(cv1T1, ValueKind == VK_RValue); return; } @@ -3668,12 +3788,11 @@ static void TryValueInitialization(Sema &S, // 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; + MultiExprArg Args(&InitListAsExpr, InitList ? 1 : 0); bool InitListSyntax = InitList; - return TryConstructorInitialization(S, Entity, Kind, Args, NumArgs, T, - Sequence, InitListSyntax); + return TryConstructorInitialization(S, Entity, Kind, Args, T, Sequence, + InitListSyntax); } } @@ -3696,7 +3815,7 @@ static void TryDefaultInitialization(Sema &S, // constructor for T is called (and the initialization is ill-formed if // T has no accessible default constructor); if (DestType->isRecordType() && S.getLangOpts().CPlusPlus) { - TryConstructorInitialization(S, Entity, Kind, 0, 0, DestType, Sequence); + TryConstructorInitialization(S, Entity, Kind, None, DestType, Sequence); return; } @@ -4068,11 +4187,25 @@ static bool TryOCLZeroEventInitialization(Sema &S, InitializationSequence::InitializationSequence(Sema &S, const InitializedEntity &Entity, const InitializationKind &Kind, - Expr **Args, - unsigned NumArgs) + MultiExprArg Args) : FailedCandidateSet(Kind.getLocation()) { ASTContext &Context = S.Context; + // Eliminate non-overload placeholder types in the arguments. We + // need to do this before checking whether types are dependent + // because lowering a pseudo-object expression might well give us + // something of dependent type. + for (unsigned I = 0, E = Args.size(); I != E; ++I) + if (Args[I]->getType()->isNonOverloadPlaceholderType()) { + // FIXME: should we be doing this here? + ExprResult result = S.CheckPlaceholderExpr(Args[I]); + if (result.isInvalid()) { + SetFailed(FK_PlaceholderType); + return; + } + Args[I] = result.take(); + } + // C++0x [dcl.init]p16: // The semantics of initializers are as follows. The destination type is // the type of the object or reference being initialized and the source @@ -4082,7 +4215,7 @@ InitializationSequence::InitializationSequence(Sema &S, QualType DestType = Entity.getType(); if (DestType->isDependentType() || - Expr::hasAnyTypeDependentArguments(llvm::makeArrayRef(Args, NumArgs))) { + Expr::hasAnyTypeDependentArguments(Args)) { SequenceKind = DependentSequence; return; } @@ -4090,21 +4223,9 @@ InitializationSequence::InitializationSequence(Sema &S, // Almost everything is a normal sequence. setSequenceKind(NormalSequence); - for (unsigned I = 0; I != NumArgs; ++I) - if (Args[I]->getType()->isNonOverloadPlaceholderType()) { - // FIXME: should we be doing this here? - ExprResult result = S.CheckPlaceholderExpr(Args[I]); - if (result.isInvalid()) { - SetFailed(FK_PlaceholderType); - return; - } - Args[I] = result.take(); - } - - QualType SourceType; Expr *Initializer = 0; - if (NumArgs == 1) { + if (Args.size() == 1) { Initializer = Args[0]; if (!isa(Initializer)) SourceType = Initializer->getType(); @@ -4126,7 +4247,7 @@ InitializationSequence::InitializationSequence(Sema &S, // (8.3.2), shall be initialized by an object, or function, of type T or // by an object that can be converted into a T. // (Therefore, multiple arguments are not permitted.) - if (NumArgs != 1) + if (Args.size() != 1) SetFailed(FK_TooManyInitsForReference); else TryReferenceInitialization(S, Entity, Kind, Args[0], *this); @@ -4135,7 +4256,7 @@ InitializationSequence::InitializationSequence(Sema &S, // - If the initializer is (), the object is value-initialized. if (Kind.getKind() == InitializationKind::IK_Value || - (Kind.getKind() == InitializationKind::IK_Direct && NumArgs == 0)) { + (Kind.getKind() == InitializationKind::IK_Direct && Args.empty())) { TryValueInitialization(S, Entity, Kind, *this); return; } @@ -4232,7 +4353,7 @@ InitializationSequence::InitializationSequence(Sema &S, (Kind.getKind() == InitializationKind::IK_Copy && (Context.hasSameUnqualifiedType(SourceType, DestType) || S.IsDerivedFrom(SourceType, DestType)))) - TryConstructorInitialization(S, Entity, Kind, Args, NumArgs, + TryConstructorInitialization(S, Entity, Kind, Args, Entity.getType(), *this); // - Otherwise (i.e., for the remaining copy-initialization cases), // user-defined conversion sequences that can convert from the source @@ -4245,11 +4366,11 @@ InitializationSequence::InitializationSequence(Sema &S, return; } - if (NumArgs > 1) { + if (Args.size() > 1) { SetFailed(FK_TooManyInitsForScalar); return; } - assert(NumArgs == 1 && "Zero-argument case handled above"); + assert(Args.size() == 1 && "Zero-argument case handled above"); // - Otherwise, if the source type is a (possibly cv-qualified) class // type, conversion functions are considered. @@ -4350,6 +4471,7 @@ getAssignmentAction(const InitializedEntity &Entity) { case InitializedEntity::EK_ComplexElement: case InitializedEntity::EK_BlockElement: case InitializedEntity::EK_LambdaCapture: + case InitializedEntity::EK_CompoundLiteralInit: return Sema::AA_Initializing; } @@ -4372,6 +4494,7 @@ static bool shouldBindAsTemporary(const InitializedEntity &Entity) { case InitializedEntity::EK_Exception: case InitializedEntity::EK_BlockElement: case InitializedEntity::EK_LambdaCapture: + case InitializedEntity::EK_CompoundLiteralInit: return false; case InitializedEntity::EK_Parameter: @@ -4402,6 +4525,7 @@ static bool shouldDestroyTemporary(const InitializedEntity &Entity) { case InitializedEntity::EK_Temporary: case InitializedEntity::EK_ArrayElement: case InitializedEntity::EK_Exception: + case InitializedEntity::EK_CompoundLiteralInit: return true; } @@ -4483,6 +4607,7 @@ static SourceLocation getInitializationLoc(const InitializedEntity &Entity, case InitializedEntity::EK_VectorElement: case InitializedEntity::EK_ComplexElement: case InitializedEntity::EK_BlockElement: + case InitializedEntity::EK_CompoundLiteralInit: return Initializer->getLocStart(); } llvm_unreachable("missed an InitializedEntity kind?"); @@ -4619,8 +4744,7 @@ static ExprResult CopyObject(Sema &S, // Determine the arguments required to actually perform the // constructor call (we might have derived-to-base conversions, or // the copy constructor may have default arguments). - if (S.CompleteConstructorCall(Constructor, MultiExprArg(&CurInitExpr, 1), - Loc, ConstructorArgs)) + if (S.CompleteConstructorCall(Constructor, CurInitExpr, Loc, ConstructorArgs)) return ExprError(); // Actually perform the constructor call. @@ -4711,6 +4835,31 @@ static bool isReferenceBinding(const InitializationSequence::Step &s) { s.Kind == InitializationSequence::SK_BindReferenceToTemporary; } +/// Returns true if the parameters describe a constructor initialization of +/// an explicit temporary object, e.g. "Point(x, y)". +static bool isExplicitTemporary(const InitializedEntity &Entity, + const InitializationKind &Kind, + unsigned NumArgs) { + switch (Entity.getKind()) { + case InitializedEntity::EK_Temporary: + case InitializedEntity::EK_CompoundLiteralInit: + break; + default: + return false; + } + + switch (Kind.getKind()) { + case InitializationKind::IK_DirectList: + return true; + // FIXME: Hack to work around cast weirdness. + case InitializationKind::IK_Direct: + case InitializationKind::IK_Value: + return NumArgs != 1; + default: + return false; + } +} + static ExprResult PerformConstructorInitialization(Sema &S, const InitializedEntity &Entity, @@ -4761,14 +4910,11 @@ PerformConstructorInitialization(Sema &S, return ExprError(); - if (Entity.getKind() == InitializedEntity::EK_Temporary && - (Kind.getKind() == InitializationKind::IK_DirectList || - (NumArgs != 1 && // FIXME: Hack to work around cast weirdness - (Kind.getKind() == InitializationKind::IK_Direct || - Kind.getKind() == InitializationKind::IK_Value)))) { + if (isExplicitTemporary(Entity, Kind, NumArgs)) { // An explicitly-constructed temporary, e.g., X(1, 2). S.MarkFunctionReferenced(Loc, Constructor); - S.DiagnoseUseOfDecl(Constructor, Loc); + if (S.DiagnoseUseOfDecl(Constructor, Loc)) + return ExprError(); TypeSourceInfo *TSInfo = Entity.getTypeSourceInfo(); if (!TSInfo) @@ -4827,7 +4973,8 @@ PerformConstructorInitialization(Sema &S, // Only check access if all of that succeeded. S.CheckConstructorAccess(Loc, Constructor, Entity, Step.Function.FoundDecl.getAccess()); - S.DiagnoseUseOfDecl(Step.Function.FoundDecl, Loc); + if (S.DiagnoseUseOfDecl(Step.Function.FoundDecl, Loc)) + return ExprError(); if (shouldBindAsTemporary(Entity)) CurInit = S.MaybeBindToTemporary(CurInit.takeAs()); @@ -4865,6 +5012,7 @@ InitializedEntityOutlivesFullExpression(const InitializedEntity &Entity) { case InitializedEntity::EK_Parameter: case InitializedEntity::EK_Temporary: case InitializedEntity::EK_LambdaCapture: + case InitializedEntity::EK_CompoundLiteralInit: // The entity being initialized might not outlive the full-expression. return false; } @@ -4879,8 +5027,7 @@ InitializationSequence::Perform(Sema &S, MultiExprArg Args, QualType *ResultType) { if (Failed()) { - unsigned NumArgs = Args.size(); - Diagnose(S, Entity, Kind, Args.data(), NumArgs); + Diagnose(S, Entity, Kind, Args); return ExprError(); } @@ -4988,6 +5135,7 @@ InitializationSequence::Perform(Sema &S, case SK_QualificationConversionLValue: case SK_QualificationConversionXValue: case SK_QualificationConversionRValue: + case SK_LValueToRValue: case SK_ConversionSequence: case SK_ListInitialization: case SK_UnwrapInitList: @@ -5030,7 +5178,8 @@ InitializationSequence::Perform(Sema &S, // Overload resolution determined which function invoke; update the // initializer to reflect that choice. S.CheckAddressOfMemberAccess(CurInit.get(), Step->Function.FoundDecl); - S.DiagnoseUseOfDecl(Step->Function.FoundDecl, Kind.getLocation()); + if (S.DiagnoseUseOfDecl(Step->Function.FoundDecl, Kind.getLocation())) + return ExprError(); CurInit = S.FixOverloadedFunctionReference(CurInit, Step->Function.FoundDecl, Step->Function.Function); @@ -5076,13 +5225,18 @@ InitializationSequence::Perform(Sema &S, } case SK_BindReference: - if (FieldDecl *BitField = CurInit.get()->getBitField()) { - // References cannot bind to bit fields (C++ [dcl.init.ref]p5). + // References cannot bind to bit-fields (C++ [dcl.init.ref]p5). + if (CurInit.get()->refersToBitField()) { + // We don't necessarily have an unambiguous source bit-field. + FieldDecl *BitField = CurInit.get()->getSourceBitField(); S.Diag(Kind.getLocation(), diag::err_reference_bind_to_bitfield) << Entity.getType().isVolatileQualified() - << BitField->getDeclName() + << (BitField ? BitField->getDeclName() : DeclarationName()) + << (BitField != NULL) << CurInit.get()->getSourceRange(); - S.Diag(BitField->getLocation(), diag::note_bitfield_decl); + if (BitField) + S.Diag(BitField->getLocation(), diag::note_bitfield_decl); + return ExprError(); } @@ -5104,6 +5258,9 @@ InitializationSequence::Perform(Sema &S, break; case SK_BindReferenceToTemporary: + // Make sure the "temporary" is actually an rvalue. + assert(CurInit.get()->isRValue() && "not a temporary"); + // Check exception specifications if (S.CheckExceptionSpecCompatibility(CurInit.get(), DestType)) return ExprError(); @@ -5163,7 +5320,8 @@ InitializationSequence::Perform(Sema &S, S.CheckConstructorAccess(Kind.getLocation(), Constructor, Entity, FoundFn.getAccess()); - S.DiagnoseUseOfDecl(FoundFn, Kind.getLocation()); + if (S.DiagnoseUseOfDecl(FoundFn, Kind.getLocation())) + return ExprError(); CastKind = CK_ConstructorConversion; QualType Class = S.Context.getTypeDeclType(Constructor->getParent()); @@ -5177,7 +5335,8 @@ InitializationSequence::Perform(Sema &S, CXXConversionDecl *Conversion = cast(Fn); S.CheckMemberOperatorAccess(Kind.getLocation(), CurInit.get(), 0, FoundFn); - S.DiagnoseUseOfDecl(FoundFn, Kind.getLocation()); + if (S.DiagnoseUseOfDecl(FoundFn, Kind.getLocation())) + return ExprError(); // FIXME: Should we move this initialization into a separate // derived-to-base conversion? I believe the answer is "no", because @@ -5211,7 +5370,8 @@ InitializationSequence::Perform(Sema &S, S.CheckDestructorAccess(CurInit.get()->getLocStart(), Destructor, S.PDiag(diag::err_access_dtor_temp) << T); S.MarkFunctionReferenced(CurInit.get()->getLocStart(), Destructor); - S.DiagnoseUseOfDecl(Destructor, CurInit.get()->getLocStart()); + if (S.DiagnoseUseOfDecl(Destructor, CurInit.get()->getLocStart())) + return ExprError(); } } @@ -5241,6 +5401,16 @@ InitializationSequence::Perform(Sema &S, break; } + case SK_LValueToRValue: { + assert(CurInit.get()->isGLValue() && "cannot load from a prvalue"); + CurInit = S.Owned(ImplicitCastExpr::Create(S.Context, Step->Type, + CK_LValueToRValue, + CurInit.take(), + /*BasePath=*/0, + VK_RValue)); + break; + } + case SK_ConversionSequence: { Sema::CheckedConversionKind CCK = Kind.isCStyleCast()? Sema::CCK_CStyleCast @@ -5483,7 +5653,8 @@ InitializationSequence::Perform(Sema &S, S.MarkFunctionReferenced(Kind.getLocation(), Destructor); S.CheckDestructorAccess(Kind.getLocation(), Destructor, S.PDiag(diag::err_access_dtor_temp) << E); - S.DiagnoseUseOfDecl(Destructor, Kind.getLocation()); + if (S.DiagnoseUseOfDecl(Destructor, Kind.getLocation())) + return ExprError(); } } } @@ -5621,7 +5792,7 @@ static void emitBadConversionNotes(Sema &S, const InitializedEntity &entity, bool InitializationSequence::Diagnose(Sema &S, const InitializedEntity &Entity, const InitializationKind &Kind, - Expr **Args, unsigned NumArgs) { + ArrayRef Args) { if (!Failed()) return false; @@ -5629,7 +5800,7 @@ bool InitializationSequence::Diagnose(Sema &S, switch (Failure) { case FK_TooManyInitsForReference: // FIXME: Customize for the initialized entity? - if (NumArgs == 0) { + if (Args.empty()) { // Dig out the reference subobject which is uninitialized and diagnose it. // If this is value-initialization, this could be nested some way within // the target type. @@ -5641,7 +5812,7 @@ bool InitializationSequence::Diagnose(Sema &S, (void)Diagnosed; } else // FIXME: diagnostic below could be better! S.Diag(Kind.getLocation(), diag::err_reference_has_multiple_inits) - << SourceRange(Args[0]->getLocStart(), Args[NumArgs - 1]->getLocEnd()); + << SourceRange(Args.front()->getLocStart(), Args.back()->getLocEnd()); break; case FK_ArrayNeedsInitList: @@ -5688,16 +5859,14 @@ bool InitializationSequence::Diagnose(Sema &S, << DestType << Args[0]->getType() << Args[0]->getSourceRange(); - FailedCandidateSet.NoteCandidates(S, OCD_ViableCandidates, - llvm::makeArrayRef(Args, NumArgs)); + FailedCandidateSet.NoteCandidates(S, OCD_ViableCandidates, Args); break; case OR_No_Viable_Function: S.Diag(Kind.getLocation(), diag::err_typecheck_nonviable_condition) << Args[0]->getType() << DestType.getNonReferenceType() << Args[0]->getSourceRange(); - FailedCandidateSet.NoteCandidates(S, OCD_AllCandidates, - llvm::makeArrayRef(Args, NumArgs)); + FailedCandidateSet.NoteCandidates(S, OCD_AllCandidates, Args); break; case OR_Deleted: { @@ -5790,7 +5959,7 @@ bool InitializationSequence::Diagnose(Sema &S, R = SourceRange(InitList->getInit(0)->getLocEnd(), InitList->getLocEnd()); else - R = SourceRange(Args[0]->getLocEnd(), Args[NumArgs - 1]->getLocEnd()); + R = SourceRange(Args.front()->getLocEnd(), Args.back()->getLocEnd()); R.setBegin(S.PP.getLocForEndOfToken(R.getBegin())); if (Kind.isCStyleOrFunctionalCast()) @@ -5815,15 +5984,14 @@ bool InitializationSequence::Diagnose(Sema &S, case FK_ListConstructorOverloadFailed: case FK_ConstructorOverloadFailed: { SourceRange ArgsRange; - if (NumArgs) - ArgsRange = SourceRange(Args[0]->getLocStart(), - Args[NumArgs - 1]->getLocEnd()); + if (Args.size()) + ArgsRange = SourceRange(Args.front()->getLocStart(), + Args.back()->getLocEnd()); if (Failure == FK_ListConstructorOverloadFailed) { - assert(NumArgs == 1 && "List construction from other than 1 argument."); + assert(Args.size() == 1 && "List construction from other than 1 argument."); InitListExpr *InitList = cast(Args[0]); - Args = InitList->getInits(); - NumArgs = InitList->getNumInits(); + Args = MultiExprArg(InitList->getInits(), InitList->getNumInits()); } // FIXME: Using "DestType" for the entity we're printing is probably @@ -5832,8 +6000,7 @@ bool InitializationSequence::Diagnose(Sema &S, case OR_Ambiguous: S.Diag(Kind.getLocation(), diag::err_ovl_ambiguous_init) << DestType << ArgsRange; - FailedCandidateSet.NoteCandidates(S, OCD_ViableCandidates, - llvm::makeArrayRef(Args, NumArgs)); + FailedCandidateSet.NoteCandidates(S, OCD_ViableCandidates, Args); break; case OR_No_Viable_Function: @@ -5880,8 +6047,7 @@ bool InitializationSequence::Diagnose(Sema &S, S.Diag(Kind.getLocation(), diag::err_ovl_no_viable_function_in_init) << DestType << ArgsRange; - FailedCandidateSet.NoteCandidates(S, OCD_AllCandidates, - llvm::makeArrayRef(Args, NumArgs)); + FailedCandidateSet.NoteCandidates(S, OCD_AllCandidates, Args); break; case OR_Deleted: { @@ -6185,6 +6351,10 @@ void InitializationSequence::dump(raw_ostream &OS) const { OS << "qualification conversion (lvalue)"; break; + case SK_LValueToRValue: + OS << "load (lvalue to rvalue)"; + break; + case SK_ConversionSequence: OS << "implicit conversion sequence ("; S->ICS->DebugPrint(); // FIXME: use OS @@ -6395,7 +6565,7 @@ Sema::CanPerformCopyInitialization(const InitializedEntity &Entity, InitializationKind Kind = InitializationKind::CreateCopy(InitE->getLocStart(), SourceLocation()); - InitializationSequence Seq(*this, Entity, Kind, &InitE, 1); + InitializationSequence Seq(*this, Entity, Kind, InitE); return !Seq.Failed(); } @@ -6417,10 +6587,10 @@ Sema::PerformCopyInitialization(const InitializedEntity &Entity, InitializationKind Kind = InitializationKind::CreateCopy(InitE->getLocStart(), EqualLoc, AllowExplicit); - InitializationSequence Seq(*this, Entity, Kind, &InitE, 1); + InitializationSequence Seq(*this, Entity, Kind, InitE); Init.release(); - ExprResult Result = Seq.Perform(*this, Entity, Kind, MultiExprArg(&InitE, 1)); + ExprResult Result = Seq.Perform(*this, Entity, Kind, InitE); if (!Result.isInvalid() && TopLevelOfInitList) DiagnoseNarrowingInInitList(*this, Seq, Entity.getType(), diff --git a/lib/Sema/SemaLambda.cpp b/lib/Sema/SemaLambda.cpp index 53fa6dafdd3..c7ba3cc822f 100644 --- a/lib/Sema/SemaLambda.cpp +++ b/lib/Sema/SemaLambda.cpp @@ -452,8 +452,7 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro, FunctionProtoType::ExtProtoInfo EPI; EPI.HasTrailingReturn = true; EPI.TypeQuals |= DeclSpec::TQ_const; - QualType MethodTy = Context.getFunctionType(Context.DependentTy, - ArrayRef(), + QualType MethodTy = Context.getFunctionType(Context.DependentTy, None, EPI); MethodTyInfo = Context.getTrivialTypeSourceInfo(MethodTy); ExplicitParams = false; @@ -708,7 +707,7 @@ static void addFunctionPointerConversion(Sema &S, FunctionProtoType::ExtProtoInfo ExtInfo; ExtInfo.TypeQuals = Qualifiers::Const; QualType ConvTy = - S.Context.getFunctionType(FunctionPtrTy, ArrayRef(), ExtInfo); + S.Context.getFunctionType(FunctionPtrTy, None, ExtInfo); SourceLocation Loc = IntroducerRange.getBegin(); DeclarationName Name @@ -779,8 +778,7 @@ static void addBlockPointerConversion(Sema &S, FunctionProtoType::ExtProtoInfo ExtInfo; ExtInfo.TypeQuals = Qualifiers::Const; - QualType ConvTy = S.Context.getFunctionType(BlockPtrTy, ArrayRef(), - ExtInfo); + QualType ConvTy = S.Context.getFunctionType(BlockPtrTy, None, ExtInfo); SourceLocation Loc = IntroducerRange.getBegin(); DeclarationName Name @@ -862,6 +860,7 @@ ExprResult Sema::ActOnLambdaExpr(SourceLocation StartLoc, Stmt *Body, CaptureDefault = LCD_ByCopy; break; + case CapturingScopeInfo::ImpCap_CapturedRegion: case CapturingScopeInfo::ImpCap_LambdaByref: CaptureDefault = LCD_ByRef; break; @@ -949,6 +948,7 @@ ExprResult Sema::ActOnLambdaExpr(SourceLocation StartLoc, Stmt *Body, if (!CurContext->isDependentContext()) { switch (ExprEvalContexts.back().Context) { case Unevaluated: + case UnevaluatedAbstract: // We don't actually diagnose this case immediately, because we // could be within a context where we might find out later that // the expression is potentially evaluated (e.g., for typeid). diff --git a/lib/Sema/SemaLookup.cpp b/lib/Sema/SemaLookup.cpp index f26b8ed7f7a..9ab3b2d08ec 100644 --- a/lib/Sema/SemaLookup.cpp +++ b/lib/Sema/SemaLookup.cpp @@ -731,7 +731,7 @@ static bool LookupDirect(Sema &S, LookupResult &R, const DeclContext *DC) { EPI.NumExceptions = 0; QualType ExpectedType = R.getSema().Context.getFunctionType(R.getLookupName().getCXXNameType(), - ArrayRef(), EPI); + None, EPI); // Perform template argument deduction against the type that we would // expect the function to have. @@ -884,6 +884,8 @@ bool Sema::CppLookupName(LookupResult &R, Scope *S) { // } // } // + UnqualUsingDirectiveSet UDirs; + bool VisitedUsingDirectives = false; DeclContext *OutsideOfTemplateParamDC = 0; for (; S && !isNamespaceOrTranslationUnitScope(S); S = S->getParent()) { DeclContext *Ctx = static_cast(S->getEntity()); @@ -957,9 +959,28 @@ bool Sema::CppLookupName(LookupResult &R, Scope *S) { // If this is a file context, we need to perform unqualified name // lookup considering using directives. if (Ctx->isFileContext()) { - UnqualUsingDirectiveSet UDirs; - UDirs.visit(Ctx, Ctx); - UDirs.done(); + // If we haven't handled using directives yet, do so now. + if (!VisitedUsingDirectives) { + // Add using directives from this context up to the top level. + for (DeclContext *UCtx = Ctx; UCtx; UCtx = UCtx->getParent()) { + if (UCtx->isTransparentContext()) + continue; + + UDirs.visit(UCtx, UCtx); + } + + // Find the innermost file scope, so we can add using directives + // from local scopes. + Scope *InnermostFileScope = S; + while (InnermostFileScope && + !isNamespaceOrTranslationUnitScope(InnermostFileScope)) + InnermostFileScope = InnermostFileScope->getParent(); + UDirs.visitScopeChain(Initial, InnermostFileScope); + + UDirs.done(); + + VisitedUsingDirectives = true; + } if (CppNamespaceLookup(*this, R, Context, Ctx, UDirs)) { R.resolveKind(); @@ -994,11 +1015,11 @@ bool Sema::CppLookupName(LookupResult &R, Scope *S) { // // FIXME: Cache this sorted list in Scope structure, and DeclContext, so we // don't build it for each lookup! - - UnqualUsingDirectiveSet UDirs; - UDirs.visitScopeChain(Initial, S); - UDirs.done(); - + if (!VisitedUsingDirectives) { + UDirs.visitScopeChain(Initial, S); + UDirs.done(); + } + // Lookup namespace scope, and global scope. // Unqualified name lookup in C++ requires looking into scopes // that aren't strictly lexical, and therefore we walk through the @@ -2066,6 +2087,10 @@ addAssociatedClassesAndNamespaces(AssociatedLookup &Result, QualType Ty) { case Type::Complex: break; + // Non-deduced auto types only get here for error cases. + case Type::Auto: + break; + // If T is an Objective-C object or interface type, or a pointer to an // object or interface type, the associated namespace is the global // namespace. @@ -2561,6 +2586,12 @@ Sema::LookupLiteralOperator(Scope *S, LookupResult &R, bool IsRaw = false; bool IsExactMatch = false; + // If the declaration we found is invalid, skip it. + if (D->isInvalidDecl()) { + F.erase(); + continue; + } + if (FunctionDecl *FD = dyn_cast(D)) { if (FD->getNumParams() == 1 && FD->getParamDecl(0)->getType()->getAs()) @@ -3405,7 +3436,7 @@ class NamespaceSpecifierSet { } DeclContextList NamespaceSpecifierSet::BuildContextChain(DeclContext *Start) { - assert(Start && "Bulding a context chain from a null context"); + assert(Start && "Building a context chain from a null context"); DeclContextList Chain; for (DeclContext *DC = Start->getPrimaryContext(); DC != NULL; DC = DC->getLookupParent()) { @@ -3761,13 +3792,6 @@ TypoCorrection Sema::CorrectTypo(const DeclarationNameInfo &TypoName, if (S && S->isInObjcMethodScope() && Typo == getSuperIdentifier()) return TypoCorrection(); - // This is for testing. - if (Diags.getWarnOnSpellCheck()) { - unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Warning, - "spell-checking initiated for %0"); - Diag(TypoName.getLoc(), DiagID) << TypoName.getName(); - } - NamespaceSpecifierSet Namespaces(Context, CurContext, SS); TypoCorrectionConsumer Consumer(*this, Typo); diff --git a/lib/Sema/SemaObjCProperty.cpp b/lib/Sema/SemaObjCProperty.cpp index c348a9cb768..91f0881ae09 100644 --- a/lib/Sema/SemaObjCProperty.cpp +++ b/lib/Sema/SemaObjCProperty.cpp @@ -996,8 +996,10 @@ Decl *Sema::ActOnPropertyImplDecl(Scope *S, PropertyIvarType->getAs()) { const ObjCInterfaceDecl *ObjI = ObjT->getInterfaceDecl(); if (ObjI && ObjI->isArcWeakrefUnavailable()) { - Diag(PropertyDiagLoc, diag::err_arc_weak_unavailable_property); - Diag(property->getLocation(), diag::note_property_declare); + Diag(property->getLocation(), + diag::err_arc_weak_unavailable_property) << PropertyIvarType; + Diag(ClassImpDecl->getLocation(), diag::note_implemented_by_class) + << ClassImpDecl->getName(); err = true; } } @@ -1660,8 +1662,7 @@ void Sema::DefaultSynthesizeProperties(Scope *S, Decl *D) { } void Sema::DiagnoseUnimplementedProperties(Scope *S, ObjCImplDecl* IMPDecl, - ObjCContainerDecl *CDecl, - const SelectorSet &InsMap) { + ObjCContainerDecl *CDecl) { ObjCContainerDecl::PropertyMap NoNeedToImplPropMap; ObjCInterfaceDecl *IDecl; // Gather properties which need not be implemented in this class @@ -1690,6 +1691,26 @@ void Sema::DiagnoseUnimplementedProperties(Scope *S, ObjCImplDecl* IMPDecl, EI = IMPDecl->propimpl_end(); I != EI; ++I) PropImplMap.insert(I->getPropertyDecl()); + SelectorSet InsMap; + // Collect property accessors implemented in current implementation. + for (ObjCImplementationDecl::instmeth_iterator + I = IMPDecl->instmeth_begin(), E = IMPDecl->instmeth_end(); I!=E; ++I) + InsMap.insert((*I)->getSelector()); + + ObjCCategoryDecl *C = dyn_cast(CDecl); + ObjCInterfaceDecl *PrimaryClass = 0; + if (C && !C->IsClassExtension()) + if ((PrimaryClass = C->getClassInterface())) + // Report unimplemented properties in the category as well. + if (ObjCImplDecl *IMP = PrimaryClass->getImplementation()) { + // When reporting on missing setter/getters, do not report when + // setter/getter is implemented in category's primary class + // implementation. + for (ObjCImplementationDecl::instmeth_iterator + I = IMP->instmeth_begin(), E = IMP->instmeth_end(); I!=E; ++I) + InsMap.insert((*I)->getSelector()); + } + for (ObjCContainerDecl::PropertyMap::iterator P = PropMap.begin(), E = PropMap.end(); P != E; ++P) { ObjCPropertyDecl *Prop = P->second; @@ -1699,7 +1720,13 @@ void Sema::DiagnoseUnimplementedProperties(Scope *S, ObjCImplDecl* IMPDecl, PropImplMap.count(Prop) || Prop->getAvailability() == AR_Unavailable) continue; - if (!InsMap.count(Prop->getGetterName())) { + // When reporting on missing property getter implementation in + // categories, do not report when they are declared in primary class, + // class's protocol, or one of it super classes. This is because, + // the class is going to implement them. + if (!InsMap.count(Prop->getGetterName()) && + (PrimaryClass == 0 || + !PrimaryClass->lookupPropertyAccessor(Prop->getGetterName(), C))) { Diag(IMPDecl->getLocation(), isa(CDecl) ? diag::warn_setter_getter_impl_required_in_category : @@ -1713,8 +1740,13 @@ void Sema::DiagnoseUnimplementedProperties(Scope *S, ObjCImplDecl* IMPDecl, Diag(RID->getLocation(), diag::note_suppressed_class_declare); } - - if (!Prop->isReadOnly() && !InsMap.count(Prop->getSetterName())) { + // When reporting on missing property setter implementation in + // categories, do not report when they are declared in primary class, + // class's protocol, or one of it super classes. This is because, + // the class is going to implement them. + if (!Prop->isReadOnly() && !InsMap.count(Prop->getSetterName()) && + (PrimaryClass == 0 || + !PrimaryClass->lookupPropertyAccessor(Prop->getSetterName(), C))) { Diag(IMPDecl->getLocation(), isa(CDecl) ? diag::warn_setter_getter_impl_required_in_category : @@ -1975,8 +2007,7 @@ void Sema::ProcessPropertyDecl(ObjCPropertyDecl *property, /*TInfo=*/0, SC_None, 0); - SetterMethod->setMethodParams(Context, Argument, - ArrayRef()); + SetterMethod->setMethodParams(Context, Argument, None); AddPropertyAttrs(*this, SetterMethod, property); diff --git a/lib/Sema/SemaOpenMP.cpp b/lib/Sema/SemaOpenMP.cpp index b8acb2d7310..c815d4f9abc 100644 --- a/lib/Sema/SemaOpenMP.cpp +++ b/lib/Sema/SemaOpenMP.cpp @@ -165,8 +165,8 @@ OMPThreadPrivateDecl *Sema::CheckOMPThreadPrivateDecl( continue; } - // Check if threadspecified is set. - if (VD->isThreadSpecified()) { + // Check if this is a TLS variable. + if (VD->getTLSKind()) { Diag(ILoc, diag::err_omp_var_thread_local) << VD; Diag(VD->getLocation(), diag::note_forward_declaration) << VD; continue; diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index 89d495ddc19..529ba127cba 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -42,13 +42,15 @@ CreateFunctionRefExpr(Sema &S, FunctionDecl *Fn, NamedDecl *FoundDecl, bool HadMultipleCandidates, SourceLocation Loc = SourceLocation(), const DeclarationNameLoc &LocInfo = DeclarationNameLoc()){ + if (S.DiagnoseUseOfDecl(FoundDecl, Loc)) + return ExprError(); + DeclRefExpr *DRE = new (S.Context) DeclRefExpr(Fn, false, Fn->getType(), VK_LValue, Loc, LocInfo); if (HadMultipleCandidates) DRE->setHadMultipleCandidates(true); S.MarkDeclRefReferenced(DRE); - S.DiagnoseUseOfDecl(FoundDecl, Loc); ExprResult E = S.Owned(DRE); E = S.DefaultFunctionArrayConversion(E.take()); @@ -940,6 +942,9 @@ Sema::CheckOverload(Scope *S, FunctionDecl *New, const LookupResult &Old, continue; } + if (!shouldLinkPossiblyHiddenDecl(*I, New)) + continue; + Match = *I; return Ovl_Match; } @@ -1832,7 +1837,7 @@ bool Sema::IsIntegralPromotion(Expr *From, QualType FromType, QualType ToType) { // conversion. using llvm::APSInt; if (From) - if (FieldDecl *MemberDecl = From->getBitField()) { + if (FieldDecl *MemberDecl = From->getSourceBitField()) { APSInt BitWidth; if (FromType->isIntegralType(Context) && MemberDecl->getBitWidth()->isIntegerConstantExpr(BitWidth, Context)) { @@ -5024,7 +5029,7 @@ ExprResult Sema::CheckConvertedConstantExpression(Expr *From, QualType T, Expr::EvalResult Eval; Eval.Diag = &Notes; - if (!Result.get()->EvaluateAsRValue(Eval, Context)) { + if (!Result.get()->EvaluateAsRValue(Eval, Context) || !Eval.Val.isInt()) { // The expression can't be folded, so we can't keep it at this position in // the AST. Result = ExprError(); @@ -5473,7 +5478,7 @@ void Sema::AddFunctionCandidates(const UnresolvedSetImpl &Fns, void Sema::AddMethodCandidate(DeclAccessPair FoundDecl, QualType ObjectType, Expr::Classification ObjectClassification, - Expr **Args, unsigned NumArgs, + ArrayRef Args, OverloadCandidateSet& CandidateSet, bool SuppressUserConversions) { NamedDecl *Decl = FoundDecl.getDecl(); @@ -5488,12 +5493,12 @@ void Sema::AddMethodCandidate(DeclAccessPair FoundDecl, AddMethodTemplateCandidate(TD, FoundDecl, ActingContext, /*ExplicitArgs*/ 0, ObjectType, ObjectClassification, - llvm::makeArrayRef(Args, NumArgs), CandidateSet, + Args, CandidateSet, SuppressUserConversions); } else { AddMethodCandidate(cast(Decl), FoundDecl, ActingContext, ObjectType, ObjectClassification, - llvm::makeArrayRef(Args, NumArgs), + Args, CandidateSet, SuppressUserConversions); } } @@ -5721,6 +5726,14 @@ Sema::AddConversionCandidate(CXXConversionDecl *Conversion, if (!CandidateSet.isNewCandidate(Conversion)) return; + // If the conversion function has an undeduced return type, trigger its + // deduction now. + if (getLangOpts().CPlusPlus1y && ConvType->isUndeducedType()) { + if (DeduceReturnType(Conversion, From->getExprLoc())) + return; + ConvType = Conversion->getConversionType().getNonReferenceType(); + } + // Overload resolution is always an unevaluated context. EnterExpressionEvaluationContext Unevaluated(*this, Sema::Unevaluated); @@ -5800,7 +5813,7 @@ Sema::AddConversionCandidate(CXXConversionDecl *Conversion, // there are 0 arguments (i.e., nothing is allocated using ASTContext's // allocator). QualType CallResultType = ConversionType.getNonLValueExprType(Context); - CallExpr Call(Context, &ConversionFn, MultiExprArg(), CallResultType, VK, + CallExpr Call(Context, &ConversionFn, None, CallResultType, VK, From->getLocStart()); ImplicitConversionSequence ICS = TryCopyInitialization(*this, &Call, ToType, @@ -5963,7 +5976,7 @@ void Sema::AddSurrogateCandidate(CXXConversionDecl *Conversion, // Determine the implicit conversion sequences for each of the // arguments. - for (unsigned ArgIdx = 0; ArgIdx < Args.size(); ++ArgIdx) { + for (unsigned ArgIdx = 0, N = Args.size(); ArgIdx != N; ++ArgIdx) { if (ArgIdx < NumArgsInProto) { // (C++ 13.3.2p3): for F to be a viable function, there shall // exist for each argument an implicit conversion sequence @@ -6000,7 +6013,7 @@ void Sema::AddSurrogateCandidate(CXXConversionDecl *Conversion, /// [over.match.oper]). void Sema::AddMemberOperatorCandidates(OverloadedOperatorKind Op, SourceLocation OpLoc, - Expr **Args, unsigned NumArgs, + ArrayRef Args, OverloadCandidateSet& CandidateSet, SourceRange OpRange) { DeclarationName OpName = Context.DeclarationNames.getCXXOperatorName(Op); @@ -6015,13 +6028,15 @@ void Sema::AddMemberOperatorCandidates(OverloadedOperatorKind Op, // constructed as follows: QualType T1 = Args[0]->getType(); - // -- If T1 is a class type, the set of member candidates is the - // result of the qualified lookup of T1::operator@ - // (13.3.1.1.1); otherwise, the set of member candidates is - // empty. + // -- If T1 is a complete class type or a class currently being + // defined, the set of member candidates is the result of the + // qualified lookup of T1::operator@ (13.3.1.1.1); otherwise, + // the set of member candidates is empty. if (const RecordType *T1Rec = T1->getAs()) { - // Complete the type if it can be completed. Otherwise, we're done. - if (RequireCompleteType(OpLoc, T1, 0)) + // Complete the type if it can be completed. + RequireCompleteType(OpLoc, T1, 0); + // If the type is neither complete nor being defined, bail out now. + if (!T1Rec->getDecl()->getDefinition()) return; LookupResult Operators(*this, OpName, OpLoc, LookupOrdinaryName); @@ -6033,7 +6048,8 @@ void Sema::AddMemberOperatorCandidates(OverloadedOperatorKind Op, Oper != OperEnd; ++Oper) AddMethodCandidate(Oper.getPair(), Args[0]->getType(), - Args[0]->Classify(Context), Args + 1, NumArgs - 1, + Args[0]->Classify(Context), + Args.slice(1), CandidateSet, /* SuppressUserConversions = */ false); } @@ -6048,7 +6064,7 @@ void Sema::AddMemberOperatorCandidates(OverloadedOperatorKind Op, /// (at the beginning of the argument list) that will be contextually /// converted to bool. void Sema::AddBuiltinCandidate(QualType ResultTy, QualType *ParamTys, - Expr **Args, unsigned NumArgs, + ArrayRef Args, OverloadCandidateSet& CandidateSet, bool IsAssignmentOperator, unsigned NumContextualBoolArguments) { @@ -6056,20 +6072,20 @@ void Sema::AddBuiltinCandidate(QualType ResultTy, QualType *ParamTys, EnterExpressionEvaluationContext Unevaluated(*this, Sema::Unevaluated); // Add this candidate - OverloadCandidate &Candidate = CandidateSet.addCandidate(NumArgs); + OverloadCandidate &Candidate = CandidateSet.addCandidate(Args.size()); Candidate.FoundDecl = DeclAccessPair::make(0, AS_none); Candidate.Function = 0; Candidate.IsSurrogate = false; Candidate.IgnoreObjectArgument = false; Candidate.BuiltinTypes.ResultTy = ResultTy; - for (unsigned ArgIdx = 0; ArgIdx < NumArgs; ++ArgIdx) + for (unsigned ArgIdx = 0, N = Args.size(); ArgIdx != N; ++ArgIdx) Candidate.BuiltinTypes.ParamTypes[ArgIdx] = ParamTys[ArgIdx]; // Determine the implicit conversion sequences for each of the // arguments. Candidate.Viable = true; - Candidate.ExplicitCallArguments = NumArgs; - for (unsigned ArgIdx = 0; ArgIdx < NumArgs; ++ArgIdx) { + Candidate.ExplicitCallArguments = Args.size(); + for (unsigned ArgIdx = 0, N = Args.size(); ArgIdx != N; ++ArgIdx) { // C++ [over.match.oper]p4: // For the built-in assignment operators, conversions of the // left operand are restricted as follows: @@ -6395,15 +6411,14 @@ BuiltinCandidateTypeSet::AddTypesConvertedFrom(QualType Ty, /// given type to the candidate set. static void AddBuiltinAssignmentOperatorCandidates(Sema &S, QualType T, - Expr **Args, - unsigned NumArgs, + ArrayRef Args, OverloadCandidateSet &CandidateSet) { QualType ParamTypes[2]; // T& operator=(T&, T) ParamTypes[0] = S.Context.getLValueReferenceType(T); ParamTypes[1] = T; - S.AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 2, CandidateSet, + S.AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, CandidateSet, /*IsAssignmentOperator=*/true); if (!S.Context.getCanonicalType(T).isVolatileQualified()) { @@ -6411,7 +6426,7 @@ static void AddBuiltinAssignmentOperatorCandidates(Sema &S, ParamTypes[0] = S.Context.getLValueReferenceType(S.Context.getVolatileType(T)); ParamTypes[1] = T; - S.AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 2, CandidateSet, + S.AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, CandidateSet, /*IsAssignmentOperator=*/true); } } @@ -6482,8 +6497,7 @@ namespace { class BuiltinOperatorOverloadBuilder { // Common instance state available to all overload candidate addition methods. Sema &S; - Expr **Args; - unsigned NumArgs; + ArrayRef Args; Qualifiers VisibleTypeConversionsQuals; bool HasArithmeticOrEnumeralCandidateType; SmallVectorImpl &CandidateTypes; @@ -6606,10 +6620,10 @@ class BuiltinOperatorOverloadBuilder { }; // Non-volatile version. - if (NumArgs == 1) - S.AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 1, CandidateSet); + if (Args.size() == 1) + S.AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, CandidateSet); else - S.AddBuiltinCandidate(CandidateTy, ParamTypes, Args, 2, CandidateSet); + S.AddBuiltinCandidate(CandidateTy, ParamTypes, Args, CandidateSet); // Use a heuristic to reduce number of builtin candidates in the set: // add volatile version only if there are conversions to a volatile type. @@ -6617,10 +6631,10 @@ class BuiltinOperatorOverloadBuilder { ParamTypes[0] = S.Context.getLValueReferenceType( S.Context.getVolatileType(CandidateTy)); - if (NumArgs == 1) - S.AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 1, CandidateSet); + if (Args.size() == 1) + S.AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, CandidateSet); else - S.AddBuiltinCandidate(CandidateTy, ParamTypes, Args, 2, CandidateSet); + S.AddBuiltinCandidate(CandidateTy, ParamTypes, Args, CandidateSet); } // Add restrict version only if there are conversions to a restrict type @@ -6630,10 +6644,10 @@ class BuiltinOperatorOverloadBuilder { ParamTypes[0] = S.Context.getLValueReferenceType( S.Context.getCVRQualifiedType(CandidateTy, Qualifiers::Restrict)); - if (NumArgs == 1) - S.AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 1, CandidateSet); + if (Args.size() == 1) + S.AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, CandidateSet); else - S.AddBuiltinCandidate(CandidateTy, ParamTypes, Args, 2, CandidateSet); + S.AddBuiltinCandidate(CandidateTy, ParamTypes, Args, CandidateSet); if (HasVolatile) { ParamTypes[0] @@ -6641,11 +6655,10 @@ class BuiltinOperatorOverloadBuilder { S.Context.getCVRQualifiedType(CandidateTy, (Qualifiers::Volatile | Qualifiers::Restrict))); - if (NumArgs == 1) - S.AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 1, - CandidateSet); + if (Args.size() == 1) + S.AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, CandidateSet); else - S.AddBuiltinCandidate(CandidateTy, ParamTypes, Args, 2, CandidateSet); + S.AddBuiltinCandidate(CandidateTy, ParamTypes, Args, CandidateSet); } } @@ -6653,12 +6666,12 @@ class BuiltinOperatorOverloadBuilder { public: BuiltinOperatorOverloadBuilder( - Sema &S, Expr **Args, unsigned NumArgs, + Sema &S, ArrayRef Args, Qualifiers VisibleTypeConversionsQuals, bool HasArithmeticOrEnumeralCandidateType, SmallVectorImpl &CandidateTypes, OverloadCandidateSet &CandidateSet) - : S(S), Args(Args), NumArgs(NumArgs), + : S(S), Args(Args), VisibleTypeConversionsQuals(VisibleTypeConversionsQuals), HasArithmeticOrEnumeralCandidateType( HasArithmeticOrEnumeralCandidateType), @@ -6760,7 +6773,7 @@ class BuiltinOperatorOverloadBuilder { continue; S.AddBuiltinCandidate(S.Context.getLValueReferenceType(PointeeTy), - &ParamTy, Args, 1, CandidateSet); + &ParamTy, Args, CandidateSet); } } @@ -6777,7 +6790,7 @@ class BuiltinOperatorOverloadBuilder { for (unsigned Arith = FirstPromotedArithmeticType; Arith < LastPromotedArithmeticType; ++Arith) { QualType ArithTy = getArithmeticType(Arith); - S.AddBuiltinCandidate(ArithTy, &ArithTy, Args, 1, CandidateSet); + S.AddBuiltinCandidate(ArithTy, &ArithTy, Args, CandidateSet); } // Extension: We also add these operators for vector types. @@ -6786,7 +6799,7 @@ class BuiltinOperatorOverloadBuilder { VecEnd = CandidateTypes[0].vector_end(); Vec != VecEnd; ++Vec) { QualType VecTy = *Vec; - S.AddBuiltinCandidate(VecTy, &VecTy, Args, 1, CandidateSet); + S.AddBuiltinCandidate(VecTy, &VecTy, Args, CandidateSet); } } @@ -6801,7 +6814,7 @@ class BuiltinOperatorOverloadBuilder { PtrEnd = CandidateTypes[0].pointer_end(); Ptr != PtrEnd; ++Ptr) { QualType ParamTy = *Ptr; - S.AddBuiltinCandidate(ParamTy, &ParamTy, Args, 1, CandidateSet); + S.AddBuiltinCandidate(ParamTy, &ParamTy, Args, CandidateSet); } } @@ -6817,7 +6830,7 @@ class BuiltinOperatorOverloadBuilder { for (unsigned Int = FirstPromotedIntegralType; Int < LastPromotedIntegralType; ++Int) { QualType IntTy = getArithmeticType(Int); - S.AddBuiltinCandidate(IntTy, &IntTy, Args, 1, CandidateSet); + S.AddBuiltinCandidate(IntTy, &IntTy, Args, CandidateSet); } // Extension: We also add this operator for vector types. @@ -6826,7 +6839,7 @@ class BuiltinOperatorOverloadBuilder { VecEnd = CandidateTypes[0].vector_end(); Vec != VecEnd; ++Vec) { QualType VecTy = *Vec; - S.AddBuiltinCandidate(VecTy, &VecTy, Args, 1, CandidateSet); + S.AddBuiltinCandidate(VecTy, &VecTy, Args, CandidateSet); } } @@ -6840,7 +6853,7 @@ class BuiltinOperatorOverloadBuilder { /// Set of (canonical) types that we've already handled. llvm::SmallPtrSet AddedTypes; - for (unsigned ArgIdx = 0; ArgIdx < NumArgs; ++ArgIdx) { + for (unsigned ArgIdx = 0, N = Args.size(); ArgIdx != N; ++ArgIdx) { for (BuiltinCandidateTypeSet::iterator MemPtr = CandidateTypes[ArgIdx].member_pointer_begin(), MemPtrEnd = CandidateTypes[ArgIdx].member_pointer_end(); @@ -6851,8 +6864,7 @@ class BuiltinOperatorOverloadBuilder { continue; QualType ParamTypes[2] = { *MemPtr, *MemPtr }; - S.AddBuiltinCandidate(S.Context.BoolTy, ParamTypes, Args, 2, - CandidateSet); + S.AddBuiltinCandidate(S.Context.BoolTy, ParamTypes, Args, CandidateSet); } } } @@ -6884,7 +6896,7 @@ class BuiltinOperatorOverloadBuilder { llvm::DenseSet > UserDefinedBinaryOperators; - for (unsigned ArgIdx = 0; ArgIdx < NumArgs; ++ArgIdx) { + for (unsigned ArgIdx = 0, N = Args.size(); ArgIdx != N; ++ArgIdx) { if (CandidateTypes[ArgIdx].enumeration_begin() != CandidateTypes[ArgIdx].enumeration_end()) { for (OverloadCandidateSet::iterator C = CandidateSet.begin(), @@ -6917,7 +6929,7 @@ class BuiltinOperatorOverloadBuilder { /// Set of (canonical) types that we've already handled. llvm::SmallPtrSet AddedTypes; - for (unsigned ArgIdx = 0; ArgIdx < NumArgs; ++ArgIdx) { + for (unsigned ArgIdx = 0, N = Args.size(); ArgIdx != N; ++ArgIdx) { for (BuiltinCandidateTypeSet::iterator Ptr = CandidateTypes[ArgIdx].pointer_begin(), PtrEnd = CandidateTypes[ArgIdx].pointer_end(); @@ -6927,8 +6939,7 @@ class BuiltinOperatorOverloadBuilder { continue; QualType ParamTypes[2] = { *Ptr, *Ptr }; - S.AddBuiltinCandidate(S.Context.BoolTy, ParamTypes, Args, 2, - CandidateSet); + S.AddBuiltinCandidate(S.Context.BoolTy, ParamTypes, Args, CandidateSet); } for (BuiltinCandidateTypeSet::iterator Enum = CandidateTypes[ArgIdx].enumeration_begin(), @@ -6944,17 +6955,16 @@ class BuiltinOperatorOverloadBuilder { continue; QualType ParamTypes[2] = { *Enum, *Enum }; - S.AddBuiltinCandidate(S.Context.BoolTy, ParamTypes, Args, 2, - CandidateSet); + S.AddBuiltinCandidate(S.Context.BoolTy, ParamTypes, Args, CandidateSet); } if (CandidateTypes[ArgIdx].hasNullPtrType()) { CanQualType NullPtrTy = S.Context.getCanonicalType(S.Context.NullPtrTy); if (AddedTypes.insert(NullPtrTy) && - !UserDefinedBinaryOperators.count(std::make_pair(NullPtrTy, + !UserDefinedBinaryOperators.count(std::make_pair(NullPtrTy, NullPtrTy))) { QualType ParamTypes[2] = { NullPtrTy, NullPtrTy }; - S.AddBuiltinCandidate(S.Context.BoolTy, ParamTypes, Args, 2, + S.AddBuiltinCandidate(S.Context.BoolTy, ParamTypes, Args, CandidateSet); } } @@ -6999,8 +7009,7 @@ class BuiltinOperatorOverloadBuilder { if (Arg == 0 || Op == OO_Plus) { // operator+(T*, ptrdiff_t) or operator-(T*, ptrdiff_t) // T* operator+(ptrdiff_t, T*); - S.AddBuiltinCandidate(*Ptr, AsymetricParamTypes, Args, 2, - CandidateSet); + S.AddBuiltinCandidate(*Ptr, AsymetricParamTypes, Args, CandidateSet); } if (Op == OO_Minus) { // ptrdiff_t operator-(T, T); @@ -7009,7 +7018,7 @@ class BuiltinOperatorOverloadBuilder { QualType ParamTypes[2] = { *Ptr, *Ptr }; S.AddBuiltinCandidate(S.Context.getPointerDiffType(), ParamTypes, - Args, 2, CandidateSet); + Args, CandidateSet); } } } @@ -7057,7 +7066,7 @@ class BuiltinOperatorOverloadBuilder { QualType Result = isComparison ? S.Context.BoolTy : getUsualArithmeticConversions(Left, Right); - S.AddBuiltinCandidate(Result, LandR, Args, 2, CandidateSet); + S.AddBuiltinCandidate(Result, LandR, Args, CandidateSet); } } @@ -7080,7 +7089,7 @@ class BuiltinOperatorOverloadBuilder { Result = *Vec2; } - S.AddBuiltinCandidate(Result, LandR, Args, 2, CandidateSet); + S.AddBuiltinCandidate(Result, LandR, Args, CandidateSet); } } } @@ -7112,7 +7121,7 @@ class BuiltinOperatorOverloadBuilder { QualType Result = (Op == OO_LessLess || Op == OO_GreaterGreater) ? LandR[0] : getUsualArithmeticConversions(Left, Right); - S.AddBuiltinCandidate(Result, LandR, Args, 2, CandidateSet); + S.AddBuiltinCandidate(Result, LandR, Args, CandidateSet); } } } @@ -7136,8 +7145,7 @@ class BuiltinOperatorOverloadBuilder { if (!AddedTypes.insert(S.Context.getCanonicalType(*Enum))) continue; - AddBuiltinAssignmentOperatorCandidates(S, *Enum, Args, 2, - CandidateSet); + AddBuiltinAssignmentOperatorCandidates(S, *Enum, Args, CandidateSet); } for (BuiltinCandidateTypeSet::iterator @@ -7147,8 +7155,7 @@ class BuiltinOperatorOverloadBuilder { if (!AddedTypes.insert(S.Context.getCanonicalType(*MemPtr))) continue; - AddBuiltinAssignmentOperatorCandidates(S, *MemPtr, Args, 2, - CandidateSet); + AddBuiltinAssignmentOperatorCandidates(S, *MemPtr, Args, CandidateSet); } } } @@ -7188,7 +7195,7 @@ class BuiltinOperatorOverloadBuilder { S.Context.getLValueReferenceType(*Ptr), isEqualOp ? *Ptr : S.Context.getPointerDiffType(), }; - S.AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 2, CandidateSet, + S.AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, CandidateSet, /*IsAssigmentOperator=*/ isEqualOp); bool NeedVolatile = !(*Ptr).isVolatileQualified() && @@ -7197,7 +7204,7 @@ class BuiltinOperatorOverloadBuilder { // volatile version ParamTypes[0] = S.Context.getLValueReferenceType(S.Context.getVolatileType(*Ptr)); - S.AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 2, CandidateSet, + S.AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, CandidateSet, /*IsAssigmentOperator=*/isEqualOp); } @@ -7206,7 +7213,7 @@ class BuiltinOperatorOverloadBuilder { // restrict version ParamTypes[0] = S.Context.getLValueReferenceType(S.Context.getRestrictType(*Ptr)); - S.AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 2, CandidateSet, + S.AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, CandidateSet, /*IsAssigmentOperator=*/isEqualOp); if (NeedVolatile) { @@ -7216,8 +7223,7 @@ class BuiltinOperatorOverloadBuilder { S.Context.getCVRQualifiedType(*Ptr, (Qualifiers::Volatile | Qualifiers::Restrict))); - S.AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 2, - CandidateSet, + S.AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, CandidateSet, /*IsAssigmentOperator=*/isEqualOp); } } @@ -7238,7 +7244,7 @@ class BuiltinOperatorOverloadBuilder { }; // non-volatile version - S.AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 2, CandidateSet, + S.AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, CandidateSet, /*IsAssigmentOperator=*/true); bool NeedVolatile = !(*Ptr).isVolatileQualified() && @@ -7247,8 +7253,8 @@ class BuiltinOperatorOverloadBuilder { // volatile version ParamTypes[0] = S.Context.getLValueReferenceType(S.Context.getVolatileType(*Ptr)); - S.AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 2, - CandidateSet, /*IsAssigmentOperator=*/true); + S.AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, CandidateSet, + /*IsAssigmentOperator=*/true); } if (!(*Ptr).isRestrictQualified() && @@ -7256,8 +7262,8 @@ class BuiltinOperatorOverloadBuilder { // restrict version ParamTypes[0] = S.Context.getLValueReferenceType(S.Context.getRestrictType(*Ptr)); - S.AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 2, - CandidateSet, /*IsAssigmentOperator=*/true); + S.AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, CandidateSet, + /*IsAssigmentOperator=*/true); if (NeedVolatile) { // volatile restrict version @@ -7266,9 +7272,8 @@ class BuiltinOperatorOverloadBuilder { S.Context.getCVRQualifiedType(*Ptr, (Qualifiers::Volatile | Qualifiers::Restrict))); - S.AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 2, - CandidateSet, /*IsAssigmentOperator=*/true); - + S.AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, CandidateSet, + /*IsAssigmentOperator=*/true); } } } @@ -7300,7 +7305,7 @@ class BuiltinOperatorOverloadBuilder { // Add this built-in operator as a candidate (VQ is empty). ParamTypes[0] = S.Context.getLValueReferenceType(getArithmeticType(Left)); - S.AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 2, CandidateSet, + S.AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, CandidateSet, /*IsAssigmentOperator=*/isEqualOp); // Add this built-in operator as a candidate (VQ is 'volatile'). @@ -7308,8 +7313,7 @@ class BuiltinOperatorOverloadBuilder { ParamTypes[0] = S.Context.getVolatileType(getArithmeticType(Left)); ParamTypes[0] = S.Context.getLValueReferenceType(ParamTypes[0]); - S.AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 2, - CandidateSet, + S.AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, CandidateSet, /*IsAssigmentOperator=*/isEqualOp); } } @@ -7328,15 +7332,14 @@ class BuiltinOperatorOverloadBuilder { ParamTypes[1] = *Vec2; // Add this built-in operator as a candidate (VQ is empty). ParamTypes[0] = S.Context.getLValueReferenceType(*Vec1); - S.AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 2, CandidateSet, + S.AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, CandidateSet, /*IsAssigmentOperator=*/isEqualOp); // Add this built-in operator as a candidate (VQ is 'volatile'). if (VisibleTypeConversionsQuals.hasVolatile()) { ParamTypes[0] = S.Context.getVolatileType(*Vec1); ParamTypes[0] = S.Context.getLValueReferenceType(ParamTypes[0]); - S.AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 2, - CandidateSet, + S.AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, CandidateSet, /*IsAssigmentOperator=*/isEqualOp); } } @@ -7368,14 +7371,13 @@ class BuiltinOperatorOverloadBuilder { // Add this built-in operator as a candidate (VQ is empty). ParamTypes[0] = S.Context.getLValueReferenceType(getArithmeticType(Left)); - S.AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 2, CandidateSet); + S.AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, CandidateSet); if (VisibleTypeConversionsQuals.hasVolatile()) { // Add this built-in operator as a candidate (VQ is 'volatile'). ParamTypes[0] = getArithmeticType(Left); ParamTypes[0] = S.Context.getVolatileType(ParamTypes[0]); ParamTypes[0] = S.Context.getLValueReferenceType(ParamTypes[0]); - S.AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 2, - CandidateSet); + S.AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, CandidateSet); } } } @@ -7390,13 +7392,13 @@ class BuiltinOperatorOverloadBuilder { // bool operator||(bool, bool); void addExclaimOverload() { QualType ParamTy = S.Context.BoolTy; - S.AddBuiltinCandidate(ParamTy, &ParamTy, Args, 1, CandidateSet, + S.AddBuiltinCandidate(ParamTy, &ParamTy, Args, CandidateSet, /*IsAssignmentOperator=*/false, /*NumContextualBoolArguments=*/1); } void addAmpAmpOrPipePipeOverload() { QualType ParamTypes[2] = { S.Context.BoolTy, S.Context.BoolTy }; - S.AddBuiltinCandidate(S.Context.BoolTy, ParamTypes, Args, 2, CandidateSet, + S.AddBuiltinCandidate(S.Context.BoolTy, ParamTypes, Args, CandidateSet, /*IsAssignmentOperator=*/false, /*NumContextualBoolArguments=*/2); } @@ -7424,7 +7426,7 @@ class BuiltinOperatorOverloadBuilder { QualType ResultTy = S.Context.getLValueReferenceType(PointeeType); // T& operator[](T*, ptrdiff_t) - S.AddBuiltinCandidate(ResultTy, ParamTypes, Args, 2, CandidateSet); + S.AddBuiltinCandidate(ResultTy, ParamTypes, Args, CandidateSet); } for (BuiltinCandidateTypeSet::iterator @@ -7439,7 +7441,7 @@ class BuiltinOperatorOverloadBuilder { QualType ResultTy = S.Context.getLValueReferenceType(PointeeType); // T& operator[](ptrdiff_t, T*) - S.AddBuiltinCandidate(ResultTy, ParamTypes, Args, 2, CandidateSet); + S.AddBuiltinCandidate(ResultTy, ParamTypes, Args, CandidateSet); } } @@ -7490,7 +7492,7 @@ class BuiltinOperatorOverloadBuilder { continue; T = Q1.apply(S.Context, T); QualType ResultTy = S.Context.getLValueReferenceType(T); - S.AddBuiltinCandidate(ResultTy, ParamTypes, Args, 2, CandidateSet); + S.AddBuiltinCandidate(ResultTy, ParamTypes, Args, CandidateSet); } } } @@ -7518,7 +7520,7 @@ class BuiltinOperatorOverloadBuilder { continue; QualType ParamTypes[2] = { *Ptr, *Ptr }; - S.AddBuiltinCandidate(*Ptr, ParamTypes, Args, 2, CandidateSet); + S.AddBuiltinCandidate(*Ptr, ParamTypes, Args, CandidateSet); } for (BuiltinCandidateTypeSet::iterator @@ -7529,7 +7531,7 @@ class BuiltinOperatorOverloadBuilder { continue; QualType ParamTypes[2] = { *MemPtr, *MemPtr }; - S.AddBuiltinCandidate(*MemPtr, ParamTypes, Args, 2, CandidateSet); + S.AddBuiltinCandidate(*MemPtr, ParamTypes, Args, CandidateSet); } if (S.getLangOpts().CPlusPlus11) { @@ -7544,7 +7546,7 @@ class BuiltinOperatorOverloadBuilder { continue; QualType ParamTypes[2] = { *Enum, *Enum }; - S.AddBuiltinCandidate(*Enum, ParamTypes, Args, 2, CandidateSet); + S.AddBuiltinCandidate(*Enum, ParamTypes, Args, CandidateSet); } } } @@ -7561,7 +7563,7 @@ class BuiltinOperatorOverloadBuilder { void Sema::AddBuiltinOperatorCandidates(OverloadedOperatorKind Op, SourceLocation OpLoc, - Expr **Args, unsigned NumArgs, + llvm::ArrayRef Args, OverloadCandidateSet& CandidateSet) { // Find all of the types that the arguments can convert to, but only // if the operator we're looking at has built-in operator candidates @@ -7569,13 +7571,13 @@ Sema::AddBuiltinOperatorCandidates(OverloadedOperatorKind Op, // candidate types or either arithmetic or enumeral candidate types. Qualifiers VisibleTypeConversionsQuals; VisibleTypeConversionsQuals.addConst(); - for (unsigned ArgIdx = 0; ArgIdx < NumArgs; ++ArgIdx) + for (unsigned ArgIdx = 0, N = Args.size(); ArgIdx != N; ++ArgIdx) VisibleTypeConversionsQuals += CollectVRQualifiers(Context, Args[ArgIdx]); bool HasNonRecordCandidateType = false; bool HasArithmeticOrEnumeralCandidateType = false; SmallVector CandidateTypes; - for (unsigned ArgIdx = 0; ArgIdx < NumArgs; ++ArgIdx) { + for (unsigned ArgIdx = 0, N = Args.size(); ArgIdx != N; ++ArgIdx) { CandidateTypes.push_back(BuiltinCandidateTypeSet(*this)); CandidateTypes[ArgIdx].AddTypesConvertedFrom(Args[ArgIdx]->getType(), OpLoc, @@ -7596,12 +7598,12 @@ Sema::AddBuiltinOperatorCandidates(OverloadedOperatorKind Op, // // We can't exit early for !, ||, or &&, since there we have always have // 'bool' overloads. - if (!HasNonRecordCandidateType && + if (!HasNonRecordCandidateType && !(Op == OO_Exclaim || Op == OO_AmpAmp || Op == OO_PipePipe)) return; // Setup an object to manage the common state for building overloads. - BuiltinOperatorOverloadBuilder OpBuilder(*this, Args, NumArgs, + BuiltinOperatorOverloadBuilder OpBuilder(*this, Args, VisibleTypeConversionsQuals, HasArithmeticOrEnumeralCandidateType, CandidateTypes, CandidateSet); @@ -7628,12 +7630,12 @@ Sema::AddBuiltinOperatorCandidates(OverloadedOperatorKind Op, break; case OO_Plus: // '+' is either unary or binary - if (NumArgs == 1) + if (Args.size() == 1) OpBuilder.addUnaryPlusPointerOverloads(); // Fall through. case OO_Minus: // '-' is either unary or binary - if (NumArgs == 1) { + if (Args.size() == 1) { OpBuilder.addUnaryPlusOrMinusArithmeticOverloads(); } else { OpBuilder.addBinaryPlusOrMinusPointerOverloads(Op); @@ -7642,7 +7644,7 @@ Sema::AddBuiltinOperatorCandidates(OverloadedOperatorKind Op, break; case OO_Star: // '*' is either unary or binary - if (NumArgs == 1) + if (Args.size() == 1) OpBuilder.addUnaryStarPointerOverloads(); else OpBuilder.addGenericBinaryArithmeticOverloads(/*isComparison=*/false); @@ -7680,7 +7682,7 @@ Sema::AddBuiltinOperatorCandidates(OverloadedOperatorKind Op, break; case OO_Amp: // '&' is either unary or binary - if (NumArgs == 1) + if (Args.size() == 1) // C++ [over.match.oper]p3: // -- For the operator ',', the unary operator '&', or the // operator '->', the built-in candidates set is empty. @@ -8508,13 +8510,35 @@ void DiagnoseBadDeduction(Sema &S, OverloadCandidate *Cand, return; } - case Sema::TDK_NonDeducedMismatch: + case Sema::TDK_NonDeducedMismatch: { // FIXME: Provide a source location to indicate what we couldn't match. + TemplateArgument FirstTA = *Cand->DeductionFailure.getFirstArg(); + TemplateArgument SecondTA = *Cand->DeductionFailure.getSecondArg(); + if (FirstTA.getKind() == TemplateArgument::Template && + SecondTA.getKind() == TemplateArgument::Template) { + TemplateName FirstTN = FirstTA.getAsTemplate(); + TemplateName SecondTN = SecondTA.getAsTemplate(); + if (FirstTN.getKind() == TemplateName::Template && + SecondTN.getKind() == TemplateName::Template) { + if (FirstTN.getAsTemplateDecl()->getName() == + SecondTN.getAsTemplateDecl()->getName()) { + // FIXME: This fixes a bad diagnostic where both templates are named + // the same. This particular case is a bit difficult since: + // 1) It is passed as a string to the diagnostic printer. + // 2) The diagnostic printer only attempts to find a better + // name for types, not decls. + // Ideally, this should folded into the diagnostic printer. + S.Diag(Fn->getLocation(), + diag::note_ovl_candidate_non_deduced_mismatch_qualified) + << FirstTN.getAsTemplateDecl() << SecondTN.getAsTemplateDecl(); + return; + } + } + } S.Diag(Fn->getLocation(), diag::note_ovl_candidate_non_deduced_mismatch) - << *Cand->DeductionFailure.getFirstArg() - << *Cand->DeductionFailure.getSecondArg(); + << FirstTA << SecondTA; return; - + } // TODO: diagnose these individually, then kill off // note_ovl_candidate_bad_deduction, which is uselessly vague. case Sema::TDK_MiscellaneousDeductionFailure: @@ -9109,17 +9133,19 @@ class AddressOfFunctionResolver = S.DeduceTemplateArguments(FunctionTemplate, &OvlExplicitTemplateArgs, TargetFunctionType, Specialization, - Info)) { + Info, /*InOverloadResolution=*/true)) { // FIXME: make a note of the failed deduction for diagnostics. (void)Result; return false; } - // Template argument deduction ensures that we have an exact match. + // Template argument deduction ensures that we have an exact match or + // compatible pointer-to-function arguments that would be adjusted by ICS. // This function template specicalization works. Specialization = cast(Specialization->getCanonicalDecl()); - assert(TargetFunctionType - == Context.getCanonicalType(Specialization->getType())); + assert(S.isSameOrCompatibleFunctionType( + Context.getCanonicalType(Specialization->getType()), + Context.getCanonicalType(TargetFunctionType))); Matches.push_back(std::make_pair(CurAccessFunPair, Specialization)); return true; } @@ -9141,6 +9167,13 @@ class AddressOfFunctionResolver if (S.CheckCUDATarget(Caller, FunDecl)) return false; + // If any candidate has a placeholder return type, trigger its deduction + // now. + if (S.getLangOpts().CPlusPlus1y && + FunDecl->getResultType()->isUndeducedType() && + S.DeduceReturnType(FunDecl, SourceExpr->getLocStart(), Complain)) + return false; + QualType ResultTy; if (Context.hasSameUnqualifiedType(TargetFunctionType, FunDecl->getType()) || @@ -9384,7 +9417,8 @@ Sema::ResolveSingleFunctionTemplateSpecialization(OverloadExpr *ovl, TemplateDeductionInfo Info(ovl->getNameLoc()); if (TemplateDeductionResult Result = DeduceTemplateArguments(FunctionTemplate, &ExplicitTemplateArgs, - Specialization, Info)) { + Specialization, Info, + /*InOverloadResolution=*/true)) { // FIXME: make a note of the failed deduction for diagnostics. (void)Result; continue; @@ -9406,6 +9440,11 @@ Sema::ResolveSingleFunctionTemplateSpecialization(OverloadExpr *ovl, if (FoundResult) *FoundResult = I.getPair(); } + if (Matched && getLangOpts().CPlusPlus1y && + Matched->getResultType()->isUndeducedType() && + DeduceReturnType(Matched, ovl->getExprLoc(), Complain)) + return 0; + return Matched; } @@ -9932,7 +9971,8 @@ static ExprResult FinishOverloadedCallExpr(Sema &SemaRef, Scope *S, Expr *Fn, case OR_Success: { FunctionDecl *FDecl = (*Best)->Function; SemaRef.CheckUnresolvedLookupAccess(ULE, (*Best)->FoundDecl); - SemaRef.DiagnoseUseOfDecl(FDecl, ULE->getNameLoc()); + if (SemaRef.DiagnoseUseOfDecl(FDecl, ULE->getNameLoc())) + return ExprError(); Fn = SemaRef.FixOverloadedFunctionReference(Fn, (*Best)->FoundDecl, FDecl); return SemaRef.BuildResolvedCallExpr(Fn, FDecl, LParenLoc, Args, NumArgs, RParenLoc, ExecConfig); @@ -10065,6 +10105,8 @@ Sema::CreateOverloadedUnaryOp(SourceLocation OpLoc, unsigned OpcIn, NumArgs = 2; } + ArrayRef ArgsArray(Args, NumArgs); + if (Input->isTypeDependent()) { if (Fns.empty()) return Owned(new (Context) UnaryOperator(Input, @@ -10079,8 +10121,7 @@ Sema::CreateOverloadedUnaryOp(SourceLocation OpLoc, unsigned OpcIn, NestedNameSpecifierLoc(), OpNameInfo, /*ADL*/ true, IsOverloaded(Fns), Fns.begin(), Fns.end()); - return Owned(new (Context) CXXOperatorCallExpr(Context, Op, Fn, - llvm::makeArrayRef(Args, NumArgs), + return Owned(new (Context) CXXOperatorCallExpr(Context, Op, Fn, ArgsArray, Context.DependentTy, VK_RValue, OpLoc, false)); @@ -10090,20 +10131,18 @@ Sema::CreateOverloadedUnaryOp(SourceLocation OpLoc, unsigned OpcIn, OverloadCandidateSet CandidateSet(OpLoc); // Add the candidates from the given function set. - AddFunctionCandidates(Fns, llvm::makeArrayRef(Args, NumArgs), CandidateSet, - false); + AddFunctionCandidates(Fns, ArgsArray, CandidateSet, false); // Add operator candidates that are member functions. - AddMemberOperatorCandidates(Op, OpLoc, &Args[0], NumArgs, CandidateSet); + AddMemberOperatorCandidates(Op, OpLoc, ArgsArray, CandidateSet); // Add candidates from ADL. - AddArgumentDependentLookupCandidates(OpName, /*Operator*/ true, - OpLoc, llvm::makeArrayRef(Args, NumArgs), - /*ExplicitTemplateArgs*/ 0, + AddArgumentDependentLookupCandidates(OpName, /*Operator*/ true, OpLoc, + ArgsArray, /*ExplicitTemplateArgs*/ 0, CandidateSet); // Add builtin operator candidates. - AddBuiltinOperatorCandidates(Op, OpLoc, &Args[0], NumArgs, CandidateSet); + AddBuiltinOperatorCandidates(Op, OpLoc, ArgsArray, CandidateSet); bool HadMultipleCandidates = (CandidateSet.size() > 1); @@ -10154,8 +10193,7 @@ Sema::CreateOverloadedUnaryOp(SourceLocation OpLoc, unsigned OpcIn, Args[0] = Input; CallExpr *TheCall = - new (Context) CXXOperatorCallExpr(Context, Op, FnExpr.take(), - llvm::makeArrayRef(Args, NumArgs), + new (Context) CXXOperatorCallExpr(Context, Op, FnExpr.take(), ArgsArray, ResultTy, VK, OpLoc, false); if (CheckCallReturnType(FnDecl->getResultType(), OpLoc, TheCall, @@ -10181,8 +10219,7 @@ Sema::CreateOverloadedUnaryOp(SourceLocation OpLoc, unsigned OpcIn, // This is an erroneous use of an operator which can be overloaded by // a non-member function. Check for non-member operators which were // defined too late to be candidates. - if (DiagnoseTwoPhaseOperatorLookup(*this, Op, OpLoc, - llvm::makeArrayRef(Args, NumArgs))) + if (DiagnoseTwoPhaseOperatorLookup(*this, Op, OpLoc, ArgsArray)) // FIXME: Recover by calling the found function. return ExprError(); @@ -10195,8 +10232,7 @@ Sema::CreateOverloadedUnaryOp(SourceLocation OpLoc, unsigned OpcIn, << UnaryOperator::getOpcodeStr(Opc) << Input->getType() << Input->getSourceRange(); - CandidateSet.NoteCandidates(*this, OCD_ViableCandidates, - llvm::makeArrayRef(Args, NumArgs), + CandidateSet.NoteCandidates(*this, OCD_ViableCandidates, ArgsArray, UnaryOperator::getOpcodeStr(Opc), OpLoc); return ExprError(); @@ -10206,8 +10242,7 @@ Sema::CreateOverloadedUnaryOp(SourceLocation OpLoc, unsigned OpcIn, << UnaryOperator::getOpcodeStr(Opc) << getDeletedOrUnavailableSuffix(Best->Function) << Input->getSourceRange(); - CandidateSet.NoteCandidates(*this, OCD_AllCandidates, - llvm::makeArrayRef(Args, NumArgs), + CandidateSet.NoteCandidates(*this, OCD_AllCandidates, ArgsArray, UnaryOperator::getOpcodeStr(Opc), OpLoc); return ExprError(); } @@ -10315,7 +10350,7 @@ Sema::CreateOverloadedBinOp(SourceLocation OpLoc, AddFunctionCandidates(Fns, Args, CandidateSet, false); // Add operator candidates that are member functions. - AddMemberOperatorCandidates(Op, OpLoc, Args, 2, CandidateSet); + AddMemberOperatorCandidates(Op, OpLoc, Args, CandidateSet); // Add candidates from ADL. AddArgumentDependentLookupCandidates(OpName, /*Operator*/ true, @@ -10324,7 +10359,7 @@ Sema::CreateOverloadedBinOp(SourceLocation OpLoc, CandidateSet); // Add builtin operator candidates. - AddBuiltinOperatorCandidates(Op, OpLoc, Args, 2, CandidateSet); + AddBuiltinOperatorCandidates(Op, OpLoc, Args, CandidateSet); bool HadMultipleCandidates = (CandidateSet.size() > 1); @@ -10545,10 +10580,10 @@ Sema::CreateOverloadedArraySubscriptExpr(SourceLocation LLoc, // Subscript can only be overloaded as a member function. // Add operator candidates that are member functions. - AddMemberOperatorCandidates(OO_Subscript, LLoc, Args, 2, CandidateSet); + AddMemberOperatorCandidates(OO_Subscript, LLoc, Args, CandidateSet); // Add builtin operator candidates. - AddBuiltinOperatorCandidates(OO_Subscript, LLoc, Args, 2, CandidateSet); + AddBuiltinOperatorCandidates(OO_Subscript, LLoc, Args, CandidateSet); bool HadMultipleCandidates = (CandidateSet.size() > 1); @@ -10815,7 +10850,8 @@ Sema::BuildCallToMemberFunction(Scope *S, Expr *MemExprE, Method = cast(Best->Function); FoundDecl = Best->FoundDecl; CheckUnresolvedMemberAccess(UnresExpr, Best->FoundDecl); - DiagnoseUseOfDecl(Best->FoundDecl, UnresExpr->getNameLoc()); + if (DiagnoseUseOfDecl(Best->FoundDecl, UnresExpr->getNameLoc())) + return ExprError(); break; case OR_No_Viable_Function: @@ -10956,7 +10992,8 @@ Sema::BuildCallToObjectOfClassType(Scope *S, Expr *Obj, for (LookupResult::iterator Oper = R.begin(), OperEnd = R.end(); Oper != OperEnd; ++Oper) { AddMethodCandidate(Oper.getPair(), Object.get()->getType(), - Object.get()->Classify(Context), Args, NumArgs, CandidateSet, + Object.get()->Classify(Context), + llvm::makeArrayRef(Args, NumArgs), CandidateSet, /*SuppressUserConversions=*/ false); } @@ -11066,7 +11103,8 @@ Sema::BuildCallToObjectOfClassType(Scope *S, Expr *Obj, Best->Conversions[0].UserDefined.ConversionFunction); CheckMemberOperatorAccess(LParenLoc, Object.get(), 0, Best->FoundDecl); - DiagnoseUseOfDecl(Best->FoundDecl, LParenLoc); + if (DiagnoseUseOfDecl(Best->FoundDecl, LParenLoc)) + return ExprError(); // We selected one of the surrogate functions that converts the // object parameter to a function pointer. Perform the conversion @@ -11248,7 +11286,7 @@ Sema::BuildOverloadedArrowExpr(Scope *S, Expr *Base, SourceLocation OpLoc) { for (LookupResult::iterator Oper = R.begin(), OperEnd = R.end(); Oper != OperEnd; ++Oper) { AddMethodCandidate(Oper.getPair(), Base->getType(), Base->Classify(Context), - 0, 0, CandidateSet, /*SuppressUserConversions=*/false); + None, CandidateSet, /*SuppressUserConversions=*/false); } bool HadMultipleCandidates = (CandidateSet.size() > 1); @@ -11363,7 +11401,7 @@ ExprResult Sema::BuildLiteralOperatorCall(LookupResult &R, // Check the argument types. This should almost always be a no-op, except // that array-to-pointer decay is applied to string literals. Expr *ConvArgs[2]; - for (unsigned ArgIdx = 0; ArgIdx != Args.size(); ++ArgIdx) { + for (unsigned ArgIdx = 0, N = Args.size(); ArgIdx != N; ++ArgIdx) { ExprResult InputInit = PerformCopyInitialization( InitializedEntity::InitializeParameter(Context, FD->getParamDecl(ArgIdx)), SourceLocation(), Args[ArgIdx]); @@ -11420,7 +11458,7 @@ Sema::BuildForRangeBeginEndCall(Scope *S, SourceLocation Loc, << RangeLoc << BEF << Range->getType(); return FRS_DiagnosticIssued; } - *CallExpr = ActOnCallExpr(S, MemberRef.get(), Loc, MultiExprArg(), Loc, 0); + *CallExpr = ActOnCallExpr(S, MemberRef.get(), Loc, None, Loc, 0); if (CallExpr->isInvalid()) { *CallExpr = ExprError(); Diag(Range->getLocStart(), diag::note_in_for_range) diff --git a/lib/Sema/SemaPseudoObject.cpp b/lib/Sema/SemaPseudoObject.cpp index b135507b1ac..054d557e92d 100644 --- a/lib/Sema/SemaPseudoObject.cpp +++ b/lib/Sema/SemaPseudoObject.cpp @@ -153,6 +153,23 @@ namespace { refExpr->getRBracket()); } }; + + struct MSPropertyRefRebuilder : Rebuilder { + Expr *NewBase; + MSPropertyRefRebuilder(Sema &S, Expr *newBase) + : Rebuilder(S), NewBase(newBase) {} + + typedef MSPropertyRefExpr specific_type; + Expr *rebuildSpecific(MSPropertyRefExpr *refExpr) { + assert(refExpr->getBaseExpr()); + + return new (S.Context) + MSPropertyRefExpr(NewBase, refExpr->getPropertyDecl(), + refExpr->isArrow(), refExpr->getType(), + refExpr->getValueKind(), refExpr->getQualifierLoc(), + refExpr->getMemberLoc()); + } + }; class PseudoOpBuilder { public: @@ -284,6 +301,18 @@ namespace { ExprResult buildSet(Expr *op, SourceLocation, bool); }; + class MSPropertyOpBuilder : public PseudoOpBuilder { + MSPropertyRefExpr *RefExpr; + + public: + MSPropertyOpBuilder(Sema &S, MSPropertyRefExpr *refExpr) : + PseudoOpBuilder(S, refExpr->getSourceRange().getBegin()), + RefExpr(refExpr) {} + + Expr *rebuildAndCaptureObject(Expr *); + ExprResult buildGet(); + ExprResult buildSet(Expr *op, SourceLocation, bool); + }; } /// Capture the given expression in an OpaqueValueExpr. @@ -412,7 +441,8 @@ PseudoOpBuilder::buildIncDecOperation(Scope *Sc, SourceLocation opcLoc, QualType resultType = result.get()->getType(); // That's the postfix result. - if (UnaryOperator::isPostfix(opcode) && CanCaptureValueOfType(resultType)) { + if (UnaryOperator::isPostfix(opcode) && + (result.get()->isTypeDependent() || CanCaptureValueOfType(resultType))) { result = capture(result.take()); setResultToLastSemantic(); } @@ -633,12 +663,11 @@ ExprResult ObjCPropertyOpBuilder::buildGet() { assert(InstanceReceiver || RefExpr->isSuperReceiver()); msg = S.BuildInstanceMessageImplicit(InstanceReceiver, receiverType, GenericLoc, Getter->getSelector(), - Getter, MultiExprArg()); + Getter, None); } else { msg = S.BuildClassMessageImplicit(receiverType, RefExpr->isSuperReceiver(), - GenericLoc, - Getter->getSelector(), Getter, - MultiExprArg()); + GenericLoc, Getter->getSelector(), + Getter, None); } return msg; } @@ -1088,8 +1117,7 @@ bool ObjCSubscriptOpBuilder::findAtIndexGetter() { /*TInfo=*/0, SC_None, 0); - AtIndexGetter->setMethodParams(S.Context, Argument, - ArrayRef()); + AtIndexGetter->setMethodParams(S.Context, Argument, None); } if (!AtIndexGetter) { @@ -1213,7 +1241,7 @@ bool ObjCSubscriptOpBuilder::findAtIndexSetter() { SC_None, 0); Params.push_back(key); - AtIndexSetter->setMethodParams(S.Context, Params, ArrayRef()); + AtIndexSetter->setMethodParams(S.Context, Params, None); } if (!AtIndexSetter) { @@ -1323,6 +1351,77 @@ ExprResult ObjCSubscriptOpBuilder::buildSet(Expr *op, SourceLocation opcLoc, return msg; } +//===----------------------------------------------------------------------===// +// MSVC __declspec(property) references +//===----------------------------------------------------------------------===// + +Expr *MSPropertyOpBuilder::rebuildAndCaptureObject(Expr *syntacticBase) { + Expr *NewBase = capture(RefExpr->getBaseExpr()); + + syntacticBase = + MSPropertyRefRebuilder(S, NewBase).rebuild(syntacticBase); + + return syntacticBase; +} + +ExprResult MSPropertyOpBuilder::buildGet() { + if (!RefExpr->getPropertyDecl()->hasGetter()) { + S.Diag(RefExpr->getMemberLoc(), diag::err_no_getter_for_property) + << RefExpr->getPropertyDecl()->getName(); + return ExprError(); + } + + UnqualifiedId GetterName; + IdentifierInfo *II = RefExpr->getPropertyDecl()->getGetterId(); + GetterName.setIdentifier(II, RefExpr->getMemberLoc()); + CXXScopeSpec SS; + SS.Adopt(RefExpr->getQualifierLoc()); + ExprResult GetterExpr = S.ActOnMemberAccessExpr( + S.getCurScope(), RefExpr->getBaseExpr(), SourceLocation(), + RefExpr->isArrow() ? tok::arrow : tok::period, SS, SourceLocation(), + GetterName, 0, true); + if (GetterExpr.isInvalid()) { + S.Diag(RefExpr->getMemberLoc(), diag::error_cannot_find_suitable_getter) + << RefExpr->getPropertyDecl()->getName(); + return ExprError(); + } + + MultiExprArg ArgExprs; + return S.ActOnCallExpr(S.getCurScope(), GetterExpr.take(), + RefExpr->getSourceRange().getBegin(), ArgExprs, + RefExpr->getSourceRange().getEnd()); +} + +ExprResult MSPropertyOpBuilder::buildSet(Expr *op, SourceLocation sl, + bool captureSetValueAsResult) { + if (!RefExpr->getPropertyDecl()->hasSetter()) { + S.Diag(RefExpr->getMemberLoc(), diag::err_no_setter_for_property) + << RefExpr->getPropertyDecl()->getName(); + return ExprError(); + } + + UnqualifiedId SetterName; + IdentifierInfo *II = RefExpr->getPropertyDecl()->getSetterId(); + SetterName.setIdentifier(II, RefExpr->getMemberLoc()); + CXXScopeSpec SS; + SS.Adopt(RefExpr->getQualifierLoc()); + ExprResult SetterExpr = S.ActOnMemberAccessExpr( + S.getCurScope(), RefExpr->getBaseExpr(), SourceLocation(), + RefExpr->isArrow() ? tok::arrow : tok::period, SS, SourceLocation(), + SetterName, 0, true); + if (SetterExpr.isInvalid()) { + S.Diag(RefExpr->getMemberLoc(), diag::error_cannot_find_suitable_setter) + << RefExpr->getPropertyDecl()->getName(); + return ExprError(); + } + + SmallVector ArgExprs; + ArgExprs.push_back(op); + return S.ActOnCallExpr(S.getCurScope(), SetterExpr.take(), + RefExpr->getSourceRange().getBegin(), ArgExprs, + op->getSourceRange().getEnd()); +} + //===----------------------------------------------------------------------===// // General Sema routines. //===----------------------------------------------------------------------===// @@ -1338,6 +1437,10 @@ ExprResult Sema::checkPseudoObjectRValue(Expr *E) { = dyn_cast(opaqueRef)) { ObjCSubscriptOpBuilder builder(*this, refExpr); return builder.buildRValueOperation(E); + } else if (MSPropertyRefExpr *refExpr + = dyn_cast(opaqueRef)) { + MSPropertyOpBuilder builder(*this, refExpr); + return builder.buildRValueOperation(E); } else { llvm_unreachable("unknown pseudo-object kind!"); } @@ -1360,6 +1463,10 @@ ExprResult Sema::checkPseudoObjectIncDec(Scope *Sc, SourceLocation opcLoc, } else if (isa(opaqueRef)) { Diag(opcLoc, diag::err_illegal_container_subscripting_op); return ExprError(); + } else if (MSPropertyRefExpr *refExpr + = dyn_cast(opaqueRef)) { + MSPropertyOpBuilder builder(*this, refExpr); + return builder.buildIncDecOperation(Sc, opcLoc, opcode, op); } else { llvm_unreachable("unknown pseudo-object kind!"); } @@ -1389,6 +1496,10 @@ ExprResult Sema::checkPseudoObjectAssignment(Scope *S, SourceLocation opcLoc, = dyn_cast(opaqueRef)) { ObjCSubscriptOpBuilder builder(*this, refExpr); return builder.buildAssignmentOperation(S, opcLoc, opcode, LHS, RHS); + } else if (MSPropertyRefExpr *refExpr + = dyn_cast(opaqueRef)) { + MSPropertyOpBuilder builder(*this, refExpr); + return builder.buildAssignmentOperation(S, opcLoc, opcode, LHS, RHS); } else { llvm_unreachable("unknown pseudo-object kind!"); } @@ -1414,6 +1525,10 @@ static Expr *stripOpaqueValuesFromPseudoObjectRef(Sema &S, Expr *E) { OpaqueValueExpr *keyOVE = cast(refExpr->getKeyExpr()); return ObjCSubscriptRefRebuilder(S, baseOVE->getSourceExpr(), keyOVE->getSourceExpr()).rebuild(E); + } else if (MSPropertyRefExpr *refExpr + = dyn_cast(opaqueRef)) { + OpaqueValueExpr *baseOVE = cast(refExpr->getBaseExpr()); + return MSPropertyRefRebuilder(S, baseOVE->getSourceExpr()).rebuild(E); } else { llvm_unreachable("unknown pseudo-object kind!"); } diff --git a/lib/Sema/SemaStmt.cpp b/lib/Sema/SemaStmt.cpp index ff1db821b65..248665ac86c 100644 --- a/lib/Sema/SemaStmt.cpp +++ b/lib/Sema/SemaStmt.cpp @@ -22,7 +22,6 @@ #include "clang/AST/StmtCXX.h" #include "clang/AST/StmtObjC.h" #include "clang/AST/TypeLoc.h" -#include "clang/Basic/TargetInfo.h" #include "clang/Lex/Preprocessor.h" #include "clang/Sema/Initialization.h" #include "clang/Sema/Lookup.h" @@ -77,9 +76,22 @@ StmtResult Sema::ActOnDeclStmt(DeclGroupPtrTy dg, SourceLocation StartLoc, void Sema::ActOnForEachDeclStmt(DeclGroupPtrTy dg) { DeclGroupRef DG = dg.getAsVal(); - // If we have an invalid decl, just return. - if (DG.isNull() || !DG.isSingleDecl()) return; - VarDecl *var = cast(DG.getSingleDecl()); + // If we don't have a declaration, or we have an invalid declaration, + // just return. + if (DG.isNull() || !DG.isSingleDecl()) + return; + + Decl *decl = DG.getSingleDecl(); + if (!decl || decl->isInvalidDecl()) + return; + + // Only variable declarations are permitted. + VarDecl *var = dyn_cast(decl); + if (!var) { + Diag(decl->getLocation(), diag::err_non_variable_decl_in_for); + decl->setInvalidDecl(); + return; + } // suppress any potential 'unused variable' warning. var->setUsed(); @@ -355,7 +367,7 @@ Sema::ActOnCaseStmt(SourceLocation CaseLoc, Expr *LHSVal, // Recover from an error by just forgetting about it. } } - + LHSVal = ActOnFinishFullExpr(LHSVal, LHSVal->getExprLoc(), false, getLangOpts().CPlusPlus11).take(); if (RHSVal) @@ -1426,9 +1438,10 @@ Sema::ActOnForStmt(SourceLocation ForLoc, SourceLocation LParenLoc, VarDecl *VD = dyn_cast(*DI); if (VD && VD->isLocalVarDecl() && !VD->hasLocalStorage()) VD = 0; - if (VD == 0) - Diag((*DI)->getLocation(), diag::err_non_variable_decl_in_for); - // FIXME: mark decl erroneous! + if (VD == 0) { + Diag((*DI)->getLocation(), diag::err_non_local_variable_decl_in_for); + (*DI)->setInvalidDecl(); + } } } } @@ -1562,14 +1575,41 @@ Sema::ActOnObjCForCollectionStmt(SourceLocation ForLoc, return StmtError(Diag((*DS->decl_begin())->getLocation(), diag::err_toomany_element_decls)); - VarDecl *D = cast(DS->getSingleDecl()); + VarDecl *D = dyn_cast(DS->getSingleDecl()); + if (!D || D->isInvalidDecl()) + return StmtError(); + FirstType = D->getType(); // C99 6.8.5p3: The declaration part of a 'for' statement shall only // declare identifiers for objects having storage class 'auto' or // 'register'. if (!D->hasLocalStorage()) return StmtError(Diag(D->getLocation(), - diag::err_non_variable_decl_in_for)); + diag::err_non_local_variable_decl_in_for)); + + // If the type contained 'auto', deduce the 'auto' to 'id'. + if (FirstType->getContainedAutoType()) { + OpaqueValueExpr OpaqueId(D->getLocation(), Context.getObjCIdType(), + VK_RValue); + Expr *DeducedInit = &OpaqueId; + if (DeduceAutoType(D->getTypeSourceInfo(), DeducedInit, FirstType) == + DAR_Failed) + DiagnoseAutoDeductionFailure(D, DeducedInit); + if (FirstType.isNull()) { + D->setInvalidDecl(); + return StmtError(); + } + + D->setType(FirstType); + + if (ActiveTemplateInstantiations.empty()) { + SourceLocation Loc = + D->getTypeSourceInfo()->getTypeLoc().getBeginLoc(); + Diag(Loc, diag::warn_auto_var_is_id) + << D->getDeclName(); + } + } + } else { Expr *FirstE = cast(First); if (!FirstE->isTypeDependent() && !FirstE->isLValue()) @@ -1601,20 +1641,19 @@ Sema::ActOnObjCForCollectionStmt(SourceLocation ForLoc, /// Finish building a variable declaration for a for-range statement. /// \return true if an error occurs. static bool FinishForRangeVarDecl(Sema &SemaRef, VarDecl *Decl, Expr *Init, - SourceLocation Loc, int diag) { + SourceLocation Loc, int DiagID) { // Deduce the type for the iterator variable now rather than leaving it to // AddInitializerToDecl, so we can produce a more suitable diagnostic. - TypeSourceInfo *InitTSI = 0; + QualType InitType; if ((!isa(Init) && Init->getType()->isVoidType()) || - SemaRef.DeduceAutoType(Decl->getTypeSourceInfo(), Init, InitTSI) == + SemaRef.DeduceAutoType(Decl->getTypeSourceInfo(), Init, InitType) == Sema::DAR_Failed) - SemaRef.Diag(Loc, diag) << Init->getType(); - if (!InitTSI) { + SemaRef.Diag(Loc, DiagID) << Init->getType(); + if (InitType.isNull()) { Decl->setInvalidDecl(); return true; } - Decl->setTypeSourceInfo(InitTSI); - Decl->setType(InitTSI->getType()); + Decl->setType(InitType); // In ARC, infer lifetime. // FIXME: ARC may want to turn this into 'const __unsafe_unretained' if @@ -1879,7 +1918,15 @@ Sema::BuildCXXForRangeStmt(SourceLocation ForLoc, SourceLocation ColonLoc, StmtResult BeginEndDecl = BeginEnd; ExprResult NotEqExpr = Cond, IncrExpr = Inc; - if (!BeginEndDecl.get() && !RangeVarType->isDependentType()) { + if (RangeVarType->isDependentType()) { + // The range is implicitly used as a placeholder when it is dependent. + RangeVar->setUsed(); + + // Deduce any 'auto's in the loop variable as 'DependentTy'. We'll fill + // them in properly when we instantiate the loop. + if (!LoopVar->isInvalidDecl() && Kind != BFRK_Check) + LoopVar->setType(SubstAutoType(LoopVar->getType(), Context.DependentTy)); + } else if (!BeginEndDecl.get()) { SourceLocation RangeLoc = RangeVar->getLocation(); const QualType RangeVarNonRefType = RangeVarType.getNonReferenceType(); @@ -1934,6 +1981,8 @@ Sema::BuildCXXForRangeStmt(SourceLocation ForLoc, SourceLocation ColonLoc, RangeLoc)); else if (const VariableArrayType *VAT = dyn_cast(UnqAT)) + // FIXME: Need to build an OpaqueValueExpr for this rather than + // recomputing it! BoundExpr = VAT->getSizeExpr(); else { // Can't be a DependentSizedArrayType or an IncompleteArrayType since @@ -2064,9 +2113,6 @@ Sema::BuildCXXForRangeStmt(SourceLocation ForLoc, SourceLocation ColonLoc, if (LoopVar->isInvalidDecl()) NoteForRangeBeginEndFunction(*this, BeginExpr.get(), BEF_begin); } - } else { - // The range is implicitly used as a placeholder when it is dependent. - RangeVar->setUsed(); } // Don't bother to actually allocate the result if we're just trying to @@ -2263,7 +2309,7 @@ Sema::PerformMoveOrCopyInitialization(const InitializedEntity &Entity, InitializationKind Kind = InitializationKind::CreateCopy(Value->getLocStart(), Value->getLocStart()); - InitializationSequence Seq(*this, Entity, Kind, &InitExpr, 1); + InitializationSequence Seq(*this, Entity, Kind, InitExpr); // [...] If overload resolution fails, or if the type of the first // parameter of the selected constructor is not an rvalue reference @@ -2296,7 +2342,7 @@ Sema::PerformMoveOrCopyInitialization(const InitializedEntity &Entity, // Complete type-checking the initialization of the return type // using the constructor we found. - Res = Seq.Perform(*this, Entity, Kind, MultiExprArg(&Value, 1)); + Res = Seq.Perform(*this, Entity, Kind, Value); } } } @@ -2359,6 +2405,10 @@ Sema::ActOnCapScopeReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) { Diag(ReturnLoc, diag::err_noreturn_block_has_return_expr); return StmtError(); } + } else if (CapturedRegionScopeInfo *CurRegion = + dyn_cast(CurCap)) { + Diag(ReturnLoc, diag::err_return_in_captured_stmt) << CurRegion->getRegionName(); + return StmtError(); } else { LambdaScopeInfo *LSI = cast(CurCap); if (LSI->CallOperator->getType()->getAs()->getNoReturnAttr()){ @@ -2432,12 +2482,80 @@ Sema::ActOnCapScopeReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) { return Owned(Result); } +/// Deduce the return type for a function from a returned expression, per +/// C++1y [dcl.spec.auto]p6. +bool Sema::DeduceFunctionTypeFromReturnExpr(FunctionDecl *FD, + SourceLocation ReturnLoc, + Expr *&RetExpr, + AutoType *AT) { + TypeLoc OrigResultType = FD->getTypeSourceInfo()->getTypeLoc(). + IgnoreParens().castAs().getResultLoc(); + QualType Deduced; + + if (RetExpr) { + // If the deduction is for a return statement and the initializer is + // a braced-init-list, the program is ill-formed. + if (isa(RetExpr)) { + Diag(RetExpr->getExprLoc(), diag::err_auto_fn_return_init_list); + return true; + } + + // Otherwise, [...] deduce a value for U using the rules of template + // argument deduction. + DeduceAutoResult DAR = DeduceAutoType(OrigResultType, RetExpr, Deduced); + + if (DAR == DAR_Failed && !FD->isInvalidDecl()) + Diag(RetExpr->getExprLoc(), diag::err_auto_fn_deduction_failure) + << OrigResultType.getType() << RetExpr->getType(); + + if (DAR != DAR_Succeeded) + return true; + } else { + // In the case of a return with no operand, the initializer is considered + // to be void(). + // + // Deduction here can only succeed if the return type is exactly 'cv auto' + // or 'decltype(auto)', so just check for that case directly. + if (!OrigResultType.getType()->getAs()) { + Diag(ReturnLoc, diag::err_auto_fn_return_void_but_not_auto) + << OrigResultType.getType(); + return true; + } + // We always deduce U = void in this case. + Deduced = SubstAutoType(OrigResultType.getType(), Context.VoidTy); + if (Deduced.isNull()) + return true; + } + + // If a function with a declared return type that contains a placeholder type + // has multiple return statements, the return type is deduced for each return + // statement. [...] if the type deduced is not the same in each deduction, + // the program is ill-formed. + if (AT->isDeduced() && !FD->isInvalidDecl()) { + AutoType *NewAT = Deduced->getContainedAutoType(); + if (!Context.hasSameType(AT->getDeducedType(), NewAT->getDeducedType())) { + Diag(ReturnLoc, diag::err_auto_fn_different_deductions) + << (AT->isDecltypeAuto() ? 1 : 0) + << NewAT->getDeducedType() << AT->getDeducedType(); + return true; + } + } else if (!FD->isInvalidDecl()) { + // Update all declarations of the function to have the deduced return type. + Context.adjustDeducedFunctionResultType(FD, Deduced); + } + + return false; +} + StmtResult Sema::ActOnReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) { // Check for unexpanded parameter packs. if (RetValExp && DiagnoseUnexpandedParameterPack(RetValExp)) return StmtError(); + // FIXME: Unify this and C++1y auto function handling. In particular, we + // should allow 'return { 1, 2, 3 };' in a lambda to deduce + // 'std::initializer_list'. if (isa(getCurFunction())) return ActOnCapScopeReturnStmt(ReturnLoc, RetValExp); @@ -2460,6 +2578,23 @@ Sema::ActOnReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) { } else // If we don't have a function/method context, bail. return StmtError(); + // FIXME: Add a flag to the ScopeInfo to indicate whether we're performing + // deduction. + bool HasDependentReturnType = FnRetType->isDependentType(); + if (getLangOpts().CPlusPlus1y) { + if (AutoType *AT = FnRetType->getContainedAutoType()) { + FunctionDecl *FD = cast(CurContext); + if (CurContext->isDependentContext()) + HasDependentReturnType = true; + else if (DeduceFunctionTypeFromReturnExpr(FD, ReturnLoc, RetValExp, AT)) { + FD->setInvalidDecl(); + return StmtError(); + } else { + FnRetType = FD->getResultType(); + } + } + } + ReturnStmt *Result = 0; if (FnRetType->isVoidType()) { if (RetValExp) { @@ -2525,7 +2660,7 @@ Sema::ActOnReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) { } Result = new (Context) ReturnStmt(ReturnLoc, RetValExp, 0); - } else if (!RetValExp && !FnRetType->isDependentType()) { + } else if (!RetValExp && !HasDependentReturnType) { unsigned DiagID = diag::warn_return_missing_expr; // C90 6.6.6.4p4 // C99 6.8.6.4p1 (ext_ since GCC warns) if (getLangOpts().C99) DiagID = diag::ext_return_missing_expr; @@ -2536,9 +2671,9 @@ Sema::ActOnReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) { Diag(ReturnLoc, DiagID) << getCurMethodDecl()->getDeclName() << 1/*meth*/; Result = new (Context) ReturnStmt(ReturnLoc); } else { - assert(RetValExp || FnRetType->isDependentType()); + assert(RetValExp || HasDependentReturnType); const VarDecl *NRVOCandidate = 0; - if (!FnRetType->isDependentType() && !RetValExp->isTypeDependent()) { + if (!HasDependentReturnType && !RetValExp->isTypeDependent()) { // we have a non-void function with an expression, continue checking QualType RetType = (RelatedRetType.isNull() ? FnRetType : RelatedRetType); @@ -2870,3 +3005,124 @@ StmtResult Sema::ActOnMSDependentExistsStmt(SourceLocation KeywordLoc, GetNameFromUnqualifiedId(Name), Nested); } + +RecordDecl* +Sema::CreateCapturedStmtRecordDecl(CapturedDecl *&CD, SourceLocation Loc, + unsigned NumParams) { + DeclContext *DC = CurContext; + while (!(DC->isFunctionOrMethod() || DC->isRecord() || DC->isFileContext())) + DC = DC->getParent(); + + RecordDecl *RD = 0; + if (getLangOpts().CPlusPlus) + RD = CXXRecordDecl::Create(Context, TTK_Struct, DC, Loc, Loc, /*Id=*/0); + else + RD = RecordDecl::Create(Context, TTK_Struct, DC, Loc, Loc, /*Id=*/0); + + DC->addDecl(RD); + RD->setImplicit(); + RD->startDefinition(); + + CD = CapturedDecl::Create(Context, CurContext, NumParams); + DC->addDecl(CD); + + // Build the context parameter + assert(NumParams > 0 && "CapturedStmt requires context parameter"); + DC = CapturedDecl::castToDeclContext(CD); + IdentifierInfo *VarName = &Context.Idents.get("__context"); + QualType ParamType = Context.getPointerType(Context.getTagDeclType(RD)); + ImplicitParamDecl *Param + = ImplicitParamDecl::Create(Context, DC, Loc, VarName, ParamType); + DC->addDecl(Param); + + CD->setContextParam(Param); + + return RD; +} + +static void buildCapturedStmtCaptureList( + SmallVectorImpl &Captures, + SmallVectorImpl &CaptureInits, + ArrayRef Candidates) { + + typedef ArrayRef::const_iterator CaptureIter; + for (CaptureIter Cap = Candidates.begin(); Cap != Candidates.end(); ++Cap) { + + if (Cap->isThisCapture()) { + Captures.push_back(CapturedStmt::Capture(Cap->getLocation(), + CapturedStmt::VCK_This)); + CaptureInits.push_back(Cap->getCopyExpr()); + continue; + } + + assert(Cap->isReferenceCapture() && + "non-reference capture not yet implemented"); + + Captures.push_back(CapturedStmt::Capture(Cap->getLocation(), + CapturedStmt::VCK_ByRef, + Cap->getVariable())); + CaptureInits.push_back(Cap->getCopyExpr()); + } +} + +void Sema::ActOnCapturedRegionStart(SourceLocation Loc, Scope *CurScope, + CapturedRegionKind Kind, + unsigned NumParams) { + CapturedDecl *CD = 0; + RecordDecl *RD = CreateCapturedStmtRecordDecl(CD, Loc, NumParams); + + // Enter the capturing scope for this captured region. + PushCapturedRegionScope(CurScope, CD, RD, Kind); + + if (CurScope) + PushDeclContext(CurScope, CD); + else + CurContext = CD; + + PushExpressionEvaluationContext(PotentiallyEvaluated); +} + +void Sema::ActOnCapturedRegionError() { + DiscardCleanupsInEvaluationContext(); + PopExpressionEvaluationContext(); + + CapturedRegionScopeInfo *RSI = getCurCapturedRegion(); + RecordDecl *Record = RSI->TheRecordDecl; + Record->setInvalidDecl(); + + SmallVector Fields; + for (RecordDecl::field_iterator I = Record->field_begin(), + E = Record->field_end(); I != E; ++I) + Fields.push_back(*I); + ActOnFields(/*Scope=*/0, Record->getLocation(), Record, Fields, + SourceLocation(), SourceLocation(), /*AttributeList=*/0); + + PopDeclContext(); + PopFunctionScopeInfo(); +} + +StmtResult Sema::ActOnCapturedRegionEnd(Stmt *S) { + CapturedRegionScopeInfo *RSI = getCurCapturedRegion(); + + SmallVector Captures; + SmallVector CaptureInits; + buildCapturedStmtCaptureList(Captures, CaptureInits, RSI->Captures); + + CapturedDecl *CD = RSI->TheCapturedDecl; + RecordDecl *RD = RSI->TheRecordDecl; + + CapturedStmt *Res = CapturedStmt::Create(getASTContext(), S, + RSI->CapRegionKind, Captures, + CaptureInits, CD, RD); + + CD->setBody(Res->getCapturedStmt()); + RD->completeDefinition(); + + DiscardCleanupsInEvaluationContext(); + PopExpressionEvaluationContext(); + + PopDeclContext(); + PopFunctionScopeInfo(); + + return Owned(Res); +} diff --git a/lib/Sema/SemaStmtAsm.cpp b/lib/Sema/SemaStmtAsm.cpp index da33bdf717e..fce95bebd1a 100644 --- a/lib/Sema/SemaStmtAsm.cpp +++ b/lib/Sema/SemaStmtAsm.cpp @@ -22,18 +22,6 @@ #include "clang/Sema/ScopeInfo.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/BitVector.h" -#include "llvm/ADT/SmallString.h" -#include "llvm/MC/MCAsmInfo.h" -#include "llvm/MC/MCContext.h" -#include "llvm/MC/MCObjectFileInfo.h" -#include "llvm/MC/MCParser/MCAsmParser.h" -#include "llvm/MC/MCRegisterInfo.h" -#include "llvm/MC/MCStreamer.h" -#include "llvm/MC/MCSubtargetInfo.h" -#include "llvm/MC/MCTargetAsmParser.h" -#include "llvm/Support/SourceMgr.h" -#include "llvm/Support/TargetRegistry.h" -#include "llvm/Support/TargetSelect.h" using namespace clang; using namespace sema; @@ -381,158 +369,60 @@ StmtResult Sema::ActOnGCCAsmStmt(SourceLocation AsmLoc, bool IsSimple, return Owned(NS); } -// getSpelling - Get the spelling of the AsmTok token. -static StringRef getSpelling(Sema &SemaRef, Token AsmTok) { - StringRef Asm; - SmallString<512> TokenBuf; - TokenBuf.resize(512); - bool StringInvalid = false; - Asm = SemaRef.PP.getSpelling(AsmTok, TokenBuf, &StringInvalid); - assert (!StringInvalid && "Expected valid string!"); - return Asm; -} +ExprResult Sema::LookupInlineAsmIdentifier(CXXScopeSpec &SS, + SourceLocation TemplateKWLoc, + UnqualifiedId &Id, + InlineAsmIdentifierInfo &Info, + bool IsUnevaluatedContext) { + Info.clear(); -// Build the inline assembly string. Returns true on error. -static bool buildMSAsmString(Sema &SemaRef, - SourceLocation AsmLoc, - ArrayRef AsmToks, - SmallVectorImpl &TokOffsets, - std::string &AsmString) { - assert (!AsmToks.empty() && "Didn't expect an empty AsmToks!"); - - SmallString<512> Asm; - for (unsigned i = 0, e = AsmToks.size(); i < e; ++i) { - bool isNewAsm = ((i == 0) || - AsmToks[i].isAtStartOfLine() || - AsmToks[i].is(tok::kw_asm)); - if (isNewAsm) { - if (i != 0) - Asm += "\n\t"; - - if (AsmToks[i].is(tok::kw_asm)) { - i++; // Skip __asm - if (i == e) { - SemaRef.Diag(AsmLoc, diag::err_asm_empty); - return true; - } + if (IsUnevaluatedContext) + PushExpressionEvaluationContext(UnevaluatedAbstract, + ReuseLambdaContextDecl); - } - } + ExprResult Result = ActOnIdExpression(getCurScope(), SS, TemplateKWLoc, Id, + /*trailing lparen*/ false, + /*is & operand*/ false); - if (i && AsmToks[i].hasLeadingSpace() && !isNewAsm) - Asm += ' '; + if (IsUnevaluatedContext) + PopExpressionEvaluationContext(); - StringRef Spelling = getSpelling(SemaRef, AsmToks[i]); - Asm += Spelling; - TokOffsets.push_back(Asm.size()); - } - AsmString = Asm.str(); - return false; -} + if (!Result.isUsable()) return Result; -namespace { - -class MCAsmParserSemaCallbackImpl : public llvm::MCAsmParserSemaCallback { - Sema &SemaRef; - SourceLocation AsmLoc; - ArrayRef AsmToks; - ArrayRef TokOffsets; - -public: - MCAsmParserSemaCallbackImpl(Sema &Ref, SourceLocation Loc, - ArrayRef Toks, - ArrayRef Offsets) - : SemaRef(Ref), AsmLoc(Loc), AsmToks(Toks), TokOffsets(Offsets) { } - ~MCAsmParserSemaCallbackImpl() {} - - void *LookupInlineAsmIdentifier(StringRef Name, void *SrcLoc, - unsigned &Length, unsigned &Size, - unsigned &Type, bool &IsVarDecl){ - SourceLocation Loc = SourceLocation::getFromPtrEncoding(SrcLoc); - - NamedDecl *OpDecl = SemaRef.LookupInlineAsmIdentifier(Name, Loc, Length, - Size, Type, - IsVarDecl); - return static_cast(OpDecl); - } + Result = CheckPlaceholderExpr(Result.take()); + if (!Result.isUsable()) return Result; - bool LookupInlineAsmField(StringRef Base, StringRef Member, - unsigned &Offset) { - return SemaRef.LookupInlineAsmField(Base, Member, Offset, AsmLoc); - } + QualType T = Result.get()->getType(); - static void MSAsmDiagHandlerCallback(const llvm::SMDiagnostic &D, - void *Context) { - ((MCAsmParserSemaCallbackImpl*)Context)->MSAsmDiagHandler(D); - } - void MSAsmDiagHandler(const llvm::SMDiagnostic &D) { - // Compute an offset into the inline asm buffer. - // FIXME: This isn't right if .macro is involved (but hopefully, no - // real-world code does that). - const llvm::SourceMgr &LSM = *D.getSourceMgr(); - const llvm::MemoryBuffer *LBuf = - LSM.getMemoryBuffer(LSM.FindBufferContainingLoc(D.getLoc())); - unsigned Offset = D.getLoc().getPointer() - LBuf->getBufferStart(); - - // Figure out which token that offset points into. - const unsigned *OffsetPtr = - std::lower_bound(TokOffsets.begin(), TokOffsets.end(), Offset); - unsigned TokIndex = OffsetPtr - TokOffsets.begin(); - - // If we come up with an answer which seems sane, use it; otherwise, - // just point at the __asm keyword. - // FIXME: Assert the answer is sane once we handle .macro correctly. - SourceLocation Loc = AsmLoc; - if (TokIndex < AsmToks.size()) { - const Token *Tok = &AsmToks[TokIndex]; - Loc = Tok->getLocation(); - Loc = Loc.getLocWithOffset(Offset - (*OffsetPtr - Tok->getLength())); - } - SemaRef.Diag(Loc, diag::err_inline_ms_asm_parsing) << D.getMessage(); + // For now, reject dependent types. + if (T->isDependentType()) { + Diag(Id.getLocStart(), diag::err_asm_incomplete_type) << T; + return ExprError(); } -}; -} - -NamedDecl *Sema::LookupInlineAsmIdentifier(StringRef Name, SourceLocation Loc, - unsigned &Length, unsigned &Size, - unsigned &Type, bool &IsVarDecl) { - Length = 1; - Size = 0; - Type = 0; - IsVarDecl = false; - LookupResult Result(*this, &Context.Idents.get(Name), Loc, - Sema::LookupOrdinaryName); - - if (!LookupName(Result, getCurScope())) { - // If we don't find anything, return null; the AsmParser will assume - // it is a label of some sort. - return 0; + // Any sort of function type is fine. + if (T->isFunctionType()) { + return Result; } - if (!Result.isSingleResult()) { - // FIXME: Diagnose result. - return 0; + // Otherwise, it needs to be a complete type. + if (RequireCompleteExprType(Result.get(), diag::err_asm_incomplete_type)) { + return ExprError(); } - NamedDecl *FoundDecl = Result.getFoundDecl(); - if (isa(FoundDecl)) - return FoundDecl; - if (VarDecl *Var = dyn_cast(FoundDecl)) { - QualType Ty = Var->getType(); - Type = Size = Context.getTypeSizeInChars(Ty).getQuantity(); - if (Ty->isArrayType()) { - const ArrayType *ATy = Context.getAsArrayType(Ty); - Type = Context.getTypeSizeInChars(ATy->getElementType()).getQuantity(); - Length = Size / Type; - } - IsVarDecl = true; - return FoundDecl; + // Compute the type size (and array length if applicable?). + Info.Type = Info.Size = Context.getTypeSizeInChars(T).getQuantity(); + if (T->isArrayType()) { + const ArrayType *ATy = Context.getAsArrayType(T); + Info.Type = Context.getTypeSizeInChars(ATy->getElementType()).getQuantity(); + Info.Length = Info.Size / Info.Type; } - // FIXME: Handle other kinds of results? (FieldDecl, etc.) - // FIXME: Diagnose if we find something we can't handle, like a typedef. - return 0; + // We can work with the expression as long as it's not an r-value. + if (!Result.get()->isRValue()) + Info.IsVarDecl = true; + + return Result; } bool Sema::LookupInlineAsmField(StringRef Base, StringRef Member, @@ -579,124 +469,18 @@ bool Sema::LookupInlineAsmField(StringRef Base, StringRef Member, } StmtResult Sema::ActOnMSAsmStmt(SourceLocation AsmLoc, SourceLocation LBraceLoc, - ArrayRef AsmToks,SourceLocation EndLoc) { - SmallVector Names; - SmallVector ConstraintRefs; - SmallVector Exprs; - SmallVector ClobberRefs; - - llvm::Triple TheTriple = Context.getTargetInfo().getTriple(); - llvm::Triple::ArchType ArchTy = TheTriple.getArch(); - bool UnsupportedArch = ArchTy != llvm::Triple::x86 && - ArchTy != llvm::Triple::x86_64; - if (UnsupportedArch) - Diag(AsmLoc, diag::err_msasm_unsupported_arch) << TheTriple.getArchName(); - - // Empty asm statements don't need to instantiate the AsmParser, etc. - if (UnsupportedArch || AsmToks.empty()) { - StringRef EmptyAsmStr; - MSAsmStmt *NS = - new (Context) MSAsmStmt(Context, AsmLoc, LBraceLoc, /*IsSimple*/ true, - /*IsVolatile*/ true, AsmToks, /*NumOutputs*/ 0, - /*NumInputs*/ 0, Names, ConstraintRefs, Exprs, - EmptyAsmStr, ClobberRefs, EndLoc); - return Owned(NS); - } - - std::string AsmString; - SmallVector TokOffsets; - if (buildMSAsmString(*this, AsmLoc, AsmToks, TokOffsets, AsmString)) - return StmtError(); - - // Get the target specific parser. - std::string Error; - const std::string &TT = TheTriple.getTriple(); - const llvm::Target *TheTarget(llvm::TargetRegistry::lookupTarget(TT, Error)); - - OwningPtr MAI(TheTarget->createMCAsmInfo(TT)); - OwningPtr MRI(TheTarget->createMCRegInfo(TT)); - OwningPtr MOFI(new llvm::MCObjectFileInfo()); - OwningPtr - STI(TheTarget->createMCSubtargetInfo(TT, "", "")); - - llvm::SourceMgr SrcMgr; - llvm::MCContext Ctx(*MAI, *MRI, MOFI.get(), &SrcMgr); - llvm::MemoryBuffer *Buffer = - llvm::MemoryBuffer::getMemBuffer(AsmString, ""); - - // Tell SrcMgr about this buffer, which is what the parser will pick up. - SrcMgr.AddNewSourceBuffer(Buffer, llvm::SMLoc()); - - OwningPtr Str(createNullStreamer(Ctx)); - OwningPtr - Parser(createMCAsmParser(SrcMgr, Ctx, *Str.get(), *MAI)); - OwningPtr - TargetParser(TheTarget->createMCAsmParser(*STI, *Parser)); - - // Get the instruction descriptor. - const llvm::MCInstrInfo *MII = TheTarget->createMCInstrInfo(); - llvm::MCInstPrinter *IP = - TheTarget->createMCInstPrinter(1, *MAI, *MII, *MRI, *STI); - - // Change to the Intel dialect. - Parser->setAssemblerDialect(1); - Parser->setTargetParser(*TargetParser.get()); - Parser->setParsingInlineAsm(true); - TargetParser->setParsingInlineAsm(true); - - MCAsmParserSemaCallbackImpl MCAPSI(*this, AsmLoc, AsmToks, TokOffsets); - TargetParser->setSemaCallback(&MCAPSI); - SrcMgr.setDiagHandler(MCAsmParserSemaCallbackImpl::MSAsmDiagHandlerCallback, - &MCAPSI); - - unsigned NumOutputs; - unsigned NumInputs; - std::string AsmStringIR; - SmallVector, 4> OpDecls; - SmallVector Constraints; - SmallVector Clobbers; - if (Parser->parseMSInlineAsm(AsmLoc.getPtrEncoding(), AsmStringIR, - NumOutputs, NumInputs, OpDecls, Constraints, - Clobbers, MII, IP, MCAPSI)) - return StmtError(); - - // Build the vector of clobber StringRefs. - unsigned NumClobbers = Clobbers.size(); - ClobberRefs.resize(NumClobbers); - for (unsigned i = 0; i != NumClobbers; ++i) - ClobberRefs[i] = StringRef(Clobbers[i]); - - // Recast the void pointers and build the vector of constraint StringRefs. - unsigned NumExprs = NumOutputs + NumInputs; - Names.resize(NumExprs); - ConstraintRefs.resize(NumExprs); - Exprs.resize(NumExprs); - for (unsigned i = 0, e = NumExprs; i != e; ++i) { - NamedDecl *OpDecl = static_cast(OpDecls[i].first); - if (!OpDecl) - return StmtError(); - - DeclarationNameInfo NameInfo(OpDecl->getDeclName(), AsmLoc); - ExprResult OpExpr = BuildDeclarationNameExpr(CXXScopeSpec(), NameInfo, - OpDecl); - if (OpExpr.isInvalid()) - return StmtError(); - - // Need address of variable. - if (OpDecls[i].second) - OpExpr = BuildUnaryOp(getCurScope(), AsmLoc, clang::UO_AddrOf, - OpExpr.take()); - - Names[i] = OpDecl->getIdentifier(); - ConstraintRefs[i] = StringRef(Constraints[i]); - Exprs[i] = OpExpr.take(); - } - - bool IsSimple = NumExprs > 0; + ArrayRef AsmToks, + StringRef AsmString, + unsigned NumOutputs, unsigned NumInputs, + ArrayRef Constraints, + ArrayRef Clobbers, + ArrayRef Exprs, + SourceLocation EndLoc) { + bool IsSimple = (NumOutputs != 0 || NumInputs != 0); MSAsmStmt *NS = new (Context) MSAsmStmt(Context, AsmLoc, LBraceLoc, IsSimple, /*IsVolatile*/ true, AsmToks, NumOutputs, NumInputs, - Names, ConstraintRefs, Exprs, AsmStringIR, - ClobberRefs, EndLoc); + Constraints, Exprs, AsmString, + Clobbers, EndLoc); return Owned(NS); } diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index 990626189e1..b9695cc1e16 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -3842,8 +3842,7 @@ CheckTemplateArgumentAddressOfObjectOrFunction(Sema &S, } // A template argument must have static storage duration. - // FIXME: Ensure this works for thread_local as well as __thread. - if (Var->isThreadSpecified()) { + if (Var->getTLSKind()) { S.Diag(Arg->getLocStart(), diag::err_template_arg_thread_local) << Arg->getSourceRange(); S.Diag(Var->getLocation(), diag::note_template_arg_refers_here); @@ -6461,6 +6460,7 @@ Sema::ActOnExplicitInstantiation(Scope *S, // Set source locations for keywords. Specialization->setExternLoc(ExternLoc); Specialization->setTemplateKeywordLoc(TemplateLoc); + Specialization->setRBraceLoc(SourceLocation()); if (Attr) ProcessDeclAttributeList(S, Specialization, Attr); diff --git a/lib/Sema/SemaTemplateDeduction.cpp b/lib/Sema/SemaTemplateDeduction.cpp index f3bbe8a0f10..8efc7a02634 100644 --- a/lib/Sema/SemaTemplateDeduction.cpp +++ b/lib/Sema/SemaTemplateDeduction.cpp @@ -52,7 +52,11 @@ namespace clang { TDF_SkipNonDependent = 0x08, /// \brief Whether we are performing template argument deduction for /// parameters and arguments in a top-level template argument - TDF_TopLevelParameterTypeList = 0x10 + TDF_TopLevelParameterTypeList = 0x10, + /// \brief Within template argument deduction from overload resolution per + /// C++ [over.over] allow matching function types that are compatible in + /// terms of noreturn and default calling convention adjustments. + TDF_InOverloadResolution = 0x20 }; } @@ -867,6 +871,32 @@ static bool hasInconsistentOrSupersetQualifiersOf(QualType ParamType, == ParamQs.getCVRQualifiers()); } +/// \brief Compare types for equality with respect to possibly compatible +/// function types (noreturn adjustment, implicit calling conventions). If any +/// of parameter and argument is not a function, just perform type comparison. +/// +/// \param Param the template parameter type. +/// +/// \param Arg the argument type. +bool Sema::isSameOrCompatibleFunctionType(CanQualType Param, + CanQualType Arg) { + const FunctionType *ParamFunction = Param->getAs(), + *ArgFunction = Arg->getAs(); + + // Just compare if not functions. + if (!ParamFunction || !ArgFunction) + return Param == Arg; + + // Noreturn adjustment. + QualType AdjustedParam; + if (IsNoReturnConversion(Param, Arg, AdjustedParam)) + return Arg == Context.getCanonicalType(AdjustedParam); + + // FIXME: Compatible calling conventions. + + return Param == Arg; +} + /// \brief Deduce the template arguments by comparing the parameter type and /// the argument type (C++ [temp.deduct.type]). /// @@ -1103,6 +1133,8 @@ DeduceTemplateArgumentsByTypeMatch(Sema &S, return Sema::TDK_Success; // Check the cv-qualifiers on the parameter and argument types. + CanQualType CanParam = S.Context.getCanonicalType(Param); + CanQualType CanArg = S.Context.getCanonicalType(Arg); if (!(TDF & TDF_IgnoreQualifiers)) { if (TDF & TDF_ParamWithReferenceType) { if (hasInconsistentOrSupersetQualifiersOf(Param, Arg)) @@ -1114,14 +1146,25 @@ DeduceTemplateArgumentsByTypeMatch(Sema &S, // If the parameter type is not dependent, there is nothing to deduce. if (!Param->isDependentType()) { - if (!(TDF & TDF_SkipNonDependent) && Param != Arg) - return Sema::TDK_NonDeducedMismatch; - + if (!(TDF & TDF_SkipNonDependent)) { + bool NonDeduced = (TDF & TDF_InOverloadResolution)? + !S.isSameOrCompatibleFunctionType(CanParam, CanArg) : + Param != Arg; + if (NonDeduced) { + return Sema::TDK_NonDeducedMismatch; + } + } return Sema::TDK_Success; } - } else if (!Param->isDependentType() && - Param.getUnqualifiedType() == Arg.getUnqualifiedType()) { - return Sema::TDK_Success; + } else if (!Param->isDependentType()) { + CanQualType ParamUnqualType = CanParam.getUnqualifiedType(), + ArgUnqualType = CanArg.getUnqualifiedType(); + bool Success = (TDF & TDF_InOverloadResolution)? + S.isSameOrCompatibleFunctionType(ParamUnqualType, + ArgUnqualType) : + ParamUnqualType == ArgUnqualType; + if (Success) + return Sema::TDK_Success; } switch (Param->getTypeClass()) { @@ -2761,21 +2804,25 @@ Sema::FinishTemplateArgumentDeduction(FunctionTemplateDecl *FunctionTemplate, /// Gets the type of a function for template-argument-deducton /// purposes when it's considered as part of an overload set. -static QualType GetTypeOfFunction(ASTContext &Context, - const OverloadExpr::FindResult &R, +static QualType GetTypeOfFunction(Sema &S, const OverloadExpr::FindResult &R, FunctionDecl *Fn) { + // We may need to deduce the return type of the function now. + if (S.getLangOpts().CPlusPlus1y && Fn->getResultType()->isUndeducedType() && + S.DeduceReturnType(Fn, R.Expression->getExprLoc(), /*Diagnose*/false)) + return QualType(); + if (CXXMethodDecl *Method = dyn_cast(Fn)) if (Method->isInstance()) { // An instance method that's referenced in a form that doesn't // look like a member pointer is just invalid. if (!R.HasFormOfMemberPointer) return QualType(); - return Context.getMemberPointerType(Fn->getType(), - Context.getTypeDeclType(Method->getParent()).getTypePtr()); + return S.Context.getMemberPointerType(Fn->getType(), + S.Context.getTypeDeclType(Method->getParent()).getTypePtr()); } if (!R.IsAddressOfOperand) return Fn->getType(); - return Context.getPointerType(Fn->getType()); + return S.Context.getPointerType(Fn->getType()); } /// Apply the deduction rules for overload sets. @@ -2809,7 +2856,7 @@ ResolveOverloadForDeduction(Sema &S, TemplateParameterList *TemplateParams, // But we can still look for an explicit specialization. if (FunctionDecl *ExplicitSpec = S.ResolveSingleFunctionTemplateSpecialization(Ovl)) - return GetTypeOfFunction(S.Context, R, ExplicitSpec); + return GetTypeOfFunction(S, R, ExplicitSpec); } return QualType(); @@ -2842,7 +2889,7 @@ ResolveOverloadForDeduction(Sema &S, TemplateParameterList *TemplateParams, } FunctionDecl *Fn = cast(D); - QualType ArgType = GetTypeOfFunction(S.Context, R, Fn); + QualType ArgType = GetTypeOfFunction(S, R, Fn); if (ArgType.isNull()) continue; // Function-to-pointer conversion. @@ -3316,7 +3363,8 @@ Sema::DeduceTemplateArguments(FunctionTemplateDecl *FunctionTemplate, TemplateArgumentListInfo *ExplicitTemplateArgs, QualType ArgFunctionType, FunctionDecl *&Specialization, - TemplateDeductionInfo &Info) { + TemplateDeductionInfo &Info, + bool InOverloadResolution) { if (FunctionTemplate->isInvalidDecl()) return TDK_Invalid; @@ -3347,12 +3395,23 @@ Sema::DeduceTemplateArguments(FunctionTemplateDecl *FunctionTemplate, Deduced.resize(TemplateParams->size()); + // If the function has a deduced return type, substitute it for a dependent + // type so that we treat it as a non-deduced context in what follows. + bool HasUndeducedReturnType = false; + if (getLangOpts().CPlusPlus1y && InOverloadResolution && + Function->getResultType()->isUndeducedType()) { + FunctionType = SubstAutoType(FunctionType, Context.DependentTy); + HasUndeducedReturnType = true; + } + if (!ArgFunctionType.isNull()) { + unsigned TDF = TDF_TopLevelParameterTypeList; + if (InOverloadResolution) TDF |= TDF_InOverloadResolution; // Deduce template arguments from the function type. if (TemplateDeductionResult Result = DeduceTemplateArgumentsByTypeMatch(*this, TemplateParams, - FunctionType, ArgFunctionType, Info, - Deduced, TDF_TopLevelParameterTypeList)) + FunctionType, ArgFunctionType, + Info, Deduced, TDF)) return Result; } @@ -3362,12 +3421,26 @@ Sema::DeduceTemplateArguments(FunctionTemplateDecl *FunctionTemplate, Specialization, Info)) return Result; - // If the requested function type does not match the actual type of the - // specialization, template argument deduction fails. - if (!ArgFunctionType.isNull() && - !Context.hasSameType(ArgFunctionType, Specialization->getType())) + // If the function has a deduced return type, deduce it now, so we can check + // that the deduced function type matches the requested type. + if (HasUndeducedReturnType && + Specialization->getResultType()->isUndeducedType() && + DeduceReturnType(Specialization, Info.getLocation(), false)) return TDK_MiscellaneousDeductionFailure; + // If the requested function type does not match the actual type of the + // specialization with respect to arguments of compatible pointer to function + // types, template argument deduction fails. + if (!ArgFunctionType.isNull()) { + if (InOverloadResolution && !isSameOrCompatibleFunctionType( + Context.getCanonicalType(Specialization->getType()), + Context.getCanonicalType(ArgFunctionType))) + return TDK_MiscellaneousDeductionFailure; + else if(!InOverloadResolution && + !Context.hasSameType(Specialization->getType(), ArgFunctionType)) + return TDK_MiscellaneousDeductionFailure; + } + return TDK_Success; } @@ -3499,9 +3572,11 @@ Sema::TemplateDeductionResult Sema::DeduceTemplateArguments(FunctionTemplateDecl *FunctionTemplate, TemplateArgumentListInfo *ExplicitTemplateArgs, FunctionDecl *&Specialization, - TemplateDeductionInfo &Info) { + TemplateDeductionInfo &Info, + bool InOverloadResolution) { return DeduceTemplateArguments(FunctionTemplate, ExplicitTemplateArgs, - QualType(), Specialization, Info); + QualType(), Specialization, Info, + InOverloadResolution); } namespace { @@ -3522,13 +3597,19 @@ namespace { // auto &&lref = lvalue; // must transform into "rvalue reference to T" not "rvalue reference to // auto type deduced as T" in order for [temp.deduct.call]p3 to apply. - if (isa(Replacement)) { + if (!Replacement.isNull() && isa(Replacement)) { QualType Result = Replacement; - TemplateTypeParmTypeLoc NewTL = TLB.push(Result); + TemplateTypeParmTypeLoc NewTL = + TLB.push(Result); NewTL.setNameLoc(TL.getNameLoc()); return Result; } else { - QualType Result = RebuildAutoType(Replacement); + bool Dependent = + !Replacement.isNull() && Replacement->isDependentType(); + QualType Result = + SemaRef.Context.getAutoType(Dependent ? QualType() : Replacement, + TL.getTypePtr()->isDecltypeAuto(), + Dependent); AutoTypeLoc NewTL = TLB.push(Result); NewTL.setNameLoc(TL.getNameLoc()); return Result; @@ -3539,69 +3620,63 @@ namespace { // Lambdas never need to be transformed. 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()){ - if (MPT->getClass()->isDependentType()) - return true; - Ty = MPT->getPointeeType(); - } else if (const FunctionProtoType *FPT = Ty->getAs()){ - 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()) { - return true; - } else if (const VectorType *VT = Ty->getAs()) { - Ty = VT->getElementType(); - } else { - break; - } + QualType Apply(TypeLoc TL) { + // Create some scratch storage for the transformed type locations. + // FIXME: We're just going to throw this information away. Don't build it. + TypeLocBuilder TLB; + TLB.reserve(TL.getFullDataSize()); + return TransformType(TLB, TL); } - assert(Ty->getAs() && "didn't find 'auto' in auto type"); - return false; - } + }; +} + +Sema::DeduceAutoResult +Sema::DeduceAutoType(TypeSourceInfo *Type, Expr *&Init, QualType &Result) { + return DeduceAutoType(Type->getTypeLoc(), Init, Result); } -/// \brief Deduce the type for an auto type-specifier (C++0x [dcl.spec.auto]p6) +/// \brief Deduce the type for an auto type-specifier (C++11 [dcl.spec.auto]p6) /// /// \param Type the type pattern using the auto type-specifier. -/// /// \param Init the initializer for the variable whose type is to be deduced. -/// /// \param Result if type deduction was successful, this will be set to the -/// deduced type. This may still contain undeduced autos if the type is -/// dependent. This will be set to null if deduction succeeded, but auto -/// substitution failed; the appropriate diagnostic will already have been -/// produced in that case. +/// deduced type. Sema::DeduceAutoResult -Sema::DeduceAutoType(TypeSourceInfo *Type, Expr *&Init, - TypeSourceInfo *&Result) { +Sema::DeduceAutoType(TypeLoc Type, Expr *&Init, QualType &Result) { if (Init->getType()->isNonOverloadPlaceholderType()) { - ExprResult result = CheckPlaceholderExpr(Init); - if (result.isInvalid()) return DAR_FailedAlreadyDiagnosed; - Init = result.take(); + ExprResult NonPlaceholder = CheckPlaceholderExpr(Init); + if (NonPlaceholder.isInvalid()) + return DAR_FailedAlreadyDiagnosed; + Init = NonPlaceholder.take(); } - if (Init->isTypeDependent() || isDependentAutoType(Type->getType())) { - Result = Type; + if (Init->isTypeDependent() || Type.getType()->isDependentType()) { + Result = SubstituteAutoTransform(*this, Context.DependentTy).Apply(Type); + assert(!Result.isNull() && "substituting DependentTy can't fail"); return DAR_Succeeded; } + // If this is a 'decltype(auto)' specifier, do the decltype dance. + // Since 'decltype(auto)' can only occur at the top of the type, we + // don't need to go digging for it. + if (const AutoType *AT = Type.getType()->getAs()) { + if (AT->isDecltypeAuto()) { + if (isa(Init)) { + Diag(Init->getLocStart(), diag::err_decltype_auto_initializer_list); + return DAR_FailedAlreadyDiagnosed; + } + + QualType Deduced = BuildDecltypeType(Init, Init->getLocStart()); + // FIXME: Support a non-canonical deduced type for 'auto'. + Deduced = Context.getCanonicalType(Deduced); + Result = SubstituteAutoTransform(*this, Deduced).Apply(Type); + if (Result.isNull()) + return DAR_FailedAlreadyDiagnosed; + return DAR_Succeeded; + } + } + SourceLocation Loc = Init->getExprLoc(); LocalInstantiationScope InstScope(*this); @@ -3615,10 +3690,9 @@ Sema::DeduceAutoType(TypeSourceInfo *Type, Expr *&Init, FixedSizeTemplateParameterList<1> TemplateParams(Loc, Loc, &TemplParamPtr, Loc); - TypeSourceInfo *FuncParamInfo = - SubstituteAutoTransform(*this, TemplArg).TransformType(Type); - assert(FuncParamInfo && "substituting template parameter for 'auto' failed"); - QualType FuncParam = FuncParamInfo->getType(); + QualType FuncParam = SubstituteAutoTransform(*this, TemplArg).Apply(Type); + assert(!FuncParam.isNull() && + "substituting template parameter for 'auto' failed"); // Deduce type of TemplParam in Func(Init) SmallVector Deduced; @@ -3659,21 +3733,27 @@ Sema::DeduceAutoType(TypeSourceInfo *Type, Expr *&Init, return DAR_FailedAlreadyDiagnosed; } - Result = SubstituteAutoTransform(*this, DeducedType).TransformType(Type); + Result = SubstituteAutoTransform(*this, DeducedType).Apply(Type); + if (Result.isNull()) + return DAR_FailedAlreadyDiagnosed; // Check that the deduced argument type is compatible with the original // argument type per C++ [temp.deduct.call]p4. - if (!InitList && Result && - CheckOriginalCallArgDeduction(*this, + if (!InitList && !Result.isNull() && + CheckOriginalCallArgDeduction(*this, Sema::OriginalCallArg(FuncParam,0,InitType), - Result->getType())) { - Result = 0; + Result)) { + Result = QualType(); return DAR_Failed; } return DAR_Succeeded; } +QualType Sema::SubstAutoType(QualType Type, QualType Deduced) { + return SubstituteAutoTransform(*this, Deduced).TransformType(Type); +} + void Sema::DiagnoseAutoDeductionFailure(VarDecl *VDecl, Expr *Init) { if (isa(Init)) Diag(VDecl->getLocation(), @@ -3685,6 +3765,22 @@ void Sema::DiagnoseAutoDeductionFailure(VarDecl *VDecl, Expr *Init) { << Init->getSourceRange(); } +bool Sema::DeduceReturnType(FunctionDecl *FD, SourceLocation Loc, + bool Diagnose) { + assert(FD->getResultType()->isUndeducedType()); + + if (FD->getTemplateInstantiationPattern()) + InstantiateFunctionDefinition(Loc, FD); + + bool StillUndeduced = FD->getResultType()->isUndeducedType(); + if (StillUndeduced && Diagnose && !FD->isInvalidDecl()) { + Diag(Loc, diag::err_auto_fn_used_before_defined) << FD; + Diag(FD->getLocation(), diag::note_callee_decl) << FD; + } + + return StillUndeduced; +} + static void MarkUsedTemplateParameters(ASTContext &Ctx, QualType T, bool OnlyDeduced, @@ -4053,7 +4149,7 @@ static bool isSameTemplate(TemplateDecl *T1, TemplateDecl *T2) { /// template argument deduction. UnresolvedSetIterator Sema::getMostSpecialized(UnresolvedSetIterator SpecBegin, - UnresolvedSetIterator SpecEnd, + UnresolvedSetIterator SpecEnd, TemplatePartialOrderingContext TPOC, unsigned NumCallArguments, SourceLocation Loc, @@ -4110,11 +4206,10 @@ Sema::getMostSpecialized(UnresolvedSetIterator SpecBegin, } // Diagnose the ambiguity. - if (Complain) + if (Complain) { Diag(Loc, AmbigDiag); - if (Complain) - // FIXME: Can we order the candidates in some sane way? + // FIXME: Can we order the candidates in some sane way? for (UnresolvedSetIterator I = SpecBegin; I != SpecEnd; ++I) { PartialDiagnostic PD = CandidateDiag; PD << getTemplateArgumentBindingsText( @@ -4125,6 +4220,7 @@ Sema::getMostSpecialized(UnresolvedSetIterator SpecBegin, TargetType); Diag((*I)->getLocation(), PD); } + } return SpecEnd; } diff --git a/lib/Sema/SemaTemplateInstantiate.cpp b/lib/Sema/SemaTemplateInstantiate.cpp index f755b8ca452..7ef04e964dc 100644 --- a/lib/Sema/SemaTemplateInstantiate.cpp +++ b/lib/Sema/SemaTemplateInstantiate.cpp @@ -2701,11 +2701,10 @@ void LocalInstantiationScope::InstantiatedLocal(const Decl *D, Decl *Inst) { llvm::PointerUnion &Stored = LocalDecls[D]; if (Stored.isNull()) Stored = Inst; - else if (Stored.is()) { + else if (DeclArgumentPack *Pack = Stored.dyn_cast()) + Pack->push_back(Inst); + else assert(Stored.get() == Inst && "Already instantiated this local"); - Stored = Inst; - } else - LocalDecls[D].get()->push_back(Inst); } void LocalInstantiationScope::InstantiatedLocalPackArg(const Decl *D, diff --git a/lib/Sema/SemaTemplateInstantiateDecl.cpp b/lib/Sema/SemaTemplateInstantiateDecl.cpp index 33e83d07d63..d1428c51a4f 100644 --- a/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -339,7 +339,7 @@ Decl *TemplateDeclInstantiator::VisitVarDecl(VarDecl *D) { D->getLocation(), D->getIdentifier(), DI->getType(), DI, D->getStorageClass()); - Var->setThreadSpecified(D->isThreadSpecified()); + Var->setTSCSpec(D->getTSCSpec()); Var->setInitStyle(D->getInitStyle()); Var->setCXXForRangeDecl(D->isCXXForRangeDecl()); Var->setConstexpr(D->isConstexpr()); @@ -525,6 +525,53 @@ Decl *TemplateDeclInstantiator::VisitFieldDecl(FieldDecl *D) { return Field; } +Decl *TemplateDeclInstantiator::VisitMSPropertyDecl(MSPropertyDecl *D) { + bool Invalid = false; + TypeSourceInfo *DI = D->getTypeSourceInfo(); + + if (DI->getType()->isVariablyModifiedType()) { + SemaRef.Diag(D->getLocation(), diag::err_property_is_variably_modified) + << D->getName(); + Invalid = true; + } else if (DI->getType()->isInstantiationDependentType()) { + DI = SemaRef.SubstType(DI, TemplateArgs, + D->getLocation(), D->getDeclName()); + if (!DI) { + DI = D->getTypeSourceInfo(); + Invalid = true; + } else if (DI->getType()->isFunctionType()) { + // C++ [temp.arg.type]p3: + // If a declaration acquires a function type through a type + // dependent on a template-parameter and this causes a + // declaration that does not use the syntactic form of a + // function declarator to have function type, the program is + // ill-formed. + SemaRef.Diag(D->getLocation(), diag::err_field_instantiates_to_function) + << DI->getType(); + Invalid = true; + } + } else { + SemaRef.MarkDeclarationsReferencedInType(D->getLocation(), DI->getType()); + } + + MSPropertyDecl *Property = new (SemaRef.Context) + MSPropertyDecl(Owner, D->getLocation(), + D->getDeclName(), DI->getType(), DI, + D->getLocStart(), + D->getGetterId(), D->getSetterId()); + + SemaRef.InstantiateAttrs(TemplateArgs, D, Property, LateAttrs, + StartingScope); + + if (Invalid) + Property->setInvalidDecl(); + + Property->setAccess(D->getAccess()); + Owner->addDecl(Property); + + return Property; +} + Decl *TemplateDeclInstantiator::VisitIndirectFieldDecl(IndirectFieldDecl *D) { NamedDecl **NamedChain = new (SemaRef.Context)NamedDecl*[D->getChainingSize()]; @@ -754,7 +801,7 @@ void TemplateDeclInstantiator::InstantiateEnumDefinition( // FIXME: Fixup LBraceLoc SemaRef.ActOnEnumBody(Enum->getLocation(), SourceLocation(), Enum->getRBraceLoc(), Enum, - Enumerators.data(), Enumerators.size(), + Enumerators, 0, 0); } @@ -1105,12 +1152,11 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(FunctionDecl *D, // this declaration. FunctionTemplateDecl *FunctionTemplate = D->getDescribedFunctionTemplate(); if (FunctionTemplate && !TemplateParams) { - std::pair Innermost - = TemplateArgs.getInnermost(); + ArrayRef Innermost = TemplateArgs.getInnermost(); void *InsertPos = 0; FunctionDecl *SpecFunc - = FunctionTemplate->findSpecialization(Innermost.first, Innermost.second, + = FunctionTemplate->findSpecialization(Innermost.begin(), Innermost.size(), InsertPos); // If we already have a function template specialization, return it. @@ -1162,7 +1208,7 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(FunctionDecl *D, FunctionDecl *Function = FunctionDecl::Create(SemaRef.Context, DC, D->getInnerLocStart(), D->getNameInfo(), T, TInfo, - D->getStorageClass(), + D->getCanonicalDecl()->getStorageClass(), D->isInlineSpecified(), D->hasWrittenPrototype(), D->isConstexpr()); @@ -1235,12 +1281,11 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(FunctionDecl *D, } } else if (FunctionTemplate) { // Record this function template specialization. - std::pair Innermost - = TemplateArgs.getInnermost(); + ArrayRef Innermost = TemplateArgs.getInnermost(); Function->setFunctionTemplateSpecialization(FunctionTemplate, TemplateArgumentList::CreateCopy(SemaRef.Context, - Innermost.first, - Innermost.second), + Innermost.begin(), + Innermost.size()), /*InsertPos=*/0); } else if (isFriend) { // Note, we need this connection even if the friend doesn't have a body. @@ -1413,12 +1458,12 @@ TemplateDeclInstantiator::VisitCXXMethodDecl(CXXMethodDecl *D, // We are creating a function template specialization from a function // template. Check whether there is already a function template // specialization for this particular set of template arguments. - std::pair Innermost - = TemplateArgs.getInnermost(); + ArrayRef Innermost = TemplateArgs.getInnermost(); void *InsertPos = 0; FunctionDecl *SpecFunc - = FunctionTemplate->findSpecialization(Innermost.first, Innermost.second, + = FunctionTemplate->findSpecialization(Innermost.begin(), + Innermost.size(), InsertPos); // If we already have a function template specialization, return it. @@ -1513,6 +1558,36 @@ TemplateDeclInstantiator::VisitCXXMethodDecl(CXXMethodDecl *D, Constructor->isExplicit(), Constructor->isInlineSpecified(), false, Constructor->isConstexpr()); + + // Claim that the instantiation of a constructor or constructor template + // inherits the same constructor that the template does. + if (CXXConstructorDecl *Inh = const_cast( + Constructor->getInheritedConstructor())) { + // If we're instantiating a specialization of a function template, our + // "inherited constructor" will actually itself be a function template. + // Instantiate a declaration of it, too. + if (FunctionTemplate) { + assert(!TemplateParams && Inh->getDescribedFunctionTemplate() && + !Inh->getParent()->isDependentContext() && + "inheriting constructor template in dependent context?"); + Sema::InstantiatingTemplate Inst(SemaRef, Constructor->getLocation(), + Inh); + if (Inst) + return 0; + Sema::ContextRAII SavedContext(SemaRef, Inh->getDeclContext()); + LocalInstantiationScope LocalScope(SemaRef); + + // Use the same template arguments that we deduced for the inheriting + // constructor. There's no way they could be deduced differently. + MultiLevelTemplateArgumentList InheritedArgs; + InheritedArgs.addOuterTemplateArguments(TemplateArgs.getInnermost()); + Inh = cast_or_null( + SemaRef.SubstDecl(Inh, Inh->getDeclContext(), InheritedArgs)); + if (!Inh) + return 0; + } + cast(Method)->setInheritedConstructor(Inh); + } } else if (CXXDestructorDecl *Destructor = dyn_cast(D)) { Method = CXXDestructorDecl::Create(SemaRef.Context, Record, StartLoc, NameInfo, T, TInfo, @@ -1526,10 +1601,10 @@ TemplateDeclInstantiator::VisitCXXMethodDecl(CXXMethodDecl *D, Conversion->isConstexpr(), Conversion->getLocEnd()); } else { + StorageClass SC = D->isStatic() ? SC_Static : SC_None; Method = CXXMethodDecl::Create(SemaRef.Context, Record, StartLoc, NameInfo, T, TInfo, - D->getStorageClass(), - D->isInlineSpecified(), + SC, D->isInlineSpecified(), D->isConstexpr(), D->getLocEnd()); } @@ -1565,12 +1640,11 @@ TemplateDeclInstantiator::VisitCXXMethodDecl(CXXMethodDecl *D, Method->setDescribedFunctionTemplate(FunctionTemplate); } else if (FunctionTemplate) { // Record this function template specialization. - std::pair Innermost - = TemplateArgs.getInnermost(); + ArrayRef Innermost = TemplateArgs.getInnermost(); Method->setFunctionTemplateSpecialization(FunctionTemplate, TemplateArgumentList::CreateCopy(SemaRef.Context, - Innermost.first, - Innermost.second), + Innermost.begin(), + Innermost.size()), /*InsertPos=*/0); } else if (!isFriend) { // Record that this is an instantiation of a member function. @@ -2688,15 +2762,16 @@ 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"); + ExceptionSpecificationType NewEST = EST_Uninstantiated; + if (EPI.ExceptionSpecType == EST_Unevaluated) + NewEST = EST_Unevaluated; // Mark the function has having an uninstantiated exception specification. const FunctionProtoType *NewProto = New->getType()->getAs(); assert(NewProto && "Template instantiation without function prototype?"); EPI = NewProto->getExtProtoInfo(); - EPI.ExceptionSpecType = EST_Uninstantiated; + EPI.ExceptionSpecType = NewEST; EPI.ExceptionSpecDecl = New; EPI.ExceptionSpecTemplate = ExceptionSpecTemplate; New->setType(SemaRef.Context.getFunctionType(NewProto->getResultType(), @@ -2733,7 +2808,6 @@ TemplateDeclInstantiator::InitMethodInstantiation(CXXMethodDecl *New, if (Tmpl->isVirtualAsWritten()) New->setVirtualAsWritten(true); - // FIXME: attributes // FIXME: New needs a pointer to Tmpl return false; } @@ -2820,13 +2894,15 @@ void Sema::InstantiateFunctionDefinition(SourceLocation PointOfInstantiation, return; } - // C++0x [temp.explicit]p9: - // Except for inline functions, other explicit instantiation declarations - // have the effect of suppressing the implicit instantiation of the entity - // to which they refer. + // C++1y [temp.explicit]p10: + // Except for inline functions, declarations with types deduced from their + // initializer or return value, and class template specializations, other + // explicit instantiation declarations have the effect of suppressing the + // implicit instantiation of the entity to which they refer. if (Function->getTemplateSpecializationKind() == TSK_ExplicitInstantiationDeclaration && - !PatternDecl->isInlined()) + !PatternDecl->isInlined() && + !PatternDecl->getResultType()->isUndeducedType()) return; if (PatternDecl->isInlined()) diff --git a/lib/Sema/SemaTemplateVariadic.cpp b/lib/Sema/SemaTemplateVariadic.cpp index c0ad2be6d40..db885aeec7b 100644 --- a/lib/Sema/SemaTemplateVariadic.cpp +++ b/lib/Sema/SemaTemplateVariadic.cpp @@ -727,6 +727,7 @@ bool Sema::containsUnexpandedParameterPacks(Declarator &D) { case TST_interface: case TST_class: case TST_auto: + case TST_decltype_auto: case TST_unknown_anytype: case TST_image1d_t: case TST_image1d_array_t: diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp index 7169eeab9b9..0959f7d66ad 100644 --- a/lib/Sema/SemaType.cpp +++ b/lib/Sema/SemaType.cpp @@ -793,9 +793,7 @@ static QualType ConvertDeclSpecToType(TypeProcessingState &state) { // "At least one type specifier shall be given in the declaration // specifiers in each declaration, and in the specifier-qualifier list in // each struct declaration and type name." - // FIXME: Does Microsoft really have the implicit int extension in C++? - if (S.getLangOpts().CPlusPlus && - !S.getLangOpts().MicrosoftExt) { + if (S.getLangOpts().CPlusPlus) { S.Diag(DeclLoc, diag::err_missing_type_specifier) << DS.getSourceRange(); @@ -994,11 +992,14 @@ static QualType ConvertDeclSpecToType(TypeProcessingState &state) { } break; - case DeclSpec::TST_auto: { + case DeclSpec::TST_auto: // TypeQuals handled by caller. - Result = Context.getAutoType(QualType()); + Result = Context.getAutoType(QualType(), /*decltype(auto)*/false); + break; + + case DeclSpec::TST_decltype_auto: + Result = Context.getAutoType(QualType(), /*decltype(auto)*/true); break; - } case DeclSpec::TST_unknown_anytype: Result = Context.UnknownAnyTy; @@ -1457,12 +1458,6 @@ QualType Sema::BuildArrayType(QualType T, ArrayType::ArraySizeModifier ASM, return QualType(); } - if (T->getContainedAutoType()) { - Diag(Loc, diag::err_illegal_decl_array_of_auto) - << getPrintableNameForEntity(Entity) << T; - return QualType(); - } - if (const RecordType *EltTy = T->getAs()) { // If the element type is a struct or union that contains a variadic // array, accept it as a GNU extension: C99 6.7.2.1p2. @@ -1572,6 +1567,7 @@ QualType Sema::BuildArrayType(QualType T, ArrayType::ArraySizeModifier ASM, if (!getLangOpts().C99) { if (T->isVariableArrayType()) { // Prohibit the use of non-POD types in VLAs. + // FIXME: C++1y allows this. QualType BaseT = Context.getBaseElementType(T); if (!T->isDependentType() && !BaseT.isPODType(Context) && @@ -1587,7 +1583,9 @@ QualType Sema::BuildArrayType(QualType T, ArrayType::ArraySizeModifier ASM, } // Just extwarn about VLAs. else - Diag(Loc, diag::ext_vla); + Diag(Loc, getLangOpts().CPlusPlus1y + ? diag::warn_cxx11_compat_array_of_runtime_bound + : diag::ext_vla); } else if (ASM != ArrayType::Normal || Quals != 0) Diag(Loc, getLangOpts().CPlusPlus? diag::err_c99_array_usage_cxx @@ -2020,6 +2018,8 @@ static QualType GetDeclSpecTypeForDeclarator(TypeProcessingState &state, // The TagDecl owned by the DeclSpec. TagDecl *OwnedTagDecl = 0; + bool ContainsPlaceholderType = false; + switch (D.getName().getKind()) { case UnqualifiedId::IK_ImplicitSelfParam: case UnqualifiedId::IK_OperatorFunctionId: @@ -2027,6 +2027,7 @@ static QualType GetDeclSpecTypeForDeclarator(TypeProcessingState &state, case UnqualifiedId::IK_LiteralOperatorId: case UnqualifiedId::IK_TemplateId: T = ConvertDeclSpecToType(state); + ContainsPlaceholderType = D.getDeclSpec().containsPlaceholderType(); if (!D.isInvalidType() && D.getDeclSpec().isTypeSpecOwned()) { OwnedTagDecl = cast(D.getDeclSpec().getRepAsDecl()); @@ -2050,6 +2051,7 @@ static QualType GetDeclSpecTypeForDeclarator(TypeProcessingState &state, // converts to. T = SemaRef.GetTypeFromParser(D.getName().ConversionFunctionId, &ReturnTypeInfo); + ContainsPlaceholderType = T->getContainedAutoType(); break; } @@ -2060,7 +2062,7 @@ static QualType GetDeclSpecTypeForDeclarator(TypeProcessingState &state, // In C++11, a function declarator using 'auto' must have a trailing return // type (this is checked later) and we can skip this. In other languages // using auto, we need to check regardless. - if (D.getDeclSpec().getTypeSpecType() == DeclSpec::TST_auto && + if (ContainsPlaceholderType && (!SemaRef.getLangOpts().CPlusPlus11 || !D.isFunctionDeclarator())) { int Error = -1; @@ -2103,10 +2105,15 @@ static QualType GetDeclSpecTypeForDeclarator(TypeProcessingState &state, Error = 10; // Type alias break; case Declarator::TrailingReturnContext: - Error = 11; // Function return type + if (!SemaRef.getLangOpts().CPlusPlus1y) + Error = 11; // Function return type + break; + case Declarator::ConversionIdContext: + if (!SemaRef.getLangOpts().CPlusPlus1y) + Error = 12; // conversion-type-id break; case Declarator::TypeNameContext: - Error = 12; // Generic + Error = 13; // Generic break; case Declarator::FileContext: case Declarator::BlockContext: @@ -2142,15 +2149,19 @@ static QualType GetDeclSpecTypeForDeclarator(TypeProcessingState &state, } } + SourceRange AutoRange = D.getDeclSpec().getTypeSpecTypeLoc(); + if (D.getName().getKind() == UnqualifiedId::IK_ConversionFunctionId) + AutoRange = D.getName().getSourceRange(); + if (Error != -1) { - SemaRef.Diag(D.getDeclSpec().getTypeSpecTypeLoc(), - diag::err_auto_not_allowed) - << Error; + SemaRef.Diag(AutoRange.getBegin(), diag::err_auto_not_allowed) + << Error << AutoRange; T = SemaRef.Context.IntTy; D.setInvalidType(true); } else - SemaRef.Diag(D.getDeclSpec().getTypeSpecTypeLoc(), - diag::warn_cxx98_compat_auto_type_specifier); + SemaRef.Diag(AutoRange.getBegin(), + diag::warn_cxx98_compat_auto_type_specifier) + << AutoRange; } if (SemaRef.getLangOpts().CPlusPlus && @@ -2182,6 +2193,7 @@ static QualType GetDeclSpecTypeForDeclarator(TypeProcessingState &state, D.setInvalidType(true); break; case Declarator::TypeNameContext: + case Declarator::ConversionIdContext: case Declarator::TemplateParamContext: case Declarator::CXXNewContext: case Declarator::CXXCatchContext: @@ -2399,6 +2411,46 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, (T->castAs()->getTypeQuals() != 0 || T->castAs()->getRefQualifier() != RQ_None); + // If T is 'decltype(auto)', the only declarators we can have are parens + // and at most one function declarator if this is a function declaration. + if (const AutoType *AT = T->getAs()) { + if (AT->isDecltypeAuto()) { + for (unsigned I = 0, E = D.getNumTypeObjects(); I != E; ++I) { + unsigned Index = E - I - 1; + DeclaratorChunk &DeclChunk = D.getTypeObject(Index); + unsigned DiagId = diag::err_decltype_auto_compound_type; + unsigned DiagKind = 0; + switch (DeclChunk.Kind) { + case DeclaratorChunk::Paren: + continue; + case DeclaratorChunk::Function: { + unsigned FnIndex; + if (D.isFunctionDeclarationContext() && + D.isFunctionDeclarator(FnIndex) && FnIndex == Index) + continue; + DiagId = diag::err_decltype_auto_function_declarator_not_declaration; + break; + } + case DeclaratorChunk::Pointer: + case DeclaratorChunk::BlockPointer: + case DeclaratorChunk::MemberPointer: + DiagKind = 0; + break; + case DeclaratorChunk::Reference: + DiagKind = 1; + break; + case DeclaratorChunk::Array: + DiagKind = 2; + break; + } + + S.Diag(DeclChunk.Loc, DiagId) << DiagKind; + D.setInvalidType(true); + break; + } + } + } + // Walk the DeclTypeInfo, building the recursive type as we go. // DeclTypeInfos are ordered from the identifier out, which is // opposite of what we want :). @@ -2527,6 +2579,15 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, } } + if (const AutoType *AT = T->getContainedAutoType()) { + // We've already diagnosed this for decltype(auto). + if (!AT->isDecltypeAuto()) + S.Diag(DeclType.Loc, diag::err_illegal_decl_array_of_auto) + << getPrintableNameForEntity(Name) << T; + T = QualType(); + break; + } + T = S.BuildArrayType(T, ASM, ArraySize, ATI.TypeQuals, SourceRange(DeclType.Loc, DeclType.EndLoc), Name); break; @@ -2544,7 +2605,8 @@ 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.hasTrailingReturnType() && chunkIndex == 0) { + !FTI.hasTrailingReturnType() && chunkIndex == 0 && + !S.getLangOpts().CPlusPlus1y) { S.Diag(D.getDeclSpec().getTypeSpecTypeLoc(), diag::err_auto_missing_trailing_return); T = Context.IntTy; @@ -2557,7 +2619,8 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, << T << D.getDeclSpec().getSourceRange(); D.setInvalidType(true); } else if (D.getContext() != Declarator::LambdaExprContext && - (T.hasQualifiers() || !isa(T))) { + (T.hasQualifiers() || !isa(T) || + cast(T)->isDecltypeAuto())) { S.Diag(D.getDeclSpec().getTypeSpecTypeLoc(), diag::err_trailing_return_without_auto) << T << D.getDeclSpec().getSourceRange(); @@ -3044,6 +3107,7 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, case Declarator::ObjCCatchContext: case Declarator::BlockLiteralContext: case Declarator::LambdaExprContext: + case Declarator::ConversionIdContext: case Declarator::TrailingReturnContext: case Declarator::TemplateTypeArgContext: // FIXME: We may want to allow parameter packs in block-literal contexts @@ -3781,7 +3845,7 @@ static bool handleObjCOwnershipTypeAttr(TypeProcessingState &state, QualType &type) { bool NonObjCPointer = false; - if (!type->isDependentType()) { + if (!type->isDependentType() && !type->isUndeducedType()) { if (const PointerType *ptr = type->getAs()) { QualType pointee = ptr->getPointeeType(); if (pointee->isObjCRetainableType() || pointee->isPointerType()) @@ -4806,7 +4870,7 @@ bool Sema::RequireLiteralType(SourceLocation Loc, QualType T, QualType ElemType = Context.getBaseElementType(T); RequireCompleteType(Loc, ElemType, 0); - if (T->isLiteralType()) + if (T->isLiteralType(Context)) return false; if (Diagnoser.Suppressed) @@ -4848,7 +4912,7 @@ bool Sema::RequireLiteralType(SourceLocation Loc, QualType T, } else if (RD->hasNonLiteralTypeFieldsOrBases()) { for (CXXRecordDecl::base_class_const_iterator I = RD->bases_begin(), E = RD->bases_end(); I != E; ++I) { - if (!I->getType()->isLiteralType()) { + if (!I->getType()->isLiteralType(Context)) { Diag(I->getLocStart(), diag::note_non_literal_base_class) << RD << I->getType() << I->getSourceRange(); @@ -4857,7 +4921,7 @@ 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() || + if (!I->getType()->isLiteralType(Context) || I->getType().isVolatileQualified()) { Diag(I->getLocation(), diag::note_non_literal_field) << RD << *I << I->getType() diff --git a/lib/Sema/TreeTransform.h b/lib/Sema/TreeTransform.h index bdd68a7bde9..89e23ef4605 100644 --- a/lib/Sema/TreeTransform.h +++ b/lib/Sema/TreeTransform.h @@ -754,17 +754,20 @@ class TreeTransform { UnaryTransformType::UTTKind UKind, SourceLocation Loc); - /// \brief Build a new C++0x decltype type. + /// \brief Build a new C++11 decltype type. /// /// By default, performs semantic analysis when building the decltype type. /// Subclasses may override this routine to provide different behavior. QualType RebuildDecltypeType(Expr *Underlying, SourceLocation Loc); - /// \brief Build a new C++0x auto type. + /// \brief Build a new C++11 auto type. /// /// By default, builds a new AutoType with the given deduced type. - QualType RebuildAutoType(QualType Deduced) { - return SemaRef.Context.getAutoType(Deduced); + QualType RebuildAutoType(QualType Deduced, bool IsDecltypeAuto) { + // Note, IsDependent is always false here: we implicitly convert an 'auto' + // which has been deduced to a dependent type into an undeduced 'auto', so + // that we'll retry deduction after the transformation. + return SemaRef.Context.getAutoType(Deduced, IsDecltypeAuto); } /// \brief Build a new template specialization type. @@ -1187,8 +1190,16 @@ class TreeTransform { /// By default, performs semantic analysis to build the new statement. /// Subclasses may override this routine to provide different behavior. StmtResult RebuildMSAsmStmt(SourceLocation AsmLoc, SourceLocation LBraceLoc, - ArrayRef AsmToks, SourceLocation EndLoc) { - return getSema().ActOnMSAsmStmt(AsmLoc, LBraceLoc, AsmToks, EndLoc); + ArrayRef AsmToks, + StringRef AsmString, + unsigned NumOutputs, unsigned NumInputs, + ArrayRef Constraints, + ArrayRef Clobbers, + ArrayRef Exprs, + SourceLocation EndLoc) { + return getSema().ActOnMSAsmStmt(AsmLoc, LBraceLoc, AsmToks, AsmString, + NumOutputs, NumInputs, + Constraints, Clobbers, Exprs, EndLoc); } /// \brief Build a new Objective-C \@try statement. @@ -1338,6 +1349,23 @@ class TreeTransform { Expr *Cond, Expr *Inc, Stmt *LoopVar, SourceLocation RParenLoc) { + // If we've just learned that the range is actually an Objective-C + // collection, treat this as an Objective-C fast enumeration loop. + if (DeclStmt *RangeStmt = dyn_cast(Range)) { + if (RangeStmt->isSingleDecl()) { + if (VarDecl *RangeVar = dyn_cast(RangeStmt->getSingleDecl())) { + if (RangeVar->isInvalidDecl()) + return StmtError(); + + Expr *RangeExpr = RangeVar->getInit(); + if (!RangeExpr->isTypeDependent() && + RangeExpr->getType()->isObjCObjectPointerType()) + return getSema().ActOnObjCForCollectionStmt(ForLoc, LoopVar, RangeExpr, + RParenLoc); + } + } + } + return getSema().BuildCXXForRangeStmt(ForLoc, ColonLoc, Range, BeginEnd, Cond, Inc, LoopVar, RParenLoc, Sema::BFRK_Rebuild); @@ -1966,6 +1994,17 @@ class TreeTransform { Param)); } + /// \brief Build a new C++11 default-initialization expression. + /// + /// By default, builds a new default field initialization expression, which + /// does not require any semantic analysis. Subclasses may override this + /// routine to provide different behavior. + ExprResult RebuildCXXDefaultInitExpr(SourceLocation Loc, + FieldDecl *Field) { + return getSema().Owned(CXXDefaultInitExpr::Create(getSema().Context, Loc, + Field)); + } + /// \brief Build a new C++ zero-initialization expression. /// /// By default, performs semantic analysis to build the new expression. @@ -1974,7 +2013,7 @@ class TreeTransform { SourceLocation LParenLoc, SourceLocation RParenLoc) { return getSema().BuildCXXTypeConstructExpr(TSInfo, LParenLoc, - MultiExprArg(), RParenLoc); + None, RParenLoc); } /// \brief Build a new C++ "new" expression. @@ -2609,13 +2648,13 @@ ExprResult TreeTransform::TransformInitializer(Expr *Init, // Revert value-initialization back to empty parens. if (CXXScalarValueInitExpr *VIE = dyn_cast(Init)) { SourceRange Parens = VIE->getSourceRange(); - return getDerived().RebuildParenListExpr(Parens.getBegin(), MultiExprArg(), + return getDerived().RebuildParenListExpr(Parens.getBegin(), None, Parens.getEnd()); } // FIXME: We shouldn't build ImplicitValueInitExprs for direct-initialization. if (isa(Init)) - return getDerived().RebuildParenListExpr(SourceLocation(), MultiExprArg(), + return getDerived().RebuildParenListExpr(SourceLocation(), None, SourceLocation()); // Revert initialization by constructor back to a parenthesized or braced list @@ -3382,7 +3421,7 @@ TreeTransform::TransformQualifiedType(TypeLocBuilder &TLB, Qs.removeObjCLifetime(); Deduced = SemaRef.Context.getQualifiedType(Deduced.getUnqualifiedType(), Qs); - Result = SemaRef.Context.getAutoType(Deduced); + Result = SemaRef.Context.getAutoType(Deduced, AutoTy->isDecltypeAuto()); TLB.TypeWasModifiedSafely(Result); } else { // Otherwise, complain about the addition of a qualifier to an @@ -4475,8 +4514,9 @@ QualType TreeTransform::TransformAutoType(TypeLocBuilder &TLB, } QualType Result = TL.getType(); - if (getDerived().AlwaysRebuild() || NewDeduced != OldDeduced) { - Result = getDerived().RebuildAutoType(NewDeduced); + if (getDerived().AlwaysRebuild() || NewDeduced != OldDeduced || + T->isDependentType()) { + Result = getDerived().RebuildAutoType(NewDeduced, T->isDecltypeAuto()); if (Result.isNull()) return QualType(); } @@ -5617,8 +5657,30 @@ TreeTransform::TransformMSAsmStmt(MSAsmStmt *S) { ArrayRef AsmToks = llvm::makeArrayRef(S->getAsmToks(), S->getNumAsmToks()); + bool HadError = false, HadChange = false; + + ArrayRef SrcExprs = S->getAllExprs(); + SmallVector TransformedExprs; + TransformedExprs.reserve(SrcExprs.size()); + for (unsigned i = 0, e = SrcExprs.size(); i != e; ++i) { + ExprResult Result = getDerived().TransformExpr(SrcExprs[i]); + if (!Result.isUsable()) { + HadError = true; + } else { + HadChange |= (Result.get() != SrcExprs[i]); + TransformedExprs.push_back(Result.take()); + } + } + + if (HadError) return StmtError(); + if (!HadChange && !getDerived().AlwaysRebuild()) + return Owned(S); + return getDerived().RebuildMSAsmStmt(S->getAsmLoc(), S->getLBraceLoc(), - AsmToks, S->getEndLoc()); + AsmToks, S->getAsmString(), + S->getNumOutputs(), S->getNumInputs(), + S->getAllConstraints(), S->getClobbers(), + TransformedExprs, S->getEndLoc()); } template @@ -5919,12 +5981,15 @@ TreeTransform::TransformCXXForRangeStmt(CXXForRangeStmt *S) { BeginEnd.get() != S->getBeginEndStmt() || Cond.get() != S->getCond() || Inc.get() != S->getInc() || - LoopVar.get() != S->getLoopVarStmt()) + LoopVar.get() != S->getLoopVarStmt()) { NewStmt = getDerived().RebuildCXXForRangeStmt(S->getForLoc(), S->getColonLoc(), Range.get(), BeginEnd.get(), Cond.get(), Inc.get(), LoopVar.get(), S->getRParenLoc()); + if (NewStmt.isInvalid()) + return StmtError(); + } StmtResult Body = getDerived().TransformStmt(S->getBody()); if (Body.isInvalid()) @@ -5932,12 +5997,15 @@ TreeTransform::TransformCXXForRangeStmt(CXXForRangeStmt *S) { // Body has changed but we didn't rebuild the for-range statement. Rebuild // it now so we have a new statement to attach the body to. - if (Body.get() != S->getBody() && NewStmt.get() == S) + if (Body.get() != S->getBody() && NewStmt.get() == S) { NewStmt = getDerived().RebuildCXXForRangeStmt(S->getForLoc(), S->getColonLoc(), Range.get(), BeginEnd.get(), Cond.get(), Inc.get(), LoopVar.get(), S->getRParenLoc()); + if (NewStmt.isInvalid()) + return StmtError(); + } if (NewStmt.get() == S) return SemaRef.Owned(S); @@ -6014,6 +6082,32 @@ TreeTransform::TransformMSDependentExistsStmt( SubStmt.get()); } +template +ExprResult +TreeTransform::TransformMSPropertyRefExpr(MSPropertyRefExpr *E) { + NestedNameSpecifierLoc QualifierLoc; + if (E->getQualifierLoc()) { + QualifierLoc + = getDerived().TransformNestedNameSpecifierLoc(E->getQualifierLoc()); + if (!QualifierLoc) + return ExprError(); + } + + MSPropertyDecl *PD = cast_or_null( + getDerived().TransformDecl(E->getMemberLoc(), E->getPropertyDecl())); + if (!PD) + return ExprError(); + + ExprResult Base = getDerived().TransformExpr(E->getBaseExpr()); + if (Base.isInvalid()) + return ExprError(); + + return new (SemaRef.getASTContext()) + MSPropertyRefExpr(Base.get(), PD, E->isArrow(), + SemaRef.getASTContext().PseudoObjectTy, VK_LValue, + QualifierLoc, E->getMemberLoc()); +} + template StmtResult TreeTransform::TransformSEHTryStmt(SEHTryStmt *S) { @@ -6159,6 +6253,8 @@ TreeTransform::TransformCharacterLiteral(CharacterLiteral *E) { template ExprResult TreeTransform::TransformUserDefinedLiteral(UserDefinedLiteral *E) { + if (FunctionDecl *FD = E->getDirectCallee()) + SemaRef.MarkFunctionReferenced(E->getLocStart(), FD); return SemaRef.MaybeBindToTemporary(E); } @@ -7218,6 +7314,21 @@ TreeTransform::TransformCXXDefaultArgExpr(CXXDefaultArgExpr *E) { return getDerived().RebuildCXXDefaultArgExpr(E->getUsedLocation(), Param); } +template +ExprResult +TreeTransform::TransformCXXDefaultInitExpr(CXXDefaultInitExpr *E) { + FieldDecl *Field + = cast_or_null(getDerived().TransformDecl(E->getLocStart(), + E->getField())); + if (!Field) + return ExprError(); + + if (!getDerived().AlwaysRebuild() && Field == E->getField()) + return SemaRef.Owned(E); + + return getDerived().RebuildCXXDefaultInitExpr(E->getExprLoc(), Field); +} + template ExprResult TreeTransform::TransformCXXScalarValueInitExpr( @@ -8592,16 +8703,11 @@ TreeTransform::TransformObjCEncodeExpr(ObjCEncodeExpr *E) { template ExprResult TreeTransform:: TransformObjCIndirectCopyRestoreExpr(ObjCIndirectCopyRestoreExpr *E) { - ExprResult result = getDerived().TransformExpr(E->getSubExpr()); - if (result.isInvalid()) return ExprError(); - Expr *subExpr = result.take(); - - if (!getDerived().AlwaysRebuild() && - subExpr == E->getSubExpr()) - return SemaRef.Owned(E); - - return SemaRef.Owned(new(SemaRef.Context) - ObjCIndirectCopyRestoreExpr(subExpr, E->getType(), E->shouldCopy())); + // This is a kind of implicit conversion, and it needs to get dropped + // and recomputed for the same general reasons that ImplicitCastExprs + // do, as well a more specific one: this expression is only valid when + // it appears *immediately* as an argument expression. + return getDerived().TransformExpr(E->getSubExpr()); } template @@ -9340,6 +9446,23 @@ TreeTransform::RebuildCXXPseudoDestructorExpr(Expr *Base, /*TemplateArgs*/ 0); } +template +StmtResult +TreeTransform::TransformCapturedStmt(CapturedStmt *S) { + SourceLocation Loc = S->getLocStart(); + unsigned NumParams = S->getCapturedDecl()->getNumParams(); + getSema().ActOnCapturedRegionStart(Loc, /*CurScope*/0, + S->getCapturedRegionKind(), NumParams); + StmtResult Body = getDerived().TransformStmt(S->getCapturedStmt()); + + if (Body.isInvalid()) { + getSema().ActOnCapturedRegionError(); + return StmtError(); + } + + return getSema().ActOnCapturedRegionEnd(Body.take()); +} + } // end namespace clang #endif // LLVM_CLANG_SEMA_TREETRANSFORM_H diff --git a/lib/Serialization/ASTCommon.cpp b/lib/Serialization/ASTCommon.cpp index 7bbe6b18f91..24b268f36df 100644 --- a/lib/Serialization/ASTCommon.cpp +++ b/lib/Serialization/ASTCommon.cpp @@ -119,6 +119,7 @@ serialization::getDefinitiveDeclContext(const DeclContext *DC) { case Decl::CXXConversion: case Decl::ObjCMethod: case Decl::Block: + case Decl::Captured: // Objective C categories, category implementations, and class // implementations can only be defined in one place. case Decl::ObjCCategory: @@ -180,6 +181,7 @@ bool serialization::isRedeclarableDeclKind(unsigned Kind) { case Decl::UnresolvedUsingValue: case Decl::IndirectField: case Decl::Field: + case Decl::MSProperty: case Decl::ObjCIvar: case Decl::ObjCAtDefsField: case Decl::ImplicitParam: @@ -202,6 +204,7 @@ bool serialization::isRedeclarableDeclKind(unsigned Kind) { case Decl::FriendTemplate: case Decl::StaticAssert: case Decl::Block: + case Decl::Captured: case Decl::ClassScopeFunctionSpecialization: case Decl::Import: case Decl::OMPThreadPrivate: diff --git a/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp index d9844152b74..22caeb8656b 100644 --- a/lib/Serialization/ASTReader.cpp +++ b/lib/Serialization/ASTReader.cpp @@ -257,7 +257,8 @@ static bool checkPreprocessorOptions(const PreprocessorOptions &PPOpts, const PreprocessorOptions &ExistingPPOpts, DiagnosticsEngine *Diags, FileManager &FileMgr, - std::string &SuggestedPredefines) { + std::string &SuggestedPredefines, + const LangOptions &LangOpts) { // Check macro definitions. MacroDefinitionsMap ASTFileMacros; collectMacroDefinitions(PPOpts, ASTFileMacros); @@ -323,6 +324,15 @@ static bool checkPreprocessorOptions(const PreprocessorOptions &PPOpts, return true; } + // Detailed record is important since it is used for the module cache hash. + if (LangOpts.Modules && + PPOpts.DetailedRecord != ExistingPPOpts.DetailedRecord) { + if (Diags) { + Diags->Report(diag::err_pch_pp_detailed_record) << PPOpts.DetailedRecord; + } + return true; + } + // Compute the #include and #include_macros lines we need. for (unsigned I = 0, N = ExistingPPOpts.Includes.size(); I != N; ++I) { StringRef File = ExistingPPOpts.Includes[I]; @@ -363,7 +373,8 @@ bool PCHValidator::ReadPreprocessorOptions(const PreprocessorOptions &PPOpts, return checkPreprocessorOptions(PPOpts, ExistingPPOpts, Complain? &Reader.Diags : 0, PP.getFileManager(), - SuggestedPredefines); + SuggestedPredefines, + PP.getLangOpts()); } void PCHValidator::ReadHeaderFileInfo(const HeaderFileInfo &HFI, @@ -428,8 +439,12 @@ ASTSelectorLookupTrait::ReadData(Selector, const unsigned char* d, data_type Result; Result.ID = Reader.getGlobalSelectorID(F, ReadUnalignedLE32(d)); - unsigned NumInstanceMethods = ReadUnalignedLE16(d); - unsigned NumFactoryMethods = ReadUnalignedLE16(d); + unsigned NumInstanceMethodsAndBits = ReadUnalignedLE16(d); + unsigned NumFactoryMethodsAndBits = ReadUnalignedLE16(d); + Result.InstanceBits = NumInstanceMethodsAndBits & 0x3; + Result.FactoryBits = NumFactoryMethodsAndBits & 0x3; + unsigned NumInstanceMethods = NumInstanceMethodsAndBits >> 2; + unsigned NumFactoryMethods = NumFactoryMethodsAndBits >> 2; // Load instance methods for (unsigned I = 0; I != NumInstanceMethods; ++I) { @@ -1088,6 +1103,19 @@ bool ASTReader::ReadBlockAbbrevs(BitstreamCursor &Cursor, unsigned BlockID) { } } +Token ASTReader::ReadToken(ModuleFile &F, const RecordData &Record, + unsigned &Idx) { + Token Tok; + Tok.startToken(); + Tok.setLocation(ReadSourceLocation(F, Record, Idx)); + Tok.setLength(Record[Idx++]); + if (IdentifierInfo *II = getLocalIdentifier(F, Record[Idx++])) + Tok.setIdentifierInfo(II); + Tok.setKind((tok::TokenKind)Record[Idx++]); + Tok.setFlag((Token::TokenFlags)Record[Idx++]); + return Tok; +} + MacroInfo *ASTReader::ReadMacroRecord(ModuleFile &F, uint64_t Offset) { BitstreamCursor &Stream = F.MacroCursor; @@ -1188,14 +1216,8 @@ MacroInfo *ASTReader::ReadMacroRecord(ModuleFile &F, uint64_t Offset) { // erroneous, just pretend we didn't see this. if (Macro == 0) break; - Token Tok; - Tok.startToken(); - Tok.setLocation(ReadSourceLocation(F, Record[0])); - Tok.setLength(Record[1]); - if (IdentifierInfo *II = getLocalIdentifier(F, Record[2])) - Tok.setIdentifierInfo(II); - Tok.setKind((tok::TokenKind)Record[3]); - Tok.setFlag((Token::TokenFlags)Record[4]); + unsigned Idx = 0; + Token Tok = ReadToken(F, Record, Idx); Macro->AddTokenToBody(Tok); break; } @@ -1563,9 +1585,9 @@ void ASTReader::installPCHMacroDirectives(IdentifierInfo *II, } /// \brief For the given macro definitions, check if they are both in system -/// modules and if one of the two is in the clang builtin headers. -static bool isSystemAndClangMacro(MacroInfo *PrevMI, MacroInfo *NewMI, - Module *NewOwner, ASTReader &Reader) { +/// modules. +static bool areDefinedInSystemModules(MacroInfo *PrevMI, MacroInfo *NewMI, + Module *NewOwner, ASTReader &Reader) { assert(PrevMI && NewMI); if (!NewOwner) return false; @@ -1576,22 +1598,7 @@ static bool isSystemAndClangMacro(MacroInfo *PrevMI, MacroInfo *NewMI, return false; if (PrevOwner == NewOwner) return false; - if (!PrevOwner->IsSystem || !NewOwner->IsSystem) - return false; - - SourceManager &SM = Reader.getSourceManager(); - FileID PrevFID = SM.getFileID(PrevMI->getDefinitionLoc()); - FileID NewFID = SM.getFileID(NewMI->getDefinitionLoc()); - const FileEntry *PrevFE = SM.getFileEntryForID(PrevFID); - const FileEntry *NewFE = SM.getFileEntryForID(NewFID); - if (PrevFE == 0 || NewFE == 0) - return false; - - Preprocessor &PP = Reader.getPreprocessor(); - ModuleMap &ModMap = PP.getHeaderSearchInfo().getModuleMap(); - const DirectoryEntry *BuiltinDir = ModMap.getBuiltinIncludeDir(); - - return (PrevFE->getDir() == BuiltinDir) != (NewFE->getDir() == BuiltinDir); + return PrevOwner->IsSystem && NewOwner->IsSystem; } void ASTReader::installImportedMacro(IdentifierInfo *II, MacroDirective *MD, @@ -1607,15 +1614,12 @@ void ASTReader::installImportedMacro(IdentifierInfo *II, MacroDirective *MD, if (NewMI != PrevMI && !PrevMI->isIdenticalTo(*NewMI, PP, /*Syntactically=*/true)) { // Before marking the macros as ambiguous, check if this is a case where - // the system macro uses a not identical definition compared to a macro - // from the clang headers. For example: + // both macros are in system headers. If so, we trust that the system + // did not get it wrong. This also handles cases where Clang's own + // headers have a different spelling of certain system macros: // #define LONG_MAX __LONG_MAX__ (clang's limits.h) // #define LONG_MAX 0x7fffffffffffffffL (system's limits.h) - // in which case don't mark them to avoid the "ambiguous macro expansion" - // warning. - // FIXME: This should go away if the system headers get "fixed" to use - // identical definitions. - if (!isSystemAndClangMacro(PrevMI, NewMI, Owner, *this)) { + if (!areDefinedInSystemModules(PrevMI, NewMI, Owner, *this)) { PrevDef.getDirective()->setAmbiguous(true); DefMD->setAmbiguous(true); } @@ -2754,7 +2758,7 @@ static void moveMethodToBackOfGlobalList(Sema &S, ObjCMethodDecl *Method) { ObjCMethodList &Start = Method->isInstanceMethod()? Known->second.first : Known->second.second; bool Found = false; - for (ObjCMethodList *List = &Start; List; List = List->Next) { + for (ObjCMethodList *List = &Start; List; List = List->getNext()) { if (!Found) { if (List->Method == Method) { Found = true; @@ -2764,8 +2768,8 @@ static void moveMethodToBackOfGlobalList(Sema &S, ObjCMethodDecl *Method) { } } - if (List->Next) - List->Method = List->Next->Method; + if (List->getNext()) + List->Method = List->getNext()->Method; else List->Method = Method; } @@ -3324,10 +3328,10 @@ void ASTReader::finalizeForWriting() { HiddenNamesMap.clear(); } -/// SkipCursorToControlBlock - Given a cursor at the start of an AST file, scan -/// ahead and drop the cursor into the start of the CONTROL_BLOCK, returning -/// false on success and true on failure. -static bool SkipCursorToControlBlock(BitstreamCursor &Cursor) { +/// \brief Given a cursor at the start of an AST file, scan ahead and drop the +/// cursor into the start of the given block ID, returning false on success and +/// true on failure. +static bool SkipCursorToBlock(BitstreamCursor &Cursor, unsigned BlockID) { while (1) { llvm::BitstreamEntry Entry = Cursor.advance(); switch (Entry.Kind) { @@ -3341,8 +3345,8 @@ static bool SkipCursorToControlBlock(BitstreamCursor &Cursor) { break; case llvm::BitstreamEntry::SubBlock: - if (Entry.ID == CONTROL_BLOCK_ID) { - if (Cursor.EnterSubBlock(CONTROL_BLOCK_ID)) + if (Entry.ID == BlockID) { + if (Cursor.EnterSubBlock(BlockID)) return true; // Found it! return false; @@ -3386,7 +3390,7 @@ std::string ASTReader::getOriginalSourceFile(const std::string &ASTFileName, } // Scan for the CONTROL_BLOCK_ID block. - if (SkipCursorToControlBlock(Stream)) { + if (SkipCursorToBlock(Stream, CONTROL_BLOCK_ID)) { Diags.Report(diag::err_fe_pch_malformed_block) << ASTFileName; return std::string(); } @@ -3441,7 +3445,7 @@ namespace { bool Complain, std::string &SuggestedPredefines) { return checkPreprocessorOptions(ExistingPPOpts, PPOpts, 0, FileMgr, - SuggestedPredefines); + SuggestedPredefines, ExistingLangOpts); } }; } @@ -3473,8 +3477,29 @@ bool ASTReader::readASTFileControlBlock(StringRef Filename, } // Scan for the CONTROL_BLOCK_ID block. - if (SkipCursorToControlBlock(Stream)) + if (SkipCursorToBlock(Stream, CONTROL_BLOCK_ID)) return true; + + bool NeedsInputFiles = Listener.needsInputFileVisitation(); + BitstreamCursor InputFilesCursor; + if (NeedsInputFiles) { + InputFilesCursor = Stream; + if (SkipCursorToBlock(InputFilesCursor, INPUT_FILES_BLOCK_ID)) + return true; + + // Read the abbreviations + while (true) { + uint64_t Offset = InputFilesCursor.GetCurrentBitNo(); + unsigned Code = InputFilesCursor.ReadCode(); + + // We expect all abbrevs to be at the start of the block. + if (Code != llvm::bitc::DEFINE_ABBREV) { + InputFilesCursor.JumpToBit(Offset); + break; + } + InputFilesCursor.ReadAbbrevRecord(); + } + } // Scan for ORIGINAL_FILE inside the control block. RecordData Record; @@ -3532,6 +3557,35 @@ bool ASTReader::readASTFileControlBlock(StringRef Filename, break; } + case INPUT_FILE_OFFSETS: { + if (!NeedsInputFiles) + break; + + unsigned NumInputFiles = Record[0]; + unsigned NumUserFiles = Record[1]; + const uint32_t *InputFileOffs = (const uint32_t *)Blob.data(); + for (unsigned I = 0; I != NumInputFiles; ++I) { + // Go find this input file. + bool isSystemFile = I >= NumUserFiles; + BitstreamCursor &Cursor = InputFilesCursor; + SavedStreamPosition SavedPosition(Cursor); + Cursor.JumpToBit(InputFileOffs[I]); + + unsigned Code = Cursor.ReadCode(); + RecordData Record; + StringRef Blob; + bool shouldContinue = false; + switch ((InputFileRecordTypes)Cursor.readRecord(Code, Record, &Blob)) { + case INPUT_FILE: + shouldContinue = Listener.visitInputFile(Blob, isSystemFile); + break; + } + if (!shouldContinue) + break; + } + break; + } + default: // No other validation to perform. break; @@ -3907,6 +3961,7 @@ bool ASTReader::ParseLanguageOptions(const RecordData &Record, LangOpts.CommentOpts.BlockCommandNames.push_back( ReadString(Record, Idx)); } + LangOpts.CommentOpts.ParseAllComments = Record[Idx++]; return Listener.ReadLanguageOptions(LangOpts, Complain); } @@ -4017,6 +4072,7 @@ bool ASTReader::ParsePreprocessorOptions(const RecordData &Record, } PPOpts.UsePredefines = Record[Idx++]; + PPOpts.DetailedRecord = Record[Idx++]; PPOpts.ImplicitPCHInclude = ReadString(Record, Idx); PPOpts.ImplicitPTHInclude = ReadString(Record, Idx); PPOpts.ObjCXXARCStandardLibrary = @@ -4639,8 +4695,12 @@ QualType ASTReader::readTypeRecord(unsigned Index) { return Context.getUnaryTransformType(BaseType, UnderlyingType, UKind); } - case TYPE_AUTO: - return Context.getAutoType(readType(*Loc.F, Record, Idx)); + case TYPE_AUTO: { + QualType Deduced = readType(*Loc.F, Record, Idx); + bool IsDecltypeAuto = Record[Idx++]; + bool IsDependent = Deduced.isNull() ? Record[Idx++] : false; + return Context.getAutoType(Deduced, IsDecltypeAuto, IsDependent); + } case TYPE_RECORD: { if (Record.size() != 2) { @@ -6015,8 +6075,8 @@ void ASTReader::InitializeSema(Sema &S) { // Makes sure any declarations that were deserialized "too early" // still get added to the identifier's declaration chains. for (unsigned I = 0, N = PreloadedDecls.size(); I != N; ++I) { - NamedDecl *ND = cast(PreloadedDecls[I]->getMostRecentDecl()); - SemaObj->pushExternalDeclIntoScope(ND, PreloadedDecls[I]->getDeclName()); + pushExternalDeclIntoScope(PreloadedDecls[I], + PreloadedDecls[I]->getDeclName()); } PreloadedDecls.clear(); @@ -6122,7 +6182,10 @@ StringRef ASTIdentifierIterator::Next() { return Result; } -IdentifierIterator *ASTReader::getIdentifiers() const { +IdentifierIterator *ASTReader::getIdentifiers() { + if (!loadGlobalIndex()) + return GlobalIndex->createIdentifierIterator(); + return new ASTIdentifierIterator(*this); } @@ -6131,13 +6194,16 @@ namespace clang { namespace serialization { ASTReader &Reader; Selector Sel; unsigned PriorGeneration; + unsigned InstanceBits; + unsigned FactoryBits; SmallVector InstanceMethods; SmallVector FactoryMethods; public: ReadMethodPoolVisitor(ASTReader &Reader, Selector Sel, unsigned PriorGeneration) - : Reader(Reader), Sel(Sel), PriorGeneration(PriorGeneration) { } + : Reader(Reader), Sel(Sel), PriorGeneration(PriorGeneration), + InstanceBits(0), FactoryBits(0) { } static bool visit(ModuleFile &M, void *UserData) { ReadMethodPoolVisitor *This @@ -6170,6 +6236,8 @@ namespace clang { namespace serialization { This->InstanceMethods.append(Data.Instance.begin(), Data.Instance.end()); This->FactoryMethods.append(Data.Factory.begin(), Data.Factory.end()); + This->InstanceBits = Data.InstanceBits; + This->FactoryBits = Data.FactoryBits; return true; } @@ -6182,6 +6250,9 @@ namespace clang { namespace serialization { ArrayRef getFactoryMethods() const { return FactoryMethods; } + + unsigned getInstanceBits() const { return InstanceBits; } + unsigned getFactoryBits() const { return FactoryBits; } }; } } // end namespace clang::serialization @@ -6219,6 +6290,8 @@ void ASTReader::ReadMethodPool(Selector Sel) { addMethodsToPool(S, Visitor.getInstanceMethods(), Pos->second.first); addMethodsToPool(S, Visitor.getFactoryMethods(), Pos->second.second); + Pos->second.first.setBits(Visitor.getInstanceBits()); + Pos->second.second.setBits(Visitor.getFactoryBits()); } void ASTReader::ReadKnownNamespaces( @@ -6417,8 +6490,7 @@ ASTReader::SetGloballyVisibleDecls(IdentifierInfo *II, // Introduce this declaration into the translation-unit scope // and add it to the declaration chain for this identifier, so // that (unqualified) name lookup will find it. - NamedDecl *ND = cast(D->getMostRecentDecl()); - SemaObj->pushExternalDeclIntoScope(ND, II); + pushExternalDeclIntoScope(D, II); } else { // Queue this declaration so that it will be added to the // translation unit scope and identifier's declaration chain @@ -7165,9 +7237,9 @@ void ASTReader::ReadComments() { (RawComment::CommentKind) Record[Idx++]; bool IsTrailingComment = Record[Idx++]; bool IsAlmostTrailingComment = Record[Idx++]; - Comments.push_back(new (Context) RawComment(SR, Kind, - IsTrailingComment, - IsAlmostTrailingComment)); + Comments.push_back(new (Context) RawComment( + SR, Kind, IsTrailingComment, IsAlmostTrailingComment, + Context.getLangOpts().CommentOpts.ParseAllComments)); break; } } @@ -7205,8 +7277,7 @@ void ASTReader::finishPendingActions() { TLD != TLDEnd; ++TLD) { IdentifierInfo *II = TLD->first; for (unsigned I = 0, N = TLD->second.size(); I != N; ++I) { - NamedDecl *ND = cast(TLD->second[I]->getMostRecentDecl()); - SemaObj->pushExternalDeclIntoScope(ND, II); + pushExternalDeclIntoScope(cast(TLD->second[I]), II); } } @@ -7346,6 +7417,21 @@ void ASTReader::FinishedDeserializing() { } } +void ASTReader::pushExternalDeclIntoScope(NamedDecl *D, DeclarationName Name) { + D = cast(D->getMostRecentDecl()); + + if (SemaObj->IdResolver.tryAddTopLevelDecl(D, Name) && SemaObj->TUScope) { + SemaObj->TUScope->AddDecl(D); + } else if (SemaObj->TUScope) { + // Adding the decl to IdResolver may have failed because it was already in + // (even though it was not added in scope). If it is already in, make sure + // it gets in the scope as well. + if (std::find(SemaObj->IdResolver.begin(Name), + SemaObj->IdResolver.end(), D) != SemaObj->IdResolver.end()) + SemaObj->TUScope->AddDecl(D); + } +} + ASTReader::ASTReader(Preprocessor &PP, ASTContext &Context, StringRef isysroot, bool DisableValidation, bool AllowASTWithCompilerErrors, bool UseGlobalIndex) diff --git a/lib/Serialization/ASTReaderDecl.cpp b/lib/Serialization/ASTReaderDecl.cpp index 0fbdd7e5dae..f7fa818e9b3 100644 --- a/lib/Serialization/ASTReaderDecl.cpp +++ b/lib/Serialization/ASTReaderDecl.cpp @@ -244,6 +244,7 @@ namespace clang { void VisitCXXDestructorDecl(CXXDestructorDecl *D); void VisitCXXConversionDecl(CXXConversionDecl *D); void VisitFieldDecl(FieldDecl *FD); + void VisitMSPropertyDecl(MSPropertyDecl *FD); void VisitIndirectFieldDecl(IndirectFieldDecl *FD); void VisitVarDecl(VarDecl *VD); void VisitImplicitParamDecl(ImplicitParamDecl *PD); @@ -265,6 +266,7 @@ namespace clang { void VisitFriendTemplateDecl(FriendTemplateDecl *D); void VisitStaticAssertDecl(StaticAssertDecl *D); void VisitBlockDecl(BlockDecl *BD); + void VisitCapturedDecl(CapturedDecl *CD); void VisitEmptyDecl(EmptyDecl *D); std::pair VisitDeclContext(DeclContext *DC); @@ -847,6 +849,7 @@ void ASTDeclReader::VisitObjCCategoryImplDecl(ObjCCategoryImplDecl *D) { void ASTDeclReader::VisitObjCImplementationDecl(ObjCImplementationDecl *D) { VisitObjCImplDecl(D); D->setSuperClass(ReadDeclAs(Record, Idx)); + D->SuperLoc = ReadSourceLocation(Record, Idx); D->setIvarLBraceLoc(ReadSourceLocation(Record, Idx)); D->setIvarRBraceLoc(ReadSourceLocation(Record, Idx)); D->setHasNonZeroConstructors(Record[Idx++]); @@ -879,6 +882,12 @@ void ASTDeclReader::VisitFieldDecl(FieldDecl *FD) { } } +void ASTDeclReader::VisitMSPropertyDecl(MSPropertyDecl *PD) { + VisitDeclaratorDecl(PD); + PD->GetterId = Reader.GetIdentifierInfo(F, Record, Idx); + PD->SetterId = Reader.GetIdentifierInfo(F, Record, Idx); +} + void ASTDeclReader::VisitIndirectFieldDecl(IndirectFieldDecl *FD) { VisitValueDecl(FD); @@ -895,7 +904,7 @@ void ASTDeclReader::VisitVarDecl(VarDecl *VD) { VisitDeclaratorDecl(VD); VD->VarDeclBits.SClass = (StorageClass)Record[Idx++]; - VD->VarDeclBits.ThreadSpecified = Record[Idx++]; + VD->VarDeclBits.TSCSpec = Record[Idx++]; VD->VarDeclBits.InitStyle = Record[Idx++]; VD->VarDeclBits.ExceptionVar = Record[Idx++]; VD->VarDeclBits.NRVOVariable = Record[Idx++]; @@ -987,6 +996,13 @@ void ASTDeclReader::VisitBlockDecl(BlockDecl *BD) { captures.end(), capturesCXXThis); } +void ASTDeclReader::VisitCapturedDecl(CapturedDecl *CD) { + VisitDecl(CD); + // Body is set by VisitCapturedStmt. + for (unsigned i = 0; i < CD->NumParams; ++i) + CD->setParam(i, ReadDeclAs(Record, Idx)); +} + void ASTDeclReader::VisitLinkageSpecDecl(LinkageSpecDecl *D) { VisitDecl(D); D->setLanguage((LinkageSpecDecl::LanguageIDs)Record[Idx++]); @@ -2136,6 +2152,12 @@ Decl *ASTReader::ReadDeclRecord(DeclID ID) { case DECL_BLOCK: D = BlockDecl::CreateDeserialized(Context, ID); break; + case DECL_MS_PROPERTY: + D = MSPropertyDecl::CreateDeserialized(Context, ID); + break; + case DECL_CAPTURED: + D = CapturedDecl::CreateDeserialized(Context, ID, Record[Idx++]); + break; case DECL_CXX_BASE_SPECIFIERS: Error("attempt to read a C++ base-specifier record as a declaration"); return 0; diff --git a/lib/Serialization/ASTReaderInternals.h b/lib/Serialization/ASTReaderInternals.h index 327da4403a3..9149b18480f 100644 --- a/lib/Serialization/ASTReaderInternals.h +++ b/lib/Serialization/ASTReaderInternals.h @@ -152,6 +152,8 @@ class ASTSelectorLookupTrait { public: struct data_type { SelectorID ID; + unsigned InstanceBits; + unsigned FactoryBits; SmallVector Instance; SmallVector Factory; }; diff --git a/lib/Serialization/ASTReaderStmt.cpp b/lib/Serialization/ASTReaderStmt.cpp index 078ecb7a06d..e1357ba5e69 100644 --- a/lib/Serialization/ASTReaderStmt.cpp +++ b/lib/Serialization/ASTReaderStmt.cpp @@ -17,6 +17,7 @@ #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/StmtVisitor.h" +#include "clang/Lex/Token.h" #include "llvm/ADT/SmallString.h" using namespace clang; using namespace clang::serialization; @@ -32,14 +33,22 @@ namespace clang { const ASTReader::RecordData &Record; unsigned &Idx; + Token ReadToken(const RecordData &R, unsigned &I) { + return Reader.ReadToken(F, R, I); + } + SourceLocation ReadSourceLocation(const RecordData &R, unsigned &I) { return Reader.ReadSourceLocation(F, R, I); } - + SourceRange ReadSourceRange(const RecordData &R, unsigned &I) { return Reader.ReadSourceRange(F, R, I); } - + + std::string ReadString(const RecordData &R, unsigned &I) { + return Reader.ReadString(R, I); + } + TypeSourceInfo *GetTypeSourceInfo(const RecordData &R, unsigned &I) { return Reader.GetTypeSourceInfo(F, R, I); } @@ -286,18 +295,25 @@ void ASTStmtReader::VisitDeclStmt(DeclStmt *S) { } } -void ASTStmtReader::VisitGCCAsmStmt(GCCAsmStmt *S) { +void ASTStmtReader::VisitAsmStmt(AsmStmt *S) { VisitStmt(S); - unsigned NumOutputs = Record[Idx++]; - unsigned NumInputs = Record[Idx++]; - unsigned NumClobbers = Record[Idx++]; + S->NumOutputs = Record[Idx++]; + S->NumInputs = Record[Idx++]; + S->NumClobbers = Record[Idx++]; S->setAsmLoc(ReadSourceLocation(Record, Idx)); - S->setRParenLoc(ReadSourceLocation(Record, Idx)); S->setVolatile(Record[Idx++]); S->setSimple(Record[Idx++]); +} +void ASTStmtReader::VisitGCCAsmStmt(GCCAsmStmt *S) { + VisitAsmStmt(S); + S->setRParenLoc(ReadSourceLocation(Record, Idx)); S->setAsmString(cast_or_null(Reader.ReadSubStmt())); + unsigned NumOutputs = S->getNumOutputs(); + unsigned NumInputs = S->getNumInputs(); + unsigned NumClobbers = S->getNumClobbers(); + // Outputs and inputs SmallVector Names; SmallVector Constraints; @@ -320,8 +336,75 @@ void ASTStmtReader::VisitGCCAsmStmt(GCCAsmStmt *S) { } void ASTStmtReader::VisitMSAsmStmt(MSAsmStmt *S) { - // FIXME: Statement reader not yet implemented for MS style inline asm. + VisitAsmStmt(S); + S->LBraceLoc = ReadSourceLocation(Record, Idx); + S->EndLoc = ReadSourceLocation(Record, Idx); + S->NumAsmToks = Record[Idx++]; + std::string AsmStr = ReadString(Record, Idx); + + // Read the tokens. + SmallVector AsmToks; + AsmToks.reserve(S->NumAsmToks); + for (unsigned i = 0, e = S->NumAsmToks; i != e; ++i) { + AsmToks.push_back(ReadToken(Record, Idx)); + } + + // The calls to reserve() for the FooData vectors are mandatory to + // prevent dead StringRefs in the Foo vectors. + + // Read the clobbers. + SmallVector ClobbersData; + SmallVector Clobbers; + ClobbersData.reserve(S->NumClobbers); + Clobbers.reserve(S->NumClobbers); + for (unsigned i = 0, e = S->NumClobbers; i != e; ++i) { + ClobbersData.push_back(ReadString(Record, Idx)); + Clobbers.push_back(ClobbersData.back()); + } + + // Read the operands. + unsigned NumOperands = S->NumOutputs + S->NumInputs; + SmallVector Exprs; + SmallVector ConstraintsData; + SmallVector Constraints; + Exprs.reserve(NumOperands); + ConstraintsData.reserve(NumOperands); + Constraints.reserve(NumOperands); + for (unsigned i = 0; i != NumOperands; ++i) { + Exprs.push_back(cast(Reader.ReadSubStmt())); + ConstraintsData.push_back(ReadString(Record, Idx)); + Constraints.push_back(ConstraintsData.back()); + } + + S->initialize(Reader.getContext(), AsmStr, AsmToks, + Constraints, Exprs, Clobbers); +} + +void ASTStmtReader::VisitCapturedStmt(CapturedStmt *S) { VisitStmt(S); + S->setCapturedDecl(ReadDeclAs(Record, Idx)); + S->setCapturedRegionKind(static_cast(Record[Idx++])); + S->setCapturedRecordDecl(ReadDeclAs(Record, Idx)); + + // Capture inits + for (CapturedStmt::capture_init_iterator I = S->capture_init_begin(), + E = S->capture_init_end(); + I != E; ++I) + *I = Reader.ReadSubExpr(); + + // Body + S->setCapturedStmt(Reader.ReadSubStmt()); + S->getCapturedDecl()->setBody(S->getCapturedStmt()); + + // Captures + for (CapturedStmt::capture_iterator I = S->capture_begin(), + E = S->capture_end(); + I != E; ++I) { + I->VarAndKind.setPointer(ReadDeclAs(Record, Idx)); + I->VarAndKind + .setInt(static_cast(Record[Idx++])); + I->Loc = ReadSourceLocation(Record, Idx); + } } void ASTStmtReader::VisitExpr(Expr *E) { @@ -1226,6 +1309,12 @@ void ASTStmtReader::VisitCXXDefaultArgExpr(CXXDefaultArgExpr *E) { E->Loc = ReadSourceLocation(Record, Idx); } +void ASTStmtReader::VisitCXXDefaultInitExpr(CXXDefaultInitExpr *E) { + VisitExpr(E); + E->Field = ReadDeclAs(Record, Idx); + E->Loc = ReadSourceLocation(Record, Idx); +} + void ASTStmtReader::VisitCXXBindTemporaryExpr(CXXBindTemporaryExpr *E) { VisitExpr(E); E->setTemporary(Reader.ReadCXXTemporary(F, Record, Idx)); @@ -1498,6 +1587,15 @@ void ASTStmtReader::VisitOpaqueValueExpr(OpaqueValueExpr *E) { //===----------------------------------------------------------------------===// // Microsoft Expressions and Statements //===----------------------------------------------------------------------===// +void ASTStmtReader::VisitMSPropertyRefExpr(MSPropertyRefExpr *E) { + VisitExpr(E); + E->IsArrow = (Record[Idx++] != 0); + E->BaseExpr = Reader.ReadSubExpr(); + E->QualifierLoc = Reader.ReadNestedNameSpecifierLoc(F, Record, Idx); + E->MemberLoc = ReadSourceLocation(Record, Idx); + E->TheDecl = ReadDeclAs(Record, Idx); +} + void ASTStmtReader::VisitCXXUuidofExpr(CXXUuidofExpr *E) { VisitExpr(E); E->setSourceRange(ReadSourceRange(Record, Idx)); @@ -1715,6 +1813,11 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) { S = new (Context) MSAsmStmt(Empty); break; + case STMT_CAPTURED: + S = CapturedStmt::CreateDeserialized(Context, + Record[ASTStmtReader::NumExprFields]); + break; + case EXPR_PREDEFINED: S = new (Context) PredefinedExpr(Empty); break; @@ -2073,6 +2176,9 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) { case EXPR_CXX_UUIDOF_EXPR: S = new (Context) CXXUuidofExpr(Empty, true); break; + case EXPR_CXX_PROPERTY_REF_EXPR: + S = new (Context) MSPropertyRefExpr(Empty); + break; case EXPR_CXX_UUIDOF_TYPE: S = new (Context) CXXUuidofExpr(Empty, false); break; @@ -2091,6 +2197,9 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) { S = new (Context) CXXDefaultArgExpr(Empty); break; } + case EXPR_CXX_DEFAULT_INIT: + S = new (Context) CXXDefaultInitExpr(Empty); + break; case EXPR_CXX_BIND_TEMPORARY: S = new (Context) CXXBindTemporaryExpr(Empty); break; diff --git a/lib/Serialization/ASTWriter.cpp b/lib/Serialization/ASTWriter.cpp index cf93d1cf01a..b8ada04e5d8 100644 --- a/lib/Serialization/ASTWriter.cpp +++ b/lib/Serialization/ASTWriter.cpp @@ -245,6 +245,9 @@ void ASTTypeWriter::VisitUnaryTransformType(const UnaryTransformType *T) { void ASTTypeWriter::VisitAutoType(const AutoType *T) { Writer.AddTypeRef(T->getDeducedType(), Record); + Record.push_back(T->isDecltypeAuto()); + if (T->getDeducedType().isNull()) + Record.push_back(T->isDependentType()); Code = TYPE_AUTO; } @@ -907,6 +910,7 @@ void ASTWriter::WriteBlockInfoBlock() { RECORD(DECL_OBJC_PROPERTY); RECORD(DECL_OBJC_PROPERTY_IMPL); RECORD(DECL_FIELD); + RECORD(DECL_MS_PROPERTY); RECORD(DECL_VAR); RECORD(DECL_IMPLICIT_PARAM); RECORD(DECL_PARM_VAR); @@ -1069,6 +1073,7 @@ void ASTWriter::WriteControlBlock(Preprocessor &PP, ASTContext &Context, I != IEnd; ++I) { AddString(*I, Record); } + Record.push_back(LangOpts.CommentOpts.ParseAllComments); Stream.EmitRecord(LANGUAGE_OPTIONS, Record); @@ -1167,6 +1172,8 @@ void ASTWriter::WriteControlBlock(Preprocessor &PP, ASTContext &Context, AddString(PPOpts.MacroIncludes[I], Record); Record.push_back(PPOpts.UsePredefines); + // Detailed record is important since it is used for the module cache hash. + Record.push_back(PPOpts.DetailedRecord); AddString(PPOpts.ImplicitPCHInclude, Record); AddString(PPOpts.ImplicitPTHInclude, Record); Record.push_back(static_cast(PPOpts.ObjCXXARCStandardLibrary)); @@ -2005,18 +2012,7 @@ void ASTWriter::WritePreprocessor(const Preprocessor &PP, bool IsModule) { // tokens in it because they are created by the parser, and thus can't // be in a macro definition. const Token &Tok = MI->getReplacementToken(TokNo); - - Record.push_back(Tok.getLocation().getRawEncoding()); - Record.push_back(Tok.getLength()); - - // FIXME: When reading literal tokens, reconstruct the literal pointer - // if it is needed. - AddIdentifierRef(Tok.getIdentifierInfo(), Record); - // FIXME: Should translate token kind to a stable encoding. - Record.push_back(Tok.getKind()); - // FIXME: Should translate token flags to a stable encoding. - Record.push_back(Tok.getFlags()); - + AddToken(Tok, Record); Stream.EmitRecord(PP_TOKEN, Record); Record.clear(); } @@ -2690,11 +2686,11 @@ class ASTMethodPoolTrait { clang::io::Emit16(Out, KeyLen); unsigned DataLen = 4 + 2 + 2; // 2 bytes for each of the method counts for (const ObjCMethodList *Method = &Methods.Instance; Method; - Method = Method->Next) + Method = Method->getNext()) if (Method->Method) DataLen += 4; for (const ObjCMethodList *Method = &Methods.Factory; Method; - Method = Method->Next) + Method = Method->getNext()) if (Method->Method) DataLen += 4; clang::io::Emit16(Out, DataLen); @@ -2720,24 +2716,31 @@ class ASTMethodPoolTrait { clang::io::Emit32(Out, Methods.ID); unsigned NumInstanceMethods = 0; for (const ObjCMethodList *Method = &Methods.Instance; Method; - Method = Method->Next) + Method = Method->getNext()) if (Method->Method) ++NumInstanceMethods; unsigned NumFactoryMethods = 0; for (const ObjCMethodList *Method = &Methods.Factory; Method; - Method = Method->Next) + Method = Method->getNext()) if (Method->Method) ++NumFactoryMethods; - clang::io::Emit16(Out, NumInstanceMethods); - clang::io::Emit16(Out, NumFactoryMethods); + unsigned InstanceBits = Methods.Instance.getBits(); + assert(InstanceBits < 4); + unsigned NumInstanceMethodsAndBits = + (NumInstanceMethods << 2) | InstanceBits; + unsigned FactoryBits = Methods.Factory.getBits(); + assert(FactoryBits < 4); + unsigned NumFactoryMethodsAndBits = (NumFactoryMethods << 2) | FactoryBits; + clang::io::Emit16(Out, NumInstanceMethodsAndBits); + clang::io::Emit16(Out, NumFactoryMethodsAndBits); for (const ObjCMethodList *Method = &Methods.Instance; Method; - Method = Method->Next) + Method = Method->getNext()) if (Method->Method) clang::io::Emit32(Out, Writer.getDeclID(Method->Method)); for (const ObjCMethodList *Method = &Methods.Factory; Method; - Method = Method->Next) + Method = Method->getNext()) if (Method->Method) clang::io::Emit32(Out, Writer.getDeclID(Method->Method)); @@ -2786,12 +2789,12 @@ void ASTWriter::WriteSelectors(Sema &SemaRef) { // Selector already exists. Did it change? bool changed = false; for (ObjCMethodList *M = &Data.Instance; !changed && M && M->Method; - M = M->Next) { + M = M->getNext()) { if (!M->Method->isFromASTFile()) changed = true; } for (ObjCMethodList *M = &Data.Factory; !changed && M && M->Method; - M = M->Next) { + M = M->getNext()) { if (!M->Method->isFromASTFile()) changed = true; } @@ -3096,7 +3099,28 @@ class ASTIdentifierTableTrait { for (SmallVector::reverse_iterator D = Decls.rbegin(), DEnd = Decls.rend(); D != DEnd; ++D) - clang::io::Emit32(Out, Writer.getDeclID(*D)); + clang::io::Emit32(Out, Writer.getDeclID(getMostRecentLocalDecl(*D))); + } + + /// \brief Returns the most recent local decl or the given decl if there are + /// no local ones. The given decl is assumed to be the most recent one. + Decl *getMostRecentLocalDecl(Decl *Orig) { + // The only way a "from AST file" decl would be more recent from a local one + // is if it came from a module. + if (!PP.getLangOpts().Modules) + return Orig; + + // Look for a local in the decl chain. + for (Decl *D = Orig; D; D = D->getPreviousDecl()) { + if (!D->isFromASTFile()) + return D; + // If we come up a decl from a (chained-)PCH stop since we won't find a + // local one. + if (D->getOwningModuleID() == 0) + break; + } + + return Orig; } }; } // end anonymous namespace @@ -3626,6 +3650,19 @@ void ASTWriter::WriteAttributes(ArrayRef Attrs, } } +void ASTWriter::AddToken(const Token &Tok, RecordDataImpl &Record) { + AddSourceLocation(Tok.getLocation(), Record); + Record.push_back(Tok.getLength()); + + // FIXME: When reading literal tokens, reconstruct the literal pointer + // if it is needed. + AddIdentifierRef(Tok.getIdentifierInfo(), Record); + // FIXME: Should translate token kind to a stable encoding. + Record.push_back(Tok.getKind()); + // FIXME: Should translate token flags to a stable encoding. + Record.push_back(Tok.getFlags()); +} + void ASTWriter::AddString(StringRef Str, RecordDataImpl &Record) { Record.push_back(Str.size()); Record.insert(Record.end(), Str.begin(), Str.end()); diff --git a/lib/Serialization/ASTWriterDecl.cpp b/lib/Serialization/ASTWriterDecl.cpp index 023599d0db8..67349db687d 100644 --- a/lib/Serialization/ASTWriterDecl.cpp +++ b/lib/Serialization/ASTWriterDecl.cpp @@ -81,6 +81,7 @@ namespace clang { void VisitCXXDestructorDecl(CXXDestructorDecl *D); void VisitCXXConversionDecl(CXXConversionDecl *D); void VisitFieldDecl(FieldDecl *D); + void VisitMSPropertyDecl(MSPropertyDecl *D); void VisitIndirectFieldDecl(IndirectFieldDecl *D); void VisitVarDecl(VarDecl *D); void VisitImplicitParamDecl(ImplicitParamDecl *D); @@ -102,6 +103,7 @@ namespace clang { void VisitFriendTemplateDecl(FriendTemplateDecl *D); void VisitStaticAssertDecl(StaticAssertDecl *D); void VisitBlockDecl(BlockDecl *D); + void VisitCapturedDecl(CapturedDecl *D); void VisitEmptyDecl(EmptyDecl *D); void VisitDeclContext(DeclContext *DC, uint64_t LexicalOffset, @@ -611,6 +613,7 @@ void ASTDeclWriter::VisitObjCCategoryImplDecl(ObjCCategoryImplDecl *D) { void ASTDeclWriter::VisitObjCImplementationDecl(ObjCImplementationDecl *D) { VisitObjCImplDecl(D); Writer.AddDeclRef(D->getSuperClass(), Record); + Writer.AddSourceLocation(D->getSuperClassLoc(), Record); Writer.AddSourceLocation(D->getIvarLBraceLoc(), Record); Writer.AddSourceLocation(D->getIvarRBraceLoc(), Record); Record.push_back(D->hasNonZeroConstructors()); @@ -662,6 +665,13 @@ void ASTDeclWriter::VisitFieldDecl(FieldDecl *D) { Code = serialization::DECL_FIELD; } +void ASTDeclWriter::VisitMSPropertyDecl(MSPropertyDecl *D) { + VisitDeclaratorDecl(D); + Writer.AddIdentifierRef(D->getGetterId(), Record); + Writer.AddIdentifierRef(D->getSetterId(), Record); + Code = serialization::DECL_MS_PROPERTY; +} + void ASTDeclWriter::VisitIndirectFieldDecl(IndirectFieldDecl *D) { VisitValueDecl(D); Record.push_back(D->getChainingSize()); @@ -677,7 +687,7 @@ void ASTDeclWriter::VisitVarDecl(VarDecl *D) { VisitRedeclarable(D); VisitDeclaratorDecl(D); Record.push_back(D->getStorageClass()); - Record.push_back(D->isThreadSpecified()); + Record.push_back(D->getTSCSpec()); Record.push_back(D->getInitStyle()); Record.push_back(D->isExceptionVariable()); Record.push_back(D->isNRVOVariable()); @@ -766,7 +776,7 @@ void ASTDeclWriter::VisitParmVarDecl(ParmVarDecl *D) { // Check things we know are true of *every* PARM_VAR_DECL, which is more than // just us assuming it. - assert(!D->isThreadSpecified() && "PARM_VAR_DECL can't be __thread"); + assert(!D->getTSCSpec() && "PARM_VAR_DECL can't use TLS"); assert(D->getAccess() == AS_none && "PARM_VAR_DECL can't be public/private"); assert(!D->isExceptionVariable() && "PARM_VAR_DECL can't be exception var"); assert(D->getPreviousDecl() == 0 && "PARM_VAR_DECL can't be redecl"); @@ -816,6 +826,15 @@ void ASTDeclWriter::VisitBlockDecl(BlockDecl *D) { Code = serialization::DECL_BLOCK; } +void ASTDeclWriter::VisitCapturedDecl(CapturedDecl *CD) { + Record.push_back(CD->getNumParams()); + VisitDecl(CD); + // Body is stored by VisitCapturedStmt. + for (unsigned i = 0; i < CD->getNumParams(); ++i) + Writer.AddDeclRef(CD->getParam(i), Record); + Code = serialization::DECL_CAPTURED; +} + void ASTDeclWriter::VisitLinkageSpecDecl(LinkageSpecDecl *D) { VisitDecl(D); Record.push_back(D->getLanguage()); @@ -1515,7 +1534,7 @@ void ASTWriter::WriteDeclsBlockAbbrevs() { Abv->Add(BitCodeAbbrevOp(0)); // hasExtInfo // VarDecl Abv->Add(BitCodeAbbrevOp(0)); // StorageClass - Abv->Add(BitCodeAbbrevOp(0)); // isThreadSpecified + Abv->Add(BitCodeAbbrevOp(0)); // getTSCSpec Abv->Add(BitCodeAbbrevOp(0)); // hasCXXDirectInitializer Abv->Add(BitCodeAbbrevOp(0)); // isExceptionVariable Abv->Add(BitCodeAbbrevOp(0)); // isNRVOVariable @@ -1594,7 +1613,7 @@ void ASTWriter::WriteDeclsBlockAbbrevs() { Abv->Add(BitCodeAbbrevOp(0)); // hasExtInfo // VarDecl Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // StorageClass - Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // isThreadSpecified + Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 2)); // getTSCSpec Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // CXXDirectInitializer Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // isExceptionVariable Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // isNRVOVariable diff --git a/lib/Serialization/ASTWriterStmt.cpp b/lib/Serialization/ASTWriterStmt.cpp index b6f1d54d407..5f7ac01bae9 100644 --- a/lib/Serialization/ASTWriterStmt.cpp +++ b/lib/Serialization/ASTWriterStmt.cpp @@ -17,6 +17,7 @@ #include "clang/AST/DeclObjC.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/StmtVisitor.h" +#include "clang/Lex/Token.h" #include "llvm/Bitcode/BitstreamWriter.h" using namespace clang; @@ -216,15 +217,19 @@ void ASTStmtWriter::VisitDeclStmt(DeclStmt *S) { Code = serialization::STMT_DECL; } -void ASTStmtWriter::VisitGCCAsmStmt(GCCAsmStmt *S) { +void ASTStmtWriter::VisitAsmStmt(AsmStmt *S) { VisitStmt(S); Record.push_back(S->getNumOutputs()); Record.push_back(S->getNumInputs()); Record.push_back(S->getNumClobbers()); Writer.AddSourceLocation(S->getAsmLoc(), Record); - Writer.AddSourceLocation(S->getRParenLoc(), Record); Record.push_back(S->isVolatile()); Record.push_back(S->isSimple()); +} + +void ASTStmtWriter::VisitGCCAsmStmt(GCCAsmStmt *S) { + VisitAsmStmt(S); + Writer.AddSourceLocation(S->getRParenLoc(), Record); Writer.AddStmt(S->getAsmString()); // Outputs @@ -249,12 +254,72 @@ void ASTStmtWriter::VisitGCCAsmStmt(GCCAsmStmt *S) { } void ASTStmtWriter::VisitMSAsmStmt(MSAsmStmt *S) { - // FIXME: Statement writer not yet implemented for MS style inline asm. - VisitStmt(S); + VisitAsmStmt(S); + Writer.AddSourceLocation(S->getLBraceLoc(), Record); + Writer.AddSourceLocation(S->getEndLoc(), Record); + Record.push_back(S->getNumAsmToks()); + Writer.AddString(S->getAsmString(), Record); + + // Tokens + for (unsigned I = 0, N = S->getNumAsmToks(); I != N; ++I) { + Writer.AddToken(S->getAsmToks()[I], Record); + } + + // Clobbers + for (unsigned I = 0, N = S->getNumClobbers(); I != N; ++I) { + Writer.AddString(S->getClobber(I), Record); + } + + // Outputs + for (unsigned I = 0, N = S->getNumOutputs(); I != N; ++I) { + Writer.AddStmt(S->getOutputExpr(I)); + Writer.AddString(S->getOutputConstraint(I), Record); + } + + // Inputs + for (unsigned I = 0, N = S->getNumInputs(); I != N; ++I) { + Writer.AddStmt(S->getInputExpr(I)); + Writer.AddString(S->getInputConstraint(I), Record); + } Code = serialization::STMT_MSASM; } +void ASTStmtWriter::VisitCapturedStmt(CapturedStmt *S) { + VisitStmt(S); + // NumCaptures + Record.push_back(std::distance(S->capture_begin(), S->capture_end())); + + // CapturedDecl and captured region kind + Writer.AddDeclRef(S->getCapturedDecl(), Record); + Record.push_back(S->getCapturedRegionKind()); + + Writer.AddDeclRef(S->getCapturedRecordDecl(), Record); + + // Capture inits + for (CapturedStmt::capture_init_iterator I = S->capture_init_begin(), + E = S->capture_init_end(); + I != E; ++I) + Writer.AddStmt(*I); + + // Body + Writer.AddStmt(S->getCapturedStmt()); + + // Captures + for (CapturedStmt::capture_iterator I = S->capture_begin(), + E = S->capture_end(); + I != E; ++I) { + if (I->capturesThis()) + Writer.AddDeclRef(0, Record); + else + Writer.AddDeclRef(I->getCapturedVar(), Record); + Record.push_back(I->getCaptureKind()); + Writer.AddSourceLocation(I->getLocation(), Record); + } + + Code = serialization::STMT_CAPTURED; +} + void ASTStmtWriter::VisitExpr(Expr *E) { VisitStmt(E); Writer.AddTypeRef(E->getType(), Record); @@ -1216,6 +1281,13 @@ void ASTStmtWriter::VisitCXXDefaultArgExpr(CXXDefaultArgExpr *E) { Code = serialization::EXPR_CXX_DEFAULT_ARG; } +void ASTStmtWriter::VisitCXXDefaultInitExpr(CXXDefaultInitExpr *E) { + VisitExpr(E); + Writer.AddDeclRef(E->getField(), Record); + Writer.AddSourceLocation(E->getExprLoc(), Record); + Code = serialization::EXPR_CXX_DEFAULT_INIT; +} + void ASTStmtWriter::VisitCXXBindTemporaryExpr(CXXBindTemporaryExpr *E) { VisitExpr(E); Writer.AddCXXTemporary(E->getTemporary(), Record); @@ -1534,6 +1606,16 @@ void ASTStmtWriter::VisitAsTypeExpr(AsTypeExpr *E) { //===----------------------------------------------------------------------===// // Microsoft Expressions and Statements. //===----------------------------------------------------------------------===// +void ASTStmtWriter::VisitMSPropertyRefExpr(MSPropertyRefExpr *E) { + VisitExpr(E); + Record.push_back(E->isArrow()); + Writer.AddStmt(E->getBaseExpr()); + Writer.AddNestedNameSpecifierLoc(E->getQualifierLoc(), Record); + Writer.AddSourceLocation(E->getMemberLoc(), Record); + Writer.AddDeclRef(E->getPropertyDecl(), Record); + Code = serialization::EXPR_CXX_PROPERTY_REF_EXPR; +} + void ASTStmtWriter::VisitCXXUuidofExpr(CXXUuidofExpr *E) { VisitExpr(E); Writer.AddSourceRange(E->getSourceRange(), Record); diff --git a/lib/Serialization/GlobalModuleIndex.cpp b/lib/Serialization/GlobalModuleIndex.cpp index f9acb847284..b6693e40e51 100644 --- a/lib/Serialization/GlobalModuleIndex.cpp +++ b/lib/Serialization/GlobalModuleIndex.cpp @@ -818,3 +818,34 @@ GlobalModuleIndex::writeIndex(FileManager &FileMgr, StringRef Path) { // We're done. return EC_None; } + +namespace { + class GlobalIndexIdentifierIterator : public IdentifierIterator { + /// \brief The current position within the identifier lookup table. + IdentifierIndexTable::key_iterator Current; + + /// \brief The end position within the identifier lookup table. + IdentifierIndexTable::key_iterator End; + + public: + explicit GlobalIndexIdentifierIterator(IdentifierIndexTable &Idx) { + Current = Idx.key_begin(); + End = Idx.key_end(); + } + + virtual StringRef Next() { + if (Current == End) + return StringRef(); + + StringRef Result = *Current; + ++Current; + return Result; + } + }; +} + +IdentifierIterator *GlobalModuleIndex::createIdentifierIterator() const { + IdentifierIndexTable &Table = + *static_cast(IdentifierIndex); + return new GlobalIndexIdentifierIterator(Table); +} diff --git a/lib/StaticAnalyzer/Checkers/AllocationDiagnostics.cpp b/lib/StaticAnalyzer/Checkers/AllocationDiagnostics.cpp new file mode 100644 index 00000000000..3dec8a58c92 --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/AllocationDiagnostics.cpp @@ -0,0 +1,24 @@ +//=- AllocationDiagnostics.cpp - Config options for allocation diags *- C++ -*-// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Declares the configuration functions for leaks/allocation diagnostics. +// +//===-------------------------- + +#include "AllocationDiagnostics.h" + +namespace clang { +namespace ento { + +bool shouldIncludeAllocationSiteInLeakDiagnostics(AnalyzerOptions &AOpts) { + return AOpts.getBooleanOption("leak-diagnostics-reference-allocation", + false); +} + +}} diff --git a/lib/StaticAnalyzer/Checkers/AllocationDiagnostics.h b/lib/StaticAnalyzer/Checkers/AllocationDiagnostics.h new file mode 100644 index 00000000000..2b314a3b749 --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/AllocationDiagnostics.h @@ -0,0 +1,31 @@ +//=--- AllocationDiagnostics.h - Config options for allocation diags *- C++ -*-// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Declares the configuration functions for leaks/allocation diagnostics. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_SA_LIB_CHECKERS_ALLOC_DIAGS_H +#define LLVM_CLANG_SA_LIB_CHECKERS_ALLOC_DIAGS_H + +#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" + +namespace clang { namespace ento { + +/// \brief Returns true if leak diagnostics should directly reference +/// the allocatin site (where possible). +/// +/// The default is false. +/// +bool shouldIncludeAllocationSiteInLeakDiagnostics(AnalyzerOptions &AOpts); + +}} + +#endif + diff --git a/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp b/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp index 533a324e750..fba14a0fc49 100644 --- a/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp +++ b/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp @@ -123,18 +123,29 @@ void NilArgChecker::WarnIfNilArg(CheckerContext &C, if (Class == FC_NSArray) { os << "Array element cannot be nil"; } else if (Class == FC_NSDictionary) { - if (Arg == 0) - os << "Dictionary object cannot be nil"; - else { + if (Arg == 0) { + os << "Value stored into '"; + os << GetReceiverInterfaceName(msg) << "' cannot be nil"; + } else { assert(Arg == 1); - os << "Dictionary key cannot be nil"; + os << "'"<< GetReceiverInterfaceName(msg) << "' key cannot be nil"; } } else llvm_unreachable("Missing foundation class for the subscript expr"); } else { - os << "Argument to '" << GetReceiverInterfaceName(msg) << "' method '" - << msg.getSelector().getAsString() << "' cannot be nil"; + if (Class == FC_NSDictionary) { + if (Arg == 0) + os << "Value argument "; + else { + assert(Arg == 1); + os << "Key argument "; + } + os << "to '" << msg.getSelector().getAsString() << "' cannot be nil"; + } else { + os << "Argument to '" << GetReceiverInterfaceName(msg) << "' method '" + << msg.getSelector().getAsString() << "' cannot be nil"; + } } BugReport *R = new BugReport(*BT, os.str(), N); @@ -377,7 +388,7 @@ void CFNumberCreateChecker::checkPreStmt(const CallExpr *CE, // FIXME: If the pointee isn't an integer type, should we flag a warning? // People can do weird stuff with pointers. - if (!T->isIntegerType()) + if (!T->isIntegralOrEnumerationType()) return; uint64_t SourceSize = Ctx.getTypeSize(T); @@ -748,38 +759,81 @@ static bool isKnownNonNilCollectionType(QualType T) { } } -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; - +/// Assumes that the collection is non-nil. +/// +/// If the collection is known to be nil, returns NULL to indicate an infeasible +/// path. +static ProgramStateRef checkCollectionNonNil(CheckerContext &C, + ProgramStateRef State, + const ObjCForCollectionStmt *FCS) { + if (!State) + return NULL; + + SVal CollectionVal = C.getSVal(FCS->getCollection()); + Optional KnownCollection = CollectionVal.getAs(); + if (!KnownCollection) + return State; + + ProgramStateRef StNonNil, StNil; + llvm::tie(StNonNil, StNil) = State->assume(*KnownCollection); + if (StNil && !StNonNil) { + // The collection is nil. This path is infeasible. + return NULL; + } + + return StNonNil; +} + +/// Assumes that the collection elements are non-nil. +/// +/// This only applies if the collection is one of those known not to contain +/// nil values. +static ProgramStateRef checkElementNonNil(CheckerContext &C, + ProgramStateRef State, + const ObjCForCollectionStmt *FCS) { + if (!State) + return NULL; + // 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. + if (!isKnownNonNilCollectionType(FCS->getCollection()->getType())) + return State; + + const LocationContext *LCtx = C.getLocationContext(); const Stmt *Element = FCS->getElement(); - SVal ElementVar; + + // FIXME: Copied from ExprEngineObjC. + Optional ElementLoc; if (const DeclStmt *DS = dyn_cast(Element)) { const VarDecl *ElemDecl = cast(DS->getSingleDecl()); assert(ElemDecl->getInit() == 0); - ElementVar = State->getLValue(ElemDecl, C.getLocationContext()); + ElementLoc = State->getLValue(ElemDecl, LCtx); } else { - ElementVar = State->getSVal(Element, C.getLocationContext()); + ElementLoc = State->getSVal(Element, LCtx).getAs(); } - if (!ElementVar.getAs()) - return; + if (!ElementLoc) + return State; // Go ahead and assume the value is non-nil. - SVal Val = State->getSVal(ElementVar.castAs()); - State = State->assume(Val.castAs(), true); - C.addTransition(State); + SVal Val = State->getSVal(*ElementLoc); + return State->assume(Val.castAs(), true); +} + +void ObjCLoopChecker::checkPostStmt(const ObjCForCollectionStmt *FCS, + CheckerContext &C) const { + // Check if this is the branch for the end of the loop. + SVal CollectionSentinel = C.getSVal(FCS); + if (CollectionSentinel.isZeroConstant()) + return; + + ProgramStateRef State = C.getState(); + State = checkCollectionNonNil(C, State, FCS); + State = checkElementNonNil(C, State, FCS); + + if (!State) + C.generateSink(); + else if (State != C.getState()) + C.addTransition(State); } namespace { diff --git a/lib/StaticAnalyzer/Checkers/CMakeLists.txt b/lib/StaticAnalyzer/Checkers/CMakeLists.txt index b7df10e7ffb..7da68251063 100644 --- a/lib/StaticAnalyzer/Checkers/CMakeLists.txt +++ b/lib/StaticAnalyzer/Checkers/CMakeLists.txt @@ -4,6 +4,7 @@ clang_tablegen(Checkers.inc -gen-clang-sa-checkers TARGET ClangSACheckers) add_clang_library(clangStaticAnalyzerCheckers + AllocationDiagnostics.cpp AnalyzerStatsChecker.cpp ArrayBoundChecker.cpp ArrayBoundCheckerV2.cpp @@ -42,8 +43,8 @@ add_clang_library(clangStaticAnalyzerCheckers MallocSizeofChecker.cpp NSAutoreleasePoolChecker.cpp NSErrorChecker.cpp - NonNullParamChecker.cpp NoReturnFunctionChecker.cpp + NonNullParamChecker.cpp ObjCAtSyncChecker.cpp ObjCContainersASTChecker.cpp ObjCContainersChecker.cpp diff --git a/lib/StaticAnalyzer/Checkers/CStringChecker.cpp b/lib/StaticAnalyzer/Checkers/CStringChecker.cpp index cc55e9f6ecf..aa1ca6f2f80 100644 --- a/lib/StaticAnalyzer/Checkers/CStringChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/CStringChecker.cpp @@ -114,6 +114,8 @@ class CStringChecker : public Checker< eval::Call, bool isBounded = false, bool ignoreCase = false) const; + void evalStrsep(CheckerContext &C, const CallExpr *CE) const; + // Utility methods std::pair static assumeZero(CheckerContext &C, @@ -1752,6 +1754,63 @@ void CStringChecker::evalStrcmpCommon(CheckerContext &C, const CallExpr *CE, C.addTransition(state); } +void CStringChecker::evalStrsep(CheckerContext &C, const CallExpr *CE) const { + //char *strsep(char **stringp, const char *delim); + if (CE->getNumArgs() < 2) + return; + + // Sanity: does the search string parameter match the return type? + const Expr *SearchStrPtr = CE->getArg(0); + QualType CharPtrTy = SearchStrPtr->getType()->getPointeeType(); + if (CharPtrTy.isNull() || + CE->getType().getUnqualifiedType() != CharPtrTy.getUnqualifiedType()) + return; + + CurrentFunctionDescription = "strsep()"; + ProgramStateRef State = C.getState(); + const LocationContext *LCtx = C.getLocationContext(); + + // Check that the search string pointer is non-null (though it may point to + // a null string). + SVal SearchStrVal = State->getSVal(SearchStrPtr, LCtx); + State = checkNonNull(C, State, SearchStrPtr, SearchStrVal); + if (!State) + return; + + // Check that the delimiter string is non-null. + const Expr *DelimStr = CE->getArg(1); + SVal DelimStrVal = State->getSVal(DelimStr, LCtx); + State = checkNonNull(C, State, DelimStr, DelimStrVal); + if (!State) + return; + + SValBuilder &SVB = C.getSValBuilder(); + SVal Result; + if (Optional SearchStrLoc = SearchStrVal.getAs()) { + // Get the current value of the search string pointer, as a char*. + Result = State->getSVal(*SearchStrLoc, CharPtrTy); + + // Invalidate the search string, representing the change of one delimiter + // character to NUL. + State = InvalidateBuffer(C, State, SearchStrPtr, Result); + + // Overwrite the search string pointer. The new value is either an address + // further along in the same string, or NULL if there are no more tokens. + State = State->bindLoc(*SearchStrLoc, + SVB.conjureSymbolVal(getTag(), CE, LCtx, CharPtrTy, + C.blockCount())); + } else { + assert(SearchStrVal.isUnknown()); + // Conjure a symbolic value. It's the best we can do. + Result = SVB.conjureSymbolVal(0, CE, LCtx, C.blockCount()); + } + + // Set the return value, and finish. + State = State->BindExpr(CE, LCtx, Result); + C.addTransition(State); +} + + //===----------------------------------------------------------------------===// // The driver method, and other Checker callbacks. //===----------------------------------------------------------------------===// @@ -1762,6 +1821,7 @@ bool CStringChecker::evalCall(const CallExpr *CE, CheckerContext &C) const { if (!FDecl) return false; + // FIXME: Poorly-factored string switches are slow. FnCheck evalFunction = 0; if (C.isCLibraryFunction(FDecl, "memcpy")) evalFunction = &CStringChecker::evalMemcpy; @@ -1793,6 +1853,8 @@ bool CStringChecker::evalCall(const CallExpr *CE, CheckerContext &C) const { evalFunction = &CStringChecker::evalStrcasecmp; else if (C.isCLibraryFunction(FDecl, "strncasecmp")) evalFunction = &CStringChecker::evalStrncasecmp; + else if (C.isCLibraryFunction(FDecl, "strsep")) + evalFunction = &CStringChecker::evalStrsep; else if (C.isCLibraryFunction(FDecl, "bcopy")) evalFunction = &CStringChecker::evalBcopy; else if (C.isCLibraryFunction(FDecl, "bcmp")) diff --git a/lib/StaticAnalyzer/Checkers/CStringSyntaxChecker.cpp b/lib/StaticAnalyzer/Checkers/CStringSyntaxChecker.cpp index 3a57a56aea6..92c0eef3e88 100644 --- a/lib/StaticAnalyzer/Checkers/CStringSyntaxChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/CStringSyntaxChecker.cpp @@ -101,6 +101,8 @@ class WalkAST: public StmtVisitor { // - strncat(dst, src, sizeof(dst) - 1); // - strncat(dst, src, sizeof(dst)); bool WalkAST::containsBadStrncatPattern(const CallExpr *CE) { + if (CE->getNumArgs() != 3) + return false; const Expr *DstArg = CE->getArg(0); const Expr *SrcArg = CE->getArg(1); const Expr *LenArg = CE->getArg(2); diff --git a/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp b/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp index 7ef13ab5386..63080ea230f 100644 --- a/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp +++ b/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp @@ -345,7 +345,7 @@ void WalkAST::checkCall_getpw(const CallExpr *CE, const FunctionDecl *FD) { return; // Verify the first argument type is integer. - if (!FPT->getArgType(0)->isIntegerType()) + if (!FPT->getArgType(0)->isIntegralOrUnscopedEnumerationType()) return; // Verify the second argument type is char*. @@ -602,7 +602,7 @@ void WalkAST::checkCall_rand(const CallExpr *CE, const FunctionDecl *FD) { if (!PT) return; - if (! PT->getPointeeType()->isIntegerType()) + if (! PT->getPointeeType()->isIntegralOrUnscopedEnumerationType()) return; } else if (FTP->getNumArgs() != 0) @@ -725,7 +725,7 @@ void WalkAST::checkUncheckedReturnValue(CallExpr *CE) { // The arguments must be integers. for (unsigned i = 0; i < FTP->getNumArgs(); i++) - if (! FTP->getArgType(i)->isIntegerType()) + if (! FTP->getArgType(i)->isIntegralOrUnscopedEnumerationType()) return; // Issue a warning. diff --git a/lib/StaticAnalyzer/Checkers/Checkers.td b/lib/StaticAnalyzer/Checkers/Checkers.td index 3db3fb9962a..fc35b223ee7 100644 --- a/lib/StaticAnalyzer/Checkers/Checkers.td +++ b/lib/StaticAnalyzer/Checkers/Checkers.td @@ -167,6 +167,11 @@ def ReturnUndefChecker : Checker<"UndefReturn">, //===----------------------------------------------------------------------===// let ParentPackage = Cplusplus in { + +def NewDeleteChecker : Checker<"NewDelete">, + HelpText<"Check for double-free and use-after-free problems. Traces memory managed by new/delete.">, + DescFile<"MallocChecker.cpp">; + } // end: "cplusplus" let ParentPackage = CplusplusAlpha in { @@ -175,8 +180,8 @@ def VirtualCallChecker : Checker<"VirtualCall">, HelpText<"Check virtual function calls during construction or destruction">, DescFile<"VirtualCallChecker.cpp">; -def NewDeleteChecker : Checker<"NewDelete">, - HelpText<"Check for memory leaks, double free, and use-after-free problems. Traces memory managed by new/delete.">, +def NewDeleteLeaksChecker : Checker<"NewDeleteLeaks">, + HelpText<"Check for memory leaks. Traces memory managed by new/delete.">, DescFile<"MallocChecker.cpp">; } // end: "alpha.cplusplus" diff --git a/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp b/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp index f2e3e6d7815..f336a6e6805 100644 --- a/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp @@ -101,7 +101,8 @@ void ReachableCode::computeReachableBlocks() { } } -static const Expr *LookThroughTransitiveAssignments(const Expr *Ex) { +static const Expr * +LookThroughTransitiveAssignmentsAndCommaOperators(const Expr *Ex) { while (Ex) { const BinaryOperator *BO = dyn_cast(Ex->IgnoreParenCasts()); @@ -111,6 +112,10 @@ static const Expr *LookThroughTransitiveAssignments(const Expr *Ex) { Ex = BO->getRHS(); continue; } + if (BO->getOpcode() == BO_Comma) { + Ex = BO->getRHS(); + continue; + } break; } return Ex; @@ -266,7 +271,9 @@ class DeadStoreObs : public LiveVariables::Observer { if (VarDecl *VD = dyn_cast(DR->getDecl())) { // Special case: check for assigning null to a pointer. // This is a common form of defensive programming. - const Expr *RHS = LookThroughTransitiveAssignments(B->getRHS()); + const Expr *RHS = + LookThroughTransitiveAssignmentsAndCommaOperators(B->getRHS()); + RHS = RHS->IgnoreParenCasts(); QualType T = VD->getType(); if (T->isPointerType() || T->isObjCObjectPointerType()) { @@ -274,7 +281,6 @@ class DeadStoreObs : public LiveVariables::Observer { return; } - RHS = RHS->IgnoreParenCasts(); // Special case: self-assignments. These are often used to shut up // "unused variable" compiler warnings. if (const DeclRefExpr *RhsDR = dyn_cast(RHS)) @@ -326,7 +332,7 @@ class DeadStoreObs : public LiveVariables::Observer { // Look through transitive assignments, e.g.: // int x = y = 0; - E = LookThroughTransitiveAssignments(E); + E = LookThroughTransitiveAssignmentsAndCommaOperators(E); // Don't warn on C++ objects (yet) until we can show that their // constructors/destructors don't have side effects. diff --git a/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp b/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp index 29b4a637cda..fe12866e51c 100644 --- a/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp +++ b/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp @@ -7,7 +7,7 @@ // //===----------------------------------------------------------------------===// // -// This file defines a checkers that display debugging information. +// This file defines checkers that display debugging information. // //===----------------------------------------------------------------------===// diff --git a/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp b/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp index 9f176a4b5bf..759aa6605ee 100644 --- a/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp +++ b/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp @@ -27,7 +27,8 @@ namespace { class DynamicTypePropagation: public Checker< check::PreCall, check::PostCall, - check::PostStmt > { + check::PostStmt, + check::PostStmt > { const ObjCObjectType *getObjectTypeForAllocAndNew(const ObjCMessageExpr *MsgE, CheckerContext &C) const; @@ -38,6 +39,7 @@ class DynamicTypePropagation: void checkPreCall(const CallEvent &Call, CheckerContext &C) const; void checkPostCall(const CallEvent &Call, CheckerContext &C) const; void checkPostStmt(const ImplicitCastExpr *CastE, CheckerContext &C) const; + void checkPostStmt(const CXXNewExpr *NewE, CheckerContext &C) const; }; } @@ -190,6 +192,20 @@ void DynamicTypePropagation::checkPostStmt(const ImplicitCastExpr *CastE, return; } +void DynamicTypePropagation::checkPostStmt(const CXXNewExpr *NewE, + CheckerContext &C) const { + if (NewE->isArray()) + return; + + // We only track dynamic type info for regions. + const MemRegion *MR = C.getSVal(NewE).getAsRegion(); + if (!MR) + return; + + C.addTransition(C.getState()->setDynamicTypeInfo(MR, NewE->getType(), + /*CanBeSubclass=*/false)); +} + const ObjCObjectType * DynamicTypePropagation::getObjectTypeForAllocAndNew(const ObjCMessageExpr *MsgE, CheckerContext &C) const { diff --git a/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp b/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp index 5ed28e955d4..cc940be7b18 100644 --- a/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp @@ -437,6 +437,7 @@ visit(const ObjCImplementationDecl *ImplD) const { // Remove ivars invalidated by the partial invalidation methods. They do not // need to be invalidated in the regular invalidation methods. + bool AtImplementationContainsAtLeastOnePartialInvalidationMethod = false; for (MethodSet::iterator I = PartialInfo.InvalidationMethods.begin(), E = PartialInfo.InvalidationMethods.end(); I != E; ++I) { @@ -446,6 +447,8 @@ visit(const ObjCImplementationDecl *ImplD) const { const ObjCMethodDecl *D = ImplD->getMethod(InterfD->getSelector(), InterfD->isInstanceMethod()); if (D && D->hasBody()) { + AtImplementationContainsAtLeastOnePartialInvalidationMethod = true; + bool CalledAnotherInvalidationMethod = false; // The MethodCrowler is going to remove the invalidated ivars. MethodCrawler(Ivars, @@ -471,7 +474,7 @@ visit(const ObjCImplementationDecl *ImplD) const { containsInvalidationMethod(InterfaceD, Info, /*LookForPartial*/ false); // Report an error in case none of the invalidation methods are declared. - if (!Info.needsInvalidation()) { + if (!Info.needsInvalidation() && !PartialInfo.needsInvalidation()) { if (Filter.check_MissingInvalidationMethod) reportNoInvalidationMethod(FirstIvarDecl, IvarToPopertyMap, InterfaceD, /*MissingDeclaration*/ true); @@ -520,9 +523,19 @@ visit(const ObjCImplementationDecl *ImplD) const { } // Report an error in case none of the invalidation methods are implemented. - if (!AtImplementationContainsAtLeastOneInvalidationMethod) - reportNoInvalidationMethod(FirstIvarDecl, IvarToPopertyMap, InterfaceD, - /*MissingDeclaration*/ false); + if (!AtImplementationContainsAtLeastOneInvalidationMethod) { + if (AtImplementationContainsAtLeastOnePartialInvalidationMethod) { + // Warn on the ivars that were not invalidated by the prrtial + // invalidation methods. + for (IvarSet::const_iterator + I = Ivars.begin(), E = Ivars.end(); I != E; ++I) + reportIvarNeedsInvalidation(I->first, IvarToPopertyMap, 0); + } else { + // Otherwise, no invalidation methods were implemented. + reportNoInvalidationMethod(FirstIvarDecl, IvarToPopertyMap, InterfaceD, + /*MissingDeclaration*/ false); + } + } } void IvarInvalidationCheckerImpl:: @@ -551,19 +564,27 @@ reportNoInvalidationMethod(const ObjCIvarDecl *FirstIvarDecl, void IvarInvalidationCheckerImpl:: reportIvarNeedsInvalidation(const ObjCIvarDecl *IvarD, - const IvarToPropMapTy &IvarToPopertyMap, - const ObjCMethodDecl *MethodD) const { + const IvarToPropMapTy &IvarToPopertyMap, + const ObjCMethodDecl *MethodD) const { SmallString<128> sbuf; llvm::raw_svector_ostream os(sbuf); printIvar(os, IvarD, IvarToPopertyMap); os << "needs to be invalidated or set to nil"; - PathDiagnosticLocation MethodDecLocation = - PathDiagnosticLocation::createEnd(MethodD->getBody(), - BR.getSourceManager(), - Mgr.getAnalysisDeclContext(MethodD)); - BR.EmitBasicReport(MethodD, "Incomplete invalidation", - categories::CoreFoundationObjectiveC, os.str(), - MethodDecLocation); + if (MethodD) { + PathDiagnosticLocation MethodDecLocation = + PathDiagnosticLocation::createEnd(MethodD->getBody(), + BR.getSourceManager(), + Mgr.getAnalysisDeclContext(MethodD)); + BR.EmitBasicReport(MethodD, "Incomplete invalidation", + categories::CoreFoundationObjectiveC, os.str(), + MethodDecLocation); + } else { + BR.EmitBasicReport(IvarD, "Incomplete invalidation", + categories::CoreFoundationObjectiveC, os.str(), + PathDiagnosticLocation::createBegin(IvarD, + BR.getSourceManager())); + + } } void IvarInvalidationCheckerImpl::MethodCrawler::markInvalidated( diff --git a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp index 4b0e7661d8d..5d3eb65148d 100644 --- a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -50,7 +50,12 @@ class RefState { Released, // The responsibility for freeing resources has transfered from // this reference. A relinquished symbol should not be freed. - Relinquished }; + Relinquished, + // We are no longer guaranteed to have observed all manipulations + // of this pointer/memory. For example, it could have been + // passed as a parameter to an opaque function. + Escaped + }; const Stmt *S; unsigned K : 2; // Kind enum, but stored as a bitfield. @@ -58,12 +63,15 @@ class RefState { // family. RefState(Kind k, const Stmt *s, unsigned family) - : S(s), K(k), Family(family) {} + : S(s), K(k), Family(family) { + assert(family != AF_None); + } public: bool isAllocated() const { return K == Allocated; } bool isReleased() const { return K == Released; } bool isRelinquished() const { return K == Relinquished; } - AllocationFamily getAllocationFamily() const { + bool isEscaped() const { return K == Escaped; } + AllocationFamily getAllocationFamily() const { return (AllocationFamily)Family; } const Stmt *getStmt() const { return S; } @@ -81,6 +89,9 @@ class RefState { static RefState getRelinquished(unsigned family, const Stmt *s) { return RefState(Relinquished, s, family); } + static RefState getEscaped(const RefState *RS) { + return RefState(Escaped, RS->getStmt(), RS->getAllocationFamily()); + } void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(K); @@ -136,7 +147,7 @@ class MallocChecker : public Checker, - check::PreStmt, + check::PreCall, check::PostStmt, check::PostStmt, check::PreStmt, @@ -164,12 +175,13 @@ class MallocChecker : public CheckergetAllocationFamily() == AF_None || - RsBase->getAllocationFamily() == getAllocationFamily(C, ParentExpr); - - // Check if an expected deallocation function matches the real one. - if (!DeallocMatchesAlloc && RsBase->isAllocated()) { - ReportMismatchedDealloc(C, ArgExpr->getSourceRange(), ParentExpr, RsBase); - return 0; - } - - // Check double free. - if (DeallocMatchesAlloc && - (RsBase->isReleased() || RsBase->isRelinquished()) && + // Check for double free first. + if ((RsBase->isReleased() || RsBase->isRelinquished()) && !didPreviousFreeFail(State, SymBase, PreviousRetStatusSymbol)) { ReportDoubleFree(C, ParentExpr->getSourceRange(), RsBase->isReleased(), SymBase, PreviousRetStatusSymbol); return 0; - } - // Check if the memory location being freed is the actual location - // allocated, or an offset. - RegionOffset Offset = R->getAsOffset(); - if (RsBase->isAllocated() && - Offset.isValid() && - !Offset.hasSymbolicOffset() && - Offset.getOffset() != 0) { - const Expr *AllocExpr = cast(RsBase->getStmt()); - ReportOffsetFree(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr, - AllocExpr); - return 0; + // If the pointer is allocated or escaped, but we are now trying to free it, + // check that the call to free is proper. + } else if (RsBase->isAllocated() || RsBase->isEscaped()) { + + // Check if an expected deallocation function matches the real one. + bool DeallocMatchesAlloc = + RsBase->getAllocationFamily() == getAllocationFamily(C, ParentExpr); + if (!DeallocMatchesAlloc) { + ReportMismatchedDealloc(C, ArgExpr->getSourceRange(), + ParentExpr, RsBase, SymBase); + return 0; + } + + // Check if the memory location being freed is the actual location + // allocated, or an offset. + RegionOffset Offset = R->getAsOffset(); + if (Offset.isValid() && + !Offset.hasSymbolicOffset() && + Offset.getOffset() != 0) { + const Expr *AllocExpr = cast(RsBase->getStmt()); + ReportOffsetFree(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr, + AllocExpr); + return 0; + } } } @@ -1056,7 +1070,8 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C, } } - AllocationFamily Family = RsBase ? RsBase->getAllocationFamily() : AF_None; + AllocationFamily Family = RsBase ? RsBase->getAllocationFamily() + : getAllocationFamily(C, ParentExpr); // Normal free. if (Hold) return State->set(SymBase, @@ -1067,7 +1082,7 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C, RefState::getReleased(Family, ParentExpr)); } -bool MallocChecker::isTrackedFamily(AllocationFamily Family) const { +bool MallocChecker::isTrackedByCurrentChecker(AllocationFamily Family) const { switch (Family) { case AF_Malloc: { if (!Filter.CMallocOptimistic && !Filter.CMallocPessimistic) @@ -1081,22 +1096,24 @@ bool MallocChecker::isTrackedFamily(AllocationFamily Family) const { return true; } case AF_None: { - return true; + llvm_unreachable("no family"); } } llvm_unreachable("unhandled family"); } -bool MallocChecker::isTrackedFamily(CheckerContext &C, - const Stmt *AllocDeallocStmt) const { - return isTrackedFamily(getAllocationFamily(C, AllocDeallocStmt)); +bool +MallocChecker::isTrackedByCurrentChecker(CheckerContext &C, + const Stmt *AllocDeallocStmt) const { + return isTrackedByCurrentChecker(getAllocationFamily(C, AllocDeallocStmt)); } -bool MallocChecker::isTrackedFamily(CheckerContext &C, SymbolRef Sym) const { - const RefState *RS = C.getState()->get(Sym); +bool MallocChecker::isTrackedByCurrentChecker(CheckerContext &C, + SymbolRef Sym) const { - return RS ? isTrackedFamily(RS->getAllocationFamily()) - : isTrackedFamily(AF_None); + const RefState *RS = C.getState()->get(Sym); + assert(RS); + return isTrackedByCurrentChecker(RS->getAllocationFamily()); } bool MallocChecker::SummarizeValue(raw_ostream &os, SVal V) { @@ -1194,7 +1211,7 @@ void MallocChecker::ReportBadFree(CheckerContext &C, SVal ArgVal, !Filter.CNewDeleteChecker) return; - if (!isTrackedFamily(C, DeallocExpr)) + if (!isTrackedByCurrentChecker(C, DeallocExpr)) return; if (ExplodedNode *N = C.generateSink()) { @@ -1236,7 +1253,8 @@ void MallocChecker::ReportBadFree(CheckerContext &C, SVal ArgVal, void MallocChecker::ReportMismatchedDealloc(CheckerContext &C, SourceRange Range, const Expr *DeallocExpr, - const RefState *RS) const { + const RefState *RS, + SymbolRef Sym) const { if (!Filter.CMismatchedDeallocatorChecker) return; @@ -1266,7 +1284,9 @@ void MallocChecker::ReportMismatchedDealloc(CheckerContext &C, os << ", not " << DeallocOs.str(); BugReport *R = new BugReport(*BT_MismatchedDealloc, os.str(), N); + R->markInteresting(Sym); R->addRange(Range); + R->addVisitor(new MallocBugVisitor(Sym)); C.emitReport(R); } } @@ -1279,7 +1299,7 @@ void MallocChecker::ReportOffsetFree(CheckerContext &C, SVal ArgVal, !Filter.CNewDeleteChecker) return; - if (!isTrackedFamily(C, AllocExpr)) + if (!isTrackedByCurrentChecker(C, AllocExpr)) return; ExplodedNode *N = C.generateSink(); @@ -1331,7 +1351,7 @@ void MallocChecker::ReportUseAfterFree(CheckerContext &C, SourceRange Range, !Filter.CNewDeleteChecker) return; - if (!isTrackedFamily(C, Sym)) + if (!isTrackedByCurrentChecker(C, Sym)) return; if (ExplodedNode *N = C.generateSink()) { @@ -1356,7 +1376,7 @@ void MallocChecker::ReportDoubleFree(CheckerContext &C, SourceRange Range, !Filter.CNewDeleteChecker) return; - if (!isTrackedFamily(C, Sym)) + if (!isTrackedByCurrentChecker(C, Sym)) return; if (ExplodedNode *N = C.generateSink()) { @@ -1510,13 +1530,19 @@ MallocChecker::getAllocationSite(const ExplodedNode *N, SymbolRef Sym, // Find the most recent expression bound to the symbol in the current // context. - if (!ReferenceRegion) { - if (const MemRegion *MR = C.getLocationRegionIfPostStore(N)) { - SVal Val = State->getSVal(MR); - if (Val.getAsLocSymbol() == Sym) - ReferenceRegion = MR; + if (!ReferenceRegion) { + if (const MemRegion *MR = C.getLocationRegionIfPostStore(N)) { + SVal Val = State->getSVal(MR); + if (Val.getAsLocSymbol() == Sym) { + const VarRegion* VR = MR->getBaseRegion()->getAs(); + // Do not show local variables belonging to a function other than + // where the error is reported. + if (!VR || + (VR->getStackFrame() == LeakContext->getCurrentStackFrame())) + ReferenceRegion = MR; + } + } } - } // Allocation node, is the last node in the current context in which the // symbol was tracked. @@ -1532,12 +1558,21 @@ void MallocChecker::reportLeak(SymbolRef Sym, ExplodedNode *N, CheckerContext &C) const { if (!Filter.CMallocOptimistic && !Filter.CMallocPessimistic && - !Filter.CNewDeleteChecker) + !Filter.CNewDeleteLeaksChecker) return; - if (!isTrackedFamily(C, Sym)) + const RefState *RS = C.getState()->get(Sym); + assert(RS && "cannot leak an untracked symbol"); + AllocationFamily Family = RS->getAllocationFamily(); + if (!isTrackedByCurrentChecker(Family)) return; + // Special case for new and new[]; these are controlled by a separate checker + // flag so that they can be selectively disabled. + if (Family == AF_CXXNew || Family == AF_CXXNewArray) + if (!Filter.CNewDeleteLeaksChecker) + return; + assert(N); if (!BT_Leak) { BT_Leak.reset(new BugType("Memory leak", "Memory Error")); @@ -1570,11 +1605,11 @@ 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 && Region->canPrintPretty()) { - os << " of memory pointed to by '"; + os << "Potential leak of memory pointed to by "; Region->printPretty(os); - os << '\''; + } else { + os << "Potential memory leak"; } BugReport *R = new BugReport(*BT_Leak, os.str(), N, @@ -1638,26 +1673,39 @@ void MallocChecker::checkDeadSymbols(SymbolReaper &SymReaper, C.addTransition(state->set(RS), N); } -void MallocChecker::checkPreStmt(const CallExpr *CE, CheckerContext &C) const { +void MallocChecker::checkPreCall(const CallEvent &Call, + CheckerContext &C) const { + // We will check for double free in the post visit. - if ((Filter.CMallocOptimistic || Filter.CMallocPessimistic) && - isFreeFunction(C.getCalleeDecl(CE), C.getASTContext())) - return; + if (const AnyFunctionCall *FC = dyn_cast(&Call)) { + const FunctionDecl *FD = FC->getDecl(); + if (!FD) + return; - if (Filter.CNewDeleteChecker && - isStandardNewDelete(C.getCalleeDecl(CE), C.getASTContext())) - return; + if ((Filter.CMallocOptimistic || Filter.CMallocPessimistic) && + isFreeFunction(FD, C.getASTContext())) + return; - // Check use after free, when a freed pointer is passed to a call. - ProgramStateRef State = C.getState(); - for (CallExpr::const_arg_iterator I = CE->arg_begin(), - E = CE->arg_end(); I != E; ++I) { - const Expr *A = *I; - if (A->getType().getTypePtr()->isAnyPointerType()) { - SymbolRef Sym = C.getSVal(A).getAsSymbol(); + if (Filter.CNewDeleteChecker && + isStandardNewDelete(FD, C.getASTContext())) + return; + } + + // Check if the callee of a method is deleted. + if (const CXXInstanceCall *CC = dyn_cast(&Call)) { + SymbolRef Sym = CC->getCXXThisVal().getAsSymbol(); + if (!Sym || checkUseAfterFree(Sym, C, CC->getCXXThisExpr())) + return; + } + + // Check arguments for being used after free. + for (unsigned I = 0, E = Call.getNumArgs(); I != E; ++I) { + SVal ArgSVal = Call.getArgSVal(I); + if (ArgSVal.getAs()) { + SymbolRef Sym = ArgSVal.getAsSymbol(); if (!Sym) continue; - if (checkUseAfterFree(Sym, C, A)) + if (checkUseAfterFree(Sym, C, Call.getArgExpr(I))) return; } } @@ -1976,8 +2024,10 @@ ProgramStateRef MallocChecker::checkPointerEscapeAux(ProgramStateRef State, SymbolRef sym = *I; if (const RefState *RS = State->get(sym)) { - if (RS->isAllocated() && CheckRefState(RS)) + if (RS->isAllocated() && CheckRefState(RS)) { State = State->remove(sym); + State = State->set(sym, RefState::getEscaped(RS)); + } } } return State; @@ -2042,7 +2092,7 @@ MallocChecker::MallocBugVisitor::VisitNode(const ExplodedNode *N, } else if (isReleased(RS, RSPrev, S)) { Msg = "Memory is released"; StackHint = new StackHintGeneratorForSymbol(Sym, - "Returned released memory"); + "Returning; memory was released"); } else if (isRelinquished(RS, RSPrev, S)) { Msg = "Memory ownership is transfered"; StackHint = new StackHintGeneratorForSymbol(Sym, ""); @@ -2102,6 +2152,14 @@ void MallocChecker::printState(raw_ostream &Out, ProgramStateRef State, } } +void ento::registerNewDeleteLeaksChecker(CheckerManager &mgr) { + registerCStringCheckerBasic(mgr); + mgr.registerChecker()->Filter.CNewDeleteLeaksChecker = true; + // We currently treat NewDeleteLeaks checker as a subchecker of NewDelete + // checker. + mgr.registerChecker()->Filter.CNewDeleteChecker = true; +} + #define REGISTER_CHECKER(name) \ void ento::register##name(CheckerManager &mgr) {\ registerCStringCheckerBasic(mgr); \ diff --git a/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp b/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp index ce7d4ccf7a0..d29f34fb03e 100644 --- a/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp @@ -188,7 +188,7 @@ class MallocSizeofChecker : public Checker { for (CallExpr::const_arg_iterator ai = i->AllocCall->arg_begin(), ae = i->AllocCall->arg_end(); ai != ae; ++ai) { - if (!(*ai)->getType()->isIntegerType()) + if (!(*ai)->getType()->isIntegralOrUnscopedEnumerationType()) continue; SizeofFinder SFinder; diff --git a/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp b/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp index 79409e85bda..0f456ea8d78 100644 --- a/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp @@ -37,6 +37,8 @@ #include "llvm/ADT/StringExtras.h" #include +#include "AllocationDiagnostics.h" + using namespace clang; using namespace ento; using llvm::StrInStrNoCase; @@ -324,7 +326,7 @@ void RefVal::print(raw_ostream &Out) const { break; case RefVal::ErrorOverAutorelease: - Out << "Over autoreleased"; + Out << "Over-autoreleased"; break; case RefVal::ErrorReturnedNotOwned: @@ -1114,12 +1116,14 @@ RetainSummaryManager::getFunctionSummary(const FunctionDecl *FD) { // correctly. ScratchArgs = AF.add(ScratchArgs, 12, StopTracking); S = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, DoNothing); - } else if (FName == "dispatch_set_context") { + } else if (FName == "dispatch_set_context" || + FName == "xpc_connection_set_context") { // - The analyzer currently doesn't have // a good way to reason about the finalizer function for libdispatch. // If we pass a context object that is memory managed, stop tracking it. + // - Same problem, but for XPC. // FIXME: this hack should possibly go away once we can handle - // libdispatch finalizers. + // libdispatch and XPC finalizers. ScratchArgs = AF.add(ScratchArgs, 1, StopTracking); S = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, DoNothing); } else if (FName.startswith("NSLog")) { @@ -1660,10 +1664,10 @@ namespace { class OverAutorelease : public CFRefBug { public: OverAutorelease() - : CFRefBug("Object sent -autorelease too many times") {} + : CFRefBug("Object autoreleased too many times") {} const char *getDescription() const { - return "Object sent -autorelease too many times"; + return "Object autoreleased too many times"; } }; @@ -1773,11 +1777,11 @@ namespace { class CFRefLeakReport : public CFRefReport { const MemRegion* AllocBinding; - public: CFRefLeakReport(CFRefBug &D, const LangOptions &LOpts, bool GCEnabled, const SummaryLogTy &Log, ExplodedNode *n, SymbolRef sym, - CheckerContext &Ctx); + CheckerContext &Ctx, + bool IncludeAllocationLine); PathDiagnosticLocation getLocation(const SourceManager &SM) const { assert(Location.isValid()); @@ -2048,7 +2052,7 @@ PathDiagnosticPiece *CFRefReportVisitor::VisitNode(const ExplodedNode *N, return 0; assert(PrevV.getAutoreleaseCount() < CurrV.getAutoreleaseCount()); - os << "Object sent -autorelease message"; + os << "Object autoreleased"; break; } @@ -2134,32 +2138,86 @@ PathDiagnosticPiece *CFRefReportVisitor::VisitNode(const ExplodedNode *N, // Find the first node in the current function context that referred to the // tracked symbol and the memory location that value was stored to. Note, the // value is only reported if the allocation occurred in the same function as -// the leak. -static std::pair +// the leak. The function can also return a location context, which should be +// treated as interesting. +struct AllocationInfo { + const ExplodedNode* N; + const MemRegion *R; + const LocationContext *InterestingMethodContext; + AllocationInfo(const ExplodedNode *InN, + const MemRegion *InR, + const LocationContext *InInterestingMethodContext) : + N(InN), R(InR), InterestingMethodContext(InInterestingMethodContext) {} +}; + +static AllocationInfo GetAllocationSite(ProgramStateManager& StateMgr, const ExplodedNode *N, SymbolRef Sym) { - const ExplodedNode *Last = N; + const ExplodedNode *AllocationNode = N; + const ExplodedNode *AllocationNodeInCurrentContext = N; const MemRegion* FirstBinding = 0; const LocationContext *LeakContext = N->getLocationContext(); + // The location context of the init method called on the leaked object, if + // available. + const LocationContext *InitMethodContext = 0; + while (N) { ProgramStateRef St = N->getState(); + const LocationContext *NContext = N->getLocationContext(); if (!getRefBinding(St, Sym)) break; StoreManager::FindUniqueBinding FB(Sym); StateMgr.iterBindings(St, FB); - if (FB) FirstBinding = FB.getRegion(); + + if (FB) { + const MemRegion *R = FB.getRegion(); + const VarRegion *VR = R->getBaseRegion()->getAs(); + // Do not show local variables belonging to a function other than + // where the error is reported. + if (!VR || VR->getStackFrame() == LeakContext->getCurrentStackFrame()) + FirstBinding = R; + } - // Allocation node, is the last node in the current context in which the - // symbol was tracked. - if (N->getLocationContext() == LeakContext) - Last = N; + // AllocationNode is the last node in which the symbol was tracked. + AllocationNode = N; + + // AllocationNodeInCurrentContext, is the last node in the current context + // in which the symbol was tracked. + if (NContext == LeakContext) + AllocationNodeInCurrentContext = N; + + // Find the last init that was called on the given symbol and store the + // init method's location context. + if (!InitMethodContext) + if (Optional CEP = N->getLocation().getAs()) { + const Stmt *CE = CEP->getCallExpr(); + if (const ObjCMessageExpr *ME = dyn_cast_or_null(CE)) { + const Stmt *RecExpr = ME->getInstanceReceiver(); + if (RecExpr) { + SVal RecV = St->getSVal(RecExpr, NContext); + if (ME->getMethodFamily() == OMF_init && RecV.getAsSymbol() == Sym) + InitMethodContext = CEP->getCalleeContext(); + } + } + } N = N->pred_empty() ? NULL : *(N->pred_begin()); } + // If we are reporting a leak of the object that was allocated with alloc, + // mark its init method as interesting. + const LocationContext *InterestingMethodContext = 0; + if (InitMethodContext) { + const ProgramPoint AllocPP = AllocationNode->getLocation(); + if (Optional SP = AllocPP.getAs()) + if (const ObjCMessageExpr *ME = SP->getStmtAs()) + if (ME->getMethodFamily() == OMF_alloc) + InterestingMethodContext = InitMethodContext; + } + // If allocation happened in a function different from the leak node context, // do not report the binding. assert(N && "Could not find allocation node"); @@ -2167,7 +2225,9 @@ GetAllocationSite(ProgramStateManager& StateMgr, const ExplodedNode *N, FirstBinding = 0; } - return std::make_pair(Last, FirstBinding); + return AllocationInfo(AllocationNodeInCurrentContext, + FirstBinding, + InterestingMethodContext); } PathDiagnosticPiece* @@ -2190,12 +2250,12 @@ CFRefLeakReportVisitor::getEndPath(BugReporterContext &BRC, // We are reporting a leak. Walk up the graph to get to the first node where // the symbol appeared, and also get the first VarDecl that tracked object // is stored to. - const ExplodedNode *AllocNode = 0; - const MemRegion* FirstBinding = 0; - - llvm::tie(AllocNode, FirstBinding) = + AllocationInfo AllocI = GetAllocationSite(BRC.getStateManager(), EndN, Sym); + const MemRegion* FirstBinding = AllocI.R; + BR.markInteresting(AllocI.InterestingMethodContext); + SourceManager& SM = BRC.getSourceManager(); // Compute an actual location for the leak. Sometimes a leak doesn't @@ -2267,8 +2327,9 @@ CFRefLeakReportVisitor::getEndPath(BugReporterContext &BRC, CFRefLeakReport::CFRefLeakReport(CFRefBug &D, const LangOptions &LOpts, bool GCEnabled, const SummaryLogTy &Log, ExplodedNode *n, SymbolRef sym, - CheckerContext &Ctx) -: CFRefReport(D, LOpts, GCEnabled, Log, n, sym, false) { + CheckerContext &Ctx, + bool IncludeAllocationLine) + : CFRefReport(D, LOpts, GCEnabled, Log, n, sym, false) { // Most bug reports are cached at the location where they occurred. // With leaks, we want to unique them by the location where they were @@ -2282,9 +2343,13 @@ CFRefLeakReport::CFRefLeakReport(CFRefBug &D, const LangOptions &LOpts, const SourceManager& SMgr = Ctx.getSourceManager(); - llvm::tie(AllocNode, AllocBinding) = // Set AllocBinding. + AllocationInfo AllocI = GetAllocationSite(Ctx.getStateManager(), getErrorNode(), sym); + AllocNode = AllocI.N; + AllocBinding = AllocI.R; + markInteresting(AllocI.InterestingMethodContext); + // 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.) @@ -2295,8 +2360,17 @@ CFRefLeakReport::CFRefLeakReport(CFRefBug &D, const LangOptions &LOpts, else AllocStmt = P.castAs().getStmt(); assert(AllocStmt && "All allocations must come from explicit calls"); - Location = PathDiagnosticLocation::createBegin(AllocStmt, SMgr, - n->getLocationContext()); + + PathDiagnosticLocation AllocLocation = + PathDiagnosticLocation::createBegin(AllocStmt, SMgr, + AllocNode->getLocationContext()); + Location = AllocLocation; + + // Set uniqieing info, which will be used for unique the bug reports. The + // leaks should be uniqued on the allocation site. + UniqueingLocation = AllocLocation; + UniqueingDecl = AllocNode->getLocationContext()->getDecl(); + // Fill in the description of the bug. Description.clear(); llvm::raw_string_ostream os(Description); @@ -2305,9 +2379,13 @@ CFRefLeakReport::CFRefLeakReport(CFRefBug &D, const LangOptions &LOpts, os << "(when using garbage collection) "; os << "of an object"; - // FIXME: AllocBinding doesn't get populated for RegionStore yet. - if (AllocBinding) + if (AllocBinding) { os << " stored into '" << AllocBinding->getString() << '\''; + if (IncludeAllocationLine) { + FullSourceLoc SL(AllocStmt->getLocStart(), Ctx.getSourceManager()); + os << " (allocated on line " << SL.getSpellingLineNumber() << ")"; + } + } addVisitor(new CFRefLeakReportVisitor(sym, GCEnabled, Log)); } @@ -2348,8 +2426,14 @@ class RetainCountChecker mutable SummaryLogTy SummaryLog; mutable bool ShouldResetSummaryLog; + /// Optional setting to indicate if leak reports should include + /// the allocation line. + mutable bool IncludeAllocationLine; + public: - RetainCountChecker() : ShouldResetSummaryLog(false) {} + RetainCountChecker(AnalyzerOptions &AO) + : ShouldResetSummaryLog(false), + IncludeAllocationLine(shouldIncludeAllocationSiteInLeakDiagnostics(AO)) {} virtual ~RetainCountChecker() { DeleteContainerSeconds(DeadSymbolTags); @@ -3294,7 +3378,8 @@ void RetainCountChecker::checkReturnWithRetEffect(const ReturnStmt *S, CFRefReport *report = new CFRefLeakReport(*getLeakAtReturnBug(LOpts, GCEnabled), LOpts, GCEnabled, SummaryLog, - N, Sym, C); + N, Sym, C, IncludeAllocationLine); + C.emitReport(report); } } @@ -3480,10 +3565,12 @@ RetainCountChecker::handleAutoreleaseCounts(ProgramStateRef state, if (N) { SmallString<128> sbuf; llvm::raw_svector_ostream os(sbuf); - os << "Object over-autoreleased: object was sent -autorelease "; + os << "Object was autoreleased "; if (V.getAutoreleaseCount() > 1) - os << V.getAutoreleaseCount() << " times "; - os << "but the object has a +" << V.getCount() << " retain count"; + os << V.getAutoreleaseCount() << " times but the object "; + else + os << "but "; + os << "has a +" << V.getCount() << " retain count"; if (!overAutorelease) overAutorelease.reset(new OverAutorelease()); @@ -3534,7 +3621,8 @@ RetainCountChecker::processLeaks(ProgramStateRef state, assert(BT && "BugType not initialized."); CFRefLeakReport *report = new CFRefLeakReport(*BT, LOpts, GCEnabled, - SummaryLog, N, *I, Ctx); + SummaryLog, N, *I, Ctx, + IncludeAllocationLine); Ctx.emitReport(report); } } @@ -3656,6 +3744,6 @@ void RetainCountChecker::printState(raw_ostream &Out, ProgramStateRef State, //===----------------------------------------------------------------------===// void ento::registerRetainCountChecker(CheckerManager &Mgr) { - Mgr.registerChecker(); + Mgr.registerChecker(Mgr.getAnalyzerOptions()); } diff --git a/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp b/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp index 7a5d9936010..ed96c401a7a 100644 --- a/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp @@ -55,8 +55,17 @@ void ReturnUndefChecker::checkPreStmt(const ReturnStmt *RS, // void test() { // return foo(); // } - if (RT.isNull() || !RT->isVoidType()) - emitUndef(C, RetE); + if (!RT.isNull() && RT->isVoidType()) + return; + + // Not all blocks have explicitly-specified return types; if the return type + // is not available, but the return value expression has 'void' type, assume + // Sema already checked it. + if (RT.isNull() && isa(SFC->getDecl()) && + RetE->getType()->isVoidType()) + return; + + emitUndef(C, RetE); return; } diff --git a/lib/StaticAnalyzer/Core/BugReporter.cpp b/lib/StaticAnalyzer/Core/BugReporter.cpp index 8f8eb3bb850..a85235c3e40 100644 --- a/lib/StaticAnalyzer/Core/BugReporter.cpp +++ b/lib/StaticAnalyzer/Core/BugReporter.cpp @@ -52,77 +52,22 @@ void BugReporterContext::anchor() {} // Helper routines for walking the ExplodedGraph and fetching statements. //===----------------------------------------------------------------------===// -static inline const Stmt *GetStmt(const ProgramPoint &P) { - if (Optional SP = P.getAs()) - return SP->getStmt(); - if (Optional BE = P.getAs()) - return BE->getSrc()->getTerminator(); - if (Optional CE = P.getAs()) - return CE->getCallExpr(); - if (Optional CEE = P.getAs()) - return CEE->getCalleeContext()->getCallSite(); - - return 0; -} - -static inline const ExplodedNode* -GetPredecessorNode(const ExplodedNode *N) { - return N->pred_empty() ? NULL : *(N->pred_begin()); -} - -static inline const ExplodedNode* -GetSuccessorNode(const ExplodedNode *N) { - return N->succ_empty() ? NULL : *(N->succ_begin()); -} - static const Stmt *GetPreviousStmt(const ExplodedNode *N) { - for (N = GetPredecessorNode(N); N; N = GetPredecessorNode(N)) - if (const Stmt *S = GetStmt(N->getLocation())) + for (N = N->getFirstPred(); N; N = N->getFirstPred()) + if (const Stmt *S = PathDiagnosticLocation::getStmt(N)) return S; return 0; } -static const Stmt *GetNextStmt(const ExplodedNode *N) { - for (N = GetSuccessorNode(N); N; N = GetSuccessorNode(N)) - if (const Stmt *S = GetStmt(N->getLocation())) { - // Check if the statement is '?' or '&&'/'||'. These are "merges", - // not actual statement points. - switch (S->getStmtClass()) { - case Stmt::ChooseExprClass: - case Stmt::BinaryConditionalOperatorClass: continue; - case Stmt::ConditionalOperatorClass: continue; - case Stmt::BinaryOperatorClass: { - BinaryOperatorKind Op = cast(S)->getOpcode(); - if (Op == BO_LAnd || Op == BO_LOr) - continue; - break; - } - default: - break; - } - return S; - } - - return 0; -} - static inline const Stmt* GetCurrentOrPreviousStmt(const ExplodedNode *N) { - if (const Stmt *S = GetStmt(N->getLocation())) + if (const Stmt *S = PathDiagnosticLocation::getStmt(N)) return S; return GetPreviousStmt(N); } -static inline const Stmt* -GetCurrentOrNextStmt(const ExplodedNode *N) { - if (const Stmt *S = GetStmt(N->getLocation())) - return S; - - return GetNextStmt(N); -} - //===----------------------------------------------------------------------===// // Diagnostic cleanup. //===----------------------------------------------------------------------===// @@ -198,10 +143,16 @@ static void removeRedundantMsgs(PathPieces &path) { } } +/// A map from PathDiagnosticPiece to the LocationContext of the inlined +/// function call it represents. +typedef llvm::DenseMap + LocationContextMap; + /// Recursively scan through a path and prune out calls and macros pieces /// that aren't needed. Return true if afterwards the path contains /// "interesting stuff" which means it shouldn't be pruned from the parent path. -bool BugReporter::RemoveUnneededCalls(PathPieces &pieces, BugReport *R) { +static bool removeUnneededCalls(PathPieces &pieces, BugReport *R, + LocationContextMap &LCM) { bool containsSomethingInteresting = false; const unsigned N = pieces.size(); @@ -222,13 +173,13 @@ bool BugReporter::RemoveUnneededCalls(PathPieces &pieces, BugReport *R) { case PathDiagnosticPiece::Call: { PathDiagnosticCallPiece *call = cast(piece); // Check if the location context is interesting. - assert(LocationContextMap.count(call)); - if (R->isInteresting(LocationContextMap[call])) { + assert(LCM.count(&call->path)); + if (R->isInteresting(LCM[&call->path])) { containsSomethingInteresting = true; break; } - if (!RemoveUnneededCalls(call->path, R)) + if (!removeUnneededCalls(call->path, R, LCM)) continue; containsSomethingInteresting = true; @@ -236,7 +187,7 @@ bool BugReporter::RemoveUnneededCalls(PathPieces &pieces, BugReport *R) { } case PathDiagnosticPiece::Macro: { PathDiagnosticMacroPiece *macro = cast(piece); - if (!RemoveUnneededCalls(macro->subPieces, R)) + if (!removeUnneededCalls(macro->subPieces, R, LCM)) continue; containsSomethingInteresting = true; break; @@ -355,7 +306,7 @@ class PathDiagnosticBuilder : public BugReporterContext { PathDiagnosticLocation PathDiagnosticBuilder::ExecutionContinues(const ExplodedNode *N) { - if (const Stmt *S = GetNextStmt(N)) + if (const Stmt *S = PathDiagnosticLocation::getNextStmt(N)) return PathDiagnosticLocation(S, getSourceManager(), LC); return PathDiagnosticLocation::createDeclEnd(N->getLocationContext(), @@ -566,6 +517,7 @@ static void CompactPathDiagnostic(PathPieces &path, const SourceManager& SM); static bool GenerateMinimalPathDiagnostic(PathDiagnostic& PD, PathDiagnosticBuilder &PDB, const ExplodedNode *N, + LocationContextMap &LCM, ArrayRef visitors) { SourceManager& SMgr = PDB.getSourceManager(); @@ -578,7 +530,7 @@ static bool GenerateMinimalPathDiagnostic(PathDiagnostic& PD, while (NextNode) { N = NextNode; PDB.LC = N->getLocationContext(); - NextNode = GetPredecessorNode(N); + NextNode = N->getFirstPred(); ProgramPoint P = N->getLocation(); @@ -586,8 +538,8 @@ static bool GenerateMinimalPathDiagnostic(PathDiagnostic& PD, if (Optional CE = P.getAs()) { PathDiagnosticCallPiece *C = PathDiagnosticCallPiece::construct(N, *CE, SMgr); - GRBugReporter& BR = PDB.getBugReporter(); - BR.addCallPieceLocationContextPair(C, CE->getCalleeContext()); + // Record the mapping from call piece to LocationContext. + LCM[&C->path] = CE->getCalleeContext(); PD.getActivePath().push_front(C); PD.pushActivePath(&C->path); CallStack.push_back(StackDiagPair(C, N)); @@ -610,8 +562,8 @@ static bool GenerateMinimalPathDiagnostic(PathDiagnostic& PD, } else { const Decl *Caller = CE->getLocationContext()->getDecl(); C = PathDiagnosticCallPiece::construct(PD.getActivePath(), Caller); - GRBugReporter& BR = PDB.getBugReporter(); - BR.addCallPieceLocationContextPair(C, CE->getCalleeContext()); + // Record the mapping from call piece to LocationContext. + LCM[&C->path] = CE->getCalleeContext(); } C->setCallee(*CE, SMgr); @@ -640,7 +592,7 @@ static bool GenerateMinimalPathDiagnostic(PathDiagnostic& PD, case Stmt::GotoStmtClass: case Stmt::IndirectGotoStmtClass: { - const Stmt *S = GetNextStmt(N); + const Stmt *S = PathDiagnosticLocation::getNextStmt(N); if (!S) break; @@ -929,6 +881,50 @@ class ContextLocation : public PathDiagnosticLocation { bool isDead() const { return IsDead; } }; +static PathDiagnosticLocation cleanUpLocation(PathDiagnosticLocation L, + const LocationContext *LC, + bool firstCharOnly = false) { + if (const Stmt *S = L.asStmt()) { + const Stmt *Original = S; + while (1) { + // Adjust the location for some expressions that are best referenced + // by one of their subexpressions. + switch (S->getStmtClass()) { + default: + break; + case Stmt::ParenExprClass: + case Stmt::GenericSelectionExprClass: + S = cast(S)->IgnoreParens(); + firstCharOnly = true; + continue; + case Stmt::BinaryConditionalOperatorClass: + case Stmt::ConditionalOperatorClass: + S = cast(S)->getCond(); + firstCharOnly = true; + continue; + case Stmt::ChooseExprClass: + S = cast(S)->getCond(); + firstCharOnly = true; + continue; + case Stmt::BinaryOperatorClass: + S = cast(S)->getLHS(); + firstCharOnly = true; + continue; + } + + break; + } + + if (S != Original) + L = PathDiagnosticLocation(S, L.getManager(), LC); + } + + if (firstCharOnly) + L = PathDiagnosticLocation::createSingleLocation(L); + + return L; +} + class EdgeBuilder { std::vector CLocs; typedef std::vector::iterator iterator; @@ -943,53 +939,12 @@ class EdgeBuilder { PathDiagnosticLocation getContextLocation(const PathDiagnosticLocation &L); - PathDiagnosticLocation cleanUpLocation(PathDiagnosticLocation L, - bool firstCharOnly = false) { - if (const Stmt *S = L.asStmt()) { - const Stmt *Original = S; - while (1) { - // Adjust the location for some expressions that are best referenced - // by one of their subexpressions. - switch (S->getStmtClass()) { - default: - break; - case Stmt::ParenExprClass: - case Stmt::GenericSelectionExprClass: - S = cast(S)->IgnoreParens(); - firstCharOnly = true; - continue; - case Stmt::BinaryConditionalOperatorClass: - case Stmt::ConditionalOperatorClass: - S = cast(S)->getCond(); - firstCharOnly = true; - continue; - case Stmt::ChooseExprClass: - S = cast(S)->getCond(); - firstCharOnly = true; - continue; - case Stmt::BinaryOperatorClass: - S = cast(S)->getLHS(); - firstCharOnly = true; - continue; - } - - break; - } - if (S != Original) - L = PathDiagnosticLocation(S, L.getManager(), PDB.LC); - } - - if (firstCharOnly) - L = PathDiagnosticLocation::createSingleLocation(L); - - return L; - } void popLocation() { if (!CLocs.back().isDead() && CLocs.back().asLocation().isFileID()) { // For contexts, we only one the first character as the range. - rawAddEdge(cleanUpLocation(CLocs.back(), true)); + rawAddEdge(cleanUpLocation(CLocs.back(), PDB.LC, true)); } CLocs.pop_back(); } @@ -1026,7 +981,8 @@ class EdgeBuilder { PrevLoc = PathDiagnosticLocation(); } - void addEdge(PathDiagnosticLocation NewLoc, bool alwaysAdd = false); + void addEdge(PathDiagnosticLocation NewLoc, bool alwaysAdd = false, + bool IsPostJump = false); void rawAddEdge(PathDiagnosticLocation NewLoc); @@ -1102,8 +1058,8 @@ void EdgeBuilder::rawAddEdge(PathDiagnosticLocation NewLoc) { return; } - const PathDiagnosticLocation &NewLocClean = cleanUpLocation(NewLoc); - const PathDiagnosticLocation &PrevLocClean = cleanUpLocation(PrevLoc); + const PathDiagnosticLocation &NewLocClean = cleanUpLocation(NewLoc, PDB.LC); + const PathDiagnosticLocation &PrevLocClean = cleanUpLocation(PrevLoc, PDB.LC); if (PrevLocClean.asLocation().isInvalid()) { PrevLoc = NewLoc; @@ -1122,7 +1078,8 @@ void EdgeBuilder::rawAddEdge(PathDiagnosticLocation NewLoc) { PrevLoc = NewLoc; } -void EdgeBuilder::addEdge(PathDiagnosticLocation NewLoc, bool alwaysAdd) { +void EdgeBuilder::addEdge(PathDiagnosticLocation NewLoc, bool alwaysAdd, + bool IsPostJump) { if (!alwaysAdd && NewLoc.asLocation().isMacroID()) return; @@ -1135,13 +1092,14 @@ void EdgeBuilder::addEdge(PathDiagnosticLocation NewLoc, bool alwaysAdd) { // Is the top location context the same as the one for the new location? if (TopContextLoc == CLoc) { if (alwaysAdd) { - if (IsConsumedExpr(TopContextLoc) && - !IsControlFlowExpr(TopContextLoc.asStmt())) - TopContextLoc.markDead(); + if (IsConsumedExpr(TopContextLoc)) + TopContextLoc.markDead(); rawAddEdge(NewLoc); } + if (IsPostJump) + TopContextLoc.markDead(); return; } @@ -1149,13 +1107,13 @@ void EdgeBuilder::addEdge(PathDiagnosticLocation NewLoc, bool alwaysAdd) { if (alwaysAdd) { rawAddEdge(NewLoc); - if (IsConsumedExpr(CLoc) && !IsControlFlowExpr(CLoc.asStmt())) { - CLocs.push_back(ContextLocation(CLoc, true)); + if (IsConsumedExpr(CLoc)) { + CLocs.push_back(ContextLocation(CLoc, /*IsDead=*/true)); return; } } - CLocs.push_back(CLoc); + CLocs.push_back(ContextLocation(CLoc, /*IsDead=*/IsPostJump)); return; } @@ -1340,7 +1298,7 @@ static const Stmt *getStmtBeforeCond(ParentMap &PM, const Stmt *Term, if (!isContainedByStmt(PM, Term, S)) return S; } - N = GetPredecessorNode(N); + N = N->getFirstPred(); } return 0; } @@ -1376,6 +1334,7 @@ static bool isInLoopBody(ParentMap &PM, const Stmt *S, const Stmt *Term) { static bool GenerateExtensivePathDiagnostic(PathDiagnostic& PD, PathDiagnosticBuilder &PDB, const ExplodedNode *N, + LocationContextMap &LCM, ArrayRef visitors) { EdgeBuilder EB(PD, PDB); const SourceManager& SM = PDB.getSourceManager(); @@ -1385,7 +1344,7 @@ static bool GenerateExtensivePathDiagnostic(PathDiagnostic& PD, const ExplodedNode *NextNode = N->pred_empty() ? NULL : *(N->pred_begin()); while (NextNode) { N = NextNode; - NextNode = GetPredecessorNode(N); + NextNode = N->getFirstPred(); ProgramPoint P = N->getLocation(); do { @@ -1406,10 +1365,9 @@ static bool GenerateExtensivePathDiagnostic(PathDiagnostic& PD, PathDiagnosticCallPiece *C = PathDiagnosticCallPiece::construct(N, *CE, SM); - GRBugReporter& BR = PDB.getBugReporter(); - BR.addCallPieceLocationContextPair(C, CE->getCalleeContext()); + LCM[&C->path] = CE->getCalleeContext(); - EB.addEdge(C->callReturn, true); + EB.addEdge(C->callReturn, /*AlwaysAdd=*/true, /*IsPostJump=*/true); EB.flushLocations(); PD.getActivePath().push_front(C); @@ -1444,8 +1402,7 @@ static bool GenerateExtensivePathDiagnostic(PathDiagnostic& PD, } else { const Decl *Caller = CE->getLocationContext()->getDecl(); C = PathDiagnosticCallPiece::construct(PD.getActivePath(), Caller); - GRBugReporter& BR = PDB.getBugReporter(); - BR.addCallPieceLocationContextPair(C, CE->getCalleeContext()); + LCM[&C->path] = CE->getCalleeContext(); } C->setCallee(*CE, SM); @@ -1573,6 +1530,458 @@ static bool GenerateExtensivePathDiagnostic(PathDiagnostic& PD, return PDB.getBugReport()->isValid(); } +/// \brief Adds a sanitized control-flow diagnostic edge to a path. +static void addEdgeToPath(PathPieces &path, + PathDiagnosticLocation &PrevLoc, + PathDiagnosticLocation NewLoc, + const LocationContext *LC) { + if (!NewLoc.isValid()) + return; + + SourceLocation NewLocL = NewLoc.asLocation(); + if (NewLocL.isInvalid() || NewLocL.isMacroID()) + return; + + if (!PrevLoc.isValid()) { + PrevLoc = NewLoc; + return; + } + + // FIXME: ignore intra-macro edges for now. + if (NewLoc.asLocation().getExpansionLoc() == + PrevLoc.asLocation().getExpansionLoc()) + return; + + path.push_front(new PathDiagnosticControlFlowPiece(NewLoc, + PrevLoc)); + PrevLoc = NewLoc; +} + +static bool +GenerateAlternateExtensivePathDiagnostic(PathDiagnostic& PD, + PathDiagnosticBuilder &PDB, + const ExplodedNode *N, + LocationContextMap &LCM, + ArrayRef visitors) { + + BugReport *report = PDB.getBugReport(); + const SourceManager& SM = PDB.getSourceManager(); + StackDiagVector CallStack; + InterestingExprs IE; + + // Record the last location for a given visited stack frame. + llvm::DenseMap + PrevLocMap; + + const ExplodedNode *NextNode = N->getFirstPred(); + while (NextNode) { + N = NextNode; + NextNode = N->getFirstPred(); + ProgramPoint P = N->getLocation(); + const LocationContext *LC = N->getLocationContext(); + assert(!LCM[&PD.getActivePath()] || LCM[&PD.getActivePath()] == LC); + LCM[&PD.getActivePath()] = LC; + PathDiagnosticLocation &PrevLoc = PrevLocMap[LC->getCurrentStackFrame()]; + + do { + if (Optional PS = P.getAs()) { + // For expressions, make sure we propagate the + // interesting symbols correctly. + if (const Expr *Ex = PS->getStmtAs()) + reversePropagateIntererstingSymbols(*PDB.getBugReport(), IE, + N->getState().getPtr(), Ex, + N->getLocationContext()); + + PathDiagnosticLocation L = + PathDiagnosticLocation(PS->getStmt(), SM, LC); + addEdgeToPath(PD.getActivePath(), PrevLoc, L, LC); + break; + } + + // Have we encountered an exit from a function call? + if (Optional CE = P.getAs()) { + const Stmt *S = CE->getCalleeContext()->getCallSite(); + // Propagate the interesting symbols accordingly. + if (const Expr *Ex = dyn_cast_or_null(S)) { + reversePropagateIntererstingSymbols(*PDB.getBugReport(), IE, + N->getState().getPtr(), Ex, + N->getLocationContext()); + } + + // We are descending into a call (backwards). Construct + // a new call piece to contain the path pieces for that call. + PathDiagnosticCallPiece *C = + PathDiagnosticCallPiece::construct(N, *CE, SM); + + // Record the location context for this call piece. + LCM[&C->path] = CE->getCalleeContext(); + + // Add the edge to the return site. + addEdgeToPath(PD.getActivePath(), PrevLoc, C->callReturn, LC); + + // Make the contents of the call the active path for now. + PD.pushActivePath(&C->path); + CallStack.push_back(StackDiagPair(C, N)); + break; + } + + // Have we encountered an entrance to a call? It may be + // the case that we have not encountered a matching + // call exit before this point. This means that the path + // terminated within the call itself. + if (Optional CE = P.getAs()) { + // Add an edge to the start of the function. + const Decl *D = CE->getCalleeContext()->getDecl(); + addEdgeToPath(PD.getActivePath(), PrevLoc, + PathDiagnosticLocation::createBegin(D, SM), LC); + + // Did we visit an entire call? + bool VisitedEntireCall = PD.isWithinCall(); + PD.popActivePath(); + + PathDiagnosticCallPiece *C; + if (VisitedEntireCall) { + C = cast(PD.getActivePath().front()); + } else { + const Decl *Caller = CE->getLocationContext()->getDecl(); + C = PathDiagnosticCallPiece::construct(PD.getActivePath(), Caller); + LCM[&C->path] = CE->getCalleeContext(); + } + C->setCallee(*CE, SM); + + if (!CallStack.empty()) { + assert(CallStack.back().first == C); + CallStack.pop_back(); + } + break; + } + + // Block edges. + if (Optional BE = P.getAs()) { + // 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); + } + } + + // Are we jumping to the head of a loop? Add a special diagnostic. + if (const Stmt *Loop = BE->getSrc()->getLoopTarget()) { + PathDiagnosticLocation L(Loop, SM, PDB.LC); + const CompoundStmt *CS = NULL; + + if (const ForStmt *FS = dyn_cast(Loop)) + CS = dyn_cast(FS->getBody()); + else if (const WhileStmt *WS = dyn_cast(Loop)) + CS = dyn_cast(WS->getBody()); + + PathDiagnosticEventPiece *p = + new PathDiagnosticEventPiece(L, "Looping back to the head " + "of the loop"); + p->setPrunable(true); + + addEdgeToPath(PD.getActivePath(), PrevLoc, p->getLocation(), LC); + PD.getActivePath().push_front(p); + + if (CS) { + addEdgeToPath(PD.getActivePath(), PrevLoc, + PathDiagnosticLocation::createEndBrace(CS, SM), LC); + } + } + + const CFGBlock *BSrc = BE->getSrc(); + ParentMap &PM = PDB.getParentMap(); + + if (const Stmt *Term = BSrc->getTerminator()) { + // Are we jumping past the loop body without ever executing the + // loop (because the condition was false)? + if (isLoopJumpPastBody(Term, &*BE) && + !isInLoopBody(PM, + getStmtBeforeCond(PM, + BSrc->getTerminatorCondition(), + N), + Term)) + { + PathDiagnosticLocation L(Term, SM, PDB.LC); + PathDiagnosticEventPiece *PE = + new PathDiagnosticEventPiece(L, "Loop body executed 0 times"); + PE->setPrunable(true); + addEdgeToPath(PD.getActivePath(), PrevLoc, + PE->getLocation(), LC); + PD.getActivePath().push_front(PE); + } + } + break; + } + } while (0); + + if (!NextNode) + continue; + + // Add pieces from custom visitors. + for (ArrayRef::iterator I = visitors.begin(), + E = visitors.end(); + I != E; ++I) { + if (PathDiagnosticPiece *p = (*I)->VisitNode(N, NextNode, PDB, *report)) { + addEdgeToPath(PD.getActivePath(), PrevLoc, p->getLocation(), LC); + PD.getActivePath().push_front(p); + updateStackPiecesWithMessage(p, CallStack); + } + } + } + + return report->isValid(); +} + +const Stmt *getLocStmt(PathDiagnosticLocation L) { + if (!L.isValid()) + return 0; + return L.asStmt(); +} + +const Stmt *getStmtParent(const Stmt *S, ParentMap &PM) { + if (!S) + return 0; + return PM.getParentIgnoreParens(S); +} + +#if 0 +static bool isConditionForTerminator(const Stmt *S, const Stmt *Cond) { + // Note that we intentionally to do not handle || and && here. + switch (S->getStmtClass()) { + case Stmt::ForStmtClass: + return cast(S)->getCond() == Cond; + case Stmt::WhileStmtClass: + return cast(S)->getCond() == Cond; + case Stmt::DoStmtClass: + return cast(S)->getCond() == Cond; + case Stmt::ChooseExprClass: + return cast(S)->getCond() == Cond; + case Stmt::IndirectGotoStmtClass: + return cast(S)->getTarget() == Cond; + case Stmt::SwitchStmtClass: + return cast(S)->getCond() == Cond; + case Stmt::BinaryConditionalOperatorClass: + return cast(S)->getCond() == Cond; + case Stmt::ConditionalOperatorClass: + return cast(S)->getCond() == Cond; + case Stmt::ObjCForCollectionStmtClass: + return cast(S)->getElement() == Cond; + default: + return false; + } +} +#endif + +typedef llvm::DenseSet + ControlFlowBarrierSet; + +typedef llvm::DenseSet + OptimizedCallsSet; + +static bool isBarrier(ControlFlowBarrierSet &CFBS, + const PathDiagnosticControlFlowPiece *P) { + return CFBS.count(P); +} + +static bool optimizeEdges(PathPieces &path, SourceManager &SM, + ControlFlowBarrierSet &CFBS, + OptimizedCallsSet &OCS, + LocationContextMap &LCM) { + bool hasChanges = false; + const LocationContext *LC = LCM[&path]; + assert(LC); + bool isFirst = true; + + for (PathPieces::iterator I = path.begin(), E = path.end(); I != E; ) { + bool wasFirst = isFirst; + isFirst = false; + + // Optimize subpaths. + if (PathDiagnosticCallPiece *CallI = dyn_cast(*I)){ + // Record the fact that a call has been optimized so we only do the + // effort once. + if (!OCS.count(CallI)) { + while (optimizeEdges(CallI->path, SM, CFBS, OCS, LCM)) {} + OCS.insert(CallI); + } + ++I; + continue; + } + + // Pattern match the current piece and its successor. + PathDiagnosticControlFlowPiece *PieceI = + dyn_cast(*I); + + if (!PieceI) { + ++I; + continue; + } + + ParentMap &PM = LC->getParentMap(); + const Stmt *s1Start = getLocStmt(PieceI->getStartLocation()); + const Stmt *s1End = getLocStmt(PieceI->getEndLocation()); + const Stmt *level1 = getStmtParent(s1Start, PM); + const Stmt *level2 = getStmtParent(s1End, PM); + + if (wasFirst) { +#if 0 + // Apply the "first edge" case for Rule V. here. + if (s1Start && level1 && isConditionForTerminator(level1, s1Start)) { + PathDiagnosticLocation NewLoc(level2, SM, LC); + PieceI->setStartLocation(NewLoc); + CFBS.insert(PieceI); + return true; + } +#endif + // Apply the "first edge" case for Rule III. here. + if (!isBarrier(CFBS, PieceI) && + level1 && level2 && level2 == PM.getParent(level1)) { + path.erase(I); + // Since we are erasing the current edge at the start of the + // path, just return now so we start analyzing the start of the path + // again. + return true; + } + } + + PathPieces::iterator NextI = I; ++NextI; + if (NextI == E) + break; + + PathDiagnosticControlFlowPiece *PieceNextI = + dyn_cast(*NextI); + + if (!PieceNextI) { + ++I; + continue; + } + + const Stmt *s2Start = getLocStmt(PieceNextI->getStartLocation()); + const Stmt *s2End = getLocStmt(PieceNextI->getEndLocation()); + const Stmt *level3 = getStmtParent(s2Start, PM); + const Stmt *level4 = getStmtParent(s2End, PM); + + // Rule I. + // + // If we have two consecutive control edges whose end/begin locations + // are at the same level (e.g. statements or top-level expressions within + // a compound statement, or siblings share a single ancestor expression), + // then merge them if they have no interesting intermediate event. + // + // For example: + // + // (1.1 -> 1.2) -> (1.2 -> 1.3) becomes (1.1 -> 1.3) because the common + // parent is '1'. Here 'x.y.z' represents the hierarchy of statements. + // + // NOTE: this will be limited later in cases where we add barriers + // to prevent this optimization. + // + if (level1 && level1 == level2 && level1 == level3 && level1 == level4) { + PieceI->setEndLocation(PieceNextI->getEndLocation()); + path.erase(NextI); + hasChanges = true; + continue; + } + + // Rule II. + // + // If we have two consecutive control edges where we decend to a + // subexpression and then pop out merge them. + // + // NOTE: this will be limited later in cases where we add barriers + // to prevent this optimization. + // + // For example: + // + // (1.1 -> 1.1.1) -> (1.1.1 -> 1.2) becomes (1.1 -> 1.2). + if (level1 && level2 && + level1 == level4 && + level2 == level3 && PM.getParentIgnoreParens(level2) == level1) { + PieceI->setEndLocation(PieceNextI->getEndLocation()); + path.erase(NextI); + hasChanges = true; + continue; + } + + // Rule III. + // + // Eliminate unnecessary edges where we descend to a subexpression from + // a statement at the same level as our parent. + // + // NOTE: this will be limited later in cases where we add barriers + // to prevent this optimization. + // + // For example: + // + // (1.1 -> 1.1.1) -> (1.1.1 -> X) becomes (1.1 -> X). + // + if (level1 && level2 && level1 == PM.getParentIgnoreParens(level2)) { + PieceI->setEndLocation(PieceNextI->getEndLocation()); + path.erase(NextI); + hasChanges = true; + continue; + } + + // Rule IV. + // + // Eliminate unnecessary edges where we ascend from a subexpression to + // a statement at the same level as our parent. + // + // NOTE: this will be limited later in cases where we add barriers + // to prevent this optimization. + // + // For example: + // + // (X -> 1.1.1) -> (1.1.1 -> 1.1) becomes (X -> 1.1). + // [first edge] (1.1.1 -> 1.1) -> eliminate + // + if (level2 && level4 && level2 == level3 && level4 == PM.getParent(level2)){ + PieceI->setEndLocation(PieceNextI->getEndLocation()); + path.erase(NextI); + hasChanges = true; + continue; + } +#if 0 + // Rule V. + // + // Replace terminator conditions with terminators when the condition + // itself has no control-flow. + // + // For example: + // + // (X -> condition) -> (condition -> Y) becomes (X -> term) -> (term -> Y) + // [first edge] (condition -> Y) becomes (term -> Y) + // + // This applies to 'if', 'for', 'while', 'do .. while', 'switch'... + // + if (!isBarrier(CFBS, PieceNextI) && + s1End && s1End == s2Start && level2) { + if (isConditionForTerminator(level2, s1End)) { + PathDiagnosticLocation NewLoc(level2, SM, LC); + PieceI->setEndLocation(NewLoc); + PieceNextI->setStartLocation(NewLoc); + CFBS.insert(PieceI); + hasChanges = true; + continue; + } + + } +#endif + + // No changes at this index? Move to the next one. + ++I; + } + + // No changes. + return hasChanges; +} + //===----------------------------------------------------------------------===// // Methods for BugType and subclasses. //===----------------------------------------------------------------------===// @@ -1758,7 +2167,7 @@ const Stmt *BugReport::getStmt() const { S = GetPreviousStmt(ErrorNode); } if (!S) - S = GetStmt(ProgP); + S = PathDiagnosticLocation::getStmt(ErrorNode); return S; } @@ -1785,22 +2194,7 @@ PathDiagnosticLocation BugReport::getLocation(const SourceManager &SM) const { if (ErrorNode) { assert(!Location.isValid() && "Either Location or ErrorNode should be specified but not both."); - - if (const Stmt *S = GetCurrentOrPreviousStmt(ErrorNode)) { - const LocationContext *LC = ErrorNode->getLocationContext(); - - // For member expressions, return the location of the '.' or '->'. - if (const MemberExpr *ME = dyn_cast(S)) - return PathDiagnosticLocation::createMemberLoc(ME, SM); - // For binary operators, return the location of the operator. - if (const BinaryOperator *B = dyn_cast(S)) - return PathDiagnosticLocation::createOperatorLoc(B, SM); - - if (ErrorNode->getLocation().getAs()) - return PathDiagnosticLocation::createEnd(S, SM, LC); - - return PathDiagnosticLocation::createBegin(S, SM, LC); - } + return PathDiagnosticLocation::createEndOfPath(ErrorNode, SM); } else { assert(Location.isValid()); return Location; @@ -2010,7 +2404,8 @@ bool TrimmedGraph::popNextReportGraph(ReportGraph &GraphWrapper) { while (true) { // Create the equivalent node in the new graph with the same state // and location. - ExplodedNode *NewN = GNew->getNode(OrigN->getLocation(), OrigN->getState()); + ExplodedNode *NewN = GNew->getNode(OrigN->getLocation(), OrigN->getState(), + OrigN->isSink()); // Store the mapping to the original node. InterExplodedGraphMap::const_iterator IMitr = InverseMap.find(OrigN); @@ -2165,6 +2560,13 @@ bool GRBugReporter::generatePathDiagnostic(PathDiagnostic& PD, typedef PathDiagnosticConsumer::PathGenerationScheme PathGenerationScheme; PathGenerationScheme ActiveScheme = PC.getGenerationScheme(); + if (ActiveScheme == PathDiagnosticConsumer::Extensive) { + AnalyzerOptions &options = getEngine().getAnalysisManager().options; + if (options.getBooleanOption("path-diagnostics-alternate", false)) { + ActiveScheme = PathDiagnosticConsumer::AlternateExtensive; + } + } + TrimmedGraph TrimG(&getGraph(), errorNodes); ReportGraph ErrorGraph; @@ -2186,6 +2588,7 @@ bool GRBugReporter::generatePathDiagnostic(PathDiagnostic& PD, BugReport::VisitorList visitors; unsigned origReportConfigToken, finalReportConfigToken; + LocationContextMap LCM; // While generating diagnostics, it's possible the visitors will decide // new symbols and regions are interesting, or add other visitors based on @@ -2220,12 +2623,19 @@ bool GRBugReporter::generatePathDiagnostic(PathDiagnostic& PD, PD.setEndOfPath(LastPiece); } + // Make sure we get a clean location context map so we don't + // hold onto old mappings. + LCM.clear(); + switch (ActiveScheme) { + case PathDiagnosticConsumer::AlternateExtensive: + GenerateAlternateExtensivePathDiagnostic(PD, PDB, N, LCM, visitors); + break; case PathDiagnosticConsumer::Extensive: - GenerateExtensivePathDiagnostic(PD, PDB, N, visitors); + GenerateExtensivePathDiagnostic(PD, PDB, N, LCM, visitors); break; case PathDiagnosticConsumer::Minimal: - GenerateMinimalPathDiagnostic(PD, PDB, N, visitors); + GenerateMinimalPathDiagnostic(PD, PDB, N, LCM, visitors); break; case PathDiagnosticConsumer::None: GenerateVisitorsOnlyPathDiagnostic(PD, PDB, N, visitors); @@ -2249,12 +2659,19 @@ bool GRBugReporter::generatePathDiagnostic(PathDiagnostic& PD, if (R->shouldPrunePath() && getEngine().getAnalysisManager().options.shouldPrunePaths()) { - bool stillHasNotes = RemoveUnneededCalls(PD.getMutablePieces(), R); + bool stillHasNotes = removeUnneededCalls(PD.getMutablePieces(), R, LCM); assert(stillHasNotes); (void)stillHasNotes; } adjustCallLocations(PD.getMutablePieces()); + + if (ActiveScheme == PathDiagnosticConsumer::AlternateExtensive) { + ControlFlowBarrierSet CFBS; + OptimizedCallsSet OCS; + while (optimizeEdges(PD.getMutablePieces(), getSourceManager(), CFBS, + OCS, LCM)) {} + } } // We found a report and didn't suppress it. diff --git a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp index f600362da94..e078745737f 100644 --- a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp +++ b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp @@ -41,7 +41,7 @@ bool bugreporter::isDeclRefExprToReference(const Expr *E) { } const Expr *bugreporter::getDerefExpr(const Stmt *S) { - // Pattern match for a few useful cases (do something smarter later): + // Pattern match for a few useful cases: // a[0], p->f, *p const Expr *E = dyn_cast(S); if (!E) @@ -61,6 +61,10 @@ const Expr *bugreporter::getDerefExpr(const Stmt *S) { else if (const MemberExpr *ME = dyn_cast(E)) { if (ME->isArrow() || isDeclRefExprToReference(ME->getBase())) { return ME->getBase()->IgnoreParenCasts(); + } else { + // If we have a member expr with a dot, the base must have been + // dereferenced. + return getDerefExpr(ME->getBase()); } } else if (const ObjCIvarRefExpr *IvarRef = dyn_cast(E)) { @@ -69,6 +73,9 @@ const Expr *bugreporter::getDerefExpr(const Stmt *S) { else if (const ArraySubscriptExpr *AE = dyn_cast(E)) { return AE->getBase(); } + else if (isDeclRefExprToReference(E)) { + return E; + } break; } @@ -307,9 +314,9 @@ class ReturnVisitor : public BugReporterVisitorImpl { if (LValue) { if (const MemRegion *MR = LValue->getAsRegion()) { if (MR->canPrintPretty()) { - Out << " (reference to '"; + Out << " (reference to "; MR->printPretty(Out); - Out << "')"; + Out << ")"; } } } else { @@ -411,6 +418,35 @@ void FindLastStoreBRVisitor ::Profile(llvm::FoldingSetNodeID &ID) const { ID.AddBoolean(EnableNullFPSuppression); } +/// Returns true if \p N represents the DeclStmt declaring and initializing +/// \p VR. +static bool isInitializationOfVar(const ExplodedNode *N, const VarRegion *VR) { + Optional P = N->getLocationAs(); + if (!P) + return false; + + const DeclStmt *DS = P->getStmtAs(); + if (!DS) + return false; + + if (DS->getSingleDecl() != VR->getDecl()) + return false; + + const MemSpaceRegion *VarSpace = VR->getMemorySpace(); + const StackSpaceRegion *FrameSpace = dyn_cast(VarSpace); + if (!FrameSpace) { + // If we ever directly evaluate global DeclStmts, this assertion will be + // invalid, but this still seems preferable to silently accepting an + // initialization that may be for a path-sensitive variable. + assert(VR->getDecl()->isStaticLocal() && "non-static stackless VarRegion"); + return true; + } + + assert(VR->getDecl()->hasLocalStorage()); + const LocationContext *LCtx = N->getLocationContext(); + return FrameSpace->getStackFrame() == LCtx->getCurrentStackFrame(); +} + PathDiagnosticPiece *FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ, const ExplodedNode *Pred, BugReporterContext &BRC, @@ -425,13 +461,9 @@ PathDiagnosticPiece *FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ, // First see if we reached the declaration of the region. if (const VarRegion *VR = dyn_cast(R)) { - if (Optional P = Pred->getLocationAs()) { - if (const DeclStmt *DS = P->getStmtAs()) { - if (DS->getSingleDecl() == VR->getDecl()) { - StoreSite = Pred; - InitE = VR->getDecl()->getInit(); - } - } + if (isInitializationOfVar(Pred, VR)) { + StoreSite = Pred; + InitE = VR->getDecl()->getInit(); } } @@ -511,9 +543,6 @@ PathDiagnosticPiece *FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ, } } - if (!R->canPrintPretty()) - return 0; - // Okay, we've found the binding. Emit an appropriate message. SmallString<256> sbuf; llvm::raw_svector_ostream os(sbuf); @@ -525,9 +554,11 @@ PathDiagnosticPiece *FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ, const VarRegion *VR = dyn_cast(R); if (DS) { - action = "initialized to "; + action = R->canPrintPretty() ? "initialized to " : + "Initializing to "; } else if (isa(S)) { - action = "captured by block as "; + action = R->canPrintPretty() ? "captured by block as " : + "Captured by block as "; if (VR) { // See if we can get the BlockVarRegion. ProgramStateRef State = StoreSite->getState(); @@ -545,12 +576,10 @@ PathDiagnosticPiece *FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ, } if (action) { - if (!R) - return 0; - - os << '\''; - R->printPretty(os); - os << "' "; + if (R->canPrintPretty()) { + R->printPretty(os); + os << " "; + } if (V.getAs()) { bool b = false; @@ -573,14 +602,18 @@ PathDiagnosticPiece *FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ, if (V.isUndef()) { if (isa(R)) { const VarDecl *VD = cast(DS->getSingleDecl()); - if (VD->getInit()) - os << "initialized to a garbage value"; - else - os << "declared without an initial value"; + if (VD->getInit()) { + os << (R->canPrintPretty() ? "initialized" : "Initializing") + << " to a garbage value"; + } else { + os << (R->canPrintPretty() ? "declared" : "Declaring") + << " without an initial value"; + } } } else { - os << "initialized here"; + os << (R->canPrintPretty() ? "initialized" : "Initialized") + << " here"; } } } @@ -606,10 +639,11 @@ PathDiagnosticPiece *FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ, // Printed parameter indexes are 1-based, not 0-based. unsigned Idx = Param->getFunctionScopeIndex() + 1; - os << " via " << Idx << llvm::getOrdinalSuffix(Idx) << " parameter '"; - - R->printPretty(os); - os << '\''; + os << " via " << Idx << llvm::getOrdinalSuffix(Idx) << " parameter"; + if (R->canPrintPretty()) { + os << " "; + R->printPretty(os); + } } } @@ -619,27 +653,42 @@ PathDiagnosticPiece *FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ, if (R->isBoundable()) { if (const TypedValueRegion *TR = dyn_cast(R)) { if (TR->getValueType()->isObjCObjectPointerType()) { - os << "nil object reference stored to "; + os << "nil object reference stored"; b = true; } } } + if (!b) { + if (R->canPrintPretty()) + os << "Null pointer value stored"; + else + os << "Storing null pointer value"; + } + + } else if (V.isUndef()) { + if (R->canPrintPretty()) + os << "Uninitialized value stored"; + else + os << "Storing uninitialized value"; - if (!b) - os << "Null pointer value stored to "; - } - else if (V.isUndef()) { - os << "Uninitialized value stored to "; } else if (Optional CV = V.getAs()) { - os << "The value " << CV->getValue() << " is assigned to "; - } - else - os << "Value assigned to "; + if (R->canPrintPretty()) + os << "The value " << CV->getValue() << " is assigned"; + else + os << "Assigning " << CV->getValue(); - os << '\''; - R->printPretty(os); - os << '\''; + } else { + if (R->canPrintPretty()) + os << "Value assigned"; + else + os << "Assigning value"; + } + + if (R->canPrintPretty()) { + os << " to "; + R->printPretty(os); + } } // Construct a new PathDiagnosticPiece. @@ -682,6 +731,14 @@ TrackConstraintBRVisitor::VisitNode(const ExplodedNode *N, if (IsSatisfied) return NULL; + // Start tracking after we see the first state in which the value is + // constrained. + if (!IsTrackingTurnedOn) + if (!isUnderconstrained(N)) + IsTrackingTurnedOn = true; + if (!IsTrackingTurnedOn) + return 0; + // Check if in the previous state it was feasible for this constraint // to *not* be true. if (isUnderconstrained(PrevN)) { @@ -691,8 +748,7 @@ TrackConstraintBRVisitor::VisitNode(const ExplodedNode *N, // As a sanity check, make sure that the negation of the constraint // was infeasible in the current state. If it is feasible, we somehow // missed the transition point. - if (isUnderconstrained(N)) - return NULL; + assert(!isUnderconstrained(N)); // We found the transition point for the constraint. We now need to // pretty-print the constraint. (work-in-progress) @@ -853,7 +909,7 @@ bool bugreporter::trackNullOrUndefValue(const ExplodedNode *N, Inner = Ex; } - if (IsArg) { + if (IsArg && !Inner) { assert(N->getLocation().getAs() && "Tracking arg but not at call"); } else { // Walk through nodes until we get one that matches the statement exactly. @@ -882,7 +938,7 @@ bool bugreporter::trackNullOrUndefValue(const ExplodedNode *N, // At this point in the path, the receiver should be live since we are at the // message send expr. If it is nil, start tracking it. if (const Expr *Receiver = NilReceiverBRVisitor::getNilReceiver(S, N)) - trackNullOrUndefValue(N, Receiver, report, IsArg, EnableNullFPSuppression); + trackNullOrUndefValue(N, Receiver, report, false, EnableNullFPSuppression); // See if the expression we're interested refers to a variable. @@ -926,45 +982,24 @@ bool bugreporter::trackNullOrUndefValue(const ExplodedNode *N, if (R) { // Mark both the variable region and its contents as interesting. - SVal V = state->getRawSVal(loc::MemRegionVal(R)); - - // If the value matches the default for the variable region, that - // might mean that it's been cleared out of the state. Fall back to - // the full argument expression (with casts and such intact). - if (IsArg) { - bool UseArgValue = V.isUnknownOrUndef() || V.isZeroConstant(); - if (!UseArgValue) { - const SymbolRegionValue *SRV = - dyn_cast_or_null(V.getAsLocSymbol()); - if (SRV) - UseArgValue = (SRV->getRegion() == R); - } - if (UseArgValue) - V = state->getSValAsScalarOrLoc(S, N->getLocationContext()); - } + SVal V = LVState->getRawSVal(loc::MemRegionVal(R)); report.markInteresting(R); report.markInteresting(V); report.addVisitor(new UndefOrNullArgVisitor(R)); - if (isa(R)) { - TrackConstraintBRVisitor *VI = - new TrackConstraintBRVisitor(loc::MemRegionVal(R), false); - report.addVisitor(VI); - } - // If the contents are symbolic, find out when they became null. - if (V.getAsLocSymbol()) { + if (V.getAsLocSymbol(/*IncludeBaseRegions*/ true)) { BugReporterVisitor *ConstraintTracker = new TrackConstraintBRVisitor(V.castAs(), false); report.addVisitor(ConstraintTracker); // Add visitor, which will suppress inline defensive checks. - if (N->getState()->isNull(V).isConstrainedTrue() && + if (LVState->isNull(V).isConstrainedTrue() && EnableNullFPSuppression) { BugReporterVisitor *IDCSuppressor = new SuppressInlineDefensiveChecksVisitor(V.castAs(), - N); + LVNode); report.addVisitor(IDCSuppressor); } } @@ -994,7 +1029,13 @@ bool bugreporter::trackNullOrUndefValue(const ExplodedNode *N, if (Optional L = V.getAs()) { // At this point we are dealing with the region's LValue. // However, if the rvalue is a symbolic region, we should track it as well. - SVal RVal = state->getSVal(L->getRegion()); + // Try to use the correct type when looking up the value. + SVal RVal; + if (const Expr *E = dyn_cast(S)) + RVal = state->getRawSVal(L.getValue(), E->getType()); + else + RVal = state->getSVal(L->getRegion()); + const MemRegion *RegionRVal = RVal.getAsRegion(); report.addVisitor(new UndefOrNullArgVisitor(L->getRegion())); @@ -1030,10 +1071,22 @@ PathDiagnosticPiece *NilReceiverBRVisitor::VisitNode(const ExplodedNode *N, if (!P) return 0; - const Expr *Receiver = getNilReceiver(P->getStmt(), N); + const Stmt *S = P->getStmt(); + const Expr *Receiver = getNilReceiver(S, N); if (!Receiver) return 0; + llvm::SmallString<256> Buf; + llvm::raw_svector_ostream OS(Buf); + + if (const ObjCMessageExpr *ME = dyn_cast(S)) { + OS << "'" << ME->getSelector().getAsString() << "' not called"; + } + else { + OS << "No method is called"; + } + OS << " because the receiver is nil"; + // The receiver was nil, and hence the method was skipped. // Register a BugReporterVisitor to issue a message telling us how // the receiver was null. @@ -1042,8 +1095,7 @@ PathDiagnosticPiece *NilReceiverBRVisitor::VisitNode(const ExplodedNode *N, // Issue a message saying that the method was skipped. PathDiagnosticLocation L(Receiver, BRC.getSourceManager(), N->getLocationContext()); - return new PathDiagnosticEventPiece(L, "No method is called " - "because the receiver is nil"); + return new PathDiagnosticEventPiece(L, OS.str()); } // Registers every VarDecl inside a Stmt with a last store visitor. @@ -1372,7 +1424,7 @@ ConditionBRVisitor::VisitConditionVariable(StringRef LhsString, Out << (tookTrue ? "not nil" : "nil"); else if (Ty->isBooleanType()) Out << (tookTrue ? "true" : "false"); - else if (Ty->isIntegerType()) + else if (Ty->isIntegralOrEnumerationType()) Out << (tookTrue ? "non-zero" : "zero"); else return 0; diff --git a/lib/StaticAnalyzer/Core/CallEvent.cpp b/lib/StaticAnalyzer/Core/CallEvent.cpp index 45b2e219d9e..dfd20b8b332 100644 --- a/lib/StaticAnalyzer/Core/CallEvent.cpp +++ b/lib/StaticAnalyzer/Core/CallEvent.cpp @@ -239,8 +239,20 @@ QualType CallEvent::getDeclaredResultType(const Decl *D) { assert(D); if (const FunctionDecl* FD = dyn_cast(D)) return FD->getResultType(); - else if (const ObjCMethodDecl* MD = dyn_cast(D)) + if (const ObjCMethodDecl* MD = dyn_cast(D)) return MD->getResultType(); + if (const BlockDecl *BD = dyn_cast(D)) { + // Blocks are difficult because the return type may not be stored in the + // BlockDecl itself. The AST should probably be enhanced, but for now we + // just do what we can. + QualType Ty = BD->getSignatureAsWritten()->getType(); + if (const FunctionType *FT = Ty->getAs()) + if (!FT->getResultType()->isDependentType()) + return FT->getResultType(); + + return QualType(); + } + return QualType(); } diff --git a/lib/StaticAnalyzer/Core/Environment.cpp b/lib/StaticAnalyzer/Core/Environment.cpp index fe352aa8b4c..7b133f6bf64 100644 --- a/lib/StaticAnalyzer/Core/Environment.cpp +++ b/lib/StaticAnalyzer/Core/Environment.cpp @@ -80,43 +80,17 @@ SVal Environment::getSVal(const EnvironmentEntry &Entry, llvm_unreachable("Should have been handled by ignoreTransparentExprs"); case Stmt::AddrLabelExprClass: - return svalBuilder.makeLoc(cast(S)); - - case Stmt::CharacterLiteralClass: { - const CharacterLiteral *C = cast(S); - return svalBuilder.makeIntVal(C->getValue(), C->getType()); - } - + case Stmt::CharacterLiteralClass: case Stmt::CXXBoolLiteralExprClass: - return svalBuilder.makeBoolVal(cast(S)); - case Stmt::CXXScalarValueInitExprClass: - case Stmt::ImplicitValueInitExprClass: { - QualType Ty = cast(S)->getType(); - return svalBuilder.makeZeroVal(Ty); - } - + case Stmt::ImplicitValueInitExprClass: case Stmt::IntegerLiteralClass: - return svalBuilder.makeIntVal(cast(S)); - case Stmt::ObjCBoolLiteralExprClass: - return svalBuilder.makeBoolVal(cast(S)); - - // For special C0xx nullptr case, make a null pointer SVal. case Stmt::CXXNullPtrLiteralExprClass: - return svalBuilder.makeNull(); - - case Stmt::ObjCStringLiteralClass: { - MemRegionManager &MRMgr = svalBuilder.getRegionManager(); - const ObjCStringLiteral *SL = cast(S); - return svalBuilder.makeLoc(MRMgr.getObjCStringRegion(SL)); - } - - case Stmt::StringLiteralClass: { - MemRegionManager &MRMgr = svalBuilder.getRegionManager(); - const StringLiteral *SL = cast(S); - return svalBuilder.makeLoc(MRMgr.getStringRegion(SL)); - } + case Stmt::ObjCStringLiteralClass: + case Stmt::StringLiteralClass: + // Known constants; defer to SValBuilder. + return svalBuilder.getConstantVal(cast(S)).getValue(); case Stmt::ReturnStmtClass: { const ReturnStmt *RS = cast(S); @@ -127,10 +101,8 @@ SVal Environment::getSVal(const EnvironmentEntry &Entry, // Handle all other Stmt* using a lookup. default: - break; + return lookupExpr(EnvironmentEntry(S, LCtx)); } - - return lookupExpr(EnvironmentEntry(S, LCtx)); } Environment EnvironmentManager::bindExpr(Environment Env, diff --git a/lib/StaticAnalyzer/Core/ExprEngine.cpp b/lib/StaticAnalyzer/Core/ExprEngine.cpp index ab4dbd75251..bfe4e15a715 100644 --- a/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -106,7 +106,8 @@ ProgramStateRef ExprEngine::getInitialState(const LocationContext *InitLoc) { const ParmVarDecl *PD = FD->getParamDecl(0); QualType T = PD->getType(); - if (!T->isIntegerType()) + const BuiltinType *BT = dyn_cast(T); + if (!BT || !BT->isInteger()) break; const MemRegion *R = state->getRegion(PD, InitLoc); @@ -180,7 +181,8 @@ ExprEngine::createTemporaryRegionIfNeeded(ProgramStateRef State, } else { // We need to create a region no matter what. For sanity, make sure we don't // try to stuff a Loc into a non-pointer temporary region. - assert(!V.getAs() || Loc::isLocType(Result->getType())); + assert(!V.getAs() || Loc::isLocType(Result->getType()) || + Result->getType()->isMemberPointerType()); } ProgramStateManager &StateMgr = State->getStateManager(); @@ -602,11 +604,13 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, switch (S->getStmtClass()) { // C++ and ARC stuff we don't support yet. case Expr::ObjCIndirectCopyRestoreExprClass: + case Stmt::CXXDefaultInitExprClass: case Stmt::CXXDependentScopeMemberExprClass: case Stmt::CXXPseudoDestructorExprClass: case Stmt::CXXTryStmtClass: case Stmt::CXXTypeidExprClass: case Stmt::CXXUuidofExprClass: + case Stmt::MSPropertyRefExprClass: case Stmt::CXXUnresolvedConstructExprClass: case Stmt::DependentScopeDeclRefExprClass: case Stmt::UnaryTypeTraitExprClass: @@ -653,6 +657,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::SwitchStmtClass: case Stmt::WhileStmtClass: case Expr::MSDependentExistsStmtClass: + case Stmt::CapturedStmtClass: llvm_unreachable("Stmt should not be in analyzer evaluation loop"); case Stmt::ObjCSubscriptRefExprClass: @@ -736,21 +741,22 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, const CXXDefaultArgExpr *DefaultE = cast(S); const Expr *ArgE = DefaultE->getExpr(); - // Avoid creating and destroying a lot of APSInts. - SVal V; - llvm::APSInt Result; + bool IsTemporary = false; + if (const MaterializeTemporaryExpr *MTE = + dyn_cast(ArgE)) { + ArgE = MTE->GetTemporaryExpr(); + IsTemporary = true; + } + + Optional ConstantVal = svalBuilder.getConstantVal(ArgE); + if (!ConstantVal) + ConstantVal = UnknownVal(); for (ExplodedNodeSet::iterator I = PreVisit.begin(), E = PreVisit.end(); I != E; ++I) { ProgramStateRef State = (*I)->getState(); - - if (ArgE->EvaluateAsInt(Result, getContext())) - V = svalBuilder.makeIntVal(Result); - else - V = State->getSVal(ArgE, LCtx); - - State = State->BindExpr(DefaultE, LCtx, V); - if (DefaultE->isGLValue()) + State = State->BindExpr(DefaultE, LCtx, *ConstantVal); + if (IsTemporary) State = createTemporaryRegionIfNeeded(State, LCtx, DefaultE, DefaultE); Bldr2.generateNode(S, *I, State); @@ -860,9 +866,13 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, const LocationContext *LCtx = Pred->getLocationContext(); ProgramStateRef NewState = createTemporaryRegionIfNeeded(State, LCtx, OCE->getArg(0)); - if (NewState != State) + if (NewState != State) { Pred = Bldr.generateNode(OCE, Pred, NewState, /*Tag=*/0, ProgramPoint::PreStmtKind); + // Did we cache out? + if (!Pred) + break; + } } } // FALLTHROUGH @@ -1235,7 +1245,7 @@ static SVal RecoverCastedSymbol(ProgramStateManager& StateMgr, while (const CastExpr *CE = dyn_cast(Ex)) { QualType T = CE->getType(); - if (!T->isIntegerType()) + if (!T->isIntegralOrEnumerationType()) return UnknownVal(); uint64_t newBits = Ctx.getTypeSize(T); @@ -1250,7 +1260,8 @@ static SVal RecoverCastedSymbol(ProgramStateManager& StateMgr, // We reached a non-cast. Is it a symbolic value? QualType T = Ex->getType(); - if (!bitsInit || !T->isIntegerType() || Ctx.getTypeSize(T) > bits) + if (!bitsInit || !T->isIntegralOrEnumerationType() || + Ctx.getTypeSize(T) > bits) return UnknownVal(); return state->getSVal(Ex, LCtx); @@ -1342,7 +1353,7 @@ void ExprEngine::processBranch(const Stmt *Condition, const Stmt *Term, if (X.isUnknownOrUndef()) { // Give it a chance to recover from unknown. if (const Expr *Ex = dyn_cast(Condition)) { - if (Ex->getType()->isIntegerType()) { + if (Ex->getType()->isIntegralOrEnumerationType()) { // Try to recover some path-sensitivity. Right now casts of symbolic // integers that promote their values are currently not tracked well. // If 'Condition' is such an expression, try and recover the @@ -1802,7 +1813,8 @@ ExprEngine::notifyCheckersOfPointerEscape(ProgramStateRef State, return getCheckerManager().runCheckersForPointerEscape(State, *Invalidated, 0, - PSK_EscapeOther); + PSK_EscapeOther, + IsConst); // Note: Due to current limitations of RegionStore, we only process the top // level const pointers correctly. The lower level const pointers are diff --git a/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/lib/StaticAnalyzer/Core/ExprEngineC.cpp index 3a3c9713dd2..67aeab60033 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineC.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineC.cpp @@ -68,12 +68,14 @@ void ExprEngine::VisitBinaryOperator(const BinaryOperator* B, // SymSymExpr. unsigned Count = currBldrCtx->blockCount(); if (LeftV.getAs() && - RHS->getType()->isIntegerType() && RightV.isUnknown()) { + RHS->getType()->isIntegralOrEnumerationType() && + RightV.isUnknown()) { RightV = svalBuilder.conjureSymbolVal(RHS, LCtx, RHS->getType(), Count); } if (RightV.getAs() && - LHS->getType()->isIntegerType() && LeftV.isUnknown()) { + LHS->getType()->isIntegralOrEnumerationType() && + LeftV.isUnknown()) { LeftV = svalBuilder.conjureSymbolVal(LHS, LCtx, LHS->getType(), Count); } @@ -401,26 +403,32 @@ void ExprEngine::VisitCompoundLiteralExpr(const CompoundLiteralExpr *CL, ExplodedNodeSet &Dst) { StmtNodeBuilder B(Pred, Dst, *currBldrCtx); - const InitListExpr *ILE - = cast(CL->getInitializer()->IgnoreParens()); + ProgramStateRef State = Pred->getState(); + const LocationContext *LCtx = Pred->getLocationContext(); + + const Expr *Init = CL->getInitializer(); + SVal V = State->getSVal(CL->getInitializer(), LCtx); - ProgramStateRef state = Pred->getState(); - SVal ILV = state->getSVal(ILE, Pred->getLocationContext()); - const LocationContext *LC = Pred->getLocationContext(); - state = state->bindCompoundLiteral(CL, LC, ILV); - - // 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)); + if (isa(Init)) { + // No work needed. Just pass the value up to this expression. + } else { + assert(isa(Init)); + Loc CLLoc = State->getLValue(CL, LCtx); + State = State->bindLoc(CLLoc, V); + + // 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()) + V = CLLoc; + } + + B.generateNode(CL, Pred, State->BindExpr(CL, LCtx, V)); } void ExprEngine::VisitDeclStmt(const DeclStmt *DS, ExplodedNode *Pred, @@ -615,11 +623,15 @@ void ExprEngine::VisitGuardedExpr(const Expr *Ex, const Expr *R, ExplodedNode *Pred, ExplodedNodeSet &Dst) { + assert(L && R); + StmtNodeBuilder B(Pred, Dst, *currBldrCtx); ProgramStateRef state = Pred->getState(); const LocationContext *LCtx = Pred->getLocationContext(); const CFGBlock *SrcBlock = 0; + // Find the predecessor block. + ProgramStateRef SrcState = state; for (const ExplodedNode *N = Pred ; N ; N = *N->pred_begin()) { ProgramPoint PP = N->getLocation(); if (PP.getAs() || PP.getAs()) { @@ -627,6 +639,7 @@ void ExprEngine::VisitGuardedExpr(const Expr *Ex, continue; } SrcBlock = PP.castAs().getSrc(); + SrcState = N->getState(); break; } @@ -642,14 +655,25 @@ void ExprEngine::VisitGuardedExpr(const Expr *Ex, CFGElement CE = *I; if (Optional CS = CE.getAs()) { const Expr *ValEx = cast(CS->getStmt()); - hasValue = true; - V = state->getSVal(ValEx, LCtx); + ValEx = ValEx->IgnoreParens(); + + // For GNU extension '?:' operator, the left hand side will be an + // OpaqueValueExpr, so get the underlying expression. + if (const OpaqueValueExpr *OpaqueEx = dyn_cast(L)) + L = OpaqueEx->getSourceExpr(); + + // If the last expression in the predecessor block matches true or false + // subexpression, get its the value. + if (ValEx == L->IgnoreParens() || ValEx == R->IgnoreParens()) { + hasValue = true; + V = SrcState->getSVal(ValEx, LCtx); + } break; } } - assert(hasValue); - (void) hasValue; + if (!hasValue) + V = svalBuilder.conjureSymbolVal(0, Ex, LCtx, currBldrCtx->blockCount()); // Generate a new node with the binding from the appropriate path. B.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, V, true)); @@ -662,8 +686,9 @@ VisitOffsetOfExpr(const OffsetOfExpr *OOE, APSInt IV; if (OOE->EvaluateAsInt(IV, getContext())) { assert(IV.getBitWidth() == getContext().getTypeSize(OOE->getType())); - assert(OOE->getType()->isIntegerType()); - assert(IV.isSigned() == OOE->getType()->isSignedIntegerOrEnumerationType()); + assert(OOE->getType()->isBuiltinType()); + assert(OOE->getType()->getAs()->isInteger()); + assert(IV.isSigned() == OOE->getType()->isSignedIntegerType()); SVal X = svalBuilder.makeIntVal(IV); B.generateNode(OOE, Pred, Pred->getState()->BindExpr(OOE, Pred->getLocationContext(), diff --git a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp index f01e4e76401..06570a4b4a9 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -676,46 +676,40 @@ static CallInlinePolicy mayInlineCallKind(const CallEvent &Call, return CIP_Allowed; } -/// Returns true if the given C++ class is a container. -/// -/// Our heuristic for this is whether it contains a method named 'begin()' or a -/// nested type named 'iterator'. -static bool isContainerClass(const ASTContext &Ctx, const CXXRecordDecl *RD) { - // Don't record any path information. - CXXBasePaths Paths(false, false, false); - - const IdentifierInfo &BeginII = Ctx.Idents.get("begin"); - DeclarationName BeginName = Ctx.DeclarationNames.getIdentifier(&BeginII); - DeclContext::lookup_const_result BeginDecls = RD->lookup(BeginName); - if (!BeginDecls.empty()) - return true; - if (RD->lookupInBases(&CXXRecordDecl::FindOrdinaryMember, - BeginName.getAsOpaquePtr(), - Paths)) - return true; - - const IdentifierInfo &IterII = Ctx.Idents.get("iterator"); - DeclarationName IteratorName = Ctx.DeclarationNames.getIdentifier(&IterII); - DeclContext::lookup_const_result IterDecls = RD->lookup(IteratorName); - if (!IterDecls.empty()) +/// Returns true if the given C++ class contains a member with the given name. +static bool hasMember(const ASTContext &Ctx, const CXXRecordDecl *RD, + StringRef Name) { + const IdentifierInfo &II = Ctx.Idents.get(Name); + DeclarationName DeclName = Ctx.DeclarationNames.getIdentifier(&II); + if (!RD->lookup(DeclName).empty()) return true; + + CXXBasePaths Paths(false, false, false); if (RD->lookupInBases(&CXXRecordDecl::FindOrdinaryMember, - IteratorName.getAsOpaquePtr(), + DeclName.getAsOpaquePtr(), Paths)) return true; return false; } +/// Returns true if the given C++ class is a container or iterator. +/// +/// Our heuristic for this is whether it contains a method named 'begin()' or a +/// nested type named 'iterator' or 'iterator_category'. +static bool isContainerClass(const ASTContext &Ctx, const CXXRecordDecl *RD) { + return hasMember(Ctx, RD, "begin") || + hasMember(Ctx, RD, "iterator") || + hasMember(Ctx, RD, "iterator_category"); +} + /// Returns true if the given function refers to a constructor or destructor of -/// a C++ container. +/// a C++ container or iterator. /// /// We generally do a poor job modeling most containers right now, and would -/// prefer not to inline their methods. +/// prefer not to inline their setup and teardown. static bool isContainerCtorOrDtor(const ASTContext &Ctx, const FunctionDecl *FD) { - // Heuristic: a type is a container if it contains a "begin()" method - // or a type named "iterator". if (!(isa(FD) || isa(FD))) return false; diff --git a/lib/StaticAnalyzer/Core/MemRegion.cpp b/lib/StaticAnalyzer/Core/MemRegion.cpp index b3a1e65b19a..42073d4841f 100644 --- a/lib/StaticAnalyzer/Core/MemRegion.cpp +++ b/lib/StaticAnalyzer/Core/MemRegion.cpp @@ -555,38 +555,75 @@ void StackLocalsSpaceRegion::dumpToStream(raw_ostream &os) const { } bool MemRegion::canPrintPretty() const { + return canPrintPrettyAsExpr(); +} + +bool MemRegion::canPrintPrettyAsExpr() const { return false; } void MemRegion::printPretty(raw_ostream &os) const { + assert(canPrintPretty() && "This region cannot be printed pretty."); + os << "'"; + printPrettyAsExpr(os); + os << "'"; + return; +} + +void MemRegion::printPrettyAsExpr(raw_ostream &os) const { + llvm_unreachable("This region cannot be printed pretty."); return; } -bool VarRegion::canPrintPretty() const { +bool VarRegion::canPrintPrettyAsExpr() const { return true; } -void VarRegion::printPretty(raw_ostream &os) const { +void VarRegion::printPrettyAsExpr(raw_ostream &os) const { os << getDecl()->getName(); } -bool ObjCIvarRegion::canPrintPretty() const { +bool ObjCIvarRegion::canPrintPrettyAsExpr() const { return true; } -void ObjCIvarRegion::printPretty(raw_ostream &os) const { +void ObjCIvarRegion::printPrettyAsExpr(raw_ostream &os) const { os << getDecl()->getName(); } bool FieldRegion::canPrintPretty() const { - return superRegion->canPrintPretty(); + return true; } -void FieldRegion::printPretty(raw_ostream &os) const { - superRegion->printPretty(os); +bool FieldRegion::canPrintPrettyAsExpr() const { + return superRegion->canPrintPrettyAsExpr(); +} + +void FieldRegion::printPrettyAsExpr(raw_ostream &os) const { + assert(canPrintPrettyAsExpr()); + superRegion->printPrettyAsExpr(os); os << "." << getDecl()->getName(); } +void FieldRegion::printPretty(raw_ostream &os) const { + if (canPrintPrettyAsExpr()) { + os << "\'"; + printPrettyAsExpr(os); + os << "'"; + } else { + os << "field " << "\'" << getDecl()->getName() << "'"; + } + return; +} + +bool CXXBaseObjectRegion::canPrintPrettyAsExpr() const { + return superRegion->canPrintPrettyAsExpr(); +} + +void CXXBaseObjectRegion::printPrettyAsExpr(raw_ostream &os) const { + superRegion->printPrettyAsExpr(os); +} + //===----------------------------------------------------------------------===// // MemRegionManager methods. //===----------------------------------------------------------------------===// @@ -1043,6 +1080,17 @@ const MemRegion *MemRegion::StripCasts(bool StripBaseCasts) const { } } +const SymbolicRegion *MemRegion::getSymbolicBase() const { + const SubRegion *SubR = dyn_cast(this); + + while (SubR) { + if (const SymbolicRegion *SymR = dyn_cast(SubR)) + return SymR; + SubR = dyn_cast(SubR->getSuperRegion()); + } + return 0; +} + // FIXME: Merge with the implementation of the same method in Store.cpp static bool IsCompleteType(ASTContext &Ctx, QualType Ty) { if (const RecordType *RT = Ty->getAs()) { diff --git a/lib/StaticAnalyzer/Core/PathDiagnostic.cpp b/lib/StaticAnalyzer/Core/PathDiagnostic.cpp index 7c0fb14a5c8..03513106ecd 100644 --- a/lib/StaticAnalyzer/Core/PathDiagnostic.cpp +++ b/lib/StaticAnalyzer/Core/PathDiagnostic.cpp @@ -297,11 +297,16 @@ static Optional comparePiece(const PathDiagnosticPiece &X, static Optional comparePath(const PathPieces &X, const PathPieces &Y) { if (X.size() != Y.size()) return X.size() < Y.size(); - for (unsigned i = 0, n = X.size(); i != n; ++i) { - Optional b = comparePiece(*X[i], *Y[i]); + + PathPieces::const_iterator X_I = X.begin(), X_end = X.end(); + PathPieces::const_iterator Y_I = Y.begin(), Y_end = Y.end(); + + for ( ; X_I != X_end && Y_I != Y_end; ++X_I, ++Y_I) { + Optional b = comparePiece(**X_I, **Y_I); if (b.hasValue()) return b.getValue(); } + return None; } @@ -608,31 +613,73 @@ PathDiagnosticLocation return PathDiagnosticLocation(S, SMng, P.getLocationContext()); } +const Stmt *PathDiagnosticLocation::getStmt(const ExplodedNode *N) { + ProgramPoint P = N->getLocation(); + if (Optional SP = P.getAs()) + return SP->getStmt(); + if (Optional BE = P.getAs()) + return BE->getSrc()->getTerminator(); + if (Optional CE = P.getAs()) + return CE->getCallExpr(); + if (Optional CEE = P.getAs()) + return CEE->getCalleeContext()->getCallSite(); + if (Optional PIPP = P.getAs()) + return PIPP->getInitializer()->getInit(); + + return 0; +} + +const Stmt *PathDiagnosticLocation::getNextStmt(const ExplodedNode *N) { + for (N = N->getFirstSucc(); N; N = N->getFirstSucc()) { + if (const Stmt *S = getStmt(N)) { + // Check if the statement is '?' or '&&'/'||'. These are "merges", + // not actual statement points. + switch (S->getStmtClass()) { + case Stmt::ChooseExprClass: + case Stmt::BinaryConditionalOperatorClass: + case Stmt::ConditionalOperatorClass: + continue; + case Stmt::BinaryOperatorClass: { + BinaryOperatorKind Op = cast(S)->getOpcode(); + if (Op == BO_LAnd || Op == BO_LOr) + continue; + break; + } + default: + break; + } + // We found the statement, so return it. + return S; + } + } + + return 0; +} + PathDiagnosticLocation - PathDiagnosticLocation::createEndOfPath(const ExplodedNode* N, + PathDiagnosticLocation::createEndOfPath(const ExplodedNode *N, const SourceManager &SM) { assert(N && "Cannot create a location with a null node."); + const Stmt *S = getStmt(N); - const ExplodedNode *NI = N; - const Stmt *S = 0; - - while (NI) { - ProgramPoint P = NI->getLocation(); - if (Optional PS = P.getAs()) { - S = PS->getStmt(); - if (P.getAs()) - return PathDiagnosticLocation::createEnd(S, SM, - NI->getLocationContext()); - break; - } else if (Optional BE = P.getAs()) { - S = BE->getSrc()->getTerminator(); - break; - } - NI = NI->succ_empty() ? 0 : *(NI->succ_begin()); - } + if (!S) + S = getNextStmt(N); if (S) { - const LocationContext *LC = NI->getLocationContext(); + ProgramPoint P = N->getLocation(); + const LocationContext *LC = N->getLocationContext(); + + // For member expressions, return the location of the '.' or '->'. + if (const MemberExpr *ME = dyn_cast(S)) + return PathDiagnosticLocation::createMemberLoc(ME, SM); + + // For binary operators, return the location of the operator. + if (const BinaryOperator *B = dyn_cast(S)) + return PathDiagnosticLocation::createOperatorLoc(B, SM); + + if (P.getAs()) + return PathDiagnosticLocation::createEnd(S, SM, LC); + if (S->getLocStart().isValid()) return PathDiagnosticLocation(S, SM, LC); return PathDiagnosticLocation(getValidSourceLocation(S, LC), SM); diff --git a/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp b/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp index 7dcc088d18a..850955561ec 100644 --- a/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp +++ b/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp @@ -296,6 +296,8 @@ static void ReportCall(raw_ostream &o, for (PathPieces::const_iterator I = P.path.begin(), E = P.path.end();I!=E;++I) ReportPiece(o, **I, FM, SM, LangOpts, indent, depth, true); + + --depth; IntrusiveRefCntPtr callExit = P.getCallExitEvent(); diff --git a/lib/StaticAnalyzer/Core/ProgramState.cpp b/lib/StaticAnalyzer/Core/ProgramState.cpp index bff2242925e..653b69bf486 100644 --- a/lib/StaticAnalyzer/Core/ProgramState.cpp +++ b/lib/StaticAnalyzer/Core/ProgramState.cpp @@ -111,14 +111,6 @@ ProgramStateManager::removeDeadBindings(ProgramStateRef state, return ConstraintMgr->removeDeadBindings(Result, SymReaper); } -ProgramStateRef ProgramState::bindCompoundLiteral(const CompoundLiteralExpr *CL, - const LocationContext *LC, - SVal V) const { - const StoreRef &newStore = - getStateManager().StoreMgr->bindCompoundLiteral(getStore(), CL, LC, V); - return makeWithStore(newStore); -} - ProgramStateRef ProgramState::bindLoc(Loc LV, SVal V, bool notifyChanges) const { ProgramStateManager &Mgr = getStateManager(); ProgramStateRef newState = makeWithStore(Mgr.StoreMgr->Bind(getStore(), @@ -270,7 +262,7 @@ SVal ProgramState::getSValAsScalarOrLoc(const MemRegion *R) const { if (const TypedValueRegion *TR = dyn_cast(R)) { QualType T = TR->getValueType(); - if (Loc::isLocType(T) || T->isIntegerType()) + if (Loc::isLocType(T) || T->isIntegralOrEnumerationType()) return getSVal(R); } @@ -383,7 +375,7 @@ ConditionTruthVal ProgramState::isNull(SVal V) const { if (V.isConstant()) return false; - SymbolRef Sym = V.getAsSymbol(); + SymbolRef Sym = V.getAsSymbol(/* IncludeBaseRegion */ true); if (!Sym) return ConditionTruthVal(); diff --git a/lib/StaticAnalyzer/Core/RegionStore.cpp b/lib/StaticAnalyzer/Core/RegionStore.cpp index 0f4a6824a24..88c4eee4bb2 100644 --- a/lib/StaticAnalyzer/Core/RegionStore.cpp +++ b/lib/StaticAnalyzer/Core/RegionStore.cpp @@ -19,10 +19,12 @@ #include "clang/Analysis/Analyses/LiveVariables.h" #include "clang/Analysis/AnalysisContext.h" #include "clang/Basic/TargetInfo.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h" #include "llvm/ADT/ImmutableList.h" #include "llvm/ADT/ImmutableMap.h" #include "llvm/ADT/Optional.h" @@ -323,6 +325,7 @@ class invalidateRegionsWorker; class RegionStoreManager : public StoreManager { public: const RegionStoreFeatures Features; + RegionBindings::Factory RBFactory; mutable ClusterBindings::Factory CBFactory; @@ -332,6 +335,16 @@ class RegionStoreManager : public StoreManager { SValListTy> LazyBindingsMapTy; LazyBindingsMapTy LazyBindingsMap; + /// The largest number of fields a struct can have and still be + /// considered "small". + /// + /// This is currently used to decide whether or not it is worth "forcing" a + /// LazyCompoundVal on bind. + /// + /// This is controlled by 'region-store-small-struct-limit' option. + /// To disable all small-struct-dependent behavior, set the option to "0". + unsigned SmallStructLimit; + /// \brief A helper used to populate the work list with the given set of /// regions. void populateWorkList(invalidateRegionsWorker &W, @@ -342,7 +355,14 @@ class RegionStoreManager : public StoreManager { public: RegionStoreManager(ProgramStateManager& mgr, const RegionStoreFeatures &f) : StoreManager(mgr), Features(f), - RBFactory(mgr.getAllocator()), CBFactory(mgr.getAllocator()) {} + RBFactory(mgr.getAllocator()), CBFactory(mgr.getAllocator()), + SmallStructLimit(0) { + if (SubEngine *Eng = StateMgr.getOwningEngine()) { + AnalyzerOptions &Options = Eng->getAnalysisManager().options; + SmallStructLimit = + Options.getOptionAsInteger("region-store-small-struct-limit", 2); + } + } /// setImplicitDefaultValue - Set the default binding for the provided @@ -409,19 +429,20 @@ class RegionStoreManager : public StoreManager { .getRootWithoutRetain(), *this); } - /// \brief Create a new store that binds a value to a compound literal. + /// Attempt to extract the fields of \p LCV and bind them to the struct region + /// \p R. /// - /// \param ST The original store whose bindings are the basis for the new - /// store. + /// This path is used when it seems advantageous to "force" loading the values + /// within a LazyCompoundVal to bind memberwise to the struct region, rather + /// than using a Default binding at the base of the entire region. This is a + /// heuristic attempting to avoid building long chains of LazyCompoundVals. /// - /// \param CL The compound literal to bind (the binding key). - /// - /// \param LC The LocationContext for the binding. - /// - /// \param V The value to bind to the compound literal. - StoreRef bindCompoundLiteral(Store ST, - const CompoundLiteralExpr *CL, - const LocationContext *LC, SVal V); + /// \returns The updated store bindings, or \c None if binding non-lazily + /// would be too expensive. + Optional tryBindSmallStruct(RegionBindingsConstRef B, + const TypedValueRegion *R, + const RecordDecl *RD, + nonloc::LazyCompoundVal LCV); /// BindStruct - Bind a compound value to a structure. RegionBindingsRef bindStruct(RegionBindingsConstRef B, @@ -490,8 +511,7 @@ class RegionStoreManager : public StoreManager { SVal getBindingForFieldOrElementCommon(RegionBindingsConstRef B, const TypedValueRegion *R, - QualType Ty, - const MemRegion *superR); + QualType Ty); SVal getLazyBinding(const SubRegion *LazyBindingRegion, RegionBindingsRef LazyBinding); @@ -604,6 +624,17 @@ ento::CreateFieldsOnlyRegionStoreManager(ProgramStateManager &StMgr) { //===----------------------------------------------------------------------===// namespace { +/// Used to determine which global regions are automatically included in the +/// initial worklist of a ClusterAnalysis. +enum GlobalsFilterKind { + /// Don't include any global regions. + GFK_None, + /// Only include system globals. + GFK_SystemOnly, + /// Include all global regions. + GFK_All +}; + template class ClusterAnalysis { protected: @@ -620,19 +651,36 @@ class ClusterAnalysis { SValBuilder &svalBuilder; RegionBindingsRef B; - - const bool includeGlobals; +private: + GlobalsFilterKind GlobalsFilter; + +protected: const ClusterBindings *getCluster(const MemRegion *R) { return B.lookup(R); } + /// Returns true if the memory space of the given region is one of the global + /// regions specially included at the start of analysis. + bool isInitiallyIncludedGlobalRegion(const MemRegion *R) { + switch (GlobalsFilter) { + case GFK_None: + return false; + case GFK_SystemOnly: + return isa(R->getMemorySpace()); + case GFK_All: + return isa(R->getMemorySpace()); + } + + llvm_unreachable("unknown globals filter"); + } + public: ClusterAnalysis(RegionStoreManager &rm, ProgramStateManager &StateMgr, - RegionBindingsRef b, const bool includeGlobals) + RegionBindingsRef b, GlobalsFilterKind GFK) : RM(rm), Ctx(StateMgr.getContext()), svalBuilder(StateMgr.getSValBuilder()), - B(b), includeGlobals(includeGlobals) {} + B(b), GlobalsFilter(GFK) {} RegionBindingsRef getRegionBindings() const { return B; } @@ -650,9 +698,9 @@ class ClusterAnalysis { assert(!Cluster.isEmpty() && "Empty clusters should be removed"); static_cast(this)->VisitAddedToCluster(Base, Cluster); - if (includeGlobals) - if (isa(Base->getMemorySpace())) - AddToWorkList(Base, &Cluster); + // If this is an interesting global region, add it the work list up front. + if (isInitiallyIncludedGlobalRegion(Base)) + AddToWorkList(WorkListElement(Base), &Cluster); } } @@ -905,8 +953,8 @@ class invalidateRegionsWorker : public ClusterAnalysis InvalidatedSymbols &is, InvalidatedSymbols &inConstIS, StoreManager::InvalidatedRegions *r, - bool includeGlobals) - : ClusterAnalysis(rm, stateMgr, b, includeGlobals), + GlobalsFilterKind GFK) + : ClusterAnalysis(rm, stateMgr, b, GFK), Ex(ex), Count(count), LCtx(lctx), IS(is), ConstIS(inConstIS), Regions(r){} /// \param IsConst Specifies if the region we are invalidating is constant. @@ -949,6 +997,7 @@ void invalidateRegionsWorker::VisitCluster(const MemRegion *baseR, for (ClusterBindings::iterator I = C->begin(), E = C->end(); I != E; ++I) VisitBinding(I.getData()); + // Invalidate the contents of a non-const base region. if (!IsConst) B = B.remove(baseR); } @@ -981,18 +1030,19 @@ void invalidateRegionsWorker::VisitCluster(const MemRegion *baseR, } // Symbolic region? - SymbolRef RegionSym = 0; - if (const SymbolicRegion *SR = dyn_cast(baseR)) - RegionSym = SR->getSymbol(); + if (const SymbolicRegion *SR = dyn_cast(baseR)) { + SymbolRef RegionSym = SR->getSymbol(); - if (IsConst) { // Mark that symbol touched by the invalidation. - ConstIS.insert(RegionSym); - return; + if (IsConst) + ConstIS.insert(RegionSym); + else + IS.insert(RegionSym); } - - // Mark that symbol touched by the invalidation. - IS.insert(RegionSym); + + // Nothing else should be done for a const region. + if (IsConst) + return; // Otherwise, we have a normal data region. Record that we touched the region. if (Regions) @@ -1013,7 +1063,13 @@ void invalidateRegionsWorker::VisitCluster(const MemRegion *baseR, const TypedValueRegion *TR = cast(baseR); QualType T = TR->getValueType(); - // Invalidate the binding. + if (isInitiallyIncludedGlobalRegion(baseR)) { + // If the region is a global and we are invalidating all globals, + // erasing the entry is good enough. This causes all globals to be lazily + // symbolicated from the same base symbol. + return; + } + if (T->isStructureOrClassType()) { // Invalidate the region by setting its default value to // conjured symbol. The type of the symbol is irrelavant. @@ -1031,16 +1087,6 @@ void invalidateRegionsWorker::VisitCluster(const MemRegion *baseR, B = B.addBinding(baseR, BindingKey::Default, V); return; } - - if (includeGlobals && - isa(baseR->getMemorySpace())) { - // If the region is a global and we are invalidating all globals, - // just erase the entry. This causes all globals to be lazily - // symbolicated from the same base symbol. - B = B.removeBinding(baseR); - return; - } - DefinedOrUnknownSVal V = svalBuilder.conjureSymbolVal(baseR, Ex, LCtx, T,Count); @@ -1116,9 +1162,19 @@ RegionStoreManager::invalidateRegions(Store store, InvalidatedRegions *TopLevelRegions, InvalidatedRegions *TopLevelConstRegions, InvalidatedRegions *Invalidated) { - RegionBindingsRef B = RegionStoreManager::getRegionBindings(store); + GlobalsFilterKind GlobalsFilter; + if (Call) { + if (Call->isInSystemHeader()) + GlobalsFilter = GFK_SystemOnly; + else + GlobalsFilter = GFK_All; + } else { + GlobalsFilter = GFK_None; + } + + RegionBindingsRef B = getRegionBindings(store); invalidateRegionsWorker W(*this, StateMgr, B, Ex, Count, LCtx, IS, ConstIS, - Invalidated, false); + Invalidated, GlobalsFilter); // Scan the bindings and generate the clusters. W.GenerateClusters(); @@ -1138,14 +1194,17 @@ RegionStoreManager::invalidateRegions(Store store, // invalidate them. (Note that function-static and immutable globals are never // invalidated by this.) // TODO: This could possibly be more precise with modules. - if (Call) { + switch (GlobalsFilter) { + case GFK_All: + B = invalidateGlobalRegion(MemRegion::GlobalInternalSpaceRegionKind, + Ex, Count, LCtx, B, Invalidated); + // FALLTHROUGH + case GFK_SystemOnly: B = invalidateGlobalRegion(MemRegion::GlobalSystemSpaceRegionKind, Ex, Count, LCtx, B, Invalidated); - - if (!Call->isInSystemHeader()) { - B = invalidateGlobalRegion(MemRegion::GlobalInternalSpaceRegionKind, - Ex, Count, LCtx, B, Invalidated); - } + // FALLTHROUGH + case GFK_None: + break; } return StoreRef(B.asStore(), *this); @@ -1506,7 +1565,7 @@ SVal RegionStoreManager::getBindingForElement(RegionBindingsConstRef B, } } } - return getBindingForFieldOrElementCommon(B, R, R->getElementType(),superR); + return getBindingForFieldOrElementCommon(B, R, R->getElementType()); } SVal RegionStoreManager::getBindingForField(RegionBindingsConstRef B, @@ -1517,7 +1576,7 @@ SVal RegionStoreManager::getBindingForField(RegionBindingsConstRef B, return *V; QualType Ty = R->getValueType(); - return getBindingForFieldOrElementCommon(B, R, Ty, R->getSuperRegion()); + return getBindingForFieldOrElementCommon(B, R, Ty); } Optional @@ -1580,8 +1639,7 @@ SVal RegionStoreManager::getLazyBinding(const SubRegion *LazyBindingRegion, SVal RegionStoreManager::getBindingForFieldOrElementCommon(RegionBindingsConstRef B, const TypedValueRegion *R, - QualType Ty, - const MemRegion *superR) { + QualType Ty) { // At this point we have already checked in either getBindingForElement or // getBindingForField if 'R' has a direct binding. @@ -1614,8 +1672,9 @@ RegionStoreManager::getBindingForFieldOrElementCommon(RegionBindingsConstRef B, // quickly result in a warning. bool hasPartialLazyBinding = false; - const SubRegion *Base = dyn_cast(superR); - while (Base) { + const SubRegion *SR = dyn_cast(R); + while (SR) { + const MemRegion *Base = SR->getSuperRegion(); if (Optional D = getBindingForDerivedDefaultValue(B, Base, R, Ty)) { if (D->getAs()) { hasPartialLazyBinding = true; @@ -1633,7 +1692,7 @@ RegionStoreManager::getBindingForFieldOrElementCommon(RegionBindingsConstRef B, // If our super region is a field or element itself, walk up the region // hierarchy to see if there is a default value installed in an ancestor. - Base = dyn_cast(Base->getSuperRegion()); + SR = dyn_cast(Base); } if (R->hasStackNonParametersStorage()) { @@ -1641,7 +1700,7 @@ RegionStoreManager::getBindingForFieldOrElementCommon(RegionBindingsConstRef B, // Currently we don't reason specially about Clang-style vectors. Check // if superR is a vector and if so return Unknown. if (const TypedValueRegion *typedSuperR = - dyn_cast(superR)) { + dyn_cast(R->getSuperRegion())) { if (typedSuperR->getValueType()->isVectorType()) return UnknownVal(); } @@ -1682,26 +1741,6 @@ SVal RegionStoreManager::getBindingForObjCIvar(RegionBindingsConstRef B, return getBindingForLazySymbol(R); } -static Optional getConstValue(SValBuilder &SVB, const VarDecl *VD) { - ASTContext &Ctx = SVB.getContext(); - if (!VD->getType().isConstQualified()) - return None; - - const Expr *Init = VD->getInit(); - if (!Init) - return None; - - llvm::APSInt Result; - if (!Init->isGLValue() && Init->EvaluateAsInt(Result, Ctx)) - return SVB.makeIntVal(Result); - - if (Init->isNullPointerConstant(Ctx, Expr::NPC_ValueDependentIsNotNull)) - return SVB.makeNull(); - - // FIXME: Handle other possible constant expressions. - return None; -} - SVal RegionStoreManager::getBindingForVar(RegionBindingsConstRef B, const VarRegion *R) { @@ -1718,8 +1757,10 @@ SVal RegionStoreManager::getBindingForVar(RegionBindingsConstRef B, return svalBuilder.getRegionValueSymbolVal(R); // Is 'VD' declared constant? If so, retrieve the constant value. - if (Optional V = getConstValue(svalBuilder, VD)) - return *V; + if (VD->getType().isConstQualified()) + if (const Expr *Init = VD->getInit()) + if (Optional V = svalBuilder.getConstantVal(Init)) + return *V; // This must come after the check for constants because closure-captured // constant variables may appear in UnknownSpaceRegion. @@ -1891,14 +1932,6 @@ RegionStoreManager::bind(RegionBindingsConstRef B, Loc L, SVal V) { return NewB.addBinding(BindingKey::Make(R, BindingKey::Direct), V); } -// FIXME: this method should be merged into Bind(). -StoreRef RegionStoreManager::bindCompoundLiteral(Store ST, - const CompoundLiteralExpr *CL, - const LocationContext *LC, - SVal V) { - return Bind(ST, loc::MemRegionVal(MRMgr.getCompoundLiteralRegion(CL, LC)), V); -} - RegionBindingsRef RegionStoreManager::setImplicitDefaultValue(RegionBindingsConstRef B, const MemRegion *R, @@ -1907,7 +1940,7 @@ RegionStoreManager::setImplicitDefaultValue(RegionBindingsConstRef B, if (Loc::isLocType(T)) V = svalBuilder.makeNull(); - else if (T->isIntegerType()) + else if (T->isIntegralOrEnumerationType()) V = svalBuilder.makeZeroVal(T); else if (T->isStructureOrClassType() || T->isArrayType()) { // Set the default value to a zero constant when it is a structure @@ -1977,7 +2010,7 @@ RegionStoreManager::bindArray(RegionBindingsConstRef B, else if (ElementTy->isArrayType()) NewB = bindArray(NewB, ER, *VI); else - NewB = bind(NewB, svalBuilder.makeLoc(ER), *VI); + NewB = bind(NewB, loc::MemRegionVal(ER), *VI); } // If the init list is shorter than the array length, set the @@ -2018,17 +2051,59 @@ RegionBindingsRef RegionStoreManager::bindVector(RegionBindingsConstRef B, NonLoc Idx = svalBuilder.makeArrayIndex(index); const ElementRegion *ER = MRMgr.getElementRegion(ElemType, Idx, R, Ctx); - + if (ElemType->isArrayType()) NewB = bindArray(NewB, ER, *VI); else if (ElemType->isStructureOrClassType()) NewB = bindStruct(NewB, ER, *VI); else - NewB = bind(NewB, svalBuilder.makeLoc(ER), *VI); + NewB = bind(NewB, loc::MemRegionVal(ER), *VI); } return NewB; } +Optional +RegionStoreManager::tryBindSmallStruct(RegionBindingsConstRef B, + const TypedValueRegion *R, + const RecordDecl *RD, + nonloc::LazyCompoundVal LCV) { + FieldVector Fields; + + if (const CXXRecordDecl *Class = dyn_cast(RD)) + if (Class->getNumBases() != 0 || Class->getNumVBases() != 0) + return None; + + for (RecordDecl::field_iterator I = RD->field_begin(), E = RD->field_end(); + I != E; ++I) { + const FieldDecl *FD = *I; + if (FD->isUnnamedBitfield()) + continue; + + // If there are too many fields, or if any of the fields are aggregates, + // just use the LCV as a default binding. + if (Fields.size() == SmallStructLimit) + return None; + + QualType Ty = FD->getType(); + if (!(Ty->isScalarType() || Ty->isReferenceType())) + return None; + + Fields.push_back(*I); + } + + RegionBindingsRef NewB = B; + + for (FieldVector::iterator I = Fields.begin(), E = Fields.end(); I != E; ++I){ + const FieldRegion *SourceFR = MRMgr.getFieldRegion(*I, LCV.getRegion()); + SVal V = getBindingForField(getRegionBindings(LCV.getStore()), SourceFR); + + const FieldRegion *DestFR = MRMgr.getFieldRegion(*I, R); + NewB = bind(NewB, loc::MemRegionVal(DestFR), V); + } + + return NewB; +} + RegionBindingsRef RegionStoreManager::bindStruct(RegionBindingsConstRef B, const TypedValueRegion* R, SVal V) { @@ -2039,13 +2114,19 @@ RegionBindingsRef RegionStoreManager::bindStruct(RegionBindingsConstRef B, assert(T->isStructureOrClassType()); const RecordType* RT = T->getAs(); - RecordDecl *RD = RT->getDecl(); + const RecordDecl *RD = RT->getDecl(); if (!RD->isCompleteDefinition()) return B; // Handle lazy compound values and symbolic values. - if (V.getAs() || V.getAs()) + if (Optional LCV = + V.getAs()) { + if (Optional NewB = tryBindSmallStruct(B, R, RD, *LCV)) + return *NewB; + return bindAggregate(B, R, V); + } + if (V.getAs()) return bindAggregate(B, R, V); // We may get non-CompoundVal accidentally due to imprecise cast logic or @@ -2077,7 +2158,7 @@ RegionBindingsRef RegionStoreManager::bindStruct(RegionBindingsConstRef B, else if (FTy->isStructureOrClassType()) NewB = bindStruct(NewB, FR, *VI); else - NewB = bind(NewB, svalBuilder.makeLoc(FR), *VI); + NewB = bind(NewB, loc::MemRegionVal(FR), *VI); ++VI; } @@ -2115,8 +2196,7 @@ class removeDeadBindingsWorker : ProgramStateManager &stateMgr, RegionBindingsRef b, SymbolReaper &symReaper, const StackFrameContext *LCtx) - : ClusterAnalysis(rm, stateMgr, b, - /* includeGlobals = */ false), + : ClusterAnalysis(rm, stateMgr, b, GFK_None), SymReaper(symReaper), CurrentLCtx(LCtx) {} // Called by ClusterAnalysis. diff --git a/lib/StaticAnalyzer/Core/SValBuilder.cpp b/lib/StaticAnalyzer/Core/SValBuilder.cpp index c72e7808010..9d77a3ef58f 100644 --- a/lib/StaticAnalyzer/Core/SValBuilder.cpp +++ b/lib/StaticAnalyzer/Core/SValBuilder.cpp @@ -33,7 +33,7 @@ DefinedOrUnknownSVal SValBuilder::makeZeroVal(QualType type) { if (Loc::isLocType(type)) return makeNull(); - if (type->isIntegerType()) + if (type->isIntegralOrEnumerationType()) return makeIntVal(0, type); // FIXME: Handle floats. @@ -106,12 +106,19 @@ SValBuilder::getRegionValueSymbolVal(const TypedValueRegion* region) { return nonloc::SymbolVal(sym); } -DefinedOrUnknownSVal SValBuilder::conjureSymbolVal(const void *symbolTag, - const Expr *expr, +DefinedOrUnknownSVal SValBuilder::conjureSymbolVal(const void *SymbolTag, + const Expr *Ex, const LocationContext *LCtx, - unsigned count) { - QualType T = expr->getType(); - return conjureSymbolVal(symbolTag, expr, LCtx, T, count); + unsigned Count) { + QualType T = Ex->getType(); + + // Compute the type of the result. If the expression is not an R-value, the + // result should be a location. + QualType ExType = Ex->getType(); + if (Ex->isGLValue()) + T = LCtx->getAnalysisDeclContext()->getASTContext().getPointerType(ExType); + + return conjureSymbolVal(SymbolTag, Ex, LCtx, T, Count); } DefinedOrUnknownSVal SValBuilder::conjureSymbolVal(const void *symbolTag, @@ -217,6 +224,68 @@ loc::MemRegionVal SValBuilder::getCXXThis(const CXXRecordDecl *D, return loc::MemRegionVal(getRegionManager().getCXXThisRegion(PT, SFC)); } +Optional SValBuilder::getConstantVal(const Expr *E) { + E = E->IgnoreParens(); + + switch (E->getStmtClass()) { + // Handle expressions that we treat differently from the AST's constant + // evaluator. + case Stmt::AddrLabelExprClass: + return makeLoc(cast(E)); + + case Stmt::CXXScalarValueInitExprClass: + case Stmt::ImplicitValueInitExprClass: + return makeZeroVal(E->getType()); + + case Stmt::ObjCStringLiteralClass: { + const ObjCStringLiteral *SL = cast(E); + return makeLoc(getRegionManager().getObjCStringRegion(SL)); + } + + case Stmt::StringLiteralClass: { + const StringLiteral *SL = cast(E); + return makeLoc(getRegionManager().getStringRegion(SL)); + } + + // Fast-path some expressions to avoid the overhead of going through the AST's + // constant evaluator + case Stmt::CharacterLiteralClass: { + const CharacterLiteral *C = cast(E); + return makeIntVal(C->getValue(), C->getType()); + } + + case Stmt::CXXBoolLiteralExprClass: + return makeBoolVal(cast(E)); + + case Stmt::IntegerLiteralClass: + return makeIntVal(cast(E)); + + case Stmt::ObjCBoolLiteralExprClass: + return makeBoolVal(cast(E)); + + case Stmt::CXXNullPtrLiteralExprClass: + return makeNull(); + + // If we don't have a special case, fall back to the AST's constant evaluator. + default: { + // Don't try to come up with a value for materialized temporaries. + if (E->isGLValue()) + return None; + + ASTContext &Ctx = getContext(); + llvm::APSInt Result; + if (E->EvaluateAsInt(Result, Ctx)) + return makeIntVal(Result); + + if (Loc::isLocType(E->getType())) + if (E->isNullPointerConstant(Ctx, Expr::NPC_ValueDependentIsNotNull)) + return makeNull(); + + return None; + } + } +} + //===----------------------------------------------------------------------===// SVal SValBuilder::makeSymExprValNN(ProgramStateRef State, @@ -320,6 +389,22 @@ SVal SValBuilder::evalCast(SVal val, QualType castTy, QualType originalTy) { if (val.isUnknownOrUndef() || castTy == originalTy) return val; + if (castTy->isBooleanType()) { + if (val.isUnknownOrUndef()) + return val; + if (val.isConstant()) + return makeTruthVal(!val.isZeroConstant(), castTy); + if (SymbolRef Sym = val.getAsSymbol()) { + BasicValueFactory &BVF = getBasicValueFactory(); + // FIXME: If we had a state here, we could see if the symbol is known to + // be zero, but we don't. + return makeNonLoc(Sym, BO_NE, BVF.getValue(0, Sym->getType()), castTy); + } + + assert(val.getAs()); + return makeTruthVal(true, castTy); + } + // For const casts, casts to void, just propagate the value. if (!castTy->isVariableArrayType() && !originalTy->isVariableArrayType()) if (shouldBeModeledWithNoOp(Context, Context.getPointerType(castTy), @@ -327,11 +412,11 @@ SVal SValBuilder::evalCast(SVal val, QualType castTy, QualType originalTy) { return val; // Check for casts from pointers to integers. - if (castTy->isIntegerType() && Loc::isLocType(originalTy)) + if (castTy->isIntegralOrEnumerationType() && Loc::isLocType(originalTy)) return evalCastFromLoc(val.castAs(), castTy); // Check for casts from integers to pointers. - if (Loc::isLocType(castTy) && originalTy->isIntegerType()) { + if (Loc::isLocType(castTy) && originalTy->isIntegralOrEnumerationType()) { if (Optional LV = val.getAs()) { if (const MemRegion *R = LV->getLoc().getAsRegion()) { StoreManager &storeMgr = StateMgr.getStoreManager(); @@ -361,7 +446,7 @@ SVal SValBuilder::evalCast(SVal val, QualType castTy, QualType originalTy) { // Are we casting from an array to an integer? If so, cast the decayed // pointer value to an integer. - assert(castTy->isIntegerType()); + assert(castTy->isIntegralOrEnumerationType()); // FIXME: Keep these here for now in case we decide soon that we // need the original decayed type. @@ -373,7 +458,7 @@ 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()) + if (castTy->isIntegralOrEnumerationType()) return evalCastFromLoc(loc::MemRegionVal(R), castTy); // FIXME: We should handle the case where we strip off view layers to get diff --git a/lib/StaticAnalyzer/Core/SVals.cpp b/lib/StaticAnalyzer/Core/SVals.cpp index 38e216f28c0..650691535f7 100644 --- a/lib/StaticAnalyzer/Core/SVals.cpp +++ b/lib/StaticAnalyzer/Core/SVals.cpp @@ -64,14 +64,18 @@ const FunctionDecl *SVal::getAsFunctionDecl() const { /// /// Implicit casts (ex: void* -> char*) can turn Symbolic region into Element /// region. If that is the case, gets the underlining region. -SymbolRef SVal::getAsLocSymbol() const { +/// When IncludeBaseRegions is set to true and the SubRegion is non-symbolic, +/// the first symbolic parent region is returned. +SymbolRef SVal::getAsLocSymbol(bool IncludeBaseRegions) const { // FIXME: should we consider SymbolRef wrapped in CodeTextRegion? if (Optional X = getAs()) return X->getLoc().getAsLocSymbol(); if (Optional X = getAs()) { - const MemRegion *R = X->stripCasts(); - if (const SymbolicRegion *SymR = dyn_cast(R)) + const MemRegion *R = X->getRegion(); + if (const SymbolicRegion *SymR = IncludeBaseRegions ? + R->getSymbolicBase() : + dyn_cast(R->StripCasts())) return SymR->getSymbol(); } return 0; @@ -99,13 +103,17 @@ SymbolRef SVal::getLocSymbolInBase() const { // TODO: The next 3 functions have to be simplified. /// \brief If this SVal wraps a symbol return that SymbolRef. -/// Otherwise return 0. -SymbolRef SVal::getAsSymbol() const { +/// Otherwise, return 0. +/// +/// Casts are ignored during lookup. +/// \param IncludeBaseRegions The boolean that controls whether the search +/// should continue to the base regions if the region is not symbolic. +SymbolRef SVal::getAsSymbol(bool IncludeBaseRegion) const { // FIXME: should we consider SymbolRef wrapped in CodeTextRegion? if (Optional X = getAs()) return X->getSymbol(); - return getAsLocSymbol(); + return getAsLocSymbol(IncludeBaseRegion); } /// getAsSymbolicExpression - If this Sval wraps a symbolic expression then diff --git a/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp b/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp index 9b759df48f2..a06268dd331 100644 --- a/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp +++ b/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp @@ -90,20 +90,15 @@ ProgramStateRef SimpleConstraintManager::assumeAux(ProgramStateRef state, case loc::MemRegionKind: { // FIXME: Should this go into the storemanager? - const MemRegion *R = Cond.castAs().getRegion(); - const SubRegion *SubR = dyn_cast(R); - - while (SubR) { - // FIXME: now we only find the first symbolic region. - if (const SymbolicRegion *SymR = dyn_cast(SubR)) { - const llvm::APSInt &zero = getBasicVals().getZeroWithPtrWidth(); - if (Assumption) - return assumeSymNE(state, SymR->getSymbol(), zero, zero); - else - return assumeSymEQ(state, SymR->getSymbol(), zero, zero); - } - SubR = dyn_cast(SubR->getSuperRegion()); + + // FIXME: now we only find the first symbolic region. + if (const SymbolicRegion *SymR = R->getSymbolicBase()) { + const llvm::APSInt &zero = getBasicVals().getZeroWithPtrWidth(); + if (Assumption) + return assumeSymNE(state, SymR->getSymbol(), zero, zero); + else + return assumeSymEQ(state, SymR->getSymbol(), zero, zero); } // FALL-THROUGH. @@ -137,7 +132,7 @@ SimpleConstraintManager::assumeAuxForSymbol(ProgramStateRef State, QualType T = Sym->getType(); // None of the constraint solvers currently support non-integer types. - if (!T->isIntegerType()) + if (!T->isIntegralOrEnumerationType()) return State; const llvm::APSInt &zero = BVF.getValue(0, T); diff --git a/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp b/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp index 5cc8926a444..ee627f2baa3 100644 --- a/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp +++ b/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp @@ -109,7 +109,7 @@ SVal SimpleSValBuilder::evalCastFromNonLoc(NonLoc val, QualType castTy) { // Only handle casts from integers to integers - if val is an integer constant // being cast to a non integer type, produce unknown. - if (!isLocType && !castTy->isIntegerType()) + if (!isLocType && !castTy->isIntegralOrEnumerationType()) return UnknownVal(); llvm::APSInt i = val.castAs().getValue(); @@ -137,7 +137,7 @@ SVal SimpleSValBuilder::evalCastFromLoc(Loc val, QualType castTy) { if (castTy->isUnionType()) return UnknownVal(); - if (castTy->isIntegerType()) { + if (castTy->isIntegralOrEnumerationType()) { unsigned BitWidth = Context.getTypeSize(castTy); if (!val.getAs()) @@ -438,9 +438,13 @@ SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state, case BO_GE: case BO_EQ: case BO_NE: + assert(resultTy->isBooleanType() || + resultTy == getConditionType()); + assert(symIntExpr->getType()->isBooleanType() || + getContext().hasSameUnqualifiedType(symIntExpr->getType(), + getConditionType())); // Negate the comparison and make a value. opc = BinaryOperator::negateComparisonOp(opc); - assert(symIntExpr->getType() == resultTy); return makeNonLoc(symIntExpr->getLHS(), opc, symIntExpr->getRHS(), resultTy); } diff --git a/lib/StaticAnalyzer/Core/Store.cpp b/lib/StaticAnalyzer/Core/Store.cpp index a0c24fedcfc..690ed08ffc7 100644 --- a/lib/StaticAnalyzer/Core/Store.cpp +++ b/lib/StaticAnalyzer/Core/Store.cpp @@ -289,62 +289,82 @@ SVal StoreManager::evalDerivedToBase(SVal Derived, QualType BaseType, return loc::MemRegionVal(BaseReg); } -SVal StoreManager::evalDynamicCast(SVal Base, QualType DerivedType, +/// Returns the static type of the given region, if it represents a C++ class +/// object. +/// +/// This handles both fully-typed regions, where the dynamic type is known, and +/// symbolic regions, where the dynamic type is merely bounded (and even then, +/// only ostensibly!), but does not take advantage of any dynamic type info. +static const CXXRecordDecl *getCXXRecordType(const MemRegion *MR) { + if (const TypedValueRegion *TVR = dyn_cast(MR)) + return TVR->getValueType()->getAsCXXRecordDecl(); + if (const SymbolicRegion *SR = dyn_cast(MR)) + return SR->getSymbol()->getType()->getPointeeCXXRecordDecl(); + return 0; +} + +SVal StoreManager::evalDynamicCast(SVal Base, QualType TargetType, bool &Failed) { Failed = false; - Optional BaseRegVal = Base.getAs(); - if (!BaseRegVal) + const MemRegion *MR = Base.getAsRegion(); + if (!MR) return UnknownVal(); - const MemRegion *BaseRegion = BaseRegVal->stripCasts(/*StripBases=*/false); // Assume the derived class is a pointer or a reference to a CXX record. - DerivedType = DerivedType->getPointeeType(); - assert(!DerivedType.isNull()); - const CXXRecordDecl *DerivedDecl = DerivedType->getAsCXXRecordDecl(); - if (!DerivedDecl && !DerivedType->isVoidType()) + TargetType = TargetType->getPointeeType(); + assert(!TargetType.isNull()); + const CXXRecordDecl *TargetClass = TargetType->getAsCXXRecordDecl(); + if (!TargetClass && !TargetType->isVoidType()) return UnknownVal(); // Drill down the CXXBaseObject chains, which represent upcasts (casts from // derived to base). - const MemRegion *SR = BaseRegion; - while (const TypedRegion *TSR = dyn_cast_or_null(SR)) { - QualType BaseType = TSR->getLocationType()->getPointeeType(); - assert(!BaseType.isNull()); - const CXXRecordDecl *SRDecl = BaseType->getAsCXXRecordDecl(); - if (!SRDecl) - return UnknownVal(); - + while (const CXXRecordDecl *MRClass = getCXXRecordType(MR)) { // If found the derived class, the cast succeeds. - if (SRDecl == DerivedDecl) - return loc::MemRegionVal(TSR); + if (MRClass == TargetClass) + return loc::MemRegionVal(MR); - if (!DerivedType->isVoidType()) { + if (!TargetType->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)) - return evalDerivedToBase(loc::MemRegionVal(TSR), Paths.front()); + if (MRClass->isDerivedFrom(TargetClass, Paths)) + return evalDerivedToBase(loc::MemRegionVal(MR), Paths.front()); } - if (const CXXBaseObjectRegion *R = dyn_cast(TSR)) + if (const CXXBaseObjectRegion *BaseR = dyn_cast(MR)) { // Drill down the chain to get the derived classes. - SR = R->getSuperRegion(); - else { - // We reached the bottom of the hierarchy. - - // If this is a cast to void*, return the region. - if (DerivedType->isVoidType()) - return loc::MemRegionVal(TSR); + MR = BaseR->getSuperRegion(); + continue; + } - // We did not find the derived class. We we must be casting the base to - // derived, so the cast should fail. - Failed = true; - return UnknownVal(); + // If this is a cast to void*, return the region. + if (TargetType->isVoidType()) + return loc::MemRegionVal(MR); + + // Strange use of reinterpret_cast can give us paths we don't reason + // about well, by putting in ElementRegions where we'd expect + // CXXBaseObjectRegions. If it's a valid reinterpret_cast (i.e. if the + // derived class has a zero offset from the base class), then it's safe + // to strip the cast; if it's invalid, -Wreinterpret-base-class should + // catch it. In the interest of performance, the analyzer will silently + // do the wrong thing in the invalid case (because offsets for subregions + // will be wrong). + const MemRegion *Uncasted = MR->StripCasts(/*IncludeBaseCasts=*/false); + if (Uncasted == MR) { + // We reached the bottom of the hierarchy and did not find the derived + // class. We we must be casting the base to derived, so the cast should + // fail. + break; } + + MR = Uncasted; } - + + // We failed if the region we ended up with has perfect type info. + Failed = isa(MR); return UnknownVal(); } diff --git a/lib/StaticAnalyzer/Core/SymbolManager.cpp b/lib/StaticAnalyzer/Core/SymbolManager.cpp index de2f5bc7b37..7c75b6c3d2f 100644 --- a/lib/StaticAnalyzer/Core/SymbolManager.cpp +++ b/lib/StaticAnalyzer/Core/SymbolManager.cpp @@ -340,8 +340,8 @@ bool SymbolManager::canSymbolicate(QualType T) { if (Loc::isLocType(T)) return true; - if (T->isIntegerType()) - return T->isScalarType(); + if (T->isIntegralOrEnumerationType()) + return true; if (T->isRecordType() && !T->isUnionType()) return true; diff --git a/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp b/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp index 4fad5a8a7c5..e7def088196 100644 --- a/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp +++ b/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp @@ -100,11 +100,12 @@ void ClangCheckerRegistry::warnIncompatible(DiagnosticsEngine *diags, } -CheckerManager *ento::createCheckerManager(const AnalyzerOptions &opts, +CheckerManager *ento::createCheckerManager(AnalyzerOptions &opts, const LangOptions &langOpts, ArrayRef plugins, DiagnosticsEngine &diags) { - OwningPtr checkerMgr(new CheckerManager(langOpts)); + OwningPtr checkerMgr(new CheckerManager(langOpts, + &opts)); SmallVector checkerOpts; for (unsigned i = 0, e = opts.CheckersControlList.size(); i != e; ++i) { diff --git a/test/ARCMT/check-with-serialized-diag.m b/test/ARCMT/check-with-serialized-diag.m index d8073d04693..77bad96dcc3 100644 --- a/test/ARCMT/check-with-serialized-diag.m +++ b/test/ARCMT/check-with-serialized-diag.m @@ -43,13 +43,13 @@ void test1(A *a, struct UnsafeS *unsafeS) { // CHECK-NEXT: Number FIXITs = 0 // CHECK-NEXT: {{.*}}check-with-serialized-diag.m:34:4: error: [rewriter] it is not safe to remove 'retain' message on a global variable // CHECK-NEXT: Number FIXITs = 0 -// CHECK-NEXT: {{.*}}check-with-serialized-diag.m:32:4: error: ARC forbids explicit message send of 'retain' -// CHECK-NEXT: Range: {{.*}}check-with-serialized-diag.m:32:23 {{.*}}check-with-serialized-diag.m:32:29 +// CHECK-NEXT: {{.*}}check-with-serialized-diag.m:32:23: error: ARC forbids explicit message send of 'retain' +// CHECK-NEXT: Range: {{.*}}check-with-serialized-diag.m:32:4 {{.*}}check-with-serialized-diag.m:32:22 // CHECK-NEXT: Number FIXITs = 0 -// CHECK-NEXT: {{.*}}check-with-serialized-diag.m:34:4: error: ARC forbids explicit message send of 'retain' -// CHECK-NEXT: Range: {{.*}}check-with-serialized-diag.m:34:15 {{.*}}check-with-serialized-diag.m:34:21 +// CHECK-NEXT: {{.*}}check-with-serialized-diag.m:34:15: error: ARC forbids explicit message send of 'retain' +// CHECK-NEXT: Range: {{.*}}check-with-serialized-diag.m:34:4 {{.*}}check-with-serialized-diag.m:34:14 // CHECK-NEXT: Number FIXITs = 0 -// CHECK-NEXT: {{.*}}check-with-serialized-diag.m:35:4: error: ARC forbids explicit message send of 'retainCount' -// CHECK-NEXT: Range: {{.*}}check-with-serialized-diag.m:35:6 {{.*}}check-with-serialized-diag.m:35:17 +// CHECK-NEXT: {{.*}}check-with-serialized-diag.m:35:6: error: ARC forbids explicit message send of 'retainCount' +// CHECK-NEXT: Range: {{.*}}check-with-serialized-diag.m:35:4 {{.*}}check-with-serialized-diag.m:35:5 // CHECK-NEXT: Number FIXITs = 0 diff --git a/test/ARCMT/migrate-plist-output.m b/test/ARCMT/migrate-plist-output.m index 12efa93f075..377dce30caf 100644 --- a/test/ARCMT/migrate-plist-output.m +++ b/test/ARCMT/migrate-plist-output.m @@ -26,7 +26,7 @@ void test(id p) { // CHECK: location // CHECK: // CHECK: line10 -// CHECK: col4 +// CHECK: col6 // CHECK: file0 // CHECK: // CHECK: ranges @@ -34,12 +34,12 @@ void test(id p) { // CHECK: // CHECK: // CHECK: line10 -// CHECK: col6 +// CHECK: col4 // CHECK: file0 // CHECK: // CHECK: // CHECK: line10 -// CHECK: col12 +// CHECK: col4 // CHECK: file0 // CHECK: // CHECK: diff --git a/test/ARCMT/objcmt-subscripting-literals.m b/test/ARCMT/objcmt-subscripting-literals.m index 8cef0919bba..014c1092999 100644 --- a/test/ARCMT/objcmt-subscripting-literals.m +++ b/test/ARCMT/objcmt-subscripting-literals.m @@ -157,6 +157,7 @@ typedef const struct __CFString * CFStringRef; dict = [NSDictionary dictionaryWithObjects:[NSArray arrayWithObjects:@"1", [NSArray array], nil] forKeys:[NSArray arrayWithObjects:@"A", [arr objectAtIndex:2], nil]]; dict = [NSDictionary dictionaryWithObjects:[NSArray arrayWithObjects:@"1", @"2", nil] forKeys:arr]; dict = [NSDictionary dictionaryWithObjects:[NSArray arrayWithObjects:@"1", @"2", nil] forKeys:@[@"A", @"B"]]; + dict = [NSDictionary dictionaryWithObjectsAndKeys:[NSArray array], @"A", [NSArray array], @"B", nil]; } @end diff --git a/test/ARCMT/objcmt-subscripting-literals.m.result b/test/ARCMT/objcmt-subscripting-literals.m.result index 0ca6dca1fea..e9ff8df34dc 100644 --- a/test/ARCMT/objcmt-subscripting-literals.m.result +++ b/test/ARCMT/objcmt-subscripting-literals.m.result @@ -157,6 +157,7 @@ typedef const struct __CFString * CFStringRef; dict = @{@"A": @"1", arr[2]: @[]}; dict = [NSDictionary dictionaryWithObjects:@[@"1", @"2"] forKeys:arr]; dict = @{@"A": @"1", @"B": @"2"}; + dict = @{@"A": @[], @"B": @[]}; } @end diff --git a/test/ASTMerge/function.c b/test/ASTMerge/function.c index 320bca2a36f..8a8a0305145 100644 --- a/test/ASTMerge/function.c +++ b/test/ASTMerge/function.c @@ -9,7 +9,7 @@ // CHECK: function1.c:4:6: note: declared here with type 'void (void)' // CHECK: 2 errors generated -// expected-error@3 {{external function 'f1' declared with incompatible types}} -// expected-note@2 {{declared here}} -// expected-error@5 {{external function 'f3' declared with incompatible types}} -// expected-note@4 {{declared here}} +// expected-error@Inputs/function2.c:3 {{external function 'f1' declared with incompatible types}} +// expected-note@Inputs/function1.c:2 {{declared here}} +// expected-error@Inputs/function2.c:5 {{external function 'f3' declared with incompatible types}} +// expected-note@Inputs/function1.c:4 {{declared here}} diff --git a/test/Analysis/Inputs/system-header-simulator-cxx.h b/test/Analysis/Inputs/system-header-simulator-cxx.h index eee0e31a6f8..6e434a04b23 100644 --- a/test/Analysis/Inputs/system-header-simulator-cxx.h +++ b/test/Analysis/Inputs/system-header-simulator-cxx.h @@ -80,6 +80,12 @@ namespace std { *OI++ = *II++; return OI; } + + struct input_iterator_tag { }; + struct output_iterator_tag { }; + struct forward_iterator_tag : public input_iterator_tag { }; + struct bidirectional_iterator_tag : public forward_iterator_tag { }; + struct random_access_iterator_tag : public bidirectional_iterator_tag { }; } void* operator new(std::size_t, const std::nothrow_t&) throw(); diff --git a/test/Analysis/Inputs/system-header-simulator.h b/test/Analysis/Inputs/system-header-simulator.h index 04688c782a0..dd1cd4942f8 100644 --- a/test/Analysis/Inputs/system-header-simulator.h +++ b/test/Analysis/Inputs/system-header-simulator.h @@ -5,6 +5,10 @@ // suppressed. #pragma clang system_header +#ifdef __cplusplus +#define restrict /*restrict*/ +#endif + typedef struct _FILE FILE; extern FILE *stdin; extern FILE *stdout; @@ -14,8 +18,11 @@ extern FILE *__stdinp; extern FILE *__stdoutp; extern FILE *__stderrp; - +int scanf(const char *restrict format, ...); int fscanf(FILE *restrict, const char *restrict, ...); +int printf(const char *restrict format, ...); +int fprintf(FILE *restrict, const char *restrict, ...); +int getchar(void); // Note, on some platforms errno macro gets replaced with a function call. extern int errno; @@ -37,6 +44,8 @@ typedef __darwin_off_t fpos_t; void setbuf(FILE * restrict, char * restrict); int setvbuf(FILE * restrict, char * restrict, int, size_t); +FILE *fopen(const char * restrict, const char * restrict); +int fclose(FILE *); FILE *funopen(const void *, int (*)(void *, char *, int), int (*)(void *, const char *, int), diff --git a/test/Analysis/Malloc+MismatchedDeallocator+NewDelete.cpp b/test/Analysis/Malloc+MismatchedDeallocator+NewDelete.cpp index b0bb1735b49..5a596d47eca 100644 --- a/test/Analysis/Malloc+MismatchedDeallocator+NewDelete.cpp +++ b/test/Analysis/Malloc+MismatchedDeallocator+NewDelete.cpp @@ -1,8 +1,7 @@ -// RUN: %clang_cc1 -analyze -analyzer-checker=core,unix.Malloc,unix.MismatchedDeallocator,alpha.cplusplus.NewDelete -analyzer-store region -std=c++11 -verify %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core,unix.Malloc,unix.MismatchedDeallocator,cplusplus.NewDelete -std=c++11 -verify %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core,unix.Malloc,unix.MismatchedDeallocator,cplusplus.NewDelete,alpha.cplusplus.NewDeleteLeaks -DLEAKS -std=c++11 -verify %s -typedef __typeof(sizeof(int)) size_t; -void *malloc(size_t); -void free(void *); +#include "Inputs/system-header-simulator-for-malloc.h" //-------------------------------------------------- // Check that unix.Malloc catches all types of bugs. @@ -15,7 +14,7 @@ void testMallocDoubleFree() { void testMallocLeak() { int *p = (int *)malloc(sizeof(int)); -} // expected-warning{{Memory is never released; potential leak of memory pointed to by 'p'}} +} // expected-warning{{Potential leak of memory pointed to by 'p'}} void testMallocUseAfterFree() { int *p = (int *)malloc(sizeof(int)); @@ -52,7 +51,10 @@ void testNewDoubleFree() { void testNewLeak() { int *p = new int; -} // expected-warning{{Memory is never released; potential leak of memory pointed to by 'p'}} +} +#ifdef LEAKS +// expected-warning@-2 {{Potential leak of memory pointed to by 'p'}} +#endif void testNewUseAfterFree() { int *p = (int *)operator new(0); @@ -69,3 +71,35 @@ void testNewOffsetFree() { int *p = new int; operator delete(++p); // expected-warning{{Argument to operator delete is offset by 4 bytes from the start of memory allocated by 'new'}} } + +//---------------------------------------------------------------- +// Test that we check for free errors on escaped pointers. +//---------------------------------------------------------------- +void changePtr(int **p); +static int *globalPtr; +void changePointee(int *p); + +void testMismatchedChangePtrThroughCall() { + int *p = (int*)malloc(sizeof(int)*4); + changePtr(&p); + delete p; // no-warning the value of the pointer might have changed +} + +void testMismatchedChangePointeeThroughCall() { + int *p = (int*)malloc(sizeof(int)*4); + changePointee(p); + delete p; // expected-warning{{Memory allocated by malloc() should be deallocated by free(), not 'delete'}} +} + +void testShouldReportDoubleFreeNotMismatched() { + int *p = (int*)malloc(sizeof(int)*4); + globalPtr = p; + free(p); + delete globalPtr; // expected-warning {{Attempt to free released memory}} +} + +void testMismatchedChangePointeeThroughAssignment() { + int *arr = new int[4]; + globalPtr = arr; + delete arr; // expected-warning{{Memory allocated by 'new[]' should be deallocated by 'delete[]', not 'delete'}} +} \ No newline at end of file diff --git a/test/Analysis/Malloc+NewDelete_intersections.cpp b/test/Analysis/Malloc+NewDelete_intersections.cpp index 7a0ef8e13c6..310663646a1 100644 --- a/test/Analysis/Malloc+NewDelete_intersections.cpp +++ b/test/Analysis/Malloc+NewDelete_intersections.cpp @@ -1,14 +1,15 @@ -// RUN: %clang_cc1 -analyze -analyzer-checker=core,unix.Malloc,alpha.cplusplus.NewDelete -analyzer-store region -std=c++11 -verify %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core,unix.Malloc,cplusplus.NewDelete -std=c++11 -verify %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core,unix.Malloc,cplusplus.NewDelete,alpha.cplusplus.NewDeleteLeaks -std=c++11 -verify %s typedef __typeof(sizeof(int)) size_t; void *malloc(size_t); void free(void *); //------------------------------------------------------------------- -// Check that unix.Malloc + alpha.cplusplus.NewDelete does not enable +// Check that unix.Malloc + cplusplus.NewDelete does not enable // warnings produced by unix.MismatchedDeallocator. //------------------------------------------------------------------- void testMismatchedDeallocator() { int *p = (int *)malloc(sizeof(int)); delete p; -} // expected-warning{{Memory is never released; potential leak of memory pointed to by 'p'}} +} // expected-warning{{Potential leak of memory pointed to by 'p'}} diff --git a/test/Analysis/alloc-match-dealloc.mm b/test/Analysis/MismatchedDeallocator-checker-test.mm similarity index 100% rename from test/Analysis/alloc-match-dealloc.mm rename to test/Analysis/MismatchedDeallocator-checker-test.mm diff --git a/test/Analysis/MismatchedDeallocator-path-notes.cpp b/test/Analysis/MismatchedDeallocator-path-notes.cpp new file mode 100644 index 00000000000..369d8f69756 --- /dev/null +++ b/test/Analysis/MismatchedDeallocator-path-notes.cpp @@ -0,0 +1,159 @@ +// RUN: %clang_cc1 -analyze -analyzer-checker=core,unix.MismatchedDeallocator -analyzer-output=text -verify %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core,unix.MismatchedDeallocator -analyzer-output=plist %s -o %t.plist +// RUN: FileCheck --input-file=%t.plist %s + +void changePointee(int *p); +void test() { + int *p = new int[1]; + // expected-note@-1 {{Memory is allocated}} + changePointee(p); + delete p; // expected-warning {{Memory allocated by 'new[]' should be deallocated by 'delete[]', not 'delete'}} + // expected-note@-1 {{Memory allocated by 'new[]' should be deallocated by 'delete[]', not 'delete'}} +} + +// CHECK: diagnostics +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: path +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line7 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line7 +// CHECK-NEXT: col5 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line7 +// CHECK-NEXT: col12 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line7 +// CHECK-NEXT: col14 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line7 +// CHECK-NEXT: col12 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line7 +// CHECK-NEXT: col12 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line7 +// CHECK-NEXT: col21 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: depth0 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Memory is allocated +// CHECK-NEXT: message +// CHECK-NEXT: Memory is allocated +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line7 +// CHECK-NEXT: col12 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line7 +// CHECK-NEXT: col14 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line10 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line10 +// CHECK-NEXT: col8 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line10 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line10 +// CHECK-NEXT: col10 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line10 +// CHECK-NEXT: col10 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: depth0 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Memory allocated by 'new[]' should be deallocated by 'delete[]', not 'delete' +// CHECK-NEXT: message +// CHECK-NEXT: Memory allocated by 'new[]' should be deallocated by 'delete[]', not 'delete' +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: descriptionMemory allocated by 'new[]' should be deallocated by 'delete[]', not 'delete' +// CHECK-NEXT: categoryMemory Error +// CHECK-NEXT: typeBad deallocator +// CHECK-NEXT: issue_context_kindfunction +// CHECK-NEXT: issue_contexttest +// CHECK-NEXT: issue_hash4 +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line10 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: diff --git a/test/Analysis/NSContainers.m b/test/Analysis/NSContainers.m index d6fded5fd05..540c7a4eabd 100644 --- a/test/Analysis/NSContainers.m +++ b/test/Analysis/NSContainers.m @@ -115,32 +115,32 @@ void testNilArgNSArray1() { // NSMutableDictionary and NSDictionary APIs. void testNilArgNSMutableDictionary1(NSMutableDictionary *d, NSString* key) { - [d setObject:0 forKey:key]; // expected-warning {{Argument to 'NSMutableDictionary' method 'setObject:forKey:' cannot be nil}} + [d setObject:0 forKey:key]; // expected-warning {{Value argument to 'setObject:forKey:' cannot be nil}} } void testNilArgNSMutableDictionary2(NSMutableDictionary *d, NSObject *obj) { - [d setObject:obj forKey:0]; // expected-warning {{Argument to 'NSMutableDictionary' method 'setObject:forKey:' cannot be nil}} + [d setObject:obj forKey:0]; // expected-warning {{Key argument to 'setObject:forKey:' cannot be nil}} } void testNilArgNSMutableDictionary3(NSMutableDictionary *d) { - [d removeObjectForKey:0]; // expected-warning {{Argument to 'NSMutableDictionary' method 'removeObjectForKey:' cannot be nil}} + [d removeObjectForKey:0]; // expected-warning {{Value argument to 'removeObjectForKey:' cannot be nil}} } void testNilArgNSMutableDictionary5(NSMutableDictionary *d, NSString* key) { - d[key] = 0; // expected-warning {{Dictionary object cannot be nil}} + d[key] = 0; // expected-warning {{Value stored into 'NSMutableDictionary' cannot be nil}} } void testNilArgNSMutableDictionary6(NSMutableDictionary *d, NSString *key) { if (key) ; - d[key] = 0; // expected-warning {{Dictionary key cannot be nil}} - // expected-warning@-1 {{Dictionary object cannot be nil}} + d[key] = 0; // expected-warning {{'NSMutableDictionary' key cannot be nil}} + // expected-warning@-1 {{Value stored into 'NSMutableDictionary' cannot be nil}} } NSDictionary *testNilArgNSDictionary1(NSString* key) { - return [NSDictionary dictionaryWithObject:0 forKey:key]; // expected-warning {{Argument to 'NSDictionary' method 'dictionaryWithObject:forKey:' cannot be nil}} + return [NSDictionary dictionaryWithObject:0 forKey:key]; // expected-warning {{Value argument to 'dictionaryWithObject:forKey:' cannot be nil}} } NSDictionary *testNilArgNSDictionary2(NSObject *obj) { - return [NSDictionary dictionaryWithObject:obj forKey:0]; // expected-warning {{Argument to 'NSDictionary' method 'dictionaryWithObject:forKey:' cannot be nil}} + return [NSDictionary dictionaryWithObject:obj forKey:0]; // expected-warning {{Key argument to 'dictionaryWithObject:forKey:' cannot be nil}} } // Test inline defensive checks suppression. diff --git a/test/Analysis/NewDelete+MismatchedDeallocator_intersections.cpp b/test/Analysis/NewDelete+MismatchedDeallocator_intersections.cpp index 23b70b89fb0..b606f23ec8c 100644 --- a/test/Analysis/NewDelete+MismatchedDeallocator_intersections.cpp +++ b/test/Analysis/NewDelete+MismatchedDeallocator_intersections.cpp @@ -1,4 +1,5 @@ -// RUN: %clang_cc1 -analyze -analyzer-checker=core,alpha.cplusplus.NewDelete,unix.MismatchedDeallocator -analyzer-store region -std=c++11 -verify %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core,cplusplus.NewDelete,unix.MismatchedDeallocator -std=c++11 -verify %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core,cplusplus.NewDelete,alpha.cplusplus.NewDeleteLeaks,unix.MismatchedDeallocator -DLEAKS -std=c++11 -verify %s // expected-no-diagnostics typedef __typeof(sizeof(int)) size_t; diff --git a/test/Analysis/NewDelete-checker-test.cpp b/test/Analysis/NewDelete-checker-test.cpp index c31d7f30322..5d134bc36e2 100644 --- a/test/Analysis/NewDelete-checker-test.cpp +++ b/test/Analysis/NewDelete-checker-test.cpp @@ -1,4 +1,5 @@ -// RUN: %clang_cc1 -analyze -analyzer-checker=core,alpha.cplusplus.NewDelete -analyzer-store region -std=c++11 -fblocks -verify %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core,cplusplus.NewDelete -std=c++11 -fblocks -verify %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core,alpha.cplusplus.NewDeleteLeaks -DLEAKS -std=c++11 -fblocks -verify %s #include "Inputs/system-header-simulator-cxx.h" typedef __typeof__(sizeof(int)) size_t; @@ -12,29 +13,46 @@ int *global; //----- Standard non-placement operators void testGlobalOpNew() { void *p = operator new(0); -} // expected-warning{{Memory is never released; potential leak}} +} +#ifdef LEAKS +// expected-warning@-2{{Potential leak of memory pointed to by 'p'}} +#endif void testGlobalOpNewArray() { void *p = operator new[](0); -} // expected-warning{{Memory is never released; potential leak}} +} +#ifdef LEAKS +// expected-warning@-2{{Potential leak of memory pointed to by 'p'}} +#endif void testGlobalNewExpr() { int *p = new int; -} // expected-warning{{Memory is never released; potential leak}} +} +#ifdef LEAKS +// expected-warning@-2{{Potential leak of memory pointed to by 'p'}} +#endif void testGlobalNewExprArray() { int *p = new int[0]; -} // expected-warning{{Memory is never released; potential leak}} +} +#ifdef LEAKS +// expected-warning@-2{{Potential leak of memory pointed to by 'p'}} +#endif //----- Standard nothrow placement operators void testGlobalNoThrowPlacementOpNewBeforeOverload() { void *p = operator new(0, std::nothrow); -} // expected-warning{{Memory is never released; potential leak}} +} +#ifdef LEAKS +// expected-warning@-2{{Potential leak of memory pointed to by 'p'}} +#endif void testGlobalNoThrowPlacementExprNewBeforeOverload() { int *p = new(std::nothrow) int; -} // expected-warning{{Memory is never released; potential leak}} - +} +#ifdef LEAKS +// expected-warning@-2{{Potential leak of memory pointed to by 'p'}} +#endif //----- Standard pointer placement operators void testGlobalPointerPlacementNew() { @@ -72,14 +90,59 @@ void testNewInvalidationPlacement(PtrWrapper *w) { // other checks //--------------- -void f(int *); +class SomeClass { +public: + void f(int *p); +}; -void testUseAfterDelete() { +void f(int *p1, int *p2 = 0, int *p3 = 0); +void g(SomeClass &c, ...); + +void testUseFirstArgAfterDelete() { int *p = new int; delete p; f(p); // expected-warning{{Use of memory after it is freed}} } +void testUseMiddleArgAfterDelete(int *p) { + delete p; + f(0, p); // expected-warning{{Use of memory after it is freed}} +} + +void testUseLastArgAfterDelete(int *p) { + delete p; + f(0, 0, p); // expected-warning{{Use of memory after it is freed}} +} + +void testUseSeveralArgsAfterDelete(int *p) { + delete p; + f(p, p, p); // expected-warning{{Use of memory after it is freed}} +} + +void testUseRefArgAfterDelete(SomeClass &c) { + delete &c; + g(c); // expected-warning{{Use of memory after it is freed}} +} + +void testVariadicArgAfterDelete() { + SomeClass c; + int *p = new int; + delete p; + g(c, 0, p); // expected-warning{{Use of memory after it is freed}} +} + +void testUseMethodArgAfterDelete(int *p) { + SomeClass *c = new SomeClass; + delete p; + c->f(p); // expected-warning{{Use of memory after it is freed}} +} + +void testUseThisAfterDelete() { + SomeClass *c = new SomeClass; + delete c; + c->f(0); // expected-warning{{Use of memory after it is freed}} +} + void testDeleteAlloca() { int *p = (int *)__builtin_alloca(sizeof(int)); delete p; // expected-warning{{Memory allocated by alloca() should not be deallocated}} diff --git a/test/Analysis/NewDelete-custom.cpp b/test/Analysis/NewDelete-custom.cpp index 7d7796bccb6..c64bfce2dee 100644 --- a/test/Analysis/NewDelete-custom.cpp +++ b/test/Analysis/NewDelete-custom.cpp @@ -1,6 +1,12 @@ -// RUN: %clang_cc1 -analyze -analyzer-checker=core,alpha.cplusplus.NewDelete,unix.Malloc -analyzer-store region -std=c++11 -fblocks -verify %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core,cplusplus.NewDelete,unix.Malloc -std=c++11 -fblocks -verify %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core,cplusplus.NewDelete,alpha.cplusplus.NewDeleteLeaks,unix.Malloc -std=c++11 -DLEAKS -fblocks -verify %s #include "Inputs/system-header-simulator-cxx.h" +#ifndef LEAKS +// expected-no-diagnostics +#endif + + void *allocator(std::size_t size); void *operator new[](std::size_t size) throw() { return allocator(size); } @@ -19,7 +25,10 @@ void testNewMethod() { C *p2 = new C; // no warn C *c3 = ::new C; -} // expected-warning{{Memory is never released; potential leak}} +} +#ifdef LEAKS +// expected-warning@-2{{Potential leak of memory pointed to by 'c3'}} +#endif void testOpNewArray() { void *p = operator new[](0); // call is inlined, no warn @@ -27,7 +36,11 @@ void testOpNewArray() { void testNewExprArray() { int *p = new int[0]; -} // expected-warning{{Memory is never released; potential leak}} +} +#ifdef LEAKS +// expected-warning@-2{{Potential leak of memory pointed to by 'p'}} +#endif + //----- Custom non-placement operators void testOpNew() { @@ -36,16 +49,26 @@ void testOpNew() { void testNewExpr() { int *p = new int; -} // expected-warning{{Memory is never released; potential leak}} +} +#ifdef LEAKS +// expected-warning@-2{{Potential leak of memory pointed to by 'p'}} +#endif + //----- Custom NoThrow placement operators void testOpNewNoThrow() { void *p = operator new(0, std::nothrow); -} // expected-warning{{Memory is never released; potential leak}} +} +#ifdef LEAKS +// expected-warning@-2{{Potential leak of memory pointed to by 'p'}} +#endif void testNewExprNoThrow() { int *p = new(std::nothrow) int; -} // expected-warning{{Memory is never released; potential leak}} +} +#ifdef LEAKS +// expected-warning@-2{{Potential leak of memory pointed to by 'p'}} +#endif //----- Custom placement operators void testOpNewPlacement() { diff --git a/test/Analysis/NewDelete-intersections.mm b/test/Analysis/NewDelete-intersections.mm index 3a87e4f3f11..9024ed57668 100644 --- a/test/Analysis/NewDelete-intersections.mm +++ b/test/Analysis/NewDelete-intersections.mm @@ -1,4 +1,5 @@ -// RUN: %clang_cc1 -analyze -analyzer-checker=core,alpha.cplusplus.NewDelete -analyzer-store region -std=c++11 -fblocks -verify %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core,cplusplus.NewDelete -std=c++11 -fblocks -verify %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core,cplusplus.NewDelete,alpha.cplusplus.NewDeleteLeaks -std=c++11 -DLEAKS -fblocks -verify %s #include "Inputs/system-header-simulator-cxx.h" #include "Inputs/system-header-simulator-objc.h" @@ -39,16 +40,25 @@ void testDeleteMalloced() { void testFreeOpNew() { void *p = operator new(0); free(p); -} // expected-warning{{Memory is never released; potential leak}} +} +#ifdef LEAKS +// expected-warning@-2 {{Potential leak of memory pointed to by 'p'}} +#endif void testFreeNewExpr() { int *p = new int; free(p); -} // expected-warning{{Memory is never released; potential leak}} +} +#ifdef LEAKS +// expected-warning@-2 {{Potential leak of memory pointed to by 'p'}} +#endif void testObjcFreeNewed() { int *p = new int; - NSData *nsdata = [NSData dataWithBytesNoCopy:p length:sizeof(int) freeWhenDone:1]; // expected-warning{{Memory is never released; potential leak}} + NSData *nsdata = [NSData dataWithBytesNoCopy:p length:sizeof(int) freeWhenDone:1]; +#ifdef LEAKS + // expected-warning@-2 {{Potential leak of memory pointed to by 'p'}} +#endif } void testFreeAfterDelete() { diff --git a/test/Analysis/NewDelete-path-notes.cpp b/test/Analysis/NewDelete-path-notes.cpp index eeb610537b8..85b71be68ce 100644 --- a/test/Analysis/NewDelete-path-notes.cpp +++ b/test/Analysis/NewDelete-path-notes.cpp @@ -1,5 +1,5 @@ -// RUN: %clang_cc1 -analyze -analyzer-checker=alpha.cplusplus.NewDelete,unix.Malloc -analyzer-output=text -verify %s -// RUN: %clang_cc1 -analyze -analyzer-checker=alpha.cplusplus.NewDelete,unix.Malloc -analyzer-output=plist %s -o %t.plist +// RUN: %clang_cc1 -analyze -analyzer-checker=cplusplus.NewDelete,unix.Malloc -analyzer-output=text -verify %s +// RUN: %clang_cc1 -analyze -analyzer-checker=cplusplus.NewDelete,unix.Malloc -analyzer-output=plist %s -o %t.plist // RUN: FileCheck --input-file=%t.plist %s void test() { @@ -15,80 +15,68 @@ void test() { // expected-note@-1 {{Attempt to free released memory}} } +struct Odd { + void kill() { + delete this; // expected-note {{Memory is released}} + } +}; + +void test(Odd *odd) { + odd->kill(); // expected-note{{Calling 'Odd::kill'}} + // expected-note@-1 {{Returning; memory was released}} + delete odd; // expected-warning {{Attempt to free released memory}} + // expected-note@-1 {{Attempt to free released memory}} +} + // CHECK: diagnostics -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: path // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: kindcontrol -// CHECK-NEXT: edges -// CHECK-NEXT: +// CHECK-NEXT: path +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line6 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line6 +// CHECK-NEXT: col5 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line6 +// CHECK-NEXT: col12 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line6 +// CHECK-NEXT: col14 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location // CHECK-NEXT: -// CHECK-NEXT: start -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line6 -// CHECK-NEXT: col3 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line6 -// CHECK-NEXT: col5 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: end -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line6 -// CHECK-NEXT: col12 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line6 -// CHECK-NEXT: col14 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: +// CHECK-NEXT: line6 +// CHECK-NEXT: col12 +// CHECK-NEXT: file0 // CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: kindevent -// CHECK-NEXT: location -// CHECK-NEXT: -// CHECK-NEXT: line6 -// CHECK-NEXT: col12 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: ranges -// CHECK-NEXT: +// CHECK-NEXT: ranges // CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line6 -// CHECK-NEXT: col12 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line6 -// CHECK-NEXT: col18 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: depth0 -// CHECK-NEXT: extended_message -// CHECK-NEXT: Memory is allocated -// CHECK-NEXT: message -// CHECK-NEXT: Memory is allocated -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: kindcontrol -// CHECK-NEXT: edges -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: start // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line6 @@ -97,227 +85,467 @@ void test() { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line6 -// CHECK-NEXT: col14 +// CHECK-NEXT: col18 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: depth0 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Memory is allocated +// CHECK-NEXT: message +// CHECK-NEXT: Memory is allocated +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line6 +// CHECK-NEXT: col12 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line6 +// CHECK-NEXT: col14 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line8 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line8 +// CHECK-NEXT: col4 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line8 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line8 +// CHECK-NEXT: col4 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line8 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line8 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line8 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line8 -// CHECK-NEXT: col3 +// CHECK-NEXT: col7 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line8 -// CHECK-NEXT: col4 +// CHECK-NEXT: col7 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: kindcontrol -// CHECK-NEXT: edges -// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: depth0 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Assuming 'p' is non-null +// CHECK-NEXT: message +// CHECK-NEXT: Assuming 'p' is non-null +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line8 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line8 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line11 +// CHECK-NEXT: col5 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line11 +// CHECK-NEXT: col10 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location // CHECK-NEXT: -// CHECK-NEXT: start +// CHECK-NEXT: line11 +// CHECK-NEXT: col5 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line8 -// CHECK-NEXT: col3 +// CHECK-NEXT: line11 +// CHECK-NEXT: col5 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line8 -// CHECK-NEXT: col4 +// CHECK-NEXT: line11 +// CHECK-NEXT: col12 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: depth0 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Memory is released +// CHECK-NEXT: message +// CHECK-NEXT: Memory is released +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line11 +// CHECK-NEXT: col5 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line11 +// CHECK-NEXT: col10 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line14 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line14 +// CHECK-NEXT: col8 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line14 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line8 -// CHECK-NEXT: col7 +// CHECK-NEXT: line14 +// CHECK-NEXT: col3 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line8 -// CHECK-NEXT: col7 +// CHECK-NEXT: line14 +// CHECK-NEXT: col10 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: kindevent -// CHECK-NEXT: location -// CHECK-NEXT: -// CHECK-NEXT: line8 -// CHECK-NEXT: col7 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: ranges -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line8 -// CHECK-NEXT: col7 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line8 -// CHECK-NEXT: col7 -// CHECK-NEXT: file0 -// CHECK-NEXT: // CHECK-NEXT: +// CHECK-NEXT: depth0 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Attempt to free released memory +// CHECK-NEXT: message +// CHECK-NEXT: Attempt to free released memory +// CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: depth0 -// CHECK-NEXT: extended_message -// CHECK-NEXT: Assuming 'p' is non-null -// CHECK-NEXT: message -// CHECK-NEXT: Assuming 'p' is non-null +// CHECK-NEXT: descriptionAttempt to free released memory +// CHECK-NEXT: categoryMemory Error +// CHECK-NEXT: typeDouble free +// CHECK-NEXT: issue_context_kindfunction +// CHECK-NEXT: issue_contexttest +// CHECK-NEXT: issue_hash9 +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line14 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: kindcontrol -// CHECK-NEXT: edges -// CHECK-NEXT: +// CHECK-NEXT: path +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location // CHECK-NEXT: -// CHECK-NEXT: start +// CHECK-NEXT: line25 +// CHECK-NEXT: col2 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line8 -// CHECK-NEXT: col7 +// CHECK-NEXT: line25 +// CHECK-NEXT: col2 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line8 -// CHECK-NEXT: col7 +// CHECK-NEXT: line25 +// CHECK-NEXT: col12 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: depth0 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Calling 'Odd::kill' +// CHECK-NEXT: message +// CHECK-NEXT: Calling 'Odd::kill' +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line19 +// CHECK-NEXT: col2 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: depth1 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Entered call from 'test' +// CHECK-NEXT: message +// CHECK-NEXT: Entered call from 'test' +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line19 +// CHECK-NEXT: col2 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line19 +// CHECK-NEXT: col5 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line20 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line20 +// CHECK-NEXT: col8 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line20 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line11 -// CHECK-NEXT: col5 +// CHECK-NEXT: line20 +// CHECK-NEXT: col3 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line11 -// CHECK-NEXT: col10 +// CHECK-NEXT: line20 +// CHECK-NEXT: col13 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: kindevent -// CHECK-NEXT: location -// CHECK-NEXT: -// CHECK-NEXT: line11 -// CHECK-NEXT: col5 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: ranges -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line11 -// CHECK-NEXT: col5 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line11 -// CHECK-NEXT: col12 -// CHECK-NEXT: file0 -// CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: depth0 -// CHECK-NEXT: extended_message -// CHECK-NEXT: Memory is released -// CHECK-NEXT: message -// CHECK-NEXT: Memory is released -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: kindcontrol -// CHECK-NEXT: edges -// CHECK-NEXT: +// CHECK-NEXT: depth1 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Memory is released +// CHECK-NEXT: message +// CHECK-NEXT: Memory is released +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location // CHECK-NEXT: -// CHECK-NEXT: start +// CHECK-NEXT: line25 +// CHECK-NEXT: col2 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line11 -// CHECK-NEXT: col5 +// CHECK-NEXT: line25 +// CHECK-NEXT: col2 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line11 -// CHECK-NEXT: col10 +// CHECK-NEXT: line25 +// CHECK-NEXT: col12 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: depth0 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Returning; memory was released +// CHECK-NEXT: message +// CHECK-NEXT: Returning; memory was released +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line25 +// CHECK-NEXT: col2 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line25 +// CHECK-NEXT: col4 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line27 +// CHECK-NEXT: col2 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line27 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line27 +// CHECK-NEXT: col2 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line14 -// CHECK-NEXT: col3 +// CHECK-NEXT: line27 +// CHECK-NEXT: col2 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line14 -// CHECK-NEXT: col8 +// CHECK-NEXT: line27 +// CHECK-NEXT: col11 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: kindevent -// CHECK-NEXT: location -// CHECK-NEXT: -// CHECK-NEXT: line14 -// CHECK-NEXT: col3 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: ranges -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line14 -// CHECK-NEXT: col3 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line14 -// CHECK-NEXT: col10 -// CHECK-NEXT: file0 -// CHECK-NEXT: // CHECK-NEXT: +// CHECK-NEXT: depth0 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Attempt to free released memory +// CHECK-NEXT: message +// CHECK-NEXT: Attempt to free released memory +// CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: depth0 -// CHECK-NEXT: extended_message -// CHECK-NEXT: Attempt to free released memory -// CHECK-NEXT: message -// CHECK-NEXT: Attempt to free released memory +// CHECK-NEXT: descriptionAttempt to free released memory +// CHECK-NEXT: categoryMemory Error +// CHECK-NEXT: typeDouble free +// CHECK-NEXT: issue_context_kindfunction +// CHECK-NEXT: issue_contexttest +// CHECK-NEXT: issue_hash3 +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line27 +// CHECK-NEXT: col2 +// CHECK-NEXT: file0 +// CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: descriptionAttempt to free released memory -// CHECK-NEXT: categoryMemory Error -// CHECK-NEXT: typeDouble free -// CHECK-NEXT: issue_context_kindfunction -// CHECK-NEXT: issue_contexttest -// CHECK-NEXT: issue_hash9 -// CHECK-NEXT: location -// CHECK-NEXT: -// CHECK-NEXT: line14 -// CHECK-NEXT: col3 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: diff --git a/test/Analysis/NewDelete-variadic.cpp b/test/Analysis/NewDelete-variadic.cpp index 129af1f9c69..53dba463bbf 100644 --- a/test/Analysis/NewDelete-variadic.cpp +++ b/test/Analysis/NewDelete-variadic.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -analyze -analyzer-checker=core,alpha.cplusplus.NewDelete,unix.Malloc -analyzer-store region -std=c++11 -fblocks -verify %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core,cplusplus.NewDelete,alpha.cplusplus.NewDeleteLeaks,unix.Malloc -std=c++11 -fblocks -verify %s // expected-no-diagnostics namespace std { diff --git a/test/Analysis/analyzer-config.c b/test/Analysis/analyzer-config.c index 96b948352b3..55b1df9ca8a 100644 --- a/test/Analysis/analyzer-config.c +++ b/test/Analysis/analyzer-config.c @@ -1,4 +1,4 @@ -// RUN: %clang --analyze %s -o /dev/null -Xclang -analyzer-checker=debug.ConfigDumper > %t 2>&1 +// RUN: %clang -target x86_64-apple-darwin10 --analyze %s -o /dev/null -Xclang -analyzer-checker=debug.ConfigDumper > %t 2>&1 // RUN: FileCheck --input-file=%t %s void bar() {} @@ -11,10 +11,12 @@ void foo() { bar(); } // CHECK-NEXT: graph-trim-interval = 1000 // CHECK-NEXT: ipa = dynamic-bifurcate // CHECK-NEXT: ipa-always-inline-size = 3 +// CHECK-NEXT: leak-diagnostics-reference-allocation = false // CHECK-NEXT: max-inlinable-size = 50 // CHECK-NEXT: max-nodes = 150000 // CHECK-NEXT: max-times-inline-large = 32 // CHECK-NEXT: mode = deep +// CHECK-NEXT: region-store-small-struct-limit = 2 // CHECK-NEXT: [stats] -// CHECK-NEXT: num-entries = 10 +// CHECK-NEXT: num-entries = 12 diff --git a/test/Analysis/analyzer-config.cpp b/test/Analysis/analyzer-config.cpp index 1224204f8c7..bf18a5eb3e7 100644 --- a/test/Analysis/analyzer-config.cpp +++ b/test/Analysis/analyzer-config.cpp @@ -1,4 +1,4 @@ -// RUN: %clang --analyze %s -o /dev/null -Xclang -analyzer-checker=debug.ConfigDumper > %t 2>&1 +// RUN: %clang -target x86_64-apple-darwin10 --analyze %s -o /dev/null -Xclang -analyzer-checker=debug.ConfigDumper > %t 2>&1 // RUN: FileCheck --input-file=%t %s void bar() {} @@ -21,9 +21,11 @@ class Foo { // CHECK-NEXT: graph-trim-interval = 1000 // CHECK-NEXT: ipa = dynamic-bifurcate // CHECK-NEXT: ipa-always-inline-size = 3 +// CHECK-NEXT: leak-diagnostics-reference-allocation = false // CHECK-NEXT: max-inlinable-size = 50 // CHECK-NEXT: max-nodes = 150000 // CHECK-NEXT: max-times-inline-large = 32 // CHECK-NEXT: mode = deep +// CHECK-NEXT: region-store-small-struct-limit = 2 // CHECK-NEXT: [stats] -// CHECK-NEXT: num-entries = 14 +// CHECK-NEXT: num-entries = 16 diff --git a/test/Analysis/bool-assignment.cpp b/test/Analysis/bool-assignment.c similarity index 66% rename from test/Analysis/bool-assignment.cpp rename to test/Analysis/bool-assignment.c index 9361d93aab3..0f782fbfd9a 100644 --- a/test/Analysis/bool-assignment.cpp +++ b/test/Analysis/bool-assignment.c @@ -1,27 +1,32 @@ -// RUN: %clang_cc1 -analyze -analyzer-checker=core,alpha.core.BoolAssignment -analyzer-store=region -verify %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core,alpha.core.BoolAssignment -analyzer-store=region -verify -std=c99 -Dbool=_Bool %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core,alpha.core.BoolAssignment -analyzer-store=region -verify -x c++ %s -// Test C++'s bool +// Test C++'s bool and C's _Bool. +// FIXME: We stopped warning on these when SValBuilder got smarter about +// casts to bool. Arguably, however, these conversions are okay; the result +// is always 'true' or 'false'. -void test_cppbool_initialization(int y) { +void test_stdbool_initialization(int y) { + bool constant = 2; // no-warning if (y < 0) { - bool x = y; // expected-warning {{Assignment of a non-Boolean value}} + bool x = y; // no-warning return; } if (y > 1) { - bool x = y; // expected-warning {{Assignment of a non-Boolean value}} + bool x = y; // no-warning return; } bool x = y; // no-warning } -void test_cppbool_assignment(int y) { +void test_stdbool_assignment(int y) { bool x = 0; // no-warning if (y < 0) { - x = y; // expected-warning {{Assignment of a non-Boolean value}} + x = y; // no-warning return; } if (y > 1) { - x = y; // expected-warning {{Assignment of a non-Boolean value}} + x = y; // no-warning return; } x = y; // no-warning @@ -32,6 +37,7 @@ void test_cppbool_assignment(int y) { typedef signed char BOOL; void test_BOOL_initialization(int y) { + BOOL constant = 2; // expected-warning {{Assignment of a non-Boolean value}} if (y < 0) { BOOL x = y; // expected-warning {{Assignment of a non-Boolean value}} return; @@ -62,6 +68,7 @@ void test_BOOL_assignment(int y) { typedef unsigned char Boolean; void test_Boolean_initialization(int y) { + Boolean constant = 2; // expected-warning {{Assignment of a non-Boolean value}} if (y < 0) { Boolean x = y; // expected-warning {{Assignment of a non-Boolean value}} return; diff --git a/test/Analysis/bool-assignment2.c b/test/Analysis/bool-assignment2.c deleted file mode 100644 index 22f4237adfd..00000000000 --- a/test/Analysis/bool-assignment2.c +++ /dev/null @@ -1,35 +0,0 @@ -// RUN: %clang_cc1 -std=c99 -analyze -analyzer-checker=core,alpha.core.BoolAssignment -analyzer-store=region -verify %s - -// Test stdbool.h's _Bool - -// Prior to C99, stdbool.h uses this typedef, but even in ANSI C mode, _Bool -// appears to be defined. - -// #if __STDC_VERSION__ < 199901L -// typedef int _Bool; -// #endif - -void test_stdbool_initialization(int y) { - if (y < 0) { - _Bool x = y; // expected-warning {{Assignment of a non-Boolean value}} - return; - } - if (y > 1) { - _Bool x = y; // expected-warning {{Assignment of a non-Boolean value}} - return; - } - _Bool x = y; // no-warning -} - -void test_stdbool_assignment(int y) { - _Bool x = 0; // no-warning - if (y < 0) { - x = y; // expected-warning {{Assignment of a non-Boolean value}} - return; - } - if (y > 1) { - x = y; // expected-warning {{Assignment of a non-Boolean value}} - return; - } - x = y; // no-warning -} diff --git a/test/Analysis/casts.c b/test/Analysis/casts.c index 087bd978e11..3e2f8077ede 100644 --- a/test/Analysis/casts.c +++ b/test/Analysis/casts.c @@ -1,6 +1,7 @@ -// RUN: %clang_cc1 -triple x86_64-apple-darwin9 -analyze -analyzer-checker=core,alpha.core -analyzer-store=region -verify %s -// RUN: %clang_cc1 -triple i386-apple-darwin9 -analyze -analyzer-checker=core,alpha.core -analyzer-store=region -verify %s -// expected-no-diagnostics +// RUN: %clang_cc1 -triple x86_64-apple-darwin9 -analyze -analyzer-checker=core,alpha.core,debug.ExprInspection -analyzer-store=region -verify %s +// RUN: %clang_cc1 -triple i386-apple-darwin9 -analyze -analyzer-checker=core,alpha.core,debug.ExprInspection -analyzer-store=region -verify %s + +extern void clang_analyzer_eval(_Bool); // Test if the 'storage' region gets properly initialized after it is cast to // 'struct sockaddr *'. @@ -85,3 +86,34 @@ int foo (int* p) { } return 0; } + +void castsToBool() { + clang_analyzer_eval(0); // expected-warning{{FALSE}} + clang_analyzer_eval(0U); // expected-warning{{FALSE}} + clang_analyzer_eval((void *)0); // expected-warning{{FALSE}} + + clang_analyzer_eval(1); // expected-warning{{TRUE}} + clang_analyzer_eval(1U); // expected-warning{{TRUE}} + clang_analyzer_eval(-1); // expected-warning{{TRUE}} + clang_analyzer_eval(0x100); // expected-warning{{TRUE}} + clang_analyzer_eval(0x100U); // expected-warning{{TRUE}} + clang_analyzer_eval((void *)0x100); // expected-warning{{TRUE}} + + extern int symbolicInt; + clang_analyzer_eval(symbolicInt); // expected-warning{{UNKNOWN}} + if (symbolicInt) + clang_analyzer_eval(symbolicInt); // expected-warning{{TRUE}} + + extern void *symbolicPointer; + clang_analyzer_eval(symbolicPointer); // expected-warning{{UNKNOWN}} + if (symbolicPointer) + clang_analyzer_eval(symbolicPointer); // expected-warning{{TRUE}} + + int localInt; + clang_analyzer_eval(&localInt); // expected-warning{{TRUE}} + clang_analyzer_eval(&castsToBool); // expected-warning{{TRUE}} + clang_analyzer_eval("abc"); // expected-warning{{TRUE}} + + extern float globalFloat; + clang_analyzer_eval(globalFloat); // expected-warning{{UNKNOWN}} +} diff --git a/test/Analysis/conditional-operator-path-notes.c b/test/Analysis/conditional-operator-path-notes.c index c781ddf8330..a8af394a26d 100644 --- a/test/Analysis/conditional-operator-path-notes.c +++ b/test/Analysis/conditional-operator-path-notes.c @@ -242,12 +242,12 @@ void testBinaryLHSProblem(int *p) { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line10 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line10 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -259,7 +259,7 @@ void testBinaryLHSProblem(int *p) { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line10 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: ranges @@ -293,7 +293,7 @@ void testBinaryLHSProblem(int *p) { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line10 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -882,12 +882,12 @@ void testBinaryLHSProblem(int *p) { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line44 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line44 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -899,7 +899,7 @@ void testBinaryLHSProblem(int *p) { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line44 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: ranges @@ -933,7 +933,7 @@ void testBinaryLHSProblem(int *p) { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line44 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: diff --git a/test/Analysis/conditional-operator.cpp b/test/Analysis/conditional-operator.cpp new file mode 100644 index 00000000000..5a3c325b91d --- /dev/null +++ b/test/Analysis/conditional-operator.cpp @@ -0,0 +1,17 @@ +// RUN: %clang -cc1 -analyze -analyzer-checker=core,debug.ExprInspection %s -analyzer-output=text -verify + +void clang_analyzer_eval(bool); + +// Test that the analyzer does not crash on GNU extension operator "?:". +void NoCrashTest(int x, int y) { + int w = x ?: y; +} + +void OperatorEvaluationTest(int y) { + int x = 1; + int w = x ?: y; // expected-note {{'?' condition is true}} + + // TODO: We are not precise when processing the "?:" operator in C++. + clang_analyzer_eval(w == 1); // expected-warning{{UNKNOWN}} + // expected-note@-1{{UNKNOWN}} +} \ No newline at end of file diff --git a/test/Analysis/coverage.c b/test/Analysis/coverage.c index 38e84e17cee..9e437d21827 100644 --- a/test/Analysis/coverage.c +++ b/test/Analysis/coverage.c @@ -33,26 +33,26 @@ static void function_which_doesnt_give_up_nested(int *x, int *y) { void coverage1(int *x) { function_which_gives_up(x); char *m = (char*)malloc(12); -} // expected-warning {{potential leak}} +} // expected-warning {{Potential leak of memory pointed to by 'm'}} void coverage2(int *x) { if (x) { function_which_gives_up(x); char *m = (char*)malloc(12); } -} // expected-warning {{potential leak}} +} // expected-warning {{Potential leak of memory pointed to by 'm'}} void coverage3(int *x) { x++; function_which_gives_up(x); char *m = (char*)malloc(12); -} // expected-warning {{potential leak}} +} // expected-warning {{Potential leak of memory pointed to by 'm'}} void coverage4(int *x) { *x += another_function(x); function_which_gives_up(x); char *m = (char*)malloc(12); -} // expected-warning {{potential leak}} +} // expected-warning {{Potential leak of memory pointed to by 'm'}} void coverage5(int *x) { for (int i = 0; i<7; ++i) @@ -66,7 +66,7 @@ void coverage6(int *x) { function_which_gives_up(x); } char *m = (char*)malloc(12); -} // expected-warning {{potential leak}} +} // expected-warning {{Potential leak of memory pointed to by 'm'}} int coverage7_inline(int *i) { function_which_doesnt_give_up(&i); @@ -78,7 +78,7 @@ void coverage8(int *x) { function_which_doesnt_give_up_nested(x, &y); y = (*x)/y; // expected-warning {{Division by zero}} char *m = (char*)malloc(12); -} // expected-warning {{potential leak}} +} // expected-warning {{Potential leak of memory pointed to by 'm'}} void function_which_gives_up_settonull(int **x) { *x = 0; diff --git a/test/Analysis/cstring-syntax-cxx.cpp b/test/Analysis/cstring-syntax-cxx.cpp index bae3d0a1642..39c978ab600 100644 --- a/test/Analysis/cstring-syntax-cxx.cpp +++ b/test/Analysis/cstring-syntax-cxx.cpp @@ -15,3 +15,8 @@ void test(X a, X b) { X c = a + b; } +// Ensure we don't crash on custom-defined strncat. +char strncat (); +int main () { + return strncat (); +} \ No newline at end of file diff --git a/test/Analysis/dead-stores.c b/test/Analysis/dead-stores.c index 165a12b59b1..067a0504f13 100644 --- a/test/Analysis/dead-stores.c +++ b/test/Analysis/dead-stores.c @@ -547,3 +547,25 @@ int *radar11185138_baz() { return y; } +int getInt(); +int *getPtr(); +void testBOComma() { + int x0 = (getInt(), 0); // expected-warning{{unused variable 'x0'}} + int x1 = (getInt(), getInt()); // expected-warning {{Value stored to 'x1' during its initialization is never read}} // expected-warning{{unused variable 'x1'}} + int x2 = (getInt(), getInt(), getInt()); //expected-warning{{Value stored to 'x2' during its initialization is never read}} // expected-warning{{unused variable 'x2'}} + int x3; + x3 = (getInt(), getInt(), 0); // expected-warning{{Value stored to 'x3' is never read}} + int x4 = (getInt(), (getInt(), 0)); // expected-warning{{unused variable 'x4'}} + int y; + int x5 = (getInt(), (y = 0)); // expected-warning{{unused variable 'x5'}} + int x6 = (getInt(), (y = getInt())); //expected-warning {{Value stored to 'x6' during its initialization is never read}} // expected-warning{{unused variable 'x6'}} + int x7 = 0, x8 = getInt(); //expected-warning {{Value stored to 'x8' during its initialization is never read}} // expected-warning{{unused variable 'x8'}} // expected-warning{{unused variable 'x7'}} + int x9 = getInt(), x10 = 0; //expected-warning {{Value stored to 'x9' during its initialization is never read}} // expected-warning{{unused variable 'x9'}} // expected-warning{{unused variable 'x10'}} + int m = getInt(), mm, mmm; //expected-warning {{Value stored to 'm' during its initialization is never read}} // expected-warning{{unused variable 'm'}} // expected-warning{{unused variable 'mm'}} // expected-warning{{unused variable 'mmm'}} + int n, nn = getInt(); //expected-warning {{Value stored to 'nn' during its initialization is never read}} // expected-warning{{unused variable 'n'}} // expected-warning{{unused variable 'nn'}} + + int *p; + p = (getPtr(), (int *)0); // no warning + +} + diff --git a/test/Analysis/derived-to-base.cpp b/test/Analysis/derived-to-base.cpp index b846d2c28bb..0664189a956 100644 --- a/test/Analysis/derived-to-base.cpp +++ b/test/Analysis/derived-to-base.cpp @@ -2,6 +2,7 @@ // RUN: %clang_cc1 -analyze -analyzer-checker=core,debug.ExprInspection -DCONSTRUCTORS=1 -analyzer-config c++-inlining=constructors -verify %s void clang_analyzer_eval(bool); +void clang_analyzer_checkInlined(bool); class A { protected: @@ -363,3 +364,89 @@ namespace Redeclaration { } }; +namespace PR15394 { + namespace Original { + class Base { + public: + virtual int f() = 0; + int i; + }; + + class Derived1 : public Base { + public: + int j; + }; + + class Derived2 : public Derived1 { + public: + virtual int f() { + clang_analyzer_checkInlined(true); // expected-warning{{TRUE}} + return i + j; + } + }; + + void testXXX() { + Derived1 *d1p = reinterpret_cast(new Derived2); + d1p->i = 1; + d1p->j = 2; + clang_analyzer_eval(d1p->f() == 3); // expected-warning{{TRUE}} + } + } + + namespace VirtualInDerived { + class Base { + public: + int i; + }; + + class Derived1 : public Base { + public: + virtual int f() = 0; + int j; + }; + + class Derived2 : public Derived1 { + public: + virtual int f() { + clang_analyzer_checkInlined(true); // expected-warning{{TRUE}} + return i + j; + } + }; + + void test() { + Derived1 *d1p = reinterpret_cast(new Derived2); + d1p->i = 1; + d1p->j = 2; + clang_analyzer_eval(d1p->f() == 3); // expected-warning{{TRUE}} + } + } + + namespace NoCast { + class Base { + public: + int i; + }; + + class Derived1 : public Base { + public: + virtual int f() = 0; + int j; + }; + + class Derived2 : public Derived1 { + public: + virtual int f() { + clang_analyzer_checkInlined(true); // expected-warning{{TRUE}} + return i + j; + } + }; + + void test() { + Derived1 *d1p = new Derived2; + d1p->i = 1; + d1p->j = 2; + clang_analyzer_eval(d1p->f() == 3); // expected-warning{{TRUE}} + } + } +}; + diff --git a/test/Analysis/diagnostics/deref-track-symbolic-region.c b/test/Analysis/diagnostics/deref-track-symbolic-region.c index 94774dd61da..03716de9ffe 100644 --- a/test/Analysis/diagnostics/deref-track-symbolic-region.c +++ b/test/Analysis/diagnostics/deref-track-symbolic-region.c @@ -24,6 +24,21 @@ void test(struct S syz, int *pp) { // expected-note@-1{{Dereference of null pointer (loaded from field 'x')}} } +void testTrackConstraintBRVisitorIsTrackingTurnedOn(struct S syz, int *pp) { + int m = 0; + syz.x = foo(); // expected-note{{Value assigned to 'syz.x'}} + + struct S *ps = &syz; + if (ps->x) + //expected-note@-1{{Taking false branch}} + //expected-note@-2{{Assuming pointer value is null}} + + m++; + int *p = syz.x; //expected-note {{'p' initialized to a null pointer value}} + m = *p; // expected-warning {{Dereference of null pointer (loaded from variable 'p')}} + // expected-note@-1 {{Dereference of null pointer (loaded from variable 'p')}} +} + // CHECK: diagnostics // CHECK-NEXT: // CHECK-NEXT: @@ -300,6 +315,341 @@ void test(struct S syz, int *pp) { // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: path +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line28 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line28 +// CHECK-NEXT: col5 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line29 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line29 +// CHECK-NEXT: col5 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line29 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line29 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line29 +// CHECK-NEXT: col15 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: depth0 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Value assigned to 'syz.x' +// CHECK-NEXT: message +// CHECK-NEXT: Value assigned to 'syz.x' +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line29 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line29 +// CHECK-NEXT: col5 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line32 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line32 +// CHECK-NEXT: col4 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line32 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line32 +// CHECK-NEXT: col4 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line32 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line32 +// CHECK-NEXT: col8 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line32 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line32 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line32 +// CHECK-NEXT: col11 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: depth0 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Assuming pointer value is null +// CHECK-NEXT: message +// CHECK-NEXT: Assuming pointer value is null +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line32 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line32 +// CHECK-NEXT: col8 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line37 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line37 +// CHECK-NEXT: col5 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line37 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line37 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line37 +// CHECK-NEXT: col8 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: depth0 +// CHECK-NEXT: extended_message +// CHECK-NEXT: 'p' initialized to a null pointer value +// CHECK-NEXT: message +// CHECK-NEXT: 'p' initialized to a null pointer value +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line37 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line37 +// CHECK-NEXT: col5 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line38 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line38 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line38 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line38 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line38 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line38 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line38 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line38 +// CHECK-NEXT: col8 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line38 +// CHECK-NEXT: col8 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: depth0 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Dereference of null pointer (loaded from variable 'p') +// CHECK-NEXT: message +// CHECK-NEXT: Dereference of null pointer (loaded from variable 'p') +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: descriptionDereference of null pointer (loaded from variable 'p') +// CHECK-NEXT: categoryLogic error +// CHECK-NEXT: typeDereference of null pointer +// CHECK-NEXT: issue_context_kindfunction +// CHECK-NEXT: issue_contexttestTrackConstraintBRVisitorIsTrackingTurnedOn +// CHECK-NEXT: issue_hash11 +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line38 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: diff --git a/test/Analysis/diagnostics/explicit-suppression.cpp b/test/Analysis/diagnostics/explicit-suppression.cpp index 79afeed6c56..57d2d16f89c 100644 --- a/test/Analysis/diagnostics/explicit-suppression.cpp +++ b/test/Analysis/diagnostics/explicit-suppression.cpp @@ -12,69 +12,6 @@ void clang_analyzer_eval(bool); void testCopyNull(int *I, int *E) { std::copy(I, E, (int *)0); #ifndef SUPPRESSED - // This line number comes from system-header-simulator-cxx.h. - // expected-warning@79 {{Dereference of null pointer}} + // expected-warning@../Inputs/system-header-simulator-cxx.h:80 {{Dereference of null pointer}} #endif } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -// PR15613: expected-* can't refer to diagnostics in other source files. -// The current implementation only matches line numbers, but has an upper limit -// of the number of lines in the main source file. diff --git a/test/Analysis/diagnostics/undef-value-param.c b/test/Analysis/diagnostics/undef-value-param.c index 597bf91fa24..5855f507f9f 100644 --- a/test/Analysis/diagnostics/undef-value-param.c +++ b/test/Analysis/diagnostics/undef-value-param.c @@ -328,7 +328,7 @@ double testPassingParentRegionStruct(int x) { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: depth1 +// CHECK-NEXT: depth0 // CHECK-NEXT: extended_message // CHECK-NEXT: Returning from 'foo' // CHECK-NEXT: message @@ -390,46 +390,12 @@ double testPassingParentRegionStruct(int x) { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line26 -// CHECK-NEXT: col5 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line26 -// CHECK-NEXT: col10 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: kindcontrol -// CHECK-NEXT: edges -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: start -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line26 -// CHECK-NEXT: col5 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line26 -// CHECK-NEXT: col10 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: end -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line26 -// CHECK-NEXT: col12 +// CHECK-NEXT: col14 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line26 -// CHECK-NEXT: col13 +// CHECK-NEXT: col14 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -441,7 +407,7 @@ double testPassingParentRegionStruct(int x) { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line26 -// CHECK-NEXT: col12 +// CHECK-NEXT: col14 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: ranges @@ -475,7 +441,7 @@ double testPassingParentRegionStruct(int x) { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line26 -// CHECK-NEXT: col12 +// CHECK-NEXT: col14 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -713,7 +679,7 @@ double testPassingParentRegionStruct(int x) { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: depth1 +// CHECK-NEXT: depth0 // CHECK-NEXT: extended_message // CHECK-NEXT: Returning from 'initArray' // CHECK-NEXT: message @@ -741,46 +707,12 @@ double testPassingParentRegionStruct(int x) { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line42 -// CHECK-NEXT: col5 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line42 -// CHECK-NEXT: col10 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: kindcontrol -// CHECK-NEXT: edges -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: start -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line42 -// CHECK-NEXT: col5 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line42 -// CHECK-NEXT: col10 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: end -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line42 -// CHECK-NEXT: col12 +// CHECK-NEXT: col14 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line42 -// CHECK-NEXT: col12 +// CHECK-NEXT: col14 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -792,7 +724,7 @@ double testPassingParentRegionStruct(int x) { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line42 -// CHECK-NEXT: col12 +// CHECK-NEXT: col14 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: ranges @@ -826,7 +758,7 @@ double testPassingParentRegionStruct(int x) { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line42 -// CHECK-NEXT: col12 +// CHECK-NEXT: col14 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -1127,7 +1059,7 @@ double testPassingParentRegionStruct(int x) { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: depth1 +// CHECK-NEXT: depth0 // CHECK-NEXT: extended_message // CHECK-NEXT: Returning from 'initStruct' // CHECK-NEXT: message diff --git a/test/Analysis/diagnostics/undef-value-param.m b/test/Analysis/diagnostics/undef-value-param.m index d6c8a16795a..4de83bfb9c8 100644 --- a/test/Analysis/diagnostics/undef-value-param.m +++ b/test/Analysis/diagnostics/undef-value-param.m @@ -1,5 +1,6 @@ // RUN: %clang_cc1 -analyze -analyzer-checker=core,osx -analyzer-output=text -verify %s -// RUN: %clang_cc1 -analyze -analyzer-checker=core,osx -analyzer-output=plist-multi-file %s -o - | FileCheck %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core,osx -analyzer-output=plist-multi-file %s -o %t.plist +// RUN: FileCheck --input-file=%t.plist %s typedef signed char BOOL; @protocol NSObject - (BOOL)isEqual:(id)object; @end @@ -86,12 +87,12 @@ static void CreateRefUndef(SCDynamicStoreRef *storeRef, unsigned x) { // CHECK-NEXT: start // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line34 +// CHECK-NEXT: line35 // CHECK-NEXT: col5 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line34 +// CHECK-NEXT: line35 // CHECK-NEXT: col21 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -99,12 +100,12 @@ static void CreateRefUndef(SCDynamicStoreRef *storeRef, unsigned x) { // CHECK-NEXT: end // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line35 +// CHECK-NEXT: line36 // CHECK-NEXT: col5 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line35 +// CHECK-NEXT: line36 // CHECK-NEXT: col13 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -116,7 +117,7 @@ static void CreateRefUndef(SCDynamicStoreRef *storeRef, unsigned x) { // CHECK-NEXT: kindevent // CHECK-NEXT: location // CHECK-NEXT: -// CHECK-NEXT: line35 +// CHECK-NEXT: line36 // CHECK-NEXT: col5 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -124,12 +125,12 @@ static void CreateRefUndef(SCDynamicStoreRef *storeRef, unsigned x) { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line35 +// CHECK-NEXT: line36 // CHECK-NEXT: col5 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line35 +// CHECK-NEXT: line36 // CHECK-NEXT: col27 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -145,7 +146,7 @@ static void CreateRefUndef(SCDynamicStoreRef *storeRef, unsigned x) { // CHECK-NEXT: kindevent // CHECK-NEXT: location // CHECK-NEXT: -// CHECK-NEXT: line52 +// CHECK-NEXT: line53 // CHECK-NEXT: col1 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -163,12 +164,12 @@ static void CreateRefUndef(SCDynamicStoreRef *storeRef, unsigned x) { // CHECK-NEXT: start // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line52 +// CHECK-NEXT: line53 // CHECK-NEXT: col1 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line52 +// CHECK-NEXT: line53 // CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -176,12 +177,12 @@ static void CreateRefUndef(SCDynamicStoreRef *storeRef, unsigned x) { // CHECK-NEXT: end // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line53 +// CHECK-NEXT: line54 // CHECK-NEXT: col5 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line53 +// CHECK-NEXT: line54 // CHECK-NEXT: col12 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -197,12 +198,12 @@ static void CreateRefUndef(SCDynamicStoreRef *storeRef, unsigned x) { // CHECK-NEXT: start // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line53 +// CHECK-NEXT: line54 // CHECK-NEXT: col5 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line53 +// CHECK-NEXT: line54 // CHECK-NEXT: col12 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -210,12 +211,12 @@ static void CreateRefUndef(SCDynamicStoreRef *storeRef, unsigned x) { // CHECK-NEXT: end // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line55 +// CHECK-NEXT: line56 // CHECK-NEXT: col5 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line55 +// CHECK-NEXT: line56 // CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -231,12 +232,12 @@ static void CreateRefUndef(SCDynamicStoreRef *storeRef, unsigned x) { // CHECK-NEXT: start // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line55 +// CHECK-NEXT: line56 // CHECK-NEXT: col5 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line55 +// CHECK-NEXT: line56 // CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -244,12 +245,12 @@ static void CreateRefUndef(SCDynamicStoreRef *storeRef, unsigned x) { // CHECK-NEXT: end // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line55 +// CHECK-NEXT: line56 // CHECK-NEXT: col9 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line55 +// CHECK-NEXT: line56 // CHECK-NEXT: col11 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -261,7 +262,7 @@ static void CreateRefUndef(SCDynamicStoreRef *storeRef, unsigned x) { // CHECK-NEXT: kindevent // CHECK-NEXT: location // CHECK-NEXT: -// CHECK-NEXT: line55 +// CHECK-NEXT: line56 // CHECK-NEXT: col9 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -269,12 +270,12 @@ static void CreateRefUndef(SCDynamicStoreRef *storeRef, unsigned x) { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line55 +// CHECK-NEXT: line56 // CHECK-NEXT: col9 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line55 +// CHECK-NEXT: line56 // CHECK-NEXT: col11 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -294,12 +295,12 @@ static void CreateRefUndef(SCDynamicStoreRef *storeRef, unsigned x) { // CHECK-NEXT: start // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line55 +// CHECK-NEXT: line56 // CHECK-NEXT: col9 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line55 +// CHECK-NEXT: line56 // CHECK-NEXT: col11 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -307,12 +308,12 @@ static void CreateRefUndef(SCDynamicStoreRef *storeRef, unsigned x) { // CHECK-NEXT: end // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line58 +// CHECK-NEXT: line59 // CHECK-NEXT: col9 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line58 +// CHECK-NEXT: line59 // CHECK-NEXT: col17 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -328,12 +329,12 @@ static void CreateRefUndef(SCDynamicStoreRef *storeRef, unsigned x) { // CHECK-NEXT: start // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line58 +// CHECK-NEXT: line59 // CHECK-NEXT: col9 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line58 +// CHECK-NEXT: line59 // CHECK-NEXT: col17 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -341,12 +342,12 @@ static void CreateRefUndef(SCDynamicStoreRef *storeRef, unsigned x) { // CHECK-NEXT: end // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line59 +// CHECK-NEXT: line60 // CHECK-NEXT: col9 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line59 +// CHECK-NEXT: line60 // CHECK-NEXT: col11 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -358,7 +359,7 @@ static void CreateRefUndef(SCDynamicStoreRef *storeRef, unsigned x) { // CHECK-NEXT: kindevent // CHECK-NEXT: location // CHECK-NEXT: -// CHECK-NEXT: line59 +// CHECK-NEXT: line60 // CHECK-NEXT: col9 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -366,12 +367,12 @@ static void CreateRefUndef(SCDynamicStoreRef *storeRef, unsigned x) { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line59 +// CHECK-NEXT: line60 // CHECK-NEXT: col9 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line59 +// CHECK-NEXT: line60 // CHECK-NEXT: col15 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -391,12 +392,12 @@ static void CreateRefUndef(SCDynamicStoreRef *storeRef, unsigned x) { // CHECK-NEXT: start // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line59 +// CHECK-NEXT: line60 // CHECK-NEXT: col9 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line59 +// CHECK-NEXT: line60 // CHECK-NEXT: col11 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -404,12 +405,12 @@ static void CreateRefUndef(SCDynamicStoreRef *storeRef, unsigned x) { // CHECK-NEXT: end // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line61 +// CHECK-NEXT: line62 // CHECK-NEXT: col5 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line61 +// CHECK-NEXT: line62 // CHECK-NEXT: col5 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -421,7 +422,7 @@ static void CreateRefUndef(SCDynamicStoreRef *storeRef, unsigned x) { // CHECK-NEXT: kindevent // CHECK-NEXT: location // CHECK-NEXT: -// CHECK-NEXT: line61 +// CHECK-NEXT: line62 // CHECK-NEXT: col5 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -429,12 +430,12 @@ static void CreateRefUndef(SCDynamicStoreRef *storeRef, unsigned x) { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line61 +// CHECK-NEXT: line62 // CHECK-NEXT: col5 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line61 +// CHECK-NEXT: line62 // CHECK-NEXT: col19 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -450,7 +451,7 @@ static void CreateRefUndef(SCDynamicStoreRef *storeRef, unsigned x) { // CHECK-NEXT: kindevent // CHECK-NEXT: location // CHECK-NEXT: -// CHECK-NEXT: line35 +// CHECK-NEXT: line36 // CHECK-NEXT: col5 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -458,18 +459,18 @@ static void CreateRefUndef(SCDynamicStoreRef *storeRef, unsigned x) { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line35 +// CHECK-NEXT: line36 // CHECK-NEXT: col5 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line35 +// CHECK-NEXT: line36 // CHECK-NEXT: col27 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: depth1 +// CHECK-NEXT: depth0 // CHECK-NEXT: extended_message // CHECK-NEXT: Returning from 'CreateRef' // CHECK-NEXT: message @@ -483,12 +484,12 @@ static void CreateRefUndef(SCDynamicStoreRef *storeRef, unsigned x) { // CHECK-NEXT: start // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line35 +// CHECK-NEXT: line36 // CHECK-NEXT: col5 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line35 +// CHECK-NEXT: line36 // CHECK-NEXT: col13 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -496,12 +497,12 @@ static void CreateRefUndef(SCDynamicStoreRef *storeRef, unsigned x) { // CHECK-NEXT: end // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line38 +// CHECK-NEXT: line39 // CHECK-NEXT: col5 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line38 +// CHECK-NEXT: line39 // CHECK-NEXT: col13 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -513,7 +514,7 @@ static void CreateRefUndef(SCDynamicStoreRef *storeRef, unsigned x) { // CHECK-NEXT: kindevent // CHECK-NEXT: location // CHECK-NEXT: -// CHECK-NEXT: line38 +// CHECK-NEXT: line39 // CHECK-NEXT: col5 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -521,12 +522,12 @@ static void CreateRefUndef(SCDynamicStoreRef *storeRef, unsigned x) { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line38 +// CHECK-NEXT: line39 // CHECK-NEXT: col15 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line38 +// CHECK-NEXT: line39 // CHECK-NEXT: col22 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -547,7 +548,7 @@ static void CreateRefUndef(SCDynamicStoreRef *storeRef, unsigned x) { // CHECK-NEXT: issue_hash5 // CHECK-NEXT: location // CHECK-NEXT: -// CHECK-NEXT: line38 +// CHECK-NEXT: line39 // CHECK-NEXT: col5 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -559,7 +560,7 @@ static void CreateRefUndef(SCDynamicStoreRef *storeRef, unsigned x) { // CHECK-NEXT: kindevent // CHECK-NEXT: location // CHECK-NEXT: -// CHECK-NEXT: line43 +// CHECK-NEXT: line44 // CHECK-NEXT: col5 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -567,12 +568,12 @@ static void CreateRefUndef(SCDynamicStoreRef *storeRef, unsigned x) { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line43 +// CHECK-NEXT: line44 // CHECK-NEXT: col5 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line43 +// CHECK-NEXT: line44 // CHECK-NEXT: col30 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -592,12 +593,12 @@ static void CreateRefUndef(SCDynamicStoreRef *storeRef, unsigned x) { // CHECK-NEXT: start // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line43 +// CHECK-NEXT: line44 // CHECK-NEXT: col5 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line43 +// CHECK-NEXT: line44 // CHECK-NEXT: col21 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -605,12 +606,12 @@ static void CreateRefUndef(SCDynamicStoreRef *storeRef, unsigned x) { // CHECK-NEXT: end // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line44 +// CHECK-NEXT: line45 // CHECK-NEXT: col5 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line44 +// CHECK-NEXT: line45 // CHECK-NEXT: col18 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -622,7 +623,7 @@ static void CreateRefUndef(SCDynamicStoreRef *storeRef, unsigned x) { // CHECK-NEXT: kindevent // CHECK-NEXT: location // CHECK-NEXT: -// CHECK-NEXT: line44 +// CHECK-NEXT: line45 // CHECK-NEXT: col5 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -630,12 +631,12 @@ static void CreateRefUndef(SCDynamicStoreRef *storeRef, unsigned x) { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line44 +// CHECK-NEXT: line45 // CHECK-NEXT: col5 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line44 +// CHECK-NEXT: line45 // CHECK-NEXT: col32 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -651,7 +652,7 @@ static void CreateRefUndef(SCDynamicStoreRef *storeRef, unsigned x) { // CHECK-NEXT: kindevent // CHECK-NEXT: location // CHECK-NEXT: -// CHECK-NEXT: line64 +// CHECK-NEXT: line65 // CHECK-NEXT: col1 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -669,12 +670,12 @@ static void CreateRefUndef(SCDynamicStoreRef *storeRef, unsigned x) { // CHECK-NEXT: start // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line64 +// CHECK-NEXT: line65 // CHECK-NEXT: col1 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line64 +// CHECK-NEXT: line65 // CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -682,12 +683,12 @@ static void CreateRefUndef(SCDynamicStoreRef *storeRef, unsigned x) { // CHECK-NEXT: end // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line65 +// CHECK-NEXT: line66 // CHECK-NEXT: col3 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line65 +// CHECK-NEXT: line66 // CHECK-NEXT: col10 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -703,12 +704,12 @@ static void CreateRefUndef(SCDynamicStoreRef *storeRef, unsigned x) { // CHECK-NEXT: start // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line65 +// CHECK-NEXT: line66 // CHECK-NEXT: col3 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line65 +// CHECK-NEXT: line66 // CHECK-NEXT: col10 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -716,12 +717,12 @@ static void CreateRefUndef(SCDynamicStoreRef *storeRef, unsigned x) { // CHECK-NEXT: end // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line67 +// CHECK-NEXT: line68 // CHECK-NEXT: col3 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line67 +// CHECK-NEXT: line68 // CHECK-NEXT: col4 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -737,12 +738,12 @@ static void CreateRefUndef(SCDynamicStoreRef *storeRef, unsigned x) { // CHECK-NEXT: start // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line67 +// CHECK-NEXT: line68 // CHECK-NEXT: col3 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line67 +// CHECK-NEXT: line68 // CHECK-NEXT: col4 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -750,12 +751,12 @@ static void CreateRefUndef(SCDynamicStoreRef *storeRef, unsigned x) { // CHECK-NEXT: end // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line67 +// CHECK-NEXT: line68 // CHECK-NEXT: col7 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line67 +// CHECK-NEXT: line68 // CHECK-NEXT: col9 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -767,7 +768,7 @@ static void CreateRefUndef(SCDynamicStoreRef *storeRef, unsigned x) { // CHECK-NEXT: kindevent // CHECK-NEXT: location // CHECK-NEXT: -// CHECK-NEXT: line67 +// CHECK-NEXT: line68 // CHECK-NEXT: col7 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -775,12 +776,12 @@ static void CreateRefUndef(SCDynamicStoreRef *storeRef, unsigned x) { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line67 +// CHECK-NEXT: line68 // CHECK-NEXT: col7 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line67 +// CHECK-NEXT: line68 // CHECK-NEXT: col9 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -800,12 +801,12 @@ static void CreateRefUndef(SCDynamicStoreRef *storeRef, unsigned x) { // CHECK-NEXT: start // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line67 +// CHECK-NEXT: line68 // CHECK-NEXT: col7 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line67 +// CHECK-NEXT: line68 // CHECK-NEXT: col9 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -813,12 +814,12 @@ static void CreateRefUndef(SCDynamicStoreRef *storeRef, unsigned x) { // CHECK-NEXT: end // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line70 +// CHECK-NEXT: line71 // CHECK-NEXT: col5 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line70 +// CHECK-NEXT: line71 // CHECK-NEXT: col13 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -830,7 +831,7 @@ static void CreateRefUndef(SCDynamicStoreRef *storeRef, unsigned x) { // CHECK-NEXT: kindevent // CHECK-NEXT: location // CHECK-NEXT: -// CHECK-NEXT: line44 +// CHECK-NEXT: line45 // CHECK-NEXT: col5 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -838,18 +839,18 @@ static void CreateRefUndef(SCDynamicStoreRef *storeRef, unsigned x) { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line44 +// CHECK-NEXT: line45 // CHECK-NEXT: col5 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line44 +// CHECK-NEXT: line45 // CHECK-NEXT: col32 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: depth1 +// CHECK-NEXT: depth0 // CHECK-NEXT: extended_message // CHECK-NEXT: Returning from 'CreateRefUndef' // CHECK-NEXT: message @@ -863,12 +864,12 @@ static void CreateRefUndef(SCDynamicStoreRef *storeRef, unsigned x) { // CHECK-NEXT: start // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line44 +// CHECK-NEXT: line45 // CHECK-NEXT: col5 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line44 +// CHECK-NEXT: line45 // CHECK-NEXT: col18 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -876,12 +877,12 @@ static void CreateRefUndef(SCDynamicStoreRef *storeRef, unsigned x) { // CHECK-NEXT: end // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line47 +// CHECK-NEXT: line48 // CHECK-NEXT: col5 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line47 +// CHECK-NEXT: line48 // CHECK-NEXT: col13 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -893,7 +894,7 @@ static void CreateRefUndef(SCDynamicStoreRef *storeRef, unsigned x) { // CHECK-NEXT: kindevent // CHECK-NEXT: location // CHECK-NEXT: -// CHECK-NEXT: line47 +// CHECK-NEXT: line48 // CHECK-NEXT: col5 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -901,12 +902,12 @@ static void CreateRefUndef(SCDynamicStoreRef *storeRef, unsigned x) { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line47 +// CHECK-NEXT: line48 // CHECK-NEXT: col15 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line47 +// CHECK-NEXT: line48 // CHECK-NEXT: col22 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -927,7 +928,7 @@ static void CreateRefUndef(SCDynamicStoreRef *storeRef, unsigned x) { // CHECK-NEXT: issue_hash5 // CHECK-NEXT: location // CHECK-NEXT: -// CHECK-NEXT: line47 +// CHECK-NEXT: line48 // CHECK-NEXT: col5 // CHECK-NEXT: file0 // CHECK-NEXT: diff --git a/test/Analysis/enum.cpp b/test/Analysis/enum.cpp new file mode 100644 index 00000000000..571fa7ba228 --- /dev/null +++ b/test/Analysis/enum.cpp @@ -0,0 +1,26 @@ +// RUN: %clang_cc1 -analyze -std=c++11 -analyzer-checker=debug.ExprInspection %s + +void clang_analyzer_eval(bool); + +enum class Foo { + Zero +}; + +bool pr15703(int x) { + return Foo::Zero == (Foo)x; // don't crash +} + +void testCasting(int i) { + Foo f = static_cast(i); + int j = static_cast(f); + if (i == 0) + { + clang_analyzer_eval(f == Foo::Zero); // expected-warning{{TRUE}} + clang_analyzer_eval(j == 0); // expected-warning{{TRUE}} + } + else + { + clang_analyzer_eval(f == Foo::Zero); // expected-warning{{FALSE}} + clang_analyzer_eval(j == 0); // expected-warning{{FALSE}} + } +} diff --git a/test/Analysis/global-region-invalidation.c b/test/Analysis/global-region-invalidation.c index 77de9dd3264..bff22012c0c 100644 --- a/test/Analysis/global-region-invalidation.c +++ b/test/Analysis/global-region-invalidation.c @@ -44,7 +44,10 @@ int testErrnoSystem() { fscanf(stdin, "%d", &i); // errno gets invalidated here. return 5 / errno; // no-warning } - return 0; + + errno = 0; + fscanf(stdin, "%d", &i); // errno gets invalidated here. + return 5 / errno; // no-warning } // Test that errno gets invalidated by internal calls. diff --git a/test/Analysis/global_region_invalidation.mm b/test/Analysis/global_region_invalidation.mm index f853470a5fd..2369c09d22f 100644 --- a/test/Analysis/global_region_invalidation.mm +++ b/test/Analysis/global_region_invalidation.mm @@ -2,6 +2,8 @@ void clang_analyzer_eval(int); +#include "Inputs/system-header-simulator.h" + void use(int); id foo(int x) { if (x) @@ -19,27 +21,168 @@ void testGlobalRef() { } extern int globalInt; +struct IntWrapper { + int value; +}; +extern struct IntWrapper globalStruct; extern void invalidateGlobals(); void testGlobalInvalidation() { + clang_analyzer_eval(globalInt == 42); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(globalStruct.value == 43); // expected-warning{{UNKNOWN}} + if (globalInt != 42) return; + if (globalStruct.value != 43) + return; clang_analyzer_eval(globalInt == 42); // expected-warning{{TRUE}} + clang_analyzer_eval(globalStruct.value == 43); // expected-warning{{TRUE}} invalidateGlobals(); clang_analyzer_eval(globalInt == 42); // expected-warning{{UNKNOWN}} -} + clang_analyzer_eval(globalStruct.value == 43); // expected-warning{{UNKNOWN}} + // Repeat to make sure we don't get the /same/ new symbolic values. + if (globalInt != 42) + return; + if (globalStruct.value != 43) + return; + clang_analyzer_eval(globalInt == 42); // expected-warning{{TRUE}} + clang_analyzer_eval(globalStruct.value == 43); // expected-warning{{TRUE}} -//--------------------------------- -// False negatives -//--------------------------------- + invalidateGlobals(); + clang_analyzer_eval(globalInt == 42); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(globalStruct.value == 43); // expected-warning{{UNKNOWN}} +} void testGlobalInvalidationWithDirectBinding() { + clang_analyzer_eval(globalInt == 42); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(globalStruct.value == 43); // expected-warning{{UNKNOWN}} + globalInt = 42; + globalStruct.value = 43; clang_analyzer_eval(globalInt == 42); // expected-warning{{TRUE}} + clang_analyzer_eval(globalStruct.value == 43); // expected-warning{{TRUE}} invalidateGlobals(); - // FIXME: Should be UNKNOWN. - clang_analyzer_eval(globalInt == 42); // expected-warning{{TRUE}} + clang_analyzer_eval(globalInt == 42); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(globalStruct.value == 43); // expected-warning{{UNKNOWN}} +} + +void testStaticLocals(void) { + static int i; + int tmp; + + extern int someSymbolicValue(); + i = someSymbolicValue(); + + if (i == 5) { + clang_analyzer_eval(i == 5); // expected-warning{{TRUE}} + scanf("%d", &tmp); + clang_analyzer_eval(i == 5); // expected-warning{{TRUE}} + invalidateGlobals(); + clang_analyzer_eval(i == 5); // expected-warning{{TRUE}} + } + + i = 6; + clang_analyzer_eval(i == 6); // expected-warning{{TRUE}} + scanf("%d", &tmp); + clang_analyzer_eval(i == 6); // expected-warning{{TRUE}} + invalidateGlobals(); + clang_analyzer_eval(i == 6); // expected-warning{{TRUE}} + + i = someSymbolicValue(); + if (i == 7) { + clang_analyzer_eval(i == 7); // expected-warning{{TRUE}} + scanf("%d", &i); + clang_analyzer_eval(i == 7); // expected-warning{{UNKNOWN}} + } + + i = 8; + clang_analyzer_eval(i == 8); // expected-warning{{TRUE}} + scanf("%d", &i); + clang_analyzer_eval(i == 8); // expected-warning{{UNKNOWN}} +} + +void testNonSystemGlobals(void) { + extern int i; + int tmp; + + if (i == 5) { + clang_analyzer_eval(i == 5); // expected-warning{{TRUE}} + scanf("%d", &tmp); + clang_analyzer_eval(i == 5); // expected-warning{{TRUE}} + invalidateGlobals(); + clang_analyzer_eval(i == 5); // expected-warning{{UNKNOWN}} + } + + i = 6; + clang_analyzer_eval(i == 6); // expected-warning{{TRUE}} + scanf("%d", &tmp); + clang_analyzer_eval(i == 6); // expected-warning{{TRUE}} + invalidateGlobals(); + clang_analyzer_eval(i == 6); // expected-warning{{UNKNOWN}} + + if (i == 7) { + clang_analyzer_eval(i == 7); // expected-warning{{TRUE}} + scanf("%d", &i); + clang_analyzer_eval(i == 7); // expected-warning{{UNKNOWN}} + } + + i = 8; + clang_analyzer_eval(i == 8); // expected-warning{{TRUE}} + scanf("%d", &i); + clang_analyzer_eval(i == 8); // expected-warning{{UNKNOWN}} +} + +void testWrappedGlobals(void) { + extern char c; + SomeStruct s; + + if (c == 'C') { + s.p = &c; + clang_analyzer_eval(c == 'C'); // expected-warning{{TRUE}} + fakeSystemHeaderCall(0); + clang_analyzer_eval(c == 'C'); // expected-warning{{TRUE}} + fakeSystemHeaderCall(&s); + clang_analyzer_eval(c == 'C'); // expected-warning{{UNKNOWN}} + } + + c = 'c'; + s.p = &c; + clang_analyzer_eval(c == 'c'); // expected-warning{{TRUE}} + fakeSystemHeaderCall(0); + clang_analyzer_eval(c == 'c'); // expected-warning{{TRUE}} + fakeSystemHeaderCall(&s); + clang_analyzer_eval(c == 'c'); // expected-warning{{UNKNOWN}} + + if (c == 'C') { + s.p = &c; + clang_analyzer_eval(c == 'C'); // expected-warning{{TRUE}} + fakeSystemHeaderCall(0); + clang_analyzer_eval(c == 'C'); // expected-warning{{TRUE}} + fakeSystemHeaderCall(&s); + clang_analyzer_eval(c == 'C'); // expected-warning{{UNKNOWN}} + } +} + +void testWrappedStaticsViaGlobal(void) { + static char c; + extern SomeStruct s; + + extern char getSomeChar(); + c = getSomeChar(); + + if (c == 'C') { + s.p = &c; + clang_analyzer_eval(c == 'C'); // expected-warning{{TRUE}} + invalidateGlobals(); + clang_analyzer_eval(c == 'C'); // expected-warning{{UNKNOWN}} + } + + c = 'c'; + s.p = &c; + clang_analyzer_eval(c == 'c'); // expected-warning{{TRUE}} + invalidateGlobals(); + clang_analyzer_eval(c == 'c'); // expected-warning{{UNKNOWN}} } diff --git a/test/Analysis/inline-plist.c b/test/Analysis/inline-plist.c index a2dd98a6d06..dcdd23f74bb 100644 --- a/test/Analysis/inline-plist.c +++ b/test/Analysis/inline-plist.c @@ -244,12 +244,12 @@ void test_block_arg() { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line18 -// CHECK-NEXT: col10 +// CHECK-NEXT: col11 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line18 -// CHECK-NEXT: col10 +// CHECK-NEXT: col11 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -261,7 +261,7 @@ void test_block_arg() { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line18 -// CHECK-NEXT: col10 +// CHECK-NEXT: col11 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: ranges @@ -295,7 +295,7 @@ void test_block_arg() { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line18 -// CHECK-NEXT: col10 +// CHECK-NEXT: col11 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -443,11 +443,45 @@ void test_block_arg() { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line23 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line23 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line23 +// CHECK-NEXT: col6 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line23 +// CHECK-NEXT: col6 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: // CHECK-NEXT: kindevent // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line23 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: ranges @@ -481,7 +515,7 @@ void test_block_arg() { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line23 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -760,11 +794,45 @@ void test_block_arg() { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line33 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line33 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line33 +// CHECK-NEXT: col6 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line33 +// CHECK-NEXT: col6 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: // CHECK-NEXT: kindevent // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line33 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: ranges @@ -798,7 +866,7 @@ void test_block_arg() { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line33 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -946,11 +1014,45 @@ void test_block_arg() { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line60 +// CHECK-NEXT: col5 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line60 +// CHECK-NEXT: col5 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line60 +// CHECK-NEXT: col8 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line60 +// CHECK-NEXT: col8 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: // CHECK-NEXT: kindevent // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line60 -// CHECK-NEXT: col5 +// CHECK-NEXT: col8 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: ranges @@ -981,7 +1083,7 @@ void test_block_arg() { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line60 -// CHECK-NEXT: col5 +// CHECK-NEXT: col8 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -1214,7 +1316,7 @@ void test_block_arg() { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: depth1 +// CHECK-NEXT: depth0 // CHECK-NEXT: extended_message // CHECK-NEXT: Returning to caller // CHECK-NEXT: message @@ -1229,40 +1331,6 @@ void test_block_arg() { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line66 -// CHECK-NEXT: col3 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line66 -// CHECK-NEXT: col5 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: end -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line66 -// CHECK-NEXT: col12 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line66 -// CHECK-NEXT: col12 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: kindcontrol -// CHECK-NEXT: edges -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: start -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line66 // CHECK-NEXT: col12 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -1339,12 +1407,12 @@ void test_block_arg() { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line70 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line70 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -1356,7 +1424,7 @@ void test_block_arg() { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line70 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: ranges @@ -1390,7 +1458,7 @@ void test_block_arg() { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line70 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -1560,7 +1628,7 @@ void test_block_arg() { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: depth1 +// CHECK-NEXT: depth0 // CHECK-NEXT: extended_message // CHECK-NEXT: Returning to caller // CHECK-NEXT: message @@ -1588,12 +1656,12 @@ void test_block_arg() { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line78 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line78 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -1605,7 +1673,7 @@ void test_block_arg() { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line78 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: ranges @@ -1639,7 +1707,7 @@ void test_block_arg() { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line78 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -1809,7 +1877,7 @@ void test_block_arg() { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: depth1 +// CHECK-NEXT: depth0 // CHECK-NEXT: extended_message // CHECK-NEXT: Returning to caller // CHECK-NEXT: message @@ -1837,12 +1905,12 @@ void test_block_arg() { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line86 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line86 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -1854,7 +1922,7 @@ void test_block_arg() { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line86 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: ranges @@ -1888,7 +1956,7 @@ void test_block_arg() { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line86 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: diff --git a/test/Analysis/inline-unique-reports.c b/test/Analysis/inline-unique-reports.c index 9a8cd7f495a..5c42135b049 100644 --- a/test/Analysis/inline-unique-reports.c +++ b/test/Analysis/inline-unique-reports.c @@ -15,172 +15,290 @@ void test_bug_2() { bug(p); } -// CHECK: -// CHECK: -// CHECK: -// CHECK: files -// CHECK: -// CHECK: -// CHECK: diagnostics -// CHECK: -// CHECK: -// CHECK: path -// CHECK: -// CHECK: -// CHECK: kindcontrol -// CHECK: edges -// CHECK: -// CHECK: -// CHECK: start -// CHECK: -// CHECK: -// CHECK: line14 -// CHECK: col3 -// CHECK: file0 -// CHECK: -// CHECK: -// CHECK: line14 -// CHECK: col5 -// CHECK: file0 -// CHECK: -// CHECK: -// CHECK: end -// CHECK: -// CHECK: -// CHECK: line15 -// CHECK: col3 -// CHECK: file0 -// CHECK: -// CHECK: -// CHECK: line15 -// CHECK: col5 -// CHECK: file0 -// CHECK: -// CHECK: -// CHECK: -// CHECK: -// CHECK: -// CHECK: -// CHECK: kindevent -// CHECK: location -// CHECK: -// CHECK: line15 -// CHECK: col3 -// CHECK: file0 -// CHECK: -// CHECK: ranges -// CHECK: -// CHECK: -// CHECK: -// CHECK: line15 -// CHECK: col3 -// CHECK: file0 -// CHECK: -// CHECK: -// CHECK: line15 -// CHECK: col8 -// CHECK: file0 -// CHECK: -// CHECK: -// CHECK: -// CHECK: depth0 -// CHECK: extended_message -// CHECK: Calling 'bug' -// CHECK: message -// CHECK: Calling 'bug' -// CHECK: -// CHECK: -// CHECK: kindevent -// CHECK: location -// CHECK: -// CHECK: line4 -// CHECK: col1 -// CHECK: file0 -// CHECK: -// CHECK: depth1 -// CHECK: extended_message -// CHECK: Entered call from 'test_bug_2' -// CHECK: message -// CHECK: Entered call from 'test_bug_2' -// CHECK: -// CHECK: -// CHECK: kindcontrol -// CHECK: edges -// CHECK: -// CHECK: -// CHECK: start -// CHECK: -// CHECK: -// CHECK: line4 -// CHECK: col1 -// CHECK: file0 -// CHECK: -// CHECK: -// CHECK: line4 -// CHECK: col6 -// CHECK: file0 -// CHECK: -// CHECK: -// CHECK: end -// CHECK: -// CHECK: -// CHECK: line5 -// CHECK: col3 -// CHECK: file0 -// CHECK: -// CHECK: -// CHECK: line5 -// CHECK: col3 -// CHECK: file0 -// CHECK: -// CHECK: -// CHECK: -// CHECK: -// CHECK: -// CHECK: -// CHECK: kindevent -// CHECK: location -// CHECK: -// CHECK: line5 -// CHECK: col3 -// CHECK: file0 -// CHECK: -// CHECK: ranges -// CHECK: -// CHECK: -// CHECK: -// CHECK: line5 -// CHECK: col4 -// CHECK: file0 -// CHECK: -// CHECK: -// CHECK: line5 -// CHECK: col4 -// CHECK: file0 -// CHECK: -// CHECK: -// CHECK: -// CHECK: depth1 -// CHECK: extended_message -// CHECK: Dereference of null pointer (loaded from variable 'p') -// CHECK: message -// CHECK: Dereference of null pointer (loaded from variable 'p') -// CHECK: -// CHECK: -// CHECK: descriptionDereference of null pointer (loaded from variable 'p') -// CHECK: categoryLogic error -// CHECK: typeDereference of null pointer -// CHECK: issue_context_kindfunction -// CHECK: issue_contextbug -// CHECK: issue_hash1 -// CHECK: location -// CHECK: -// CHECK: line5 -// CHECK: col3 -// CHECK: file0 -// CHECK: -// CHECK: -// CHECK: -// CHECK: -// CHECK: +// CHECK: diagnostics +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: path +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line14 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line14 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line14 +// CHECK-NEXT: col8 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: depth0 +// CHECK-NEXT: extended_message +// CHECK-NEXT: 'p' initialized to a null pointer value +// CHECK-NEXT: message +// CHECK-NEXT: 'p' initialized to a null pointer value +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line14 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line14 +// CHECK-NEXT: col5 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line15 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line15 +// CHECK-NEXT: col5 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line15 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line15 +// CHECK-NEXT: col5 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line15 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line15 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line15 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line15 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line15 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: depth0 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Passing null pointer value via 1st parameter 'p' +// CHECK-NEXT: message +// CHECK-NEXT: Passing null pointer value via 1st parameter 'p' +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line15 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line15 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line15 +// CHECK-NEXT: col8 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: depth0 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Calling 'bug' +// CHECK-NEXT: message +// CHECK-NEXT: Calling 'bug' +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line4 +// CHECK-NEXT: col1 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: depth1 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Entered call from 'test_bug_2' +// CHECK-NEXT: message +// CHECK-NEXT: Entered call from 'test_bug_2' +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line4 +// CHECK-NEXT: col1 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line4 +// CHECK-NEXT: col6 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line5 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line5 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line5 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line5 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line5 +// CHECK-NEXT: col6 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line5 +// CHECK-NEXT: col6 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line5 +// CHECK-NEXT: col6 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line5 +// CHECK-NEXT: col4 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line5 +// CHECK-NEXT: col4 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: depth1 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Dereference of null pointer (loaded from variable 'p') +// CHECK-NEXT: message +// CHECK-NEXT: Dereference of null pointer (loaded from variable 'p') +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: descriptionDereference of null pointer (loaded from variable 'p') +// CHECK-NEXT: categoryLogic error +// CHECK-NEXT: typeDereference of null pointer +// CHECK-NEXT: issue_context_kindfunction +// CHECK-NEXT: issue_contextbug +// CHECK-NEXT: issue_hash1 +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line5 +// CHECK-NEXT: col6 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: diff --git a/test/Analysis/inline.cpp b/test/Analysis/inline.cpp index a16fa00d12b..909e18017b3 100644 --- a/test/Analysis/inline.cpp +++ b/test/Analysis/inline.cpp @@ -262,12 +262,33 @@ namespace DefaultArgs { } int defaultReference(const int &input = 42) { - return input; + return -input; + } + int defaultReferenceZero(const int &input = 0) { + return -input; } void testReference() { - clang_analyzer_eval(defaultReference(1) == 1); // expected-warning{{TRUE}} - clang_analyzer_eval(defaultReference() == 42); // expected-warning{{TRUE}} + clang_analyzer_eval(defaultReference(1) == -1); // expected-warning{{TRUE}} + clang_analyzer_eval(defaultReference() == -42); // expected-warning{{TRUE}} + + clang_analyzer_eval(defaultReferenceZero(1) == -1); // expected-warning{{TRUE}} + clang_analyzer_eval(defaultReferenceZero() == 0); // expected-warning{{TRUE}} +} + + double defaultFloatReference(const double &i = 42) { + return -i; + } + double defaultFloatReferenceZero(const double &i = 0) { + return -i; + } + + void testFloatReference() { + clang_analyzer_eval(defaultFloatReference(1) == -1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(defaultFloatReference() == -42); // expected-warning{{UNKNOWN}} + + clang_analyzer_eval(defaultFloatReferenceZero(1) == -1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(defaultFloatReferenceZero() == 0); // expected-warning{{UNKNOWN}} } } @@ -351,9 +372,7 @@ namespace VirtualWithSisterCasts { void testCastViaNew(B *b) { Grandchild *g = new (b) Grandchild(); - // FIXME: We actually now have perfect type info because of 'new'. - // This should be TRUE. - clang_analyzer_eval(g->foo() == 42); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(g->foo() == 42); // expected-warning{{TRUE}} g->x = 42; clang_analyzer_eval(g->x == 42); // expected-warning{{TRUE}} diff --git a/test/Analysis/inlining/containers.cpp b/test/Analysis/inlining/containers.cpp index 4500dff6dcf..73b2957ad6d 100644 --- a/test/Analysis/inlining/containers.cpp +++ b/test/Analysis/inlining/containers.cpp @@ -78,7 +78,9 @@ void testWrappers(BeginOnlySet &w1, IteratorStructOnlySet &w2, } -#else +#else // HEADER + +#include "../Inputs/system-header-simulator-cxx.h" class MySet { int *storage; @@ -152,8 +154,13 @@ class BeginOnlySet { public: struct IterImpl { MySet::iterator impl; + typedef std::forward_iterator_tag iterator_category; + IterImpl(MySet::iterator i) : impl(i) { - clang_analyzer_checkInlined(true); // expected-warning {{TRUE}} + clang_analyzer_checkInlined(true); +#if INLINE + // expected-warning@-2 {{TRUE}} +#endif } }; @@ -231,4 +238,4 @@ class IteratorStructOnlySet { } }; -#endif +#endif // HEADER diff --git a/test/Analysis/inlining/dyn-dispatch-bifurcate.cpp b/test/Analysis/inlining/dyn-dispatch-bifurcate.cpp index 890e5640cef..d219446fc96 100644 --- a/test/Analysis/inlining/dyn-dispatch-bifurcate.cpp +++ b/test/Analysis/inlining/dyn-dispatch-bifurcate.cpp @@ -16,6 +16,11 @@ void testKnown() { clang_analyzer_eval(a.get() == 0); // expected-warning{{TRUE}} } +void testNew() { + A *a = new A(); + clang_analyzer_eval(a->get() == 0); // expected-warning{{TRUE}} +} + namespace ReinterpretDisruptsDynamicTypeInfo { class Parent {}; diff --git a/test/Analysis/inlining/eager-reclamation-path-notes.c b/test/Analysis/inlining/eager-reclamation-path-notes.c index f3e7376156b..65613658fcb 100644 --- a/test/Analysis/inlining/eager-reclamation-path-notes.c +++ b/test/Analysis/inlining/eager-reclamation-path-notes.c @@ -120,40 +120,6 @@ void testChainedCalls() { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line21 -// CHECK-NEXT: col3 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line21 -// CHECK-NEXT: col5 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: end -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line21 -// CHECK-NEXT: col10 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line21 -// CHECK-NEXT: col16 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: kindcontrol -// CHECK-NEXT: edges -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: start -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line21 // CHECK-NEXT: col10 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -286,11 +252,45 @@ void testChainedCalls() { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line6 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line6 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line6 +// CHECK-NEXT: col8 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line6 +// CHECK-NEXT: col8 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: // CHECK-NEXT: kindevent // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line6 -// CHECK-NEXT: col3 +// CHECK-NEXT: col8 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: ranges @@ -324,7 +324,7 @@ void testChainedCalls() { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line6 -// CHECK-NEXT: col3 +// CHECK-NEXT: col8 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -577,40 +577,6 @@ void testChainedCalls() { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line33 -// CHECK-NEXT: col3 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line33 -// CHECK-NEXT: col6 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: end -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line33 -// CHECK-NEXT: col11 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line33 -// CHECK-NEXT: col17 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: kindcontrol -// CHECK-NEXT: edges -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: start -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line33 // CHECK-NEXT: col11 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -743,11 +709,45 @@ void testChainedCalls() { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line28 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line28 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line28 +// CHECK-NEXT: col8 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line28 +// CHECK-NEXT: col8 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: // CHECK-NEXT: kindevent // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line28 -// CHECK-NEXT: col3 +// CHECK-NEXT: col8 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: ranges @@ -781,7 +781,7 @@ void testChainedCalls() { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line28 -// CHECK-NEXT: col3 +// CHECK-NEXT: col8 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: diff --git a/test/Analysis/inlining/eager-reclamation-path-notes.cpp b/test/Analysis/inlining/eager-reclamation-path-notes.cpp index 3ee9d92b017..05411bbc777 100644 --- a/test/Analysis/inlining/eager-reclamation-path-notes.cpp +++ b/test/Analysis/inlining/eager-reclamation-path-notes.cpp @@ -202,7 +202,7 @@ int memberCallBaseDisappears() { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: depth1 +// CHECK-NEXT: depth0 // CHECK-NEXT: extended_message // CHECK-NEXT: Returning from 'getNullWrapper' // CHECK-NEXT: message @@ -217,40 +217,6 @@ int memberCallBaseDisappears() { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line24 -// CHECK-NEXT: col3 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line24 -// CHECK-NEXT: col12 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: end -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line24 -// CHECK-NEXT: col21 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line24 -// CHECK-NEXT: col34 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: kindcontrol -// CHECK-NEXT: edges -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: start -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line24 // CHECK-NEXT: col21 // CHECK-NEXT: file0 // CHECK-NEXT: diff --git a/test/Analysis/inlining/false-positive-suppression.c b/test/Analysis/inlining/false-positive-suppression.c index a836d9c6243..c5c6bb875ea 100644 --- a/test/Analysis/inlining/false-positive-suppression.c +++ b/test/Analysis/inlining/false-positive-suppression.c @@ -143,6 +143,27 @@ void testTrackNullVariable() { #endif } +void inlinedIsDifferent(int inlined) { + int i; + + // We were erroneously picking up the inner stack frame's initialization, + // even though the error occurs in the outer stack frame! + int *p = inlined ? &i : getNull(); + + if (!inlined) + inlinedIsDifferent(1); + + *p = 1; +#ifndef SUPPRESSED + // expected-warning@-2 {{Dereference of null pointer}} +#endif +} + +void testInlinedIsDifferent() { + // + inlinedIsDifferent(0); +} + // --------------------------------------- // FALSE NEGATIVES (over-suppression) diff --git a/test/Analysis/inlining/false-positive-suppression.m b/test/Analysis/inlining/false-positive-suppression.m new file mode 100644 index 00000000000..53ec138367e --- /dev/null +++ b/test/Analysis/inlining/false-positive-suppression.m @@ -0,0 +1,38 @@ +// RUN: %clang_cc1 -analyze -analyzer-checker=core -analyzer-config suppress-null-return-paths=false -verify %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core -verify -DSUPPRESSED=1 %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core -analyzer-config avoid-suppressing-null-argument-paths=true -DSUPPRESSED=1 -DNULL_ARGS=1 -verify %s + +#ifdef SUPPRESSED +// expected-no-diagnostics +#endif + +@interface PointerWrapper +- (int *)getPtr; +- (id)getObject; +@end + +id getNil() { + return 0; +} + +void testNilReceiverHelperA(int *x) { + *x = 1; +#ifndef SUPPRESSED + // expected-warning@-2 {{Dereference of null pointer}} +#endif +} + +void testNilReceiverHelperB(int *x) { + *x = 1; +#ifndef SUPPRESSED + // expected-warning@-2 {{Dereference of null pointer}} +#endif +} + +void testNilReceiver(int coin) { + id x = getNil(); + if (coin) + testNilReceiverHelperA([x getPtr]); + else + testNilReceiverHelperB([[x getObject] getPtr]); +} diff --git a/test/Analysis/inlining/inline-defensive-checks.c b/test/Analysis/inlining/inline-defensive-checks.c index df3a8f22811..aa7f70030c0 100644 --- a/test/Analysis/inlining/inline-defensive-checks.c +++ b/test/Analysis/inlining/inline-defensive-checks.c @@ -97,3 +97,16 @@ void test24(char *buffer) { use(buffer); buffer[1] = 'b'; } + +// Ensure idc works on pointers with constant offset. +void idcchar(const char *s2) { + if(s2) + ; +} +void testConstantOffset(char *value) { + char *cursor = value + 5; + idcchar(cursor); + if (*cursor) { + cursor++; + } +} diff --git a/test/Analysis/inlining/inline-defensive-checks.cpp b/test/Analysis/inlining/inline-defensive-checks.cpp index 37bccbdc5b9..b69c5356578 100644 --- a/test/Analysis/inlining/inline-defensive-checks.cpp +++ b/test/Analysis/inlining/inline-defensive-checks.cpp @@ -52,4 +52,22 @@ void radar13224271_caller() Ty value; radar13224271_callee(getTyVal(), value ); notNullArg(value); // no-warning +} + +struct Foo { + int *ptr; + Foo(int *p) { + *p = 1; // no-warning + } +}; +void idc(int *p3) { + if (p3) + ; +} +int *retNull() { + return 0; +} +void test(int *p1, int *p2) { + idc(p1); + Foo f(p1); } \ No newline at end of file diff --git a/test/Analysis/inlining/path-notes.c b/test/Analysis/inlining/path-notes.c index b128aabf7e9..660988535b3 100644 --- a/test/Analysis/inlining/path-notes.c +++ b/test/Analysis/inlining/path-notes.c @@ -107,6 +107,39 @@ void testUseOfNullPointer() { // expected-note@-4 {{Calling 'usePointer'}} } +struct X { char *p; }; + +void setFieldToNull(struct X *x) { + x->p = 0; // expected-note {{Null pointer value stored to field 'p'}} +} + +int testSetFieldToNull(struct X *x) { + setFieldToNull(x); // expected-note {{Calling 'setFieldToNull'}} + // expected-note@-1{{Returning from 'setFieldToNull'}} + return *x->p; + // expected-warning@-1 {{Dereference of null pointer (loaded from field 'p')}} + // expected-note@-2 {{Dereference of null pointer (loaded from field 'p')}} +} + +struct Outer { + struct Inner { + int *p; + } inner; +}; + +void test(struct Outer *wrapperPtr) { + wrapperPtr->inner.p = 0; // expected-note {{Null pointer value stored to field 'p'}} + *wrapperPtr->inner.p = 1; //expected-warning {{Dereference of null pointer (loaded from field 'p')}} + // expected-note@-1 {{Dereference of null pointer (loaded from field 'p')}} +} + +void test4(int **p) { + if (*p) return; // expected-note {{Taking false branch}} + // expected-note@-1 {{Assuming pointer value is null}} + **p = 1; // expected-warning {{Dereference of null pointer}} + // expected-note@-1 {{Dereference of null pointer}} +} + // CHECK: diagnostics // CHECK-NEXT: // CHECK-NEXT: @@ -241,7 +274,7 @@ void testUseOfNullPointer() { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: depth1 +// CHECK-NEXT: depth0 // CHECK-NEXT: extended_message // CHECK-NEXT: Returning from 'zero' // CHECK-NEXT: message @@ -269,12 +302,12 @@ void testUseOfNullPointer() { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line14 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line14 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -286,7 +319,7 @@ void testUseOfNullPointer() { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line14 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: ranges @@ -320,7 +353,7 @@ void testUseOfNullPointer() { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line14 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -425,11 +458,45 @@ void testUseOfNullPointer() { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line24 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line24 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line24 +// CHECK-NEXT: col6 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line24 +// CHECK-NEXT: col6 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: // CHECK-NEXT: kindevent // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line24 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: ranges @@ -463,7 +530,7 @@ void testUseOfNullPointer() { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line24 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -631,11 +698,45 @@ void testUseOfNullPointer() { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line39 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line39 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line39 +// CHECK-NEXT: col6 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line39 +// CHECK-NEXT: col6 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: // CHECK-NEXT: kindevent // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line39 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: ranges @@ -669,7 +770,7 @@ void testUseOfNullPointer() { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line39 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -837,11 +938,45 @@ void testUseOfNullPointer() { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line51 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line51 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line51 +// CHECK-NEXT: col6 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line51 +// CHECK-NEXT: col6 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: // CHECK-NEXT: kindevent // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line51 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: ranges @@ -875,7 +1010,7 @@ void testUseOfNullPointer() { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line51 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -1108,7 +1243,7 @@ void testUseOfNullPointer() { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: depth1 +// CHECK-NEXT: depth0 // CHECK-NEXT: extended_message // CHECK-NEXT: Returning from 'getZero' // CHECK-NEXT: message @@ -1123,40 +1258,6 @@ void testUseOfNullPointer() { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line65 -// CHECK-NEXT: col3 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line65 -// CHECK-NEXT: col3 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: end -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line65 -// CHECK-NEXT: col4 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line65 -// CHECK-NEXT: col10 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: kindcontrol -// CHECK-NEXT: edges -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: start -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line65 // CHECK-NEXT: col4 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -1170,12 +1271,12 @@ void testUseOfNullPointer() { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line65 -// CHECK-NEXT: col3 +// CHECK-NEXT: col14 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line65 -// CHECK-NEXT: col3 +// CHECK-NEXT: col14 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -1187,7 +1288,7 @@ void testUseOfNullPointer() { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line65 -// CHECK-NEXT: col3 +// CHECK-NEXT: col14 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: ranges @@ -1221,7 +1322,7 @@ void testUseOfNullPointer() { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line65 -// CHECK-NEXT: col3 +// CHECK-NEXT: col14 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -1454,7 +1555,7 @@ void testUseOfNullPointer() { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: depth1 +// CHECK-NEXT: depth0 // CHECK-NEXT: extended_message // CHECK-NEXT: Returning from 'getZero' // CHECK-NEXT: message @@ -1469,40 +1570,6 @@ void testUseOfNullPointer() { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line72 -// CHECK-NEXT: col3 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line72 -// CHECK-NEXT: col8 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: end -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line72 -// CHECK-NEXT: col11 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line72 -// CHECK-NEXT: col17 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: kindcontrol -// CHECK-NEXT: edges -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: start -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line72 // CHECK-NEXT: col11 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -1800,7 +1867,7 @@ void testUseOfNullPointer() { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: depth1 +// CHECK-NEXT: depth0 // CHECK-NEXT: extended_message // CHECK-NEXT: Returning from 'getZero' // CHECK-NEXT: message @@ -1815,40 +1882,6 @@ void testUseOfNullPointer() { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line79 -// CHECK-NEXT: col3 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line79 -// CHECK-NEXT: col5 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: end -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line79 -// CHECK-NEXT: col12 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line79 -// CHECK-NEXT: col18 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: kindcontrol -// CHECK-NEXT: edges -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: start -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line79 // CHECK-NEXT: col12 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -1925,12 +1958,12 @@ void testUseOfNullPointer() { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line83 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line83 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -1942,7 +1975,7 @@ void testUseOfNullPointer() { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line83 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: ranges @@ -1976,7 +2009,7 @@ void testUseOfNullPointer() { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line83 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -2209,7 +2242,7 @@ void testUseOfNullPointer() { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: depth1 +// CHECK-NEXT: depth0 // CHECK-NEXT: extended_message // CHECK-NEXT: Returning from 'getZero' // CHECK-NEXT: message @@ -2224,12 +2257,12 @@ void testUseOfNullPointer() { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line88 -// CHECK-NEXT: col3 +// CHECK-NEXT: col7 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line88 -// CHECK-NEXT: col3 +// CHECK-NEXT: col13 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -2237,46 +2270,12 @@ void testUseOfNullPointer() { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line88 -// CHECK-NEXT: col7 +// CHECK-NEXT: col3 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line88 -// CHECK-NEXT: col13 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: kindcontrol -// CHECK-NEXT: edges -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: start -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line88 -// CHECK-NEXT: col7 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line88 -// CHECK-NEXT: col13 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: end -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line88 -// CHECK-NEXT: col3 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line88 -// CHECK-NEXT: col3 +// CHECK-NEXT: col3 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -2334,12 +2333,12 @@ void testUseOfNullPointer() { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line92 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line92 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -2351,7 +2350,7 @@ void testUseOfNullPointer() { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line92 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: ranges @@ -2385,7 +2384,7 @@ void testUseOfNullPointer() { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line92 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -2618,47 +2617,13 @@ void testUseOfNullPointer() { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: depth1 +// CHECK-NEXT: depth0 // CHECK-NEXT: extended_message // CHECK-NEXT: Returning from 'getZero' // CHECK-NEXT: message // CHECK-NEXT: Returning from 'getZero' // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: kindcontrol -// CHECK-NEXT: edges -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: start -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line103 -// CHECK-NEXT: col3 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line103 -// CHECK-NEXT: col12 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: end -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line103 -// CHECK-NEXT: col14 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line103 -// CHECK-NEXT: col20 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: // CHECK-NEXT: kindevent // CHECK-NEXT: location // CHECK-NEXT: @@ -2765,11 +2730,45 @@ void testUseOfNullPointer() { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line97 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line97 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line97 +// CHECK-NEXT: col6 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line97 +// CHECK-NEXT: col6 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: // CHECK-NEXT: kindevent // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line97 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: ranges @@ -2803,7 +2802,542 @@ void testUseOfNullPointer() { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line97 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: path +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line117 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line117 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line117 +// CHECK-NEXT: col19 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: depth0 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Calling 'setFieldToNull' +// CHECK-NEXT: message +// CHECK-NEXT: Calling 'setFieldToNull' +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line112 +// CHECK-NEXT: col1 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: depth1 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Entered call from 'testSetFieldToNull' +// CHECK-NEXT: message +// CHECK-NEXT: Entered call from 'testSetFieldToNull' +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line112 +// CHECK-NEXT: col1 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line112 +// CHECK-NEXT: col4 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line113 +// CHECK-NEXT: col2 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line113 +// CHECK-NEXT: col2 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line113 +// CHECK-NEXT: col2 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line113 +// CHECK-NEXT: col2 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line113 +// CHECK-NEXT: col9 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: depth1 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Null pointer value stored to field 'p' +// CHECK-NEXT: message +// CHECK-NEXT: Null pointer value stored to field 'p' +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line117 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line117 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line117 +// CHECK-NEXT: col19 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: depth0 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Returning from 'setFieldToNull' +// CHECK-NEXT: message +// CHECK-NEXT: Returning from 'setFieldToNull' +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line117 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line117 +// CHECK-NEXT: col16 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line119 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line119 +// CHECK-NEXT: col8 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line119 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line119 +// CHECK-NEXT: col8 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line119 +// CHECK-NEXT: col10 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line119 +// CHECK-NEXT: col10 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line119 +// CHECK-NEXT: col10 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line119 +// CHECK-NEXT: col14 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line119 +// CHECK-NEXT: col14 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: depth0 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Dereference of null pointer (loaded from field 'p') +// CHECK-NEXT: message +// CHECK-NEXT: Dereference of null pointer (loaded from field 'p') +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: descriptionDereference of null pointer (loaded from field 'p') +// CHECK-NEXT: categoryLogic error +// CHECK-NEXT: typeDereference of null pointer +// CHECK-NEXT: issue_context_kindfunction +// CHECK-NEXT: issue_contexttestSetFieldToNull +// CHECK-NEXT: issue_hash3 +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line119 +// CHECK-NEXT: col10 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: path +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line131 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line131 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line131 +// CHECK-NEXT: col25 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: depth0 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Null pointer value stored to field 'p' +// CHECK-NEXT: message +// CHECK-NEXT: Null pointer value stored to field 'p' +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line131 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line131 +// CHECK-NEXT: col12 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line132 +// CHECK-NEXT: col24 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line132 +// CHECK-NEXT: col24 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line132 +// CHECK-NEXT: col24 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line132 +// CHECK-NEXT: col22 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line132 +// CHECK-NEXT: col22 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: depth0 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Dereference of null pointer (loaded from field 'p') +// CHECK-NEXT: message +// CHECK-NEXT: Dereference of null pointer (loaded from field 'p') +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: descriptionDereference of null pointer (loaded from field 'p') +// CHECK-NEXT: categoryLogic error +// CHECK-NEXT: typeDereference of null pointer +// CHECK-NEXT: issue_context_kindfunction +// CHECK-NEXT: issue_contexttest +// CHECK-NEXT: issue_hash2 +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line132 +// CHECK-NEXT: col24 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: path +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line137 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line137 +// CHECK-NEXT: col4 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line137 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line137 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line137 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line137 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line137 +// CHECK-NEXT: col8 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: depth0 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Assuming pointer value is null +// CHECK-NEXT: message +// CHECK-NEXT: Assuming pointer value is null +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line137 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line137 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line139 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line139 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line139 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line139 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line139 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line139 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line139 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line139 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line139 +// CHECK-NEXT: col9 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: depth0 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Dereference of null pointer +// CHECK-NEXT: message +// CHECK-NEXT: Dereference of null pointer +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: descriptionDereference of null pointer +// CHECK-NEXT: categoryLogic error +// CHECK-NEXT: typeDereference of null pointer +// CHECK-NEXT: issue_context_kindfunction +// CHECK-NEXT: issue_contexttest4 +// CHECK-NEXT: issue_hash3 +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line139 +// CHECK-NEXT: col7 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: diff --git a/test/Analysis/inlining/path-notes.cpp b/test/Analysis/inlining/path-notes.cpp index 895ee28e8b6..e0c83cbe329 100644 --- a/test/Analysis/inlining/path-notes.cpp +++ b/test/Analysis/inlining/path-notes.cpp @@ -206,6 +206,64 @@ void testPathNoteOnInitializer() { // expected-note@-1 {{Calling constructor for 'FooWithInitializer'}} } +int testNonPrintableAssignment(int **p) { + int *&y = *p; // expected-note {{'y' initialized here}} + y = 0; // expected-note {{Storing null pointer value}} + return *y; // expected-warning {{Dereference of null pointer (loaded from variable 'y')}} + // expected-note@-1 {{Dereference of null pointer (loaded from variable 'y')}} +} + +struct Base { int *x; }; +struct Derived : public Base {}; + +void test(Derived d) { + d.x = 0; //expected-note {{Null pointer value stored to 'd.x'}} + *d.x = 1; // expected-warning {{Dereference of null pointer (loaded from field 'x')}} + // expected-note@-1 {{Dereference of null pointer (loaded from field 'x')}} +} + +struct Owner { + struct Wrapper { + int x; + }; + Wrapper *arr; + void testGetDerefExprOnMemberExprWithADot(); +}; + +void Owner::testGetDerefExprOnMemberExprWithADot() { + if (arr) // expected-note {{Assuming pointer value is null}} + // expected-note@-1 {{Taking false branch}} + ; + arr[1].x = 1; //expected-warning {{Dereference of null pointer}} + //expected-note@-1 {{Dereference of null pointer}} +} + +void testGetDerefExprOnMemberExprWithADot() { + Owner::Wrapper *arr; // expected-note {{'arr' declared without an initial value}} + arr[2].x = 1; // expected-warning {{Dereference of undefined pointer value}} + // expected-note@-1 {{Dereference of undefined pointer value}} +} + + + +class A { +public: + void bar() const {} +}; +const A& testDeclRefExprToReferenceInGetDerefExpr(const A *ptr) { + const A& val = *ptr; //expected-note {{'val' initialized here}} + + // This is not valid C++; if 'ptr' were null, creating 'ref' would be illegal. + // However, this is not checked at runtime, so this branch is actually + // possible. + if (&val == 0) { //expected-note {{Assuming pointer value is null}} + // expected-note@-1 {{Taking true branch}} + val.bar(); // expected-warning {{Called C++ object pointer is null}} + // expected-note@-1 {{Called C++ object pointer is null}} + } + + return val; +} // CHECK: diagnostics // CHECK-NEXT: // CHECK-NEXT: @@ -719,11 +777,45 @@ void testPathNoteOnInitializer() { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line8 +// CHECK-NEXT: col5 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line8 +// CHECK-NEXT: col5 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line8 +// CHECK-NEXT: col8 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line8 +// CHECK-NEXT: col8 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: // CHECK-NEXT: kindevent // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line8 -// CHECK-NEXT: col5 +// CHECK-NEXT: col8 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: ranges @@ -757,7 +849,7 @@ void testPathNoteOnInitializer() { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line8 -// CHECK-NEXT: col5 +// CHECK-NEXT: col8 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -973,11 +1065,45 @@ void testPathNoteOnInitializer() { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line41 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line41 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line41 +// CHECK-NEXT: col10 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line41 +// CHECK-NEXT: col10 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: // CHECK-NEXT: kindevent // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line41 -// CHECK-NEXT: col7 +// CHECK-NEXT: col10 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: ranges @@ -1011,7 +1137,7 @@ void testPathNoteOnInitializer() { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line41 -// CHECK-NEXT: col7 +// CHECK-NEXT: col10 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -1188,11 +1314,45 @@ void testPathNoteOnInitializer() { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line63 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line63 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line63 +// CHECK-NEXT: col18 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line63 +// CHECK-NEXT: col18 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: // CHECK-NEXT: kindevent // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line63 -// CHECK-NEXT: col7 +// CHECK-NEXT: col18 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: ranges @@ -1224,7 +1384,7 @@ void testPathNoteOnInitializer() { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line63 -// CHECK-NEXT: col7 +// CHECK-NEXT: col18 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -1401,11 +1561,45 @@ void testPathNoteOnInitializer() { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line68 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line68 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line68 +// CHECK-NEXT: col18 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line68 +// CHECK-NEXT: col18 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: // CHECK-NEXT: kindevent // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line68 -// CHECK-NEXT: col7 +// CHECK-NEXT: col18 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: ranges @@ -1437,7 +1631,7 @@ void testPathNoteOnInitializer() { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line68 -// CHECK-NEXT: col7 +// CHECK-NEXT: col18 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -1648,11 +1842,45 @@ void testPathNoteOnInitializer() { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line73 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line73 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line73 +// CHECK-NEXT: col18 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line73 +// CHECK-NEXT: col18 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: // CHECK-NEXT: kindevent // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line73 -// CHECK-NEXT: col7 +// CHECK-NEXT: col18 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: ranges @@ -1684,7 +1912,7 @@ void testPathNoteOnInitializer() { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line73 -// CHECK-NEXT: col7 +// CHECK-NEXT: col18 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -1929,11 +2157,45 @@ void testPathNoteOnInitializer() { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line78 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line78 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line78 +// CHECK-NEXT: col18 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line78 +// CHECK-NEXT: col18 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: // CHECK-NEXT: kindevent // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line78 -// CHECK-NEXT: col7 +// CHECK-NEXT: col18 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: ranges @@ -1967,7 +2229,7 @@ void testPathNoteOnInitializer() { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line78 -// CHECK-NEXT: col7 +// CHECK-NEXT: col18 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -2106,40 +2368,6 @@ void testPathNoteOnInitializer() { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: kindcontrol -// CHECK-NEXT: edges -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: start -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line145 -// CHECK-NEXT: col5 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line145 -// CHECK-NEXT: col5 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: end -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line145 -// CHECK-NEXT: col9 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line145 -// CHECK-NEXT: col12 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: // CHECK-NEXT: kindevent // CHECK-NEXT: location // CHECK-NEXT: @@ -2280,11 +2508,45 @@ void testPathNoteOnInitializer() { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line83 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line83 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line83 +// CHECK-NEXT: col18 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line83 +// CHECK-NEXT: col18 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: // CHECK-NEXT: kindevent // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line83 -// CHECK-NEXT: col7 +// CHECK-NEXT: col18 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: ranges @@ -2318,7 +2580,7 @@ void testPathNoteOnInitializer() { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line83 -// CHECK-NEXT: col7 +// CHECK-NEXT: col18 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -2533,11 +2795,45 @@ void testPathNoteOnInitializer() { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line88 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line88 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line88 +// CHECK-NEXT: col18 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line88 +// CHECK-NEXT: col18 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: // CHECK-NEXT: kindevent // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line88 -// CHECK-NEXT: col7 +// CHECK-NEXT: col18 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: ranges @@ -2569,7 +2865,7 @@ void testPathNoteOnInitializer() { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line88 -// CHECK-NEXT: col7 +// CHECK-NEXT: col18 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -2739,7 +3035,7 @@ void testPathNoteOnInitializer() { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: depth1 +// CHECK-NEXT: depth0 // CHECK-NEXT: extended_message // CHECK-NEXT: Returning from 'getZero' // CHECK-NEXT: message @@ -2754,40 +3050,6 @@ void testPathNoteOnInitializer() { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line173 -// CHECK-NEXT: col5 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line173 -// CHECK-NEXT: col7 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: end -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line173 -// CHECK-NEXT: col23 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line173 -// CHECK-NEXT: col29 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: kindcontrol -// CHECK-NEXT: edges -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: start -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line173 // CHECK-NEXT: col23 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -2801,12 +3063,12 @@ void testPathNoteOnInitializer() { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line173 -// CHECK-NEXT: col19 +// CHECK-NEXT: col21 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line173 -// CHECK-NEXT: col19 +// CHECK-NEXT: col21 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -2818,7 +3080,7 @@ void testPathNoteOnInitializer() { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line173 -// CHECK-NEXT: col19 +// CHECK-NEXT: col21 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: ranges @@ -2852,7 +3114,7 @@ void testPathNoteOnInitializer() { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line173 -// CHECK-NEXT: col19 +// CHECK-NEXT: col21 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -3119,7 +3381,7 @@ void testPathNoteOnInitializer() { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: depth1 +// CHECK-NEXT: depth0 // CHECK-NEXT: extended_message // CHECK-NEXT: Returning from 'getZeroByRef' // CHECK-NEXT: message @@ -3134,40 +3396,6 @@ void testPathNoteOnInitializer() { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line180 -// CHECK-NEXT: col5 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line180 -// CHECK-NEXT: col7 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: end -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line180 -// CHECK-NEXT: col23 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line180 -// CHECK-NEXT: col34 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: kindcontrol -// CHECK-NEXT: edges -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: start -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line180 // CHECK-NEXT: col23 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -3181,12 +3409,12 @@ void testPathNoteOnInitializer() { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line180 -// CHECK-NEXT: col19 +// CHECK-NEXT: col21 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line180 -// CHECK-NEXT: col19 +// CHECK-NEXT: col21 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -3198,7 +3426,7 @@ void testPathNoteOnInitializer() { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line180 -// CHECK-NEXT: col19 +// CHECK-NEXT: col21 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: ranges @@ -3232,7 +3460,7 @@ void testPathNoteOnInitializer() { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line180 -// CHECK-NEXT: col19 +// CHECK-NEXT: col21 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -3655,12 +3883,12 @@ void testPathNoteOnInitializer() { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line197 -// CHECK-NEXT: col3 +// CHECK-NEXT: col8 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line197 -// CHECK-NEXT: col3 +// CHECK-NEXT: col8 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -3672,7 +3900,7 @@ void testPathNoteOnInitializer() { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line197 -// CHECK-NEXT: col3 +// CHECK-NEXT: col8 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: ranges @@ -3704,7 +3932,814 @@ void testPathNoteOnInitializer() { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line197 -// CHECK-NEXT: col3 +// CHECK-NEXT: col8 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: path +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line210 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line210 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line210 +// CHECK-NEXT: col9 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: depth0 +// CHECK-NEXT: extended_message +// CHECK-NEXT: 'y' initialized here +// CHECK-NEXT: message +// CHECK-NEXT: 'y' initialized here +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line210 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line210 +// CHECK-NEXT: col5 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line211 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line211 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line211 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line211 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line211 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: depth0 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Storing null pointer value +// CHECK-NEXT: message +// CHECK-NEXT: Storing null pointer value +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line211 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line211 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line212 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line212 +// CHECK-NEXT: col8 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line212 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line212 +// CHECK-NEXT: col8 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line212 +// CHECK-NEXT: col10 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line212 +// CHECK-NEXT: col10 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line212 +// CHECK-NEXT: col10 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line212 +// CHECK-NEXT: col11 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line212 +// CHECK-NEXT: col11 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: depth0 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Dereference of null pointer (loaded from variable 'y') +// CHECK-NEXT: message +// CHECK-NEXT: Dereference of null pointer (loaded from variable 'y') +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: descriptionDereference of null pointer (loaded from variable 'y') +// CHECK-NEXT: categoryLogic error +// CHECK-NEXT: typeDereference of null pointer +// CHECK-NEXT: issue_context_kindfunction +// CHECK-NEXT: issue_contexttestNonPrintableAssignment +// CHECK-NEXT: issue_hash3 +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line212 +// CHECK-NEXT: col10 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: path +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line220 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line220 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line220 +// CHECK-NEXT: col9 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: depth0 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Null pointer value stored to 'd.x' +// CHECK-NEXT: message +// CHECK-NEXT: Null pointer value stored to 'd.x' +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line220 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line220 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line221 +// CHECK-NEXT: col8 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line221 +// CHECK-NEXT: col8 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line221 +// CHECK-NEXT: col8 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line221 +// CHECK-NEXT: col6 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line221 +// CHECK-NEXT: col6 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: depth0 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Dereference of null pointer (loaded from field 'x') +// CHECK-NEXT: message +// CHECK-NEXT: Dereference of null pointer (loaded from field 'x') +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: descriptionDereference of null pointer (loaded from field 'x') +// CHECK-NEXT: categoryLogic error +// CHECK-NEXT: typeDereference of null pointer +// CHECK-NEXT: issue_context_kindfunction +// CHECK-NEXT: issue_contexttest +// CHECK-NEXT: issue_hash2 +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line221 +// CHECK-NEXT: col8 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: path +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line234 +// CHECK-NEXT: col2 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line234 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line234 +// CHECK-NEXT: col6 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line234 +// CHECK-NEXT: col8 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line234 +// CHECK-NEXT: col6 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line234 +// CHECK-NEXT: col6 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line234 +// CHECK-NEXT: col8 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: depth0 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Assuming pointer value is null +// CHECK-NEXT: message +// CHECK-NEXT: Assuming pointer value is null +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line234 +// CHECK-NEXT: col6 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line234 +// CHECK-NEXT: col8 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line237 +// CHECK-NEXT: col2 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line237 +// CHECK-NEXT: col4 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line237 +// CHECK-NEXT: col2 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line237 +// CHECK-NEXT: col4 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line237 +// CHECK-NEXT: col11 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line237 +// CHECK-NEXT: col11 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line237 +// CHECK-NEXT: col11 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line237 +// CHECK-NEXT: col2 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line237 +// CHECK-NEXT: col13 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: depth0 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Dereference of null pointer +// CHECK-NEXT: message +// CHECK-NEXT: Dereference of null pointer +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: descriptionDereference of null pointer +// CHECK-NEXT: categoryLogic error +// CHECK-NEXT: typeDereference of null pointer +// CHECK-NEXT: issue_context_kindC++ method +// CHECK-NEXT: issue_contexttestGetDerefExprOnMemberExprWithADot +// CHECK-NEXT: issue_hash4 +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line237 +// CHECK-NEXT: col11 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: path +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line242 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line242 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line242 +// CHECK-NEXT: col21 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: depth0 +// CHECK-NEXT: extended_message +// CHECK-NEXT: 'arr' declared without an initial value +// CHECK-NEXT: message +// CHECK-NEXT: 'arr' declared without an initial value +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line242 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line242 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line243 +// CHECK-NEXT: col11 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line243 +// CHECK-NEXT: col11 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line243 +// CHECK-NEXT: col11 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line243 +// CHECK-NEXT: col2 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line243 +// CHECK-NEXT: col13 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: depth0 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Dereference of undefined pointer value +// CHECK-NEXT: message +// CHECK-NEXT: Dereference of undefined pointer value +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: descriptionDereference of undefined pointer value +// CHECK-NEXT: categoryLogic error +// CHECK-NEXT: typeDereference of undefined pointer value +// CHECK-NEXT: issue_context_kindfunction +// CHECK-NEXT: issue_contexttestGetDerefExprOnMemberExprWithADot +// CHECK-NEXT: issue_hash2 +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line243 +// CHECK-NEXT: col11 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: path +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line254 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line254 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line254 +// CHECK-NEXT: col14 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: depth0 +// CHECK-NEXT: extended_message +// CHECK-NEXT: 'val' initialized here +// CHECK-NEXT: message +// CHECK-NEXT: 'val' initialized here +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line254 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line254 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line259 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line259 +// CHECK-NEXT: col4 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line259 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line259 +// CHECK-NEXT: col4 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line259 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line259 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line259 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line259 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line259 +// CHECK-NEXT: col15 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: depth0 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Assuming pointer value is null +// CHECK-NEXT: message +// CHECK-NEXT: Assuming pointer value is null +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line259 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line259 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line261 +// CHECK-NEXT: col5 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line261 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line261 +// CHECK-NEXT: col5 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line261 +// CHECK-NEXT: col5 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line261 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: depth0 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Called C++ object pointer is null +// CHECK-NEXT: message +// CHECK-NEXT: Called C++ object pointer is null +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: descriptionCalled C++ object pointer is null +// CHECK-NEXT: categoryLogic error +// CHECK-NEXT: typeCalled C++ object pointer is null +// CHECK-NEXT: issue_context_kindfunction +// CHECK-NEXT: issue_contexttestDeclRefExprToReferenceInGetDerefExpr +// CHECK-NEXT: issue_hash8 +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line261 +// CHECK-NEXT: col5 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: diff --git a/test/Analysis/inlining/path-notes.m b/test/Analysis/inlining/path-notes.m index f3a7b6cc0fe..74f088a382b 100644 --- a/test/Analysis/inlining/path-notes.m +++ b/test/Analysis/inlining/path-notes.m @@ -13,7 +13,7 @@ void dispatch_sync(dispatch_queue_t, dispatch_block_t); int *getZeroIfNil(Test *x) { return x.p; - // expected-note@-1 {{No method is called because the receiver is nil}} + // expected-note@-1 {{'p' not called because the receiver is nil}} // expected-note@-2 {{Returning null pointer}} } @@ -65,6 +65,31 @@ int testDispatchSyncInliningNoPruning(int coin) { } +@interface PointerWrapper +- (int *)getPtr; +@end + +id getNil() { + return 0; +} + +void testNilReceiverHelper(int *x) { + *x = 1; // expected-warning {{Dereference of null pointer}} + // expected-note@-1 {{Dereference of null pointer (loaded from variable 'x')}} +} + +void testNilReceiver(id *x) { + if (*x) { + // expected-note@-1 {{Taking false branch}} + return; + } + testNilReceiverHelper([*x getPtr]); + // expected-note@-1 {{'getPtr' not called because the receiver is nil}} + // expected-note@-2 {{Passing null pointer value via 1st parameter 'x'}} + // expected-note@-3 {{Calling 'testNilReceiverHelper'}} +} + + // CHECK: diagnostics // CHECK-NEXT: // CHECK-NEXT: @@ -303,9 +328,9 @@ int testDispatchSyncInliningNoPruning(int coin) { // CHECK-NEXT: // CHECK-NEXT: depth1 // CHECK-NEXT: extended_message -// CHECK-NEXT: No method is called because the receiver is nil +// CHECK-NEXT: 'p' not called because the receiver is nil // CHECK-NEXT: message -// CHECK-NEXT: No method is called because the receiver is nil +// CHECK-NEXT: 'p' not called because the receiver is nil // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: kindcontrol @@ -393,7 +418,7 @@ int testDispatchSyncInliningNoPruning(int coin) { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: depth1 +// CHECK-NEXT: depth0 // CHECK-NEXT: extended_message // CHECK-NEXT: Returning from 'getZeroIfNil' // CHECK-NEXT: message @@ -408,40 +433,6 @@ int testDispatchSyncInliningNoPruning(int coin) { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line21 -// CHECK-NEXT: col3 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line21 -// CHECK-NEXT: col3 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: end -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line21 -// CHECK-NEXT: col4 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line21 -// CHECK-NEXT: col15 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: kindcontrol -// CHECK-NEXT: edges -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: start -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line21 // CHECK-NEXT: col4 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -455,12 +446,12 @@ int testDispatchSyncInliningNoPruning(int coin) { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line21 -// CHECK-NEXT: col3 +// CHECK-NEXT: col20 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line21 -// CHECK-NEXT: col3 +// CHECK-NEXT: col20 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -472,7 +463,7 @@ int testDispatchSyncInliningNoPruning(int coin) { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line21 -// CHECK-NEXT: col3 +// CHECK-NEXT: col20 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: ranges @@ -506,7 +497,7 @@ int testDispatchSyncInliningNoPruning(int coin) { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line21 -// CHECK-NEXT: col3 +// CHECK-NEXT: col20 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -689,7 +680,7 @@ int testDispatchSyncInliningNoPruning(int coin) { // CHECK-NEXT: col1 // CHECK-NEXT: file0 // CHECK-NEXT: -// CHECK-NEXT: depth2 +// CHECK-NEXT: depth1 // CHECK-NEXT: extended_message // CHECK-NEXT: Returning to caller // CHECK-NEXT: message @@ -718,7 +709,7 @@ int testDispatchSyncInliningNoPruning(int coin) { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: depth1 +// CHECK-NEXT: depth0 // CHECK-NEXT: extended_message // CHECK-NEXT: Returning from 'dispatch_sync' // CHECK-NEXT: message @@ -746,46 +737,12 @@ int testDispatchSyncInliningNoPruning(int coin) { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line43 -// CHECK-NEXT: col3 +// CHECK-NEXT: col12 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line43 -// CHECK-NEXT: col8 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: kindcontrol -// CHECK-NEXT: edges -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: start -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line43 -// CHECK-NEXT: col3 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line43 -// CHECK-NEXT: col8 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: end -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line43 -// CHECK-NEXT: col10 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line43 -// CHECK-NEXT: col10 +// CHECK-NEXT: col12 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -797,7 +754,7 @@ int testDispatchSyncInliningNoPruning(int coin) { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line43 -// CHECK-NEXT: col10 +// CHECK-NEXT: col12 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: ranges @@ -831,7 +788,7 @@ int testDispatchSyncInliningNoPruning(int coin) { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line43 -// CHECK-NEXT: col10 +// CHECK-NEXT: col12 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -1080,4 +1037,321 @@ int testDispatchSyncInliningNoPruning(int coin) { // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: path +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line82 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line82 +// CHECK-NEXT: col4 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line86 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line86 +// CHECK-NEXT: col23 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line86 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line86 +// CHECK-NEXT: col23 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line86 +// CHECK-NEXT: col26 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line86 +// CHECK-NEXT: col26 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line86 +// CHECK-NEXT: col26 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line86 +// CHECK-NEXT: col26 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line86 +// CHECK-NEXT: col27 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: depth0 +// CHECK-NEXT: extended_message +// CHECK-NEXT: 'getPtr' not called because the receiver is nil +// CHECK-NEXT: message +// CHECK-NEXT: 'getPtr' not called because the receiver is nil +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line86 +// CHECK-NEXT: col26 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line86 +// CHECK-NEXT: col26 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line86 +// CHECK-NEXT: col25 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line86 +// CHECK-NEXT: col25 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line86 +// CHECK-NEXT: col25 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line86 +// CHECK-NEXT: col25 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line86 +// CHECK-NEXT: col35 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: depth0 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Passing null pointer value via 1st parameter 'x' +// CHECK-NEXT: message +// CHECK-NEXT: Passing null pointer value via 1st parameter 'x' +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line86 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line86 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line86 +// CHECK-NEXT: col36 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: depth0 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Calling 'testNilReceiverHelper' +// CHECK-NEXT: message +// CHECK-NEXT: Calling 'testNilReceiverHelper' +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line76 +// CHECK-NEXT: col1 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: depth1 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Entered call from 'testNilReceiver' +// CHECK-NEXT: message +// CHECK-NEXT: Entered call from 'testNilReceiver' +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line76 +// CHECK-NEXT: col1 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line76 +// CHECK-NEXT: col4 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line77 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line77 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line77 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line77 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line77 +// CHECK-NEXT: col6 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line77 +// CHECK-NEXT: col6 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line77 +// CHECK-NEXT: col6 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line77 +// CHECK-NEXT: col4 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line77 +// CHECK-NEXT: col4 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: depth1 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Dereference of null pointer (loaded from variable 'x') +// CHECK-NEXT: message +// CHECK-NEXT: Dereference of null pointer (loaded from variable 'x') +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: descriptionDereference of null pointer (loaded from variable 'x') +// CHECK-NEXT: categoryLogic error +// CHECK-NEXT: typeDereference of null pointer +// CHECK-NEXT: issue_context_kindfunction +// CHECK-NEXT: issue_contexttestNilReceiverHelper +// CHECK-NEXT: issue_hash1 +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line77 +// CHECK-NEXT: col6 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: // CHECK-NEXT: diff --git a/test/Analysis/malloc-annotations.c b/test/Analysis/malloc-annotations.c index 3a260c3aefd..c197df4386e 100644 --- a/test/Analysis/malloc-annotations.c +++ b/test/Analysis/malloc-annotations.c @@ -26,7 +26,7 @@ struct stuff myglobalstuff; void f1() { int *p = malloc(12); - return; // expected-warning{{Memory is never released; potential leak}} + return; // expected-warning{{Potential leak of memory pointed to by}} } void f2() { @@ -54,17 +54,17 @@ void naf1() { void n2af1() { int *p = my_malloc2(12); - return; // expected-warning{{Memory is never released; potential leak}} + return; // expected-warning{{Potential leak of memory pointed to by}} } void af1() { int *p = my_malloc(12); - return; // expected-warning{{Memory is never released; potential leak}} + return; // expected-warning{{Potential leak of memory pointed to by}} } void af1_b() { int *p = my_malloc(12); -} // expected-warning{{Memory is never released; potential leak}} +} // expected-warning{{Potential leak of memory pointed to by}} void af1_c() { myglobalpointer = my_malloc(12); // no-warning @@ -73,7 +73,7 @@ void af1_c() { void af1_d() { struct stuff mystuff; mystuff.somefield = my_malloc(12); -} // expected-warning{{Memory is never released; potential leak}} +} // expected-warning{{Potential leak of memory pointed to by}} // Test that we can pass out allocated memory via pointer-to-pointer. void af1_e(void **pp) { @@ -239,7 +239,7 @@ char mallocGarbage () { // This tests that calloc() buffers need to be freed void callocNoFree () { char *buf = calloc(2,2); - return; // expected-warning{{never released}} + return; // expected-warning{{Potential leak of memory pointed to by}} } // These test that calloc() buffers are zeroed by default @@ -258,7 +258,7 @@ char callocZeroesBad () { if (buf[1] != 0) { free(buf); // expected-warning{{never executed}} } - return result; // expected-warning{{never released}} + return result; // expected-warning{{Potential leak of memory pointed to by}} } void testMultipleFreeAnnotations() { diff --git a/test/Analysis/malloc-interprocedural.c b/test/Analysis/malloc-interprocedural.c index 3c7bab6717c..c78cc6c6aae 100644 --- a/test/Analysis/malloc-interprocedural.c +++ b/test/Analysis/malloc-interprocedural.c @@ -32,7 +32,7 @@ static void my_free1(void *p) { static void test1() { void *data = 0; my_malloc1(&data, 4); -} // expected-warning {{Memory is never released; potential leak of memory pointed to by 'data'}} +} // expected-warning {{Potential leak of memory pointed to by 'data'}} static void test11() { void *data = 0; @@ -43,9 +43,9 @@ static void test11() { static void testUniqueingByallocationSiteInTopLevelFunction() { void *data = my_malloc2(1, 4); data = 0; - int x = 5;// expected-warning {{Memory is never released; potential leak of memory pointed to by 'data'}} + int x = 5;// expected-warning {{Potential leak of memory pointed to by 'data'}} data = my_malloc2(1, 4); -} // expected-warning {{Memory is never released; potential leak of memory pointed to by 'data'}} +} // expected-warning {{Potential leak of memory pointed to by 'data'}} static void test3() { void *data = my_malloc2(1, 4); @@ -81,7 +81,7 @@ static char *reshape(char *in) { void testThatRemoveDeadBindingsRunBeforeEachCall() { char *v = malloc(12); v = reshape(v); - v = reshape(v);// expected-warning {{Memory is never released; potential leak of memory pointed to by 'v'}} + v = reshape(v);// expected-warning {{Potential leak of memory pointed to by 'v'}} } // Test that we keep processing after 'return;' diff --git a/test/Analysis/malloc-plist.c b/test/Analysis/malloc-plist.c index ddd09db6ff8..41bb8b5793c 100644 --- a/test/Analysis/malloc-plist.c +++ b/test/Analysis/malloc-plist.c @@ -169,6 +169,28 @@ void use_function_with_leak7() { function_with_leak7(); } +// Test that we do not print the name of a variable not visible from where +// the issue is reported. +int *my_malloc() { + int *p = malloc(12); + return p; +} +void testOnlyRefferToVisibleVariables() { + my_malloc(); +} // expected-warning {{Potential leak of memory}} + +struct PointerWrapper{ + int*p; +}; +int *my_malloc_into_struct() { + struct PointerWrapper w; + w.p = malloc(12); + return w.p; +} +void testMyMalloc() { + my_malloc_into_struct(); // expected-warning {{Potential leak of memory}} +} + // CHECK: diagnostics // CHECK-NEXT: // CHECK-NEXT: @@ -378,12 +400,12 @@ void use_function_with_leak7() { // CHECK-NEXT: // CHECK-NEXT: depth0 // CHECK-NEXT: extended_message -// CHECK-NEXT: Memory is never released; potential leak of memory pointed to by 'p' +// CHECK-NEXT: Potential leak of memory pointed to by 'p' // CHECK-NEXT: message -// CHECK-NEXT: Memory is never released; potential leak of memory pointed to by 'p' +// CHECK-NEXT: Potential leak of memory pointed to by 'p' // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: descriptionMemory is never released; potential leak of memory pointed to by 'p' +// CHECK-NEXT: descriptionPotential leak of memory pointed to by 'p' // CHECK-NEXT: categoryMemory Error // CHECK-NEXT: typeMemory leak // CHECK-NEXT: issue_context_kindfunction @@ -540,12 +562,12 @@ void use_function_with_leak7() { // CHECK-NEXT: // CHECK-NEXT: depth0 // CHECK-NEXT: extended_message -// CHECK-NEXT: Memory is never released; potential leak of memory pointed to by 'A' +// CHECK-NEXT: Potential leak of memory pointed to by 'A' // CHECK-NEXT: message -// CHECK-NEXT: Memory is never released; potential leak of memory pointed to by 'A' +// CHECK-NEXT: Potential leak of memory pointed to by 'A' // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: descriptionMemory is never released; potential leak of memory pointed to by 'A' +// CHECK-NEXT: descriptionPotential leak of memory pointed to by 'A' // CHECK-NEXT: categoryMemory Error // CHECK-NEXT: typeMemory leak // CHECK-NEXT: issue_context_kindfunction @@ -925,12 +947,12 @@ void use_function_with_leak7() { // CHECK-NEXT: // CHECK-NEXT: depth0 // CHECK-NEXT: extended_message -// CHECK-NEXT: Memory is never released; potential leak of memory pointed to by 'buf' +// CHECK-NEXT: Potential leak of memory pointed to by 'buf' // CHECK-NEXT: message -// CHECK-NEXT: Memory is never released; potential leak of memory pointed to by 'buf' +// CHECK-NEXT: Potential leak of memory pointed to by 'buf' // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: descriptionMemory is never released; potential leak of memory pointed to by 'buf' +// CHECK-NEXT: descriptionPotential leak of memory pointed to by 'buf' // CHECK-NEXT: categoryMemory Error // CHECK-NEXT: typeMemory leak // CHECK-NEXT: issue_context_kindfunction @@ -1274,7 +1296,7 @@ void use_function_with_leak7() { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: depth1 +// CHECK-NEXT: depth0 // CHECK-NEXT: extended_message // CHECK-NEXT: Returned allocated memory // CHECK-NEXT: message @@ -1324,12 +1346,12 @@ void use_function_with_leak7() { // CHECK-NEXT: // CHECK-NEXT: depth0 // CHECK-NEXT: extended_message -// CHECK-NEXT: Memory is never released; potential leak of memory pointed to by 'buf' +// CHECK-NEXT: Potential leak of memory pointed to by 'buf' // CHECK-NEXT: message -// CHECK-NEXT: Memory is never released; potential leak of memory pointed to by 'buf' +// CHECK-NEXT: Potential leak of memory pointed to by 'buf' // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: descriptionMemory is never released; potential leak of memory pointed to by 'buf' +// CHECK-NEXT: descriptionPotential leak of memory pointed to by 'buf' // CHECK-NEXT: categoryMemory Error // CHECK-NEXT: typeMemory leak // CHECK-NEXT: issue_context_kindfunction @@ -1716,11 +1738,11 @@ void use_function_with_leak7() { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: depth2 +// CHECK-NEXT: depth1 // CHECK-NEXT: extended_message -// CHECK-NEXT: Returned released memory via 1st parameter +// CHECK-NEXT: Returning; memory was released via 1st parameter // CHECK-NEXT: message -// CHECK-NEXT: Returned released memory via 1st parameter +// CHECK-NEXT: Returning; memory was released via 1st parameter // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: kindcontrol @@ -1779,11 +1801,11 @@ void use_function_with_leak7() { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: depth1 +// CHECK-NEXT: depth0 // CHECK-NEXT: extended_message -// CHECK-NEXT: Returned released memory via 1st parameter +// CHECK-NEXT: Returning; memory was released via 1st parameter // CHECK-NEXT: message -// CHECK-NEXT: Returned released memory via 1st parameter +// CHECK-NEXT: Returning; memory was released via 1st parameter // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: kindcontrol @@ -2353,7 +2375,7 @@ void use_function_with_leak7() { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: depth1 +// CHECK-NEXT: depth0 // CHECK-NEXT: extended_message // CHECK-NEXT: Reallocation of 1st parameter failed // CHECK-NEXT: message @@ -2403,12 +2425,12 @@ void use_function_with_leak7() { // CHECK-NEXT: // CHECK-NEXT: depth0 // CHECK-NEXT: extended_message -// CHECK-NEXT: Memory is never released; potential leak of memory pointed to by 'buf' +// CHECK-NEXT: Potential leak of memory pointed to by 'buf' // CHECK-NEXT: message -// CHECK-NEXT: Memory is never released; potential leak of memory pointed to by 'buf' +// CHECK-NEXT: Potential leak of memory pointed to by 'buf' // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: descriptionMemory is never released; potential leak of memory pointed to by 'buf' +// CHECK-NEXT: descriptionPotential leak of memory pointed to by 'buf' // CHECK-NEXT: categoryMemory Error // CHECK-NEXT: typeMemory leak // CHECK-NEXT: issue_context_kindfunction @@ -2621,7 +2643,7 @@ void use_function_with_leak7() { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: depth1 +// CHECK-NEXT: depth0 // CHECK-NEXT: extended_message // CHECK-NEXT: Returned allocated memory // CHECK-NEXT: message @@ -2671,12 +2693,12 @@ void use_function_with_leak7() { // CHECK-NEXT: // CHECK-NEXT: depth0 // CHECK-NEXT: extended_message -// CHECK-NEXT: Memory is never released; potential leak of memory pointed to by 'v' +// CHECK-NEXT: Potential leak of memory pointed to by 'v' // CHECK-NEXT: message -// CHECK-NEXT: Memory is never released; potential leak of memory pointed to by 'v' +// CHECK-NEXT: Potential leak of memory pointed to by 'v' // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: descriptionMemory is never released; potential leak of memory pointed to by 'v' +// CHECK-NEXT: descriptionPotential leak of memory pointed to by 'v' // CHECK-NEXT: categoryMemory Error // CHECK-NEXT: typeMemory leak // CHECK-NEXT: issue_context_kindfunction @@ -2833,12 +2855,12 @@ void use_function_with_leak7() { // CHECK-NEXT: // CHECK-NEXT: depth0 // CHECK-NEXT: extended_message -// CHECK-NEXT: Memory is never released; potential leak of memory pointed to by 'm' +// CHECK-NEXT: Potential leak of memory pointed to by 'm' // CHECK-NEXT: message -// CHECK-NEXT: Memory is never released; potential leak of memory pointed to by 'm' +// CHECK-NEXT: Potential leak of memory pointed to by 'm' // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: descriptionMemory is never released; potential leak of memory pointed to by 'm' +// CHECK-NEXT: descriptionPotential leak of memory pointed to by 'm' // CHECK-NEXT: categoryMemory Error // CHECK-NEXT: typeMemory leak // CHECK-NEXT: issue_context_kindfunction @@ -3038,12 +3060,12 @@ void use_function_with_leak7() { // CHECK-NEXT: // CHECK-NEXT: depth1 // CHECK-NEXT: extended_message -// CHECK-NEXT: Memory is never released; potential leak of memory pointed to by 'x' +// CHECK-NEXT: Potential leak of memory pointed to by 'x' // CHECK-NEXT: message -// CHECK-NEXT: Memory is never released; potential leak of memory pointed to by 'x' +// CHECK-NEXT: Potential leak of memory pointed to by 'x' // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: descriptionMemory is never released; potential leak of memory pointed to by 'x' +// CHECK-NEXT: descriptionPotential leak of memory pointed to by 'x' // CHECK-NEXT: categoryMemory Error // CHECK-NEXT: typeMemory leak // CHECK-NEXT: issue_context_kindfunction @@ -3243,12 +3265,12 @@ void use_function_with_leak7() { // CHECK-NEXT: // CHECK-NEXT: depth1 // CHECK-NEXT: extended_message -// CHECK-NEXT: Memory is never released; potential leak of memory pointed to by 'x' +// CHECK-NEXT: Potential leak of memory pointed to by 'x' // CHECK-NEXT: message -// CHECK-NEXT: Memory is never released; potential leak of memory pointed to by 'x' +// CHECK-NEXT: Potential leak of memory pointed to by 'x' // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: descriptionMemory is never released; potential leak of memory pointed to by 'x' +// CHECK-NEXT: descriptionPotential leak of memory pointed to by 'x' // CHECK-NEXT: categoryMemory Error // CHECK-NEXT: typeMemory leak // CHECK-NEXT: issue_context_kindfunction @@ -3545,12 +3567,12 @@ void use_function_with_leak7() { // CHECK-NEXT: // CHECK-NEXT: depth1 // CHECK-NEXT: extended_message -// CHECK-NEXT: Memory is never released; potential leak of memory pointed to by 'x' +// CHECK-NEXT: Potential leak of memory pointed to by 'x' // CHECK-NEXT: message -// CHECK-NEXT: Memory is never released; potential leak of memory pointed to by 'x' +// CHECK-NEXT: Potential leak of memory pointed to by 'x' // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: descriptionMemory is never released; potential leak of memory pointed to by 'x' +// CHECK-NEXT: descriptionPotential leak of memory pointed to by 'x' // CHECK-NEXT: categoryMemory Error // CHECK-NEXT: typeMemory leak // CHECK-NEXT: issue_context_kindfunction @@ -3847,12 +3869,12 @@ void use_function_with_leak7() { // CHECK-NEXT: // CHECK-NEXT: depth1 // CHECK-NEXT: extended_message -// CHECK-NEXT: Memory is never released; potential leak of memory pointed to by 'x' +// CHECK-NEXT: Potential leak of memory pointed to by 'x' // CHECK-NEXT: message -// CHECK-NEXT: Memory is never released; potential leak of memory pointed to by 'x' +// CHECK-NEXT: Potential leak of memory pointed to by 'x' // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: descriptionMemory is never released; potential leak of memory pointed to by 'x' +// CHECK-NEXT: descriptionPotential leak of memory pointed to by 'x' // CHECK-NEXT: categoryMemory Error // CHECK-NEXT: typeMemory leak // CHECK-NEXT: issue_context_kindfunction @@ -4052,12 +4074,12 @@ void use_function_with_leak7() { // CHECK-NEXT: // CHECK-NEXT: depth1 // CHECK-NEXT: extended_message -// CHECK-NEXT: Memory is never released; potential leak of memory pointed to by 'x' +// CHECK-NEXT: Potential leak of memory pointed to by 'x' // CHECK-NEXT: message -// CHECK-NEXT: Memory is never released; potential leak of memory pointed to by 'x' +// CHECK-NEXT: Potential leak of memory pointed to by 'x' // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: descriptionMemory is never released; potential leak of memory pointed to by 'x' +// CHECK-NEXT: descriptionPotential leak of memory pointed to by 'x' // CHECK-NEXT: categoryMemory Error // CHECK-NEXT: typeMemory leak // CHECK-NEXT: issue_context_kindfunction @@ -4257,12 +4279,12 @@ void use_function_with_leak7() { // CHECK-NEXT: // CHECK-NEXT: depth1 // CHECK-NEXT: extended_message -// CHECK-NEXT: Memory is never released; potential leak of memory pointed to by 'x' +// CHECK-NEXT: Potential leak of memory pointed to by 'x' // CHECK-NEXT: message -// CHECK-NEXT: Memory is never released; potential leak of memory pointed to by 'x' +// CHECK-NEXT: Potential leak of memory pointed to by 'x' // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: descriptionMemory is never released; potential leak of memory pointed to by 'x' +// CHECK-NEXT: descriptionPotential leak of memory pointed to by 'x' // CHECK-NEXT: categoryMemory Error // CHECK-NEXT: typeMemory leak // CHECK-NEXT: issue_context_kindfunction @@ -4441,7 +4463,7 @@ void use_function_with_leak7() { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: depth1 +// CHECK-NEXT: depth0 // CHECK-NEXT: extended_message // CHECK-NEXT: Returned allocated memory // CHECK-NEXT: message @@ -4491,12 +4513,12 @@ void use_function_with_leak7() { // CHECK-NEXT: // CHECK-NEXT: depth0 // CHECK-NEXT: extended_message -// CHECK-NEXT: Memory is never released; potential leak +// CHECK-NEXT: Potential memory leak // CHECK-NEXT: message -// CHECK-NEXT: Memory is never released; potential leak +// CHECK-NEXT: Potential memory leak // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: descriptionMemory is never released; potential leak +// CHECK-NEXT: descriptionPotential memory leak // CHECK-NEXT: categoryMemory Error // CHECK-NEXT: typeMemory leak // CHECK-NEXT: issue_context_kindfunction @@ -4509,6 +4531,506 @@ void use_function_with_leak7() { // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: path +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line179 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line179 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line179 +// CHECK-NEXT: col13 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: depth0 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Calling 'my_malloc' +// CHECK-NEXT: message +// CHECK-NEXT: Calling 'my_malloc' +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line174 +// CHECK-NEXT: col1 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: depth1 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Entered call from 'testOnlyRefferToVisibleVariables' +// CHECK-NEXT: message +// CHECK-NEXT: Entered call from 'testOnlyRefferToVisibleVariables' +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line174 +// CHECK-NEXT: col1 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line174 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line175 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line175 +// CHECK-NEXT: col5 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line175 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line175 +// CHECK-NEXT: col5 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line175 +// CHECK-NEXT: col12 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line175 +// CHECK-NEXT: col17 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line175 +// CHECK-NEXT: col12 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line175 +// CHECK-NEXT: col12 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line175 +// CHECK-NEXT: col21 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: depth1 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Memory is allocated +// CHECK-NEXT: message +// CHECK-NEXT: Memory is allocated +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line179 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line179 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line179 +// CHECK-NEXT: col13 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: depth0 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Returned allocated memory +// CHECK-NEXT: message +// CHECK-NEXT: Returned allocated memory +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line179 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line179 +// CHECK-NEXT: col11 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line180 +// CHECK-NEXT: col1 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line180 +// CHECK-NEXT: col1 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line180 +// CHECK-NEXT: col1 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: depth0 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Potential memory leak +// CHECK-NEXT: message +// CHECK-NEXT: Potential memory leak +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: descriptionPotential memory leak +// CHECK-NEXT: categoryMemory Error +// CHECK-NEXT: typeMemory leak +// CHECK-NEXT: issue_context_kindfunction +// CHECK-NEXT: issue_contexttestOnlyRefferToVisibleVariables +// CHECK-NEXT: issue_hash1 +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line180 +// CHECK-NEXT: col1 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: path +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line191 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line191 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line191 +// CHECK-NEXT: col25 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: depth0 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Calling 'my_malloc_into_struct' +// CHECK-NEXT: message +// CHECK-NEXT: Calling 'my_malloc_into_struct' +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line185 +// CHECK-NEXT: col1 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: depth1 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Entered call from 'testMyMalloc' +// CHECK-NEXT: message +// CHECK-NEXT: Entered call from 'testMyMalloc' +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line185 +// CHECK-NEXT: col1 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line185 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line186 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line186 +// CHECK-NEXT: col8 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line186 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line186 +// CHECK-NEXT: col8 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line187 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line187 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line187 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line187 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line187 +// CHECK-NEXT: col9 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line187 +// CHECK-NEXT: col14 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line187 +// CHECK-NEXT: col9 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line187 +// CHECK-NEXT: col9 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line187 +// CHECK-NEXT: col18 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: depth1 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Memory is allocated +// CHECK-NEXT: message +// CHECK-NEXT: Memory is allocated +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line191 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line191 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line191 +// CHECK-NEXT: col25 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: depth0 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Returned allocated memory +// CHECK-NEXT: message +// CHECK-NEXT: Returned allocated memory +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line191 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line191 +// CHECK-NEXT: col23 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line192 +// CHECK-NEXT: col1 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line192 +// CHECK-NEXT: col1 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line192 +// CHECK-NEXT: col1 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: depth0 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Potential memory leak +// CHECK-NEXT: message +// CHECK-NEXT: Potential memory leak +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: descriptionPotential memory leak +// CHECK-NEXT: categoryMemory Error +// CHECK-NEXT: typeMemory leak +// CHECK-NEXT: issue_context_kindfunction +// CHECK-NEXT: issue_contexttestMyMalloc +// CHECK-NEXT: issue_hash1 +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line192 +// CHECK-NEXT: col1 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: diff --git a/test/Analysis/malloc.c b/test/Analysis/malloc.c index 7790b32b078..6071d1d4358 100644 --- a/test/Analysis/malloc.c +++ b/test/Analysis/malloc.c @@ -21,7 +21,7 @@ char *fooRetPtr(); void f1() { int *p = malloc(12); - return; // expected-warning{{Memory is never released; potential leak of memory pointed to by 'p'}} + return; // expected-warning{{Potential leak of memory pointed to by 'p'}} } void f2() { @@ -46,7 +46,7 @@ void reallocNotNullPtr(unsigned sizeIn) { char *p = (char*)malloc(size); if (p) { char *q = (char*)realloc(p, sizeIn); - char x = *q; // expected-warning {{Memory is never released; potential leak of memory pointed to by 'q'}} + char x = *q; // expected-warning {{Potential leak of memory pointed to by 'q'}} } } @@ -105,7 +105,7 @@ void reallocSizeZero5() { void reallocPtrZero1() { char *r = realloc(0, 12); -} // expected-warning {{Memory is never released; potential leak of memory pointed to by 'r'}} +} // expected-warning {{Potential leak of memory pointed to by 'r'}} void reallocPtrZero2() { char *r = realloc(0, 12); @@ -122,7 +122,7 @@ void reallocRadar6337483_1() { char *buf = malloc(100); buf = (char*)realloc(buf, 0x1000000); if (!buf) { - return;// expected-warning {{Memory is never released; potential leak}} + return;// expected-warning {{Potential leak of memory pointed to by}} } free(buf); } @@ -135,7 +135,7 @@ void reallocRadar6337483_2() { } else { free(buf2); } -} // expected-warning {{Memory is never released; potential leak}} +} // expected-warning {{Potential leak of memory pointed to by}} void reallocRadar6337483_3() { char * buf = malloc(100); @@ -153,7 +153,7 @@ void reallocRadar6337483_4() { char *buf = malloc(100); char *buf2 = (char*)realloc(buf, 0x1000000); if (!buf2) { - return; // expected-warning {{Memory is never released; potential leak}} + return; // expected-warning {{Potential leak of memory pointed to by}} } else { free(buf2); } @@ -189,7 +189,7 @@ void reallocfRadar6337483_3() { void reallocfPtrZero1() { char *r = reallocf(0, 12); -} // expected-warning {{Memory is never released; potential leak}} +} // expected-warning {{Potential leak of memory pointed to by}} // This case tests that storing malloc'ed memory to a static variable which is @@ -293,7 +293,7 @@ char mallocGarbage () { // This tests that calloc() buffers need to be freed void callocNoFree () { char *buf = calloc(2,2); - return; // expected-warning{{never released}} + return; // expected-warning{{Potential leak of memory pointed to by 'buf'}} } // These test that calloc() buffers are zeroed by default @@ -312,7 +312,7 @@ char callocZeroesBad () { if (buf[1] != 0) { free(buf); // expected-warning{{never executed}} } - return result; // expected-warning{{never released}} + return result; // expected-warning{{Potential leak of memory pointed to by 'buf'}} } void nullFree() { @@ -387,12 +387,12 @@ void mallocEscapeMalloc() { int *p = malloc(12); myfoo(p); p = malloc(12); -} // expected-warning{{Memory is never released; potential leak}} +} // expected-warning{{Potential leak of memory pointed to by}} void mallocMalloc() { int *p = malloc(12); p = malloc(12); -} // expected-warning {{Memory is never released; potential leak}} +} // expected-warning {{Potential leak of memory pointed to by}} void mallocFreeMalloc() { int *p = malloc(12); @@ -451,7 +451,7 @@ void mallocFailedOrNotLeak() { if (p == 0) return; // no warning else - return; // expected-warning {{Memory is never released; potential leak}} + return; // expected-warning {{Potential leak of memory pointed to by}} } void mallocAssignment() { @@ -461,7 +461,7 @@ void mallocAssignment() { int vallocTest() { char *mem = valloc(12); - return 0; // expected-warning {{Memory is never released; potential leak}} + return 0; // expected-warning {{Potential leak of memory pointed to by}} } void vallocEscapeFreeUse() { @@ -534,7 +534,7 @@ int *testMalloc3() { void testStructLeak() { StructWithPtr St; St.memP = malloc(12); - return; // expected-warning {{Memory is never released; potential leak of memory pointed to by 'St.memP'}} + return; // expected-warning {{Potential leak of memory pointed to by 'St.memP'}} } void testElemRegion1() { @@ -584,7 +584,7 @@ struct X* RegInvalidationDetect1(struct X *s2) { struct X *px= malloc(sizeof(struct X)); px->p = 0; px = s2; - return px; // expected-warning {{Memory is never released; potential leak}} + return px; // expected-warning {{Potential leak of memory pointed to by}} } struct X* RegInvalidationGiveUp1() { @@ -598,7 +598,7 @@ int **RegInvalidationDetect2(int **pp) { int *p = malloc(12); pp = &p; pp++; - return 0;// expected-warning {{Memory is never released; potential leak}} + return 0;// expected-warning {{Potential leak of memory pointed to by}} } extern void exit(int) __attribute__ ((__noreturn__)); @@ -674,7 +674,7 @@ int *specialMallocWithStruct() { void testStrdup(const char *s, unsigned validIndex) { char *s2 = strdup(s); s2[validIndex + 1] = 'b'; -} // expected-warning {{Memory is never released; potential leak}} +} // expected-warning {{Potential leak of memory pointed to by}} int testStrndup(const char *s, unsigned validIndex, unsigned size) { char *s2 = strndup(s, size); @@ -682,7 +682,7 @@ int testStrndup(const char *s, unsigned validIndex, unsigned size) { if (s2[validIndex] != 'a') return 0; else - return 1;// expected-warning {{Memory is never released; potential leak}} + return 1;// expected-warning {{Potential leak of memory pointed to by}} } void testStrdupContentIsDefined(const char *s, unsigned validIndex) { @@ -945,7 +945,7 @@ void localStructTest() { StructWithPtr St; StructWithPtr *pSt = &St; pSt->memP = malloc(12); -} // expected-warning{{Memory is never released; potential leak}} +} // expected-warning{{Potential leak of memory pointed to by}} #ifdef __INTPTR_TYPE__ // Test double assignment through integers. @@ -1067,14 +1067,14 @@ void testPassConstPointerIndirectlyStruct() { struct HasPtr hp; hp.p = malloc(10); memcmp(&hp, &hp, sizeof(hp)); - return; // expected-warning {{Memory is never released; potential leak of memory pointed to by 'hp.p'}} + return; // expected-warning {{Potential leak of memory pointed to by 'hp.p'}} } void testPassToSystemHeaderFunctionIndirectlyStruct() { SomeStruct ss; ss.p = malloc(1); fakeSystemHeaderCall(&ss); -} // expected-warning {{Memory is never released; potential leak of memory pointed to by 'ss.p'}} +} // expected-warning {{Potential leak of memory pointed to by 'ss.p'}} int *testOffsetAllocate(size_t size) { int *memoryBlock = (int *)malloc(size + sizeof(int)); @@ -1107,7 +1107,7 @@ void testOffsetOfRegionFreedAfterFunctionCall() { int *p = malloc(sizeof(int)*2); p += 1; myfoo(p); - free(p); // no-warning + free(p); // expected-warning{{Argument to free() is offset by 4 bytes from the start of memory allocated by malloc()}} } void testFixManipulatedPointerBeforeFree() { @@ -1160,7 +1160,7 @@ void testOffsetZeroDoubleFree() { void testOffsetPassedToStrlen() { char * string = malloc(sizeof(char)*10); string += 1; - int length = strlen(string); // expected-warning {{Memory is never released; potential leak of memory pointed to by 'string'}} + int length = strlen(string); // expected-warning {{Potential leak of memory pointed to by 'string'}} } void testOffsetPassedToStrlenThenFree() { diff --git a/test/Analysis/malloc.cpp b/test/Analysis/malloc.cpp index 54efa1c2bd7..75d06d66c2c 100644 --- a/test/Analysis/malloc.cpp +++ b/test/Analysis/malloc.cpp @@ -24,12 +24,18 @@ Foo aFunction() { // they are defined in system headers and take the const pointer to the // allocated memory. (radar://11160612) // Test default parameter. -int const_ptr_and_callback_def_param(int, const char*, int n, void(*)(void*) = 0); +int const_ptr_and_callback_def_param(int, const char*, int n, void(*)(void*) = free); void r11160612_3() { char *x = (char*)malloc(12); const_ptr_and_callback_def_param(0, x, 12); } +int const_ptr_and_callback_def_param_null(int, const char*, int n, void(*)(void*) = 0); +void r11160612_no_callback() { + char *x = (char*)malloc(12); + const_ptr_and_callback_def_param_null(0, x, 12); +} // expected-warning{{leak}} + // Test member function pointer. struct CanFreeMemory { static void myFree(void*); diff --git a/test/Analysis/malloc.mm b/test/Analysis/malloc.mm index bd9d2d2a7f7..c7fe86bf0bc 100644 --- a/test/Analysis/malloc.mm +++ b/test/Analysis/malloc.mm @@ -81,7 +81,17 @@ void testRelinquished2() { void *data = malloc(42); NSData *nsdata; free(data); - [NSData dataWithBytesNoCopy:data length:42]; // expected-warning {{Attempt to free released memory}} + [NSData dataWithBytesNoCopy:data length:42]; // expected-warning {{Use of memory after it is freed}} +} + +@interface My ++ (void)param:(void *)p; +@end + +void testUseAfterFree() { + int *p = (int *)malloc(sizeof(int)); + free(p); + [My param:p]; // expected-warning{{Use of memory after it is freed}} } void testNoCopy() { diff --git a/test/Analysis/misc-ps.c b/test/Analysis/misc-ps.c index 5369ab10615..b302860a2f9 100644 --- a/test/Analysis/misc-ps.c +++ b/test/Analysis/misc-ps.c @@ -163,3 +163,15 @@ int PR14634(int x) { return !y; } + +// PR15684: If a checker generates a sink node after generating a regular node +// and no state changes between the two, graph trimming would consider the two +// the same node, forming a loop. +struct PR15684 { + void (*callback)(int); +}; +void sinkAfterRegularNode(struct PR15684 *context) { + int uninitialized; + context->callback(uninitialized); // expected-warning {{uninitialized}} +} + diff --git a/test/Analysis/new.cpp b/test/Analysis/new.cpp index 44ae9802f81..8d3eee9baa6 100644 --- a/test/Analysis/new.cpp +++ b/test/Analysis/new.cpp @@ -8,6 +8,12 @@ extern "C" void *malloc(size_t); extern "C" void free(void *); int someGlobal; + +class SomeClass { +public: + void f(int *p); +}; + void testImplicitlyDeclaredGlobalNew() { if (someGlobal != 0) return; @@ -101,6 +107,12 @@ void testCacheOut(PtrWrapper w) { new (&w.x) (int*)(0); // we cache out here; don't crash } +void testUseAfter(int *p) { + SomeClass *c = new SomeClass; + free(p); + c->f(p); // expected-warning{{Use of memory after it is freed}} + delete c; +} //-------------------------------------------------------------------- // Check for intersection with other checkers from MallocChecker.cpp @@ -126,7 +138,7 @@ void testNewDeleteNoWarn() { void testDeleteMallocked() { int *x = (int *)malloc(sizeof(int)); delete x; // FIXME: Shoud detect pointer escape and keep silent after 'delete' is modeled properly. -} // expected-warning{{Memory is never released; potential leak}} +} // expected-warning{{Potential leak of memory pointed to by 'x'}} void testDeleteOpAfterFree() { int *p = (int *)malloc(sizeof(int)); @@ -152,6 +164,12 @@ void testCustomPlacementNewAfterFree() { p = new(0, p) int; // expected-warning{{Use of memory after it is freed}} } +void testUsingThisAfterDelete() { + SomeClass *c = new SomeClass; + delete c; + c->f(0); // no-warning +} + //-------------------------------- // Incorrectly-modelled behavior //-------------------------------- diff --git a/test/Analysis/null-deref-path-notes.m b/test/Analysis/null-deref-path-notes.m index 66514544dd1..e22d520f888 100644 --- a/test/Analysis/null-deref-path-notes.m +++ b/test/Analysis/null-deref-path-notes.m @@ -457,28 +457,47 @@ void repeatedStores(int coin) { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line33 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line33 +// CHECK-NEXT: col10 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line33 +// CHECK-NEXT: col12 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line33 +// CHECK-NEXT: col12 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: // CHECK-NEXT: kindevent // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line33 -// CHECK-NEXT: col3 +// CHECK-NEXT: col12 // CHECK-NEXT: file0 // CHECK-NEXT: -// CHECK-NEXT: ranges -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line33 -// CHECK-NEXT: col3 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line33 -// CHECK-NEXT: col18 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: // CHECK-NEXT: depth0 // CHECK-NEXT: extended_message // CHECK-NEXT: Access to instance variable 'uniqueID' results in a dereference of a null pointer (loaded from variable 'self') @@ -495,7 +514,7 @@ void repeatedStores(int coin) { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line33 -// CHECK-NEXT: col3 +// CHECK-NEXT: col12 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -697,11 +716,45 @@ void repeatedStores(int coin) { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line50 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line50 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line50 +// CHECK-NEXT: col6 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line50 +// CHECK-NEXT: col6 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: // CHECK-NEXT: kindevent // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line50 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: ranges @@ -735,7 +788,7 @@ void repeatedStores(int coin) { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line50 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: diff --git a/test/Analysis/objc-boxing.m b/test/Analysis/objc-boxing.m index 16915788d63..98310b52f49 100644 --- a/test/Analysis/objc-boxing.m +++ b/test/Analysis/objc-boxing.m @@ -34,7 +34,7 @@ id constant_string() { } id dynamic_string() { - return @(strdup("boxed dynamic string")); // expected-warning{{Memory is never released; potential leak}} + return @(strdup("boxed dynamic string")); // expected-warning{{Potential memory leak}} } id const_char_pointer(int *x) { diff --git a/test/Analysis/objc-for.m b/test/Analysis/objc-for.m index 1561ef8ddf8..ef149c4b14a 100644 --- a/test/Analysis/objc-for.m +++ b/test/Analysis/objc-for.m @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -analyze -analyzer-checker=osx.cocoa.Loops,debug.ExprInspection -verify %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core,osx.cocoa.Loops,debug.ExprInspection -verify %s void clang_analyzer_eval(int); @@ -56,3 +56,15 @@ void testWithVarInFor() { clang_analyzer_eval(x != nil); // expected-warning{{UNKNOWN}} } +void testNonNil(id a, id b) { + clang_analyzer_eval(a != nil); // expected-warning{{UNKNOWN}} + for (id x in a) + clang_analyzer_eval(a != nil); // expected-warning{{TRUE}} + + if (b != nil) + return; + for (id x in b) + *(volatile int *)0 = 1; // no-warning + clang_analyzer_eval(b != nil); // expected-warning{{FALSE}} +} + diff --git a/test/Analysis/objc-string.mm b/test/Analysis/objc-string.mm new file mode 100644 index 00000000000..c67ab5e8919 --- /dev/null +++ b/test/Analysis/objc-string.mm @@ -0,0 +1,39 @@ +// RUN: %clang_cc1 -analyze -analyzer-checker=core,debug.ExprInspection -verify %s + +void clang_analyzer_eval(bool); +@class NSString; + +void sanity() { + clang_analyzer_eval(@""); // expected-warning{{TRUE}} + clang_analyzer_eval(@"abc"); // expected-warning{{TRUE}} +} + +namespace rdar13773117 { + NSString *const kConstantGlobalString = @"foo"; + NSString *globalString = @"bar"; + + extern void invalidateGlobals(); + + void testGlobals() { + clang_analyzer_eval(kConstantGlobalString); // expected-warning{{TRUE}} + clang_analyzer_eval(globalString); // expected-warning{{UNKNOWN}} + + globalString = @"baz"; + clang_analyzer_eval(globalString); // expected-warning{{TRUE}} + + invalidateGlobals(); + + clang_analyzer_eval(kConstantGlobalString); // expected-warning{{TRUE}} + clang_analyzer_eval(globalString); // expected-warning{{UNKNOWN}} + } + + NSString *returnString(NSString *input = @"garply") { + return input; + } + + void testDefaultArg() { + clang_analyzer_eval(returnString(@"")); // expected-warning{{TRUE}} + clang_analyzer_eval(returnString(0)); // expected-warning{{FALSE}} + clang_analyzer_eval(returnString()); // expected-warning{{TRUE}} + } +} diff --git a/test/Analysis/objc-subscript.m b/test/Analysis/objc-subscript.m index 324bf1c785c..ae621c98287 100644 --- a/test/Analysis/objc-subscript.m +++ b/test/Analysis/objc-subscript.m @@ -39,9 +39,9 @@ typedef unsigned int NSUInteger; // for subscripting - (id)getDoesNotRetain:(BOOL)keyed { if (keyed) - return [self[self] autorelease]; // expected-warning{{Object sent -autorelease too many times}} + return [self[self] autorelease]; // expected-warning{{Object autoreleased too many times}} else - return [self[0] autorelease]; // expected-warning{{Object sent -autorelease too many times}} + return [self[0] autorelease]; // expected-warning{{Object autoreleased too many times}} } // for subscripting diff --git a/test/Analysis/objc_invalidation.m b/test/Analysis/objc_invalidation.m index a6f5ec3f84c..6919feaccfc 100644 --- a/test/Analysis/objc_invalidation.m +++ b/test/Analysis/objc_invalidation.m @@ -322,6 +322,47 @@ extern void NSLog(NSString *format, ...) __attribute__((format(__NSString__, 1, #endif @end +@interface SomeNotInvalidatedInPartial : SomeInvalidationImplementingObject { + SomeInvalidationImplementingObject *Ivar1; + SomeInvalidationImplementingObject *Ivar2; +#if RUN_IVAR_INVALIDATION + // expected-warning@-2 {{Instance variable Ivar2 needs to be invalidated or set to nil}} +#endif +} +-(void)partialInvalidator __attribute__((annotate("objc_instance_variable_invalidator_partial"))); +-(void)partialInvalidatorCallsPartial __attribute__((annotate("objc_instance_variable_invalidator_partial"))); +@end +@implementation SomeNotInvalidatedInPartial { + SomeInvalidationImplementingObject *Ivar3; +#if RUN_IVAR_INVALIDATION + // expected-warning@-2 {{Instance variable Ivar3 needs to be invalidated or set to nil}} +#endif +} +-(void)partialInvalidator { + Ivar1 = 0; +} +-(void)partialInvalidatorCallsPartial { + [self partialInvalidator]; +} +@end + +@interface OnlyPartialDeclsBase : NSObject +-(void)partialInvalidator __attribute__((annotate("objc_instance_variable_invalidator_partial"))); +@end +@implementation OnlyPartialDeclsBase +-(void)partialInvalidator {} +@end + +@interface OnlyPartialDecls : OnlyPartialDeclsBase { + SomeInvalidationImplementingObject *Ivar1; +#if RUN_IVAR_INVALIDATION + // expected-warning@-2 {{Instance variable Ivar1 needs to be invalidated; no invalidation method is defined in the @implementation for OnlyPartialDecls}} +#endif +} +@end +@implementation OnlyPartialDecls +@end + // False negative. @interface PartialCallsFull : SomeInvalidationImplementingObject { SomeInvalidationImplementingObject *Ivar1; diff --git a/test/Analysis/operator-calls.cpp b/test/Analysis/operator-calls.cpp index 4f686e55fdf..7461d75f673 100644 --- a/test/Analysis/operator-calls.cpp +++ b/test/Analysis/operator-calls.cpp @@ -49,3 +49,39 @@ namespace UserDefinedConversions { clang_analyzer_eval(obj); // expected-warning{{TRUE}} } } + + +namespace RValues { + struct SmallOpaque { + float x; + int operator +() const { + return (int)x; + } + }; + + struct LargeOpaque { + float x[4]; + int operator +() const { + return (int)x[0]; + } + }; + + SmallOpaque getSmallOpaque() { + SmallOpaque obj; + obj.x = 1.0; + return obj; + } + + LargeOpaque getLargeOpaque() { + LargeOpaque obj = LargeOpaque(); + obj.x[0] = 1.0; + return obj; + } + + void test(int coin) { + // Force a cache-out when we try to conjure a temporary region for the operator call. + // ...then, don't crash. + clang_analyzer_eval(+(coin ? getSmallOpaque() : getSmallOpaque())); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(+(coin ? getLargeOpaque() : getLargeOpaque())); // expected-warning{{UNKNOWN}} + } +} diff --git a/test/Analysis/plist-output-alternate.m b/test/Analysis/plist-output-alternate.m index bc9e1032fca..93d0421d665 100644 --- a/test/Analysis/plist-output-alternate.m +++ b/test/Analysis/plist-output-alternate.m @@ -113,12 +113,12 @@ void rdar8331641(int x) { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line6 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line6 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -130,7 +130,7 @@ void rdar8331641(int x) { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line6 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: ranges @@ -164,7 +164,7 @@ void rdar8331641(int x) { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line6 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -256,12 +256,12 @@ void rdar8331641(int x) { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line12 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line12 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -273,7 +273,7 @@ void rdar8331641(int x) { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line12 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: ranges @@ -307,7 +307,7 @@ void rdar8331641(int x) { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line12 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -462,12 +462,12 @@ void rdar8331641(int x) { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line19 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line19 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -479,7 +479,7 @@ void rdar8331641(int x) { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line19 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: ranges @@ -513,7 +513,7 @@ void rdar8331641(int x) { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line19 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -618,11 +618,45 @@ void rdar8331641(int x) { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line24 +// CHECK-NEXT: col5 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line24 +// CHECK-NEXT: col5 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line24 +// CHECK-NEXT: col8 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line24 +// CHECK-NEXT: col8 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: // CHECK-NEXT: kindevent // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line24 -// CHECK-NEXT: col5 +// CHECK-NEXT: col8 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: ranges @@ -656,7 +690,7 @@ void rdar8331641(int x) { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line24 -// CHECK-NEXT: col5 +// CHECK-NEXT: col8 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -811,12 +845,12 @@ void rdar8331641(int x) { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line31 -// CHECK-NEXT: col5 +// CHECK-NEXT: col8 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line31 -// CHECK-NEXT: col5 +// CHECK-NEXT: col8 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -828,7 +862,7 @@ void rdar8331641(int x) { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line31 -// CHECK-NEXT: col5 +// CHECK-NEXT: col8 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: ranges @@ -862,7 +896,7 @@ void rdar8331641(int x) { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line31 -// CHECK-NEXT: col5 +// CHECK-NEXT: col8 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -988,12 +1022,12 @@ void rdar8331641(int x) { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line38 -// CHECK-NEXT: col3 +// CHECK-NEXT: col10 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line38 -// CHECK-NEXT: col3 +// CHECK-NEXT: col10 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -1005,7 +1039,7 @@ void rdar8331641(int x) { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line38 -// CHECK-NEXT: col3 +// CHECK-NEXT: col10 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: ranges @@ -1039,7 +1073,7 @@ void rdar8331641(int x) { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line38 -// CHECK-NEXT: col3 +// CHECK-NEXT: col10 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -1328,7 +1362,7 @@ void rdar8331641(int x) { // CHECK-NEXT: typeLeak // CHECK-NEXT: issue_context_kindfunction // CHECK-NEXT: issue_contextrdar8331641 -// CHECK-NEXT: issue_hash6 +// CHECK-NEXT: issue_hash2 // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line58 diff --git a/test/Analysis/plist-output.m b/test/Analysis/plist-output.m index 80ce453818d..3dfd6be2e71 100644 --- a/test/Analysis/plist-output.m +++ b/test/Analysis/plist-output.m @@ -184,6 +184,16 @@ int RDar13295437() { RDar13295437_f(sp->i); } +@interface Foo +- (int *) returnsPointer; +@end + +int testFoo(Foo *x) { + if (x) + return 1; + return *[x returnsPointer]; +} + // CHECK: diagnostics // CHECK-NEXT: // CHECK-NEXT: @@ -240,12 +250,12 @@ int RDar13295437() { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line6 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line6 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -257,7 +267,7 @@ int RDar13295437() { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line6 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: ranges @@ -291,7 +301,7 @@ int RDar13295437() { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line6 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -383,12 +393,12 @@ int RDar13295437() { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line12 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line12 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -400,7 +410,7 @@ int RDar13295437() { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line12 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: ranges @@ -434,7 +444,7 @@ int RDar13295437() { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line12 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -589,12 +599,12 @@ int RDar13295437() { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line19 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line19 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -606,7 +616,7 @@ int RDar13295437() { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line19 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: ranges @@ -640,7 +650,7 @@ int RDar13295437() { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line19 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -745,11 +755,45 @@ int RDar13295437() { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line24 +// CHECK-NEXT: col5 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line24 +// CHECK-NEXT: col5 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line24 +// CHECK-NEXT: col8 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line24 +// CHECK-NEXT: col8 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: // CHECK-NEXT: kindevent // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line24 -// CHECK-NEXT: col5 +// CHECK-NEXT: col8 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: ranges @@ -783,7 +827,7 @@ int RDar13295437() { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line24 -// CHECK-NEXT: col5 +// CHECK-NEXT: col8 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -938,12 +982,12 @@ int RDar13295437() { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line31 -// CHECK-NEXT: col5 +// CHECK-NEXT: col8 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line31 -// CHECK-NEXT: col5 +// CHECK-NEXT: col8 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -955,7 +999,7 @@ int RDar13295437() { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line31 -// CHECK-NEXT: col5 +// CHECK-NEXT: col8 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: ranges @@ -989,7 +1033,7 @@ int RDar13295437() { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line31 -// CHECK-NEXT: col5 +// CHECK-NEXT: col8 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -1115,12 +1159,12 @@ int RDar13295437() { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line38 -// CHECK-NEXT: col3 +// CHECK-NEXT: col10 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line38 -// CHECK-NEXT: col3 +// CHECK-NEXT: col10 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -1132,7 +1176,7 @@ int RDar13295437() { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line38 -// CHECK-NEXT: col3 +// CHECK-NEXT: col10 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: ranges @@ -1166,7 +1210,7 @@ int RDar13295437() { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line38 -// CHECK-NEXT: col3 +// CHECK-NEXT: col10 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -1418,12 +1462,12 @@ int RDar13295437() { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line50 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line50 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -1435,7 +1479,7 @@ int RDar13295437() { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line50 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: ranges @@ -1469,7 +1513,7 @@ int RDar13295437() { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line50 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -1830,12 +1874,12 @@ int RDar13295437() { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line77 -// CHECK-NEXT: col5 +// CHECK-NEXT: col8 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line77 -// CHECK-NEXT: col5 +// CHECK-NEXT: col8 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -1847,7 +1891,7 @@ int RDar13295437() { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line77 -// CHECK-NEXT: col5 +// CHECK-NEXT: col8 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: ranges @@ -1881,7 +1925,7 @@ int RDar13295437() { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line77 -// CHECK-NEXT: col5 +// CHECK-NEXT: col8 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -2097,7 +2141,7 @@ int RDar13295437() { // CHECK-NEXT: typeLeak // CHECK-NEXT: issue_context_kindObjective-C method // CHECK-NEXT: issue_contexttest2 -// CHECK-NEXT: issue_hash4 +// CHECK-NEXT: issue_hash2 // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line88 @@ -2358,12 +2402,12 @@ int RDar13295437() { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line98 -// CHECK-NEXT: col5 +// CHECK-NEXT: col8 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line98 -// CHECK-NEXT: col5 +// CHECK-NEXT: col8 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -2375,7 +2419,7 @@ int RDar13295437() { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line98 -// CHECK-NEXT: col5 +// CHECK-NEXT: col8 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: ranges @@ -2409,7 +2453,7 @@ int RDar13295437() { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line98 -// CHECK-NEXT: col5 +// CHECK-NEXT: col8 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -2640,11 +2684,45 @@ int RDar13295437() { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line111 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line111 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line111 +// CHECK-NEXT: col6 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line111 +// CHECK-NEXT: col6 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: // CHECK-NEXT: kindevent // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line111 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: ranges @@ -2678,7 +2756,7 @@ int RDar13295437() { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line111 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -2909,11 +2987,45 @@ int RDar13295437() { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line121 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line121 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line121 +// CHECK-NEXT: col6 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line121 +// CHECK-NEXT: col6 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: // CHECK-NEXT: kindevent // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line121 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: ranges @@ -2947,7 +3059,7 @@ int RDar13295437() { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line121 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -3251,11 +3363,45 @@ int RDar13295437() { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line130 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line130 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line130 +// CHECK-NEXT: col6 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line130 +// CHECK-NEXT: col6 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: // CHECK-NEXT: kindevent // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line130 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: ranges @@ -3289,7 +3435,7 @@ int RDar13295437() { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line130 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -3656,11 +3802,45 @@ int RDar13295437() { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line136 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line136 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line136 +// CHECK-NEXT: col6 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line136 +// CHECK-NEXT: col6 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: // CHECK-NEXT: kindevent // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line136 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: ranges @@ -3694,7 +3874,7 @@ int RDar13295437() { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line136 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -4095,17 +4275,51 @@ int RDar13295437() { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: kindevent -// CHECK-NEXT: location -// CHECK-NEXT: -// CHECK-NEXT: line145 -// CHECK-NEXT: col3 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: ranges -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line145 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line145 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line145 +// CHECK-NEXT: col6 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line145 +// CHECK-NEXT: col6 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line145 +// CHECK-NEXT: col6 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: // CHECK-NEXT: line145 // CHECK-NEXT: col4 // CHECK-NEXT: file0 @@ -4133,7 +4347,7 @@ int RDar13295437() { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line145 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -4534,11 +4748,45 @@ int RDar13295437() { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line155 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line155 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line155 +// CHECK-NEXT: col6 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line155 +// CHECK-NEXT: col6 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: // CHECK-NEXT: kindevent // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line155 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: ranges @@ -4572,6 +4820,52 @@ int RDar13295437() { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line155 +// CHECK-NEXT: col6 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: path +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line163 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line163 +// CHECK-NEXT: col8 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line163 +// CHECK-NEXT: col8 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: depth0 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Value stored to 'x' is never read +// CHECK-NEXT: message +// CHECK-NEXT: Value stored to 'x' is never read +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: descriptionValue stored to 'x' is never read +// CHECK-NEXT: categoryDead store +// CHECK-NEXT: typeDead increment +// CHECK-NEXT: issue_context_kindfunction +// CHECK-NEXT: issue_contexttest_loop_fast_enumeration +// CHECK-NEXT: issue_hash5 +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line163 // CHECK-NEXT: col3 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -4706,11 +5000,45 @@ int RDar13295437() { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line163 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line163 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line163 +// CHECK-NEXT: col5 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line163 +// CHECK-NEXT: col6 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: // CHECK-NEXT: kindevent // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line163 -// CHECK-NEXT: col3 +// CHECK-NEXT: col5 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: ranges @@ -4744,53 +5072,7 @@ int RDar13295437() { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line163 -// CHECK-NEXT: col3 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: path -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: kindevent -// CHECK-NEXT: location -// CHECK-NEXT: -// CHECK-NEXT: line163 -// CHECK-NEXT: col3 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: ranges -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line163 -// CHECK-NEXT: col8 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: line163 -// CHECK-NEXT: col8 -// CHECK-NEXT: file0 -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: depth0 -// CHECK-NEXT: extended_message -// CHECK-NEXT: Value stored to 'x' is never read -// CHECK-NEXT: message -// CHECK-NEXT: Value stored to 'x' is never read -// CHECK-NEXT: -// CHECK-NEXT: -// CHECK-NEXT: descriptionValue stored to 'x' is never read -// CHECK-NEXT: categoryDead store -// CHECK-NEXT: typeDead increment -// CHECK-NEXT: issue_context_kindfunction -// CHECK-NEXT: issue_contexttest_loop_fast_enumeration -// CHECK-NEXT: issue_hash5 -// CHECK-NEXT: location -// CHECK-NEXT: -// CHECK-NEXT: line163 -// CHECK-NEXT: col3 +// CHECK-NEXT: col5 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -4848,12 +5130,12 @@ int RDar13295437() { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line172 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: line172 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -4865,7 +5147,7 @@ int RDar13295437() { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line172 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: ranges @@ -4899,7 +5181,7 @@ int RDar13295437() { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line172 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -5012,4 +5294,244 @@ int RDar13295437() { // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: path +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line192 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line192 +// CHECK-NEXT: col4 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line192 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line192 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line192 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line192 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line192 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: depth0 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Assuming 'x' is nil +// CHECK-NEXT: message +// CHECK-NEXT: Assuming 'x' is nil +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line192 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line192 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line194 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line194 +// CHECK-NEXT: col8 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line194 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line194 +// CHECK-NEXT: col8 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line194 +// CHECK-NEXT: col12 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line194 +// CHECK-NEXT: col12 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line194 +// CHECK-NEXT: col12 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line194 +// CHECK-NEXT: col12 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line194 +// CHECK-NEXT: col12 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: depth0 +// CHECK-NEXT: extended_message +// CHECK-NEXT: 'returnsPointer' not called because the receiver is nil +// CHECK-NEXT: message +// CHECK-NEXT: 'returnsPointer' not called because the receiver is nil +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line194 +// CHECK-NEXT: col12 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line194 +// CHECK-NEXT: col12 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line194 +// CHECK-NEXT: col10 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line194 +// CHECK-NEXT: col10 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line194 +// CHECK-NEXT: col10 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line194 +// CHECK-NEXT: col10 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line194 +// CHECK-NEXT: col28 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: depth0 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Dereference of null pointer +// CHECK-NEXT: message +// CHECK-NEXT: Dereference of null pointer +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: descriptionDereference of null pointer +// CHECK-NEXT: categoryLogic error +// CHECK-NEXT: typeDereference of null pointer +// CHECK-NEXT: issue_context_kindfunction +// CHECK-NEXT: issue_contexttestFoo +// CHECK-NEXT: issue_hash3 +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line194 +// CHECK-NEXT: col10 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: // CHECK-NEXT: diff --git a/test/Analysis/pointer-to-member.cpp b/test/Analysis/pointer-to-member.cpp index 84dfe30646d..c9150e8ca59 100644 --- a/test/Analysis/pointer-to-member.cpp +++ b/test/Analysis/pointer-to-member.cpp @@ -8,6 +8,9 @@ struct A { operator MemberPointer() const { return m_ptr ? &A::m_ptr : 0; } A *m_ptr; + + A *getPtr(); + typedef A * (A::*MemberFnPointer)(void); }; void testConditionalUse() { @@ -22,6 +25,40 @@ void testConditionalUse() { clang_analyzer_eval(obj.m_ptr); // expected-warning{{FALSE}} clang_analyzer_eval(A::MemberPointer(0)); // expected-warning{{FALSE}} clang_analyzer_eval(obj); // expected-warning{{FALSE}} + + clang_analyzer_eval(&A::getPtr); // expected-warning{{TRUE}} + clang_analyzer_eval(A::MemberFnPointer(0)); // expected-warning{{FALSE}} +} + + +void testComparison() { + clang_analyzer_eval(&A::getPtr == &A::getPtr); // expected-warning{{TRUE}} + + // FIXME: Should be TRUE. + clang_analyzer_eval(&A::m_ptr == &A::m_ptr); // expected-warning{{UNKNOWN}} +} + +namespace PR15742 { + template struct A { + A (const _T1 &, const _T2 &); + }; + + typedef void *NPIdentifier; + + template class B { + public: + typedef A MethodMapMember; + }; + + class C : public B { + public: + bool Find(const NPIdentifier *, unsigned, NPIdentifier *); + }; + + void InitStaticData () { + C::MethodMapMember(0, &C::Find); // don't crash + } } // --------------- diff --git a/test/Analysis/properties.m b/test/Analysis/properties.m index 4aa91805fda..ddd0068d369 100644 --- a/test/Analysis/properties.m +++ b/test/Analysis/properties.m @@ -102,7 +102,7 @@ typedef struct _NSZone NSZone; else value = [[NSNumber alloc] initWithInteger:0]; - return [value autorelease]; // expected-warning {{Object sent -autorelease too many times}} + return [value autorelease]; // expected-warning {{Object autoreleased too many times}} } @end @@ -111,7 +111,7 @@ NSNumber* numberFromMyNumberProperty(MyNumber* aMyNumber) { NSNumber* result = aMyNumber.myNumber; - return [result autorelease]; // expected-warning {{Object sent -autorelease too many times}} + return [result autorelease]; // expected-warning {{Object autoreleased too many times}} } diff --git a/test/Analysis/reference.cpp b/test/Analysis/reference.cpp index 8dd0baf8c3c..1dabe7bc1a3 100644 --- a/test/Analysis/reference.cpp +++ b/test/Analysis/reference.cpp @@ -102,7 +102,7 @@ void testRetroactiveNullReference(int *x) { // "null reference". So the 'if' statement ought to be dead code. // However, Clang (and other compilers) don't actually check that a pointer // value is non-null in the implementation of references, so it is possible - // to produce a supposed "null reference" at runtime. The analyzer shoeuld + // to produce a supposed "null reference" at runtime. The analyzer should // still warn when it can prove such errors. int &y = *x; if (x != 0) @@ -224,3 +224,13 @@ namespace rdar11212286 { return *x; // no-warning } } + +namespace PR15694 { + class C { + bool bit : 1; + template void bar(const T &obj) {} + void foo() { + bar(bit); // don't crash + } + }; +} diff --git a/test/Analysis/retain-release-path-notes-gc.m b/test/Analysis/retain-release-path-notes-gc.m index 913714e6cdd..f74d61fa9ad 100644 --- a/test/Analysis/retain-release-path-notes-gc.m +++ b/test/Analysis/retain-release-path-notes-gc.m @@ -139,7 +139,7 @@ void retainReleaseIgnored () { // CHECK-NEXT: extended_message // CHECK-NEXT: Call to function 'CFCreateSomething' returns a Core Foundation object with a +1 retain count. Core Foundation objects are not automatically garbage collected // CHECK-NEXT: message -// CHECK-NEXT: Call to function 'CFCreateSomething' returns a Core Foundation object with a +1 retain count. Core Foundation objects are not automatically garbage collected +// CHECK-NEXT: Call to function 'CFCreateSomething' returns a Core Foundation object with a +1 retain count. Core Foundation objects are not automatically garbage collected // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: kindcontrol @@ -202,7 +202,7 @@ void retainReleaseIgnored () { // CHECK-NEXT: extended_message // CHECK-NEXT: Object leaked: object allocated and stored into 'leaked' is not referenced later in this execution path and has a retain count of +1 // CHECK-NEXT: message -// CHECK-NEXT: Object leaked: object allocated and stored into 'leaked' is not referenced later in this execution path and has a retain count of +1 +// CHECK-NEXT: Object leaked: object allocated and stored into 'leaked' is not referenced later in this execution path and has a retain count of +1 // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: descriptionPotential leak (when using garbage collection) of an object stored into 'leaked' @@ -210,7 +210,7 @@ void retainReleaseIgnored () { // CHECK-NEXT: typeLeak of object when using garbage collection // CHECK-NEXT: issue_context_kindfunction // CHECK-NEXT: issue_contextcreationViaCFCreate -// CHECK-NEXT: issue_hash2 +// CHECK-NEXT: issue_hash1 // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line44 @@ -282,7 +282,7 @@ void retainReleaseIgnored () { // CHECK-NEXT: extended_message // CHECK-NEXT: Call to function 'CFCreateSomething' returns a Core Foundation object with a +1 retain count. Core Foundation objects are not automatically garbage collected // CHECK-NEXT: message -// CHECK-NEXT: Call to function 'CFCreateSomething' returns a Core Foundation object with a +1 retain count. Core Foundation objects are not automatically garbage collected +// CHECK-NEXT: Call to function 'CFCreateSomething' returns a Core Foundation object with a +1 retain count. Core Foundation objects are not automatically garbage collected // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: kindcontrol @@ -357,7 +357,7 @@ void retainReleaseIgnored () { // CHECK-NEXT: extended_message // CHECK-NEXT: Reference count incremented. The object now has a +2 retain count // CHECK-NEXT: message -// CHECK-NEXT: Reference count incremented. The object now has a +2 retain count +// CHECK-NEXT: Reference count incremented. The object now has a +2 retain count // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: kindcontrol @@ -432,7 +432,7 @@ void retainReleaseIgnored () { // CHECK-NEXT: extended_message // CHECK-NEXT: In GC mode a call to 'CFMakeCollectable' decrements an object's retain count and registers the object with the garbage collector. An object must have a 0 retain count to be garbage collected. After this call its retain count is +1 // CHECK-NEXT: message -// CHECK-NEXT: In GC mode a call to 'CFMakeCollectable' decrements an object's retain count and registers the object with the garbage collector. An object must have a 0 retain count to be garbage collected. After this call its retain count is +1 +// CHECK-NEXT: In GC mode a call to 'CFMakeCollectable' decrements an object's retain count and registers the object with the garbage collector. An object must have a 0 retain count to be garbage collected. After this call its retain count is +1 // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: kindcontrol @@ -507,7 +507,7 @@ void retainReleaseIgnored () { // CHECK-NEXT: extended_message // CHECK-NEXT: In GC mode a call to 'NSMakeCollectable' decrements an object's retain count and registers the object with the garbage collector. Since it now has a 0 retain count the object can be automatically collected by the garbage collector // CHECK-NEXT: message -// CHECK-NEXT: In GC mode a call to 'NSMakeCollectable' decrements an object's retain count and registers the object with the garbage collector. Since it now has a 0 retain count the object can be automatically collected by the garbage collector +// CHECK-NEXT: In GC mode a call to 'NSMakeCollectable' decrements an object's retain count and registers the object with the garbage collector. Since it now has a 0 retain count the object can be automatically collected by the garbage collector // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: kindcontrol @@ -582,7 +582,7 @@ void retainReleaseIgnored () { // CHECK-NEXT: extended_message // CHECK-NEXT: Reference count incremented. The object now has a +1 retain count. The object is not eligible for garbage collection until the retain count reaches 0 again // CHECK-NEXT: message -// CHECK-NEXT: Reference count incremented. The object now has a +1 retain count. The object is not eligible for garbage collection until the retain count reaches 0 again +// CHECK-NEXT: Reference count incremented. The object now has a +1 retain count. The object is not eligible for garbage collection until the retain count reaches 0 again // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: kindcontrol @@ -645,7 +645,7 @@ void retainReleaseIgnored () { // CHECK-NEXT: extended_message // CHECK-NEXT: Object leaked: object allocated and stored into 'leaked' is not referenced later in this execution path and has a retain count of +1 // CHECK-NEXT: message -// CHECK-NEXT: Object leaked: object allocated and stored into 'leaked' is not referenced later in this execution path and has a retain count of +1 +// CHECK-NEXT: Object leaked: object allocated and stored into 'leaked' is not referenced later in this execution path and has a retain count of +1 // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: descriptionPotential leak (when using garbage collection) of an object stored into 'leaked' @@ -653,7 +653,7 @@ void retainReleaseIgnored () { // CHECK-NEXT: typeLeak of object when using garbage collection // CHECK-NEXT: issue_context_kindfunction // CHECK-NEXT: issue_contextmakeCollectable -// CHECK-NEXT: issue_hash6 +// CHECK-NEXT: issue_hash1 // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line53 @@ -725,7 +725,7 @@ void retainReleaseIgnored () { // CHECK-NEXT: extended_message // CHECK-NEXT: Method returns an Objective-C object with a +0 retain count // CHECK-NEXT: message -// CHECK-NEXT: Method returns an Objective-C object with a +0 retain count +// CHECK-NEXT: Method returns an Objective-C object with a +0 retain count // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: kindcontrol @@ -800,7 +800,7 @@ void retainReleaseIgnored () { // CHECK-NEXT: extended_message // CHECK-NEXT: In GC mode the 'retain' message has no effect // CHECK-NEXT: message -// CHECK-NEXT: In GC mode the 'retain' message has no effect +// CHECK-NEXT: In GC mode the 'retain' message has no effect // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: kindcontrol @@ -875,7 +875,7 @@ void retainReleaseIgnored () { // CHECK-NEXT: extended_message // CHECK-NEXT: In GC mode the 'release' message has no effect // CHECK-NEXT: message -// CHECK-NEXT: In GC mode the 'release' message has no effect +// CHECK-NEXT: In GC mode the 'release' message has no effect // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: kindcontrol @@ -950,7 +950,7 @@ void retainReleaseIgnored () { // CHECK-NEXT: extended_message // CHECK-NEXT: In GC mode an 'autorelease' has no effect // CHECK-NEXT: message -// CHECK-NEXT: In GC mode an 'autorelease' has no effect +// CHECK-NEXT: In GC mode an 'autorelease' has no effect // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: kindcontrol @@ -1013,7 +1013,7 @@ void retainReleaseIgnored () { // CHECK-NEXT: extended_message // CHECK-NEXT: Incorrect decrement of the reference count of an object that is not owned at this point by the caller // CHECK-NEXT: message -// CHECK-NEXT: Incorrect decrement of the reference count of an object that is not owned at this point by the caller +// CHECK-NEXT: Incorrect decrement of the reference count of an object that is not owned at this point by the caller // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: descriptionIncorrect decrement of the reference count of an object that is not owned at this point by the caller @@ -1093,7 +1093,7 @@ void retainReleaseIgnored () { // CHECK-NEXT: extended_message // CHECK-NEXT: Call to function 'CFCreateSomething' returns a Core Foundation object with a +1 retain count. Core Foundation objects are not automatically garbage collected // CHECK-NEXT: message -// CHECK-NEXT: Call to function 'CFCreateSomething' returns a Core Foundation object with a +1 retain count. Core Foundation objects are not automatically garbage collected +// CHECK-NEXT: Call to function 'CFCreateSomething' returns a Core Foundation object with a +1 retain count. Core Foundation objects are not automatically garbage collected // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: kindcontrol @@ -1168,7 +1168,7 @@ void retainReleaseIgnored () { // CHECK-NEXT: extended_message // CHECK-NEXT: Object returned to caller as an owning reference (single retain count transferred to caller) // CHECK-NEXT: message -// CHECK-NEXT: Object returned to caller as an owning reference (single retain count transferred to caller) +// CHECK-NEXT: Object returned to caller as an owning reference (single retain count transferred to caller) // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: kindevent @@ -1197,7 +1197,7 @@ void retainReleaseIgnored () { // CHECK-NEXT: extended_message // CHECK-NEXT: Object leaked: object allocated and stored into 'object' and returned from method 'getViolation' is potentially leaked when using garbage collection. Callers of this method do not expect a returned object with a +1 retain count since they expect the object to be managed by the garbage collector // CHECK-NEXT: message -// CHECK-NEXT: Object leaked: object allocated and stored into 'object' and returned from method 'getViolation' is potentially leaked when using garbage collection. Callers of this method do not expect a returned object with a +1 retain count since they expect the object to be managed by the garbage collector +// CHECK-NEXT: Object leaked: object allocated and stored into 'object' and returned from method 'getViolation' is potentially leaked when using garbage collection. Callers of this method do not expect a returned object with a +1 retain count since they expect the object to be managed by the garbage collector // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: descriptionPotential leak (when using garbage collection) of an object stored into 'object' @@ -1205,7 +1205,7 @@ void retainReleaseIgnored () { // CHECK-NEXT: typeLeak of returned object when using garbage collection // CHECK-NEXT: issue_context_kindObjective-C method // CHECK-NEXT: issue_contextgetViolation -// CHECK-NEXT: issue_hash2 +// CHECK-NEXT: issue_hash1 // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line67 @@ -1277,7 +1277,7 @@ void retainReleaseIgnored () { // CHECK-NEXT: extended_message // CHECK-NEXT: Call to function 'CFCreateSomething' returns a Core Foundation object with a +1 retain count. Core Foundation objects are not automatically garbage collected // CHECK-NEXT: message -// CHECK-NEXT: Call to function 'CFCreateSomething' returns a Core Foundation object with a +1 retain count. Core Foundation objects are not automatically garbage collected +// CHECK-NEXT: Call to function 'CFCreateSomething' returns a Core Foundation object with a +1 retain count. Core Foundation objects are not automatically garbage collected // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: kindcontrol @@ -1352,7 +1352,7 @@ void retainReleaseIgnored () { // CHECK-NEXT: extended_message // CHECK-NEXT: Object returned to caller as an owning reference (single retain count transferred to caller) // CHECK-NEXT: message -// CHECK-NEXT: Object returned to caller as an owning reference (single retain count transferred to caller) +// CHECK-NEXT: Object returned to caller as an owning reference (single retain count transferred to caller) // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: kindevent @@ -1381,7 +1381,7 @@ void retainReleaseIgnored () { // CHECK-NEXT: extended_message // CHECK-NEXT: Object leaked: object allocated and stored into 'object' and returned from method 'copyViolation' is potentially leaked when using garbage collection. Callers of this method do not expect a returned object with a +1 retain count since they expect the object to be managed by the garbage collector // CHECK-NEXT: message -// CHECK-NEXT: Object leaked: object allocated and stored into 'object' and returned from method 'copyViolation' is potentially leaked when using garbage collection. Callers of this method do not expect a returned object with a +1 retain count since they expect the object to be managed by the garbage collector +// CHECK-NEXT: Object leaked: object allocated and stored into 'object' and returned from method 'copyViolation' is potentially leaked when using garbage collection. Callers of this method do not expect a returned object with a +1 retain count since they expect the object to be managed by the garbage collector // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: descriptionPotential leak (when using garbage collection) of an object stored into 'object' @@ -1389,7 +1389,7 @@ void retainReleaseIgnored () { // CHECK-NEXT: typeLeak of returned object when using garbage collection // CHECK-NEXT: issue_context_kindObjective-C method // CHECK-NEXT: issue_contextcopyViolation -// CHECK-NEXT: issue_hash2 +// CHECK-NEXT: issue_hash1 // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line72 diff --git a/test/Analysis/retain-release-path-notes.m b/test/Analysis/retain-release-path-notes.m index 8809c573dcc..a3c681ae375 100644 --- a/test/Analysis/retain-release-path-notes.m +++ b/test/Analysis/retain-release-path-notes.m @@ -86,15 +86,15 @@ void implicitDealloc () { void overAutorelease () { id object = [[NSObject alloc] init]; // expected-note{{Method returns an Objective-C object with a +1 retain count}} - [object autorelease]; // expected-note{{Object sent -autorelease message}} - [object autorelease]; // expected-note{{Object sent -autorelease message}} - return; // expected-warning{{Object sent -autorelease too many times}} expected-note{{Object over-autoreleased: object was sent -autorelease 2 times but the object has a +1 retain count}} + [object autorelease]; // expected-note{{Object autoreleased}} + [object autorelease]; // expected-note{{Object autoreleased}} + return; // expected-warning{{Object autoreleased too many times}} expected-note{{Object was autoreleased 2 times but the object has a +1 retain count}} } void autoreleaseUnowned (Foo *foo) { id object = foo.propertyValue; // expected-note{{Property returns an Objective-C object with a +0 retain count}} - [object autorelease]; // expected-note{{Object sent -autorelease message}} - return; // expected-warning{{Object sent -autorelease too many times}} expected-note{{Object over-autoreleased: object was sent -autorelease but the object has a +0 retain count}} + [object autorelease]; // expected-note{{Object autoreleased}} + return; // expected-warning{{Object autoreleased too many times}} expected-note{{Object was autoreleased but has a +0 retain count}} } void makeCollectableIgnored () { @@ -137,7 +137,7 @@ CFTypeRef CFGetRuleViolation () { - (id)copyAutorelease { id result = [[Foo alloc] init]; // expected-note{{Method returns an Objective-C object with a +1 retain count}} - [result autorelease]; // expected-note{{Object sent -autorelease message}} + [result autorelease]; // expected-note{{Object autoreleased}} return result; // expected-warning{{Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected}} expected-note{{Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected}} } @end @@ -190,6 +190,56 @@ void testDictionary(id key, id value) { [result release]; // expected-warning{{decrement}} expected-note{{Incorrect decrement of the reference count of an object that is not owned at this point by the caller}} } +// Test that we step into the init method when the allocated object is leaked due to early escape within init. + +static int Cond; +@interface MyObj : NSObject +-(id)initX; +-(id)initY; +-(id)initZ; ++(void)test; +@end + +@implementation MyObj + +-(id)initX { + if (Cond) // expected-note {{Assuming 'Cond' is not equal to 0}} + // expected-note@-1{{Taking true branch}} + return 0; + self = [super init]; + return self; +} + +-(id)initY { + self = [super init]; //expected-note {{Method returns an Objective-C object with a +1 retain count}} + return self; +} + +-(id)initZ { + self = [super init]; + return self; +} + ++(void)test { + // initX is inlined since we explicitely mark it as interesting + id x = [[MyObj alloc] initX]; // expected-warning {{Potential leak of an object}} + // expected-note@-1 {{Method returns an Objective-C object with a +1 retain count}} + // expected-note@-2 {{Calling 'initX'}} + // expected-note@-3 {{Returning from 'initX'}} + // expected-note@-4 {{Object leaked: allocated object is not referenced later in this execution path and has a retain count of +1}} + // initI is inlined because the allocation happens within initY + id y = [[MyObj alloc] initY]; // expected-warning {{Potential leak of an object}} + // expected-note@-1 {{Calling 'initY'}} + // expected-note@-2 {{Returning from 'initY'}} + + // initZ is not inlined + id z = [[MyObj alloc] initZ]; + // expected-note@-1 {{Object leaked: allocated object is not referenced later in this execution path and has a retain count of +1}} + + [x release]; + [z release]; +} +@end // CHECK: diagnostics // CHECK-NEXT: @@ -328,7 +378,7 @@ void testDictionary(id key, id value) { // CHECK-NEXT: typeLeak // CHECK-NEXT: issue_context_kindfunction // CHECK-NEXT: issue_contextcreationViaAlloc -// CHECK-NEXT: issue_hash2 +// CHECK-NEXT: issue_hash1 // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line47 @@ -471,7 +521,7 @@ void testDictionary(id key, id value) { // CHECK-NEXT: typeLeak // CHECK-NEXT: issue_context_kindfunction // CHECK-NEXT: issue_contextcreationViaCFCreate -// CHECK-NEXT: issue_hash2 +// CHECK-NEXT: issue_hash1 // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line52 @@ -839,7 +889,7 @@ void testDictionary(id key, id value) { // CHECK-NEXT: typeLeak // CHECK-NEXT: issue_context_kindfunction // CHECK-NEXT: issue_contextacquisitionViaMethod -// CHECK-NEXT: issue_hash5 +// CHECK-NEXT: issue_hash1 // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line60 @@ -1057,7 +1107,7 @@ void testDictionary(id key, id value) { // CHECK-NEXT: typeLeak // CHECK-NEXT: issue_context_kindfunction // CHECK-NEXT: issue_contextacquisitionViaProperty -// CHECK-NEXT: issue_hash3 +// CHECK-NEXT: issue_hash1 // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line66 @@ -1275,7 +1325,7 @@ void testDictionary(id key, id value) { // CHECK-NEXT: typeLeak // CHECK-NEXT: issue_context_kindfunction // CHECK-NEXT: issue_contextacquisitionViaCFFunction -// CHECK-NEXT: issue_hash3 +// CHECK-NEXT: issue_hash1 // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line72 @@ -1856,9 +1906,9 @@ void testDictionary(id key, id value) { // CHECK-NEXT: // CHECK-NEXT: depth0 // CHECK-NEXT: extended_message -// CHECK-NEXT: Object sent -autorelease message +// CHECK-NEXT: Object autoreleased // CHECK-NEXT: message -// CHECK-NEXT: Object sent -autorelease message +// CHECK-NEXT: Object autoreleased // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: kindcontrol @@ -1931,9 +1981,9 @@ void testDictionary(id key, id value) { // CHECK-NEXT: // CHECK-NEXT: depth0 // CHECK-NEXT: extended_message -// CHECK-NEXT: Object sent -autorelease message +// CHECK-NEXT: Object autoreleased // CHECK-NEXT: message -// CHECK-NEXT: Object sent -autorelease message +// CHECK-NEXT: Object autoreleased // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: kindcontrol @@ -1994,14 +2044,14 @@ void testDictionary(id key, id value) { // CHECK-NEXT: // CHECK-NEXT: depth0 // CHECK-NEXT: extended_message -// CHECK-NEXT: Object over-autoreleased: object was sent -autorelease 2 times but the object has a +1 retain count +// CHECK-NEXT: Object was autoreleased 2 times but the object has a +1 retain count // CHECK-NEXT: message -// CHECK-NEXT: Object over-autoreleased: object was sent -autorelease 2 times but the object has a +1 retain count +// CHECK-NEXT: Object was autoreleased 2 times but the object has a +1 retain count // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: descriptionObject sent -autorelease too many times +// CHECK-NEXT: descriptionObject autoreleased too many times // CHECK-NEXT: categoryMemory (Core Foundation/Objective-C) -// CHECK-NEXT: typeObject sent -autorelease too many times +// CHECK-NEXT: typeObject autoreleased too many times // CHECK-NEXT: issue_context_kindfunction // CHECK-NEXT: issue_contextoverAutorelease // CHECK-NEXT: issue_hash4 @@ -2149,9 +2199,9 @@ void testDictionary(id key, id value) { // CHECK-NEXT: // CHECK-NEXT: depth0 // CHECK-NEXT: extended_message -// CHECK-NEXT: Object sent -autorelease message +// CHECK-NEXT: Object autoreleased // CHECK-NEXT: message -// CHECK-NEXT: Object sent -autorelease message +// CHECK-NEXT: Object autoreleased // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: kindcontrol @@ -2212,14 +2262,14 @@ void testDictionary(id key, id value) { // CHECK-NEXT: // CHECK-NEXT: depth0 // CHECK-NEXT: extended_message -// CHECK-NEXT: Object over-autoreleased: object was sent -autorelease but the object has a +0 retain count +// CHECK-NEXT: Object was autoreleased but has a +0 retain count // CHECK-NEXT: message -// CHECK-NEXT: Object over-autoreleased: object was sent -autorelease but the object has a +0 retain count +// CHECK-NEXT: Object was autoreleased but has a +0 retain count // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: descriptionObject sent -autorelease too many times +// CHECK-NEXT: descriptionObject autoreleased too many times // CHECK-NEXT: categoryMemory (Core Foundation/Objective-C) -// CHECK-NEXT: typeObject sent -autorelease too many times +// CHECK-NEXT: typeObject autoreleased too many times // CHECK-NEXT: issue_context_kindfunction // CHECK-NEXT: issue_contextautoreleaseUnowned // CHECK-NEXT: issue_hash3 @@ -2515,7 +2565,7 @@ void testDictionary(id key, id value) { // CHECK-NEXT: typeLeak // CHECK-NEXT: issue_context_kindfunction // CHECK-NEXT: issue_contextmakeCollectableIgnored -// CHECK-NEXT: issue_hash4 +// CHECK-NEXT: issue_hash1 // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line104 @@ -2883,7 +2933,7 @@ void testDictionary(id key, id value) { // CHECK-NEXT: typeLeak of returned object // CHECK-NEXT: issue_context_kindfunction // CHECK-NEXT: issue_contextCFGetRuleViolation -// CHECK-NEXT: issue_hash2 +// CHECK-NEXT: issue_hash1 // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line114 @@ -3619,7 +3669,7 @@ void testDictionary(id key, id value) { // CHECK-NEXT: typeLeak of returned object // CHECK-NEXT: issue_context_kindObjective-C method // CHECK-NEXT: issue_contextgetViolation -// CHECK-NEXT: issue_hash2 +// CHECK-NEXT: issue_hash1 // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line135 @@ -3764,9 +3814,9 @@ void testDictionary(id key, id value) { // CHECK-NEXT: // CHECK-NEXT: depth0 // CHECK-NEXT: extended_message -// CHECK-NEXT: Object sent -autorelease message +// CHECK-NEXT: Object autoreleased // CHECK-NEXT: message -// CHECK-NEXT: Object sent -autorelease message +// CHECK-NEXT: Object autoreleased // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: kindcontrol @@ -4560,4 +4610,735 @@ void testDictionary(id key, id value) { // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: path +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line225 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line225 +// CHECK-NEXT: col4 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line225 +// CHECK-NEXT: col11 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line225 +// CHECK-NEXT: col11 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line225 +// CHECK-NEXT: col11 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line225 +// CHECK-NEXT: col11 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line225 +// CHECK-NEXT: col23 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: depth0 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Method returns an Objective-C object with a +1 retain count +// CHECK-NEXT: message +// CHECK-NEXT: Method returns an Objective-C object with a +1 retain count +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line225 +// CHECK-NEXT: col11 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line225 +// CHECK-NEXT: col11 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line225 +// CHECK-NEXT: col10 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line225 +// CHECK-NEXT: col10 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line225 +// CHECK-NEXT: col10 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line225 +// CHECK-NEXT: col10 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line225 +// CHECK-NEXT: col30 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: depth0 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Calling 'initX' +// CHECK-NEXT: message +// CHECK-NEXT: Calling 'initX' +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line205 +// CHECK-NEXT: col1 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: depth1 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Entered call from 'test' +// CHECK-NEXT: message +// CHECK-NEXT: Entered call from 'test' +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line205 +// CHECK-NEXT: col1 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line205 +// CHECK-NEXT: col1 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line206 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line206 +// CHECK-NEXT: col4 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line206 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line206 +// CHECK-NEXT: col4 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line206 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line206 +// CHECK-NEXT: col10 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line206 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line206 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line206 +// CHECK-NEXT: col10 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: depth1 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Assuming 'Cond' is not equal to 0 +// CHECK-NEXT: message +// CHECK-NEXT: Assuming 'Cond' is not equal to 0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line206 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line206 +// CHECK-NEXT: col10 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line208 +// CHECK-NEXT: col5 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line208 +// CHECK-NEXT: col10 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line225 +// CHECK-NEXT: col10 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line225 +// CHECK-NEXT: col10 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line225 +// CHECK-NEXT: col30 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: depth0 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Returning from 'initX' +// CHECK-NEXT: message +// CHECK-NEXT: Returning from 'initX' +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line225 +// CHECK-NEXT: col10 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line225 +// CHECK-NEXT: col10 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line225 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line225 +// CHECK-NEXT: col4 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line225 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line225 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line225 +// CHECK-NEXT: col6 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: depth0 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Object leaked: allocated object is not referenced later in this execution path and has a retain count of +1 +// CHECK-NEXT: message +// CHECK-NEXT: Object leaked: allocated object is not referenced later in this execution path and has a retain count of +1 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: descriptionPotential leak of an object +// CHECK-NEXT: categoryMemory (Core Foundation/Objective-C) +// CHECK-NEXT: typeLeak +// CHECK-NEXT: issue_context_kindObjective-C method +// CHECK-NEXT: issue_contexttest +// CHECK-NEXT: issue_hash2 +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line225 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: path +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line225 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line225 +// CHECK-NEXT: col4 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line225 +// CHECK-NEXT: col10 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line225 +// CHECK-NEXT: col10 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line225 +// CHECK-NEXT: col10 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line225 +// CHECK-NEXT: col10 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line231 +// CHECK-NEXT: col10 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line231 +// CHECK-NEXT: col10 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line231 +// CHECK-NEXT: col10 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line231 +// CHECK-NEXT: col10 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line231 +// CHECK-NEXT: col30 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: depth0 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Calling 'initY' +// CHECK-NEXT: message +// CHECK-NEXT: Calling 'initY' +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line213 +// CHECK-NEXT: col1 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: depth1 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Entered call from 'test' +// CHECK-NEXT: message +// CHECK-NEXT: Entered call from 'test' +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line213 +// CHECK-NEXT: col1 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line213 +// CHECK-NEXT: col1 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line214 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line214 +// CHECK-NEXT: col6 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line214 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line214 +// CHECK-NEXT: col6 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line214 +// CHECK-NEXT: col10 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line214 +// CHECK-NEXT: col10 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line214 +// CHECK-NEXT: col10 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line214 +// CHECK-NEXT: col10 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line214 +// CHECK-NEXT: col21 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: depth1 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Method returns an Objective-C object with a +1 retain count +// CHECK-NEXT: message +// CHECK-NEXT: Method returns an Objective-C object with a +1 retain count +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line231 +// CHECK-NEXT: col10 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line231 +// CHECK-NEXT: col10 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line231 +// CHECK-NEXT: col30 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: depth0 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Returning from 'initY' +// CHECK-NEXT: message +// CHECK-NEXT: Returning from 'initY' +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line231 +// CHECK-NEXT: col10 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line231 +// CHECK-NEXT: col10 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line236 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line236 +// CHECK-NEXT: col4 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line236 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line236 +// CHECK-NEXT: col4 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line236 +// CHECK-NEXT: col11 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line236 +// CHECK-NEXT: col11 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line236 +// CHECK-NEXT: col11 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line236 +// CHECK-NEXT: col11 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line236 +// CHECK-NEXT: col23 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: depth0 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Object leaked: allocated object is not referenced later in this execution path and has a retain count of +1 +// CHECK-NEXT: message +// CHECK-NEXT: Object leaked: allocated object is not referenced later in this execution path and has a retain count of +1 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: descriptionPotential leak of an object +// CHECK-NEXT: categoryMemory (Core Foundation/Objective-C) +// CHECK-NEXT: typeLeak +// CHECK-NEXT: issue_context_kindObjective-C method +// CHECK-NEXT: issue_contexttest +// CHECK-NEXT: issue_hash8 +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line236 +// CHECK-NEXT: col11 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: // CHECK-NEXT: diff --git a/test/Analysis/retain-release.m b/test/Analysis/retain-release.m index 5841650a1d3..bb4a1d169d2 100644 --- a/test/Analysis/retain-release.m +++ b/test/Analysis/retain-release.m @@ -479,20 +479,20 @@ void f13_autorelease_b() { CFMutableArrayRef A = CFArrayCreateMutable(0, 10, &kCFTypeArrayCallBacks); [(id) A autorelease]; [(id) A autorelease]; -} // expected-warning{{Object sent -autorelease too many times}} +} // expected-warning{{Object autoreleased too many times}} CFMutableArrayRef f13_autorelease_c() { CFMutableArrayRef A = CFArrayCreateMutable(0, 10, &kCFTypeArrayCallBacks); [(id) A autorelease]; [(id) A autorelease]; - return A; // expected-warning{{Object sent -autorelease too many times}} + return A; // expected-warning{{Object autoreleased too many times}} } CFMutableArrayRef f13_autorelease_d() { CFMutableArrayRef A = CFArrayCreateMutable(0, 10, &kCFTypeArrayCallBacks); [(id) A autorelease]; [(id) A autorelease]; - CFMutableArrayRef B = CFArrayCreateMutable(0, 10, &kCFTypeArrayCallBacks); // expected-warning{{Object sent -autorelease too many times}} + CFMutableArrayRef B = CFArrayCreateMutable(0, 10, &kCFTypeArrayCallBacks); // expected-warning{{Object autoreleased too many times}} CFRelease(B); // no-warning while (1) {} } @@ -1983,6 +1983,45 @@ void testCustomReturnsNotRetained() { CFRelease(getCustom()); // expected-warning {{Incorrect decrement of the reference count of an object that is not owned at this point by the caller}} } +//===----------------------------------------------------------------------===// +// Don't print variables which are out of the current scope. +//===----------------------------------------------------------------------===// +@interface MyObj12706177 : NSObject +-(id)initX; ++(void)test12706177; +@end +static int Cond; +@implementation MyObj12706177 +-(id)initX { + if (Cond) + return 0; + self = [super init]; + return self; +} ++(void)test12706177 { + id x = [[MyObj12706177 alloc] initX]; //expected-warning {{Potential leak of an object}} + [x release]; +} +@end + +//===----------------------------------------------------------------------===// +// xpc_connection_set_finalizer_f +//===----------------------------------------------------------------------===// + +typedef xpc_object_t xpc_connection_t; +typedef void (*xpc_finalizer_t)(void *value); +void xpc_connection_set_context(xpc_connection_t connection, void *ctx); +void xpc_connection_set_finalizer_f(xpc_connection_t connection, + xpc_finalizer_t finalizer); +void releaseAfterXPC(void *context) { + [(NSArray *)context release]; +} + +void rdar13783514(xpc_connection_t connection) { + xpc_connection_set_context(connection, [[NSMutableArray alloc] init]); + xpc_connection_set_finalizer_f(connection, releaseAfterXPC); +} // no-warning + // CHECK: diagnostics // CHECK-NEXT: @@ -8910,9 +8949,9 @@ void testCustomReturnsNotRetained() { // CHECK-NEXT: // CHECK-NEXT: depth0 // CHECK-NEXT: extended_message -// CHECK-NEXT: Object sent -autorelease message +// CHECK-NEXT: Object autoreleased // CHECK-NEXT: message -// CHECK-NEXT: Object sent -autorelease message +// CHECK-NEXT: Object autoreleased // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: kindcontrol @@ -8985,9 +9024,9 @@ void testCustomReturnsNotRetained() { // CHECK-NEXT: // CHECK-NEXT: depth0 // CHECK-NEXT: extended_message -// CHECK-NEXT: Object sent -autorelease message +// CHECK-NEXT: Object autoreleased // CHECK-NEXT: message -// CHECK-NEXT: Object sent -autorelease message +// CHECK-NEXT: Object autoreleased // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: kindcontrol @@ -9033,14 +9072,14 @@ void testCustomReturnsNotRetained() { // CHECK-NEXT: // CHECK-NEXT: depth0 // CHECK-NEXT: extended_message -// CHECK-NEXT: Object over-autoreleased: object was sent -autorelease 2 times but the object has a +1 retain count +// CHECK-NEXT: Object was autoreleased 2 times but the object has a +1 retain count // CHECK-NEXT: message -// CHECK-NEXT: Object over-autoreleased: object was sent -autorelease 2 times but the object has a +1 retain count +// CHECK-NEXT: Object was autoreleased 2 times but the object has a +1 retain count // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: descriptionObject sent -autorelease too many times +// CHECK-NEXT: descriptionObject autoreleased too many times // CHECK-NEXT: categoryMemory (Core Foundation/Objective-C) -// CHECK-NEXT: typeObject sent -autorelease too many times +// CHECK-NEXT: typeObject autoreleased too many times // CHECK-NEXT: issue_context_kindfunction // CHECK-NEXT: issue_contextf13_autorelease_b // CHECK-NEXT: issue_hash4 @@ -9188,9 +9227,9 @@ void testCustomReturnsNotRetained() { // CHECK-NEXT: // CHECK-NEXT: depth0 // CHECK-NEXT: extended_message -// CHECK-NEXT: Object sent -autorelease message +// CHECK-NEXT: Object autoreleased // CHECK-NEXT: message -// CHECK-NEXT: Object sent -autorelease message +// CHECK-NEXT: Object autoreleased // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: kindcontrol @@ -9263,9 +9302,9 @@ void testCustomReturnsNotRetained() { // CHECK-NEXT: // CHECK-NEXT: depth0 // CHECK-NEXT: extended_message -// CHECK-NEXT: Object sent -autorelease message +// CHECK-NEXT: Object autoreleased // CHECK-NEXT: message -// CHECK-NEXT: Object sent -autorelease message +// CHECK-NEXT: Object autoreleased // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: kindcontrol @@ -9326,14 +9365,14 @@ void testCustomReturnsNotRetained() { // CHECK-NEXT: // CHECK-NEXT: depth0 // CHECK-NEXT: extended_message -// CHECK-NEXT: Object over-autoreleased: object was sent -autorelease 2 times but the object has a +0 retain count +// CHECK-NEXT: Object was autoreleased 2 times but the object has a +0 retain count // CHECK-NEXT: message -// CHECK-NEXT: Object over-autoreleased: object was sent -autorelease 2 times but the object has a +0 retain count +// CHECK-NEXT: Object was autoreleased 2 times but the object has a +0 retain count // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: descriptionObject sent -autorelease too many times +// CHECK-NEXT: descriptionObject autoreleased too many times // CHECK-NEXT: categoryMemory (Core Foundation/Objective-C) -// CHECK-NEXT: typeObject sent -autorelease too many times +// CHECK-NEXT: typeObject autoreleased too many times // CHECK-NEXT: issue_context_kindfunction // CHECK-NEXT: issue_contextf13_autorelease_c // CHECK-NEXT: issue_hash4 @@ -9481,9 +9520,9 @@ void testCustomReturnsNotRetained() { // CHECK-NEXT: // CHECK-NEXT: depth0 // CHECK-NEXT: extended_message -// CHECK-NEXT: Object sent -autorelease message +// CHECK-NEXT: Object autoreleased // CHECK-NEXT: message -// CHECK-NEXT: Object sent -autorelease message +// CHECK-NEXT: Object autoreleased // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: kindcontrol @@ -9556,9 +9595,9 @@ void testCustomReturnsNotRetained() { // CHECK-NEXT: // CHECK-NEXT: depth0 // CHECK-NEXT: extended_message -// CHECK-NEXT: Object sent -autorelease message +// CHECK-NEXT: Object autoreleased // CHECK-NEXT: message -// CHECK-NEXT: Object sent -autorelease message +// CHECK-NEXT: Object autoreleased // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: kindcontrol @@ -9653,14 +9692,14 @@ void testCustomReturnsNotRetained() { // CHECK-NEXT: // CHECK-NEXT: depth0 // CHECK-NEXT: extended_message -// CHECK-NEXT: Object over-autoreleased: object was sent -autorelease 2 times but the object has a +1 retain count +// CHECK-NEXT: Object was autoreleased 2 times but the object has a +1 retain count // CHECK-NEXT: message -// CHECK-NEXT: Object over-autoreleased: object was sent -autorelease 2 times but the object has a +1 retain count +// CHECK-NEXT: Object was autoreleased 2 times but the object has a +1 retain count // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: descriptionObject sent -autorelease too many times +// CHECK-NEXT: descriptionObject autoreleased too many times // CHECK-NEXT: categoryMemory (Core Foundation/Objective-C) -// CHECK-NEXT: typeObject sent -autorelease too many times +// CHECK-NEXT: typeObject autoreleased too many times // CHECK-NEXT: issue_context_kindfunction // CHECK-NEXT: issue_contextf13_autorelease_d // CHECK-NEXT: issue_hash4 @@ -13586,9 +13625,9 @@ void testCustomReturnsNotRetained() { // CHECK-NEXT: // CHECK-NEXT: depth0 // CHECK-NEXT: extended_message -// CHECK-NEXT: Object sent -autorelease message +// CHECK-NEXT: Object autoreleased // CHECK-NEXT: message -// CHECK-NEXT: Object sent -autorelease message +// CHECK-NEXT: Object autoreleased // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: kindcontrol @@ -20108,9 +20147,9 @@ void testCustomReturnsNotRetained() { // CHECK-NEXT: // CHECK-NEXT: depth0 // CHECK-NEXT: extended_message -// CHECK-NEXT: Object sent -autorelease message +// CHECK-NEXT: Object autoreleased // CHECK-NEXT: message -// CHECK-NEXT: Object sent -autorelease message +// CHECK-NEXT: Object autoreleased // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: kindcontrol diff --git a/test/Analysis/retain-release.mm b/test/Analysis/retain-release.mm index 47d67eae1b9..3650d887244 100644 --- a/test/Analysis/retain-release.mm +++ b/test/Analysis/retain-release.mm @@ -83,6 +83,7 @@ typedef UInt32 CFStringEncoding; enum { kCFStringEncodingMacRoman = 0, kCFStringEncodingWindowsLatin1 = 0x0500, kCFStringEncodingISOLatin1 = 0x0201, kCFStringEncodingNextStepLatin = 0x0B01, kCFStringEncodingASCII = 0x0600, kCFStringEncodingUnicode = 0x0100, kCFStringEncodingUTF8 = 0x08000100, kCFStringEncodingNonLossyASCII = 0x0BFF , kCFStringEncodingUTF16 = 0x0100, kCFStringEncodingUTF16BE = 0x10000100, kCFStringEncodingUTF16LE = 0x14000100, kCFStringEncodingUTF32 = 0x0c000100, kCFStringEncodingUTF32BE = 0x18000100, kCFStringEncodingUTF32LE = 0x1c000100 }; extern CFStringRef CFStringCreateWithCString(CFAllocatorRef alloc, const char *cStr, CFStringEncoding encoding); +extern CFStringRef CFStringCreateCopy(CFAllocatorRef alloc, CFStringRef theString); typedef double CFTimeInterval; typedef CFTimeInterval CFAbsoluteTime; extern CFAbsoluteTime CFAbsoluteTimeGetCurrent(void); @@ -269,7 +270,6 @@ extern void CGContextDrawLinearGradient(CGContextRef context, CGGradientRef gradient, CGPoint startPoint, CGPoint endPoint, CGGradientDrawingOptions options); extern CGColorSpaceRef CGColorSpaceCreateDeviceRGB(void); - //===----------------------------------------------------------------------===// // Test cases. //===----------------------------------------------------------------------===// @@ -408,3 +408,56 @@ void testCallback() { } @end +//===----------------------------------------------------------------------===// +// Don't crash on getting a null expression from CallEnter corresponding to a +// destructor. +//===----------------------------------------------------------------------===// + +template +class Holder { +public: + Holder() throw(); + ~Holder() throw() {} + X* get() const throw(); + void reset(X* p) throw(); +private: + X* ptr_; +}; + +template +inline +Holder::Holder() throw() +: ptr_(0){} + +template +inline +X* Holder::get() const throw() { + return ptr_; +} + +template +inline +void Holder::reset(X* p) throw() { + if (ptr_ != p) { + if (ptr_ != 0) { + ::CFRelease( ptr_ ); + } + ptr_ = p; + } +} + +class radar13722286 { +public: + radar13722286() {} +private: + void PrepareBitmap(); + Holder mStr; +}; + +void radar13722286::PrepareBitmap() { + if (mStr.get() != 0) { + Holder str1; + mStr.reset( CFStringCreateCopy( 0, str1.get() ) ); //expected-warning {{Potential leak of an object}} + } +} + diff --git a/test/Analysis/stack-addr-ps.cpp b/test/Analysis/stack-addr-ps.cpp index 7aefea5095c..65d757154c8 100644 --- a/test/Analysis/stack-addr-ps.cpp +++ b/test/Analysis/stack-addr-ps.cpp @@ -1,7 +1,7 @@ // RUN: %clang_cc1 -analyze -analyzer-checker=core -analyzer-store=region -verify %s -// FIXME: Only the stack-address checking in Sema catches this right now, and -// the stack analyzer doesn't handle the ImplicitCastExpr (lvalue). +typedef __INTPTR_TYPE__ intptr_t; + const int& g() { int s; return s; // expected-warning{{Address of stack memory associated with local variable 's' returned}} expected-warning{{reference to stack memory associated with local variable 's' returned}} @@ -96,3 +96,40 @@ void *radar13226577() { return p; // expected-warning {{stack memory associated with local variable 'p' returned to caller}} } +namespace rdar13296133 { + class ConvertsToBool { + public: + operator bool() const { return this; } + }; + + class ConvertsToIntptr { + public: + operator intptr_t() const { return reinterpret_cast(this); } + }; + + class ConvertsToPointer { + public: + operator const void *() const { return this; } + }; + + intptr_t returnAsNonLoc() { + ConvertsToIntptr obj; + return obj; // expected-warning{{Address of stack memory associated with local variable 'obj' returned to caller}} + } + + bool returnAsBool() { + ConvertsToBool obj; + return obj; // no-warning + } + + intptr_t returnAsNonLocViaPointer() { + ConvertsToPointer obj; + return reinterpret_cast(static_cast(obj)); // expected-warning{{Address of stack memory associated with local variable 'obj' returned to caller}} + } + + bool returnAsBoolViaPointer() { + ConvertsToPointer obj; + return obj; // no-warning + } +} + diff --git a/test/Analysis/stackaddrleak.c b/test/Analysis/stackaddrleak.c index 10564faff38..4f81f6623e5 100644 --- a/test/Analysis/stackaddrleak.c +++ b/test/Analysis/stackaddrleak.c @@ -1,5 +1,7 @@ -// RUN: %clang_cc1 -analyze -analyzer-checker=core -analyzer-store region -verify %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core -verify -std=c99 -Dbool=_Bool %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core -verify -x c++ %s +typedef __INTPTR_TYPE__ intptr_t; char const *p; void f0() { @@ -15,7 +17,7 @@ void f1() { void f2() { p = (const char *) __builtin_alloca(12); -} // expected-warning{{Address of stack memory allocated by call to alloca() on line 17 is still referred to by the global variable 'p' upon returning to the caller. This will be a dangling reference}} +} // expected-warning{{Address of stack memory allocated by call to alloca() on line 19 is still referred to by the global variable 'p' upon returning to the caller. This will be a dangling reference}} // PR 7383 - previosly the stack address checker would crash on this example // because it would attempt to do a direct load from 'pr7383_list'. @@ -32,3 +34,25 @@ void test_multi_return() { a = &x; b = &x; } // expected-warning{{Address of stack memory associated with local variable 'x' is still referred to by the global variable 'a' upon returning}} expected-warning{{Address of stack memory associated with local variable 'x' is still referred to by the global variable 'b' upon returning}} + +intptr_t returnAsNonLoc() { + int x; + return (intptr_t)&x; // expected-warning{{Address of stack memory associated with local variable 'x' returned to caller}} +} + +bool returnAsBool() { + int x; + return &x; // no-warning +} + +void assignAsNonLoc() { + extern intptr_t ip; + int x; + ip = (intptr_t)&x; +} // expected-warning{{Address of stack memory associated with local variable 'x' is still referred to by the global variable 'ip' upon returning}} + +void assignAsBool() { + extern bool b; + int x; + b = &x; +} // no-warning diff --git a/test/Analysis/string.c b/test/Analysis/string.c index 74cf33c4bc2..6cf52f7a557 100644 --- a/test/Analysis/string.c +++ b/test/Analysis/string.c @@ -1027,6 +1027,57 @@ void strncasecmp_embedded_null () { clang_analyzer_eval(strncasecmp("ab\0zz", "ab\0yy", 4) == 0); // expected-warning{{TRUE}} } +//===----------------------------------------------------------------------=== +// strsep() +//===----------------------------------------------------------------------=== + +char *strsep(char **stringp, const char *delim); + +void strsep_null_delim(char *s) { + strsep(&s, NULL); // expected-warning{{Null pointer argument in call to strsep()}} +} + +void strsep_null_search() { + strsep(NULL, ""); // expected-warning{{Null pointer argument in call to strsep()}} +} + +void strsep_return_original_pointer(char *s) { + char *original = s; + char *result = strsep(&s, ""); // no-warning + clang_analyzer_eval(original == result); // expected-warning{{TRUE}} +} + +void strsep_null_string() { + char *s = NULL; + char *result = strsep(&s, ""); // no-warning + clang_analyzer_eval(result == NULL); // expected-warning{{TRUE}} +} + +void strsep_changes_input_pointer(char *s) { + char *original = s; + strsep(&s, ""); // no-warning + clang_analyzer_eval(s == original); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(s == NULL); // expected-warning{{UNKNOWN}} + + // Check that the value is symbolic. + if (s == NULL) { + clang_analyzer_eval(s == NULL); // expected-warning{{TRUE}} + } +} + +void strsep_changes_input_string() { + char str[] = "abc"; + + clang_analyzer_eval(str[1] == 'b'); // expected-warning{{TRUE}} + + char *s = str; + strsep(&s, "b"); // no-warning + + // The real strsep will change the first delimiter it finds into a NUL + // character. For now, we just model the invalidation. + clang_analyzer_eval(str[1] == 'b'); // expected-warning{{UNKNOWN}} +} + //===----------------------------------------------------------------------=== // FIXMEs //===----------------------------------------------------------------------=== diff --git a/test/Analysis/svalbuilder-logic.c b/test/Analysis/svalbuilder-logic.c index 41d4fe21c2f..9cf3f964bc0 100644 --- a/test/Analysis/svalbuilder-logic.c +++ b/test/Analysis/svalbuilder-logic.c @@ -6,3 +6,11 @@ int SValBuilderLogicNoCrash(int *x) { return 3 - (int)(x +3); } + +// http://llvm.org/bugs/show_bug.cgi?id=15863 +// Don't crash when mixing 'bool' and 'int' in implicit comparisons to 0. +void pr15863() { + extern int getBool(); + _Bool a = getBool(); + (void)!a; // no-warning +} diff --git a/test/Analysis/taint-tester.c b/test/Analysis/taint-tester.c index 7b0ab2a5fd3..6287198eda4 100644 --- a/test/Analysis/taint-tester.c +++ b/test/Analysis/taint-tester.c @@ -1,10 +1,6 @@ // RUN: %clang_cc1 -Wno-int-to-pointer-cast -analyze -analyzer-checker=alpha.security.taint,debug.TaintTest %s -verify -#include - -int scanf(const char *restrict format, ...); -int getchar(void); -typedef __typeof(sizeof(int)) size_t; +#include "Inputs/system-header-simulator.h" #define BUFSIZE 10 int Buffer[BUFSIZE]; @@ -87,15 +83,6 @@ void getenvTest(char *home) { } } -typedef struct _FILE FILE; -extern FILE *stdin; -extern FILE *stdout; -extern FILE *stderr; -int fscanf(FILE *restrict stream, const char *restrict format, ...); -int fprintf(FILE *stream, const char *format, ...); -int fclose(FILE *stream); -FILE *fopen(const char *path, const char *mode); - int fscanfTest(void) { FILE *fp; char s[80]; diff --git a/test/Analysis/temporaries.cpp b/test/Analysis/temporaries.cpp index 32a4d3bef46..efc0825470c 100644 --- a/test/Analysis/temporaries.cpp +++ b/test/Analysis/temporaries.cpp @@ -1,4 +1,5 @@ -// RUN: %clang_cc1 -analyze -analyzer-checker=core,debug.ExprInspection -verify -w %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core,debug.ExprInspection -verify -w -std=c++03 %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core,debug.ExprInspection -verify -w -std=c++11 %s extern bool clang_analyzer_eval(bool); @@ -76,3 +77,35 @@ namespace rdar13281951 { } } +namespace compound_literals { + struct POD { + int x, y; + }; + struct HasCtor { + HasCtor(int x, int y) : x(x), y(y) {} + int x, y; + }; + struct HasDtor { + int x, y; + ~HasDtor(); + }; + struct HasCtorDtor { + HasCtorDtor(int x, int y) : x(x), y(y) {} + ~HasCtorDtor(); + int x, y; + }; + + void test() { + clang_analyzer_eval(((POD){1, 42}).y == 42); // expected-warning{{TRUE}} + clang_analyzer_eval(((HasDtor){1, 42}).y == 42); // expected-warning{{TRUE}} + +#if __cplusplus >= 201103L + clang_analyzer_eval(((HasCtor){1, 42}).y == 42); // expected-warning{{TRUE}} + + // FIXME: should be TRUE, but we don't inline the constructors of + // temporaries because we can't model their destructors yet. + clang_analyzer_eval(((HasCtorDtor){1, 42}).y == 42); // expected-warning{{UNKNOWN}} +#endif + } +} + diff --git a/test/Analysis/uninit-vals-ps.c b/test/Analysis/uninit-vals-ps.c index 09736ef1e35..ad40b15502e 100644 --- a/test/Analysis/uninit-vals-ps.c +++ b/test/Analysis/uninit-vals-ps.c @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -analyze -analyzer-checker=core -analyzer-store=region -verify %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core -analyzer-store=region -fblocks -verify %s struct FPRec { void (*my_func)(int * x); @@ -122,6 +122,8 @@ int pr4631_f1_b(void) return x; // no-warning } +// - FP when returning a void-valued expression from +// a void function...or block. void foo_radar12278788() { return; } void test_radar12278788() { return foo_radar12278788(); // no-warning @@ -134,3 +136,16 @@ int test_radar12278788_FP() { RetVoidFuncType f = foo_radar12278788_fp; return ((RetIntFuncType)f)(); //expected-warning {{Undefined or garbage value returned to caller}} } + +void rdar13665798() { + ^() { + return foo_radar12278788(); // no-warning + }(); + ^void() { + return foo_radar12278788(); // no-warning + }(); + ^int() { + RetVoidFuncType f = foo_radar12278788_fp; + return ((RetIntFuncType)f)(); //expected-warning {{Undefined or garbage value returned to caller}} + }(); +} diff --git a/test/Analysis/uninit-vals.m b/test/Analysis/uninit-vals.m index 5a97bef2003..72b67398001 100644 --- a/test/Analysis/uninit-vals.m +++ b/test/Analysis/uninit-vals.m @@ -43,6 +43,7 @@ void PR10163 (void) { typedef struct { float x; float y; + float z; } Point; typedef struct { Point origin; @@ -53,6 +54,7 @@ Point makePoint(float x, float y) { Point result; result.x = x; result.y = y; + result.z = 0.0; return result; } @@ -85,6 +87,7 @@ void PR14765_argument(Circle *testObj) { typedef struct { int x; int y; + int z; } IntPoint; typedef struct { IntPoint origin; @@ -95,6 +98,7 @@ IntPoint makeIntPoint(int x, int y) { IntPoint result; result.x = x; result.y = y; + result.z = 0; return result; } @@ -104,6 +108,7 @@ void PR14765_test_int() { clang_analyzer_eval(testObj->size == 0); // expected-warning{{TRUE}} clang_analyzer_eval(testObj->origin.x == 0); // expected-warning{{TRUE}} clang_analyzer_eval(testObj->origin.y == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(testObj->origin.z == 0); // expected-warning{{TRUE}} testObj->origin = makeIntPoint(1, 2); if (testObj->size > 0) { ; } // warning occurs here @@ -115,6 +120,7 @@ void PR14765_test_int() { clang_analyzer_eval(testObj->size == 0); // expected-warning{{UNKNOWN}} clang_analyzer_eval(testObj->origin.x == 1); // expected-warning{{TRUE}} clang_analyzer_eval(testObj->origin.y == 2); // expected-warning{{TRUE}} + clang_analyzer_eval(testObj->origin.z == 0); // expected-warning{{TRUE}} free(testObj); } @@ -127,6 +133,7 @@ void PR14765_argument_int(IntCircle *testObj) { clang_analyzer_eval(testObj->size == oldSize); // expected-warning{{TRUE}} clang_analyzer_eval(testObj->origin.x == 1); // expected-warning{{TRUE}} clang_analyzer_eval(testObj->origin.y == 2); // expected-warning{{TRUE}} + clang_analyzer_eval(testObj->origin.z == 0); // expected-warning{{TRUE}} } @@ -141,3 +148,137 @@ void rdar13292559(Circle input) { useCircle(obj); // no-warning } + +typedef struct { + int x; + int y; +} IntPoint2D; +typedef struct { + IntPoint2D origin; + int size; +} IntCircle2D; + +IntPoint2D makeIntPoint2D(int x, int y) { + IntPoint2D result; + result.x = x; + result.y = y; + return result; +} + +void testSmallStructsCopiedPerField() { + IntPoint2D a; + a.x = 0; + + IntPoint2D b = a; + extern void useInt(int); + useInt(b.x); // no-warning + useInt(b.y); // expected-warning{{uninitialized}} +} + +void testLargeStructsNotCopiedPerField() { + IntPoint a; + a.x = 0; + + IntPoint b = a; + extern void useInt(int); + useInt(b.x); // no-warning + useInt(b.y); // no-warning +} + +void testSmallStructInLargerStruct() { + IntCircle2D *testObj = calloc(sizeof(IntCircle2D), 1); + + clang_analyzer_eval(testObj->size == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(testObj->origin.x == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(testObj->origin.y == 0); // expected-warning{{TRUE}} + + testObj->origin = makeIntPoint2D(1, 2); + if (testObj->size > 0) { ; } // warning occurs here + + clang_analyzer_eval(testObj->size == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(testObj->origin.x == 1); // expected-warning{{TRUE}} + clang_analyzer_eval(testObj->origin.y == 2); // expected-warning{{TRUE}} + + free(testObj); +} + +void testCopySmallStructIntoArgument(IntCircle2D *testObj) { + int oldSize = testObj->size; + clang_analyzer_eval(testObj->size == oldSize); // expected-warning{{TRUE}} + + testObj->origin = makeIntPoint2D(1, 2); + clang_analyzer_eval(testObj->size == oldSize); // expected-warning{{TRUE}} + clang_analyzer_eval(testObj->origin.x == 1); // expected-warning{{TRUE}} + clang_analyzer_eval(testObj->origin.y == 2); // expected-warning{{TRUE}} +} + +void testSmallStructBitfields() { + struct { + int x : 4; + int y : 4; + } a, b; + + a.x = 1; + a.y = 2; + + b = a; + clang_analyzer_eval(b.x == 1); // expected-warning{{TRUE}} + clang_analyzer_eval(b.y == 2); // expected-warning{{TRUE}} +} + +void testSmallStructBitfieldsFirstUndef() { + struct { + int x : 4; + int y : 4; + } a, b; + + a.y = 2; + + b = a; + clang_analyzer_eval(b.y == 2); // expected-warning{{TRUE}} + clang_analyzer_eval(b.x == 1); // expected-warning{{garbage}} +} + +void testSmallStructBitfieldsSecondUndef() { + struct { + int x : 4; + int y : 4; + } a, b; + + a.x = 1; + + b = a; + clang_analyzer_eval(b.x == 1); // expected-warning{{TRUE}} + clang_analyzer_eval(b.y == 2); // expected-warning{{garbage}} +} + +void testSmallStructBitfieldsFirstUnnamed() { + struct { + int : 4; + int y : 4; + } a, b, c; + + a.y = 2; + + b = a; + clang_analyzer_eval(b.y == 2); // expected-warning{{TRUE}} + + b = c; + clang_analyzer_eval(b.y == 2); // expected-warning{{garbage}} +} + +void testSmallStructBitfieldsSecondUnnamed() { + struct { + int x : 4; + int : 4; + } a, b, c; + + a.x = 1; + + b = a; + clang_analyzer_eval(b.x == 1); // expected-warning{{TRUE}} + + b = c; + clang_analyzer_eval(b.x == 1); // expected-warning{{garbage}} +} + diff --git a/test/Analysis/unix-fns.c b/test/Analysis/unix-fns.c index 8daac1c81cc..46c10136ede 100644 --- a/test/Analysis/unix-fns.c +++ b/test/Analysis/unix-fns.c @@ -1662,11 +1662,45 @@ void test_inline_dispatch_once() { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line192 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line192 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line192 +// CHECK-NEXT: col6 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line192 +// CHECK-NEXT: col6 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: // CHECK-NEXT: kindevent // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line192 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: ranges @@ -1697,7 +1731,7 @@ void test_inline_dispatch_once() { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line192 -// CHECK-NEXT: col3 +// CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -2013,11 +2047,45 @@ void test_inline_dispatch_once() { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line202 +// CHECK-NEXT: col4 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line202 +// CHECK-NEXT: col4 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line202 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line202 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: // CHECK-NEXT: kindevent // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line202 -// CHECK-NEXT: col4 +// CHECK-NEXT: col7 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: ranges @@ -2048,7 +2116,7 @@ void test_inline_dispatch_once() { // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line202 -// CHECK-NEXT: col4 +// CHECK-NEXT: col7 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: diff --git a/test/CXX/basic/basic.lookup/basic.lookup.unqual/p14.cpp b/test/CXX/basic/basic.lookup/basic.lookup.unqual/p14.cpp index 7da3087e7d6..6fba9729898 100644 --- a/test/CXX/basic/basic.lookup/basic.lookup.unqual/p14.cpp +++ b/test/CXX/basic/basic.lookup/basic.lookup.unqual/p14.cpp @@ -47,3 +47,26 @@ class Other { void Other::foo(YFloat a, YFloat b) { YFloat c = a - b; } + +// +namespace Other { + void other_foo(); +} + +namespace M2 { + using namespace Other; + + extern "C" { + namespace MInner { + extern "C" { + class Bar { + void bar(); + }; + } + } + } +} + +void M2::MInner::Bar::bar() { + other_foo(); +} diff --git a/test/CXX/basic/basic.types/p10.cpp b/test/CXX/basic/basic.types/p10.cpp index 6401c29dcff..9d99a777449 100644 --- a/test/CXX/basic/basic.types/p10.cpp +++ b/test/CXX/basic/basic.types/p10.cpp @@ -1,9 +1,16 @@ // RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++1y %s -DCXX1Y struct NonLiteral { NonLiteral(); }; // A type is a literal type if it is: +// [C++1y] - void +constexpr void f() {} +#ifndef CXX1Y +// expected-error@-2 {{'void' is not a literal type}} +#endif + // - a scalar type constexpr int f1(double) { return 0; } @@ -11,7 +18,6 @@ constexpr int f1(double) { return 0; } struct S { S(); }; constexpr int f2(S &) { return 0; } -// FIXME: I'm not entirely sure whether the following is legal or not... struct BeingDefined; extern BeingDefined beingdefined; struct BeingDefined { @@ -32,13 +38,13 @@ constexpr ClassTemp classtemplate2[] = {}; // - it has a trivial destructor struct UserProvDtor { - constexpr int f(); // expected-error {{non-literal type 'UserProvDtor' cannot have constexpr members}} + constexpr int f() const; // expected-error {{non-literal type 'UserProvDtor' cannot have constexpr members}} ~UserProvDtor(); // expected-note {{has a user-provided destructor}} }; struct NonTrivDtor { constexpr NonTrivDtor(); - constexpr int f(); // expected-error {{non-literal type 'NonTrivDtor' cannot have constexpr members}} + constexpr int f() const; // expected-error {{non-literal type 'NonTrivDtor' cannot have constexpr members}} virtual ~NonTrivDtor() = default; // expected-note {{has a non-trivial destructor}} expected-note {{because it is virtual}} }; struct NonTrivDtorBase { @@ -71,11 +77,11 @@ struct CtorTemplate { }; struct CopyCtorOnly { // expected-note {{'CopyCtorOnly' is not literal because it is not an aggregate and has no constexpr constructors other than copy or move constructors}} constexpr CopyCtorOnly(CopyCtorOnly&); - constexpr int f(); // expected-error {{non-literal type 'CopyCtorOnly' cannot have constexpr members}} + constexpr int f() const; // expected-error {{non-literal type 'CopyCtorOnly' cannot have constexpr members}} }; struct MoveCtorOnly { // expected-note {{no constexpr constructors other than copy or move constructors}} constexpr MoveCtorOnly(MoveCtorOnly&&); - constexpr int f(); // expected-error {{non-literal type 'MoveCtorOnly' cannot have constexpr members}} + constexpr int f() const; // expected-error {{non-literal type 'MoveCtorOnly' cannot have constexpr members}} }; template struct CtorArg { @@ -104,7 +110,7 @@ constexpr int f(NonLitMember) {} // expected-error {{1st parameter type 'NonLitM struct NonLitBase : S { // expected-note {{base class 'S' of non-literal type}} constexpr NonLitBase(); - constexpr int f() { return 0; } // expected-error {{non-literal type 'NonLitBase' cannot have constexpr members}} + constexpr int f() const { return 0; } // expected-error {{non-literal type 'NonLitBase' cannot have constexpr members}} }; struct LitMemBase : Agg { Agg agg; @@ -117,7 +123,7 @@ struct MemberType { constexpr int f(MemberType) { return 0; } constexpr int f(MemberType) { return 0; } // expected-error {{not a literal type}} -// - an array of literal type +// - an array of literal type [C++1y] other than an array of runtime bound struct ArrGood { Agg agg[24]; double d[12]; @@ -130,3 +136,7 @@ struct ArrBad { S s[3]; // expected-note {{data member 's' of non-literal type 'S [3]'}} }; constexpr int f(ArrBad) { return 0; } // expected-error {{1st parameter type 'ArrBad' is not a literal type}} + +constexpr int arb(int n) { + int a[n]; // expected-error {{variable of non-literal type 'int [n]' cannot be defined in a constexpr function}} +} diff --git a/test/CXX/class/class.friend/p6.cpp b/test/CXX/class/class.friend/p6.cpp index 7d7a06419a7..82ca50e485d 100644 --- a/test/CXX/class/class.friend/p6.cpp +++ b/test/CXX/class/class.friend/p6.cpp @@ -1,10 +1,18 @@ -// RUN: %clang_cc1 -fsyntax-only -Wc++11-compat -verify %s +// RUN: %clang_cc1 -fsyntax-only -Wc++11-compat -verify -std=c++98 %s +// RUN: %clang_cc1 -fsyntax-only -Wc++11-compat -verify -std=c++11 %s class A { friend static class B; // expected-error {{'static' is invalid in friend declarations}} friend extern class C; // expected-error {{'extern' is invalid in friend declarations}} - friend auto class D; // expected-warning {{incompatible with C++11}} expected-error {{'auto' is invalid in friend declarations}} friend register class E; // expected-error {{'register' is invalid in friend declarations}} friend mutable class F; // expected-error {{'mutable' is invalid in friend declarations}} friend typedef class G; // expected-error {{'typedef' is invalid in friend declarations}} + friend __thread class G; // expected-error {{'__thread' is invalid in friend declarations}} + friend _Thread_local class G; // expected-error {{'_Thread_local' is invalid in friend declarations}} + friend static _Thread_local class G; // expected-error {{'static _Thread_local' is invalid in friend declarations}} +#if __cplusplus < 201103L + friend auto class D; // expected-warning {{incompatible with C++11}} expected-error {{'auto' is invalid in friend declarations}} +#else + friend thread_local class G; // expected-error {{'thread_local' is invalid in friend declarations}} +#endif }; diff --git a/test/CXX/dcl.dcl/dcl.link/p7-2.cpp b/test/CXX/dcl.dcl/dcl.link/p7-2.cpp new file mode 100644 index 00000000000..40f61c6445a --- /dev/null +++ b/test/CXX/dcl.dcl/dcl.link/p7-2.cpp @@ -0,0 +1,7 @@ +// RUN: %clang_cc1 -ast-print -o - %s | FileCheck %s + +extern "C" void f(void); +// CHECK: extern "C" void f() + +extern "C" void v; +// CHECK: extern "C" void v diff --git a/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p1.cpp b/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p1.cpp index a3a964a1ca3..122a400d9b4 100644 --- a/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p1.cpp +++ b/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p1.cpp @@ -71,7 +71,7 @@ struct ConstexprDtor { template constexpr T ft(T t) { return t; } template T gt(T t) { return t; } struct S { - template constexpr T f(); + template constexpr T f(); // expected-warning {{C++1y}} template T g() const; }; @@ -81,7 +81,7 @@ template <> char ft(char c) { return c; } // expected-note {{previous}} template <> constexpr char ft(char nl); // expected-error {{constexpr declaration of 'ft' follows non-constexpr declaration}} template <> constexpr int gt(int nl) { return nl; } template <> notlit S::f() const { return notlit(); } -template <> constexpr int S::g() { return 0; } // expected-note {{previous}} +template <> constexpr int S::g() { return 0; } // expected-note {{previous}} expected-warning {{C++1y}} template <> int S::g() const; // expected-error {{non-constexpr declaration of 'g' follows constexpr declaration}} // specializations can drop the 'constexpr' but not the implied 'const'. template <> char S::g() { return 0; } // expected-error {{no function template matches}} diff --git a/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp b/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp index cafdd635518..4393727c193 100644 --- a/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp +++ b/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp @@ -1,4 +1,5 @@ -// RUN: %clang_cc1 -verify -std=c++11 %s +// RUN: %clang_cc1 -verify -fcxx-exceptions -triple=x86_64-linux-gnu -std=c++11 -Werror=c++1y-extensions %s +// RUN: %clang_cc1 -verify -fcxx-exceptions -triple=x86_64-linux-gnu -std=c++1y -DCXX1Y %s namespace N { typedef char C; @@ -8,7 +9,7 @@ namespace M { typedef double D; } -struct NonLiteral { // expected-note 2{{no constexpr constructors}} +struct NonLiteral { // expected-note 3{{no constexpr constructors}} NonLiteral() {} NonLiteral(int) {} }; @@ -28,45 +29,53 @@ struct SS : S { // constraints: struct T : SS, NonLiteral { // expected-note {{base class 'NonLiteral' of non-literal type}} constexpr T(); - constexpr int f(); // expected-error {{non-literal type 'T' cannot have constexpr members}} + constexpr int f() const; // expected-error {{non-literal type 'T' cannot have constexpr members}} // - it shall not be virtual; - virtual constexpr int ExplicitlyVirtual() { return 0; } // expected-error {{virtual function cannot be constexpr}} + virtual constexpr int ExplicitlyVirtual() const { return 0; } // expected-error {{virtual function cannot be constexpr}} - constexpr int ImplicitlyVirtual() { return 0; } // expected-error {{virtual function cannot be constexpr}} + constexpr int ImplicitlyVirtual() const { return 0; } // expected-error {{virtual function cannot be constexpr}} // - its return type shall be a literal type; - constexpr NonLiteral NonLiteralReturn() { return {}; } // expected-error {{constexpr function's return type 'NonLiteral' is not a literal type}} - constexpr void VoidReturn() { return; } // expected-error {{constexpr function's return type 'void' is not a literal type}} + constexpr NonLiteral NonLiteralReturn() const { return {}; } // expected-error {{constexpr function's return type 'NonLiteral' is not a literal type}} + constexpr void VoidReturn() const { return; } +#ifndef CXX1Y + // expected-error@-2 {{constexpr function's return type 'void' is not a literal type}} +#endif constexpr ~T(); // expected-error {{destructor cannot be marked constexpr}} - typedef NonLiteral F(); + typedef NonLiteral F() const; constexpr F NonLiteralReturn2; // ok until definition // - each of its parameter types shall be a literal type; - constexpr int NonLiteralParam(NonLiteral) { return 0; } // expected-error {{constexpr function's 1st parameter type 'NonLiteral' is not a literal type}} - typedef int G(NonLiteral); + constexpr int NonLiteralParam(NonLiteral) const { return 0; } // expected-error {{constexpr function's 1st parameter type 'NonLiteral' is not a literal type}} + typedef int G(NonLiteral) const; constexpr G NonLiteralParam2; // ok until definition // - its function-body shall be = delete, = default, - constexpr int Deleted() = delete; - // It's not possible for the function-body to legally be "= default" here. + constexpr int Deleted() const = delete; + // It's not possible for the function-body to legally be "= default" here + // (that is, for a non-constructor function) in C++11. // Other than constructors, only the copy- and move-assignment operators and // destructor can be defaulted. Destructors can't be constexpr since they // don't have a literal return type. Defaulted assignment operators can't be // constexpr since they can't be const. - constexpr T &operator=(const T&) = default; // expected-error {{an explicitly-defaulted copy assignment operator may not have 'const', 'constexpr' or 'volatile' qualifiers}} + constexpr T &operator=(const T&) = default; +#ifndef CXX1Y + // expected-error@-2 {{an explicitly-defaulted copy assignment operator may not have 'const', 'constexpr' or 'volatile' qualifiers}} + // expected-warning@-3 {{C++1y}} +#endif }; struct U { - constexpr U SelfReturn(); - constexpr int SelfParam(U); + constexpr U SelfReturn() const; + constexpr int SelfParam(U) const; }; struct V : virtual U { // expected-note {{here}} - constexpr int F() { return 0; } // expected-error {{constexpr member function not allowed in struct with virtual base class}} + constexpr int F() const { return 0; } // expected-error {{constexpr member function not allowed in struct with virtual base class}} }; -// or a compound-statememt that contains only -constexpr int AllowedStmts() { +// or a compound-statememt that contains only [CXX11] +constexpr int AllowedStmtsCXX11() { // - null statements ; @@ -91,34 +100,118 @@ constexpr int AllowedStmts() { // - and exactly one return statement return sizeof(K) + sizeof(C) + sizeof(K); } + +// or a compound-statement that does not contain [CXX1Y] +constexpr int DisallowedStmtsCXX1Y_1() { + // - an asm-definition + asm("int3"); // expected-error {{statement not allowed in constexpr function}} + return 0; +} +constexpr int DisallowedStmtsCXX1Y_2() { + // - a goto statement + goto x; // expected-error {{statement not allowed in constexpr function}} +x: + return 0; +} +constexpr int DisallowedStmtsCXX1Y_3() { + // - a try-block, + try {} catch (...) {} // expected-error {{statement not allowed in constexpr function}} + return 0; +} +constexpr int DisallowedStmtsCXX1Y_4() { + // - a definition of a variable of non-literal type + NonLiteral nl; // expected-error {{variable of non-literal type 'NonLiteral' cannot be defined in a constexpr function}} + return 0; +} +constexpr int DisallowedStmtsCXX1Y_5() { + // - a definition of a variable of static storage duration + static constexpr int n = 123; // expected-error {{static variable not permitted in a constexpr function}} + return n; +} +constexpr int DisallowedStmtsCXX1Y_6() { + // - a definition of a variable of thread storage duration + thread_local constexpr int n = 123; // expected-error {{thread_local variable not permitted in a constexpr function}} + return n; +} +constexpr int DisallowedStmtsCXX1Y_7() { + // - a definition of a variable for which no initialization is performed + int n; // expected-error {{variables defined in a constexpr function must be initialized}} + return 0; +} + constexpr int ForStmt() { - for (int n = 0; n < 10; ++n) // expected-error {{statement not allowed in constexpr function}} + for (int n = 0; n < 10; ++n) +#ifndef CXX1Y + // expected-error@-2 {{statement not allowed in constexpr function}} +#endif return 0; } constexpr int VarDecl() { - constexpr int a = 0; // expected-error {{variables cannot be declared in a constexpr function}} + int a = 0; +#ifndef CXX1Y + // expected-error@-2 {{variable declaration in a constexpr function is a C++1y extension}} +#endif + return 0; +} +constexpr int ConstexprVarDecl() { + constexpr int a = 0; +#ifndef CXX1Y + // expected-error@-2 {{variable declaration in a constexpr function is a C++1y extension}} +#endif return 0; } +constexpr int VarWithCtorDecl() { + Literal a; +#ifndef CXX1Y + // expected-error@-2 {{variable declaration in a constexpr function is a C++1y extension}} +#endif + return 0; +} +NonLiteral nl; +constexpr NonLiteral &ExternNonLiteralVarDecl() { + extern NonLiteral nl; +#ifndef CXX1Y + // expected-error@-2 {{variable declaration in a constexpr function is a C++1y extension}} +#endif + return nl; +} +static_assert(&ExternNonLiteralVarDecl() == &nl, ""); constexpr int FuncDecl() { - constexpr int ForwardDecl(int); // expected-error {{statement not allowed in constexpr function}} + constexpr int ForwardDecl(int); +#ifndef CXX1Y + // expected-error@-2 {{use of this statement in a constexpr function is a C++1y extension}} +#endif return ForwardDecl(42); } constexpr int ClassDecl1() { - typedef struct { } S1; // expected-error {{types cannot be defined in a constexpr function}} + typedef struct { } S1; +#ifndef CXX1Y + // expected-error@-2 {{type definition in a constexpr function is a C++1y extension}} +#endif return 0; } constexpr int ClassDecl2() { - using S2 = struct { }; // expected-error {{types cannot be defined in a constexpr function}} + using S2 = struct { }; +#ifndef CXX1Y + // expected-error@-2 {{type definition in a constexpr function is a C++1y extension}} +#endif return 0; } constexpr int ClassDecl3() { - struct S3 { }; // expected-error {{types cannot be defined in a constexpr function}} + struct S3 { }; +#ifndef CXX1Y + // expected-error@-2 {{type definition in a constexpr function is a C++1y extension}} +#endif return 0; } constexpr int NoReturn() {} // expected-error {{no return statement in constexpr function}} constexpr int MultiReturn() { - return 0; // expected-note {{return statement}} - return 0; // expected-error {{multiple return statements in constexpr function}} + return 0; + return 0; +#ifndef CXX1Y + // expected-error@-2 {{multiple return statements in constexpr function}} + // expected-note@-4 {{return statement}} +#endif } // - every constructor call and implicit conversion used in initializing the @@ -137,3 +230,60 @@ namespace DR1364 { return kGlobal; // expected-note {{read of non-const}} } } + +namespace rdar13584715 { + typedef __PTRDIFF_TYPE__ ptrdiff_t; + + template struct X { + static T value() {}; + }; + + void foo(ptrdiff_t id) { + switch (id) { + case reinterpret_cast(&X::value): // expected-error{{case value is not a constant expression}} \ + // expected-note{{reinterpret_cast is not allowed in a constant expression}} + break; + } + } +} + +namespace std_example { + constexpr int square(int x) { + return x * x; + } + constexpr long long_max() { + return 2147483647; + } + constexpr int abs(int x) { + if (x < 0) +#ifndef CXX1Y + // expected-error@-2 {{C++1y}} +#endif + x = -x; + return x; + } + constexpr int first(int n) { + static int value = n; // expected-error {{static variable not permitted}} + return value; + } + constexpr int uninit() { + int a; // expected-error {{must be initialized}} + return a; + } + constexpr int prev(int x) { + return --x; + } +#ifndef CXX1Y + // expected-error@-4 {{never produces a constant expression}} + // expected-note@-4 {{subexpression}} +#endif + constexpr int g(int x, int n) { + int r = 1; + while (--n > 0) r *= x; + return r; + } +#ifndef CXX1Y + // expected-error@-5 {{C++1y}} + // expected-error@-5 {{statement not allowed}} +#endif +} diff --git a/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p4.cpp b/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p4.cpp index ad156c8ded9..8a4fa42f002 100644 --- a/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p4.cpp +++ b/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p4.cpp @@ -1,4 +1,5 @@ -// RUN: %clang_cc1 -verify -std=c++11 -fcxx-exceptions %s +// RUN: %clang_cc1 -verify -std=c++11 -fcxx-exceptions -Werror=c++1y-extensions %s +// RUN: %clang_cc1 -verify -std=c++1y -fcxx-exceptions -DCXX1Y %s namespace N { typedef char C; @@ -82,26 +83,47 @@ struct V { } constexpr V(int(&)[1]) { - for (int n = 0; n < 10; ++n) // expected-error {{statement not allowed in constexpr constructor}} + for (int n = 0; n < 10; ++n) /**/; +#ifndef CXX1Y + // expected-error@-3 {{statement not allowed in constexpr constructor}} +#endif } constexpr V(int(&)[2]) { - constexpr int a = 0; // expected-error {{variables cannot be declared in a constexpr constructor}} + constexpr int a = 0; +#ifndef CXX1Y + // expected-error@-2 {{variable declaration in a constexpr constructor is a C++1y extension}} +#endif } constexpr V(int(&)[3]) { - constexpr int ForwardDecl(int); // expected-error {{statement not allowed in constexpr constructor}} + constexpr int ForwardDecl(int); +#ifndef CXX1Y + // expected-error@-2 {{use of this statement in a constexpr constructor is a C++1y extension}} +#endif } constexpr V(int(&)[4]) { - typedef struct { } S1; // expected-error {{types cannot be defined in a constexpr constructor}} + typedef struct { } S1; +#ifndef CXX1Y + // expected-error@-2 {{type definition in a constexpr constructor is a C++1y extension}} +#endif } constexpr V(int(&)[5]) { - using S2 = struct { }; // expected-error {{types cannot be defined in a constexpr constructor}} + using S2 = struct { }; +#ifndef CXX1Y + // expected-error@-2 {{type definition in a constexpr constructor is a C++1y extension}} +#endif } constexpr V(int(&)[6]) { - struct S3 { }; // expected-error {{types cannot be defined in a constexpr constructor}} + struct S3 { }; +#ifndef CXX1Y + // expected-error@-2 {{type definition in a constexpr constructor is a C++1y extension}} +#endif } constexpr V(int(&)[7]) { - return; // expected-error {{statement not allowed in constexpr constructor}} + return; +#ifndef CXX1Y + // expected-error@-2 {{use of this statement in a constexpr constructor is a C++1y extension}} +#endif } }; diff --git a/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p5.cpp b/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p5.cpp index bca73ee85f6..5e40f69d77b 100644 --- a/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p5.cpp +++ b/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p5.cpp @@ -102,7 +102,7 @@ X x = cmin(X(), X()); // ok, not constexpr template struct Y { constexpr Y() {} - constexpr int get() { return T(); } + constexpr int get() { return T(); } // expected-warning {{C++1y}} }; struct Z { operator int(); }; diff --git a/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p6.cpp b/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p6.cpp index 1a6dc9ecfb5..bb7f7ac326e 100644 --- a/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p6.cpp +++ b/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p6.cpp @@ -24,7 +24,7 @@ struct S { struct T {}; template struct ImplicitVirtualFromDependentBase : T { - constexpr int ImplicitlyVirtual() { return 0; } + constexpr int ImplicitlyVirtual() const { return 0; } }; constexpr int a = ImplicitVirtualFromDependentBase().ImplicitlyVirtual(); // expected-error {{constant expression}} expected-note {{cannot evaluate virtual function call}} @@ -32,7 +32,7 @@ constexpr int b = ImplicitVirtualFromDependentBase().ImplicitlyVirtual(); // constexpr int c = ImplicitVirtualFromDependentBase().ImplicitVirtualFromDependentBase::ImplicitlyVirtual(); template struct ConstexprMember { - constexpr R F() { return 0; } + constexpr R F() const { return 0; } }; constexpr int d = ConstexprMember().F(); // ok constexpr int e = ConstexprMember().F(); // expected-error {{constant expression}} diff --git a/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p8.cpp b/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p8.cpp index 344f8ce8c48..40aa600ba1f 100644 --- a/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p8.cpp +++ b/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p8.cpp @@ -3,13 +3,13 @@ using size_t = decltype(sizeof(int)); struct S { - constexpr int f(); + constexpr int f(); // expected-warning {{C++1y}} constexpr int g() const; - constexpr int h(); + constexpr int h(); // expected-warning {{C++1y}} int h(); static constexpr int Sf(); /*static*/ constexpr void *operator new(size_t) noexcept; - template constexpr T tm(); + template constexpr T tm(); // expected-warning {{C++1y}} template static constexpr T ts(); }; @@ -26,12 +26,12 @@ void f(const S &s) { } constexpr int S::f() const { return 0; } -constexpr int S::g() { return 1; } -constexpr int S::h() { return 0; } +constexpr int S::g() { return 1; } // expected-warning {{C++1y}} +constexpr int S::h() { return 0; } // expected-warning {{C++1y}} int S::h() { return 0; } constexpr int S::Sf() { return 2; } constexpr void *S::operator new(size_t) noexcept { return 0; } -template constexpr T S::tm() { return T(); } +template constexpr T S::tm() { return T(); } // expected-warning {{C++1y}} template constexpr T S::ts() { return T(); } namespace std_example { @@ -39,7 +39,7 @@ namespace std_example { class debug_flag { // expected-note {{not an aggregate and has no constexpr constructors}} public: explicit debug_flag(bool); - constexpr bool is_on(); // expected-error {{non-literal type 'std_example::debug_flag' cannot have constexpr members}} + constexpr bool is_on() const; // expected-error {{non-literal type 'std_example::debug_flag' cannot have constexpr members}} private: bool flag; }; diff --git a/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p2.cpp b/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p2.cpp index a385aa91329..83b12d4b256 100644 --- a/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p2.cpp +++ b/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p2.cpp @@ -7,7 +7,7 @@ struct S { // Note, this is not permitted: conversion-declarator cannot have a trailing return type. // FIXME: don't issue the second diagnostic for this. - operator auto(*)()->int(); // expected-error{{'auto' not allowed here}} expected-error {{C++ requires a type specifier}} + operator auto(*)()->int(); // expected-error{{'auto' not allowed in conversion function type}} expected-error {{C++ requires a type specifier}} }; typedef auto Fun(int a) -> decltype(a + a); diff --git a/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p3-1y.cpp b/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p3-1y.cpp new file mode 100644 index 00000000000..39c547b9aec --- /dev/null +++ b/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p3-1y.cpp @@ -0,0 +1,74 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++1y -DCXX1Y +// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++11 -Wno-c++1y-extensions + +// FIXME: This is in p11 (?) in C++1y. +void f() { + decltype(auto) a = a; // expected-error{{variable 'a' declared with 'auto' type cannot appear in its own initializer}} + if (decltype(auto) b = b) {} // expected-error {{variable 'b' declared with 'auto' type cannot appear in its own initializer}} + decltype(auto) c = ({ decltype(auto) d = c; 0; }); // expected-error {{variable 'c' declared with 'auto' type cannot appear in its own initializer}} +} + +void g() { + decltype(auto) a; // expected-error{{declaration of variable 'a' with type 'decltype(auto)' requires an initializer}} + + decltype(auto) *b; // expected-error{{cannot form pointer to 'decltype(auto)'}} expected-error{{declaration of variable 'b' with type 'decltype(auto) *' requires an initializer}} + + if (decltype(auto) b) {} // expected-error {{must have an initializer}} + for (;decltype(auto) b;) {} // expected-error {{must have an initializer}} + while (decltype(auto) b) {} // expected-error {{must have an initializer}} + if (decltype(auto) b = true) { (void)b; } +} + +decltype(auto) n(1,2,3); // expected-error{{initializer for variable 'n' with type 'decltype(auto)' contains multiple expressions}} + +namespace N +{ + // All of these are references, because a string literal is an lvalue. + decltype(auto) a = "const char (&)[19]", b = a, c = (a); +} + +void h() { + decltype(auto) b = 42ULL; + + for (decltype(auto) c = 0; c < b; ++c) { + } +} + +template struct same; +template struct same {}; + +void i() { + decltype(auto) x = 5; + decltype(auto) int r; // expected-error {{cannot combine with previous 'decltype(auto)' declaration specifier}} expected-error {{requires an initializer}} +} + +namespace p3_example { + template struct is_same_impl { + static const bool value = false; + }; + template struct is_same_impl { + static const bool value = true; + }; + template constexpr bool is_same() { + return is_same_impl::value; + } + + auto x = 5; + const auto *v = &x, u = 6; + static auto y = 0.0; + auto int r; // expected-warning {{storage class}} expected-error {{file-scope}} + + static_assert(is_same(), ""); + static_assert(is_same(), ""); + static_assert(is_same(), ""); + static_assert(is_same(), ""); + +#ifdef CXX1Y + auto f() -> int; + auto g() { return 0.0; } + auto h(); + + static_assert(is_same(), ""); + static_assert(is_same(), ""); +#endif +} diff --git a/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p5.cpp b/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p5.cpp index 7499829185e..0cdf3c6e053 100644 --- a/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p5.cpp +++ b/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p5.cpp @@ -11,7 +11,7 @@ struct S { friend auto; // expected-error{{'auto' not allowed in non-static struct member}} - operator auto(); // expected-error{{'auto' not allowed here}} + operator auto(); // expected-error{{'auto' not allowed in conversion function type}} }; // PR 9278: auto is not allowed in typedefs, except with a trailing return type. diff --git a/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p6-1y.cpp b/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p6-1y.cpp new file mode 100644 index 00000000000..66085eda3d7 --- /dev/null +++ b/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p6-1y.cpp @@ -0,0 +1,97 @@ +// RUN: %clang_cc1 -verify -std=c++1y %s + +namespace std { + template struct initializer_list { + const T *p; + unsigned long n; + initializer_list(const T *p, unsigned long n); + }; +} + +// FIXME: This may not be p6 in C++1y; N3638 isn't very clear whether paragraphs +// were added. It might be p8? + +int i; +int &&f(); + +using Int = int; +using IntLRef = int&; +using IntRRef = int&&; +using InitListInt = std::initializer_list; +using IntPtr = int*; + +auto x3a = i; +decltype(auto) x3d = i; +using Int = decltype(x3a); +using Int = decltype(x3d); + +auto x4a = (i); +decltype(auto) x4d = (i); +using Int = decltype(x4a); +using IntLRef = decltype(x4d); + +auto x5a = f(); +decltype(auto) x5d = f(); +using Int = decltype(x5a); +using IntRRef = decltype(x5d); + +auto x6a = { 1, 2 }; +decltype(auto) x6d = { 1, 2 }; // expected-error {{cannot deduce 'decltype(auto)' from initializer list}} +using InitListInt = decltype(x6a); + +auto *x7a = &i; +decltype(auto) *x7d = &i; // expected-error {{cannot form pointer to 'decltype(auto)'}} +using IntPtr = decltype(x7a); + +struct S {}; + +decltype(auto) f1(); +decltype(auto) (*f2)(); // expected-error {{'decltype(auto)' can only be used as a return type in a function declaration}} expected-error {{requires an initializer}} +decltype(auto) *f3(); // expected-error {{cannot form pointer to 'decltype(auto)'}} +const decltype(auto) f4(); // expected-error {{'decltype(auto)' cannot be combined with other type specifiers}} +typedef decltype(auto) f5(); // expected-error {{'decltype(auto)' can only be used as a return type in a function declaration}} +decltype(auto) ((((((f6))))())); // ok +decltype(auto) f7()(); // expected-error {{'decltype(auto)' can only be used as a return type in a function declaration}} expected-error {{function cannot return function type}} +decltype(auto) (S::*f8)(); // expected-error {{'decltype(auto)' can only be used as a return type in a function declaration}} expected-error {{requires an initializer}} +decltype(auto) &f9(); // expected-error {{cannot form reference to 'decltype(auto)'}} +decltype(auto) (&f10())[10]; // expected-error {{cannot form array of 'decltype(auto)'}} + +decltype(auto) ((((((v1)))))) = 0; // ok +decltype(auto) v2[1] = { 0 }; // expected-error {{cannot form array of 'decltype(auto)'}} +decltype(auto) &v3 = { 0 }; // expected-error {{cannot form reference to 'decltype(auto)'}} +decltype(auto) *v4 = { 0 }; // expected-error {{cannot form pointer to 'decltype(auto)'}} + +auto multi1a = 0, &multi1b = multi1a; +auto multi1c = multi1a, multi1d = multi1b; +decltype(auto) multi1e = multi1a, multi1f = multi1b; // expected-error {{'decltype(auto)' deduced as 'int' in declaration of 'multi1e' and deduced as 'int &' in declaration of 'multi1f'}} + +auto f1a() { return 0; } +decltype(auto) f1d() { return 0; } +using Int = decltype(f1a()); +using Int = decltype(f1d()); + +auto f2a(int n) { return n; } +decltype(auto) f2d(int n) { return n; } +using Int = decltype(f2a(0)); +using Int = decltype(f2d(0)); + +auto f3a(int n) { return (n); } +decltype(auto) f3d(int n) { return (n); } // expected-warning {{reference to stack memory}} +using Int = decltype(f3a(0)); +using IntLRef = decltype(f3d(0)); + +auto f4a(int n) { return f(); } +decltype(auto) f4d(int n) { return f(); } +using Int = decltype(f4a(0)); +using IntRRef = decltype(f4d(0)); + +auto f5aa(int n) { auto x = f(); return x; } +auto f5ad(int n) { decltype(auto) x = f(); return x; } +decltype(auto) f5da(int n) { auto x = f(); return x; } +decltype(auto) f5dd(int n) { decltype(auto) x = f(); return x; } // expected-error {{rvalue reference to type 'int' cannot bind to lvalue}} +using Int = decltype(f5aa(0)); +using Int = decltype(f5ad(0)); +using Int = decltype(f5da(0)); + +auto init_list_1() { return { 1, 2, 3 }; } // expected-error {{cannot deduce return type from initializer list}} +decltype(auto) init_list_2() { return { 1, 2, 3 }; } // expected-error {{cannot deduce return type from initializer list}} diff --git a/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.simple/p5-cxx0x.cpp b/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.simple/p5-cxx0x.cpp index 093bc14d47f..1ed93b13754 100644 --- a/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.simple/p5-cxx0x.cpp +++ b/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.simple/p5-cxx0x.cpp @@ -41,7 +41,9 @@ decltype( PD(), // expected-error {{private destructor}} PD()) pd1; // expected-error {{private destructor}} decltype(DD(), // expected-error {{deleted function}} - DD()) dd1; // expected-error {{deleted function}} + DD()) dd1; +decltype(A(), + DD()) dd2; // expected-error {{deleted function}} decltype( PD(), // expected-error {{temporary of type 'PD' has private destructor}} 0) pd2; @@ -76,7 +78,7 @@ namespace libcxx_example { template struct swappable { typedef decltype(swap(declval(), declval())) type; static const bool value = !is_same::value; - constexpr operator bool() { return value; } + constexpr operator bool() const { return value; } }; static_assert(swappable(), ""); diff --git a/test/CXX/dcl.dcl/p4-0x.cpp b/test/CXX/dcl.dcl/p4-0x.cpp index 31d49127e7a..1f4cdda1a1f 100644 --- a/test/CXX/dcl.dcl/p4-0x.cpp +++ b/test/CXX/dcl.dcl/p4-0x.cpp @@ -2,15 +2,15 @@ struct S { constexpr S(bool b) : b(b) {} - constexpr explicit operator bool() { return b; } + constexpr explicit operator bool() const { return b; } bool b; }; struct T { - constexpr operator int() { return 1; } + constexpr operator int() const { return 1; } }; struct U { - constexpr operator int() { return 1; } // expected-note {{candidate}} - constexpr operator long() { return 0; } // expected-note {{candidate}} + constexpr operator int() const { return 1; } // expected-note {{candidate}} + constexpr operator long() const { return 0; } // expected-note {{candidate}} }; static_assert(S(true), ""); diff --git a/test/CXX/dcl.decl/dcl.fct.def/dcl.fct.def.default/p2.cpp b/test/CXX/dcl.decl/dcl.fct.def/dcl.fct.def.default/p2.cpp index 783aba18231..b9a1bc52886 100644 --- a/test/CXX/dcl.decl/dcl.fct.def/dcl.fct.def.default/p2.cpp +++ b/test/CXX/dcl.decl/dcl.fct.def/dcl.fct.def.default/p2.cpp @@ -6,8 +6,8 @@ struct S1 { constexpr S1() = default; // expected-error {{defaulted definition of default constructor is not constexpr}} constexpr S1(const S1&) = default; constexpr S1(S1&&) = default; - constexpr S1 &operator=(const S1&) = default; // expected-error {{explicitly-defaulted copy assignment operator may not have}} - constexpr S1 &operator=(S1&&) = default; // expected-error {{explicitly-defaulted move assignment operator may not have}} + constexpr S1 &operator=(const S1&) const = default; // expected-error {{explicitly-defaulted copy assignment operator may not have}} + constexpr S1 &operator=(S1&&) const = default; // expected-error {{explicitly-defaulted move assignment operator may not have}} constexpr ~S1() = default; // expected-error {{destructor cannot be marked constexpr}} int n; }; diff --git a/test/CXX/dcl.decl/dcl.init/dcl.init.aggr/p1-0x.cpp b/test/CXX/dcl.decl/dcl.init/dcl.init.aggr/p1-0x.cpp index fef3692609f..8767678362a 100644 --- a/test/CXX/dcl.decl/dcl.init/dcl.init.aggr/p1-0x.cpp +++ b/test/CXX/dcl.decl/dcl.init/dcl.init.aggr/p1-0x.cpp @@ -1,4 +1,5 @@ // RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++1y %s -DCXX1Y // An aggregate is an array or a class... struct Aggr { @@ -18,9 +19,6 @@ struct NonAggr1a { // expected-note 2 {{candidate constructor}} NonAggr1a(int, int); // expected-note {{candidate constructor}} int k; }; -// In C++0x, 'user-provided' is only defined for special member functions, so -// this type is considered to be an aggregate. This is considered to be -// a language defect. NonAggr1a na1a = { 42 }; // expected-error {{no matching constructor for initialization of 'NonAggr1a'}} struct NonAggr1b { @@ -30,10 +28,15 @@ struct NonAggr1b { NonAggr1b na1b = { 42 }; // expected-error {{no matching constructor for initialization of 'NonAggr1b'}} // no brace-or-equal-initializers for non-static data members, ... -struct NonAggr2 { // expected-note 3 {{candidate constructor}} +// Note, this bullet was removed in C++1y. +struct NonAggr2 { int m = { 123 }; }; -NonAggr2 na2 = { 42 }; // expected-error {{no matching constructor for initialization of 'NonAggr2'}} +NonAggr2 na2 = { 42 }; +#ifndef CXX1Y +// expected-error@-2 {{no matching constructor for initialization of 'NonAggr2'}} +// expected-note@-6 3 {{candidate constructor}} +#endif // no private... struct NonAggr3 { // expected-note 3 {{candidate constructor}} diff --git a/test/CXX/dcl.decl/dcl.init/dcl.init.aggr/p7.cpp b/test/CXX/dcl.decl/dcl.init/dcl.init.aggr/p7.cpp new file mode 100644 index 00000000000..d1fbe766d52 --- /dev/null +++ b/test/CXX/dcl.decl/dcl.init/dcl.init.aggr/p7.cpp @@ -0,0 +1,23 @@ +// RUN: %clang_cc1 -std=c++1y %s -verify + +// expected-no-diagnostics + +struct S { int a; const char *b; int c; int d = b[a]; }; +constexpr S ss = { 1, "asdf" }; + +static_assert(ss.a == 1, ""); +static_assert(ss.b[2] == 'd', ""); +static_assert(ss.c == 0, ""); +static_assert(ss.d == 's', ""); + +struct X { int i, j, k = 42; }; +constexpr X a[] = { 1, 2, 3, 4, 5, 6 }; +constexpr X b[2] = { { 1, 2, 3 }, { 4, 5, 6 } }; + +constexpr bool operator==(X a, X b) { + return a.i == b.i && a.j == b.j && a.k == b.k; +} + +static_assert(sizeof(a) == sizeof(b), ""); +static_assert(a[0] == b[0], ""); +static_assert(a[1] == b[1], ""); diff --git a/test/CXX/dcl.decl/dcl.init/dcl.init.ref/p5-0x.cpp b/test/CXX/dcl.decl/dcl.init/dcl.init.ref/p5-0x.cpp index 812d0de56b9..fdfa6781fe2 100644 --- a/test/CXX/dcl.decl/dcl.init/dcl.init.ref/p5-0x.cpp +++ b/test/CXX/dcl.decl/dcl.init/dcl.init.ref/p5-0x.cpp @@ -200,3 +200,37 @@ namespace rdar13278115 { X &&f1(Y &y) { return y; } // expected-error{{rvalue reference to type 'rdar13278115::X' cannot bind to lvalue of type 'rdar13278115::Y'}} const X &&f2(Y &y) { return y; } // expected-error{{rvalue reference to type 'const rdar13278115::X' cannot bind to lvalue of type 'rdar13278115::Y'}} } + +namespace bitfields { + struct IntBitfield { + int i : 17; // expected-note 3 {{bit-field is declared here}} + }; + + // A simplified version of std::move. + template + T &&move(T &obj) { + return static_cast(obj); + } + + void test() { + int & ir1 = (lvalue().i); // expected-error{{non-const reference cannot bind to bit-field 'i'}} + int & ir2 = (xvalue().i); // expected-error{{non-const lvalue reference to type 'int' cannot bind to a temporary of type 'int'}} + int && ir3 = (xvalue().i); // no-warning + int && ir4 = move(lvalue()).i; // no-warning + + volatile int & vir1 = (lvalue().i); // expected-error{{non-const reference cannot bind to bit-field 'i'}} + volatile int & vir2 = (xvalue().i); // expected-error{{volatile lvalue reference to type 'volatile int' cannot bind to a temporary of type 'int'}} + volatile int && vir3 = (xvalue().i); // no-warning + volatile int && vir4 = move(lvalue()).i; // no-warning + + const int & cir1 = (lvalue().i); // no-warning + const int & cir2 = (xvalue().i); // no-warning + const int && cir3 = (xvalue().i); // no-warning + const int && cir4 = move(lvalue()).i; // no-warning + + const volatile int & cvir1 = (lvalue().i); // expected-error{{non-const reference cannot bind to bit-field 'i'}} + const volatile int & cvir2 = (xvalue().i); // expected-error{{volatile lvalue reference to type 'const volatile int' cannot bind to a temporary of type 'int'}} + const volatile int && cvir3 = (xvalue().i); // no-warning + const volatile int && cvir4 = move(lvalue()).i; // no-warning + } +} diff --git a/test/CXX/dcl.decl/dcl.init/dcl.init.ref/p5.cpp b/test/CXX/dcl.decl/dcl.init/dcl.init.ref/p5.cpp index 51d61a52a67..263f661208c 100644 --- a/test/CXX/dcl.decl/dcl.init/dcl.init.ref/p5.cpp +++ b/test/CXX/dcl.decl/dcl.init/dcl.init.ref/p5.cpp @@ -38,3 +38,26 @@ namespace PR6066 { return 0; } } + +namespace test3 { + struct A { + unsigned bitX : 4; // expected-note 4 {{bit-field is declared here}} + unsigned bitY : 4; // expected-note {{bit-field is declared here}} + unsigned var; + + void foo(); + }; + + void test(A *a) { + unsigned &t0 = a->bitX; // expected-error {{non-const reference cannot bind to bit-field 'bitX'}} + unsigned &t1 = (unsigned&) a->bitX; // expected-error {{non-const reference cannot bind to bit-field 'bitX'}} + unsigned &t2 = const_cast(a->bitX); // expected-error {{const_cast from bit-field lvalue to reference type 'unsigned int &'}} + unsigned &t3 = (a->foo(), a->bitX); // expected-error {{non-const reference cannot bind to bit-field 'bitX'}} + unsigned &t4 = (a->var ? a->bitX : a->bitY); // expected-error {{non-const reference cannot bind to bit-field}} + unsigned &t5 = (a->var ? a->bitX : a->bitX); // expected-error {{non-const reference cannot bind to bit-field}} + unsigned &t6 = (a->var ? a->bitX : a->var); // expected-error {{non-const reference cannot bind to bit-field}} + unsigned &t7 = (a->var ? a->var : a->bitY); // expected-error {{non-const reference cannot bind to bit-field}} + unsigned &t8 = (a->bitX = 3); // expected-error {{non-const reference cannot bind to bit-field 'bitX'}} + unsigned &t9 = (a->bitY += 3); // expected-error {{non-const reference cannot bind to bit-field 'bitY'}} + } +} diff --git a/test/CXX/except/except.spec/p1.cpp b/test/CXX/except/except.spec/p1.cpp index e184ec4ffa2..a32f37d5520 100644 --- a/test/CXX/except/except.spec/p1.cpp +++ b/test/CXX/except/except.spec/p1.cpp @@ -55,7 +55,7 @@ namespace noex { struct A {}; void g1() noexcept(A()); // expected-error {{not contextually convertible}} - void g2(bool b) noexcept(b); // expected-error {{argument to noexcept specifier must be a constant expression}} + void g2(bool b) noexcept(b); // expected-error {{argument to noexcept specifier must be a constant expression}} expected-note {{read of non-const variable 'b'}} expected-note {{here}} } diff --git a/test/CXX/except/except.spec/p14.cpp b/test/CXX/except/except.spec/p14.cpp index 99ed2fdee19..dda69e9aad6 100644 --- a/test/CXX/except/except.spec/p14.cpp +++ b/test/CXX/except/except.spec/p14.cpp @@ -112,3 +112,26 @@ namespace rdar13017229 { Typo foo(); // expected-error{{unknown type name 'Typo'}} }; } + +namespace InhCtor { + template struct X {}; + struct Base { + Base(X<0>) noexcept(true); + Base(X<1>) noexcept(false); + Base(X<2>) throw(X<2>); + template Base(T) throw(T); + }; + template struct Throw { + Throw() throw(T); + }; + struct Derived : Base, Throw> { + using Base::Base; + Throw> x; + }; + struct Test { + friend Derived::Derived(X<0>) throw(X<3>, X<4>); + friend Derived::Derived(X<1>) noexcept(false); + friend Derived::Derived(X<2>) throw(X<2>, X<3>, X<4>); + }; + static_assert(!noexcept(Derived{X<5>{}}), ""); +} diff --git a/test/CXX/expr/expr.ass/p9-cxx11.cpp b/test/CXX/expr/expr.ass/p9-cxx11.cpp index 206c82c985c..ecc6d2c3d5b 100644 --- a/test/CXX/expr/expr.ass/p9-cxx11.cpp +++ b/test/CXX/expr/expr.ass/p9-cxx11.cpp @@ -24,8 +24,8 @@ struct S { int a, b; }; struct T { - constexpr int operator=(S s) { return s.a; } - constexpr int operator+=(S s) { return s.b; } + constexpr int operator=(S s) const { return s.a; } + constexpr int operator+=(S s) const { return s.b; } }; static_assert((T() = {4, 9}) == 4, ""); static_assert((T() += {4, 9}) == 9, ""); diff --git a/test/CXX/expr/expr.const/p2-0x.cpp b/test/CXX/expr/expr.const/p2-0x.cpp index 065a12b3f23..634dee3782f 100644 --- a/test/CXX/expr/expr.const/p2-0x.cpp +++ b/test/CXX/expr/expr.const/p2-0x.cpp @@ -118,7 +118,7 @@ namespace IncompleteClassTypeAddr { constexpr S (*p2)[] = &sArr; // ok struct S { - constexpr S *operator&() { return nullptr; } + constexpr S *operator&() const { return nullptr; } }; constexpr S *q = &s; // ok static_assert(!q, ""); @@ -205,7 +205,7 @@ namespace UndefinedBehavior { constexpr const int &np = (*(int(*)[4])nullptr)[2]; // expected-error {{constant expression}} expected-note {{cannot access array element of null pointer}} struct C { - constexpr int f() { return 0; } + constexpr int f() const { return 0; } } constexpr c = C(); constexpr int k1 = c.f(); // ok constexpr int k2 = ((C*)nullptr)->f(); // expected-error {{constant expression}} expected-note {{cannot call member function on null pointer}} @@ -481,14 +481,14 @@ namespace UnspecifiedRelations { public: constexpr A() : a(0), b(0) {} int a; - constexpr bool cmp() { return &a < &b; } // expected-error {{constexpr function never produces a constant expression}} expected-note {{comparison of address of fields 'a' and 'b' of 'A' with differing access specifiers (public vs private) has unspecified value}} + constexpr bool cmp() const { return &a < &b; } // expected-error {{constexpr function never produces a constant expression}} expected-note {{comparison of address of fields 'a' and 'b' of 'A' with differing access specifiers (public vs private) has unspecified value}} private: int b; }; class B { public: A a; - constexpr bool cmp() { return &a.a < &b.a; } // expected-error {{constexpr function never produces a constant expression}} expected-note {{comparison of address of fields 'a' and 'b' of 'B' with differing access specifiers (public vs protected) has unspecified value}} + constexpr bool cmp() const { return &a.a < &b.a; } // expected-error {{constexpr function never produces a constant expression}} expected-note {{comparison of address of fields 'a' and 'b' of 'B' with differing access specifiers (public vs protected) has unspecified value}} protected: A b; }; diff --git a/test/CXX/expr/expr.const/p3-0x.cpp b/test/CXX/expr/expr.const/p3-0x.cpp index 6ddd11bcee2..047e238190f 100644 --- a/test/CXX/expr/expr.const/p3-0x.cpp +++ b/test/CXX/expr/expr.const/p3-0x.cpp @@ -101,7 +101,7 @@ int n = Val::value; // expected-error {{conversion from namespace NonConstLValue { struct S { - constexpr operator int() { return 10; } + constexpr operator int() const { return 10; } }; S s; // not constexpr // Under the FDIS, this is not a converted constant expression. diff --git a/test/CXX/expr/expr.const/p5-0x.cpp b/test/CXX/expr/expr.const/p5-0x.cpp index bdb2b23ec79..0a4ac22d06d 100644 --- a/test/CXX/expr/expr.const/p5-0x.cpp +++ b/test/CXX/expr/expr.const/p5-0x.cpp @@ -7,8 +7,8 @@ namespace std_example { struct A { constexpr A(int i) : val(i) { } - constexpr operator int() { return val; } - constexpr operator long() { return 43; } + constexpr operator int() const { return val; } + constexpr operator long() const { return 43; } private: int val; }; @@ -21,17 +21,17 @@ int ary[a]; // expected-error {{size of array has non-integer type 'const std_ex struct OK { constexpr OK() {} - constexpr operator int() { return 8; } + constexpr operator int() const { return 8; } } constexpr ok; extern struct Incomplete incomplete; // expected-note 4{{forward decl}} struct Explicit { constexpr Explicit() {} - constexpr explicit operator int() { return 4; } // expected-note 4{{here}} + constexpr explicit operator int() const { return 4; } // expected-note 4{{here}} } constexpr expl; struct Ambiguous { constexpr Ambiguous() {} - constexpr operator int() { return 2; } // expected-note 4{{here}} - constexpr operator long() { return 1; } // expected-note 4{{here}} + constexpr operator int() const { return 2; } // expected-note 4{{here}} + constexpr operator long() const { return 1; } // expected-note 4{{here}} } constexpr ambig; constexpr int test_ok = ok; // ok diff --git a/test/CXX/expr/expr.post/expr.const.cast/p1-0x.cpp b/test/CXX/expr/expr.post/expr.const.cast/p1-0x.cpp index be898761faf..76ea96fe14a 100644 --- a/test/CXX/expr/expr.post/expr.const.cast/p1-0x.cpp +++ b/test/CXX/expr/expr.post/expr.const.cast/p1-0x.cpp @@ -1,5 +1,4 @@ // RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify %s -// expected-no-diagnostics // The result of the expression const_cast(v) is of type T. If T is // an lvalue reference to object type, the result is an lvalue; if T @@ -16,3 +15,19 @@ void test_classification(const int *ptr) { int *ptr1 = const_cast(xvalue()); int *ptr2 = const_cast(prvalue()); } + +struct A { + volatile unsigned ubf : 4; + volatile unsigned uv; + volatile int sv; + void foo(); + bool pred(); +}; + +void test(A &a) { + unsigned &t0 = const_cast(a.ubf); // expected-error {{const_cast from bit-field lvalue to reference type}} + unsigned &t1 = const_cast(a.foo(), a.ubf); // expected-error {{const_cast from bit-field lvalue to reference type}} + unsigned &t2 = const_cast(a.pred() ? a.ubf : a.ubf); // expected-error {{const_cast from bit-field lvalue to reference type}} + unsigned &t3 = const_cast(a.pred() ? a.ubf : a.uv); // expected-error {{const_cast from bit-field lvalue to reference type}} + unsigned &t4 = const_cast(a.pred() ? a.ubf : a.sv); // expected-error {{const_cast from rvalue to reference type}} +} diff --git a/test/CXX/expr/expr.prim/expr.prim.lambda/default-arguments.cpp b/test/CXX/expr/expr.prim/expr.prim.lambda/default-arguments.cpp index 5dac886d4d1..38d5d0a6cd6 100644 --- a/test/CXX/expr/expr.prim/expr.prim.lambda/default-arguments.cpp +++ b/test/CXX/expr/expr.prim/expr.prim.lambda/default-arguments.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -fsyntax-only -std=c++11 %s -Wno-lambda-extensions -verify +// RUN: %clang_cc1 -fsyntax-only -std=c++11 %s -verify void defargs() { auto l1 = [](int i, int j = 17, int k = 18) { return i + j + k; }; diff --git a/test/CXX/expr/expr.prim/expr.prim.lambda/p5.cpp b/test/CXX/expr/expr.prim/expr.prim.lambda/p5.cpp index 9dffc1ff264..dc2c209af26 100644 --- a/test/CXX/expr/expr.prim/expr.prim.lambda/p5.cpp +++ b/test/CXX/expr/expr.prim/expr.prim.lambda/p5.cpp @@ -39,12 +39,10 @@ void test_quals() { bogus_override_if_virtual bogus; } -// Default arguments (8.3.6) shall not be specified in the -// parameter-declaration-clause of a lambda- declarator. -// Note: Removed by core issue 974. +// Core issue 974: default arguments (8.3.6) may be specified in the +// parameter-declaration-clause of a lambda-declarator. int test_default_args() { - return [](int i = 5, // expected-warning{{C++11 forbids default arguments for lambda expressions}} - int j = 17) { return i+j;}(5, 6); + return [](int i = 5, int j = 17) { return i+j;}(5, 6); } // Any exception-specification specified on a lambda-expression diff --git a/test/CXX/expr/expr.unary/expr.sizeof/p1.cpp b/test/CXX/expr/expr.unary/expr.sizeof/p1.cpp new file mode 100644 index 00000000000..6a59e3d7ae5 --- /dev/null +++ b/test/CXX/expr/expr.unary/expr.sizeof/p1.cpp @@ -0,0 +1,20 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +struct A { + unsigned bitX : 4; + unsigned bitY : 4; + unsigned var; + + void foo(); +}; + +void test(A *a) { + int x; + x = sizeof(a->bitX); // expected-error {{invalid application of 'sizeof' to bit-field}} + x = sizeof((unsigned) a->bitX); + x = sizeof(a->foo(), a->bitX); // expected-error {{invalid application of 'sizeof' to bit-field}} + x = sizeof(a->var ? a->bitX : a->bitY); // expected-error {{invalid application of 'sizeof' to bit-field}} + x = sizeof(a->var ? a->bitX : a->bitX); // expected-error {{invalid application of 'sizeof' to bit-field}} + x = sizeof(a->bitX = 3); // expected-error {{invalid application of 'sizeof' to bit-field}} + x = sizeof(a->bitY += 3); // expected-error {{invalid application of 'sizeof' to bit-field}} +} diff --git a/test/CXX/over/over.oper/over.literal/p2.cpp b/test/CXX/over/over.oper/over.literal/p2.cpp index c012104314b..00466fb311d 100644 --- a/test/CXX/over/over.oper/over.literal/p2.cpp +++ b/test/CXX/over/over.oper/over.literal/p2.cpp @@ -33,3 +33,12 @@ template void operator "" _h() {} template<> void operator "" _h<'a', 'b', 'c'>() {} template void operator "" _h<'a', 'b', 'c', 'd'>(); + +namespace rdar13605348 { + +class C { + double operator"" _x(long double value) { return double(value); } // expected-error{{literal operator 'operator "" _x' must be in a namespace or global scope}} + double value() { return 3.2_x; } // expected-error{{no matching literal operator for call to}} +}; + +} diff --git a/test/CXX/special/class.inhctor/elsewhere.cpp b/test/CXX/special/class.inhctor/elsewhere.cpp index 184e90298b0..b986f658249 100644 --- a/test/CXX/special/class.inhctor/elsewhere.cpp +++ b/test/CXX/special/class.inhctor/elsewhere.cpp @@ -55,3 +55,10 @@ template struct F : D { using A::A; // expected-error {{'A' is not a direct base of 'F'}} }; F fb; // expected-note {{here}} + +template +struct G : T { + using T::T; + G(int &) : G(0) {} +}; +G g(123); diff --git a/test/CXX/special/class.inhctor/p1.cpp b/test/CXX/special/class.inhctor/p1.cpp index 57e91504d68..8721dec1b40 100644 --- a/test/CXX/special/class.inhctor/p1.cpp +++ b/test/CXX/special/class.inhctor/p1.cpp @@ -2,17 +2,24 @@ // Per a core issue (no number yet), an ellipsis is always dropped. struct A { A(...); // expected-note {{here}} - A(int = 0, int = 0, int = 0, int = 0, ...); // expected-note 5{{here}} + A(int = 0, int = 0, int = 0, int = 0, ...); // expected-note 9{{here}} A(int = 0, int = 0, ...); // expected-note {{here}} + + template A(T, int = 0, ...); // expected-note 5{{here}} + + template A(const T (&)[N]); // expected-note 2{{here}} + template A(const T (&)[N], int = 0); // expected-note 2{{here}} }; -struct B : A { // expected-note 3{{candidate}} - using A::A; // expected-warning 3{{inheriting constructor does not inherit ellipsis}} expected-note 4{{candidate}} expected-note 2{{deleted}} +struct B : A { // expected-note 6{{candidate}} + using A::A; // expected-warning 4{{inheriting constructor does not inherit ellipsis}} expected-note 16{{candidate}} expected-note 3{{deleted}} }; +struct C {} c; + B b0{}; // expected-error@-1 {{call to implicitly-deleted default constructor}} -// expected-note@9 {{default constructor of 'B' is implicitly deleted because base class 'A' has multiple default constructors}} +// expected-note@-8 {{default constructor of 'B' is implicitly deleted because base class 'A' has multiple default constructors}} B b1{1}; // FIXME: explain why the inheriting constructor was deleted @@ -29,3 +36,29 @@ B b4{1,2,3,4}; B b5{1,2,3,4,5}; // expected-error@-1 {{no matching constructor for initialization of 'B'}} + +B b6{c}; +// ok + +B b7{c,0}; +// ok + +B b8{c,0,1}; +// expected-error@-1 {{no matching constructor}} + +B b9{"foo"}; +// FIXME: explain why the inheriting constructor was deleted +// expected-error@-2 {{call to deleted constructor of 'B'}} + +namespace PR15755 { + struct X { + template X(int, Ts...); + }; + struct Y : X { + using X::X; + }; + struct Z : Y { + using Y::Y; + }; + Z z(0); +} diff --git a/test/CXX/special/class.inhctor/p2.cpp b/test/CXX/special/class.inhctor/p2.cpp index e4267385ced..e6abd6840e4 100644 --- a/test/CXX/special/class.inhctor/p2.cpp +++ b/test/CXX/special/class.inhctor/p2.cpp @@ -3,7 +3,7 @@ template struct X {}; // Constructor characteristics are: -// - the template parameter list [FIXME] +// - the template parameter list // - the parameter-type-list // - absence or presence of explicit // - absence or presence of constexpr @@ -85,3 +85,37 @@ struct ConstexprEval3 : ConstexprEval, ConstexprEval2 { constexpr ConstexprEval3 ce{4, "foobar"}; static_assert(ce.k == 'a', ""); static_assert(ce.k2 == 'x', ""); + + +struct TemplateCtors { + constexpr TemplateCtors() {} + template class T> TemplateCtors(X<0>, T<0>); + template TemplateCtors(X<1>, X); + template TemplateCtors(X<2>, T); + + template TemplateCtors(int, int = 0, int = 0); // expected-note {{inherited from here}} +}; + +struct UsingTemplateCtors : TemplateCtors { + using TemplateCtors::TemplateCtors; // expected-note 4{{here}} expected-note {{candidate}} + + constexpr UsingTemplateCtors(X<0>, X<0>) {} + constexpr UsingTemplateCtors(X<1>, X<1>) {} + constexpr UsingTemplateCtors(X<2>, X<2>) {} + + template constexpr UsingTemplateCtors(int) {} // expected-note {{candidate}} + template constexpr UsingTemplateCtors(int, int) {} + template constexpr UsingTemplateCtors(int, int, int) {} +}; + +template struct Y {}; +constexpr UsingTemplateCtors uct1{ X<0>{}, X<0>{} }; +constexpr UsingTemplateCtors uct2{ X<0>{}, Y<0>{} }; // expected-error {{must be initialized by a constant expression}} expected-note {{non-constexpr}} +constexpr UsingTemplateCtors uct3{ X<1>{}, X<0>{} }; // expected-error {{must be initialized by a constant expression}} expected-note {{non-constexpr}} +constexpr UsingTemplateCtors uct4{ X<1>{}, X<1>{} }; +constexpr UsingTemplateCtors uct5{ X<2>{}, 0 }; // expected-error {{must be initialized by a constant expression}} expected-note {{non-constexpr}} +constexpr UsingTemplateCtors uct6{ X<2>{}, X<2>{} }; + +constexpr UsingTemplateCtors utc7{ 0 }; // expected-error {{ambiguous}} +constexpr UsingTemplateCtors utc8{ 0, 0 }; // ok +constexpr UsingTemplateCtors utc9{ 0, 0, 0 }; // expected-error {{must be initialized by a constant expression}} expected-note {{non-constexpr}} diff --git a/test/CXX/special/class.inhctor/p3.cpp b/test/CXX/special/class.inhctor/p3.cpp index f71ab16c0f1..7aaaa7a6f0b 100644 --- a/test/CXX/special/class.inhctor/p3.cpp +++ b/test/CXX/special/class.inhctor/p3.cpp @@ -46,3 +46,13 @@ struct U { friend T3::T3(int); friend T3::T3(int, int); }; + +struct B4 { + template explicit B4(T, int = 0); +}; +template struct T4 : B4 { + using B4::B4; // expected-note {{here}} + template T4(U); +}; +T4 t4a = {0}; +T4 t4b = {0, 0}; // expected-error {{chosen constructor is explicit}} diff --git a/test/CXX/special/class.inhctor/p4.cpp b/test/CXX/special/class.inhctor/p4.cpp index eea3bf29731..512705e4dd9 100644 --- a/test/CXX/special/class.inhctor/p4.cpp +++ b/test/CXX/special/class.inhctor/p4.cpp @@ -44,11 +44,13 @@ FA fa2{X<2>{}}; // expected-error {{calling a private constructor}} // It is deleted if the corresponding constructor [...] is deleted. struct G { G(int) = delete; + template G(T*) = delete; }; struct H : G { - using G::G; // expected-note {{marked deleted here}} + using G::G; // expected-note 2{{marked deleted here}} }; -H h(5); // expected-error {{call to implicitly-deleted function of 'H'}} +H h1(5); // expected-error {{call to implicitly-deleted function of 'H'}} +H h2("foo"); // expected-error {{call to deleted constructor of 'H'}} // Core defect: It is also deleted if multiple base constructors generate the diff --git a/test/CXX/special/class.inhctor/p7.cpp b/test/CXX/special/class.inhctor/p7.cpp index 9ae160f0547..a57e8558f5c 100644 --- a/test/CXX/special/class.inhctor/p7.cpp +++ b/test/CXX/special/class.inhctor/p7.cpp @@ -27,3 +27,21 @@ template struct B4 : B3, B1 { }; B4 b4c; B4 b4i; // expected-note {{here}} + +struct B5 { + template B5(T); // expected-note {{previous constructor}} +}; +struct B6 { + template B6(T); // expected-note {{conflicting constructor}} +}; +struct B7 { + template B7(T); +}; +struct D56 : B5, B6, B7 { + using B5::B5; // expected-note {{inherited here}} + using B6::B6; // expected-error {{already inherited}} +}; +struct D57 : B5, B6, B7 { + using B5::B5; + using B7::B7; // ok, not the same signature +}; diff --git a/test/CXX/special/class.inhctor/p8.cpp b/test/CXX/special/class.inhctor/p8.cpp index e2b07dfae81..0c857382e38 100644 --- a/test/CXX/special/class.inhctor/p8.cpp +++ b/test/CXX/special/class.inhctor/p8.cpp @@ -19,3 +19,12 @@ constexpr B b0{0}; constexpr B b1{k}; static_assert(a0.rval && !a1.rval && b0.rval && !b1.rval, ""); + +struct C { + template constexpr C(T t) : v(t) {} + int v; +}; +struct D : C { + using C::C; +}; +static_assert(D(123).v == 123, ""); diff --git a/test/CXX/stmt.stmt/stmt.iter/stmt.ranged/p1.cpp b/test/CXX/stmt.stmt/stmt.iter/stmt.ranged/p1.cpp index 3952afdeff1..b159a15b8d4 100644 --- a/test/CXX/stmt.stmt/stmt.iter/stmt.ranged/p1.cpp +++ b/test/CXX/stmt.stmt/stmt.iter/stmt.ranged/p1.cpp @@ -242,3 +242,13 @@ void example() { for (int &x : array) x *= 2; } + +namespace rdar13712739 { + template + void foo(const T& t) { + auto &x = t.get(); // expected-error{{member reference base type 'const int' is not a structure or union}} + for (auto &blah : x) { } + } + + template void foo(const int&); // expected-note{{in instantiation of function template specialization}} +} diff --git a/test/CXX/stmt.stmt/stmt.select/stmt.switch/p2-0x.cpp b/test/CXX/stmt.stmt/stmt.select/stmt.switch/p2-0x.cpp index d0f15d4d3df..fda0c5cff15 100644 --- a/test/CXX/stmt.stmt/stmt.select/stmt.switch/p2-0x.cpp +++ b/test/CXX/stmt.stmt/stmt.select/stmt.switch/p2-0x.cpp @@ -3,12 +3,12 @@ struct Value { constexpr Value(int n) : n(n) {} - constexpr operator short() { return n; } + constexpr operator short() const { return n; } int n; }; enum E { E0, E1 }; struct Alt { - constexpr operator E() { return E0; } + constexpr operator E() const { return E0; } }; constexpr short s = Alt(); diff --git a/test/CXX/temp/temp.arg/temp.arg.nontype/p1-11.cpp b/test/CXX/temp/temp.arg/temp.arg.nontype/p1-11.cpp index 59ce8b68b7c..3f65466dfc0 100644 --- a/test/CXX/temp/temp.arg/temp.arg.nontype/p1-11.cpp +++ b/test/CXX/temp/temp.arg/temp.arg.nontype/p1-11.cpp @@ -1,19 +1,23 @@ -// RUN: %clang_cc1 -std=c++11 %s -verify +// RUN: %clang_cc1 -std=c++11 %s -verify -triple x86_64-linux-gnu namespace std { typedef decltype(nullptr) nullptr_t; } -template struct IP { // expected-note 4 {{template parameter is declared here}} +template struct IP { // expected-note 5 {{template parameter is declared here}} IP *ip2; }; +template struct IR {}; + constexpr std::nullptr_t get_nullptr() { return nullptr; } constexpr std::nullptr_t np = nullptr; std::nullptr_t nonconst_np; // expected-note{{declared here}} +thread_local int tl; // expected-note {{refers here}} + IP<0> ip0; // expected-error{{null non-type template argument must be cast to template parameter type 'int *'}} IP<(0)> ip1; // expected-error{{null non-type template argument must be cast to template parameter type 'int *'}} IP ip2; @@ -23,6 +27,9 @@ IP ip5; IP ip5; // expected-error{{non-type template argument of type 'std::nullptr_t' (aka 'nullptr_t') is not a constant expression}} \ // expected-note{{read of non-constexpr variable 'nonconst_np' is not allowed in a constant expression}} IP<(float*)0> ip6; // expected-error{{null non-type template argument of type 'float *' does not match template parameter of type 'int *'}} +IP<&tl> ip7; // expected-error{{non-type template argument of type 'int *' is not a constant expression}} + +IR ir1; // expected-error{{non-type template argument refers to thread-local object}} struct X { }; template struct PM { // expected-note 2 {{template parameter is declared here}} diff --git a/test/CXX/temp/temp.decls/temp.variadic/multi-level-substitution.cpp b/test/CXX/temp/temp.decls/temp.variadic/multi-level-substitution.cpp index e0ffef5007a..82114cfa9de 100644 --- a/test/CXX/temp/temp.decls/temp.variadic/multi-level-substitution.cpp +++ b/test/CXX/temp/temp.decls/temp.variadic/multi-level-substitution.cpp @@ -193,18 +193,18 @@ namespace PacksAtDifferentLevels { template struct X6 { template - constexpr auto f1(A ...a) -> decltype(g(A(a + B())...)) { return g(A(a + B())...); } + constexpr auto f1(A ...a) const -> decltype(g(A(a + B())...)) { return g(A(a + B())...); } template - constexpr auto f2(A ...a, B ...b) -> decltype(g((&a)[b] ...)) { return g((&a)[b] ...); } // expected-note {{past-the-end}} + constexpr auto f2(A ...a, B ...b) const -> decltype(g((&a)[b] ...)) { return g((&a)[b] ...); } // expected-note {{past-the-end}} template struct Inner { template - constexpr auto f(A ...a, B ...b, C ...c) -> decltype(g(a+b+c...)) { return g(a+b+c...); } + constexpr auto f(A ...a, B ...b, C ...c) const -> decltype(g(a+b+c...)) { return g(a+b+c...); } }; }; - struct A { constexpr operator int() { return 2; } }; - struct B { constexpr operator int() { return 1; } }; + struct A { constexpr operator int() const { return 2; } }; + struct B { constexpr operator int() const { return 1; } }; static_assert(X6().f1(255, 1) == 12, ""); static_assert(X6().f2(3, 4, 0, 0) == 34, ""); diff --git a/test/CXX/temp/temp.spec/temp.explicit/p1-0x.cpp b/test/CXX/temp/temp.spec/temp.explicit/p1-0x.cpp index e0c7b35a796..f804d4db129 100644 --- a/test/CXX/temp/temp.spec/temp.explicit/p1-0x.cpp +++ b/test/CXX/temp/temp.spec/temp.explicit/p1-0x.cpp @@ -9,7 +9,7 @@ template inline void X::f(); // expected-error{{explicit instantiation cann template struct Y { - constexpr int f() { return 0; } + constexpr int f() { return 0; } // expected-warning{{C++1y}} }; template constexpr int Y::f() const; // expected-error{{explicit instantiation cannot be 'constexpr'}} diff --git a/test/CodeGen/2010-02-10-PointerName.c b/test/CodeGen/2010-02-10-PointerName.c index 2837de4b8b4..2321c01c6f6 100644 --- a/test/CodeGen/2010-02-10-PointerName.c +++ b/test/CodeGen/2010-02-10-PointerName.c @@ -1,4 +1,6 @@ -// RUN: %clang_cc1 %s -emit-llvm -g -o - | grep DW_TAG_pointer_type | grep -v {"char"} +// RUN: %clang_cc1 %s -emit-llvm -g -o - | FileCheck %s +// CHECK: DW_TAG_pointer_type +// CHECK-NOT: {"char"} char i = 1; void foo() { diff --git a/test/CodeGen/arm-asm-diag.c b/test/CodeGen/arm-asm-diag.c new file mode 100644 index 00000000000..eea7920b106 --- /dev/null +++ b/test/CodeGen/arm-asm-diag.c @@ -0,0 +1,23 @@ +// REQUIRES: arm-registered-target +// RUN: %clang_cc1 -triple armv7 %s -S -o /dev/null 2>&1 | FileCheck %s + +// rdar://13446483 +typedef __attribute__((neon_vector_type(2))) long long int64x2_t; +typedef struct int64x2x4_t { + int64x2_t val[4]; +} int64x2x4_t; +int64x2x4_t t1(const long long a[]) { + int64x2x4_t r; + __asm__("vldm %[a], { %q[r0], %q[r1], %q[r2], %q[r3] }" + : [r0] "=r"(r.val[0]), // expected-warning {{the value is truncated when put into register, use a modifier to specify the size}} + [r1] "=r"(r.val[1]), // expected-warning {{the value is truncated when put into register, use a modifier to specify the size}} + [r2] "=r"(r.val[2]), // expected-warning {{the value is truncated when put into register, use a modifier to specify the size}} + [r3] "=r"(r.val[3]) // expected-warning {{the value is truncated when put into register, use a modifier to specify the size}} + : [a] "r"(a)); + return r; +} +// We should see all four errors, rather than report a fatal error after the first. +// CHECK: error: non-trivial scalar-to-vector conversion, possible invalid constraint for vector type +// CHECK: error: non-trivial scalar-to-vector conversion, possible invalid constraint for vector type +// CHECK: error: non-trivial scalar-to-vector conversion, possible invalid constraint for vector type +// CHECK: error: non-trivial scalar-to-vector conversion, possible invalid constraint for vector type diff --git a/test/CodeGen/builtins-aarch64.c b/test/CodeGen/builtins-aarch64.c new file mode 100644 index 00000000000..8a93cb41fa4 --- /dev/null +++ b/test/CodeGen/builtins-aarch64.c @@ -0,0 +1,6 @@ +// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -O3 -emit-llvm -o - %s | FileCheck %s + +void f0(char *a, char *b) { + __clear_cache(a,b); +// CHECK: call {{.*}} @__clear_cache +} diff --git a/test/CodeGen/c-strings.c b/test/CodeGen/c-strings.c index 1021010a6dd..60a6b019129 100644 --- a/test/CodeGen/c-strings.c +++ b/test/CodeGen/c-strings.c @@ -3,12 +3,19 @@ // Should be 3 hello strings, two global (of different sizes), the rest are // shared. +// CHECK: @align = global i8 [[ALIGN:[0-9]+]] // CHECK: @.str = private unnamed_addr constant [6 x i8] c"hello\00" // CHECK: @f1.x = internal global i8* getelementptr inbounds ([6 x i8]* @.str, i32 0, i32 0) -// CHECK: @f2.x = internal global [6 x i8] c"hello\00", align 1 -// CHECK: @f3.x = internal global [8 x i8] c"hello\00\00\00", align 1 +// CHECK: @f2.x = internal global [6 x i8] c"hello\00", align [[ALIGN]] +// CHECK: @f3.x = internal global [8 x i8] c"hello\00\00\00", align [[ALIGN]] // CHECK: @f4.x = internal global %struct.s { i8* getelementptr inbounds ([6 x i8]* @.str, i32 0, i32 0) } -// CHECK: @x = global [3 x i8] c"ola", align 1 +// CHECK: @x = global [3 x i8] c"ola", align [[ALIGN]] + +#if defined(__s390x__) +unsigned char align = 2; +#else +unsigned char align = 1; +#endif void bar(const char *); diff --git a/test/CodeGen/le32-regparm.c b/test/CodeGen/le32-regparm.c index 8c1ae5eb457..c8f70694c43 100644 --- a/test/CodeGen/le32-regparm.c +++ b/test/CodeGen/le32-regparm.c @@ -1,41 +1,4 @@ -// RUN: %clang_cc1 -triple le32-unknown-nacl %s -emit-llvm -o - | FileCheck %s +// RUN: %clang_cc1 -triple le32-unknown-nacl %s -fsyntax-only -verify -#define FASTCALL __attribute__((regparm(2))) +void __attribute__((regparm(2))) fc_f1(int i, int j, int k) {} // expected-error{{'regparm' is not valid on this platform}} -typedef struct { - int aaa; - double bbbb; - int ccc[200]; -} foo; - -// 2 inreg arguments are supported. -void FASTCALL f1(int i, int j, int k); -// CHECK: define void @f1(i32 inreg %i, i32 inreg %j, i32 %k) -void f1(int i, int j, int k) { } - -// inreg structs are not supported. -// CHECK: define void @f2(%struct.foo* inreg %a) -void __attribute__((regparm(1))) f2(foo* a) {} - -// Only the first 2 arguments can be passed inreg, and the first -// non-integral type consumes remaining available registers. -// CHECK: define void @f3(%struct.foo* byval %a, i32 %b) -void __attribute__((regparm(2))) f3(foo a, int b) {} - -// Only 64 total bits are supported -// CHECK: define void @f4(i64 inreg %g, i32 %h) -void __attribute__((regparm(2))) f4(long long g, int h) {} - -typedef void (*FType)(int, int) __attribute ((regparm (2))); -FType bar; -extern void FASTCALL reduced(char b, double c, foo* d, double e, int f); - -int -main(void) { - // The presence of double c means that foo* d is not passed inreg. This - // behavior is different from current x86-32 behavior - // CHECK: call void @reduced(i8 inreg signext 0, {{.*}} %struct.foo* null - reduced(0, 0.0, 0, 0.0, 0); - // CHECK: call void {{.*}}(i32 inreg 1, i32 inreg 2) - bar(1,2); -} diff --git a/test/CodeGen/linetable-endscope.c b/test/CodeGen/linetable-endscope.c new file mode 100644 index 00000000000..236f605d7ef --- /dev/null +++ b/test/CodeGen/linetable-endscope.c @@ -0,0 +1,17 @@ +// RUN: %clang_cc1 -emit-llvm -g -triple x86_64-apple-darwin10 %s -o - | FileCheck %s + +// Check the line numbers for the ret instruction. We expect it to be +// at the closing of the lexical scope in this case. See the comments in +// CodeGenFunction::FinishFunction() for more details. + +// CHECK: define {{.*}}foo +// CHECK: store {{.*}}, !dbg ![[CONV:[0-9]+]] +// CHECK: ret {{.*}}, !dbg ![[RET:[0-9]+]] + +void foo(char c) +{ + int i; + // CHECK: ![[CONV]] = metadata !{i32 [[@LINE+1]], i32 0, metadata !{{.*}}, null} + i = c; + // CHECK: ![[RET]] = metadata !{i32 [[@LINE+1]], i32 0, metadata !{{.*}}, null} +} diff --git a/test/CodeGen/linux-arm-atomic.c b/test/CodeGen/linux-arm-atomic.c new file mode 100644 index 00000000000..c7ce1d228bb --- /dev/null +++ b/test/CodeGen/linux-arm-atomic.c @@ -0,0 +1,11 @@ +// RUN: %clang_cc1 %s -emit-llvm -o - -triple=armv7-unknown-linux | FileCheck %s +// RUN: %clang_cc1 %s -emit-llvm -o - -triple=armv6-unknown-linux | FileCheck %s +// RUN: %clang_cc1 %s -emit-llvm -o - -triple=thumbv7-unknown-linux | FileCheck %s + +typedef int _Atomic_word; +_Atomic_word exchange_and_add(volatile _Atomic_word *__mem, int __val) { + return __atomic_fetch_add(__mem, __val, __ATOMIC_ACQ_REL); +} + +// CHECK: define {{.*}} @exchange_and_add +// CHECK: atomicrmw {{.*}} add diff --git a/test/CodeGen/may-alias.c b/test/CodeGen/may-alias.c index b73ee15f874..c76724444bf 100644 --- a/test/CodeGen/may-alias.c +++ b/test/CodeGen/may-alias.c @@ -1,5 +1,5 @@ -// RUN: %clang_cc1 -Werror -triple i386-unknown-unknown -emit-llvm -O1 -disable-llvm-optzns -o %t %s -// RUN: FileCheck < %t %s +// RUN: %clang_cc1 -Werror -triple i386-unknown-unknown -emit-llvm -O1 -disable-llvm-optzns -o - %s | FileCheck %s +// RUN: %clang_cc1 -Werror -triple i386-unknown-unknown -emit-llvm -O1 -struct-path-tbaa -disable-llvm-optzns -o - %s | FileCheck %s -check-prefix=PATH // Types with the may_alias attribute should be considered equivalent // to char for aliasing. @@ -9,8 +9,10 @@ typedef int __attribute__((may_alias)) aliasing_int; void test0(aliasing_int *ai, int *i) { // CHECK: store i32 0, i32* %{{.*}}, !tbaa !1 +// PATH: store i32 0, i32* %{{.*}}, !tbaa [[TAG_CHAR:!.*]] *ai = 0; // CHECK: store i32 1, i32* %{{.*}}, !tbaa !3 +// PATH: store i32 1, i32* %{{.*}}, !tbaa [[TAG_INT:!.*]] *i = 1; } @@ -19,8 +21,10 @@ struct Test1 { int x; }; struct Test1MA { int x; } __attribute__((may_alias)); void test1(struct Test1MA *p1, struct Test1 *p2) { // CHECK: store i32 2, i32* {{%.*}}, !tbaa !1 + // PATH: store i32 2, i32* {{%.*}}, !tbaa [[TAG_CHAR]] p1->x = 2; // CHECK: store i32 3, i32* {{%.*}}, !tbaa !3 + // PATH: store i32 3, i32* {{%.*}}, !tbaa [[TAG_test1_x:!.*]] p2->x = 3; } @@ -28,3 +32,10 @@ void test1(struct Test1MA *p1, struct Test1 *p2) { // CHECK: !1 = metadata !{metadata !"omnipotent char", metadata !2} // CHECK: !2 = metadata !{metadata !"Simple C/C++ TBAA"} // CHECK: !3 = metadata !{metadata !"int", metadata !1} + +// PATH: [[TYPE_CHAR:!.*]] = metadata !{metadata !"omnipotent char", metadata !{{.*}} +// PATH: [[TAG_CHAR]] = metadata !{metadata [[TYPE_CHAR]], metadata [[TYPE_CHAR]], i64 0} +// PATH: [[TAG_INT]] = metadata !{metadata [[TYPE_INT:!.*]], metadata [[TYPE_INT]], i64 0} +// PATH: [[TYPE_INT]] = metadata !{metadata !"int", metadata [[TYPE_CHAR]] +// PATH: [[TAG_test1_x]] = metadata !{metadata [[TYPE_test1:!.*]], metadata [[TYPE_INT]], i64 0} +// PATH: [[TYPE_test1]] = metadata !{metadata !"_ZTS5Test1", metadata [[TYPE_INT]], i64 0} diff --git a/test/CodeGen/mips-inline-asm-modifiers.c b/test/CodeGen/mips-inline-asm-modifiers.c new file mode 100644 index 00000000000..7c4ca2ce144 --- /dev/null +++ b/test/CodeGen/mips-inline-asm-modifiers.c @@ -0,0 +1,35 @@ +// RUN: %clang -target mipsel-unknown-linux -S -o - -emit-llvm %s \ +// RUN: | FileCheck %s + +// This checks that the frontend will accept inline asm operand modifiers + +int printf(const char*, ...); + + // CHECK: %{{[0-9]+}} = call i32 asm ".set noreorder;\0Alw $0,$1;\0A.set reorder;\0A", "=r,*m"(i32* getelementptr inbounds ([8 x i32]* @b, i32 {{[0-9]+}}, i32 {{[0-9]+}})) #2, !srcloc !0 + // CHECK: %{{[0-9]+}} = call i32 asm "lw $0,${1:D};\0A", "=r,*m"(i32* getelementptr inbounds ([8 x i32]* @b, i32 {{[0-9]+}}, i32 {{[0-9]+}})) #2, !srcloc !1 +int b[8] = {0,1,2,3,4,5,6,7}; +int main() +{ + int i; + + // The first word. Notice, no 'D' + {asm ( + ".set noreorder;\n" + "lw %0,%1;\n" + ".set reorder;\n" + : "=r" (i) + : "m" (*(b+4)));} + + printf("%d\n",i); + + // The second word + {asm ( + "lw %0,%D1;\n" + : "=r" (i) + : "m" (*(b+4)) + );} + + printf("%d\n",i); + + return 1; +} diff --git a/test/CodeGen/ms-inline-asm-64.c b/test/CodeGen/ms-inline-asm-64.c index 8d2940d4e06..dd7b9b3349e 100644 --- a/test/CodeGen/ms-inline-asm-64.c +++ b/test/CodeGen/ms-inline-asm-64.c @@ -5,14 +5,42 @@ void t1() { int var = 10; __asm mov rax, offset var ; rax = address of myvar // CHECK: t1 -// CHECK: call void asm sideeffect inteldialect "mov rax, $0", "r,~{rax},~{dirflag},~{fpsr},~{flags}"(i32* %{{.*}}) [[NUW:#[0-9]+]] +// CHECK: call void asm sideeffect inteldialect "mov rax, $0", "r,~{rax},~{dirflag},~{fpsr},~{flags}"(i32* %{{.*}}) } void t2() { int var = 10; __asm mov [eax], offset var // CHECK: t2 -// CHECK: call void asm sideeffect inteldialect "mov [eax], $0", "r,~{dirflag},~{fpsr},~{flags}"(i32* %{{.*}}) [[NUW]] +// CHECK: call void asm sideeffect inteldialect "mov [eax], $0", "r,~{dirflag},~{fpsr},~{flags}"(i32* %{{.*}}) } -// CHECK: attributes [[NUW]] = { nounwind } +struct t3_type { int a, b; }; + +int t3() { + struct t3_type foo; + foo.a = 1; + foo.b = 2; + __asm { + lea ebx, foo + mov eax, [ebx].0 + mov [ebx].4, ecx + } + return foo.b; +// CHECK: t3 +// CHECK: call void asm sideeffect inteldialect "lea ebx, qword ptr $0\0A\09mov eax, [ebx].0\0A\09mov [ebx].4, ecx", "*m,~{eax},~{ebx},~{dirflag},~{fpsr},~{flags}"(%struct.t3_type* %{{.*}}) +} + +int t4() { + struct t3_type foo; + foo.a = 1; + foo.b = 2; + __asm { + lea ebx, foo + mov eax, [ebx].foo.a + mov [ebx].foo.b, ecx + } + return foo.b; +// CHECK: t4 +// CHECK: call void asm sideeffect inteldialect "lea ebx, qword ptr $0\0A\09mov eax, [ebx].0\0A\09mov [ebx].4, ecx", "*m,~{eax},~{ebx},~{dirflag},~{fpsr},~{flags}"(%struct.t3_type* %{{.*}}) +} diff --git a/test/CodeGen/ms-inline-asm.c b/test/CodeGen/ms-inline-asm.c index d50ecfe5b4b..c71a8df0ec7 100644 --- a/test/CodeGen/ms-inline-asm.c +++ b/test/CodeGen/ms-inline-asm.c @@ -169,36 +169,6 @@ void t17() { // CHECK: call void asm sideeffect inteldialect ".byte 0x4B", "~{dirflag},~{fpsr},~{flags}"() } -struct t18_type { int a, b; }; - -int t18() { - struct t18_type foo; - foo.a = 1; - foo.b = 2; - __asm { - lea ebx, foo - mov eax, [ebx].0 - mov [ebx].4, ecx - } - return foo.b; -// CHECK: t18 -// CHECK: call void asm sideeffect inteldialect "lea ebx, qword ptr foo\0A\09mov eax, [ebx].0\0A\09mov [ebx].4, ecx", "~{eax},~{dirflag},~{fpsr},~{flags}"() -} - -int t19() { - struct t18_type foo; - foo.a = 1; - foo.b = 2; - __asm { - lea ebx, foo - mov eax, [ebx].foo.a - mov [ebx].foo.b, ecx - } - return foo.b; -// CHECK: t19 -// CHECK: call void asm sideeffect inteldialect "lea ebx, qword ptr foo\0A\09mov eax, [ebx].0\0A\09mov [ebx].4, ecx", "~{eax},~{dirflag},~{fpsr},~{flags}"() -} - void t20() { char bar; int foo; @@ -284,9 +254,9 @@ void t25() { __asm mov eax, 0xa2h __asm mov eax, 0xa2 // CHECK: t25 -// CHECK: call void asm sideeffect inteldialect "mov eax, $$0ffffffffh", "~{eax},~{dirflag},~{fpsr},~{flags}"() -// CHECK: call void asm sideeffect inteldialect "mov eax, $$0fh", "~{eax},~{dirflag},~{fpsr},~{flags}"() -// CHECK: call void asm sideeffect inteldialect "mov eax, $$0a2h", "~{eax},~{dirflag},~{fpsr},~{flags}"() +// CHECK: call void asm sideeffect inteldialect "mov eax, $$4294967295", "~{eax},~{dirflag},~{fpsr},~{flags}"() +// CHECK: call void asm sideeffect inteldialect "mov eax, $$15", "~{eax},~{dirflag},~{fpsr},~{flags}"() +// CHECK: call void asm sideeffect inteldialect "mov eax, $$162", "~{eax},~{dirflag},~{fpsr},~{flags}"() // CHECK: call void asm sideeffect inteldialect "mov eax, $$0xa2h", "~{eax},~{dirflag},~{fpsr},~{flags}"() // CHECK: call void asm sideeffect inteldialect "mov eax, $$0xa2", "~{eax},~{dirflag},~{fpsr},~{flags}"() } @@ -310,7 +280,7 @@ void t26() { void t27() { __asm mov eax, fs:[0h] // CHECK: t27 -// CHECK: call void asm sideeffect inteldialect "mov eax, fs:[0h]", "~{eax},~{dirflag},~{fpsr},~{flags}"() +// CHECK: call void asm sideeffect inteldialect "mov eax, fs:[$$0h]", "~{eax},~{dirflag},~{fpsr},~{flags}"() } void t28() { @@ -390,3 +360,81 @@ void t34() { // CHECK: call void asm sideeffect inteldialect "prefetchnta $$64[eax]", "~{dirflag},~{fpsr},~{flags}"() // CHECK: call void asm sideeffect inteldialect "mov eax, dword ptr $$4[eax]", "~{eax},~{dirflag},~{fpsr},~{flags}"() } + +void t35() { + __asm prefetchnta [eax + (200*64)] + __asm mov eax, dword ptr [eax + (200*64)] +// CHECK: t35 +// CHECK: call void asm sideeffect inteldialect "prefetchnta [eax + ($$200*$$64)]", "~{dirflag},~{fpsr},~{flags}"() +// CHECK: call void asm sideeffect inteldialect "mov eax, dword ptr [eax + ($$200*$$64)]", "~{eax},~{dirflag},~{fpsr},~{flags}"() +} + +void t36() { + int arr[4]; + __asm mov eax, 4[arr] + __asm mov eax, 4[arr + 4] + __asm mov eax, 8[arr + 4 + 32*2 - 4] + __asm mov eax, 12[4 + arr] + __asm mov eax, 4[4 + arr + 4] + __asm mov eax, 4[64 + arr + (2*32)] + __asm mov eax, 4[64 + arr - 2*32] + __asm mov eax, [arr + 4] + __asm mov eax, [arr + 4 + 32*2 - 4] + __asm mov eax, [4 + arr] + __asm mov eax, [4 + arr + 4] + __asm mov eax, [64 + arr + (2*32)] + __asm mov eax, [64 + arr - 2*32] +// CHECK: t36 +// CHECK: call void asm sideeffect inteldialect "mov eax, dword ptr $$4$0", "*m,~{eax},~{dirflag},~{fpsr},~{flags}"([4 x i32]* %{{.*}}) +// CHECK: call void asm sideeffect inteldialect "mov eax, dword ptr $$8$0", "*m,~{eax},~{dirflag},~{fpsr},~{flags}"([4 x i32]* %{{.*}}) +// CHECK: call void asm sideeffect inteldialect "mov eax, dword ptr $$72$0", "*m,~{eax},~{dirflag},~{fpsr},~{flags}"([4 x i32]* %{{.*}}) +// CHECK: call void asm sideeffect inteldialect "mov eax, dword ptr $$16$0", "*m,~{eax},~{dirflag},~{fpsr},~{flags}"([4 x i32]* %{{.*}}) +// CHECK: call void asm sideeffect inteldialect "mov eax, dword ptr $$12$0", "*m,~{eax},~{dirflag},~{fpsr},~{flags}"([4 x i32]* %{{.*}}) +// CHECK: call void asm sideeffect inteldialect "mov eax, dword ptr $$132$0", "*m,~{eax},~{dirflag},~{fpsr},~{flags}"([4 x i32]* %{{.*}}) +// CHECK: call void asm sideeffect inteldialect "mov eax, dword ptr $$4$0", "*m,~{eax},~{dirflag},~{fpsr},~{flags}"([4 x i32]* %{{.*}}) +// CHECK: call void asm sideeffect inteldialect "mov eax, dword ptr $$4$0", "*m,~{eax},~{dirflag},~{fpsr},~{flags}"([4 x i32]* %{{.*}}) +// CHECK: call void asm sideeffect inteldialect "mov eax, dword ptr $$64$0", "*m,~{eax},~{dirflag},~{fpsr},~{flags}"([4 x i32]* %{{.*}}) +// CHECK: call void asm sideeffect inteldialect "mov eax, dword ptr $$4$0", "*m,~{eax},~{dirflag},~{fpsr},~{flags}"([4 x i32]* %{{.*}}) +// CHECK: call void asm sideeffect inteldialect "mov eax, dword ptr $$8$0", "*m,~{eax},~{dirflag},~{fpsr},~{flags}"([4 x i32]* %{{.*}}) +// CHECK: call void asm sideeffect inteldialect "mov eax, dword ptr $$128$0", "*m,~{eax},~{dirflag},~{fpsr},~{flags}"([4 x i32]* %{{.*}}) +// CHECK: call void asm sideeffect inteldialect "mov eax, dword ptr $0", "*m,~{eax},~{dirflag},~{fpsr},~{flags}"([4 x i32]* %{{.*}}) +} + +void t37() { + __asm mov eax, 4 + 8 + __asm mov eax, 4 + 8 * 16 + __asm mov eax, -4 + 8 * 16 + __asm mov eax, (4 + 4) * 16 + __asm mov eax, 4 + 8 * -16 + __asm mov eax, 4 + 16 / -8 + __asm mov eax, (16 + 16) / -8 +// CHECK: t37 +// CHECK: call void asm sideeffect inteldialect "mov eax, $$12", "~{eax},~{dirflag},~{fpsr},~{flags}"() +// CHECK: call void asm sideeffect inteldialect "mov eax, $$132", "~{eax},~{dirflag},~{fpsr},~{flags}"() +// CHECK: call void asm sideeffect inteldialect "mov eax, $$124", "~{eax},~{dirflag},~{fpsr},~{flags}"() +// CHECK: call void asm sideeffect inteldialect "mov eax, $$128", "~{eax},~{dirflag},~{fpsr},~{flags}"() +// CHECK: call void asm sideeffect inteldialect "mov eax, $$4294967172", "~{eax},~{dirflag},~{fpsr},~{flags}"() +// CHECK: call void asm sideeffect inteldialect "mov eax, $$2", "~{eax},~{dirflag},~{fpsr},~{flags}"() +// CHECK: call void asm sideeffect inteldialect "mov eax, $$4294967292", "~{eax},~{dirflag},~{fpsr},~{flags}"() +} + +void t38() { + int arr[4]; + __asm mov eax, 4+4[arr] + __asm mov eax, (4+4)[arr + 4] + __asm mov eax, 8*2[arr + 4 + 32*2 - 4] + __asm mov eax, 12+20[4 + arr] + __asm mov eax, 4*16+4[4 + arr + 4] + __asm mov eax, 4*4[64 + arr + (2*32)] + __asm mov eax, 4*(4-2)[64 + arr - 2*32] + __asm mov eax, 32*(4-2)[arr - 2*32] +// CHECK: t38 +// CHECK: call void asm sideeffect inteldialect "mov eax, dword ptr $$8$0", "*m,~{eax},~{dirflag},~{fpsr},~{flags}"([4 x i32]* %{{.*}}) +// CHECK: call void asm sideeffect inteldialect "mov eax, dword ptr $$12$0", "*m,~{eax},~{dirflag},~{fpsr},~{flags}"([4 x i32]* %{{.*}}) +// CHECK: call void asm sideeffect inteldialect "mov eax, dword ptr $$80$0", "*m,~{eax},~{dirflag},~{fpsr},~{flags}"([4 x i32]* %{{.*}}) +// CHECK: call void asm sideeffect inteldialect "mov eax, dword ptr $$36$0", "*m,~{eax},~{dirflag},~{fpsr},~{flags}"([4 x i32]* %{{.*}}) +// CHECK: call void asm sideeffect inteldialect "mov eax, dword ptr $$76$0", "*m,~{eax},~{dirflag},~{fpsr},~{flags}"([4 x i32]* %{{.*}}) +// CHECK: call void asm sideeffect inteldialect "mov eax, dword ptr $$144$0", "*m,~{eax},~{dirflag},~{fpsr},~{flags}"([4 x i32]* %{{.*}}) +// CHECK: call void asm sideeffect inteldialect "mov eax, dword ptr $$8$0", "*m,~{eax},~{dirflag},~{fpsr},~{flags}"([4 x i32]* %{{.*}}) +// CHECK: call void asm sideeffect inteldialect "mov eax, dword ptr $$0$0", "*m,~{eax},~{dirflag},~{fpsr},~{flags}"([4 x i32]* %{{.*}}) +} diff --git a/test/CodeGen/ms-inline-asm.cpp b/test/CodeGen/ms-inline-asm.cpp index 9c160be02fd..8f824f9947b 100644 --- a/test/CodeGen/ms-inline-asm.cpp +++ b/test/CodeGen/ms-inline-asm.cpp @@ -1,26 +1,105 @@ // REQUIRES: x86-64-registered-target // RUN: %clang_cc1 -x c++ %s -triple i386-apple-darwin10 -O0 -fasm-blocks -emit-llvm -o - | FileCheck %s +// rdar://13645930 + struct Foo { static int *ptr; static int a, b; + int arr[4]; struct Bar { static int *ptr; + char arr[2]; }; }; void t1() { Foo::ptr = (int *)0xDEADBEEF; Foo::Bar::ptr = (int *)0xDEADBEEF; - __asm mov eax, Foo::ptr - __asm mov eax, Foo::Bar::ptr - __asm mov eax, [Foo::ptr] - __asm mov eax, dword ptr [Foo::ptr] - __asm mov eax, dword ptr [Foo::ptr] + __asm mov eax, Foo ::ptr + __asm mov eax, Foo :: Bar :: ptr + __asm mov eax, [Foo:: ptr] + __asm mov eax, dword ptr [Foo :: ptr] + __asm mov eax, dword ptr [Foo :: ptr] // CHECK: @_Z2t1v -// CHECK: call void asm sideeffect inteldialect "mov eax, Foo::ptr", "~{eax},~{dirflag},~{fpsr},~{flags}"() -// CHECK: call void asm sideeffect inteldialect "mov eax, Foo::Bar::ptr", "~{eax},~{dirflag},~{fpsr},~{flags}"() -// CHECK: call void asm sideeffect inteldialect "mov eax, [Foo::ptr]", "~{eax},~{dirflag},~{fpsr},~{flags}"() -// CHECK: call void asm sideeffect inteldialect "mov eax, dword ptr [Foo::ptr]", "~{eax},~{dirflag},~{fpsr},~{flags}"() -// CHECK: call void asm sideeffect inteldialect "mov eax, dword ptr [Foo::ptr]", "~{eax},~{dirflag},~{fpsr},~{flags}"() +// CHECK: call void asm sideeffect inteldialect "mov eax, dword ptr $0", "*m,~{eax},~{dirflag},~{fpsr},~{flags}"(i32** @_ZN3Foo3ptrE) +// CHECK: call void asm sideeffect inteldialect "mov eax, dword ptr $0", "*m,~{eax},~{dirflag},~{fpsr},~{flags}"(i32** @_ZN3Foo3Bar3ptrE) +// CHECK: call void asm sideeffect inteldialect "mov eax, dword ptr $0", "*m,~{eax},~{dirflag},~{fpsr},~{flags}"(i32** @_ZN3Foo3ptrE) +// CHECK: call void asm sideeffect inteldialect "mov eax, dword ptr $0", "*m,~{eax},~{dirflag},~{fpsr},~{flags}"(i32** @_ZN3Foo3ptrE) +// CHECK: call void asm sideeffect inteldialect "mov eax, dword ptr $0", "*m,~{eax},~{dirflag},~{fpsr},~{flags}"(i32** @_ZN3Foo3ptrE) +} + +int gvar = 10; +void t2() { + int lvar = 10; + __asm mov eax, offset Foo::ptr + __asm mov eax, offset Foo::Bar::ptr +// CHECK: t2 +// CHECK: call void asm sideeffect inteldialect "mov eax, $0", "r,~{eax},~{dirflag},~{fpsr},~{flags}"(i32** @_ZN3Foo3ptrE) +// CHECK: call void asm sideeffect inteldialect "mov eax, $0", "r,~{eax},~{dirflag},~{fpsr},~{flags}"(i32** @_ZN3Foo3Bar3ptrE) +} + +// CHECK: define void @_Z2t3v() +void t3() { + __asm mov eax, LENGTH Foo::ptr +// CHECK: call void asm sideeffect inteldialect "mov eax, $$1", "~{eax},~{dirflag},~{fpsr},~{flags}"() + __asm mov eax, LENGTH Foo::Bar::ptr +// CHECK: call void asm sideeffect inteldialect "mov eax, $$1", "~{eax},~{dirflag},~{fpsr},~{flags}"() + __asm mov eax, LENGTH Foo::arr +// CHECK: call void asm sideeffect inteldialect "mov eax, $$4", "~{eax},~{dirflag},~{fpsr},~{flags}"() + __asm mov eax, LENGTH Foo::Bar::arr +// CHECK: call void asm sideeffect inteldialect "mov eax, $$2", "~{eax},~{dirflag},~{fpsr},~{flags}"() + + __asm mov eax, TYPE Foo::ptr +// CHECK: call void asm sideeffect inteldialect "mov eax, $$4", "~{eax},~{dirflag},~{fpsr},~{flags}"() + __asm mov eax, TYPE Foo::Bar::ptr +// CHECK: call void asm sideeffect inteldialect "mov eax, $$4", "~{eax},~{dirflag},~{fpsr},~{flags}"() + __asm mov eax, TYPE Foo::arr +// CHECK: call void asm sideeffect inteldialect "mov eax, $$4", "~{eax},~{dirflag},~{fpsr},~{flags}"() + __asm mov eax, TYPE Foo::Bar::arr +// CHECK: call void asm sideeffect inteldialect "mov eax, $$1", "~{eax},~{dirflag},~{fpsr},~{flags}"() + + __asm mov eax, SIZE Foo::ptr +// CHECK: call void asm sideeffect inteldialect "mov eax, $$4", "~{eax},~{dirflag},~{fpsr},~{flags}"() + __asm mov eax, SIZE Foo::Bar::ptr +// CHECK: call void asm sideeffect inteldialect "mov eax, $$4", "~{eax},~{dirflag},~{fpsr},~{flags}"() + __asm mov eax, SIZE Foo::arr +// CHECK: call void asm sideeffect inteldialect "mov eax, $$16", "~{eax},~{dirflag},~{fpsr},~{flags}"() + __asm mov eax, SIZE Foo::Bar::arr +// CHECK: call void asm sideeffect inteldialect "mov eax, $$2", "~{eax},~{dirflag},~{fpsr},~{flags}"() + +} + +struct T4 { + int x; + static int y; + void test(); +}; + +// CHECK: define void @_ZN2T44testEv( +void T4::test() { +// CHECK: [[T0:%.*]] = alloca [[T4:%.*]]*, +// CHECK: [[THIS:%.*]] = load [[T4]]** [[T0]] +// CHECK: [[X:%.*]] = getelementptr inbounds [[T4]]* [[THIS]], i32 0, i32 0 + __asm mov eax, x; +// CHECK: call void asm sideeffect inteldialect "mov eax, dword ptr $0", "*m,~{eax},~{dirflag},~{fpsr},~{flags}"(i32* [[X]]) + __asm mov y, eax; +// CHECK: call void asm sideeffect inteldialect "mov dword ptr $0, eax", "=*m,~{dirflag},~{fpsr},~{flags}"(i32* @_ZN2T41yE) +} + +template struct T5 { + template static T create(U); + void run(); +}; +// CHECK: define void @_Z5test5v() +void test5() { + // CHECK: [[X:%.*]] = alloca i32 + // CHECK: [[Y:%.*]] = alloca i32 + int x, y; + __asm push y + // CHECK: call void asm sideeffect inteldialect "push dword ptr $0", "=*m,~{dirflag},~{fpsr},~{flags}"(i32* [[Y]]) + __asm call T5::create + // CHECK: call void asm sideeffect inteldialect "call $0", "r,~{dirflag},~{fpsr},~{flags}"(i32 (float)* @_ZN2T5IiE6createIfEEiT_) + __asm mov x, eax + // CHECK: call void asm sideeffect inteldialect "mov dword ptr $0, eax", "=*m,~{dirflag},~{fpsr},~{flags}"(i32* [[X]]) } diff --git a/test/CodeGen/mult-alt-generic.c b/test/CodeGen/mult-alt-generic.c index 6cf6f0a2709..111679e3a98 100644 --- a/test/CodeGen/mult-alt-generic.c +++ b/test/CodeGen/mult-alt-generic.c @@ -6,7 +6,9 @@ // RUN: %clang_cc1 -triple mipsel %s -emit-llvm -o - | FileCheck %s // RUN: %clang_cc1 -triple powerpc %s -emit-llvm -o - | FileCheck %s // RUN: %clang_cc1 -triple powerpc64 %s -emit-llvm -o - | FileCheck %s +// RUN: %clang_cc1 -triple s390x %s -emit-llvm -o - | FileCheck %s // RUN: %clang_cc1 -triple sparc %s -emit-llvm -o - | FileCheck %s +// RUN: %clang_cc1 -triple sparcv9 %s -emit-llvm -o - | FileCheck %s // RUN: %clang_cc1 -triple thumb %s -emit-llvm -o - | FileCheck %s int mout0; diff --git a/test/CodeGen/pragma-pack-1.c b/test/CodeGen/pragma-pack-1.c index c30a62ac3c4..2a71c8f79b3 100644 --- a/test/CodeGen/pragma-pack-1.c +++ b/test/CodeGen/pragma-pack-1.c @@ -1,7 +1,68 @@ -// RUN: %clang_cc1 -emit-llvm -o - %s +// RUN: %clang_cc1 -triple x86_64-apple-macosx10.7.0 -emit-llvm -o - %s | FileCheck %s // PR4610 #pragma pack(4) struct ref { struct ref *next; } refs; + +// PR13580 +struct S +{ + char a[3]; +#pragma pack(1) + struct T + { + char b; + int c; + } d; +#pragma pack() + struct T2 + { + char b; + int c; + } d2; +} ss; + +struct S3 +{ + char a[3]; +#pragma pack(push, 2) + struct T3 + { + char b; + int c; + } d; +#pragma pack(pop) + struct T32 + { + char b; + int c; + } e; +} s3; + +struct S4 +{ + char a[3]; +#pragma align=packed + struct T4 + { + char b; + int c; + } d; + int e; +} s4; + +// CHECK: [[struct_ref:%[a-zA-Z0-9_.]+]] = type <{ [[struct_ref]]* }> +// CHECK: [[struct_S:%[a-zA-Z0-9_.]+]] = type { [3 x i8], [[struct_T:%[a-zA-Z0-9_.]+]], [[struct_T2:%[a-zA-Z0-9_.]+]] } +// CHECK: [[struct_T]] = type <{ i8, i32 }> +// CHECK: [[struct_T2]] = type { i8, i32 } + +// CHECK: %struct.S3 = type <{ [3 x i8], i8, %struct.T3, [2 x i8], %struct.T32 }> +// CHECK: %struct.T3 = type <{ i8, i8, i32 }> +// CHECK: %struct.T32 = type { i8, i32 } +// CHECK: %struct.S4 = type { [3 x i8], %struct.T4, i32 } +// CHECK: %struct.T4 = type <{ i8, i32 }> + +// CHECK: @refs = common global [[struct_ref]] +// CHECK: @ss = common global [[struct_S]] diff --git a/test/CodeGen/sparc-target-data.c b/test/CodeGen/sparc-target-data.c new file mode 100644 index 00000000000..bb32a2196af --- /dev/null +++ b/test/CodeGen/sparc-target-data.c @@ -0,0 +1,5 @@ +// RUN: %clang -target sparc-sun-solaris -o - -emit-llvm -S %s | FileCheck %s -check-prefix=V8 +// RUN: %clang -target sparcv9-sun-solaris -o - -emit-llvm -S %s | FileCheck %s -check-prefix=V9 + +// V8: 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-S64 +// V9: 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-v64:64:64-n32:64-S128 diff --git a/test/CodeGen/systemz-inline-asm.c b/test/CodeGen/systemz-inline-asm.c new file mode 100644 index 00000000000..8e5854f1bb6 --- /dev/null +++ b/test/CodeGen/systemz-inline-asm.c @@ -0,0 +1,131 @@ +// RUN: %clang_cc1 -triple s390x-linux-gnu -O2 -emit-llvm -o - %s | FileCheck %s + +unsigned int gi; +unsigned long gl; + +void test_store_m(unsigned int i) { + asm("st %1, %0" : "=m" (gi) : "r" (i)); +// CHECK: define void @test_store_m(i32 zeroext %i) +// CHECK: call void asm "st $1, $0", "=*m,r"(i32* @gi, i32 %i) +} + +void test_store_Q(unsigned int i) { + asm("st %1, %0" : "=Q" (gi) : "r" (i)); +// CHECK: define void @test_store_Q(i32 zeroext %i) +// CHECK: call void asm "st $1, $0", "=*Q,r"(i32* @gi, i32 %i) +} + +void test_store_R(unsigned int i) { + asm("st %1, %0" : "=R" (gi) : "r" (i)); +// CHECK: define void @test_store_R(i32 zeroext %i) +// CHECK: call void asm "st $1, $0", "=*R,r"(i32* @gi, i32 %i) +} + +void test_store_S(unsigned int i) { + asm("st %1, %0" : "=S" (gi) : "r" (i)); +// CHECK: define void @test_store_S(i32 zeroext %i) +// CHECK: call void asm "st $1, $0", "=*S,r"(i32* @gi, i32 %i) +} + +void test_store_T(unsigned int i) { + asm("st %1, %0" : "=T" (gi) : "r" (i)); +// CHECK: define void @test_store_T(i32 zeroext %i) +// CHECK: call void asm "st $1, $0", "=*T,r"(i32* @gi, i32 %i) +} + +int test_load_m() { + unsigned int i; + asm("l %0, %1" : "=r" (i) : "m" (gi)); + return i; +// CHECK: define signext i32 @test_load_m() +// CHECK: call i32 asm "l $0, $1", "=r,*m"(i32* @gi) +} + +int test_load_Q() { + unsigned int i; + asm("l %0, %1" : "=r" (i) : "Q" (gi)); + return i; +// CHECK: define signext i32 @test_load_Q() +// CHECK: call i32 asm "l $0, $1", "=r,*Q"(i32* @gi) +} + +int test_load_R() { + unsigned int i; + asm("l %0, %1" : "=r" (i) : "R" (gi)); + return i; +// CHECK: define signext i32 @test_load_R() +// CHECK: call i32 asm "l $0, $1", "=r,*R"(i32* @gi) +} + +int test_load_S() { + unsigned int i; + asm("l %0, %1" : "=r" (i) : "S" (gi)); + return i; +// CHECK: define signext i32 @test_load_S() +// CHECK: call i32 asm "l $0, $1", "=r,*S"(i32* @gi) +} + +int test_load_T() { + unsigned int i; + asm("l %0, %1" : "=r" (i) : "T" (gi)); + return i; +// CHECK: define signext i32 @test_load_T() +// CHECK: call i32 asm "l $0, $1", "=r,*T"(i32* @gi) +} + +void test_mI(unsigned char *c) { + asm volatile("cli %0, %1" :: "Q" (*c), "I" (100)); +// CHECK: define void @test_mI(i8* %c) +// CHECK: call void asm sideeffect "cli $0, $1", "*Q,I"(i8* %c, i32 100) +} + +unsigned int test_dJa(unsigned int i, unsigned int j) { + asm("sll %0, %2(%3)" : "=d" (i) : "0" (i), "J" (1000), "a" (j)); + return i; +// CHECK: define zeroext i32 @test_dJa(i32 zeroext %i, i32 zeroext %j) +// CHECK: call i32 asm "sll $0, $2($3)", "=d,0,J,a"(i32 %i, i32 1000, i32 %j) +} + +unsigned long test_rK(unsigned long i) { + asm("aghi %0, %2" : "=r" (i) : "0" (i), "K" (-30000)); + return i; +// CHECK: define i64 @test_rK(i64 %i) +// CHECK: call i64 asm "aghi $0, $2", "=r,0,K"(i64 %i, i32 -30000) +} + +unsigned long test_rL(unsigned long i) { + asm("sllg %0, %1, %2" : "=r" (i) : "r" (i), "L" (500000)); + return i; +// CHECK: define i64 @test_rL(i64 %i) +// CHECK: call i64 asm "sllg $0, $1, $2", "=r,r,L"(i64 %i, i32 500000) +} + +void test_M() { + asm volatile("#FOO %0" :: "M"(0x7fffffff)); +// CHECK: define void @test_M() +// CHECK: call void asm sideeffect "#FOO $0", "M"(i32 2147483647) +} + +float test_f32(float f, float g) { + asm("aebr %0, %2" : "=f" (f) : "0" (f), "f" (g)); + return f; +// CHECK: define float @test_f32(float %f, float %g) +// CHECK: call float asm "aebr $0, $2", "=f,0,f"(float %f, float %g) +} + +double test_f64(double f, double g) { + asm("adbr %0, %2" : "=f" (f) : "0" (f), "f" (g)); + return f; +// CHECK: define double @test_f64(double %f, double %g) +// CHECK: call double asm "adbr $0, $2", "=f,0,f"(double %f, double %g) +} + +long double test_f128(long double f, long double g) { + asm("axbr %0, %2" : "=f" (f) : "0" (f), "f" (g)); + return f; +// CHECK: define void @test_f128(fp128* noalias nocapture sret [[DEST:%.*]], fp128* byval nocapture, fp128* byval nocapture) +// CHECK: %f = load fp128* %0 +// CHECK: %g = load fp128* %1 +// CHECK: [[RESULT:%.*]] = tail call fp128 asm "axbr $0, $2", "=f,0,f"(fp128 %f, fp128 %g) +// CHECK: store fp128 [[RESULT]], fp128* [[DEST]] +} diff --git a/test/CodeGen/tbaa-class.cpp b/test/CodeGen/tbaa-class.cpp new file mode 100644 index 00000000000..967ba19a04d --- /dev/null +++ b/test/CodeGen/tbaa-class.cpp @@ -0,0 +1,226 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin -O1 -disable-llvm-optzns %s -emit-llvm -o - | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-apple-darwin -O1 -struct-path-tbaa -disable-llvm-optzns %s -emit-llvm -o - | FileCheck %s -check-prefix=PATH +// Test TBAA metadata generated by front-end. + +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +typedef unsigned long long uint64_t; +class StructA +{ +public: + uint16_t f16; + uint32_t f32; + uint16_t f16_2; + uint32_t f32_2; +}; +class StructB +{ +public: + uint16_t f16; + StructA a; + uint32_t f32; +}; +class StructC +{ +public: + uint16_t f16; + StructB b; + uint32_t f32; +}; +class StructD +{ +public: + uint16_t f16; + StructB b; + uint32_t f32; + uint8_t f8; +}; + +class StructS +{ +public: + uint16_t f16; + uint32_t f32; +}; +class StructS2 : public StructS +{ +public: + uint16_t f16_2; + uint32_t f32_2; +}; + +uint32_t g(uint32_t *s, StructA *A, uint64_t count) { +// CHECK: define i32 @{{.*}}( +// CHECK: store i32 1, i32* %{{.*}}, align 4, !tbaa !4 +// CHECK: store i32 4, i32* %{{.*}}, align 4, !tbaa !4 +// PATH: define i32 @{{.*}}( +// PATH: store i32 1, i32* %{{.*}}, align 4, !tbaa [[TAG_i32:!.*]] +// PATH: store i32 4, i32* %{{.*}}, align 4, !tbaa [[TAG_A_f32:!.*]] + *s = 1; + A->f32 = 4; + return *s; +} + +uint32_t g2(uint32_t *s, StructA *A, uint64_t count) { +// CHECK: define i32 @{{.*}}( +// CHECK: store i32 1, i32* %{{.*}}, align 4, !tbaa !4 +// CHECK: store i16 4, i16* %{{.*}}, align 2, !tbaa !5 +// PATH: define i32 @{{.*}}( +// PATH: store i32 1, i32* %{{.*}}, align 4, !tbaa [[TAG_i32]] +// PATH: store i16 4, i16* %{{.*}}, align 2, !tbaa [[TAG_A_f16:!.*]] + *s = 1; + A->f16 = 4; + return *s; +} + +uint32_t g3(StructA *A, StructB *B, uint64_t count) { +// CHECK: define i32 @{{.*}}( +// CHECK: store i32 1, i32* %{{.*}}, align 4, !tbaa !4 +// CHECK: store i32 4, i32* %{{.*}}, align 4, !tbaa !4 +// PATH: define i32 @{{.*}}( +// PATH: store i32 1, i32* %{{.*}}, align 4, !tbaa [[TAG_A_f32]] +// PATH: store i32 4, i32* %{{.*}}, align 4, !tbaa [[TAG_B_a_f32:!.*]] + A->f32 = 1; + B->a.f32 = 4; + return A->f32; +} + +uint32_t g4(StructA *A, StructB *B, uint64_t count) { +// CHECK: define i32 @{{.*}}( +// CHECK: store i32 1, i32* %{{.*}}, align 4, !tbaa !4 +// CHECK: store i16 4, i16* %{{.*}}, align 2, !tbaa !5 +// PATH: define i32 @{{.*}}( +// PATH: store i32 1, i32* %{{.*}}, align 4, !tbaa [[TAG_A_f32]] +// PATH: store i16 4, i16* %{{.*}}, align 2, !tbaa [[TAG_B_a_f16:!.*]] + A->f32 = 1; + B->a.f16 = 4; + return A->f32; +} + +uint32_t g5(StructA *A, StructB *B, uint64_t count) { +// CHECK: define i32 @{{.*}}( +// CHECK: store i32 1, i32* %{{.*}}, align 4, !tbaa !4 +// CHECK: store i32 4, i32* %{{.*}}, align 4, !tbaa !4 +// PATH: define i32 @{{.*}}( +// PATH: store i32 1, i32* %{{.*}}, align 4, !tbaa [[TAG_A_f32]] +// PATH: store i32 4, i32* %{{.*}}, align 4, !tbaa [[TAG_B_f32:!.*]] + A->f32 = 1; + B->f32 = 4; + return A->f32; +} + +uint32_t g6(StructA *A, StructB *B, uint64_t count) { +// CHECK: define i32 @{{.*}}( +// CHECK: store i32 1, i32* %{{.*}}, align 4, !tbaa !4 +// CHECK: store i32 4, i32* %{{.*}}, align 4, !tbaa !4 +// PATH: define i32 @{{.*}}( +// PATH: store i32 1, i32* %{{.*}}, align 4, !tbaa [[TAG_A_f32]] +// PATH: store i32 4, i32* %{{.*}}, align 4, !tbaa [[TAG_B_a_f32_2:!.*]] + A->f32 = 1; + B->a.f32_2 = 4; + return A->f32; +} + +uint32_t g7(StructA *A, StructS *S, uint64_t count) { +// CHECK: define i32 @{{.*}}( +// CHECK: store i32 1, i32* %{{.*}}, align 4, !tbaa !4 +// CHECK: store i32 4, i32* %{{.*}}, align 4, !tbaa !4 +// PATH: define i32 @{{.*}}( +// PATH: store i32 1, i32* %{{.*}}, align 4, !tbaa [[TAG_A_f32]] +// PATH: store i32 4, i32* %{{.*}}, align 4, !tbaa [[TAG_S_f32:!.*]] + A->f32 = 1; + S->f32 = 4; + return A->f32; +} + +uint32_t g8(StructA *A, StructS *S, uint64_t count) { +// CHECK: define i32 @{{.*}}( +// CHECK: store i32 1, i32* %{{.*}}, align 4, !tbaa !4 +// CHECK: store i16 4, i16* %{{.*}}, align 2, !tbaa !5 +// PATH: define i32 @{{.*}}( +// PATH: store i32 1, i32* %{{.*}}, align 4, !tbaa [[TAG_A_f32]] +// PATH: store i16 4, i16* %{{.*}}, align 2, !tbaa [[TAG_S_f16:!.*]] + A->f32 = 1; + S->f16 = 4; + return A->f32; +} + +uint32_t g9(StructS *S, StructS2 *S2, uint64_t count) { +// CHECK: define i32 @{{.*}}( +// CHECK: store i32 1, i32* %{{.*}}, align 4, !tbaa !4 +// CHECK: store i32 4, i32* %{{.*}}, align 4, !tbaa !4 +// PATH: define i32 @{{.*}}( +// PATH: store i32 1, i32* %{{.*}}, align 4, !tbaa [[TAG_S_f32]] +// PATH: store i32 4, i32* %{{.*}}, align 4, !tbaa [[TAG_S_f32:!.*]] + S->f32 = 1; + S2->f32 = 4; + return S->f32; +} + +uint32_t g10(StructS *S, StructS2 *S2, uint64_t count) { +// CHECK: define i32 @{{.*}}( +// CHECK: store i32 1, i32* %{{.*}}, align 4, !tbaa !4 +// CHECK: store i32 4, i32* %{{.*}}, align 4, !tbaa !4 +// PATH: define i32 @{{.*}}( +// PATH: store i32 1, i32* %{{.*}}, align 4, !tbaa [[TAG_S_f32]] +// PATH: store i32 4, i32* %{{.*}}, align 4, !tbaa [[TAG_S2_f32_2:!.*]] + S->f32 = 1; + S2->f32_2 = 4; + return S->f32; +} + +uint32_t g11(StructC *C, StructD *D, uint64_t count) { +// CHECK: define i32 @{{.*}}( +// CHECK: store i32 1, i32* %{{.*}}, align 4, !tbaa !4 +// CHECK: store i32 4, i32* %{{.*}}, align 4, !tbaa !4 +// PATH: define i32 @{{.*}}( +// PATH: store i32 1, i32* %{{.*}}, align 4, !tbaa [[TAG_C_b_a_f32:!.*]] +// PATH: store i32 4, i32* %{{.*}}, align 4, !tbaa [[TAG_D_b_a_f32:!.*]] + C->b.a.f32 = 1; + D->b.a.f32 = 4; + return C->b.a.f32; +} + +uint32_t g12(StructC *C, StructD *D, uint64_t count) { +// CHECK: define i32 @{{.*}}( +// CHECK: store i32 1, i32* %{{.*}}, align 4, !tbaa !4 +// CHECK: store i32 4, i32* %{{.*}}, align 4, !tbaa !4 +// TODO: differentiate the two accesses. +// PATH: define i32 @{{.*}}( +// PATH: store i32 1, i32* %{{.*}}, align 4, !tbaa [[TAG_B_a_f32]] +// PATH: store i32 4, i32* %{{.*}}, align 4, !tbaa [[TAG_B_a_f32]] + StructB *b1 = &(C->b); + StructB *b2 = &(D->b); + // b1, b2 have different context. + b1->a.f32 = 1; + b2->a.f32 = 4; + return b1->a.f32; +} + +// CHECK: !1 = metadata !{metadata !"omnipotent char", metadata !2} +// CHECK: !2 = metadata !{metadata !"Simple C/C++ TBAA"} +// CHECK: !4 = metadata !{metadata !"int", metadata !1} +// CHECK: !5 = metadata !{metadata !"short", metadata !1} + +// PATH: [[TYPE_CHAR:!.*]] = metadata !{metadata !"omnipotent char", metadata !3 +// PATH: [[TAG_i32]] = metadata !{metadata [[TYPE_INT:!.*]], metadata [[TYPE_INT]], i64 0} +// PATH: [[TYPE_INT]] = metadata !{metadata !"int", metadata [[TYPE_CHAR]] +// PATH: [[TAG_A_f32]] = metadata !{metadata [[TYPE_A:!.*]], metadata [[TYPE_INT]], i64 4} +// PATH: [[TYPE_A]] = metadata !{metadata !"_ZTS7StructA", metadata [[TYPE_SHORT:!.*]], i64 0, metadata [[TYPE_INT]], i64 4, metadata [[TYPE_SHORT]], i64 8, metadata [[TYPE_INT]], i64 12} +// PATH: [[TYPE_SHORT:!.*]] = metadata !{metadata !"short", metadata [[TYPE_CHAR]] +// PATH: [[TAG_A_f16]] = metadata !{metadata [[TYPE_A]], metadata [[TYPE_SHORT]], i64 0} +// PATH: [[TAG_B_a_f32]] = metadata !{metadata [[TYPE_B:!.*]], metadata [[TYPE_INT]], i64 8} +// PATH: [[TYPE_B]] = metadata !{metadata !"_ZTS7StructB", metadata [[TYPE_SHORT]], i64 0, metadata [[TYPE_A]], i64 4, metadata [[TYPE_INT]], i64 20} +// PATH: [[TAG_B_a_f16]] = metadata !{metadata [[TYPE_B]], metadata [[TYPE_SHORT]], i64 4} +// PATH: [[TAG_B_f32]] = metadata !{metadata [[TYPE_B]], metadata [[TYPE_INT]], i64 20} +// PATH: [[TAG_B_a_f32_2]] = metadata !{metadata [[TYPE_B]], metadata [[TYPE_INT]], i64 16} +// PATH: [[TAG_S_f32]] = metadata !{metadata [[TYPE_S:!.*]], metadata [[TYPE_INT]], i64 4} +// PATH: [[TYPE_S]] = metadata !{metadata !"_ZTS7StructS", metadata [[TYPE_SHORT]], i64 0, metadata [[TYPE_INT]], i64 4} +// PATH: [[TAG_S_f16]] = metadata !{metadata [[TYPE_S]], metadata [[TYPE_SHORT]], i64 0} +// PATH: [[TAG_S2_f32_2]] = metadata !{metadata [[TYPE_S2:!.*]], metadata [[TYPE_INT]], i64 12} +// PATH: [[TYPE_S2]] = metadata !{metadata !"_ZTS8StructS2", metadata [[TYPE_SHORT]], i64 8, metadata [[TYPE_INT]], i64 12} +// PATH: [[TAG_C_b_a_f32]] = metadata !{metadata [[TYPE_C:!.*]], metadata [[TYPE_INT]], i64 12} +// PATH: [[TYPE_C]] = metadata !{metadata !"_ZTS7StructC", metadata [[TYPE_SHORT]], i64 0, metadata [[TYPE_B]], i64 4, metadata [[TYPE_INT]], i64 28} +// PATH: [[TAG_D_b_a_f32]] = metadata !{metadata [[TYPE_D:!.*]], metadata [[TYPE_INT]], i64 12} +// PATH: [[TYPE_D]] = metadata !{metadata !"_ZTS7StructD", metadata [[TYPE_SHORT]], i64 0, metadata [[TYPE_B]], i64 4, metadata [[TYPE_INT]], i64 28, metadata [[TYPE_CHAR]], i64 32} diff --git a/test/CodeGen/tbaa-struct.cpp b/test/CodeGen/tbaa-struct.cpp index 12a6f4de82a..6d593a3c1b2 100644 --- a/test/CodeGen/tbaa-struct.cpp +++ b/test/CodeGen/tbaa-struct.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -emit-llvm -o - -O1 %s | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-apple-darwin -emit-llvm -o - -O1 %s | FileCheck %s // // Check that we generate !tbaa.struct metadata for struct copies. struct A { @@ -39,8 +39,36 @@ void copy3 (T1 *a, T1 *b) { // CHECK: call void @llvm.memcpy.p0i8.p0i8.i[[P]](i8* %{{.*}}, i8* %{{.*}}, i[[P]] 12, i32 4, i1 false), !tbaa.struct [[TS3:!.*]] +// Make sure that zero-length bitfield works. +#define ATTR __attribute__ ((ms_struct)) +struct five { + char a; + int :0; /* ignored; prior field is not a bitfield. */ + char b; + char c; +} ATTR; +void copy4(struct five *a, struct five *b) { + *a = *b; +} +// CHECK: call void @llvm.memcpy.p0i8.p0i8.i[[P]](i8* %{{.*}}, i8* %{{.*}}, i[[P]] 3, i32 1, i1 false), !tbaa.struct [[TS4:!.*]] + +struct six { + char a; + int :0; + char b; + char c; +}; +void copy5(struct six *a, struct six *b) { + *a = *b; +} +// CHECK: call void @llvm.memcpy.p0i8.p0i8.i[[P]](i8* %{{.*}}, i8* %{{.*}}, i[[P]] 6, i32 1, i1 false), !tbaa.struct [[TS5:!.*]] + // CHECK: [[TS]] = metadata !{i64 0, i64 2, metadata !{{.*}}, i64 4, i64 4, metadata !{{.*}}, i64 8, i64 1, metadata !{{.*}}, i64 12, i64 4, metadata !{{.*}}} +// CHECK: [[CHAR:!.*]] = metadata !{metadata !"omnipotent char", metadata !{{.*}}} +// CHECK: [[INT:!.*]] = metadata !{metadata !"int", metadata [[CHAR]]} // (offset, size) = (0,1) char; (4,2) short; (8,4) int; (12,1) char; (16,4) int; (20,4) int // CHECK: [[TS2]] = metadata !{i64 0, i64 1, metadata !{{.*}}, i64 4, i64 2, metadata !{{.*}}, i64 8, i64 4, metadata !{{.*}}, i64 12, i64 1, metadata !{{.*}}, i64 16, i64 4, metadata {{.*}}, i64 20, i64 4, metadata {{.*}}} // (offset, size) = (0,8) char; (0,2) char; (4,8) char // CHECK: [[TS3]] = metadata !{i64 0, i64 8, metadata !{{.*}}, i64 0, i64 2, metadata !{{.*}}, i64 4, i64 8, metadata !{{.*}}} +// CHECK: [[TS4]] = metadata !{i64 0, i64 1, metadata [[CHAR]], i64 1, i64 1, metadata [[CHAR]], i64 2, i64 1, metadata [[CHAR]]} +// CHECK: [[TS5]] = metadata !{i64 0, i64 1, metadata [[CHAR]], i64 4, i64 4, metadata [[INT]], i64 4, i64 1, metadata [[CHAR]], i64 5, i64 1, metadata [[CHAR]]} diff --git a/test/CodeGen/tbaa.cpp b/test/CodeGen/tbaa.cpp index c30e4a331d8..afb8893d3e6 100644 --- a/test/CodeGen/tbaa.cpp +++ b/test/CodeGen/tbaa.cpp @@ -1,8 +1,11 @@ -// RUN: %clang_cc1 -O1 -disable-llvm-optzns %s -emit-llvm -o - | FileCheck %s -// RUN: %clang_cc1 -O1 -struct-path-tbaa -disable-llvm-optzns %s -emit-llvm -o - | FileCheck %s -check-prefix=PATH +// RUN: %clang_cc1 -triple x86_64-apple-darwin -O1 -disable-llvm-optzns %s -emit-llvm -o - | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-apple-darwin -O1 -struct-path-tbaa -disable-llvm-optzns %s -emit-llvm -o - | FileCheck %s -check-prefix=PATH // Test TBAA metadata generated by front-end. -#include +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +typedef unsigned long long uint64_t; typedef struct { uint16_t f16; @@ -46,8 +49,8 @@ uint32_t g(uint32_t *s, StructA *A, uint64_t count) { // CHECK: store i32 1, i32* %{{.*}}, align 4, !tbaa !4 // CHECK: store i32 4, i32* %{{.*}}, align 4, !tbaa !4 // PATH: define i32 @{{.*}}( -// PATH: store i32 1, i32* %{{.*}}, align 4, !tbaa !4 -// PATH: store i32 4, i32* %{{.*}}, align 4, !tbaa !5 +// PATH: store i32 1, i32* %{{.*}}, align 4, !tbaa [[TAG_i32:!.*]] +// PATH: store i32 4, i32* %{{.*}}, align 4, !tbaa [[TAG_A_f32:!.*]] *s = 1; A->f32 = 4; return *s; @@ -58,8 +61,8 @@ uint32_t g2(uint32_t *s, StructA *A, uint64_t count) { // CHECK: store i32 1, i32* %{{.*}}, align 4, !tbaa !4 // CHECK: store i16 4, i16* %{{.*}}, align 2, !tbaa !5 // PATH: define i32 @{{.*}}( -// PATH: store i32 1, i32* %{{.*}}, align 4, !tbaa !4 -// PATH: store i16 4, i16* %{{.*}}, align 2, !tbaa !8 +// PATH: store i32 1, i32* %{{.*}}, align 4, !tbaa [[TAG_i32]] +// PATH: store i16 4, i16* %{{.*}}, align 2, !tbaa [[TAG_A_f16:!.*]] *s = 1; A->f16 = 4; return *s; @@ -70,8 +73,8 @@ uint32_t g3(StructA *A, StructB *B, uint64_t count) { // CHECK: store i32 1, i32* %{{.*}}, align 4, !tbaa !4 // CHECK: store i32 4, i32* %{{.*}}, align 4, !tbaa !4 // PATH: define i32 @{{.*}}( -// PATH: store i32 1, i32* %{{.*}}, align 4, !tbaa !5 -// PATH: store i32 4, i32* %{{.*}}, align 4, !tbaa !9 +// PATH: store i32 1, i32* %{{.*}}, align 4, !tbaa [[TAG_A_f32]] +// PATH: store i32 4, i32* %{{.*}}, align 4, !tbaa [[TAG_B_a_f32:!.*]] A->f32 = 1; B->a.f32 = 4; return A->f32; @@ -82,8 +85,8 @@ uint32_t g4(StructA *A, StructB *B, uint64_t count) { // CHECK: store i32 1, i32* %{{.*}}, align 4, !tbaa !4 // CHECK: store i16 4, i16* %{{.*}}, align 2, !tbaa !5 // PATH: define i32 @{{.*}}( -// PATH: store i32 1, i32* %{{.*}}, align 4, !tbaa !5 -// PATH: store i16 4, i16* %{{.*}}, align 2, !tbaa !11 +// PATH: store i32 1, i32* %{{.*}}, align 4, !tbaa [[TAG_A_f32]] +// PATH: store i16 4, i16* %{{.*}}, align 2, !tbaa [[TAG_B_a_f16:!.*]] A->f32 = 1; B->a.f16 = 4; return A->f32; @@ -94,8 +97,8 @@ uint32_t g5(StructA *A, StructB *B, uint64_t count) { // CHECK: store i32 1, i32* %{{.*}}, align 4, !tbaa !4 // CHECK: store i32 4, i32* %{{.*}}, align 4, !tbaa !4 // PATH: define i32 @{{.*}}( -// PATH: store i32 1, i32* %{{.*}}, align 4, !tbaa !5 -// PATH: store i32 4, i32* %{{.*}}, align 4, !tbaa !12 +// PATH: store i32 1, i32* %{{.*}}, align 4, !tbaa [[TAG_A_f32]] +// PATH: store i32 4, i32* %{{.*}}, align 4, !tbaa [[TAG_B_f32:!.*]] A->f32 = 1; B->f32 = 4; return A->f32; @@ -106,8 +109,8 @@ uint32_t g6(StructA *A, StructB *B, uint64_t count) { // CHECK: store i32 1, i32* %{{.*}}, align 4, !tbaa !4 // CHECK: store i32 4, i32* %{{.*}}, align 4, !tbaa !4 // PATH: define i32 @{{.*}}( -// PATH: store i32 1, i32* %{{.*}}, align 4, !tbaa !5 -// PATH: store i32 4, i32* %{{.*}}, align 4, !tbaa !13 +// PATH: store i32 1, i32* %{{.*}}, align 4, !tbaa [[TAG_A_f32]] +// PATH: store i32 4, i32* %{{.*}}, align 4, !tbaa [[TAG_B_a_f32_2:!.*]] A->f32 = 1; B->a.f32_2 = 4; return A->f32; @@ -118,8 +121,8 @@ uint32_t g7(StructA *A, StructS *S, uint64_t count) { // CHECK: store i32 1, i32* %{{.*}}, align 4, !tbaa !4 // CHECK: store i32 4, i32* %{{.*}}, align 4, !tbaa !4 // PATH: define i32 @{{.*}}( -// PATH: store i32 1, i32* %{{.*}}, align 4, !tbaa !5 -// PATH: store i32 4, i32* %{{.*}}, align 4, !tbaa !14 +// PATH: store i32 1, i32* %{{.*}}, align 4, !tbaa [[TAG_A_f32]] +// PATH: store i32 4, i32* %{{.*}}, align 4, !tbaa [[TAG_S_f32:!.*]] A->f32 = 1; S->f32 = 4; return A->f32; @@ -130,8 +133,8 @@ uint32_t g8(StructA *A, StructS *S, uint64_t count) { // CHECK: store i32 1, i32* %{{.*}}, align 4, !tbaa !4 // CHECK: store i16 4, i16* %{{.*}}, align 2, !tbaa !5 // PATH: define i32 @{{.*}}( -// PATH: store i32 1, i32* %{{.*}}, align 4, !tbaa !5 -// PATH: store i16 4, i16* %{{.*}}, align 2, !tbaa !16 +// PATH: store i32 1, i32* %{{.*}}, align 4, !tbaa [[TAG_A_f32]] +// PATH: store i16 4, i16* %{{.*}}, align 2, !tbaa [[TAG_S_f16:!.*]] A->f32 = 1; S->f16 = 4; return A->f32; @@ -142,8 +145,8 @@ uint32_t g9(StructS *S, StructS2 *S2, uint64_t count) { // CHECK: store i32 1, i32* %{{.*}}, align 4, !tbaa !4 // CHECK: store i32 4, i32* %{{.*}}, align 4, !tbaa !4 // PATH: define i32 @{{.*}}( -// PATH: store i32 1, i32* %{{.*}}, align 4, !tbaa !14 -// PATH: store i32 4, i32* %{{.*}}, align 4, !tbaa !17 +// PATH: store i32 1, i32* %{{.*}}, align 4, !tbaa [[TAG_S_f32]] +// PATH: store i32 4, i32* %{{.*}}, align 4, !tbaa [[TAG_S2_f32:!.*]] S->f32 = 1; S2->f32 = 4; return S->f32; @@ -154,8 +157,8 @@ uint32_t g10(StructS *S, StructS2 *S2, uint64_t count) { // CHECK: store i32 1, i32* %{{.*}}, align 4, !tbaa !4 // CHECK: store i16 4, i16* %{{.*}}, align 2, !tbaa !5 // PATH: define i32 @{{.*}}( -// PATH: store i32 1, i32* %{{.*}}, align 4, !tbaa !14 -// PATH: store i16 4, i16* %{{.*}}, align 2, !tbaa !19 +// PATH: store i32 1, i32* %{{.*}}, align 4, !tbaa [[TAG_S_f32]] +// PATH: store i16 4, i16* %{{.*}}, align 2, !tbaa [[TAG_S2_f16:!.*]] S->f32 = 1; S2->f16 = 4; return S->f32; @@ -166,8 +169,8 @@ uint32_t g11(StructC *C, StructD *D, uint64_t count) { // CHECK: store i32 1, i32* %{{.*}}, align 4, !tbaa !4 // CHECK: store i32 4, i32* %{{.*}}, align 4, !tbaa !4 // PATH: define i32 @{{.*}}( -// PATH: store i32 1, i32* %{{.*}}, align 4, !tbaa !20 -// PATH: store i32 4, i32* %{{.*}}, align 4, !tbaa !22 +// PATH: store i32 1, i32* %{{.*}}, align 4, !tbaa [[TAG_C_b_a_f32:!.*]] +// PATH: store i32 4, i32* %{{.*}}, align 4, !tbaa [[TAG_D_b_a_f32:!.*]] C->b.a.f32 = 1; D->b.a.f32 = 4; return C->b.a.f32; @@ -179,8 +182,8 @@ uint32_t g12(StructC *C, StructD *D, uint64_t count) { // CHECK: store i32 4, i32* %{{.*}}, align 4, !tbaa !4 // TODO: differentiate the two accesses. // PATH: define i32 @{{.*}}( -// PATH: store i32 1, i32* %{{.*}}, align 4, !tbaa !9 -// PATH: store i32 4, i32* %{{.*}}, align 4, !tbaa !9 +// PATH: store i32 1, i32* %{{.*}}, align 4, !tbaa [[TAG_B_a_f32]] +// PATH: store i32 4, i32* %{{.*}}, align 4, !tbaa [[TAG_B_a_f32]] StructB *b1 = &(C->b); StructB *b2 = &(D->b); // b1, b2 have different context. @@ -189,29 +192,64 @@ uint32_t g12(StructC *C, StructD *D, uint64_t count) { return b1->a.f32; } +// Make sure that zero-length bitfield works. +#define ATTR __attribute__ ((ms_struct)) +struct five { + char a; + int :0; /* ignored; prior field is not a bitfield. */ + char b; + char c; +} ATTR; +char g13(struct five *a, struct five *b) { + return a->b; +// CHECK: define signext i8 @{{.*}}( +// CHECK: load i8* %{{.*}}, align 1, !tbaa !1 +// PATH: define signext i8 @{{.*}}( +// PATH: load i8* %{{.*}}, align 1, !tbaa [[TAG_five_b:!.*]] +} + +struct six { + char a; + int :0; + char b; + char c; +}; +char g14(struct six *a, struct six *b) { +// CHECK: define signext i8 @{{.*}}( +// CHECK: load i8* %{{.*}}, align 1, !tbaa !1 +// PATH: define signext i8 @{{.*}}( +// PATH: load i8* %{{.*}}, align 1, !tbaa [[TAG_six_b:!.*]] + return a->b; +} + // CHECK: !1 = metadata !{metadata !"omnipotent char", metadata !2} // CHECK: !2 = metadata !{metadata !"Simple C/C++ TBAA"} // CHECK: !4 = metadata !{metadata !"int", metadata !1} // CHECK: !5 = metadata !{metadata !"short", metadata !1} -// PATH: !1 = metadata !{metadata !"omnipotent char", metadata !2} -// PATH: !4 = metadata !{metadata !"int", metadata !1} -// PATH: !5 = metadata !{metadata !6, metadata !4, i64 4} -// PATH: !6 = metadata !{metadata !"_ZTS7StructA", i64 0, metadata !7, i64 4, metadata !4} -// PATH: !7 = metadata !{metadata !"short", metadata !1} -// PATH: !8 = metadata !{metadata !6, metadata !7, i64 0} -// PATH: !9 = metadata !{metadata !10, metadata !4, i64 8} -// PATH: !10 = metadata !{metadata !"_ZTS7StructB", i64 0, metadata !7, i64 4, metadata !6, i64 20, metadata !4} -// PATH: !11 = metadata !{metadata !10, metadata !7, i64 4} -// PATH: !12 = metadata !{metadata !10, metadata !4, i64 20} -// PATH: !13 = metadata !{metadata !10, metadata !4, i64 16} -// PATH: !14 = metadata !{metadata !15, metadata !4, i64 4} -// PATH: !15 = metadata !{metadata !"_ZTS7StructS", i64 0, metadata !7, i64 4, metadata !4} -// PATH: !16 = metadata !{metadata !15, metadata !7, i64 0} -// PATH: !17 = metadata !{metadata !18, metadata !4, i64 4} -// PATH: !18 = metadata !{metadata !"_ZTS8StructS2", i64 0, metadata !7, i64 4, metadata !4} -// PATH: !19 = metadata !{metadata !18, metadata !7, i64 0} -// PATH: !20 = metadata !{metadata !21, metadata !4, i64 12} -// PATH: !21 = metadata !{metadata !"_ZTS7StructC", i64 0, metadata !7, i64 4, metadata !10, i64 28, metadata !4} -// PATH: !22 = metadata !{metadata !23, metadata !4, i64 12} -// PATH: !23 = metadata !{metadata !"_ZTS7StructD", i64 0, metadata !7, i64 4, metadata !10, i64 28, metadata !4, i64 32, metadata !1} +// PATH: [[TYPE_CHAR:!.*]] = metadata !{metadata !"omnipotent char", metadata !3 +// PATH: [[TAG_i32]] = metadata !{metadata [[TYPE_INT:!.*]], metadata [[TYPE_INT]], i64 0} +// PATH: [[TYPE_INT]] = metadata !{metadata !"int", metadata [[TYPE_CHAR]] +// PATH: [[TAG_A_f32]] = metadata !{metadata [[TYPE_A:!.*]], metadata [[TYPE_INT]], i64 4} +// PATH: [[TYPE_A]] = metadata !{metadata !"_ZTS7StructA", metadata [[TYPE_SHORT:!.*]], i64 0, metadata [[TYPE_INT]], i64 4, metadata [[TYPE_SHORT]], i64 8, metadata [[TYPE_INT]], i64 12} +// PATH: [[TYPE_SHORT:!.*]] = metadata !{metadata !"short", metadata [[TYPE_CHAR]] +// PATH: [[TAG_A_f16]] = metadata !{metadata [[TYPE_A]], metadata [[TYPE_SHORT]], i64 0} +// PATH: [[TAG_B_a_f32]] = metadata !{metadata [[TYPE_B:!.*]], metadata [[TYPE_INT]], i64 8} +// PATH: [[TYPE_B]] = metadata !{metadata !"_ZTS7StructB", metadata [[TYPE_SHORT]], i64 0, metadata [[TYPE_A]], i64 4, metadata [[TYPE_INT]], i64 20} +// PATH: [[TAG_B_a_f16]] = metadata !{metadata [[TYPE_B]], metadata [[TYPE_SHORT]], i64 4} +// PATH: [[TAG_B_f32]] = metadata !{metadata [[TYPE_B]], metadata [[TYPE_INT]], i64 20} +// PATH: [[TAG_B_a_f32_2]] = metadata !{metadata [[TYPE_B]], metadata [[TYPE_INT]], i64 16} +// PATH: [[TAG_S_f32]] = metadata !{metadata [[TYPE_S:!.*]], metadata [[TYPE_INT]], i64 4} +// PATH: [[TYPE_S]] = metadata !{metadata !"_ZTS7StructS", metadata [[TYPE_SHORT]], i64 0, metadata [[TYPE_INT]], i64 4} +// PATH: [[TAG_S_f16]] = metadata !{metadata [[TYPE_S]], metadata [[TYPE_SHORT]], i64 0} +// PATH: [[TAG_S2_f32]] = metadata !{metadata [[TYPE_S2:!.*]], metadata [[TYPE_INT]], i64 4} +// PATH: [[TYPE_S2]] = metadata !{metadata !"_ZTS8StructS2", metadata [[TYPE_SHORT]], i64 0, metadata [[TYPE_INT]], i64 4} +// PATH: [[TAG_S2_f16]] = metadata !{metadata [[TYPE_S2]], metadata [[TYPE_SHORT]], i64 0} +// PATH: [[TAG_C_b_a_f32]] = metadata !{metadata [[TYPE_C:!.*]], metadata [[TYPE_INT]], i64 12} +// PATH: [[TYPE_C]] = metadata !{metadata !"_ZTS7StructC", metadata [[TYPE_SHORT]], i64 0, metadata [[TYPE_B]], i64 4, metadata [[TYPE_INT]], i64 28} +// PATH: [[TAG_D_b_a_f32]] = metadata !{metadata [[TYPE_D:!.*]], metadata [[TYPE_INT]], i64 12} +// PATH: [[TYPE_D]] = metadata !{metadata !"_ZTS7StructD", metadata [[TYPE_SHORT]], i64 0, metadata [[TYPE_B]], i64 4, metadata [[TYPE_INT]], i64 28, metadata [[TYPE_CHAR]], i64 32} +// PATH: [[TAG_five_b]] = metadata !{metadata [[TYPE_five:!.*]], metadata [[TYPE_CHAR]], i64 1} +// PATH: [[TYPE_five]] = metadata !{metadata !"_ZTS4five", metadata [[TYPE_CHAR]], i64 0, metadata [[TYPE_CHAR]], i64 1, metadata [[TYPE_CHAR]], i64 2} +// PATH: [[TAG_six_b]] = metadata !{metadata [[TYPE_six:!.*]], metadata [[TYPE_CHAR]], i64 4} +// PATH: [[TYPE_six]] = metadata !{metadata !"_ZTS3six", metadata [[TYPE_CHAR]], i64 0, metadata [[TYPE_INT]], i64 4, metadata [[TYPE_CHAR]], i64 4, metadata [[TYPE_CHAR]], i64 5} diff --git a/test/CodeGen/thread-specifier.c b/test/CodeGen/thread-specifier.c index a2d3e62c8c5..8e21651cd88 100644 --- a/test/CodeGen/thread-specifier.c +++ b/test/CodeGen/thread-specifier.c @@ -10,6 +10,9 @@ // CHECK: @i = thread_local(initialexec) global // CHECK: @j = thread_local(localexec) global +// CHECK-NOT: @_ZTW +// CHECK-NOT: @_ZTH + __thread int a; extern __thread int b; int c() { return *&b; } diff --git a/test/CodeGen/x86_32-arguments-win32.c b/test/CodeGen/x86_32-arguments-win32.c index f18bb30fa47..77ff9e2ff3d 100644 --- a/test/CodeGen/x86_32-arguments-win32.c +++ b/test/CodeGen/x86_32-arguments-win32.c @@ -1,7 +1,7 @@ // RUN: %clang_cc1 -w -triple i386-pc-win32 -emit-llvm -o - %s | FileCheck %s // CHECK: define i64 @f1_1() -// CHECK: define void @f1_2(i32 %a0.0, i32 %a0.1) +// CHECK: define void @f1_2(%struct.s1* byval align 4 %a0) struct s1 { int a; int b; @@ -31,7 +31,7 @@ struct s4 { struct s4 f4_1(void) { while (1) {} } // CHECK: define i64 @f5_1() -// CHECK: define void @f5_2(double %a0.0) +// CHECK: define void @f5_2(%struct.s5* byval align 4) struct s5 { double a; }; @@ -39,7 +39,7 @@ struct s5 f5_1(void) { while (1) {} } void f5_2(struct s5 a0) {} // CHECK: define i32 @f6_1() -// CHECK: define void @f6_2(float %a0.0) +// CHECK: define void @f6_2(%struct.s6* byval align 4 %a0) struct s6 { float a; }; diff --git a/test/CodeGenCXX/cxx0x-initializer-stdinitializerlist.cpp b/test/CodeGenCXX/cxx0x-initializer-stdinitializerlist.cpp index 8d9fce040fd..d683493a83d 100644 --- a/test/CodeGenCXX/cxx0x-initializer-stdinitializerlist.cpp +++ b/test/CodeGenCXX/cxx0x-initializer-stdinitializerlist.cpp @@ -51,6 +51,12 @@ struct wantslist1 { // CHECK: @globalInitList1 = global %{{[^ ]+}} { i32* getelementptr inbounds ([3 x i32]* @_ZL25globalInitList1__initlist, i32 0, i32 0), i{{32|64}} 3 } std::initializer_list globalInitList1 = {1, 2, 3}; +namespace thread_local_global_array { + // CHECK: @_ZN25thread_local_global_arrayL11x__initlistE = internal thread_local global [4 x i32] [i32 1, i32 2, i32 3, i32 4] + // CHECK: @_ZN25thread_local_global_array1xE = thread_local global {{.*}} @_ZN25thread_local_global_arrayL11x__initlistE, {{.*}} i64 4 + std::initializer_list thread_local x = { 1, 2, 3, 4 }; +} + // CHECK: @_ZL25globalInitList2__initlist = internal global [2 x %{{[^ ]*}}] zeroinitializer // CHECK: @globalInitList2 = global %{{[^ ]+}} { %[[WITHARG:[^ *]+]]* getelementptr inbounds ([2 x // CHECK: appending global @@ -250,3 +256,22 @@ namespace PR12178 { map m{ {1, 2}, {3, 4} }; } + +namespace rdar13325066 { + struct X { ~X(); }; + + // CHECK: define void @_ZN12rdar133250664loopERNS_1XES1_ + void loop(X &x1, X &x2) { + // CHECK: br label + // CHECK: br i1 + // CHECK: br label + // CHECK call void @_ZN12rdar133250661XD1Ev + // CHECK: br label + // CHECK: br label + // CHECK: call void @_ZN12rdar133250661XD1Ev + // CHECK: br i1 + // CHECK: br label + // CHECK: ret void + for (X x : { x1, x2 }) { } + } +} diff --git a/test/CodeGenCXX/cxx11-thread-local-reference.cpp b/test/CodeGenCXX/cxx11-thread-local-reference.cpp new file mode 100644 index 00000000000..2ea9acda481 --- /dev/null +++ b/test/CodeGenCXX/cxx11-thread-local-reference.cpp @@ -0,0 +1,26 @@ +// RUN: %clang_cc1 -std=c++11 -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s + +int &f(); + +// CHECK: @r = thread_local global i32* null +thread_local int &r = f(); + +// CHECK: @_ZTH1r = alias void ()* @__tls_init + +int &g() { return r; } + +// CHECK: define {{.*}} @[[R_INIT:.*]]() +// CHECK: call i32* @_Z1fv() +// CHECK: store i32* %{{.*}}, i32** @r, align 8 + +// CHECK: define i32* @_Z1gv() +// CHECK: call i32* @_ZTW1r() +// CHECK: ret i32* %{{.*}} + +// CHECK: define weak_odr hidden i32* @_ZTW1r() { +// CHECK: call void @_ZTH1r() +// CHECK: load i32** @r, align 8 +// CHECK: ret i32* %{{.*}} + +// CHECK: define internal void @__tls_init() +// CHECK: call void @[[R_INIT]]() diff --git a/test/CodeGenCXX/cxx11-thread-local.cpp b/test/CodeGenCXX/cxx11-thread-local.cpp new file mode 100644 index 00000000000..a7141d133bb --- /dev/null +++ b/test/CodeGenCXX/cxx11-thread-local.cpp @@ -0,0 +1,173 @@ +// RUN: %clang_cc1 -std=c++11 -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s + +int f(); +int g(); + +// CHECK: @a = thread_local global i32 0 +thread_local int a = f(); +extern thread_local int b; +// CHECK: @c = global i32 0 +int c = b; +// CHECK: @_ZL1d = internal thread_local global i32 0 +static thread_local int d = g(); + +struct U { static thread_local int m; }; +// CHECK: @_ZN1U1mE = thread_local global i32 0 +thread_local int U::m = f(); + +template struct V { static thread_local int m; }; +template thread_local int V::m = g(); + +// CHECK: @e = global i32 0 +int e = V::m; + +// CHECK: @_ZN1VIiE1mE = weak_odr thread_local global i32 0 + +// CHECK: @_ZZ1fvE1n = internal thread_local global i32 0 + +// CHECK: @_ZGVZ1fvE1n = internal thread_local global i8 0 + +// CHECK: @_ZZ8tls_dtorvE1s = internal thread_local global +// CHECK: @_ZGVZ8tls_dtorvE1s = internal thread_local global i8 0 + +// CHECK: @_ZZ8tls_dtorvE1t = internal thread_local global +// CHECK: @_ZGVZ8tls_dtorvE1t = internal thread_local global i8 0 + +// CHECK: @_ZZ8tls_dtorvE1u = internal thread_local global +// CHECK: @_ZGVZ8tls_dtorvE1u = internal thread_local global i8 0 +// CHECK: @_ZGRZ8tls_dtorvE1u = internal thread_local global + +// CHECK: @_ZGVN1VIiE1mE = weak_odr thread_local global i64 0 + +// CHECK: @__tls_guard = internal thread_local global i8 0 + +// CHECK: @llvm.global_ctors = appending global {{.*}} @[[GLOBAL_INIT:[^ ]*]] + +// CHECK: @_ZTH1a = alias void ()* @__tls_init +// CHECK: @_ZTHL1d = alias internal void ()* @__tls_init +// CHECK: @_ZTHN1U1mE = alias void ()* @__tls_init +// CHECK: @_ZTHN1VIiE1mE = alias weak_odr void ()* @__tls_init + + +// Individual variable initialization functions: + +// CHECK: define {{.*}} @[[A_INIT:.*]]() +// CHECK: call i32 @_Z1fv() +// CHECK-NEXT: store i32 {{.*}}, i32* @a, align 4 + +// CHECK: define i32 @_Z1fv() +int f() { + // CHECK: %[[GUARD:.*]] = load i8* @_ZGVZ1fvE1n, align 1 + // CHECK: %[[NEED_INIT:.*]] = icmp eq i8 %[[GUARD]], 0 + // CHECK: br i1 %[[NEED_INIT]] + + // CHECK: %[[CALL:.*]] = call i32 @_Z1gv() + // CHECK: store i32 %[[CALL]], i32* @_ZZ1fvE1n, align 4 + // CHECK: store i8 1, i8* @_ZGVZ1fvE1n + // CHECK: br label + static thread_local int n = g(); + + // CHECK: load i32* @_ZZ1fvE1n, align 4 + return n; +} + +// CHECK: define {{.*}} @[[C_INIT:.*]]() +// CHECK: call i32* @_ZTW1b() +// CHECK-NEXT: load i32* %{{.*}}, align 4 +// CHECK-NEXT: store i32 %{{.*}}, i32* @c, align 4 + +// CHECK: define weak_odr hidden i32* @_ZTW1b() +// CHECK: br i1 icmp ne (void ()* @_ZTH1b, void ()* null), +// not null: +// CHECK: call void @_ZTH1b() +// CHECK: br label +// finally: +// CHECK: ret i32* @b + +// CHECK: define {{.*}} @[[D_INIT:.*]]() +// CHECK: call i32 @_Z1gv() +// CHECK-NEXT: store i32 %{{.*}}, i32* @_ZL1d, align 4 + +// CHECK: define {{.*}} @[[U_M_INIT:.*]]() +// CHECK: call i32 @_Z1fv() +// CHECK-NEXT: store i32 %{{.*}}, i32* @_ZN1U1mE, align 4 + +// CHECK: define {{.*}} @[[E_INIT:.*]]() +// CHECK: call i32* @_ZTWN1VIiE1mE() +// CHECK-NEXT: load i32* %{{.*}}, align 4 +// CHECK-NEXT: store i32 %{{.*}}, i32* @e, align 4 + +// CHECK: define weak_odr hidden i32* @_ZTWN1VIiE1mE() +// CHECK: call void @_ZTHN1VIiE1mE() +// CHECK: ret i32* @_ZN1VIiE1mE + + +struct S { S(); ~S(); }; +struct T { ~T(); }; + +// CHECK: define void @_Z8tls_dtorv() +void tls_dtor() { + // CHECK: load i8* @_ZGVZ8tls_dtorvE1s + // CHECK: call void @_ZN1SC1Ev(%struct.S* @_ZZ8tls_dtorvE1s) + // CHECK: call i32 @__cxa_thread_atexit({{.*}}@_ZN1SD1Ev {{.*}} @_ZZ8tls_dtorvE1s{{.*}} @__dso_handle + // CHECK: store i8 1, i8* @_ZGVZ8tls_dtorvE1s + static thread_local S s; + + // CHECK: load i8* @_ZGVZ8tls_dtorvE1t + // CHECK-NOT: _ZN1T + // CHECK: call i32 @__cxa_thread_atexit({{.*}}@_ZN1TD1Ev {{.*}}@_ZZ8tls_dtorvE1t{{.*}} @__dso_handle + // CHECK: store i8 1, i8* @_ZGVZ8tls_dtorvE1t + static thread_local T t; + + // CHECK: load i8* @_ZGVZ8tls_dtorvE1u + // CHECK: call void @_ZN1SC1Ev(%struct.S* @_ZGRZ8tls_dtorvE1u) + // CHECK: call i32 @__cxa_thread_atexit({{.*}}@_ZN1SD1Ev {{.*}} @_ZGRZ8tls_dtorvE1u{{.*}} @__dso_handle + // CHECK: store i8 1, i8* @_ZGVZ8tls_dtorvE1u + static thread_local const S &u = S(); +} + +// CHECK: declare i32 @__cxa_thread_atexit(void (i8*)*, i8*, i8*) + +// CHECK: define {{.*}} @[[V_M_INIT:.*]]() +// CHECK: load i8* bitcast (i64* @_ZGVN1VIiE1mE to i8*) +// CHECK: %[[V_M_INITIALIZED:.*]] = icmp eq i8 %{{.*}}, 0 +// CHECK: br i1 %[[V_M_INITIALIZED]], +// need init: +// CHECK: call i32 @_Z1gv() +// CHECK: store i32 %{{.*}}, i32* @_ZN1VIiE1mE, align 4 +// CHECK: store i64 1, i64* @_ZGVN1VIiE1mE +// CHECK: br label + +// CHECK: define {{.*}}@[[GLOBAL_INIT:.*]]() +// CHECK: call void @[[C_INIT]]() +// CHECK: call void @[[E_INIT]]() + + +// CHECK: define {{.*}}@__tls_init() +// CHECK: load i8* @__tls_guard +// CHECK: %[[NEED_TLS_INIT:.*]] = icmp eq i8 %{{.*}}, 0 +// CHECK: store i8 1, i8* @__tls_guard +// CHECK: br i1 %[[NEED_TLS_INIT]], +// init: +// CHECK: call void @[[A_INIT]]() +// CHECK: call void @[[D_INIT]]() +// CHECK: call void @[[U_M_INIT]]() +// CHECK: call void @[[V_M_INIT]]() + + +// CHECK: define weak_odr hidden i32* @_ZTW1a() { +// CHECK: call void @_ZTH1a() +// CHECK: ret i32* @a +// CHECK: } + + +// CHECK: declare extern_weak void @_ZTH1b() + + +// CHECK: define internal hidden i32* @_ZTWL1d() +// CHECK: call void @_ZTHL1d() +// CHECK: ret i32* @_ZL1d + +// CHECK: define weak_odr hidden i32* @_ZTWN1U1mE() +// CHECK: call void @_ZTHN1U1mE() +// CHECK: ret i32* @_ZN1U1mE diff --git a/test/CodeGenCXX/cxx1y-initializer-aggregate.cpp b/test/CodeGenCXX/cxx1y-initializer-aggregate.cpp new file mode 100644 index 00000000000..ef78c434e35 --- /dev/null +++ b/test/CodeGenCXX/cxx1y-initializer-aggregate.cpp @@ -0,0 +1,74 @@ +// RUN: %clang_cc1 -std=c++1y %s -triple x86_64-linux-gnu -emit-llvm -o - | FileCheck %s + +struct A { + int n = 0; + const char *p; + char k = p[n]; + int f(); + int x = f(); + union { + char c; + double d = 1.0; + }; +}; + +int f(); + +union B { + int a; + int f(); + int b = f(); +}; + +A a { .p = "foobar" }; +A b { 4, "bazquux", .x = 42, .c = 9 }; +A c { 1, 0, 'A', f(), { 3 } }; + +// CHECK: @[[STR_A:.*]] = {{.*}} [7 x i8] c"foobar\00" +// CHECK: @[[STR_B:.*]] = {{.*}} [8 x i8] c"bazquux\00" + +B x; +B y {}; +B z { 1 }; +// CHECK: @z = global {{.*}} { i32 1 } + +// Initialization of 'a': + +// CHECK: store i32 0, i32* getelementptr inbounds ({{.*}} @a, i32 0, i32 0) +// CHECK: store i8* {{.*}} @[[STR_A]]{{.*}}, i8** getelementptr inbounds ({{.*}} @a, i32 0, i32 1) +// CHECK: load i32* getelementptr inbounds ({{.*}} @a, i32 0, i32 0) +// CHECK: load i8** getelementptr inbounds ({{.*}} @a, i32 0, i32 1) +// CHECK: getelementptr inbounds i8* %{{.*}}, {{.*}} %{{.*}} +// CHECK: store i8 %{{.*}}, i8* getelementptr inbounds ({{.*}} @a, i32 0, i32 2) +// CHECK: call i32 @_ZN1A1fEv({{.*}} @a) +// CHECK: store i32 %{{.*}}, i32* getelementptr inbounds ({{.*}}* @a, i32 0, i32 3) +// CHECK: call void @{{.*}}C1Ev({{.*}} getelementptr inbounds (%struct.A* @a, i32 0, i32 4)) + +// Initialization of 'b': + +// CHECK: store i32 4, i32* getelementptr inbounds ({{.*}} @b, i32 0, i32 0) +// CHECK: store i8* {{.*}} @[[STR_B]]{{.*}}, i8** getelementptr inbounds ({{.*}} @b, i32 0, i32 1) +// CHECK: load i32* getelementptr inbounds ({{.*}} @b, i32 0, i32 0) +// CHECK: load i8** getelementptr inbounds ({{.*}} @b, i32 0, i32 1) +// CHECK: getelementptr inbounds i8* %{{.*}}, {{.*}} %{{.*}} +// CHECK: store i8 %{{.*}}, i8* getelementptr inbounds ({{.*}} @b, i32 0, i32 2) +// CHECK-NOT: @_ZN1A1fEv +// CHECK: store i32 42, i32* getelementptr inbounds ({{.*}}* @b, i32 0, i32 3) +// CHECK-NOT: C1Ev +// CHECK: store i8 9, i8* {{.*}} @b, i32 0, i32 4) + +// Initialization of 'c': + +// CHECK: store i32 1, i32* getelementptr inbounds ({{.*}} @c, i32 0, i32 0) +// CHECK: store i8* null, i8** getelementptr inbounds ({{.*}} @c, i32 0, i32 1) +// CHECK-NOT: load +// CHECK: store i8 65, i8* getelementptr inbounds ({{.*}} @c, i32 0, i32 2) +// CHECK: call i32 @_Z1fv() +// CHECK: store i32 %{{.*}}, i32* getelementptr inbounds ({{.*}}* @c, i32 0, i32 3) +// CHECK-NOT: C1Ev +// CHECK: store i8 3, i8* {{.*}} @c, i32 0, i32 4) + +// CHECK: call void @_ZN1BC1Ev({{.*}} @x) + +// CHECK: call i32 @_ZN1B1fEv({{.*}} @y) +// CHECK: store i32 %{{.*}}, i32* getelementptr inbounds ({{.*}} @y, i32 0, i32 0) diff --git a/test/CodeGenCXX/debug-info-namespace.cpp b/test/CodeGenCXX/debug-info-namespace.cpp index 262e996d44d..13a7914b7be 100644 --- a/test/CodeGenCXX/debug-info-namespace.cpp +++ b/test/CodeGenCXX/debug-info-namespace.cpp @@ -5,13 +5,33 @@ namespace A { namespace B { int i; } +using namespace B; } +using namespace A; + +int func(bool b) { + if (b) { + using namespace A::B; + return i; + } + using namespace A; + return B::i; +} + +// CHECK: [[CU:![0-9]*]] = {{.*}}[[MODULES:![0-9]*]], metadata !""} ; [ DW_TAG_compile_unit ] // CHECK: [[FILE:![0-9]*]] {{.*}}debug-info-namespace.cpp" +// CHECK: [[FUNC:![0-9]*]] {{.*}} ; [ DW_TAG_subprogram ] [line 9] [def] [func] +// CHECK: [[FILE2:![0-9]*]]} ; [ DW_TAG_file_type ] [{{.*}}foo.cpp] // CHECK: [[VAR:![0-9]*]] = {{.*}}, metadata [[NS:![0-9]*]], metadata !"i", {{.*}} ; [ DW_TAG_variable ] [i] -// CHECK: [[NS]] = {{.*}}, metadata [[FILE2:![0-9]*]], metadata [[CTXT:![0-9]*]], {{.*}} ; [ DW_TAG_namespace ] [B] [line 1] +// CHECK: [[NS]] = {{.*}}, metadata [[FILE2]], metadata [[CTXT:![0-9]*]], {{.*}} ; [ DW_TAG_namespace ] [B] [line 1] // CHECK: [[CTXT]] = {{.*}}, metadata [[FILE]], null, {{.*}} ; [ DW_TAG_namespace ] [A] [line 3] -// CHECK: [[FILE2]]} ; [ DW_TAG_file_type ] [{{.*}}foo.cpp] +// CHECK: [[MODULES]] = metadata !{metadata [[M1:![0-9]*]], metadata [[M2:![0-9]*]], metadata [[M3:![0-9]*]], metadata [[M4:![0-9]*]]} +// CHECK: [[M1]] = metadata !{i32 {{[0-9]*}}, metadata [[CTXT]], metadata [[NS]], i32 4} ; [ DW_TAG_imported_module ] +// CHECK: [[M2]] = metadata !{i32 {{[0-9]*}}, metadata [[CU]], metadata [[CTXT]], i32 7} ; [ DW_TAG_imported_module ] +// CHECK: [[M3]] = metadata !{i32 {{[0-9]*}}, metadata [[LEX:![0-9]*]], metadata [[NS]], i32 11} ; [ DW_TAG_imported_module ] +// CHECK: [[LEX]] = metadata !{i32 {{[0-9]*}}, metadata [[FILE2]], metadata [[FUNC]], i32 10, i32 0, i32 0} ; [ DW_TAG_lexical_block ] +// CHECK: [[M4]] = metadata !{i32 {{[0-9]*}}, metadata [[FUNC]], metadata [[CTXT]], i32 14} ; [ DW_TAG_imported_module ] // FIXME: It is confused on win32 to generate file entry when dosish filename is given. // REQUIRES: shell diff --git a/test/CodeGenCXX/extern-c.cpp b/test/CodeGenCXX/extern-c.cpp index a8c4f0cdbd3..5899b9348c5 100644 --- a/test/CodeGenCXX/extern-c.cpp +++ b/test/CodeGenCXX/extern-c.cpp @@ -36,3 +36,30 @@ namespace test2 { extern "C" X test2_b; X test2_b; } + +extern "C" { + static int unused_var; + static int unused_fn() { return 0; } + + __attribute__((used)) static int internal_var; + __attribute__((used)) static int internal_fn() { return 0; } + + __attribute__((used)) static int duplicate_internal_var; + __attribute__((used)) static int duplicate_internal_fn() { return 0; } + + namespace N { + __attribute__((used)) static int duplicate_internal_var; + __attribute__((used)) static int duplicate_internal_fn() { return 0; } + } + + // CHECK: @llvm.used = appending global {{.*}} @internal_var {{.*}} @internal_fn + + // CHECK-NOT: @unused + // CHECK-NOT: @duplicate_internal + // CHECK: @internal_var = alias internal i32* @_Z12internal_var + // CHECK-NOT: @unused + // CHECK-NOT: @duplicate_internal + // CHECK: @internal_fn = alias internal i32 ()* @_Z11internal_fnv + // CHECK-NOT: @unused + // CHECK-NOT: @duplicate_internal +} diff --git a/test/CodeGenCXX/inheriting-constructor.cpp b/test/CodeGenCXX/inheriting-constructor.cpp index adb9f6dc1a7..0f397849297 100644 --- a/test/CodeGenCXX/inheriting-constructor.cpp +++ b/test/CodeGenCXX/inheriting-constructor.cpp @@ -7,6 +7,10 @@ B::~B() {} B b(123); +struct C { template C(T); }; +struct D : C { using C::C; }; +D d(123); + // CHECK: define void @_ZN1BD0Ev // CHECK: define void @_ZN1BD1Ev // CHECK: define void @_ZN1BD2Ev @@ -14,5 +18,11 @@ B b(123); // CHECK: define linkonce_odr void @_ZN1BC1Ei( // CHECK: call void @_ZN1BC2Ei( +// CHECK: define linkonce_odr void @_ZN1DC1IiEET_( +// CHECK: call void @_ZN1DC2IiEET_( + +// CHECK: define linkonce_odr void @_ZN1DC2IiEET_( +// CHECK: call void @_ZN1CC2IiEET_( + // CHECK: define linkonce_odr void @_ZN1BC2Ei( // CHECK: call void @_ZN1AC2Ei( diff --git a/test/CodeGenCXX/linetable-cleanup.cpp b/test/CodeGenCXX/linetable-cleanup.cpp new file mode 100644 index 00000000000..4077af6d8e0 --- /dev/null +++ b/test/CodeGenCXX/linetable-cleanup.cpp @@ -0,0 +1,24 @@ +// RUN: %clang_cc1 -emit-llvm -g -triple x86_64-apple-darwin10 %s -o - | FileCheck %s + +// Check the line numbers for cleanup code with EH in combinatin with +// simple return expressions. + +// CHECK: define {{.*}}foo +// CHECK: call void @_ZN1CD1Ev(%class.C* {{.*}}), !dbg ![[CLEANUP:[0-9]+]] +// CHECK: ret i32 0, !dbg ![[RET:[0-9]+]] + +class C { +public: + ~C() {} + int i; +}; + +int foo() +{ + C c; + c.i = 42; + // This breakpoint should be at/before the cleanup code. + // CHECK: ![[CLEANUP]] = metadata !{i32 [[@LINE+1]], i32 0, metadata !{{.*}}, null} + return 0; + // CHECK: ![[RET]] = metadata !{i32 [[@LINE+1]], i32 0, metadata !{{.*}}, null} +} diff --git a/test/CodeGenCXX/mangle-ms-arg-qualifiers.cpp b/test/CodeGenCXX/mangle-ms-arg-qualifiers.cpp index 0ac9b3f121f..d03ba526497 100644 --- a/test/CodeGenCXX/mangle-ms-arg-qualifiers.cpp +++ b/test/CodeGenCXX/mangle-ms-arg-qualifiers.cpp @@ -60,6 +60,51 @@ void foo_pcrbd(const char * volatile* x) {} void foo_pcrcd(volatile char * volatile* x) {} // CHECK: "\01?foo_pcrcd@@YAXPCRCD@Z" +void foo_aad(char &x) {} +// CHECK: "\01?foo_aad@@YAXAAD@Z" + +void foo_abd(const char &x) {} +// CHECK: "\01?foo_abd@@YAXABD@Z" + +void foo_aapad(char *&x) {} +// CHECK: "\01?foo_aapad@@YAXAAPAD@Z" + +void foo_aapbd(const char *&x) {} +// CHECK: "\01?foo_aapbd@@YAXAAPBD@Z" + +void foo_abqad(char * const &x) {} +// CHECK: "\01?foo_abqad@@YAXABQAD@Z" + +void foo_abqbd(const char * const &x) {} +// CHECK: "\01?foo_abqbd@@YAXABQBD@Z" + +void foo_aay144h(int (&x)[5][5]) {} +// CHECK: "\01?foo_aay144h@@YAXAAY144H@Z" + +void foo_aay144cbh(const int (&x)[5][5]) {} +// CHECK: "\01?foo_aay144cbh@@YAXAAY144$$CBH@Z" + +void foo_qay144h(int (&&x)[5][5]) {} +// CHECK: "\01?foo_qay144h@@YAX$$QAY144H@Z" + +void foo_qay144cbh(const int (&&x)[5][5]) {} +// CHECK: "\01?foo_qay144cbh@@YAX$$QAY144$$CBH@Z" + +void foo_p6ahxz(int x()) {} +// CHECK: "\01?foo_p6ahxz@@YAXP6AHXZ@Z" + +void foo_a6ahxz(int (&x)()) {} +// CHECK: "\01?foo_a6ahxz@@YAXA6AHXZ@Z" + +void foo_q6ahxz(int (&&x)()) {} +// CHECK: "\01?foo_q6ahxz@@YAX$$Q6AHXZ@Z" + +void foo_qay04h(int x[5][5]) {} +// CHECK: "\01?foo_qay04h@@YAXQAY04H@Z" + +void foo_qay04cbh(const int x[5][5]) {} +// CHECK: "\01?foo_qay04cbh@@YAXQAY04$$CBH@Z" + typedef double Vector[3]; void foo(Vector*) {} diff --git a/test/CodeGenCXX/mangle-ms-return-qualifiers.cpp b/test/CodeGenCXX/mangle-ms-return-qualifiers.cpp index 63bc4a9eb3c..87e04c645ec 100644 --- a/test/CodeGenCXX/mangle-ms-return-qualifiers.cpp +++ b/test/CodeGenCXX/mangle-ms-return-qualifiers.cpp @@ -155,6 +155,15 @@ const volatile struct S* f5() { return 0; } struct S& f6() { return *(struct S*)0; } // CHECK: "\01?f6@@YAAAUS@@XZ" +struct S* const f7() { return 0; } +// CHECK: "\01?f7@@YAQAUS@@XZ" + +int S::* f8() { return 0; } +// CHECK: "\01?f8@@YAPQS@@HXZ" + +int S::* const f9() { return 0; } +// CHECK: "\01?f9@@YAQQS@@HXZ" + typedef int (*function_pointer)(int); function_pointer g1() { return 0; } diff --git a/test/CodeGenCXX/mangle-ms-templates.cpp b/test/CodeGenCXX/mangle-ms-templates.cpp index d0e8af48884..10e68248dc1 100644 --- a/test/CodeGenCXX/mangle-ms-templates.cpp +++ b/test/CodeGenCXX/mangle-ms-templates.cpp @@ -3,7 +3,7 @@ template class Class { public: - void method() {} + Class() {} }; class Typename { }; @@ -32,12 +32,30 @@ class BoolTemplate { void template_mangling() { Class c1; - c1.method(); -// CHECK: call {{.*}} @"\01?method@?$Class@VTypename@@@@QAEXXZ" +// CHECK: call {{.*}} @"\01??0?$Class@VTypename@@@@QAE@XZ" + + Class c1_const; +// CHECK: call {{.*}} @"\01??0?$Class@$$CBVTypename@@@@QAE@XZ" + Class c1_volatile; +// CHECK: call {{.*}} @"\01??0?$Class@$$CCVTypename@@@@QAE@XZ" + Class c1_cv; +// CHECK: call {{.*}} @"\01??0?$Class@$$CDVTypename@@@@QAE@XZ" Class > c2; - c2.method(); -// CHECK: call {{.*}} @"\01?method@?$Class@V?$Nested@VTypename@@@@@@QAEXXZ" +// CHECK: call {{.*}} @"\01??0?$Class@V?$Nested@VTypename@@@@@@QAE@XZ" + + Class c_intpc; +// CHECK: call {{.*}} @"\01??0?$Class@QAH@@QAE@XZ" + Class c_ft; +// CHECK: call {{.*}} @"\01??0?$Class@$$A6AHXZ@@QAE@XZ" + Class c_inti; +// CHECK: call {{.*}} @"\01??0?$Class@$$BY0A@H@@QAE@XZ" + Class c_int5; +// CHECK: call {{.*}} @"\01??0?$Class@$$BY04H@@QAE@XZ" + Class c_intc5; +// CHECK: call {{.*}} @"\01??0?$Class@$$BY04$$CBH@@QAE@XZ" + Class c_intpc5; +// CHECK: call {{.*}} @"\01??0?$Class@$$BY04QAH@@QAE@XZ" BoolTemplate _false; // CHECK: call {{.*}} @"\01??0?$BoolTemplate@$0A@@@QAE@XZ" diff --git a/test/CodeGenCXX/mangle-ms.cpp b/test/CodeGenCXX/mangle-ms.cpp index 6441d67a758..1b98a84823f 100644 --- a/test/CodeGenCXX/mangle-ms.cpp +++ b/test/CodeGenCXX/mangle-ms.cpp @@ -17,11 +17,8 @@ // CHECK: @"\01?l@@3P8foo@@AEHH@ZA" // CHECK: @"\01?color1@@3PANA" // CHECK: @"\01?color2@@3QBNB" - -// FIXME: The following three tests currently fail, see http://llvm.org/PR13182 -// Replace "CHECK-NOT" with "CHECK" when it is fixed. -// CHECK-NOT: @"\01?color3@@3QAY02$$CBNA" -// CHECK-NOT: @"\01?color4@@3QAY02$$CBNA" +// CHECK: @"\01?color3@@3QAY02$$CBNA" +// CHECK: @"\01?color4@@3QAY02$$CBNA" int a; diff --git a/test/CodeGenCXX/microsoft-abi-member-pointers.cpp b/test/CodeGenCXX/microsoft-abi-member-pointers.cpp index 997e007086c..3fffc9d72cb 100755 --- a/test/CodeGenCXX/microsoft-abi-member-pointers.cpp +++ b/test/CodeGenCXX/microsoft-abi-member-pointers.cpp @@ -1,10 +1,110 @@ -// RUN: %clang_cc1 -emit-llvm %s -o - -cxx-abi microsoft -triple=i386-pc-win32 | FileCheck %s +// RUN: %clang_cc1 -fno-rtti -emit-llvm %s -o - -cxx-abi microsoft -triple=i386-pc-win32 | FileCheck %s + +struct B1 { + void foo(); + int b; +}; +struct B2 { + void foo(); +}; +struct Single : B1 { + void foo(); +}; +struct Multiple : B1, B2 { + void foo(); +}; +struct Virtual : virtual B1 { + int v; + void foo(); +}; struct POD { int a; int b; }; +struct Polymorphic { + virtual void myVirtual(); + int a; + int b; +}; + +// This class uses the virtual inheritance model, yet its vbptr offset is not 0. +// We still use zero for the null field offset, despite it being a valid field +// offset. +struct NonZeroVBPtr : POD, Virtual { + int n; +}; + +struct Unspecified; + +// Check that we can lower the LLVM types and get the null initializers right. +int Single ::*s_d_memptr; +int Polymorphic::*p_d_memptr; +int Multiple ::*m_d_memptr; +int Virtual ::*v_d_memptr; +int NonZeroVBPtr::*n_d_memptr; +int Unspecified::*u_d_memptr; +// CHECK: @"\01?s_d_memptr@@3PQSingle@@HA" = global i32 -1, align 4 +// CHECK: @"\01?p_d_memptr@@3PQPolymorphic@@HA" = global i32 0, align 4 +// CHECK: @"\01?m_d_memptr@@3PQMultiple@@HA" = global i32 -1, align 4 +// CHECK: @"\01?v_d_memptr@@3PQVirtual@@HA" = global { i32, i32 } +// CHECK: { i32 0, i32 -1 }, align 4 +// CHECK: @"\01?n_d_memptr@@3PQNonZeroVBPtr@@HA" = global { i32, i32 } +// CHECK: { i32 0, i32 -1 }, align 4 +// CHECK: @"\01?u_d_memptr@@3PQUnspecified@@HA" = global { i32, i32, i32 } +// CHECK: { i32 0, i32 0, i32 -1 }, align 4 + +void (Single ::*s_f_memptr)(); +void (Multiple::*m_f_memptr)(); +void (Virtual ::*v_f_memptr)(); +// CHECK: @"\01?s_f_memptr@@3P8Single@@AEXXZA" = global i8* null, align 4 +// CHECK: @"\01?m_f_memptr@@3P8Multiple@@AEXXZA" = global { i8*, i32 } zeroinitializer, align 4 +// CHECK: @"\01?v_f_memptr@@3P8Virtual@@AEXXZA" = global { i8*, i32, i32 } zeroinitializer, align 4 + +// We can define Unspecified after locking in the inheritance model. +struct Unspecified : Virtual { + void foo(); + int u; +}; + +struct UnspecWithVBPtr; +int UnspecWithVBPtr::*forceUnspecWithVBPtr; +struct UnspecWithVBPtr : B1, virtual B2 { + int u; + void foo(); +}; + +// Test emitting non-virtual member pointers in a non-constexpr setting. +void EmitNonVirtualMemberPointers() { + void (Single ::*s_f_memptr)() = &Single::foo; + void (Multiple ::*m_f_memptr)() = &Multiple::foo; + void (Virtual ::*v_f_memptr)() = &Virtual::foo; + void (Unspecified::*u_f_memptr)() = &Unspecified::foo; + void (UnspecWithVBPtr::*u2_f_memptr)() = &UnspecWithVBPtr::foo; +// CHECK: define void @"\01?EmitNonVirtualMemberPointers@@YAXXZ"() #0 { +// CHECK: alloca i8*, align 4 +// CHECK: alloca { i8*, i32 }, align 4 +// CHECK: alloca { i8*, i32, i32 }, align 4 +// CHECK: alloca { i8*, i32, i32, i32 }, align 4 +// CHECK: store i8* bitcast (void (%{{.*}}*)* @"\01?foo@Single@@QAEXXZ" to i8*), i8** %{{.*}}, align 4 +// CHECK: store { i8*, i32 } +// CHECK: { i8* bitcast (void (%{{.*}}*)* @"\01?foo@Multiple@@QAEXXZ" to i8*), i32 0 }, +// CHECK: { i8*, i32 }* %{{.*}}, align 4 +// CHECK: store { i8*, i32, i32 } +// CHECK: { i8* bitcast (void (%{{.*}}*)* @"\01?foo@Virtual@@QAEXXZ" to i8*), i32 0, i32 0 }, +// CHECK: { i8*, i32, i32 }* %{{.*}}, align 4 +// CHECK: store { i8*, i32, i32, i32 } +// CHECK: { i8* bitcast (void (%{{.*}}*)* @"\01?foo@Unspecified@@QAEXXZ" to i8*), i32 0, i32 0, i32 0 }, +// CHECK: { i8*, i32, i32, i32 }* %{{.*}}, align 4 +// CHECK: store { i8*, i32, i32, i32 } +// CHECK: { i8* bitcast (void (%{{.*}}*)* @"\01?foo@UnspecWithVBPtr@@QAEXXZ" to i8*), +// CHECK: i32 0, i32 4, i32 0 }, +// CHECK: { i8*, i32, i32, i32 }* %{{.*}}, align 4 +// CHECK: ret void +// CHECK: } +} + void podMemPtrs() { int POD::*memptr; memptr = &POD::a; @@ -24,12 +124,6 @@ void podMemPtrs() { // CHECK: } } -struct Polymorphic { - virtual void myVirtual(); - int a; - int b; -}; - void polymorphicMemPtrs() { int Polymorphic::*memptr; memptr = &Polymorphic::a; @@ -49,3 +143,221 @@ void polymorphicMemPtrs() { // CHECK: ret void // CHECK: } } + +bool nullTestDataUnspecified(int Unspecified::*mp) { + return mp; +// CHECK: define zeroext i1 @"\01?nullTestDataUnspecified@@YA_NPQUnspecified@@H@Z"{{.*}} { +// CHECK: %{{.*}} = load { i32, i32, i32 }* %{{.*}}, align 4 +// CHECK: store { i32, i32, i32 } {{.*}} align 4 +// CHECK: %[[mp:.*]] = load { i32, i32, i32 }* %{{.*}}, align 4 +// CHECK: %[[mp0:.*]] = extractvalue { i32, i32, i32 } %[[mp]], 0 +// CHECK: %[[cmp0:.*]] = icmp ne i32 %[[mp0]], 0 +// CHECK: %[[mp1:.*]] = extractvalue { i32, i32, i32 } %[[mp]], 1 +// CHECK: %[[cmp1:.*]] = icmp ne i32 %[[mp1]], 0 +// CHECK: %[[and0:.*]] = and i1 %[[cmp0]], %[[cmp1]] +// CHECK: %[[mp2:.*]] = extractvalue { i32, i32, i32 } %[[mp]], 2 +// CHECK: %[[cmp2:.*]] = icmp ne i32 %[[mp2]], -1 +// CHECK: %[[and1:.*]] = and i1 %[[and0]], %[[cmp2]] +// CHECK: ret i1 %[[and1]] +// CHECK: } +} + +bool nullTestFunctionUnspecified(void (Unspecified::*mp)()) { + return mp; +// CHECK: define zeroext i1 @"\01?nullTestFunctionUnspecified@@YA_NP8Unspecified@@AEXXZ@Z"{{.*}} { +// CHECK: %{{.*}} = load { i8*, i32, i32, i32 }* %{{.*}}, align 4 +// CHECK: store { i8*, i32, i32, i32 } {{.*}} align 4 +// CHECK: %[[mp:.*]] = load { i8*, i32, i32, i32 }* %{{.*}}, align 4 +// CHECK: %[[mp0:.*]] = extractvalue { i8*, i32, i32, i32 } %[[mp]], 0 +// CHECK: %[[cmp0:.*]] = icmp ne i8* %[[mp0]], null +// CHECK: ret i1 %[[cmp0]] +// CHECK: } +} + +int loadDataMemberPointerVirtual(Virtual *o, int Virtual::*memptr) { + return o->*memptr; +// Test that we can unpack this aggregate member pointer and load the member +// data pointer. +// CHECK: define i32 @"\01?loadDataMemberPointerVirtual@@YAHPAUVirtual@@PQ1@H@Z"{{.*}} { +// CHECK: %[[o:.*]] = load %{{.*}}** %{{.*}}, align 4 +// CHECK: %[[memptr:.*]] = load { i32, i32 }* %{{.*}}, align 4 +// CHECK: %[[memptr0:.*]] = extractvalue { i32, i32 } %[[memptr:.*]], 0 +// CHECK: %[[memptr1:.*]] = extractvalue { i32, i32 } %[[memptr:.*]], 1 +// CHECK: %[[v6:.*]] = bitcast %{{.*}}* %[[o]] to i8* +// CHECK: %[[vbptr:.*]] = getelementptr inbounds i8* %[[v6]], i32 0 +// CHECK: %[[vbptr_a:.*]] = bitcast i8* %[[vbptr]] to i8** +// CHECK: %[[vbtable:.*]] = load i8** %[[vbptr_a:.*]] +// CHECK: %[[v7:.*]] = getelementptr inbounds i8* %[[vbtable]], i32 %[[memptr1]] +// CHECK: %[[v8:.*]] = bitcast i8* %[[v7]] to i32* +// CHECK: %[[vbase_offs:.*]] = load i32* %[[v8]] +// CHECK: %[[v10:.*]] = getelementptr inbounds i8* %[[vbptr]], i32 %[[vbase_offs]] +// CHECK: %[[offset:.*]] = getelementptr inbounds i8* %[[v10]], i32 %[[memptr0]] +// CHECK: %[[v11:.*]] = bitcast i8* %[[offset]] to i32* +// CHECK: %[[v12:.*]] = load i32* %[[v11]] +// CHECK: ret i32 %[[v12]] +// CHECK: } +} + +int loadDataMemberPointerUnspecified(Unspecified *o, int Unspecified::*memptr) { + return o->*memptr; +// Test that we can unpack this aggregate member pointer and load the member +// data pointer. +// CHECK: define i32 @"\01?loadDataMemberPointerUnspecified@@YAHPAUUnspecified@@PQ1@H@Z"{{.*}} { +// CHECK: %[[o:.*]] = load %{{.*}}** %{{.*}}, align 4 +// CHECK: %[[memptr:.*]] = load { i32, i32, i32 }* %{{.*}}, align 4 +// CHECK: %[[memptr0:.*]] = extractvalue { i32, i32, i32 } %[[memptr:.*]], 0 +// CHECK: %[[memptr1:.*]] = extractvalue { i32, i32, i32 } %[[memptr:.*]], 1 +// CHECK: %[[memptr2:.*]] = extractvalue { i32, i32, i32 } %[[memptr:.*]], 2 +// CHECK: %[[base:.*]] = bitcast %{{.*}}* %[[o]] to i8* +// CHECK: %[[is_vbase:.*]] = icmp ne i32 %[[memptr2]], 0 +// CHECK: br i1 %[[is_vbase]], label %[[vadjust:.*]], label %[[skip:.*]] +// +// CHECK: [[vadjust]] +// CHECK: %[[vbptr:.*]] = getelementptr inbounds i8* %[[base]], i32 %[[memptr1]] +// CHECK: %[[vbptr_a:.*]] = bitcast i8* %[[vbptr]] to i8** +// CHECK: %[[vbtable:.*]] = load i8** %[[vbptr_a:.*]] +// CHECK: %[[v7:.*]] = getelementptr inbounds i8* %[[vbtable]], i32 %[[memptr2]] +// CHECK: %[[v8:.*]] = bitcast i8* %[[v7]] to i32* +// CHECK: %[[vbase_offs:.*]] = load i32* %[[v8]] +// CHECK: %[[base_adj:.*]] = getelementptr inbounds i8* %[[vbptr]], i32 %[[vbase_offs]] +// +// CHECK: [[skip]] +// CHECK: %[[new_base:.*]] = phi i8* [ %[[base]], %{{.*}} ], [ %[[base_adj]], %[[vadjust]] ] +// CHECK: %[[offset:.*]] = getelementptr inbounds i8* %[[new_base]], i32 %[[memptr0]] +// CHECK: %[[v11:.*]] = bitcast i8* %[[offset]] to i32* +// CHECK: %[[v12:.*]] = load i32* %[[v11]] +// CHECK: ret i32 %[[v12]] +// CHECK: } +} + +void callMemberPointerSingle(Single *o, void (Single::*memptr)()) { + (o->*memptr)(); +// Just look for an indirect thiscall. +// CHECK: define void @"\01?callMemberPointerSingle@@{{.*}} #0 { +// CHECK: call x86_thiscallcc void %{{.*}}(%{{.*}} %{{.*}}) +// CHECK: ret void +// CHECK: } +} + +void callMemberPointerMultiple(Multiple *o, void (Multiple::*memptr)()) { + (o->*memptr)(); +// CHECK: define void @"\01?callMemberPointerMultiple@@{{.*}} #0 { +// CHECK: %[[memptr0:.*]] = extractvalue { i8*, i32 } %{{.*}}, 0 +// CHECK: %[[memptr1:.*]] = extractvalue { i8*, i32 } %{{.*}}, 1 +// CHECK: %[[this_adjusted:.*]] = getelementptr inbounds i8* %{{.*}}, i32 %[[memptr1]] +// CHECK: %[[this:.*]] = bitcast i8* %[[this_adjusted]] to {{.*}} +// CHECK: %[[fptr:.*]] = bitcast i8* %[[memptr0]] to {{.*}} +// CHECK: call x86_thiscallcc void %[[fptr]](%{{.*}} %[[this]]) +// CHECK: ret void +// CHECK: } +} + +void callMemberPointerVirtualBase(Virtual *o, void (Virtual::*memptr)()) { + (o->*memptr)(); +// This shares a lot with virtual data member pointers. +// CHECK: define void @"\01?callMemberPointerVirtualBase@@{{.*}} #0 { +// CHECK: %[[memptr0:.*]] = extractvalue { i8*, i32, i32 } %{{.*}}, 0 +// CHECK: %[[memptr1:.*]] = extractvalue { i8*, i32, i32 } %{{.*}}, 1 +// CHECK: %[[memptr2:.*]] = extractvalue { i8*, i32, i32 } %{{.*}}, 2 +// CHECK: %[[vbptr:.*]] = getelementptr inbounds i8* %{{.*}}, i32 0 +// CHECK: %[[vbptr_a:.*]] = bitcast i8* %[[vbptr]] to i8** +// CHECK: %[[vbtable:.*]] = load i8** %[[vbptr_a:.*]] +// CHECK: %[[v7:.*]] = getelementptr inbounds i8* %[[vbtable]], i32 %[[memptr2]] +// CHECK: %[[v8:.*]] = bitcast i8* %[[v7]] to i32* +// CHECK: %[[vbase_offs:.*]] = load i32* %[[v8]] +// CHECK: %[[v10:.*]] = getelementptr inbounds i8* %[[vbptr]], i32 %[[vbase_offs]] +// CHECK: %[[this_adjusted:.*]] = getelementptr inbounds i8* %[[v10]], i32 %[[memptr1]] +// CHECK: %[[fptr:.*]] = bitcast i8* %[[memptr0]] to void ({{.*}}) +// CHECK: %[[this:.*]] = bitcast i8* %[[this_adjusted]] to {{.*}} +// CHECK: call x86_thiscallcc void %[[fptr]](%{{.*}} %[[this]]) +// CHECK: ret void +// CHECK: } +} + +bool compareSingleFunctionMemptr(void (Single::*l)(), void (Single::*r)()) { + return l == r; +// Should only be one comparison here. +// CHECK: define zeroext i1 @"\01?compareSingleFunctionMemptr@@YA_NP8Single@@AEXXZ0@Z"{{.*}} { +// CHECK-NOT: icmp +// CHECK: %[[r:.*]] = icmp eq +// CHECK-NOT: icmp +// CHECK: ret i1 %[[r]] +// CHECK: } +} + +bool compareNeqSingleFunctionMemptr(void (Single::*l)(), void (Single::*r)()) { + return l != r; +// Should only be one comparison here. +// CHECK: define zeroext i1 @"\01?compareNeqSingleFunctionMemptr@@YA_NP8Single@@AEXXZ0@Z"{{.*}} { +// CHECK-NOT: icmp +// CHECK: %[[r:.*]] = icmp ne +// CHECK-NOT: icmp +// CHECK: ret i1 %[[r]] +// CHECK: } +} + +bool unspecFuncMemptrEq(void (Unspecified::*l)(), void (Unspecified::*r)()) { + return l == r; +// CHECK: define zeroext i1 @"\01?unspecFuncMemptrEq@@YA_NP8Unspecified@@AEXXZ0@Z"{{.*}} { +// CHECK: %[[lhs0:.*]] = extractvalue { i8*, i32, i32, i32 } %[[l:.*]], 0 +// CHECK: %{{.*}} = extractvalue { i8*, i32, i32, i32 } %[[r:.*]], 0 +// CHECK: %[[cmp0:.*]] = icmp eq i8* %[[lhs0]], %{{.*}} +// CHECK: %{{.*}} = extractvalue { i8*, i32, i32, i32 } %[[l]], 1 +// CHECK: %{{.*}} = extractvalue { i8*, i32, i32, i32 } %[[r]], 1 +// CHECK: %[[cmp1:.*]] = icmp eq i32 +// CHECK: %{{.*}} = extractvalue { i8*, i32, i32, i32 } %[[l]], 2 +// CHECK: %{{.*}} = extractvalue { i8*, i32, i32, i32 } %[[r]], 2 +// CHECK: %[[cmp2:.*]] = icmp eq i32 +// CHECK: %[[res12:.*]] = and i1 %[[cmp1]], %[[cmp2]] +// CHECK: %{{.*}} = extractvalue { i8*, i32, i32, i32 } %[[l]], 3 +// CHECK: %{{.*}} = extractvalue { i8*, i32, i32, i32 } %[[r]], 3 +// CHECK: %[[cmp3:.*]] = icmp eq i32 +// CHECK: %[[res123:.*]] = and i1 %[[res12]], %[[cmp3]] +// CHECK: %[[iszero:.*]] = icmp eq i8* %[[lhs0]], null +// CHECK: %[[bits_or_null:.*]] = or i1 %[[res123]], %[[iszero]] +// CHECK: %{{.*}} = and i1 %[[bits_or_null]], %[[cmp0]] +// CHECK: ret i1 %{{.*}} +// CHECK: } +} + +bool unspecFuncMemptrNeq(void (Unspecified::*l)(), void (Unspecified::*r)()) { + return l != r; +// CHECK: define zeroext i1 @"\01?unspecFuncMemptrNeq@@YA_NP8Unspecified@@AEXXZ0@Z"{{.*}} { +// CHECK: %[[lhs0:.*]] = extractvalue { i8*, i32, i32, i32 } %[[l:.*]], 0 +// CHECK: %{{.*}} = extractvalue { i8*, i32, i32, i32 } %[[r:.*]], 0 +// CHECK: %[[cmp0:.*]] = icmp ne i8* %[[lhs0]], %{{.*}} +// CHECK: %{{.*}} = extractvalue { i8*, i32, i32, i32 } %[[l]], 1 +// CHECK: %{{.*}} = extractvalue { i8*, i32, i32, i32 } %[[r]], 1 +// CHECK: %[[cmp1:.*]] = icmp ne i32 +// CHECK: %{{.*}} = extractvalue { i8*, i32, i32, i32 } %[[l]], 2 +// CHECK: %{{.*}} = extractvalue { i8*, i32, i32, i32 } %[[r]], 2 +// CHECK: %[[cmp2:.*]] = icmp ne i32 +// CHECK: %[[res12:.*]] = or i1 %[[cmp1]], %[[cmp2]] +// CHECK: %{{.*}} = extractvalue { i8*, i32, i32, i32 } %[[l]], 3 +// CHECK: %{{.*}} = extractvalue { i8*, i32, i32, i32 } %[[r]], 3 +// CHECK: %[[cmp3:.*]] = icmp ne i32 +// CHECK: %[[res123:.*]] = or i1 %[[res12]], %[[cmp3]] +// CHECK: %[[iszero:.*]] = icmp ne i8* %[[lhs0]], null +// CHECK: %[[bits_or_null:.*]] = and i1 %[[res123]], %[[iszero]] +// CHECK: %{{.*}} = or i1 %[[bits_or_null]], %[[cmp0]] +// CHECK: ret i1 %{{.*}} +// CHECK: } +} + +bool unspecDataMemptrEq(int Unspecified::*l, int Unspecified::*r) { + return l == r; +// CHECK: define zeroext i1 @"\01?unspecDataMemptrEq@@YA_NPQUnspecified@@H0@Z"{{.*}} { +// CHECK: extractvalue { i32, i32, i32 } %{{.*}}, 0 +// CHECK: extractvalue { i32, i32, i32 } %{{.*}}, 0 +// CHECK: icmp eq i32 +// CHECK: extractvalue { i32, i32, i32 } %{{.*}}, 1 +// CHECK: extractvalue { i32, i32, i32 } %{{.*}}, 1 +// CHECK: icmp eq i32 +// CHECK: extractvalue { i32, i32, i32 } %{{.*}}, 2 +// CHECK: extractvalue { i32, i32, i32 } %{{.*}}, 2 +// CHECK: icmp eq i32 +// CHECK: and i1 +// CHECK: and i1 +// CHECK: ret i1 +// CHECK: } +} diff --git a/test/CodeGenCXX/microsoft-abi-sret-and-byval.cpp b/test/CodeGenCXX/microsoft-abi-sret-and-byval.cpp new file mode 100644 index 00000000000..060c1728586 --- /dev/null +++ b/test/CodeGenCXX/microsoft-abi-sret-and-byval.cpp @@ -0,0 +1,169 @@ +// RUN: %clang_cc1 -emit-llvm %s -o - -triple=i386-pc-linux | FileCheck -check-prefix LINUX %s +// RUN: %clang_cc1 -emit-llvm %s -o - -triple=i386-pc-win32 -cxx-abi microsoft | FileCheck -check-prefix WIN32 %s +// RUN: %clang_cc1 -emit-llvm %s -o - -triple=x86_64-pc-win32 -cxx-abi microsoft | FileCheck -check-prefix WIN64 %s + +struct Empty {}; + +struct EmptyWithCtor { + EmptyWithCtor() {} +}; + +struct Small { + int x; +}; + +// This is a C++11 trivial and standard-layout struct but not a C++03 POD. +struct SmallCpp11NotCpp03Pod : Empty { + int x; +}; + +struct SmallWithCtor { + SmallWithCtor() {} + int x; +}; + +struct SmallWithVftable { + int x; + virtual void foo(); +}; + +struct Medium { + int x, y; +}; + +struct MediumWithCopyCtor { + MediumWithCopyCtor(); + MediumWithCopyCtor(const struct MediumWithCopyCtor &); + int x, y; +}; + +struct Big { + int a, b, c, d, e, f; +}; + +// Returning structs that fit into a register. +Small small_return() { return Small(); } +// LINUX: define void @_Z12small_returnv(%struct.Small* noalias sret %agg.result) +// WIN32: define i32 @"\01?small_return@@YA?AUSmall@@XZ"() +// WIN64: define i32 @"\01?small_return@@YA?AUSmall@@XZ"() + +Medium medium_return() { return Medium(); } +// LINUX: define void @_Z13medium_returnv(%struct.Medium* noalias sret %agg.result) +// WIN32: define i64 @"\01?medium_return@@YA?AUMedium@@XZ"() +// WIN64: define i64 @"\01?medium_return@@YA?AUMedium@@XZ"() + +// Returning structs that fit into a register but are not POD. +SmallCpp11NotCpp03Pod small_non_pod_return() { return SmallCpp11NotCpp03Pod(); } +// LINUX: define void @_Z20small_non_pod_returnv(%struct.SmallCpp11NotCpp03Pod* noalias sret %agg.result) +// WIN32: define void @"\01?small_non_pod_return@@YA?AUSmallCpp11NotCpp03Pod@@XZ"(%struct.SmallCpp11NotCpp03Pod* noalias sret %agg.result) +// WIN64: define void @"\01?small_non_pod_return@@YA?AUSmallCpp11NotCpp03Pod@@XZ"(%struct.SmallCpp11NotCpp03Pod* noalias sret %agg.result) + +SmallWithCtor small_with_ctor_return() { return SmallWithCtor(); } +// LINUX: define void @_Z22small_with_ctor_returnv(%struct.SmallWithCtor* noalias sret %agg.result) +// WIN32: define void @"\01?small_with_ctor_return@@YA?AUSmallWithCtor@@XZ"(%struct.SmallWithCtor* noalias sret %agg.result) +// WIN64: define void @"\01?small_with_ctor_return@@YA?AUSmallWithCtor@@XZ"(%struct.SmallWithCtor* noalias sret %agg.result) + +SmallWithVftable small_with_vftable_return() { return SmallWithVftable(); } +// LINUX: define void @_Z25small_with_vftable_returnv(%struct.SmallWithVftable* noalias sret %agg.result) +// WIN32: define void @"\01?small_with_vftable_return@@YA?AUSmallWithVftable@@XZ"(%struct.SmallWithVftable* noalias sret %agg.result) +// WIN64: define void @"\01?small_with_vftable_return@@YA?AUSmallWithVftable@@XZ"(%struct.SmallWithVftable* noalias sret %agg.result) + +MediumWithCopyCtor medium_with_copy_ctor_return() { return MediumWithCopyCtor(); } +// LINUX: define void @_Z28medium_with_copy_ctor_returnv(%struct.MediumWithCopyCtor* noalias sret %agg.result) +// WIN32: define void @"\01?medium_with_copy_ctor_return@@YA?AUMediumWithCopyCtor@@XZ"(%struct.MediumWithCopyCtor* noalias sret %agg.result) +// WIN64: define void @"\01?medium_with_copy_ctor_return@@YA?AUMediumWithCopyCtor@@XZ"(%struct.MediumWithCopyCtor* noalias sret %agg.result) + +// Returning a large struct that doesn't fit into a register. +Big big_return() { return Big(); } +// LINUX: define void @_Z10big_returnv(%struct.Big* noalias sret %agg.result) +// WIN32: define void @"\01?big_return@@YA?AUBig@@XZ"(%struct.Big* noalias sret %agg.result) +// WIN64: define void @"\01?big_return@@YA?AUBig@@XZ"(%struct.Big* noalias sret %agg.result) + + +void small_arg(Small s) {} +// LINUX: define void @_Z9small_arg5Small(%struct.Small* byval align 4 %s) +// WIN32: define void @"\01?small_arg@@YAXUSmall@@@Z"(%struct.Small* byval align 4 %s) +// WIN64: define void @"\01?small_arg@@YAXUSmall@@@Z"(i32 %s.coerce) + +void medium_arg(Medium s) {} +// LINUX: define void @_Z10medium_arg6Medium(%struct.Medium* byval align 4 %s) +// WIN32: define void @"\01?medium_arg@@YAXUMedium@@@Z"(%struct.Medium* byval align 4 %s) +// WIN64: define void @"\01?medium_arg@@YAXUMedium@@@Z"(i64 %s.coerce) + +void small_arg_with_ctor(SmallWithCtor s) {} +// LINUX: define void @_Z19small_arg_with_ctor13SmallWithCtor(%struct.SmallWithCtor* byval align 4 %s) +// WIN32: define void @"\01?small_arg_with_ctor@@YAXUSmallWithCtor@@@Z"(%struct.SmallWithCtor* byval align 4 %s) +// WIN64: define void @"\01?small_arg_with_ctor@@YAXUSmallWithCtor@@@Z"(i32 %s.coerce) + +void small_arg_with_vftable(SmallWithVftable s) {} +// LINUX: define void @_Z22small_arg_with_vftable16SmallWithVftable(%struct.SmallWithVftable* %s) +// WIN32: define void @"\01?small_arg_with_vftable@@YAXUSmallWithVftable@@@Z"(%struct.SmallWithVftable* byval align 4 %s) +// WIN64: define void @"\01?small_arg_with_vftable@@YAXUSmallWithVftable@@@Z"(%struct.SmallWithVftable* byval %s) + +void medium_arg_with_copy_ctor(MediumWithCopyCtor s) {} +// LINUX: define void @_Z25medium_arg_with_copy_ctor18MediumWithCopyCtor(%struct.MediumWithCopyCtor* %s) +// WIN32: define void @"\01?medium_arg_with_copy_ctor@@YAXUMediumWithCopyCtor@@@Z"(%struct.MediumWithCopyCtor* byval align 4 %s) +// WIN64: define void @"\01?medium_arg_with_copy_ctor@@YAXUMediumWithCopyCtor@@@Z"(%struct.MediumWithCopyCtor* byval %s) + +void big_arg(Big s) {} +// LINUX: define void @_Z7big_arg3Big(%struct.Big* byval align 4 %s) +// WIN32: define void @"\01?big_arg@@YAXUBig@@@Z"(%struct.Big* byval align 4 %s) +// WIN64: define void @"\01?big_arg@@YAXUBig@@@Z"(%struct.Big* %s) + +// FIXME: Add WIN64 tests. Currently, even the method manglings are wrong (sic!). +class Class { + public: + Small thiscall_method_small() { return Small(); } + // LINUX: define {{.*}} void @_ZN5Class21thiscall_method_smallEv(%struct.Small* noalias sret %agg.result, %class.Class* %this) + // WIN32: define {{.*}} x86_thiscallcc void @"\01?thiscall_method_small@Class@@QAE?AUSmall@@XZ"(%struct.Small* noalias sret %agg.result, %class.Class* %this) + + SmallWithCtor thiscall_method_small_with_ctor() { return SmallWithCtor(); } + // LINUX: define {{.*}} void @_ZN5Class31thiscall_method_small_with_ctorEv(%struct.SmallWithCtor* noalias sret %agg.result, %class.Class* %this) + // WIN32: define {{.*}} x86_thiscallcc void @"\01?thiscall_method_small_with_ctor@Class@@QAE?AUSmallWithCtor@@XZ"(%struct.SmallWithCtor* noalias sret %agg.result, %class.Class* %this) + + Small __cdecl cdecl_method_small() { return Small(); } + // LINUX: define {{.*}} void @_ZN5Class18cdecl_method_smallEv(%struct.Small* noalias sret %agg.result, %class.Class* %this) + // FIXME: Interesting, cdecl returns structures differently for instance + // methods and global functions. This is not supported by Clang yet... + // FIXME: Replace WIN32-NOT with WIN32 when this is fixed. + // WIN32-NOT: define {{.*}} void @"\01?cdecl_method_small@Class@@QAA?AUSmall@@XZ"(%struct.Small* noalias sret %agg.result, %class.Class* %this) + + Big __cdecl cdecl_method_big() { return Big(); } + // LINUX: define {{.*}} void @_ZN5Class16cdecl_method_bigEv(%struct.Big* noalias sret %agg.result, %class.Class* %this) + // WIN32: define {{.*}} void @"\01?cdecl_method_big@Class@@QAA?AUBig@@XZ"(%struct.Big* noalias sret %agg.result, %class.Class* %this) + + void thiscall_method_arg(Empty s) {} + // LINUX: define {{.*}} void @_ZN5Class19thiscall_method_argE5Empty(%class.Class* %this) + // WIN32: define {{.*}} void @"\01?thiscall_method_arg@Class@@QAEXUEmpty@@@Z"(%class.Class* %this, %struct.Empty* byval align 4 %s) + + void thiscall_method_arg(EmptyWithCtor s) {} + // LINUX: define {{.*}} void @_ZN5Class19thiscall_method_argE13EmptyWithCtor(%class.Class* %this) + // WIN32: define {{.*}} void @"\01?thiscall_method_arg@Class@@QAEXUEmptyWithCtor@@@Z"(%class.Class* %this, %struct.EmptyWithCtor* byval align 4 %s) + + void thiscall_method_arg(Small s) {} + // LINUX: define {{.*}} void @_ZN5Class19thiscall_method_argE5Small(%class.Class* %this, %struct.Small* byval align 4 %s) + // WIN32: define {{.*}} void @"\01?thiscall_method_arg@Class@@QAEXUSmall@@@Z"(%class.Class* %this, %struct.Small* byval align 4 %s) + + void thiscall_method_arg(SmallWithCtor s) {} + // LINUX: define {{.*}} void @_ZN5Class19thiscall_method_argE13SmallWithCtor(%class.Class* %this, %struct.SmallWithCtor* byval align 4 %s) + // WIN32: define {{.*}} void @"\01?thiscall_method_arg@Class@@QAEXUSmallWithCtor@@@Z"(%class.Class* %this, %struct.SmallWithCtor* byval align 4 %s) + + void thiscall_method_arg(Big s) {} + // LINUX: define {{.*}} void @_ZN5Class19thiscall_method_argE3Big(%class.Class* %this, %struct.Big* byval align 4 %s) + // WIN32: define {{.*}} void @"\01?thiscall_method_arg@Class@@QAEXUBig@@@Z"(%class.Class* %this, %struct.Big* byval align 4 %s) +}; + +void use_class() { + Class c; + c.thiscall_method_small(); + c.thiscall_method_small_with_ctor(); + + c.cdecl_method_small(); + c.cdecl_method_big(); + + c.thiscall_method_arg(Empty()); + c.thiscall_method_arg(EmptyWithCtor()); + c.thiscall_method_arg(Small()); + c.thiscall_method_arg(SmallWithCtor()); + c.thiscall_method_arg(Big()); +} diff --git a/test/CodeGenCXX/pr15753.cpp b/test/CodeGenCXX/pr15753.cpp new file mode 100644 index 00000000000..fd2000be6e3 --- /dev/null +++ b/test/CodeGenCXX/pr15753.cpp @@ -0,0 +1,12 @@ +// RUN: %clang_cc1 -emit-llvm %s -o - | FileCheck %s + +template static int Foo(T t); +template +int Foo(T t) { + return t; +} +template<> int Foo(int i) { + return i; +} + +// CHECK-NOT: define diff --git a/test/CodeGenCXX/scoped-enums-debug-info.cpp b/test/CodeGenCXX/scoped-enums-debug-info.cpp new file mode 100644 index 00000000000..d3ef9f70684 --- /dev/null +++ b/test/CodeGenCXX/scoped-enums-debug-info.cpp @@ -0,0 +1,26 @@ +// RUN: %clang_cc1 -std=c++11 -emit-llvm -g -o - %s | FileCheck %s +// Test that we are emitting debug info and base types for scoped enums. + +// CHECK: [ DW_TAG_enumeration_type ] [Color] {{.*}} [from int] +enum class Color { gray }; + +void f(Color); +void g() { + f(Color::gray); +} + +// CHECK: [ DW_TAG_enumeration_type ] [Colour] {{.*}} [from int] +enum struct Colour { grey }; + +void h(Colour); +void i() { + h(Colour::grey); +} + +// CHECK: [ DW_TAG_enumeration_type ] [Couleur] {{.*}} [from unsigned char] +enum class Couleur : unsigned char { gris }; + +void j(Couleur); +void k() { + j(Couleur::gris); +} diff --git a/test/CodeGenCXX/scoped-enums.cpp b/test/CodeGenCXX/scoped-enums.cpp index fca05098923..c20faaaf2e9 100644 --- a/test/CodeGenCXX/scoped-enums.cpp +++ b/test/CodeGenCXX/scoped-enums.cpp @@ -7,3 +7,11 @@ void f(Color); void g() { f(Color::red); } + +// See that struct is handled equally. +enum struct Colour { grey }; + +void h(Colour); +void i() { + h(Colour::grey); +} diff --git a/test/CodeGenCXX/throw-expressions.cpp b/test/CodeGenCXX/throw-expressions.cpp index f04185b23f1..ba8a86881a6 100644 --- a/test/CodeGenCXX/throw-expressions.cpp +++ b/test/CodeGenCXX/throw-expressions.cpp @@ -1,5 +1,4 @@ -// RUN: %clang_cc1 -fcxx-exceptions -fexceptions -emit-llvm-only -verify %s -Wno-unreachable-code -// expected-no-diagnostics +// RUN: %clang_cc1 -fcxx-exceptions -fexceptions -Wno-unreachable-code -Werror -triple x86_64-linux-gnu -emit-llvm -o - %s | FileCheck %s int val = 42; int& test1() { @@ -19,3 +18,28 @@ void test3() { int test4() { return 1 ? throw val : val; } + +// PR15923 +int test5(bool x, bool y, int z) { + return (x ? throw 1 : y) ? z : throw 2; +} +// CHECK: define i32 @_Z5test5bbi( +// CHECK: br i1 +// +// x.true: +// CHECK: call void @__cxa_throw( +// CHECK-NEXT: unreachable +// +// x.false: +// CHECK: br i1 +// +// y.true: +// CHECK: load i32* +// CHECK: br label +// +// y.false: +// CHECK: call void @__cxa_throw( +// CHECK-NEXT: unreachable +// +// end: +// CHECK: ret i32 diff --git a/test/CodeGenCXX/tls-init-funcs.cpp b/test/CodeGenCXX/tls-init-funcs.cpp new file mode 100644 index 00000000000..17299dcb7b6 --- /dev/null +++ b/test/CodeGenCXX/tls-init-funcs.cpp @@ -0,0 +1,10 @@ +// RUN: %clang_cc1 -triple x86_64-apple-macosx10.8 -std=c++11 -S -emit-llvm %s -o - | FileCheck %s + +// CHECK: @a = internal thread_local global +// CHECK: @_tlv_atexit({{.*}}@_ZN1AD1Ev + +struct A { + ~A(); +}; + +thread_local A a; diff --git a/test/CodeGenCXX/vtable-debug-info.cpp b/test/CodeGenCXX/vtable-debug-info.cpp index 9294d20e729..8710c76e0bf 100644 --- a/test/CodeGenCXX/vtable-debug-info.cpp +++ b/test/CodeGenCXX/vtable-debug-info.cpp @@ -1,4 +1,4 @@ -// RUN: %clang -c -g %s -o /dev/null +// RUN: %clang -emit-llvm -S -g %s -o /dev/null // Radar 8730409 // XFAIL: win32 diff --git a/test/CodeGenObjC/arc-blocks.m b/test/CodeGenObjC/arc-blocks.m index 3281b2aab86..c9ba2f61534 100644 --- a/test/CodeGenObjC/arc-blocks.m +++ b/test/CodeGenObjC/arc-blocks.m @@ -650,5 +650,44 @@ void test18(id x) { // CHECK-UNOPT-NEXT: ret void } +// rdar://13588325 +void test19_sink(void (^)(int)); +void test19(void (^b)(void)) { +// CHECK: define void @test19( +// Prologue. +// CHECK: [[B:%.*]] = alloca void ()*, +// CHECK-NEXT: [[BLOCK:%.*]] = alloca [[BLOCK_T:<{.*}>]], +// CHECK-NEXT: [[T0:%.*]] = bitcast void ()* {{%.*}} to i8* +// CHECK-NEXT: [[T1:%.*]] = call i8* @objc_retain(i8* [[T0]]) +// CHECK-NEXT: [[T2:%.*]] = bitcast i8* [[T1]] to void ()* +// CHECK-NEXT: store void ()* [[T2]], void ()** [[B]] + +// Block setup. We skip most of this. Note the bare retain. +// CHECK-NEXT: [[SLOTREL:%.*]] = getelementptr inbounds [[BLOCK_T]]* [[BLOCK]], i32 0, i32 5 +// CHECK: [[SLOT:%.*]] = getelementptr inbounds [[BLOCK_T]]* [[BLOCK]], i32 0, i32 5 +// CHECK-NEXT: [[T0:%.*]] = load void ()** [[B]], +// CHECK-NEXT: [[T1:%.*]] = bitcast void ()* [[T0]] to i8* +// CHECK-NEXT: [[T2:%.*]] = call i8* @objc_retain(i8* [[T1]]) +// CHECK-NEXT: [[T3:%.*]] = bitcast i8* [[T2]] to void ()* +// CHECK-NEXT: store void ()* [[T3]], void ()** [[SLOT]], +// Call. +// CHECK-NEXT: [[T0:%.*]] = bitcast [[BLOCK_T]]* [[BLOCK]] to void (i32)* +// CHECK-NEXT: call void @test19_sink(void (i32)* [[T0]]) + + test19_sink(^(int x) { b(); }); + +// Block teardown. +// CHECK-NEXT: [[T0:%.*]] = load void ()** [[SLOTREL]] +// CHECK-NEXT: [[T1:%.*]] = bitcast void ()* [[T0]] to i8* +// CHECK-NEXT: call void @objc_release(i8* [[T1]]) + +// Local cleanup. +// CHECK-NEXT: [[T0:%.*]] = load void ()** [[B]] +// CHECK-NEXT: [[T1:%.*]] = bitcast void ()* [[T0]] to i8* +// CHECK-NEXT: call void @objc_release(i8* [[T1]]) + +// CHECK-NEXT: ret void +} + // CHECK: attributes [[NUW]] = { nounwind } // CHECK-UNOPT: attributes [[NUW]] = { nounwind } diff --git a/test/CodeGenObjC/arc-linetable.m b/test/CodeGenObjC/arc-linetable.m new file mode 100644 index 00000000000..eac91f18890 --- /dev/null +++ b/test/CodeGenObjC/arc-linetable.m @@ -0,0 +1,101 @@ +// RUN: %clang_cc1 -emit-llvm -fblocks -fobjc-arc -g -triple x86_64-apple-darwin10 %s -o - | FileCheck %s + +// Legend: EXP = Return expression, RET = ret instruction + +// CHECK: define {{.*}}testNoSideEffect +// CHECK: call void @objc_storeStrong{{.*}} +// CHECK: call void @objc_storeStrong{{.*}} !dbg ![[ARC1:[0-9]+]] +// CHECK: ret {{.*}} !dbg ![[RET1:[0-9]+]] + +// CHECK: define {{.*}}testNoCleanup +// CHECK: ret {{.*}} !dbg ![[RET2:[0-9]+]] + +// CHECK: define {{.*}}testSideEffect +// CHECK: @objc_msgSend{{.*}} !dbg ![[MSG3:[0-9]+]] +// CHECK: ret {{.*}} !dbg ![[RET3:[0-9]+]] + +// CHECK: define {{.*}}testMultiline +// CHECK: @objc_msgSend{{.*}} !dbg ![[MSG4:[0-9]+]] +// CHECK: load{{.*}} !dbg ![[EXP4:[0-9]+]] +// CHECK: ret {{.*}} !dbg ![[RET4:[0-9]+]] + +// CHECK: define {{.*}}testVoid +// CHECK: call void @objc_storeStrong{{.*}} +// CHECK: call void @objc_storeStrong{{.*}} !dbg ![[ARC5:[0-9]+]] +// CHECK: ret {{.*}} !dbg ![[RET5:[0-9]+]] + +// CHECK: define {{.*}}testVoidNoReturn +// CHECK: @objc_msgSend{{.*}} !dbg ![[MSG6:[0-9]+]] +// CHECK: ret {{.*}} !dbg ![[RET6:[0-9]+]] + +// CHECK: define {{.*}}testNoCleanupSideEffect +// CHECK: @objc_msgSend{{.*}} !dbg ![[MSG7:[0-9]+]] +// CHECK: ret {{.*}} !dbg ![[RET7:[0-9]+]] + + +@interface NSObject ++ (id)alloc; +- (id)init; +- (id)retain; +@end + +@class NSString; + +@interface AppDelegate : NSObject + +@end + +@implementation AppDelegate : NSObject + +- (int)testNoSideEffect:(NSString *)foo { + // CHECK: ![[ARC1]] = metadata !{i32 [[@LINE+1]], i32 0, metadata !{{.*}}, null} + return 1; // Return expression + // CHECK: ![[RET1]] = metadata !{i32 [[@LINE+1]], i32 0, metadata !{{.*}}, null} +} // Cleanup + Ret + +- (int)testNoCleanup { + // CHECK: ![[RET2]] = metadata !{i32 [[@LINE+1]], i32 0, metadata !{{.*}}, null} + return 1; +} + +- (int)testSideEffect:(NSString *)foo { + // CHECK: ![[MSG3]] = metadata !{i32 [[@LINE+1]], i32 0, metadata !{{.*}}, null} + return [self testNoSideEffect :foo]; + // CHECK: ![[RET3]] = metadata !{i32 [[@LINE+1]], i32 0, metadata !{{.*}}, null} +} + +- (int)testMultiline:(NSString *)foo { + // CHECK: ![[MSG4]] = metadata !{i32 [[@LINE+1]], i32 0, metadata !{{.*}}, null} + int r = [self testSideEffect :foo]; + // CHECK: ![[EXP4]] = metadata !{i32 [[@LINE+1]], i32 0, metadata !{{.*}}, null} + return r; + // CHECK: ![[RET4]] = metadata !{i32 [[@LINE+1]], i32 0, metadata !{{.*}}, null} +} + +- (void)testVoid:(NSString *)foo { + // CHECK: ![[ARC5]] = metadata !{i32 [[@LINE+1]], i32 0, metadata !{{.*}}, null} + return; + // CHECK: ![[RET5]] = metadata !{i32 [[@LINE+1]], i32 0, metadata !{{.*}}, null} +} + +- (void)testVoidNoReturn:(NSString *)foo { + // CHECK: ![[MSG6]] = metadata !{i32 [[@LINE+1]], i32 0, metadata !{{.*}}, null} + [self testVoid :foo]; + // CHECK: ![[RET6]] = metadata !{i32 [[@LINE+1]], i32 0, metadata !{{.*}}, null} +} + +- (int)testNoCleanupSideEffect { + // CHECK: ![[MSG7]] = metadata !{i32 [[@LINE+1]], i32 0, metadata !{{.*}}, null} + [self testVoid :@"foo"]; + // CHECK: ![[RET7]] = metadata !{i32 [[@LINE+1]], i32 0, metadata !{{.*}}, null} + return 1; +} + + +@end + + +int main(int argc, const char** argv) { + AppDelegate *o = [[AppDelegate alloc] init]; + return [o testMultiline :@"foo"]; +} diff --git a/test/CodeGenObjC/autorelease.m b/test/CodeGenObjC/autorelease.m index 830929afb2e..f89b81a8acb 100644 --- a/test/CodeGenObjC/autorelease.m +++ b/test/CodeGenObjC/autorelease.m @@ -1,5 +1,5 @@ -// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -fobjc-arc -fobjc-runtime=macosx-10.7 -o - %s | FileCheck %s -// RUN: %clang_cc1 -triple x86_64-apple-darwin11 -emit-llvm -fobjc-runtime=macosx-10.7 -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -fobjc-arc -fobjc-runtime=macosx-10.7 -fexceptions -fobjc-exceptions -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-apple-darwin11 -emit-llvm -fobjc-runtime=macosx-10.7 -fexceptions -fobjc-exceptions -o - %s | FileCheck %s // rdar://8881826 // rdar://9412038 @@ -28,3 +28,26 @@ // CHECK: call i8* @objc_autoreleasePoolPush // CHECK: [[T:%.*]] = load i8** [[A:%.*]] // CHECK: call void @objc_autoreleasePoolPop + +// rdar://13660038 +int tryTo(int (*f)(void)) { + @try { + @autoreleasepool { + return f(); + } + } @catch (...) { + return 0; + } +} +// CHECK: define i32 @tryTo(i32 ()* +// CHECK: [[RET:%.*]] = alloca i32, +// CHECK: [[T0:%.*]] = call i8* @objc_autoreleasePoolPush() +// CHECK-NEXT: [[T1:%.*]] = load i32 ()** {{%.*}}, +// CHECK-NEXT: [[T2:%.*]] = invoke i32 [[T1]]() +// CHECK: store i32 [[T2]], i32* [[RET]] +// CHECK: invoke void @objc_autoreleasePoolPop(i8* [[T0]]) +// CHECK: landingpad { i8*, i32 } personality +// CHECK-NEXT: catch i8* null +// CHECK: call i8* @objc_begin_catch +// CHECK-NEXT: store i32 0, i32* [[RET]] +// CHECK: call void @objc_end_catch() diff --git a/test/CodeGenObjC/debug-info-block-captured-self.m b/test/CodeGenObjC/debug-info-block-captured-self.m index 0316013b310..183e91b6ec5 100644 --- a/test/CodeGenObjC/debug-info-block-captured-self.m +++ b/test/CodeGenObjC/debug-info-block-captured-self.m @@ -59,7 +59,6 @@ typedef enum { // CHECK: call void @llvm.dbg.declare(metadata !{i8* [[BLOCK_DESC]]}, metadata ![[BDMD:[0-9]+]]) // CHECK: %[[TMP1:.*]] = bitcast // CHECK-NEXT: store -// CHECK-NEXT: %[[TMP2:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, %0* }>* %[[TMP1]] // CHECK: call void @llvm.dbg.declare(metadata !{<{ i8*, i32, i32, i8*, %struct.__block_descriptor*, %0* }>** {{.*}}}, metadata ![[SELF:.*]]) // make sure we are still in the same function // CHECK: define {{.*}}__copy_helper_block_ diff --git a/test/CodeGenObjC/debug-info-block-line.m b/test/CodeGenObjC/debug-info-block-line.m index c913a972e14..2192575bb78 100644 --- a/test/CodeGenObjC/debug-info-block-line.m +++ b/test/CodeGenObjC/debug-info-block-line.m @@ -64,13 +64,16 @@ typedef enum : NSUInteger { // CHECK: define internal void @"__39-[TServer serverConnection:getCommand:]_block_invoke" // CHECK: call void @objc_storeStrong(i8** [[ZERO:%.*]], i8* [[ONE:%.*]]) [[NUW:#[0-9]+]] // CHECK: call void @objc_storeStrong(i8** [[TWO:%.*]], i8* [[THREE:%.*]]) [[NUW]] +// CHECK: call {{.*}}@objc_msgSend{{.*}}, !dbg ![[LINE_ABOVE:[0-9]+]] +// CHECK: getelementptr +// CHECK-NOT: !dbg, ![[LINE_ABOVE]] // CHECK: bitcast %5** [[TMP:%.*]] to i8** -// CHECK: call void @objc_storeStrong(i8** [[VAL1:%.*]], i8* null) [[NUW]], !dbg ![[MD1:.*]] -// CHECK: bitcast %4** [[TMP:%.*]] to i8** -// CHECK: call void @objc_storeStrong(i8** [[VAL2:%.*]], i8* null) [[NUW]], !dbg ![[MD1]] +// CHECK-NOT: !dbg, ![[LINE_ABOVE]] +// CHECK: call void @objc_storeStrong(i8** [[VAL1:%.*]], i8* null) [[NUW]] +// CHECK-NEXT: bitcast %4** [[TMP:%.*]] to i8** +// CHECK-NEXT: call void @objc_storeStrong(i8** [[VAL2:%.*]], i8* null) [[NUW]] // CHECK-NEXT: ret // CHECK: attributes [[NUW]] = { nounwind } -// CHECK: ![[MD1]] = metadata !{i32 87 [map dataWithCompletionBlock:^(NSData *data, NSError *error) { if (data) { NSString *encoded = [[data compressedData] encodedString:18]; diff --git a/test/CodeGenObjC/debug-info-blocks.m b/test/CodeGenObjC/debug-info-blocks.m index f50ddf0d8ae..3d91c9ea5cd 100644 --- a/test/CodeGenObjC/debug-info-blocks.m +++ b/test/CodeGenObjC/debug-info-blocks.m @@ -7,11 +7,10 @@ // CHECK: define {{.*}}_block_invoke // CHECK: %[[BLOCK:.*]] = bitcast i8* %.block_descriptor to <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, %0* }>*, !dbg // CHECK-NEXT: store <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, %0* }>* %[[BLOCK]], <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, %0* }>** %[[ALLOCA:.*]], align -// CHECK-NEXT: getelementptr inbounds <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, %0* }>* %[[BLOCK]], i32 0, i32 5 // CHECK-NEXT: call void @llvm.dbg.declare(metadata !{<{ i8*, i32, i32, i8*, %struct.__block_descriptor*, %0* }>** %[[ALLOCA]]}, metadata ![[SELF:[0-9]+]]) // CHECK-NEXT: call void @llvm.dbg.declare(metadata !{%1** %d}, metadata ![[D:[0-9]+]]) -// CHECK: ![[SELF]] = {{.*}} [ DW_TAG_auto_variable ] [self] [line 52] -// CHECK: ![[D]] = {{.*}} [d] [line 50] +// CHECK: ![[SELF]] = {{.*}} [ DW_TAG_auto_variable ] [self] [line 51] +// CHECK: ![[D]] = {{.*}} [d] [line 49] typedef unsigned int NSUInteger; diff --git a/test/CodeGenObjC/encode-test-3.m b/test/CodeGenObjC/encode-test-3.m index 4b39cd718ea..b76063ffd3d 100644 --- a/test/CodeGenObjC/encode-test-3.m +++ b/test/CodeGenObjC/encode-test-3.m @@ -1,12 +1,14 @@ -// RUN: %clang_cc1 -triple=i686-apple-darwin9 -emit-llvm -o %t %s -// RUN: grep -e "\^i" %t | count 1 -// RUN: grep -e "\[0i\]" %t | count 1 +// RUN: %clang_cc1 -triple=i686-apple-darwin9 -emit-llvm -o - %s | FileCheck %s int main() { int n; const char * inc = @encode(int[]); +// CHECK: ^i +// CHECK-NOT: ^i const char * vla = @encode(int[n]); +// CHECK: [0i] +// CHECK-NOT: [0i] } // PR3648 diff --git a/test/CodeGenObjC/metadata-symbols-32.m b/test/CodeGenObjC/metadata-symbols-32.m index e8d25129a75..fda909ca744 100644 --- a/test/CodeGenObjC/metadata-symbols-32.m +++ b/test/CodeGenObjC/metadata-symbols-32.m @@ -1,32 +1,32 @@ -// RUN: %clang_cc1 -triple i386-apple-darwin9 -fobjc-runtime=macosx-fragile-10.5 -emit-llvm -o %t %s - -// RUN: grep '@"\\01L_OBJC_CATEGORY_A_Cat" = internal global .*section "__OBJC,__category,regular,no_dead_strip", align 4' %t -// RUN: grep '@"\\01L_OBJC_CATEGORY_CLASS_METHODS_A_Cat" = internal global .*section "__OBJC,__cat_cls_meth,regular,no_dead_strip", align 4' %t -// RUN: grep '@"\\01L_OBJC_CATEGORY_INSTANCE_METHODS_A_Cat" = internal global .*section "__OBJC,__cat_inst_meth,regular,no_dead_strip", align 4' %t -// RUN: grep '@"\\01L_OBJC_CLASSEXT_A" = internal global .*section "__OBJC,__class_ext,regular,no_dead_strip", align 4' %t -// RUN: grep '@"\\01L_OBJC_CLASS_A" = internal global .*section "__OBJC,__class,regular,no_dead_strip", align 4' %t -// RUN: grep '@"\\01L_OBJC_CLASS_METHODS_A" = internal global .*section "__OBJC,__cls_meth,regular,no_dead_strip", align 4' %t -// RUN: grep '@"\\01L_OBJC_CLASS_NAME_[0-9]*" = internal global .*section "__TEXT,__cstring,cstring_literals", align 1' %t -// RUN: grep '@"\\01L_OBJC_CLASS_PROTOCOLS_A" = internal global .*section "__OBJC,__cat_cls_meth,regular,no_dead_strip", align 4' %t -// RUN: grep '@"\\01L_OBJC_CLASS_REFERENCES_[0-9]*" = internal global .*section "__OBJC,__cls_refs,literal_pointers,no_dead_strip", align 4' %t +// RUN: %clang_cc1 -triple i386-apple-darwin9 -fobjc-runtime=macosx-fragile-10.5 -emit-llvm -o - %s | FileCheck %s + +// CHECK: .lazy_reference .objc_class_name_J0 + +// CHECK: @"\01L_OBJC_METH_VAR_NAME_{{[0-9]*}}" = internal global {{.*}}section "__TEXT,__cstring,cstring_literals", align 1 +// CHECK: @"\01L_OBJC_METH_VAR_TYPE_{{[0-9]*}}" = internal global {{.*}}section "__TEXT,__cstring,cstring_literals", align 1 +// CHECK: @"\01L_OBJC_CLASS_NAME_{{[0-9]*}}" = internal global {{.*}}section "__TEXT,__cstring,cstring_literals", align 1 +// CHECK: @"\01L_OBJC_PROTOCOL_INSTANCE_METHODS_P" = internal global {{.*}}section "__OBJC,__cat_inst_meth,regular,no_dead_strip", align 4 +// CHECK: @"\01L_OBJC_PROTOCOL_CLASS_METHODS_P" = internal global {{.*}}section "__OBJC,__cat_cls_meth,regular,no_dead_strip", align 4 +// CHECK: @"\01L_OBJC_PROTOCOL_P" = internal global {{.*}}section "__OBJC,__protocol,regular,no_dead_strip", align 4 +// CHECK: @"\01L_OBJC_CLASS_PROTOCOLS_A" = internal global {{.*}}section "__OBJC,__cat_cls_meth,regular,no_dead_strip", align 4 +// CHECK: @"\01L_OBJC_CLASS_METHODS_A" = internal global {{.*}}section "__OBJC,__cls_meth,regular,no_dead_strip", align 4 +// CHECK: @"\01L_OBJC_METACLASS_A" = internal global {{.*}}section "__OBJC,__meta_class,regular,no_dead_strip", align 4 +// CHECK: @"\01L_OBJC_INSTANCE_VARIABLES_A" = internal global {{.*}}section "__OBJC,__instance_vars,regular,no_dead_strip", align 4 +// CHECK: @"\01L_OBJC_INSTANCE_METHODS_A" = internal global {{.*}}section "__OBJC,__inst_meth,regular,no_dead_strip", align 4 +// CHECK: @"\01L_OBJC_PROP_NAME_ATTR_{{[0-9]*}}" = internal global {{.*}}section "__TEXT,__cstring,cstring_literals", align 1 +// CHECK: @"\01l_OBJC_$_PROP_LIST_A" = internal global {{.*}}section "__OBJC,__property,regular,no_dead_strip", align 4 +// CHECK: @"\01L_OBJC_CLASSEXT_A" = internal global {{.*}}section "__OBJC,__class_ext,regular,no_dead_strip", align 4 +// CHECK: @"\01L_OBJC_CLASS_A" = internal global {{.*}}section "__OBJC,__class,regular,no_dead_strip", align 4 +// CHECK: @"\01L_OBJC_CATEGORY_INSTANCE_METHODS_A_Cat" = internal global {{.*}}section "__OBJC,__cat_inst_meth,regular,no_dead_strip", align 4 +// CHECK: @"\01L_OBJC_CATEGORY_CLASS_METHODS_A_Cat" = internal global {{.*}}section "__OBJC,__cat_cls_meth,regular,no_dead_strip", align 4 +// CHECK: @"\01L_OBJC_CATEGORY_A_Cat" = internal global {{.*}}section "__OBJC,__category,regular,no_dead_strip", align 4 +// CHECK: @"\01L_OBJC_CLASS_REFERENCES_{{[0-9]*}}" = internal global {{.*}}section "__OBJC,__cls_refs,literal_pointers,no_dead_strip", align 4 +// CHECK: @"\01L_OBJC_SELECTOR_REFERENCES_{{[0-9]*}}" = internal externally_initialized global {{.*}}section "__OBJC,__message_refs,literal_pointers,no_dead_strip", align 4 +// CHECK: @"\01L_OBJC_SYMBOLS" = internal global {{.*}}section "__OBJC,__symbols,regular,no_dead_strip", align 4 +// CHECK: @"\01L_OBJC_MODULES" = internal global {{.*}}section "__OBJC,__module_info,regular,no_dead_strip", align 4 // Clang's Obj-C 32-bit doesn't emit ivars for the root class. -// RUNX: grep '@"\\01L_OBJC_CLASS_VARIABLES_A" = internal global .*section "__OBJC,__class_vars,regular,no_dead_strip", align 4' %t && - -// RUN: grep '@"\\01L_OBJC_INSTANCE_METHODS_A" = internal global .*section "__OBJC,__inst_meth,regular,no_dead_strip", align 4' %t -// RUN: grep '@"\\01L_OBJC_INSTANCE_VARIABLES_A" = internal global .*section "__OBJC,__instance_vars,regular,no_dead_strip", align 4' %t -// RUN: grep '@"\\01L_OBJC_METACLASS_A" = internal global .*section "__OBJC,__meta_class,regular,no_dead_strip", align 4' %t -// RUN: grep '@"\\01L_OBJC_METH_VAR_NAME_[0-9]*" = internal global .*section "__TEXT,__cstring,cstring_literals", align 1' %t -// RUN: grep '@"\\01L_OBJC_METH_VAR_TYPE_[0-9]*" = internal global .*section "__TEXT,__cstring,cstring_literals", align 1' %t -// RUN: grep '@"\\01L_OBJC_MODULES" = internal global .*section "__OBJC,__module_info,regular,no_dead_strip", align 4' %t -// RUN: grep '@"\\01L_OBJC_PROP_NAME_ATTR_[0-9]*" = internal global .*section "__TEXT,__cstring,cstring_literals", align 1' %t -// RUN: grep '@"\\01L_OBJC_PROTOCOL_CLASS_METHODS_P" = internal global .*section "__OBJC,__cat_cls_meth,regular,no_dead_strip", align 4' %t -// RUN: grep '@"\\01L_OBJC_PROTOCOL_INSTANCE_METHODS_P" = internal global .*section "__OBJC,__cat_inst_meth,regular,no_dead_strip", align 4' %t -// RUN: grep '@"\\01L_OBJC_PROTOCOL_P" = internal global .*section "__OBJC,__protocol,regular,no_dead_strip", align 4' %t -// RUN: grep '@"\\01L_OBJC_SELECTOR_REFERENCES_[0-9]*" = internal externally_initialized global .*section "__OBJC,__message_refs,literal_pointers,no_dead_strip", align 4' %t -// RUN: grep '@"\\01L_OBJC_SYMBOLS" = internal global .*section "__OBJC,__symbols,regular,no_dead_strip", align 4' %t -// RUN: grep '@"\\01l_OBJC_$_PROP_LIST_A" = internal global .*section "__OBJC,__property,regular,no_dead_strip", align 4' %t -// RUN: grep "\.lazy_reference \.objc_class_name_J0" %t +// CHECKX: @"\01L_OBJC_CLASS_VARIABLES_A" = internal global {{.*}}section "__OBJC,__class_vars,regular,no_dead_strip", align 4 /* diff --git a/test/CodeGenObjC/metadata-symbols-64.m b/test/CodeGenObjC/metadata-symbols-64.m index 27017b76a8e..a89fec56de2 100644 --- a/test/CodeGenObjC/metadata-symbols-64.m +++ b/test/CodeGenObjC/metadata-symbols-64.m @@ -1,37 +1,38 @@ -// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-dispatch-method=mixed -emit-llvm -o %t %s - -// RUN: grep '@"OBJC_CLASS_$_A" = global' %t -// RUN: grep '@"OBJC_CLASS_$_B" = external global' %t -// RUN: grep '@"OBJC_IVAR_$_A._ivar" = global .* section "__DATA, __objc_ivar", align 8' %t -// RUN: grep '@"OBJC_METACLASS_$_A" = global .* section "__DATA, __objc_data", align 8' %t -// RUN: grep '@"\\01L_OBJC_CLASSLIST_REFERENCES_$_[0-9]*" = internal global .* section "__DATA, __objc_classrefs, regular, no_dead_strip", align 8' %t -// RUN: grep '@"\\01L_OBJC_CLASSLIST_SUP_REFS_$_[0-9]*" = internal global .* section "__DATA, __objc_superrefs, regular, no_dead_strip", align 8' %t | count 2 -// RUN: grep '@"\\01L_OBJC_CLASS_NAME_[0-9]*" = internal global .* section "__TEXT,__objc_classname,cstring_literals", align 1' %t -// RUN: grep '@"\\01L_OBJC_LABEL_CATEGORY_$" = internal global .* section "__DATA, __objc_catlist, regular, no_dead_strip", align 8' %t -// RUN: grep '@"\\01L_OBJC_LABEL_CLASS_$" = internal global .* section "__DATA, __objc_classlist, regular, no_dead_strip", align 8' %t -// RUN: grep '@"\\01L_OBJC_METH_VAR_NAME_[0-9]*" = internal global .* section "__TEXT,__objc_methname,cstring_literals", align 1' %t -// RUN: grep '@"\\01L_OBJC_METH_VAR_TYPE_[0-9]*" = internal global .* section "__TEXT,__objc_methtype,cstring_literals", align 1' %t -// RUN: grep '@"\\01L_OBJC_PROP_NAME_ATTR_[0-9]*" = internal global .* section "__TEXT,__cstring,cstring_literals", align 1' %t -// RUN: grep '@"\\01L_OBJC_SELECTOR_REFERENCES_*" = internal externally_initialized global .* section "__DATA, __objc_selrefs, literal_pointers, no_dead_strip"' %t -// RUN: grep '@"\\01l_OBJC_$_CATEGORY_A_$_Cat" = internal global .* section "__DATA, __objc_const", align 8' %t -// RUN: grep '@"\\01l_OBJC_$_CATEGORY_CLASS_METHODS_A_$_Cat" = internal global .* section "__DATA, __objc_const", align 8' %t -// RUN: grep '@"\\01l_OBJC_$_CATEGORY_INSTANCE_METHODS_A_$_Cat" = internal global .* section "__DATA, __objc_const", align 8' %t -// RUN: grep '@"\\01l_OBJC_$_CLASS_METHODS_A" = internal global .* section "__DATA, __objc_const", align 8' %t -// RUN: grep '@"\\01l_OBJC_$_INSTANCE_METHODS_A" = internal global .* section "__DATA, __objc_const", align 8' %t -// RUN: grep '@"\\01l_OBJC_$_INSTANCE_VARIABLES_A" = internal global .* section "__DATA, __objc_const", align 8' %t -// RUN: grep '@"\\01l_OBJC_$_PROP_LIST_A" = internal global .* section "__DATA, __objc_const", align 8' %t -// RUN: grep '@"\\01l_OBJC_$_PROTOCOL_CLASS_METHODS_P" = internal global .* section "__DATA, __objc_const", align 8' %t -// RUN: grep '@"\\01l_OBJC_$_PROTOCOL_INSTANCE_METHODS_P" = internal global .* section "__DATA, __objc_const", align 8' %t -// RUN: grep '@"\\01l_OBJC_CLASS_PROTOCOLS_$_A" = internal global .* section "__DATA, __objc_const", align 8' %t -// RUN: grep '@"\\01l_OBJC_CLASS_RO_$_A" = internal global .* section "__DATA, __objc_const", align 8' %t -// RUN: grep '@"\\01l_OBJC_LABEL_PROTOCOL_$_P" = weak hidden global .* section "__DATA, __objc_protolist, coalesced, no_dead_strip", align 8' %t -// RUN: grep '@"\\01l_OBJC_METACLASS_RO_$_A" = internal global .* section "__DATA, __objc_const", align 8' %t -// RUN: grep '@"\\01l_OBJC_PROTOCOL_$_P" = weak hidden global .* section "__DATA,__datacoal_nt,coalesced", align 8' %t -// RUN: grep '@"\\01l_objc_msgSend_fixup_alloc" = weak hidden global .* section "__DATA, __objc_msgrefs, coalesced", align 16' %t -// RUN: grep '@_objc_empty_cache = external global' %t -// RUN: grep '@_objc_empty_vtable = external global' %t -// RUN: grep '@objc_msgSend_fixup(' %t -// RUN: grep '@objc_msgSend_fpret(' %t +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-dispatch-method=mixed -emit-llvm -o - %s | FileCheck %s + +// CHECK: @"OBJC_IVAR_$_A._ivar" = global {{.*}} section "__DATA, __objc_ivar", align 8 +// CHECK: @_objc_empty_cache = external global +// CHECK: @_objc_empty_vtable = external global +// CHECK: @"OBJC_CLASS_$_A" = global +// CHECK: @"OBJC_METACLASS_$_A" = global {{.*}} section "__DATA, __objc_data", align 8 +// CHECK: @"\01L_OBJC_CLASS_NAME_{{[0-9]*}}" = internal global {{.*}} section "__TEXT,__objc_classname,cstring_literals", align 1 +// CHECK: @"\01L_OBJC_METH_VAR_NAME_{{[0-9]*}}" = internal global {{.*}} section "__TEXT,__objc_methname,cstring_literals", align 1 +// CHECK: @"\01L_OBJC_METH_VAR_TYPE_{{[0-9]*}}" = internal global {{.*}} section "__TEXT,__objc_methtype,cstring_literals", align 1 +// CHECK: @"\01l_OBJC_$_CLASS_METHODS_A" = internal global {{.*}} section "__DATA, __objc_const", align 8 +// CHECK: @"\01l_OBJC_$_PROTOCOL_INSTANCE_METHODS_P" = internal global {{.*}} section "__DATA, __objc_const", align 8 +// CHECK: @"\01l_OBJC_$_PROTOCOL_CLASS_METHODS_P" = internal global {{.*}} section "__DATA, __objc_const", align 8 +// CHECK: @"\01l_OBJC_PROTOCOL_$_P" = weak hidden global {{.*}} section "__DATA,__datacoal_nt,coalesced", align 8 +// CHECK: @"\01l_OBJC_LABEL_PROTOCOL_$_P" = weak hidden global {{.*}} section "__DATA, __objc_protolist, coalesced, no_dead_strip", align 8 +// CHECK: @"\01l_OBJC_CLASS_PROTOCOLS_$_A" = internal global {{.*}} section "__DATA, __objc_const", align 8 +// CHECK: @"\01l_OBJC_METACLASS_RO_$_A" = internal global {{.*}} section "__DATA, __objc_const", align 8 +// CHECK: @"\01l_OBJC_$_INSTANCE_METHODS_A" = internal global {{.*}} section "__DATA, __objc_const", align 8 +// CHECK: @"\01l_OBJC_$_INSTANCE_VARIABLES_A" = internal global {{.*}} section "__DATA, __objc_const", align 8 +// CHECK: @"\01L_OBJC_PROP_NAME_ATTR_{{[0-9]*}}" = internal global {{.*}} section "__TEXT,__cstring,cstring_literals", align 1 +// CHECK: @"\01l_OBJC_$_PROP_LIST_A" = internal global {{.*}} section "__DATA, __objc_const", align 8 +// CHECK: @"\01l_OBJC_CLASS_RO_$_A" = internal global {{.*}} section "__DATA, __objc_const", align 8 +// CHECK: @"\01l_OBJC_$_CATEGORY_INSTANCE_METHODS_A_$_Cat" = internal global {{.*}} section "__DATA, __objc_const", align 8 +// CHECK: @"\01l_OBJC_$_CATEGORY_CLASS_METHODS_A_$_Cat" = internal global {{.*}} section "__DATA, __objc_const", align 8 +// CHECK: @"\01l_OBJC_$_CATEGORY_A_$_Cat" = internal global {{.*}} section "__DATA, __objc_const", align 8 +// CHECK: @"\01L_OBJC_CLASSLIST_SUP_REFS_$_{{[0-9]*}}" = internal global {{.*}} section "__DATA, __objc_superrefs, regular, no_dead_strip", align 8 +// CHECK: @"\01L_OBJC_SELECTOR_REFERENCES_" = internal externally_initialized global {{.*}} section "__DATA, __objc_selrefs, literal_pointers, no_dead_strip" +// CHECK: @"\01L_OBJC_CLASSLIST_SUP_REFS_$_{{[0-9]*}}" = internal global {{.*}} section "__DATA, __objc_superrefs, regular, no_dead_strip", align 8 +// CHECK: @"OBJC_CLASS_$_B" = external global +// CHECK: @"\01L_OBJC_CLASSLIST_REFERENCES_$_{{[0-9]*}}" = internal global {{.*}} section "__DATA, __objc_classrefs, regular, no_dead_strip", align 8 +// CHECK: @"\01l_objc_msgSend_fixup_alloc" = weak hidden global {{.*}} section "__DATA, __objc_msgrefs, coalesced", align 16 +// CHECK: @"\01L_OBJC_LABEL_CLASS_$" = internal global {{.*}} section "__DATA, __objc_classlist, regular, no_dead_strip", align 8 +// CHECK: @"\01L_OBJC_LABEL_CATEGORY_$" = internal global {{.*}} section "__DATA, __objc_catlist, regular, no_dead_strip", align 8 +// CHECK: @objc_msgSend_fpret( +// CHECK: @objc_msgSend_fixup( /* diff --git a/test/CodeGenObjC/metadata_symbols.m b/test/CodeGenObjC/metadata_symbols.m index 576a55b1369..eedcf16627a 100644 --- a/test/CodeGenObjC/metadata_symbols.m +++ b/test/CodeGenObjC/metadata_symbols.m @@ -1,6 +1,12 @@ // RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -fexceptions -fobjc-exceptions -o %t %s // RUN: FileCheck -check-prefix=CHECK-X86_64 < %t %s -// RUN: grep '@"OBJC_EHTYPE_$_EH3"' %t | count 3 +// RUN: FileCheck -check-prefix=CHECK-EHTYPE < %t %s + +// We need exactly 3 of these. +// CHECK-EHTYPE: @"OBJC_EHTYPE_$_EH3" +// CHECK-EHTYPE: @"OBJC_EHTYPE_$_EH3" +// CHECK-EHTYPE: @"OBJC_EHTYPE_$_EH3" +// CHECK-EHTYPE-NOT: @"OBJC_EHTYPE_$_EH3" // CHECK-X86_64: @"OBJC_CLASS_$_A" = global {{.*}}, section "__DATA, __objc_data", align 8 // CHECK-X86_64: @"OBJC_METACLASS_$_A" = global {{.*}}, section "__DATA, __objc_data", align 8 diff --git a/test/CodeGenObjC/objc-align.m b/test/CodeGenObjC/objc-align.m index 1a9e882a131..1a96f34c5d9 100644 --- a/test/CodeGenObjC/objc-align.m +++ b/test/CodeGenObjC/objc-align.m @@ -1,14 +1,14 @@ // 32-bit -// RUN: %clang_cc1 -triple i386-apple-darwin9 -fobjc-runtime=macosx-fragile-10.5 -emit-llvm -o %t %s -// RUN: grep '@"\\01L_OBJC_CATEGORY_A_Cat" = internal global .*, section "__OBJC,__category,regular,no_dead_strip", align 4' %t -// RUN: grep '@"\\01L_OBJC_CLASS_A" = internal global .*, section "__OBJC,__class,regular,no_dead_strip", align 4' %t -// RUN: grep '@"\\01L_OBJC_CLASS_C" = internal global .*, section "__OBJC,__class,regular,no_dead_strip", align 4' %t -// RUN: grep '@"\\01L_OBJC_CLASS_PROTOCOLS_C" = internal global .*, section "__OBJC,__cat_cls_meth,regular,no_dead_strip", align 4' %t -// RUN: grep '@"\\01L_OBJC_METACLASS_A" = internal global .*, section "__OBJC,__meta_class,regular,no_dead_strip", align 4' %t -// RUN: grep '@"\\01L_OBJC_METACLASS_C" = internal global .*, section "__OBJC,__meta_class,regular,no_dead_strip", align 4' %t -// RUN: grep '@"\\01L_OBJC_MODULES" = internal global .*, section "__OBJC,__module_info,regular,no_dead_strip", align 4' %t -// RUN: grep '@"\\01L_OBJC_PROTOCOL_P" = internal global .*, section "__OBJC,__protocol,regular,no_dead_strip", align 4' %t +// RUN: %clang_cc1 -triple i386-apple-darwin9 -fobjc-runtime=macosx-fragile-10.5 -emit-llvm -o - %s | FileCheck %s +// CHECK: @"\01L_OBJC_METACLASS_A" = internal global {{.*}}, section "__OBJC,__meta_class,regular,no_dead_strip", align 4 +// CHECK: @"\01L_OBJC_CLASS_A" = internal global {{.*}}, section "__OBJC,__class,regular,no_dead_strip", align 4 +// CHECK: @"\01L_OBJC_CATEGORY_A_Cat" = internal global {{.*}}, section "__OBJC,__category,regular,no_dead_strip", align 4 +// CHECK: @"\01L_OBJC_PROTOCOL_P" = internal global {{.*}}, section "__OBJC,__protocol,regular,no_dead_strip", align 4 +// CHECK: @"\01L_OBJC_CLASS_PROTOCOLS_C" = internal global {{.*}}, section "__OBJC,__cat_cls_meth,regular,no_dead_strip", align 4 +// CHECK: @"\01L_OBJC_METACLASS_C" = internal global {{.*}}, section "__OBJC,__meta_class,regular,no_dead_strip", align 4 +// CHECK: @"\01L_OBJC_CLASS_C" = internal global {{.*}}, section "__OBJC,__class,regular,no_dead_strip", align 4 +// CHECK: @"\01L_OBJC_MODULES" = internal global {{.*}}, section "__OBJC,__module_info,regular,no_dead_strip", align 4 // 64-bit diff --git a/test/CodeGenObjC/objc-fixed-enum.m b/test/CodeGenObjC/objc-fixed-enum.m new file mode 100644 index 00000000000..55c2a7c1031 --- /dev/null +++ b/test/CodeGenObjC/objc-fixed-enum.m @@ -0,0 +1,64 @@ +// RUN: %clang_cc1 -g -emit-llvm -o - %s | FileCheck %s +// The DWARF standard says the underlying data type of an enum may be +// stored in an DW_AT_type entry in the enum DIE. This is useful to have +// so the debugger knows about the signedness of the underlying type. + +typedef long NSInteger; +#define NS_ENUM(_type, _name) enum _name : _type _name; enum _name : _type + +// Enum with no specified underlying type +typedef enum { + Enum0One, + Enum0Two +} Enum0; + +// Enum declared with the NS_ENUM macro +typedef NS_ENUM(NSInteger, Enum1) { + Enum1One = -1, + Enum1Two +}; + +// Enum declared with a fixed underlying type +typedef enum : NSInteger { + Enum2One = -1, + Enum2Two +} Enum2; + +// Typedef and declaration separately +enum : NSInteger +{ + Enum3One = -1, + Enum3Two +}; +typedef NSInteger Enum3; + +int main() { + Enum0 e0 = Enum0One; + // CHECK: call void @llvm.dbg.declare(metadata !{{.*}}, metadata ![[ENUM0:[0-9]+]]) + Enum1 e1 = Enum1One; + // CHECK: call void @llvm.dbg.declare(metadata !{{.*}}, metadata ![[ENUM1:[0-9]+]]) + Enum2 e2 = Enum2One; + // CHECK: call void @llvm.dbg.declare(metadata !{{.*}}, metadata ![[ENUM2:[0-9]+]]) + Enum3 e3 = Enum3One; + // CHECK: call void @llvm.dbg.declare(metadata !{{.*}}, metadata ![[ENUM3:[0-9]+]]) + + // -Werror and the following line ensures that these enums are not + // -treated as C++11 strongly typed enums. + return e0 != e1 && e1 == e2 && e2 == e3; +} +// CHECK: ![[ENUMERATOR0:[0-9]+]] = {{.*}}; [ DW_TAG_enumeration_type ] [line 10 +// CHECK: ![[ENUMERATOR1:[0-9]+]] = {{.*}}; [ DW_TAG_enumeration_type ] [Enum1] [line 16{{.*}}] [from NSInteger] +// CHECK: ![[ENUMERATOR3:[0-9]+]] = {{.*}}; [ DW_TAG_typedef ] [NSInteger] [line 6{{.*}}] [from long int] +// CHECK: ![[ENUMERATOR2:[0-9]+]] = {{.*}}; [ DW_TAG_enumeration_type ] [line 22{{.*}}] [from NSInteger] + +// CHECK: ![[ENUM0]] = metadata !{{{.*}}!"e0", metadata !{{[0-9]+}}, i32 {{[0-9]+}}, metadata ![[TYPE0:[0-9]+]] +// CHECK: ![[TYPE0]] = metadata !{{{.*}}!"Enum0", {{.*}} metadata ![[ENUMERATOR0]]} ; [ DW_TAG_typedef ] [Enum0] + +// CHECK: ![[ENUM1]] = metadata !{{{.*}}!"e1", metadata !{{[0-9]+}}, i32 {{[0-9]+}}, metadata ![[TYPE1:[0-9]+]] +// CHECK: ![[TYPE1]] = metadata !{{{.*}}!"Enum1", {{.*}} metadata ![[ENUMERATOR1]]} ; [ DW_TAG_typedef ] [Enum1] + +// CHECK: ![[ENUM2]] = metadata !{{{.*}}!"e2", metadata !{{[0-9]+}}, i32 {{[0-9]+}}, metadata ![[TYPE2:[0-9]+]] +// CHECK: ![[TYPE2]] = metadata !{{{.*}}!"Enum2", {{.*}} metadata ![[ENUMERATOR2]]} ; [ DW_TAG_typedef ] [Enum2] + +// CHECK: ![[ENUM3]] = metadata !{{{.*}}!"e3", metadata !{{[0-9]+}}, i32 {{[0-9]+}}, metadata ![[TYPE3:[0-9]+]] +// CHECK: ![[TYPE3]] = metadata !{{{.*}}!"Enum3", {{.*}} metadata ![[ENUMERATOR3]]} ; [ DW_TAG_typedef ] [Enum3] diff --git a/test/CodeGenObjC/synthesize_ivar-cont-class.m b/test/CodeGenObjC/synthesize_ivar-cont-class.m index 98227023318..393ec3650c1 100644 --- a/test/CodeGenObjC/synthesize_ivar-cont-class.m +++ b/test/CodeGenObjC/synthesize_ivar-cont-class.m @@ -1,5 +1,4 @@ -// RUN: %clang_cc1 -emit-llvm -o %t %s -// RUN: grep '@"OBJC_IVAR_$_XCOrganizerDeviceNodeInfo.viewController"' %t +// RUN: %clang_cc1 -emit-llvm -o - %s | FileCheck %s // PR13820 // REQUIRES: LP64 @@ -17,5 +16,6 @@ @implementation XCOrganizerDeviceNodeInfo @synthesize viewController; +// CHECK: @"OBJC_IVAR_$_XCOrganizerDeviceNodeInfo.viewController" @end diff --git a/test/CodeGenObjC/tentative-cfconstantstring.m b/test/CodeGenObjC/tentative-cfconstantstring.m new file mode 100644 index 00000000000..b7e1c4601fe --- /dev/null +++ b/test/CodeGenObjC/tentative-cfconstantstring.m @@ -0,0 +1,43 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -o - %s | FileCheck %s +// rdar://13598026 + +@interface NSObject @end + +@class NSString; + +int __CFConstantStringClassReference[24]; + +@interface Bar : NSObject ++(void)format:(NSString *)format,...; +@end + +@interface Foo : NSObject +@end + + +static inline void _inlineFunction() { + [Bar format:@" "]; +} + +@implementation Foo + + ++(NSString *)someMethod { + return @""; +} + +-(void)someMethod { + _inlineFunction(); +} +@end + +// CHECK: @__CFConstantStringClassReference = common global [24 x i32] zeroinitializer, align 16 +// CHECK: @_unnamed_cfstring_{{.*}} = private constant %struct.NSConstantString { i32* getelementptr inbounds ([24 x i32]* @__CFConstantStringClassReference, i32 0, i32 0) + +// CHECK: define internal void @_inlineFunction() +// CHECK: [[ZERO:%.*]] = load %struct._class_t** @"\01L_OBJC_CLASSLIST_REFERENCES_ +// CHECK-NEXT: [[ONE:%.*]] = load i8** @"\01L_OBJC_SELECTOR_REFERENCES_" +// CHECK-NEXT: [[TWO:%.*]] = bitcast %struct._class_t* [[ZERO]] to i8* +// CHECK-NEXT: call void (i8*, i8*, [[T:%.*]]*, ...)* bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to void (i8*, i8*, [[T:%.*]]*, ...)*)(i8* [[TWO]], i8* [[ONE]], [[T:%.*]]* bitcast (%struct.NSConstantString* @_unnamed_cfstring_{{.*}} to [[T:%.*]]*)) +// CHECK-NEXT: ret void + diff --git a/test/CodeGenObjCXX/arc.mm b/test/CodeGenObjCXX/arc.mm index 1888dbe77d8..e3e86a0b0d5 100644 --- a/test/CodeGenObjCXX/arc.mm +++ b/test/CodeGenObjCXX/arc.mm @@ -275,3 +275,25 @@ id Test39::bar() { return 0; } // CHECK: define i8* @_ZThn8_N6Test393barEv( // CHECK: call i8* @_ZN6Test393barEv( // CHECK-NEXT: ret i8* + +// rdar://13617051 +// Just a basic sanity-check that IR-gen still works after instantiating +// a non-dependent message send that requires writeback. +@interface Test40 ++ (void) foo:(id *)errorPtr; +@end +template void test40_helper() { + id x; + [Test40 foo: &x]; +}; +template void test40_helper(); +// CHECK: define weak_odr void @_Z13test40_helperIiEvv() +// CHECK: [[X:%.*]] = alloca i8* +// CHECK-NEXT: [[TEMP:%.*]] = alloca i8* +// CHECK-NEXT: store i8* null, i8** [[X]] +// CHECK: [[T0:%.*]] = load i8** [[X]] +// CHECK-NEXT: store i8* [[T0]], i8** [[TEMP]] +// CHECK: @objc_msgSend +// CHECK-NEXT: [[T0:%.*]] = load i8** [[TEMP]] +// CHECK-NEXT: call i8* @objc_retain(i8* [[T0]]) + diff --git a/test/CodeGenObjCXX/lambda-expressions.mm b/test/CodeGenObjCXX/lambda-expressions.mm index 7c1e2e4f57f..c73e1727d63 100644 --- a/test/CodeGenObjCXX/lambda-expressions.mm +++ b/test/CodeGenObjCXX/lambda-expressions.mm @@ -38,6 +38,28 @@ void f2() { global = []{ return 3; }; } // ARC: define internal i32 @___Z2f2v_block_invoke // ARC: call i32 @"_ZZ2f2vENK3$_1clEv +template void take_lambda(T &&lambda) { lambda(); } +void take_block(void (^block)()) { block(); } + +// rdar://13800041 +@interface A +- (void) test; +@end +@interface B : A @end +@implementation B +- (void) test { + take_block(^{ + take_lambda([=]{ + take_block(^{ + take_lambda([=] { + [super test]; + }); + }); + }); + }); +} +@end + // ARC: attributes [[NUW]] = { nounwind{{.*}} } // MRC: attributes [[NUW]] = { nounwind{{.*}} } diff --git a/test/CodeGenObjCXX/mangle.mm b/test/CodeGenObjCXX/mangle.mm index 2521c6076a8..45a93a196dc 100644 --- a/test/CodeGenObjCXX/mangle.mm +++ b/test/CodeGenObjCXX/mangle.mm @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 %s -triple=x86_64-apple-darwin10 -emit-llvm -o - | FileCheck %s +// RUN: %clang_cc1 %s -triple=x86_64-apple-darwin10 -std=c++11 -emit-llvm -o - | FileCheck %s // CHECK: @"_ZZ11+[A shared]E1a" = internal global // CHECK: @"_ZZ11-[A(Foo) f]E1a" = internal global @@ -54,3 +54,27 @@ uiIsVisible(); } @end + +// rdar://13434937 +// +// Don't crash when mangling an enum whose semantic context +// is a class extension (which looks anonymous in the AST). +// The other tests here are just for coverage. +@interface Test2 @end +@interface Test2 () +@property (assign) enum { T2x, T2y, T2z } axis; +@end +@interface Test2 (a) +@property (assign) enum { T2i, T2j, T2k } dimension; +@end +@implementation Test2 { +@public + enum { T2a, T2b, T2c } alt_axis; +} +@end +template struct Test2Template { Test2Template() {} }; // must have a member that we'll instantiate and mangle +void test2(Test2 *t) { + Test2Template t0; + Test2Template t1; + Test2Templatealt_axis)> t2; +} diff --git a/test/Driver/Inputs/fedora_18_tree/lib/.keep b/test/Driver/Inputs/fedora_18_tree/lib/.keep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/fedora_18_tree/usr/lib/crt1.o b/test/Driver/Inputs/fedora_18_tree/usr/lib/crt1.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/fedora_18_tree/usr/lib/crti.o b/test/Driver/Inputs/fedora_18_tree/usr/lib/crti.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/fedora_18_tree/usr/lib/crtn.o b/test/Driver/Inputs/fedora_18_tree/usr/lib/crtn.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/fedora_18_tree/usr/lib/gcc/armv7hl-redhat-linux-gnueabi/4.7.2/crtbegin.o b/test/Driver/Inputs/fedora_18_tree/usr/lib/gcc/armv7hl-redhat-linux-gnueabi/4.7.2/crtbegin.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/fedora_18_tree/usr/lib/gcc/armv7hl-redhat-linux-gnueabi/4.7.2/crtend.o b/test/Driver/Inputs/fedora_18_tree/usr/lib/gcc/armv7hl-redhat-linux-gnueabi/4.7.2/crtend.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/bin/.keep b/test/Driver/Inputs/mips_cs_tree/bin/.keep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/64/crtbegin.o b/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/64/crtbegin.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/64/crtend.o b/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/64/crtend.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/crtbegin.o b/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/crtbegin.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/crtend.o b/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/crtend.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/el/64/crtbegin.o b/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/el/64/crtbegin.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/el/64/crtend.o b/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/el/64/crtend.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/el/crtbegin.o b/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/el/crtbegin.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/el/crtend.o b/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/el/crtend.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/include-fixed/64/.keep b/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/include-fixed/64/.keep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/include-fixed/el/64/.keep b/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/include-fixed/el/64/.keep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/include-fixed/micromips/el/.keep b/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/include-fixed/micromips/el/.keep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/include-fixed/micromips/soft-float/el/.keep b/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/include-fixed/micromips/soft-float/el/.keep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/include-fixed/mips16/el/.keep b/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/include-fixed/mips16/el/.keep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/include-fixed/mips16/soft-float/el/.keep b/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/include-fixed/mips16/soft-float/el/.keep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/include-fixed/soft-float/64/.keep b/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/include-fixed/soft-float/64/.keep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/include-fixed/soft-float/el/64/.keep b/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/include-fixed/soft-float/el/64/.keep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/include/.keep b/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/include/.keep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/micromips/crtbegin.o b/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/micromips/crtbegin.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/micromips/crtend.o b/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/micromips/crtend.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/micromips/el/crtbegin.o b/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/micromips/el/crtbegin.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/micromips/el/crtend.o b/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/micromips/el/crtend.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/micromips/soft-float/crtbegin.o b/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/micromips/soft-float/crtbegin.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/micromips/soft-float/crtend.o b/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/micromips/soft-float/crtend.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/micromips/soft-float/el/crtbegin.o b/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/micromips/soft-float/el/crtbegin.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/micromips/soft-float/el/crtend.o b/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/micromips/soft-float/el/crtend.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/mips16/crtbegin.o b/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/mips16/crtbegin.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/mips16/crtend.o b/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/mips16/crtend.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/mips16/el/crtbegin.o b/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/mips16/el/crtbegin.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/mips16/el/crtend.o b/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/mips16/el/crtend.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/mips16/soft-float/crtbegin.o b/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/mips16/soft-float/crtbegin.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/mips16/soft-float/crtend.o b/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/mips16/soft-float/crtend.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/mips16/soft-float/el/crtbegin.o b/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/mips16/soft-float/el/crtbegin.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/mips16/soft-float/el/crtend.o b/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/mips16/soft-float/el/crtend.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/soft-float/64/crtbegin.o b/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/soft-float/64/crtbegin.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/soft-float/64/crtend.o b/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/soft-float/64/crtend.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/soft-float/crtbegin.o b/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/soft-float/crtbegin.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/soft-float/crtend.o b/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/soft-float/crtend.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/soft-float/el/64/crtbegin.o b/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/soft-float/el/64/crtbegin.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/soft-float/el/64/crtend.o b/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/soft-float/el/64/crtend.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/soft-float/el/crtbegin.o b/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/soft-float/el/crtbegin.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/soft-float/el/crtend.o b/test/Driver/Inputs/mips_cs_tree/lib/gcc/mips-linux-gnu/4.6.3/soft-float/el/crtend.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/include/c++/4.6.3/mips-linux-gnu/.keep b/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/include/c++/4.6.3/mips-linux-gnu/.keep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/lib/el/.keep b/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/lib/el/.keep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/lib/micromips/el/.keep b/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/lib/micromips/el/.keep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/lib/micromips/soft-float/el/.keep b/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/lib/micromips/soft-float/el/.keep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/lib/mips16/el/.keep b/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/lib/mips16/el/.keep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/lib/mips16/soft-float/el/.keep b/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/lib/mips16/soft-float/el/.keep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/lib/soft-float/el/.keep b/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/lib/soft-float/el/.keep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/lib64/el/.keep b/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/lib64/el/.keep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/lib64/soft-float/el/.keep b/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/lib64/soft-float/el/.keep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/el/lib/.keep b/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/el/lib/.keep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/el/lib64/.keep b/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/el/lib64/.keep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/el/usr/lib/crt1.o b/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/el/usr/lib/crt1.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/el/usr/lib/crti.o b/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/el/usr/lib/crti.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/el/usr/lib/crtn.o b/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/el/usr/lib/crtn.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/el/usr/lib64/crt1.o b/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/el/usr/lib64/crt1.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/el/usr/lib64/crti.o b/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/el/usr/lib64/crti.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/el/usr/lib64/crtn.o b/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/el/usr/lib64/crtn.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/lib/.keep b/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/lib/.keep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/lib64/.keep b/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/lib64/.keep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/micromips/el/lib/.keep b/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/micromips/el/lib/.keep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/micromips/el/usr/lib/crt1.o b/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/micromips/el/usr/lib/crt1.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/micromips/el/usr/lib/crti.o b/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/micromips/el/usr/lib/crti.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/micromips/el/usr/lib/crtn.o b/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/micromips/el/usr/lib/crtn.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/micromips/lib/.keep b/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/micromips/lib/.keep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/micromips/soft-float/el/lib/.keep b/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/micromips/soft-float/el/lib/.keep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/micromips/soft-float/el/usr/lib/crt1.o b/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/micromips/soft-float/el/usr/lib/crt1.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/micromips/soft-float/el/usr/lib/crti.o b/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/micromips/soft-float/el/usr/lib/crti.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/micromips/soft-float/el/usr/lib/crtn.o b/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/micromips/soft-float/el/usr/lib/crtn.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/micromips/soft-float/lib/.keep b/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/micromips/soft-float/lib/.keep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/micromips/soft-float/usr/lib/crt1.o b/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/micromips/soft-float/usr/lib/crt1.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/micromips/soft-float/usr/lib/crti.o b/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/micromips/soft-float/usr/lib/crti.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/micromips/soft-float/usr/lib/crtn.o b/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/micromips/soft-float/usr/lib/crtn.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/micromips/usr/lib/crt1.o b/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/micromips/usr/lib/crt1.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/micromips/usr/lib/crti.o b/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/micromips/usr/lib/crti.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/micromips/usr/lib/crtn.o b/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/micromips/usr/lib/crtn.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/mips16/el/lib/.keep b/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/mips16/el/lib/.keep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/mips16/el/usr/lib/crt1.o b/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/mips16/el/usr/lib/crt1.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/mips16/el/usr/lib/crti.o b/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/mips16/el/usr/lib/crti.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/mips16/el/usr/lib/crtn.o b/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/mips16/el/usr/lib/crtn.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/mips16/lib/.keep b/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/mips16/lib/.keep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/mips16/soft-float/el/lib/.keep b/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/mips16/soft-float/el/lib/.keep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/mips16/soft-float/el/usr/lib/crt1.o b/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/mips16/soft-float/el/usr/lib/crt1.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/mips16/soft-float/el/usr/lib/crti.o b/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/mips16/soft-float/el/usr/lib/crti.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/mips16/soft-float/el/usr/lib/crtn.o b/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/mips16/soft-float/el/usr/lib/crtn.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/mips16/soft-float/lib/.keep b/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/mips16/soft-float/lib/.keep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/mips16/soft-float/usr/lib/crt1.o b/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/mips16/soft-float/usr/lib/crt1.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/mips16/soft-float/usr/lib/crti.o b/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/mips16/soft-float/usr/lib/crti.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/mips16/soft-float/usr/lib/crtn.o b/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/mips16/soft-float/usr/lib/crtn.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/mips16/usr/lib/crt1.o b/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/mips16/usr/lib/crt1.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/mips16/usr/lib/crti.o b/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/mips16/usr/lib/crti.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/mips16/usr/lib/crtn.o b/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/mips16/usr/lib/crtn.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/soft-float/el/lib/.keep b/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/soft-float/el/lib/.keep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/soft-float/el/lib64/.keep b/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/soft-float/el/lib64/.keep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/soft-float/el/usr/lib/crt1.o b/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/soft-float/el/usr/lib/crt1.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/soft-float/el/usr/lib/crti.o b/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/soft-float/el/usr/lib/crti.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/soft-float/el/usr/lib/crtn.o b/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/soft-float/el/usr/lib/crtn.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/soft-float/el/usr/lib64/crt1.o b/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/soft-float/el/usr/lib64/crt1.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/soft-float/el/usr/lib64/crti.o b/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/soft-float/el/usr/lib64/crti.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/soft-float/el/usr/lib64/crtn.o b/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/soft-float/el/usr/lib64/crtn.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/soft-float/lib/.keep b/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/soft-float/lib/.keep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/soft-float/lib64/.keep b/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/soft-float/lib64/.keep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/soft-float/usr/lib/crt1.o b/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/soft-float/usr/lib/crt1.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/soft-float/usr/lib/crti.o b/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/soft-float/usr/lib/crti.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/soft-float/usr/lib/crtn.o b/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/soft-float/usr/lib/crtn.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/soft-float/usr/lib64/crt1.o b/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/soft-float/usr/lib64/crt1.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/soft-float/usr/lib64/crti.o b/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/soft-float/usr/lib64/crti.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/soft-float/usr/lib64/crtn.o b/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/soft-float/usr/lib64/crtn.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/usr/include/.keep b/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/usr/include/.keep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/usr/lib/crt1.o b/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/usr/lib/crt1.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/usr/lib/crti.o b/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/usr/lib/crti.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/usr/lib/crtn.o b/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/usr/lib/crtn.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/usr/lib64/crt1.o b/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/usr/lib64/crt1.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/usr/lib64/crti.o b/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/usr/lib64/crti.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/usr/lib64/crtn.o b/test/Driver/Inputs/mips_cs_tree/mips-linux-gnu/libc/usr/lib64/crtn.o new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/Driver/Ofast.c b/test/Driver/Ofast.c new file mode 100644 index 00000000000..1f9fc78ec1e --- /dev/null +++ b/test/Driver/Ofast.c @@ -0,0 +1,39 @@ +// RUN: %clang -Ofast -### %s 2>&1 | FileCheck -check-prefix=CHECK-OFAST %s +// RUN: %clang -O2 -Ofast -### %s 2>&1 | FileCheck -check-prefix=CHECK-OFAST %s +// RUN: %clang -fno-fast-math -Ofast -### %s 2>&1 | FileCheck -check-prefix=CHECK-OFAST %s +// RUN: %clang -fno-strict-aliasing -Ofast -### %s 2>&1 | FileCheck -check-prefix=CHECK-OFAST %s +// RUN: %clang -fno-vectorize -Ofast -### %s 2>&1 | FileCheck -check-prefix=CHECK-OFAST %s +// RUN: %clang -Ofast -O2 -### %s 2>&1 | FileCheck -check-prefix=CHECK-OFAST-O2 %s +// RUN: %clang -Ofast -fno-fast-math -### %s 2>&1 | FileCheck -check-prefix=CHECK-OFAST-NO-FAST-MATH %s +// RUN: %clang -Ofast -fno-strict-aliasing -### %s 2>&1 | FileCheck -check-prefix=CHECK-OFAST-NO-STRICT-ALIASING %s +// RUN: %clang -Ofast -fno-vectorize -### %s 2>&1 | FileCheck -check-prefix=CHECK-OFAST-NO-VECTORIZE %s + +// CHECK-OFAST: -cc1 +// CHECK-OFAST-NOT: -relaxed-aliasing +// CHECK-OFAST: -ffast-math +// CHECK-OFAST: -Ofast +// CHECK-OFAST: -vectorize-loops + +// CHECK-OFAST-O2: -cc1 +// CHECK-OFAST-O2-NOT: -relaxed-aliasing +// CHECK-OFAST-O2-NOT: -ffast-math +// CHECK-OFAST-O2-NOT: -Ofast +// CHECK-OFAST-O2: -vectorize-loops + +// CHECK-OFAST-NO-FAST-MATH: -cc1 +// CHECK-OFAST-NO-FAST-MATH-NOT: -relaxed-aliasing +// CHECK-OFAST-NO-FAST-MATH-NOT: -ffast-math +// CHECK-OFAST-NO-FAST-MATH: -Ofast +// CHECK-OFAST-NO-FAST-MATH: -vectorize-loops + +// CHECK-OFAST-NO-STRICT-ALIASING: -cc1 +// CHECK-OFAST-NO-STRICT-ALIASING: -relaxed-aliasing +// CHECK-OFAST-NO-STRICT-ALIASING: -ffast-math +// CHECK-OFAST-NO-STRICT-ALIASING: -Ofast +// CHECK-OFAST-NO-STRICT-ALIASING: -vectorize-loops + +// CHECK-OFAST-NO-VECTORIZE: -cc1 +// CHECK-OFAST-NO-VECTORIZE-NOT: -relaxed-aliasing +// CHECK-OFAST-NO-VECTORIZE: -ffast-math +// CHECK-OFAST-NO-VECTORIZE: -Ofast +// CHECK-OFAST-NO-VECTORIZE-NOT: -vectorize-loops diff --git a/test/Driver/autolink_integrated_as.c b/test/Driver/autolink_integrated_as.c new file mode 100644 index 00000000000..f1e710222a2 --- /dev/null +++ b/test/Driver/autolink_integrated_as.c @@ -0,0 +1,6 @@ +// RUN: %clang -target x86_64-apple-darwin -fsyntax-only %s -no-integrated-as -### 2>&1 | FileCheck %s + +// Test that the autolinking feature is disabled with *not* using the +// integrated assembler. + +// CHECK: -fno-autolink diff --git a/test/Driver/clang_cpp.c b/test/Driver/clang_cpp.c index 79b2f551315..bf318005665 100644 --- a/test/Driver/clang_cpp.c +++ b/test/Driver/clang_cpp.c @@ -1,4 +1,5 @@ // Verify that -include isn't included twice with -save-temps. // RUN: %clang -S -o - %s -include %t.h -save-temps -### 2> %t.log -// RUN: grep '"-include' %t.log | count 1 - +// RUN: FileCheck %s < %t.log +// CHECK: "-include +// CHECK-NOT: "-include diff --git a/test/Driver/clang_f_opts.c b/test/Driver/clang_f_opts.c index c1431a1f9e2..5451945515a 100644 --- a/test/Driver/clang_f_opts.c +++ b/test/Driver/clang_f_opts.c @@ -56,8 +56,15 @@ // RUN: %clang -### -S -fno-tree-slp-vectorize -fslp-vectorize %s 2>&1 | FileCheck -check-prefix=CHECK-SLP-VECTORIZE %s // RUN: %clang -### -S -fno-tree-slp-vectorize %s 2>&1 | FileCheck -check-prefix=CHECK-NO-SLP-VECTORIZE %s // RUN: %clang -### -S -ftree-slp-vectorize -fno-slp-vectorize %s 2>&1 | FileCheck -check-prefix=CHECK-NO-SLP-VECTORIZE %s -// CHECK-SLP-VECTORIZE: "-vectorize" -// CHECK-NO-SLP-VECTORIZE-NOT: "-vectorize" +// CHECK-SLP-VECTORIZE: "-vectorize-slp" +// CHECK-NO-SLP-VECTORIZE-NOT: "-vectorize-slp" + +// RUN: %clang -### -S -fslp-vectorize-aggressive %s 2>&1 | FileCheck -check-prefix=CHECK-SLP-VECTORIZE-AGG %s +// RUN: %clang -### -S -fno-slp-vectorize-aggressive -fslp-vectorize-aggressive %s 2>&1 | FileCheck -check-prefix=CHECK-SLP-VECTORIZE-AGG %s +// RUN: %clang -### -S -fno-slp-vectorize-aggressive %s 2>&1 | FileCheck -check-prefix=CHECK-NO-SLP-VECTORIZE-AGG %s +// RUN: %clang -### -S -fslp-vectorize-aggressive -fno-slp-vectorize-aggressive %s 2>&1 | FileCheck -check-prefix=CHECK-NO-SLP-VECTORIZE-AGG %s +// CHECK-SLP-VECTORIZE-AGG: "-vectorize-slp-aggressive" +// CHECK-NO-SLP-VECTORIZE-AGG-NOT: "-vectorize-slp-aggressive" // RUN: %clang -### -S -fextended-identifiers %s 2>&1 | FileCheck -check-prefix=CHECK-EXTENDED-IDENTIFIERS %s // RUN: %clang -### -S -fno-extended-identifiers %s 2>&1 | FileCheck -check-prefix=CHECK-NO-EXTENDED-IDENTIFIERS %s diff --git a/test/Driver/color-diagnostics.c b/test/Driver/color-diagnostics.c new file mode 100644 index 00000000000..deff5119a0f --- /dev/null +++ b/test/Driver/color-diagnostics.c @@ -0,0 +1,53 @@ +// RUN: %clang -fcolor-diagnostics -### -c %s 2>&1 \ +// RUN: | FileCheck --check-prefix=CD %s +// CHECK-CD: clang{{.*}}" "-fcolor-diagnostics" + +// RUN: %clang -fno-color-diagnostics -### -c %s 2>&1 \ +// RUN: | FileCheck --check-prefix=NCD %s +// CHECK-NCD-NOT: clang{{.*}}" "-fcolor-diagnostics" + +// RUN: %clang -fdiagnostics-color -### -c %s 2>&1 \ +// RUN: | FileCheck --check-prefix=DC %s +// CHECK-DC: clang{{.*}}" "-fcolor-diagnostics" + +// RUN: %clang -fno-diagnostics-color -### -c %s 2>&1 \ +// RUN: | FileCheck --check-prefix=NDC %s +// CHECK-NDC-NOT: clang{{.*}}" "-fcolor-diagnostics" + +// RUN: %clang -fdiagnostics-color=always -### -c %s 2>&1 \ +// RUN: | FileCheck --check-prefix=DCE_A %s +// CHECK-DCE_A: clang{{.*}}" "-fcolor-diagnostics" + +// RUN: %clang -fdiagnostics-color=never -### -c %s 2>&1 \ +// RUN: | FileCheck --check-prefix=DCE_N %s +// CHECK-DCE_N-NOT: clang{{.*}}" "-fcolor-diagnostics" + +// The test doesn't run in a PTY, so "auto" defaults to off. +// RUN: %clang -fdiagnostics-color=auto -### -c %s 2>&1 \ +// RUN: | FileCheck --check-prefix=DCE_AUTO %s +// CHECK-DCE_AUTO-NOT: clang{{.*}}" "-fcolor-diagnostics" + +// RUN: %clang -fdiagnostics-color=foo -### -c %s 2>&1 \ +// RUN: | FileCheck --check-prefix=DCE_FOO %s +// CHECK-DCE_FOO: error: the clang compiler does not support '-fdiagnostics-color=foo' + +// Check that the last flag wins. +// RUN: %clang -fno-color-diagnostics -fdiagnostics-color -### -c %s 2>&1 \ +// RUN: | FileCheck --check-prefix=NCD_DC_S %s +// CHECK-NCD_DC_S: clang{{.*}}" "-fcolor-diagnostics" + +// RUN: %clang -fcolor-diagnostics -fno-diagnostics-color -### -c %s 2>&1 \ +// RUN: | FileCheck --check-prefix=CD_NDC_S %s +// CHECK-CD_NDC_S-NOT: clang{{.*}}" "-fcolor-diagnostics" + +// RUN: %clang -fdiagnostics-color -fno-color-diagnostics -### -c %s 2>&1 \ +// RUN: | FileCheck --check-prefix=DC_NCD_S %s +// CHECK-DC_NCD_S-NOT: clang{{.*}}" "-fcolor-diagnostics" + +// RUN: %clang -fno-diagnostics-color -fcolor-diagnostics -### -c %s 2>&1 \ +// RUN: | FileCheck --check-prefix=NDC_CD_S %s +// CHECK-NDC_CD_S: clang{{.*}}" "-fcolor-diagnostics" + +// RUN: %clang -fcolor-diagnostics -fdiagnostics-color=auto -### -c %s 2>&1 \ +// RUN: | FileCheck --check-prefix=CD_DCE_AUTO_S %s +// CHECK-CD_DCE_AUTO_S-NOT: clang{{.*}}" "-fcolor-diagnostics" diff --git a/test/Driver/debug-comp-dir.S b/test/Driver/debug-comp-dir.S index ca1ca30ae6b..daf895c18ac 100644 --- a/test/Driver/debug-comp-dir.S +++ b/test/Driver/debug-comp-dir.S @@ -1,9 +1,6 @@ // RUN: cd %S && %clang -### -g %s -c 2>&1 | FileCheck -check-prefix=CHECK-PWD %s // CHECK-PWD: {{"-fdebug-compilation-dir" ".*Driver.*"}} -// RUN: env PWD=/foo %clang -### -g %s -c 2>&1 | FileCheck -check-prefix=CHECK-FOO %s -// CHECK-FOO: {{"-fdebug-compilation-dir" ".*foo"}} - // "PWD=/foo gcc" wouldn't necessarily work. You would need to pick a different // path to the same directory (try a symlink). diff --git a/test/Driver/debug.c b/test/Driver/debug.c index ca1ca30ae6b..daf895c18ac 100644 --- a/test/Driver/debug.c +++ b/test/Driver/debug.c @@ -1,9 +1,6 @@ // RUN: cd %S && %clang -### -g %s -c 2>&1 | FileCheck -check-prefix=CHECK-PWD %s // CHECK-PWD: {{"-fdebug-compilation-dir" ".*Driver.*"}} -// RUN: env PWD=/foo %clang -### -g %s -c 2>&1 | FileCheck -check-prefix=CHECK-FOO %s -// CHECK-FOO: {{"-fdebug-compilation-dir" ".*foo"}} - // "PWD=/foo gcc" wouldn't necessarily work. You would need to pick a different // path to the same directory (try a symlink). diff --git a/test/Driver/dragonfly.c b/test/Driver/dragonfly.c index 8a629da817a..4be2aadd7aa 100644 --- a/test/Driver/dragonfly.c +++ b/test/Driver/dragonfly.c @@ -1,7 +1,7 @@ -// RUN: %clang -no-canonical-prefixes -target amd64-pc-dragonfly %s -### 2> %t.log +// RUN: %clang -no-canonical-prefixes -target x86_64-pc-dragonfly %s -### 2> %t.log // RUN: FileCheck -input-file %t.log %s -// CHECK: clang{{.*}}" "-cc1" "-triple" "amd64-pc-dragonfly" -// CHECK: ld{{.*}}" "-dynamic-linker" "{{.*}}ld-elf.{{.*}}" "-o" "a.out" "{{.*}}crt1.o" "{{.*}}crti.o" "{{.*}}crtbegin.o" "{{.*}}.o" "-L{{.*}}/gcc{{.*}}" {{.*}} "-lc" "-lgcc" "{{.*}}crtend.o" "{{.*}}crtn.o" +// CHECK: clang{{.*}}" "-cc1" "-triple" "x86_64-pc-dragonfly" +// CHECK: ld{{.*}}" "--eh-frame-hdr" "-dynamic-linker" "/usr/libexec/ld-elf.so.{{.*}}" "--hash-style=both" "-o" "a.out" "{{.*}}crt1.o" "{{.*}}crti.o" "{{.*}}crtbegin.o" "{{.*}}.o" "-L{{.*}}gcc4{{.*}}" "-rpath" "{{.*}}gcc4{{.*}}" "-lc" "-lgcc" "{{.*}}crtend.o" "{{.*}}crtn.o" diff --git a/test/Driver/flags.c b/test/Driver/flags.c index 27862316f88..ff60caf2e0f 100644 --- a/test/Driver/flags.c +++ b/test/Driver/flags.c @@ -1,20 +1,26 @@ -// RUN: %clang -target i386-apple-darwin9 -### -S -msoft-float %s 2> %t.log -// RUN: grep '"-no-implicit-float"' %t.log +// RUN: %clang -target i386-apple-darwin9 -### -S -msoft-float %s 2>&1 | FileCheck -check-prefix=TEST1 %s +// TEST1: "-no-implicit-float" -// RUN: %clang -target i386-apple-darwin9 -### -S -msoft-float -mno-soft-float %s 2> %t.log -// RUN: grep '"-no-implicit-float"' %t.log | count 0 +// RUN: %clang -target i386-apple-darwin9 -### -S -msoft-float -mno-soft-float %s 2>&1 | FileCheck -check-prefix=TEST2 %s +// TEST2-NOT: "-no-implicit-float" -// RUN: %clang -target i386-apple-darwin9 -### -S -mno-soft-float %s -msoft-float 2> %t.log -// RUN: grep '"-no-implicit-float"' %t.log +// RUN: %clang -target i386-apple-darwin9 -### -S -mno-soft-float %s -msoft-float 2>&1 | FileCheck -check-prefix=TEST3 %s +// TEST3: "-no-implicit-float" -// RUN: %clang -target i386-apple-darwin9 -### -S -mno-implicit-float %s 2> %t.log -// RUN: grep '"-no-implicit-float"' %t.log +// RUN: %clang -target i386-apple-darwin9 -### -S -mno-implicit-float %s 2>&1 | FileCheck -check-prefix=TEST4 %s +// TEST4: "-no-implicit-float" -// RUN: %clang -target i386-apple-darwin9 -### -S -mkernel %s 2> %t.log -// RUN: grep '"-no-implicit-float"' %t.log +// RUN: %clang -target i386-apple-darwin9 -### -S -mno-implicit-float -mimplicit-float %s 2>&1 | FileCheck -check-prefix=TEST4A %s +// TEST4A-NOT: "-no-implicit-float" -// RUN: %clang -target i386-apple-darwin9 -### -S -mkernel -mno-soft-float %s 2> %t.log -// RUN: grep '"-no-implicit-float"' %t.log | count 0 +// RUN: %clang -target i386-apple-darwin9 -### -S -mkernel %s 2>&1 | FileCheck -check-prefix=TEST5 %s +// TEST5: "-no-implicit-float" -// RUN: %clang -target armv7-apple-darwin10 -### -S -mno-implicit-float %s 2> %t.log -// RUN: grep '"-no-implicit-float"' %t.log +// RUN: %clang -target i386-apple-darwin9 -### -S -mkernel -mno-soft-float %s 2>&1 | FileCheck -check-prefix=TEST6 %s +// TEST6-NOT: "-no-implicit-float" + +// RUN: %clang -target armv7-apple-darwin10 -### -S -mno-implicit-float %s 2>&1 | FileCheck -check-prefix=TEST7 %s +// TEST7: "-no-implicit-float" + +// RUN: %clang -target armv7-apple-darwin10 -### -S -mno-implicit-float -mimplicit-float %s 2>&1 | FileCheck -check-prefix=TEST8 %s +// TEST8-NOT: "-no-implicit-float" diff --git a/test/Driver/fparse-all-comments.c b/test/Driver/fparse-all-comments.c new file mode 100644 index 00000000000..5f825d0429a --- /dev/null +++ b/test/Driver/fparse-all-comments.c @@ -0,0 +1,5 @@ +// Check that we pass -fparse-all-comments to frontend. +// +// RUN: %clang -c %s -fparse-all-comments -### 2>&1 | FileCheck %s --check-prefix=CHECK-ARG +// +// CHECK-ARG: -fparse-all-comments diff --git a/test/Driver/fsanitize.c b/test/Driver/fsanitize.c index 1d606b43b1e..0e7522b5483 100644 --- a/test/Driver/fsanitize.c +++ b/test/Driver/fsanitize.c @@ -95,19 +95,30 @@ // CHECK-DEPRECATED: argument '-fbounds-checking' is deprecated, use '-fsanitize=bounds' instead // RUN: %clang -target x86_64-linux-gnu -fsanitize=thread %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-TSAN-NO-PIE -// CHECK-TSAN-NO-PIE: invalid argument '-fsanitize=thread' only allowed with '-pie' +// CHECK-TSAN-NO-PIE: "-mrelocation-model" "pic" "-pic-level" "2" "-pie-level" "2" +// CHECK-TSAN-NO-PIE: "-pie" // RUN: %clang -target x86_64-linux-gnu -fsanitize=memory %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-MSAN-NO-PIE -// CHECK-MSAN-NO-PIE: invalid argument '-fsanitize=memory' only allowed with '-pie' +// CHECK-MSAN-NO-PIE: "-mrelocation-model" "pic" "-pic-level" "2" "-pie-level" "2" +// CHECK-MSAN-NO-PIE: "-pie" // RUN: %clang -target x86_64-linux-gnu -fsanitize=address -fsanitize-address-zero-base-shadow %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-ASAN-ZERO-BASE-SHADOW-NO-PIE -// CHECK-ASAN-ZERO-BASE-SHADOW-NO-PIE: invalid argument '-fsanitize-address-zero-base-shadow' only allowed with '-pie' +// CHECK-ASAN-ZERO-BASE-SHADOW-NO-PIE: "-mrelocation-model" "pic" "-pic-level" "2" "-pie-level" "2" +// CHECK-ASAN-ZERO-BASE-SHADOW-NO-PIE: "-pie" // RUN: %clang -target x86_64-linux-gnu -fsanitize=address -fsanitize-address-zero-base-shadow -fno-sanitize-address-zero-base-shadow %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-ASAN-ZERO-BASE-SHADOW-CANCEL -// CHECK-ASAN-ZERO-BASE-SHADOW-CANCEL-NOT: '-fsanitize-address-zero-base-shadow' only allowed with '-pie' +// CHECK-ASAN-ZERO-BASE-SHADOW-CANCEL-NOT: "-mrelocation-model" "pic" "-pic-level" "2" "-pie-level" "2" +// CHECK-ASAN-ZERO-BASE-SHADOW-CANCEL-NOT: "-pie" // RUN: %clang -target arm-linux-androideabi -fsanitize=address %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-ANDROID-ASAN-NO-PIE -// CHECK-ANDROID-ASAN-NO-PIE: AddressSanitizer on Android requires '-pie' +// CHECK-ANDROID-ASAN-NO-PIE: "-mrelocation-model" "pic" "-pic-level" "2" "-pie-level" "2" +// CHECK-ANDROID-ASAN-NO-PIE: "-pie" + +// RUN: %clang -target arm-linux-androideabi -fsanitize=address -fsanitize-address-zero-base-shadow %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-ANDROID-ASAN-ZERO-BASE +// CHECK-ANDROID-ASAN-ZERO-BASE-NOT: argument unused during compilation + +// RUN: %clang -target arm-linux-androideabi -fsanitize=address -fno-sanitize-address-zero-base-shadow %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-ANDROID-ASAN-NO-ZERO-BASE +// CHECK-ANDROID-ASAN-NO-ZERO-BASE: '-fno-sanitize-address-zero-base-shadow' not allowed with '-fsanitize=address' // RUN: %clang -target x86_64-linux-gnu %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-RECOVER // RUN: %clang -target x86_64-linux-gnu %s -fsanitize-recover -### 2>&1 | FileCheck %s --check-prefix=CHECK-RECOVER diff --git a/test/Driver/hexagon-toolchain-elf.c b/test/Driver/hexagon-toolchain-elf.c index b3ff7b6c3a7..1a2650d16eb 100644 --- a/test/Driver/hexagon-toolchain-elf.c +++ b/test/Driver/hexagon-toolchain-elf.c @@ -560,5 +560,5 @@ // RUN: | FileCheck -check-prefix=CHECK029 %s // CHECK029: "{{.*}}clang{{.*}}" "-cc1" // CHECK029-NEXT: "{{.*}}/bin/hexagon-as" -// CHECK029: "-gdwarf-2" "--noexecstack" "--trap" "--keep-locals" +// CHECK029: "--noexecstack" "--trap" "--keep-locals" // CHECK029-NEXT: "{{.*}}/bin/hexagon-ld" diff --git a/test/Driver/hexagon-toolchain.c b/test/Driver/hexagon-toolchain.c index bfa627c421b..3e66f354c44 100644 --- a/test/Driver/hexagon-toolchain.c +++ b/test/Driver/hexagon-toolchain.c @@ -560,5 +560,5 @@ // RUN: | FileCheck -check-prefix=CHECK029 %s // CHECK029: "{{.*}}clang{{.*}}" "-cc1" // CHECK029-NEXT: "{{.*}}/bin/hexagon-as" -// CHECK029: "-gdwarf-2" "--noexecstack" "--trap" "--keep-locals" +// CHECK029: "--noexecstack" "--trap" "--keep-locals" // CHECK029-NEXT: "{{.*}}/bin/hexagon-ld" diff --git a/test/Driver/linux-ld.c b/test/Driver/linux-ld.c index 79282cbf410..ebac718b33b 100644 --- a/test/Driver/linux-ld.c +++ b/test/Driver/linux-ld.c @@ -245,6 +245,22 @@ // CHECK-UBUNTU-12-04-ARM-HF: "{{.*}}/usr/lib/gcc/arm-linux-gnueabihf/4.6.3/crtend.o" // CHECK-UBUNTU-12-04-ARM-HF: "{{.*}}/usr/lib/gcc/arm-linux-gnueabihf/4.6.3/../../../arm-linux-gnueabihf/crtn.o" // +// Check fedora 18 on arm. +// RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \ +// RUN: -target armv7-unknown-linux-gnueabihf \ +// RUN: --sysroot=%S/Inputs/fedora_18_tree \ +// RUN: | FileCheck --check-prefix=CHECK-FEDORA-18-ARM-HF %s +// CHECK-FEDORA-18-ARM-HF: "{{.*}}ld{{(.exe)?}}" "--sysroot=[[SYSROOT:[^"]+]]" +// CHECK-FEDORA-18-ARM-HF: "{{.*}}/usr/lib/gcc/armv7hl-redhat-linux-gnueabi/4.7.2/../../../crt1.o" +// CHECK-FEDORA-18-ARM-HF: "{{.*}}/usr/lib/gcc/armv7hl-redhat-linux-gnueabi/4.7.2/../../../crti.o" +// CHECK-FEDORA-18-ARM-HF: "{{.*}}/usr/lib/gcc/armv7hl-redhat-linux-gnueabi/4.7.2/crtbegin.o" +// CHECK-FEDORA-18-ARM-HF: "-L[[SYSROOT]]/usr/lib/gcc/armv7hl-redhat-linux-gnueabi/4.7.2" +// CHECK-FEDORA-18-ARM-HF: "-L[[SYSROOT]]/usr/lib/gcc/armv7hl-redhat-linux-gnueabi/4.7.2/../../.." +// CHECK-FEDORA-18-ARM-HF: "-L[[SYSROOT]]/lib" +// CHECK-FEDORA-18-ARM-HF: "-L[[SYSROOT]]/usr/lib" +// CHECK-FEDORA-18-ARM-HF: "{{.*}}/usr/lib/gcc/armv7hl-redhat-linux-gnueabi/4.7.2/crtend.o" +// CHECK-FEDORA-18-ARM-HF: "{{.*}}/usr/lib/gcc/armv7hl-redhat-linux-gnueabi/4.7.2/../../../crtn.o" +// // RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \ // RUN: -target arm-unknown-linux-gnueabi \ // RUN: --sysroot=%S/Inputs/ubuntu_12.04_LTS_multiarch_tree \ diff --git a/test/Driver/mips-abi.c b/test/Driver/mips-abi.c new file mode 100644 index 00000000000..fd2b46f41be --- /dev/null +++ b/test/Driver/mips-abi.c @@ -0,0 +1,36 @@ +// Check passing Mips ABI options to the backend. +// +// RUN: %clang -target mips-linux-gnu -### -c %s \ +// RUN: -mabi=32 2>&1 \ +// RUN: | FileCheck -check-prefix=MIPS-ABI-32 %s +// MIPS-ABI-32: "-target-abi" "o32" +// +// RUN: %clang -target mips-linux-gnu -### -c %s \ +// RUN: -mabi=o32 2>&1 \ +// RUN: | FileCheck -check-prefix=MIPS-ABI-O32 %s +// MIPS-ABI-O32: "-target-abi" "o32" +// +// RUN: %clang -target mips-linux-gnu -### -c %s \ +// RUN: -mabi=n32 2>&1 \ +// RUN: | FileCheck -check-prefix=MIPS-ABI-N32 %s +// MIPS-ABI-N32: "-target-abi" "n32" +// +// RUN: %clang -target mips64-linux-gnu -### -c %s \ +// RUN: -mabi=64 2>&1 \ +// RUN: | FileCheck -check-prefix=MIPS-ABI-64 %s +// MIPS-ABI-64: "-target-abi" "n64" +// +// RUN: %clang -target mips64-linux-gnu -### -c %s \ +// RUN: -mabi=n64 2>&1 \ +// RUN: | FileCheck -check-prefix=MIPS-ABI-N64 %s +// MIPS-ABI-N64: "-target-abi" "n64" +// +// RUN: %clang -target mips64-linux-gnu -### -c %s \ +// RUN: -mabi=o64 2>&1 \ +// RUN: | FileCheck -check-prefix=MIPS-ABI-O64 %s +// MIPS-ABI-O64: "-target-abi" "o64" +// +// RUN: %clang -target mips-linux-gnu -### -c %s \ +// RUN: -mabi=eabi 2>&1 \ +// RUN: | FileCheck -check-prefix=MIPS-ABI-EABI %s +// MIPS-ABI-EABI: "-target-abi" "eabi" diff --git a/test/Driver/mips-as.c b/test/Driver/mips-as.c index 146b1930c61..216b65607e0 100644 --- a/test/Driver/mips-as.c +++ b/test/Driver/mips-as.c @@ -1,5 +1,3 @@ -// REQUIRES: mips-registered-target -// // Check passing options to the assembler for MIPS targets. // // RUN: %clang -target mips-linux-gnu -### \ @@ -73,3 +71,47 @@ // RUN: -no-integrated-as -c %s 2>&1 \ // RUN: | FileCheck -check-prefix=MIPS-ALIAS-64R2 %s // MIPS-ALIAS-64R2: as{{(.exe)?}}" "-march" "mips64r2" "-mabi" "64" "-EB" +// +// RUN: %clang -target mips-linux-gnu -mno-mips16 -mips16 -### \ +// RUN: -no-integrated-as -c %s 2>&1 \ +// RUN: | FileCheck -check-prefix=MIPS-16 %s +// MIPS-16: as{{(.exe)?}}" "-march" "mips32" "-mabi" "32" "-EB" "-mips16" +// +// RUN: %clang -target mips-linux-gnu -mips16 -mno-mips16 -### \ +// RUN: -no-integrated-as -c %s 2>&1 \ +// RUN: | FileCheck -check-prefix=MIPS-N16 %s +// MIPS-N16: as{{(.exe)?}}" +// MIPS-N16-NOT: "-mips16" +// +// RUN: %clang -target mips-linux-gnu -mno-micromips -mmicromips -### \ +// RUN: -no-integrated-as -c %s 2>&1 \ +// RUN: | FileCheck -check-prefix=MIPS-MICRO %s +// MIPS-MICRO: as{{(.exe)?}}" "-march" "mips32" "-mabi" "32" "-EB" "-mmicromips" +// +// RUN: %clang -target mips-linux-gnu -mmicromips -mno-micromips -### \ +// RUN: -no-integrated-as -c %s 2>&1 \ +// RUN: | FileCheck -check-prefix=MIPS-NMICRO %s +// MIPS-NMICRO: as{{(.exe)?}}" +// MIPS-NMICRO-NOT: "-mmicromips" +// +// RUN: %clang -target mips-linux-gnu -mno-dsp -mdsp -### \ +// RUN: -no-integrated-as -c %s 2>&1 \ +// RUN: | FileCheck -check-prefix=MIPS-DSP %s +// MIPS-DSP: as{{(.exe)?}}" "-march" "mips32" "-mabi" "32" "-EB" "-mdsp" +// +// RUN: %clang -target mips-linux-gnu -mdsp -mno-dsp -### \ +// RUN: -no-integrated-as -c %s 2>&1 \ +// RUN: | FileCheck -check-prefix=MIPS-NDSP %s +// MIPS-NDSP: as{{(.exe)?}}" +// MIPS-NDSP-NOT: "-mdsp" +// +// RUN: %clang -target mips-linux-gnu -mno-dspr2 -mdspr2 -### \ +// RUN: -no-integrated-as -c %s 2>&1 \ +// RUN: | FileCheck -check-prefix=MIPS-DSPR2 %s +// MIPS-DSPR2: as{{(.exe)?}}" "-march" "mips32" "-mabi" "32" "-EB" "-mdspr2" +// +// RUN: %clang -target mips-linux-gnu -mdspr2 -mno-dspr2 -### \ +// RUN: -no-integrated-as -c %s 2>&1 \ +// RUN: | FileCheck -check-prefix=MIPS-NDSPR2 %s +// MIPS-NDSPR2: as{{(.exe)?}}" +// MIPS-NDSPR2-NOT: "-mdspr2" diff --git a/test/Driver/mips-cs-header-search.cpp b/test/Driver/mips-cs-header-search.cpp new file mode 100644 index 00000000000..e59fadca585 --- /dev/null +++ b/test/Driver/mips-cs-header-search.cpp @@ -0,0 +1,257 @@ +// Check frontend invocations on Mentor Graphics MIPS toolchain. +// +// = Big-endian, hard float +// RUN: %clang -no-canonical-prefixes %s -### -c -o %t.o 2>&1 \ +// RUN: -target mips-linux-gnu \ +// RUN: -gcc-toolchain %S/Inputs/mips_cs_tree \ +// RUN: | FileCheck --check-prefix=CHECK-BE-HF-32 %s +// CHECK-BE-HF-32: "-internal-isystem" +// CHECK-BE-HF-32: "[[TC:[^"]+/lib/gcc/mips-linux-gnu/4.6.3]]/../../../../mips-linux-gnu/include/c++/4.6.3" +// CHECK-BE-HF-32: "-internal-isystem" +// CHECK-BE-HF-32: "[[TC]]/../../../../mips-linux-gnu/include/c++/4.6.3/mips-linux-gnu" +// CHECK-BE-HF-32: "-internal-isystem" +// CHECK-BE-HF-32: "[[TC]]/../../../../mips-linux-gnu/include/c++/4.6.3/backward" +// CHECK-BE-HF-32: "-internal-externc-isystem" +// CHECK-BE-HF-32: "[[TC]]/include" +// CHECK-BE-HF-32: "-internal-externc-isystem" +// CHECK-BE-HF-32: "[[TC]]/../../../../mips-linux-gnu/libc/usr/include" +// +// = Big-endian, hard float, mips16 +// RUN: %clang -no-canonical-prefixes %s -### -c -o %t.o 2>&1 \ +// RUN: -target mips-linux-gnu -mips16 \ +// RUN: -gcc-toolchain %S/Inputs/mips_cs_tree \ +// RUN: | FileCheck --check-prefix=CHECK-BE-HF-16 %s +// CHECK-BE-HF-16: "-internal-isystem" +// CHECK-BE-HF-16: "[[TC:[^"]+/lib/gcc/mips-linux-gnu/4.6.3]]/../../../../mips-linux-gnu/include/c++/4.6.3" +// CHECK-BE-HF-16: "-internal-isystem" +// CHECK-BE-HF-16: "[[TC]]/../../../../mips-linux-gnu/include/c++/4.6.3/mips-linux-gnu/mips16" +// CHECK-BE-HF-16: "-internal-isystem" +// CHECK-BE-HF-16: "[[TC]]/../../../../mips-linux-gnu/include/c++/4.6.3/backward" +// CHECK-BE-HF-16: "-internal-externc-isystem" +// CHECK-BE-HF-16: "[[TC]]/include" +// CHECK-BE-HF-16: "-internal-externc-isystem" +// CHECK-BE-HF-16: "[[TC]]/../../../../mips-linux-gnu/libc/usr/include" +// +// = Big-endian, hard float, micromips +// RUN: %clang -no-canonical-prefixes %s -### -c -o %t.o 2>&1 \ +// RUN: -target mips-linux-gnu -mmicromips \ +// RUN: -gcc-toolchain %S/Inputs/mips_cs_tree \ +// RUN: | FileCheck --check-prefix=CHECK-BE-HF-MICRO %s +// CHECK-BE-HF-MICRO: "-internal-isystem" +// CHECK-BE-HF-MICRO: "[[TC:[^"]+/lib/gcc/mips-linux-gnu/4.6.3]]/../../../../mips-linux-gnu/include/c++/4.6.3" +// CHECK-BE-HF-MICRO: "-internal-isystem" +// CHECK-BE-HF-MICRO: "[[TC]]/../../../../mips-linux-gnu/include/c++/4.6.3/mips-linux-gnu/micromips" +// CHECK-BE-HF-MICRO: "-internal-isystem" +// CHECK-BE-HF-MICRO: "[[TC]]/../../../../mips-linux-gnu/include/c++/4.6.3/backward" +// CHECK-BE-HF-MICRO: "-internal-externc-isystem" +// CHECK-BE-HF-MICRO: "[[TC]]/include" +// CHECK-BE-HF-MICRO: "-internal-externc-isystem" +// CHECK-BE-HF-MICRO: "[[TC]]/../../../../mips-linux-gnu/libc/usr/include" +// +// = Big-endian, soft float +// RUN: %clang -no-canonical-prefixes %s -### -c -o %t.o 2>&1 \ +// RUN: -target mips-linux-gnu -msoft-float \ +// RUN: -gcc-toolchain %S/Inputs/mips_cs_tree \ +// RUN: | FileCheck --check-prefix=CHECK-BE-SF-32 %s +// CHECK-BE-SF-32: "-internal-isystem" +// CHECK-BE-SF-32: "[[TC:[^"]+/lib/gcc/mips-linux-gnu/4.6.3]]/../../../../mips-linux-gnu/include/c++/4.6.3" +// CHECK-BE-SF-32: "-internal-isystem" +// CHECK-BE-SF-32: "[[TC]]/../../../../mips-linux-gnu/include/c++/4.6.3/mips-linux-gnu/soft-float" +// CHECK-BE-SF-32: "-internal-isystem" +// CHECK-BE-SF-32: "[[TC]]/../../../../mips-linux-gnu/include/c++/4.6.3/backward" +// CHECK-BE-SF-32: "-internal-externc-isystem" +// CHECK-BE-SF-32: "[[TC]]/include" +// CHECK-BE-SF-32: "-internal-externc-isystem" +// CHECK-BE-SF-32: "[[TC]]/../../../../mips-linux-gnu/libc/usr/include" +// +// = Big-endian, soft float, mips16 +// RUN: %clang -no-canonical-prefixes %s -### -c -o %t.o 2>&1 \ +// RUN: -target mips-linux-gnu -msoft-float -mips16 \ +// RUN: -gcc-toolchain %S/Inputs/mips_cs_tree \ +// RUN: | FileCheck --check-prefix=CHECK-BE-SF-16 %s +// CHECK-BE-SF-16: "-internal-isystem" +// CHECK-BE-SF-16: "[[TC:[^"]+/lib/gcc/mips-linux-gnu/4.6.3]]/../../../../mips-linux-gnu/include/c++/4.6.3" +// CHECK-BE-SF-16: "-internal-isystem" +// CHECK-BE-SF-16: "[[TC]]/../../../../mips-linux-gnu/include/c++/4.6.3/mips-linux-gnu/mips16/soft-float" +// CHECK-BE-SF-16: "-internal-isystem" +// CHECK-BE-SF-16: "[[TC]]/../../../../mips-linux-gnu/include/c++/4.6.3/backward" +// CHECK-BE-SF-16: "-internal-externc-isystem" +// CHECK-BE-SF-16: "[[TC]]/include" +// CHECK-BE-SF-16: "-internal-externc-isystem" +// CHECK-BE-SF-16: "[[TC]]/../../../../mips-linux-gnu/libc/usr/include" +// +// = Big-endian, soft float, micromips +// RUN: %clang -no-canonical-prefixes %s -### -c -o %t.o 2>&1 \ +// RUN: -target mips-linux-gnu -msoft-float -mmicromips \ +// RUN: -gcc-toolchain %S/Inputs/mips_cs_tree \ +// RUN: | FileCheck --check-prefix=CHECK-BE-SF-MICRO %s +// CHECK-BE-SF-MICRO: "-internal-isystem" +// CHECK-BE-SF-MICRO: "[[TC:[^"]+/lib/gcc/mips-linux-gnu/4.6.3]]/../../../../mips-linux-gnu/include/c++/4.6.3" +// CHECK-BE-SF-MICRO: "-internal-isystem" +// CHECK-BE-SF-MICRO: "[[TC]]/../../../../mips-linux-gnu/include/c++/4.6.3/mips-linux-gnu/micromips/soft-float" +// CHECK-BE-SF-MICRO: "-internal-isystem" +// CHECK-BE-SF-MICRO: "[[TC]]/../../../../mips-linux-gnu/include/c++/4.6.3/backward" +// CHECK-BE-SF-MICRO: "-internal-externc-isystem" +// CHECK-BE-SF-MICRO: "[[TC]]/include" +// CHECK-BE-SF-MICRO: "-internal-externc-isystem" +// CHECK-BE-SF-MICRO: "[[TC]]/../../../../mips-linux-gnu/libc/usr/include" +// +// = Big-endian, hard float, 64-bit +// RUN: %clang -no-canonical-prefixes %s -### -c -o %t.o 2>&1 \ +// RUN: -target mips64-linux-gnu \ +// RUN: -gcc-toolchain %S/Inputs/mips_cs_tree \ +// RUN: | FileCheck --check-prefix=CHECK-BE-HF-64 %s +// CHECK-BE-HF-64: "-internal-isystem" +// CHECK-BE-HF-64: "[[TC:[^"]+/lib/gcc/mips-linux-gnu/4.6.3]]/../../../../mips-linux-gnu/include/c++/4.6.3" +// CHECK-BE-HF-64: "-internal-isystem" +// CHECK-BE-HF-64: "[[TC]]/../../../../mips-linux-gnu/include/c++/4.6.3/mips-linux-gnu/64" +// CHECK-BE-HF-64: "-internal-isystem" +// CHECK-BE-HF-64: "[[TC]]/../../../../mips-linux-gnu/include/c++/4.6.3/backward" +// CHECK-BE-HF-64: "-internal-externc-isystem" +// CHECK-BE-HF-64: "[[TC]]/include" +// CHECK-BE-HF-64: "-internal-externc-isystem" +// CHECK-BE-HF-64: "[[TC]]/../../../../mips-linux-gnu/libc/usr/include" +// +// = Big-endian, soft float, 64-bit +// RUN: %clang -no-canonical-prefixes %s -### -c -o %t.o 2>&1 \ +// RUN: -target mips64-linux-gnu -msoft-float \ +// RUN: -gcc-toolchain %S/Inputs/mips_cs_tree \ +// RUN: | FileCheck --check-prefix=CHECK-BE-SF-64 %s +// CHECK-BE-SF-64: "-internal-isystem" +// CHECK-BE-SF-64: "[[TC:[^"]+/lib/gcc/mips-linux-gnu/4.6.3]]/../../../../mips-linux-gnu/include/c++/4.6.3" +// CHECK-BE-SF-64: "-internal-isystem" +// CHECK-BE-SF-64: "[[TC]]/../../../../mips-linux-gnu/include/c++/4.6.3/mips-linux-gnu/soft-float/64" +// CHECK-BE-SF-64: "-internal-isystem" +// CHECK-BE-SF-64: "[[TC]]/../../../../mips-linux-gnu/include/c++/4.6.3/backward" +// CHECK-BE-SF-64: "-internal-externc-isystem" +// CHECK-BE-SF-64: "[[TC]]/include" +// CHECK-BE-SF-64: "-internal-externc-isystem" +// CHECK-BE-SF-64: "[[TC]]/../../../../mips-linux-gnu/libc/usr/include" +// +// = Little-endian, hard float +// RUN: %clang -no-canonical-prefixes %s -### -c -o %t.o 2>&1 \ +// RUN: -target mipsel-linux-gnu -mhard-float \ +// RUN: -gcc-toolchain %S/Inputs/mips_cs_tree \ +// RUN: | FileCheck --check-prefix=CHECK-EL-HF-32 %s +// CHECK-EL-HF-32: "-internal-isystem" +// CHECK-EL-HF-32: "[[TC:[^"]+/lib/gcc/mips-linux-gnu/4.6.3]]/../../../../mips-linux-gnu/include/c++/4.6.3" +// CHECK-EL-HF-32: "-internal-isystem" +// CHECK-EL-HF-32: "[[TC]]/../../../../mips-linux-gnu/include/c++/4.6.3/mips-linux-gnu/el" +// CHECK-EL-HF-32: "-internal-isystem" +// CHECK-EL-HF-32: "[[TC]]/../../../../mips-linux-gnu/include/c++/4.6.3/backward" +// CHECK-EL-HF-32: "-internal-externc-isystem" +// CHECK-EL-HF-32: "[[TC]]/include" +// CHECK-EL-HF-32: "-internal-externc-isystem" +// CHECK-EL-HF-32: "[[TC]]/../../../../mips-linux-gnu/libc/usr/include" +// +// = Little-endian, hard float, mips16 +// RUN: %clang -no-canonical-prefixes %s -### -c -o %t.o 2>&1 \ +// RUN: -target mipsel-linux-gnu -mips16 \ +// RUN: -gcc-toolchain %S/Inputs/mips_cs_tree \ +// RUN: | FileCheck --check-prefix=CHECK-EL-HF-16 %s +// CHECK-EL-HF-16: "-internal-isystem" +// CHECK-EL-HF-16: "[[TC:[^"]+/lib/gcc/mips-linux-gnu/4.6.3]]/../../../../mips-linux-gnu/include/c++/4.6.3" +// CHECK-EL-HF-16: "-internal-isystem" +// CHECK-EL-HF-16: "[[TC]]/../../../../mips-linux-gnu/include/c++/4.6.3/mips-linux-gnu/mips16/el" +// CHECK-EL-HF-16: "-internal-isystem" +// CHECK-EL-HF-16: "[[TC]]/../../../../mips-linux-gnu/include/c++/4.6.3/backward" +// CHECK-EL-HF-16: "-internal-externc-isystem" +// CHECK-EL-HF-16: "[[TC]]/include" +// CHECK-EL-HF-16: "-internal-externc-isystem" +// CHECK-EL-HF-16: "[[TC]]/../../../../mips-linux-gnu/libc/usr/include" +// +// = Little-endian, hard float, micromips +// RUN: %clang -no-canonical-prefixes %s -### -c -o %t.o 2>&1 \ +// RUN: -target mipsel-linux-gnu -mmicromips \ +// RUN: -gcc-toolchain %S/Inputs/mips_cs_tree \ +// RUN: | FileCheck --check-prefix=CHECK-EL-HF-MICRO %s +// CHECK-EL-HF-MICRO: "-internal-isystem" +// CHECK-EL-HF-MICRO: "[[TC:[^"]+/lib/gcc/mips-linux-gnu/4.6.3]]/../../../../mips-linux-gnu/include/c++/4.6.3" +// CHECK-EL-HF-MICRO: "-internal-isystem" +// CHECK-EL-HF-MICRO: "[[TC]]/../../../../mips-linux-gnu/include/c++/4.6.3/mips-linux-gnu/micromips/el" +// CHECK-EL-HF-MICRO: "-internal-isystem" +// CHECK-EL-HF-MICRO: "[[TC]]/../../../../mips-linux-gnu/include/c++/4.6.3/backward" +// CHECK-EL-HF-MICRO: "-internal-externc-isystem" +// CHECK-EL-HF-MICRO: "[[TC]]/include" +// CHECK-EL-HF-MICRO: "-internal-externc-isystem" +// CHECK-EL-HF-MICRO: "[[TC]]/../../../../mips-linux-gnu/libc/usr/include" +// +// = Little-endian, soft float +// RUN: %clang -no-canonical-prefixes %s -### -c -o %t.o 2>&1 \ +// RUN: -target mipsel-linux-gnu -mfloat-abi=soft \ +// RUN: -gcc-toolchain %S/Inputs/mips_cs_tree \ +// RUN: | FileCheck --check-prefix=CHECK-EL-SF-32 %s +// CHECK-EL-SF-32: "-internal-isystem" +// CHECK-EL-SF-32: "[[TC:[^"]+/lib/gcc/mips-linux-gnu/4.6.3]]/../../../../mips-linux-gnu/include/c++/4.6.3" +// CHECK-EL-SF-32: "-internal-isystem" +// CHECK-EL-SF-32: "[[TC]]/../../../../mips-linux-gnu/include/c++/4.6.3/mips-linux-gnu/soft-float/el" +// CHECK-EL-SF-32: "-internal-isystem" +// CHECK-EL-SF-32: "[[TC]]/../../../../mips-linux-gnu/include/c++/4.6.3/backward" +// CHECK-EL-SF-32: "-internal-externc-isystem" +// CHECK-EL-SF-32: "[[TC]]/include" +// CHECK-EL-SF-32: "-internal-externc-isystem" +// CHECK-EL-SF-32: "[[TC]]/../../../../mips-linux-gnu/libc/usr/include" +// +// = Little-endian, soft float, mips16 +// RUN: %clang -no-canonical-prefixes %s -### -c -o %t.o 2>&1 \ +// RUN: -target mipsel-linux-gnu -mips16 -msoft-float \ +// RUN: -gcc-toolchain %S/Inputs/mips_cs_tree \ +// RUN: | FileCheck --check-prefix=CHECK-EL-SF-16 %s +// CHECK-EL-SF-16: "-internal-isystem" +// CHECK-EL-SF-16: "[[TC:[^"]+/lib/gcc/mips-linux-gnu/4.6.3]]/../../../../mips-linux-gnu/include/c++/4.6.3" +// CHECK-EL-SF-16: "-internal-isystem" +// CHECK-EL-SF-16: "[[TC]]/../../../../mips-linux-gnu/include/c++/4.6.3/mips-linux-gnu/mips16/soft-float/el" +// CHECK-EL-SF-16: "-internal-isystem" +// CHECK-EL-SF-16: "[[TC]]/../../../../mips-linux-gnu/include/c++/4.6.3/backward" +// CHECK-EL-SF-16: "-internal-externc-isystem" +// CHECK-EL-SF-16: "[[TC]]/include" +// CHECK-EL-SF-16: "-internal-externc-isystem" +// CHECK-EL-SF-16: "[[TC]]/../../../../mips-linux-gnu/libc/usr/include" +// +// = Little-endian, soft float, micromips +// RUN: %clang -no-canonical-prefixes %s -### -c -o %t.o 2>&1 \ +// RUN: -target mipsel-linux-gnu -mmicromips -msoft-float \ +// RUN: -gcc-toolchain %S/Inputs/mips_cs_tree \ +// RUN: | FileCheck --check-prefix=CHECK-EL-SF-MICRO %s +// CHECK-EL-SF-MICRO: "-internal-isystem" +// CHECK-EL-SF-MICRO: "[[TC:[^"]+/lib/gcc/mips-linux-gnu/4.6.3]]/../../../../mips-linux-gnu/include/c++/4.6.3" +// CHECK-EL-SF-MICRO: "-internal-isystem" +// CHECK-EL-SF-MICRO: "[[TC]]/../../../../mips-linux-gnu/include/c++/4.6.3/mips-linux-gnu/micromips/soft-float/el" +// CHECK-EL-SF-MICRO: "-internal-isystem" +// CHECK-EL-SF-MICRO: "[[TC]]/../../../../mips-linux-gnu/include/c++/4.6.3/backward" +// CHECK-EL-SF-MICRO: "-internal-externc-isystem" +// CHECK-EL-SF-MICRO: "[[TC]]/include" +// CHECK-EL-SF-MICRO: "-internal-externc-isystem" +// CHECK-EL-SF-MICRO: "[[TC]]/../../../../mips-linux-gnu/libc/usr/include" +// +// = Little-endian, hard float, 64-bit +// RUN: %clang -no-canonical-prefixes %s -### -c -o %t.o 2>&1 \ +// RUN: -target mips64el-linux-gnu \ +// RUN: -gcc-toolchain %S/Inputs/mips_cs_tree \ +// RUN: | FileCheck --check-prefix=CHECK-EL-HF-64 %s +// CHECK-EL-HF-64: "-internal-isystem" +// CHECK-EL-HF-64: "[[TC:[^"]+/lib/gcc/mips-linux-gnu/4.6.3]]/../../../../mips-linux-gnu/include/c++/4.6.3" +// CHECK-EL-HF-64: "-internal-isystem" +// CHECK-EL-HF-64: "[[TC]]/../../../../mips-linux-gnu/include/c++/4.6.3/mips-linux-gnu/el/64" +// CHECK-EL-HF-64: "-internal-isystem" +// CHECK-EL-HF-64: "[[TC]]/../../../../mips-linux-gnu/include/c++/4.6.3/backward" +// CHECK-EL-HF-64: "-internal-externc-isystem" +// CHECK-EL-HF-64: "[[TC]]/include" +// CHECK-EL-HF-64: "-internal-externc-isystem" +// CHECK-EL-HF-64: "[[TC]]/../../../../mips-linux-gnu/libc/usr/include" +// +// = Little-endian, soft float, 64-bit +// RUN: %clang -no-canonical-prefixes %s -### -c -o %t.o 2>&1 \ +// RUN: -target mips64el-linux-gnu -msoft-float \ +// RUN: -gcc-toolchain %S/Inputs/mips_cs_tree \ +// RUN: | FileCheck --check-prefix=CHECK-EL-SF-64 %s +// CHECK-EL-SF-64: "-internal-isystem" +// CHECK-EL-SF-64: "[[TC:[^"]+/lib/gcc/mips-linux-gnu/4.6.3]]/../../../../mips-linux-gnu/include/c++/4.6.3" +// CHECK-EL-SF-64: "-internal-isystem" +// CHECK-EL-SF-64: "[[TC]]/../../../../mips-linux-gnu/include/c++/4.6.3/mips-linux-gnu/soft-float/el/64" +// CHECK-EL-SF-64: "-internal-isystem" +// CHECK-EL-SF-64: "[[TC]]/../../../../mips-linux-gnu/include/c++/4.6.3/backward" +// CHECK-EL-SF-64: "-internal-externc-isystem" +// CHECK-EL-SF-64: "[[TC]]/include" +// CHECK-EL-SF-64: "-internal-externc-isystem" +// CHECK-EL-SF-64: "[[TC]]/../../../../mips-linux-gnu/libc/usr/include" diff --git a/test/Driver/mips-cs-ld.c b/test/Driver/mips-cs-ld.c new file mode 100644 index 00000000000..ac3adfd910b --- /dev/null +++ b/test/Driver/mips-cs-ld.c @@ -0,0 +1,288 @@ +// Check ld invocations on Mentor Graphics MIPS toolchain. +// +// = Big-endian, hard float +// RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \ +// RUN: -target mips-linux-gnu \ +// RUN: -gcc-toolchain %S/Inputs/mips_cs_tree \ +// RUN: | FileCheck --check-prefix=CHECK-BE-HF-32 %s +// CHECK-BE-HF-32: "{{.*}}ld{{(.exe)?}}" +// CHECK-BE-HF-32: "--sysroot=[[TC:[^"]+]]/../../../../mips-linux-gnu/libc" +// CHECK-BE-HF-32: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/../../../../mips-linux-gnu/libc/usr/lib/../lib/crt1.o" +// CHECK-BE-HF-32: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/../../../../mips-linux-gnu/libc/usr/lib/../lib/crti.o" +// CHECK-BE-HF-32: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/crtbegin.o" +// CHECK-BE-HF-32: "-L[[TC]]" +// CHECK-BE-HF-32: "-L[[TC]]/../../../../mips-linux-gnu/lib/../lib" +// CHECK-BE-HF-32: "-L[[TC]]/../../../../mips-linux-gnu/libc/lib" +// CHECK-BE-HF-32: "-L[[TC]]/../../../../mips-linux-gnu/libc/usr/lib" +// CHECK-BE-HF-32: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/crtend.o" +// CHECK-BE-HF-32: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/../../../../mips-linux-gnu/libc/usr/lib/../lib/crtn.o" +// +// = Big-endian, hard float, mips16 +// RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \ +// RUN: -target mips-linux-gnu -mips16 \ +// RUN: -gcc-toolchain %S/Inputs/mips_cs_tree \ +// RUN: | FileCheck --check-prefix=CHECK-BE-HF-16 %s +// CHECK-BE-HF-16: "{{.*}}ld{{(.exe)?}}" +// CHECK-BE-HF-16: "--sysroot=[[TC:[^"]+]]/../../../../mips-linux-gnu/libc/mips16" +// CHECK-BE-HF-16: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/../../../../mips-linux-gnu/libc/mips16/usr/lib/../lib/crt1.o" +// CHECK-BE-HF-16: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/../../../../mips-linux-gnu/libc/mips16/usr/lib/../lib/crti.o" +// CHECK-BE-HF-16: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/mips16/crtbegin.o" +// CHECK-BE-HF-16: "-L[[TC]]/mips16" +// CHECK-BE-HF-16: "-L[[TC]]/../../../../mips-linux-gnu/lib/../lib/mips16" +// CHECK-BE-HF-16: "-L[[TC]]" +// CHECK-BE-HF-16: "-L[[TC]]/../../../../mips-linux-gnu/libc/mips16/lib" +// CHECK-BE-HF-16: "-L[[TC]]/../../../../mips-linux-gnu/libc/mips16/usr/lib" +// CHECK-BE-HF-16: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/mips16/crtend.o" +// CHECK-BE-HF-16: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/../../../../mips-linux-gnu/libc/mips16/usr/lib/../lib/crtn.o" +// +// = Big-endian, hard float, mmicromips +// RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \ +// RUN: -target mips-linux-gnu -mmicromips \ +// RUN: -gcc-toolchain %S/Inputs/mips_cs_tree \ +// RUN: | FileCheck --check-prefix=CHECK-BE-HF-MICRO %s +// CHECK-BE-HF-MICRO: "{{.*}}ld{{(.exe)?}}" +// CHECK-BE-HF-MICRO: "--sysroot=[[TC:[^"]+]]/../../../../mips-linux-gnu/libc/micromips" +// CHECK-BE-HF-MICRO: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/../../../../mips-linux-gnu/libc/micromips/usr/lib/../lib/crt1.o" +// CHECK-BE-HF-MICRO: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/../../../../mips-linux-gnu/libc/micromips/usr/lib/../lib/crti.o" +// CHECK-BE-HF-MICRO: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/micromips/crtbegin.o" +// CHECK-BE-HF-MICRO: "-L[[TC]]/micromips" +// CHECK-BE-HF-MICRO: "-L[[TC]]/../../../../mips-linux-gnu/lib/../lib/micromips" +// CHECK-BE-HF-MICRO: "-L[[TC]]" +// CHECK-BE-HF-MICRO: "-L[[TC]]/../../../../mips-linux-gnu/libc/micromips/lib" +// CHECK-BE-HF-MICRO: "-L[[TC]]/../../../../mips-linux-gnu/libc/micromips/usr/lib" +// CHECK-BE-HF-MICRO: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/micromips/crtend.o" +// CHECK-BE-HF-MICRO: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/../../../../mips-linux-gnu/libc/micromips/usr/lib/../lib/crtn.o" +// +// = Big-endian, soft float +// RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \ +// RUN: -target mips-linux-gnu -msoft-float \ +// RUN: -gcc-toolchain %S/Inputs/mips_cs_tree \ +// RUN: | FileCheck --check-prefix=CHECK-BE-SF-32 %s +// CHECK-BE-SF-32: "{{.*}}ld{{(.exe)?}}" +// CHECK-BE-SF-32: "--sysroot=[[TC:[^"]+]]/../../../../mips-linux-gnu/libc/soft-float" +// CHECK-BE-SF-32: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/../../../../mips-linux-gnu/libc/soft-float/usr/lib/../lib/crt1.o" +// CHECK-BE-SF-32: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/../../../../mips-linux-gnu/libc/soft-float/usr/lib/../lib/crti.o" +// CHECK-BE-SF-32: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/soft-float/crtbegin.o" +// CHECK-BE-SF-32: "-L[[TC]]/soft-float" +// CHECK-BE-SF-32: "-L[[TC]]/../../../../mips-linux-gnu/lib/../lib/soft-float" +// CHECK-BE-SF-32: "-L[[TC]]" +// CHECK-BE-SF-32: "-L[[TC]]/../../../../mips-linux-gnu/libc/soft-float/lib" +// CHECK-BE-SF-32: "-L[[TC]]/../../../../mips-linux-gnu/libc/soft-float/usr/lib" +// CHECK-BE-SF-32: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/soft-float/crtend.o" +// CHECK-BE-SF-32: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/../../../../mips-linux-gnu/libc/soft-float/usr/lib/../lib/crtn.o" +// +// = Big-endian, soft float, mips16 +// RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \ +// RUN: -target mips-linux-gnu -msoft-float -mips16 \ +// RUN: -gcc-toolchain %S/Inputs/mips_cs_tree \ +// RUN: | FileCheck --check-prefix=CHECK-BE-SF-16 %s +// CHECK-BE-SF-16: "{{.*}}ld{{(.exe)?}}" +// CHECK-BE-SF-16: "--sysroot=[[TC:[^"]+]]/../../../../mips-linux-gnu/libc/mips16/soft-float" +// CHECK-BE-SF-16: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/../../../../mips-linux-gnu/libc/mips16/soft-float/usr/lib/../lib/crt1.o" +// CHECK-BE-SF-16: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/../../../../mips-linux-gnu/libc/mips16/soft-float/usr/lib/../lib/crti.o" +// CHECK-BE-SF-16: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/mips16/soft-float/crtbegin.o" +// CHECK-BE-SF-16: "-L[[TC]]/mips16/soft-float" +// CHECK-BE-SF-16: "-L[[TC]]/../../../../mips-linux-gnu/lib/../lib/mips16/soft-float" +// CHECK-BE-SF-16: "-L[[TC]]" +// CHECK-BE-SF-16: "-L[[TC]]/../../../../mips-linux-gnu/libc/mips16/soft-float/lib" +// CHECK-BE-SF-16: "-L[[TC]]/../../../../mips-linux-gnu/libc/mips16/soft-float/usr/lib" +// CHECK-BE-SF-16: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/mips16/soft-float/crtend.o" +// CHECK-BE-SF-16: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/../../../../mips-linux-gnu/libc/mips16/soft-float/usr/lib/../lib/crtn.o" +// +// = Big-endian, soft float, micromips +// RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \ +// RUN: -target mips-linux-gnu -msoft-float -mmicromips \ +// RUN: -gcc-toolchain %S/Inputs/mips_cs_tree \ +// RUN: | FileCheck --check-prefix=CHECK-BE-SF-MICRO %s +// CHECK-BE-SF-MICRO: "{{.*}}ld{{(.exe)?}}" +// CHECK-BE-SF-MICRO: "--sysroot=[[TC:[^"]+]]/../../../../mips-linux-gnu/libc/micromips/soft-float" +// CHECK-BE-SF-MICRO: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/../../../../mips-linux-gnu/libc/micromips/soft-float/usr/lib/../lib/crt1.o" +// CHECK-BE-SF-MICRO: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/../../../../mips-linux-gnu/libc/micromips/soft-float/usr/lib/../lib/crti.o" +// CHECK-BE-SF-MICRO: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/micromips/soft-float/crtbegin.o" +// CHECK-BE-SF-MICRO: "-L[[TC]]/micromips/soft-float" +// CHECK-BE-SF-MICRO: "-L[[TC]]/../../../../mips-linux-gnu/lib/../lib/micromips/soft-float" +// CHECK-BE-SF-MICRO: "-L[[TC]]" +// CHECK-BE-SF-MICRO: "-L[[TC]]/../../../../mips-linux-gnu/libc/micromips/soft-float/lib" +// CHECK-BE-SF-MICRO: "-L[[TC]]/../../../../mips-linux-gnu/libc/micromips/soft-float/usr/lib" +// CHECK-BE-SF-MICRO: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/micromips/soft-float/crtend.o" +// CHECK-BE-SF-MICRO: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/../../../../mips-linux-gnu/libc/micromips/soft-float/usr/lib/../lib/crtn.o" +// +// = Big-endian, hard float, 64-bit +// RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \ +// RUN: -target mips64-linux-gnu \ +// RUN: -gcc-toolchain %S/Inputs/mips_cs_tree \ +// RUN: | FileCheck --check-prefix=CHECK-BE-HF-64 %s +// CHECK-BE-HF-64: "{{.*}}ld{{(.exe)?}}" +// CHECK-BE-HF-64: "--sysroot=[[TC:[^"]+]]/../../../../mips-linux-gnu/libc" +// CHECK-BE-HF-64: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/../../../../mips-linux-gnu/libc/usr/lib/../lib64/crt1.o" +// CHECK-BE-HF-64: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/../../../../mips-linux-gnu/libc/usr/lib/../lib64/crti.o" +// CHECK-BE-HF-64: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/64/crtbegin.o" +// CHECK-BE-HF-64: "-L[[TC]]/64" +// CHECK-BE-HF-64: "-L[[TC]]/../../../../mips-linux-gnu/lib/../lib64" +// CHECK-BE-HF-64: "-L[[TC]]/../../../../mips-linux-gnu/libc/lib/../lib64" +// CHECK-BE-HF-64: "-L[[TC]]/../../../../mips-linux-gnu/libc/usr/lib/../lib64" +// CHECK-BE-HF-64: "-L[[TC]]" +// CHECK-BE-HF-64: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/64/crtend.o" +// CHECK-BE-HF-64: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/../../../../mips-linux-gnu/libc/usr/lib/../lib64/crtn.o" +// +// = Big-endian, soft float, 64-bit +// RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \ +// RUN: -target mips64-linux-gnu -msoft-float \ +// RUN: -gcc-toolchain %S/Inputs/mips_cs_tree \ +// RUN: | FileCheck --check-prefix=CHECK-BE-SF-64 %s +// CHECK-BE-SF-64: "{{.*}}ld{{(.exe)?}}" +// CHECK-BE-SF-64: "--sysroot=[[TC:[^"]+]]/../../../../mips-linux-gnu/libc/soft-float" +// CHECK-BE-SF-64: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/../../../../mips-linux-gnu/libc/soft-float/usr/lib/../lib64/crt1.o" +// CHECK-BE-SF-64: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/../../../../mips-linux-gnu/libc/soft-float/usr/lib/../lib64/crti.o" +// CHECK-BE-SF-64: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/soft-float/64/crtbegin.o" +// CHECK-BE-SF-64: "-L[[TC]]/soft-float/64" +// CHECK-BE-SF-64: "-L[[TC]]/../../../../mips-linux-gnu/lib/../lib64/soft-float" +// CHECK-BE-SF-64: "-L[[TC]]/../../../../mips-linux-gnu/libc/soft-float/lib/../lib64" +// CHECK-BE-SF-64: "-L[[TC]]/../../../../mips-linux-gnu/libc/soft-float/usr/lib/../lib64" +// CHECK-BE-SF-64: "-L[[TC]]" +// CHECK-BE-SF-64: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/soft-float/64/crtend.o" +// CHECK-BE-SF-64: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/../../../../mips-linux-gnu/libc/soft-float/usr/lib/../lib64/crtn.o" +// +// = Little-endian, hard float +// RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \ +// RUN: -target mipsel-linux-gnu -mhard-float \ +// RUN: -gcc-toolchain %S/Inputs/mips_cs_tree \ +// RUN: | FileCheck --check-prefix=CHECK-EL-HF-32 %s +// CHECK-EL-HF-32: "{{.*}}ld{{(.exe)?}}" +// CHECK-EL-HF-32: "--sysroot=[[TC:[^"]+]]/../../../../mips-linux-gnu/libc/el" +// CHECK-EL-HF-32: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/../../../../mips-linux-gnu/libc/el/usr/lib/../lib/crt1.o" +// CHECK-EL-HF-32: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/../../../../mips-linux-gnu/libc/el/usr/lib/../lib/crti.o" +// CHECK-EL-HF-32: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/el/crtbegin.o" +// CHECK-EL-HF-32: "-L[[TC]]/el" +// CHECK-EL-HF-32: "-L[[TC]]/../../../../mips-linux-gnu/lib/../lib/el" +// CHECK-EL-HF-32: "-L[[TC]]" +// CHECK-EL-HF-32: "-L[[TC]]/../../../../mips-linux-gnu/libc/el/lib" +// CHECK-EL-HF-32: "-L[[TC]]/../../../../mips-linux-gnu/libc/el/usr/lib" +// CHECK-EL-HF-32: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/el/crtend.o" +// CHECK-EL-HF-32: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/../../../../mips-linux-gnu/libc/el/usr/lib/../lib/crtn.o" +// +// = Little-endian, hard float, mips16 +// RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \ +// RUN: -target mipsel-linux-gnu -mips16 \ +// RUN: -gcc-toolchain %S/Inputs/mips_cs_tree \ +// RUN: | FileCheck --check-prefix=CHECK-EL-HF-16 %s +// CHECK-EL-HF-16: "{{.*}}ld{{(.exe)?}}" +// CHECK-EL-HF-16: "--sysroot=[[TC:[^"]+]]/../../../../mips-linux-gnu/libc/mips16/el" +// CHECK-EL-HF-16: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/../../../../mips-linux-gnu/libc/mips16/el/usr/lib/../lib/crt1.o" +// CHECK-EL-HF-16: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/../../../../mips-linux-gnu/libc/mips16/el/usr/lib/../lib/crti.o" +// CHECK-EL-HF-16: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/mips16/el/crtbegin.o" +// CHECK-EL-HF-16: "-L[[TC]]/mips16/el" +// CHECK-EL-HF-16: "-L[[TC]]/../../../../mips-linux-gnu/lib/../lib/mips16/el" +// CHECK-EL-HF-16: "-L[[TC]]" +// CHECK-EL-HF-16: "-L[[TC]]/../../../../mips-linux-gnu/libc/mips16/el/lib" +// CHECK-EL-HF-16: "-L[[TC]]/../../../../mips-linux-gnu/libc/mips16/el/usr/lib" +// CHECK-EL-HF-16: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/mips16/el/crtend.o" +// CHECK-EL-HF-16: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/../../../../mips-linux-gnu/libc/mips16/el/usr/lib/../lib/crtn.o" +// +// = Little-endian, hard float, micromips +// RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \ +// RUN: -target mipsel-linux-gnu -mmicromips \ +// RUN: -gcc-toolchain %S/Inputs/mips_cs_tree \ +// RUN: | FileCheck --check-prefix=CHECK-EL-HF-MICRO %s +// CHECK-EL-HF-MICRO: "{{.*}}ld{{(.exe)?}}" +// CHECK-EL-HF-MICRO: "--sysroot=[[TC:[^"]+]]/../../../../mips-linux-gnu/libc/micromips/el" +// CHECK-EL-HF-MICRO: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/../../../../mips-linux-gnu/libc/micromips/el/usr/lib/../lib/crt1.o" +// CHECK-EL-HF-MICRO: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/../../../../mips-linux-gnu/libc/micromips/el/usr/lib/../lib/crti.o" +// CHECK-EL-HF-MICRO: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/micromips/el/crtbegin.o" +// CHECK-EL-HF-MICRO: "-L[[TC]]/micromips/el" +// CHECK-EL-HF-MICRO: "-L[[TC]]/../../../../mips-linux-gnu/lib/../lib/micromips/el" +// CHECK-EL-HF-MICRO: "-L[[TC]]" +// CHECK-EL-HF-MICRO: "-L[[TC]]/../../../../mips-linux-gnu/libc/micromips/el/lib" +// CHECK-EL-HF-MICRO: "-L[[TC]]/../../../../mips-linux-gnu/libc/micromips/el/usr/lib" +// CHECK-EL-HF-MICRO: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/micromips/el/crtend.o" +// CHECK-EL-HF-MICRO: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/../../../../mips-linux-gnu/libc/micromips/el/usr/lib/../lib/crtn.o" +// +// = Little-endian, soft float +// RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \ +// RUN: -target mipsel-linux-gnu -mfloat-abi=soft \ +// RUN: -gcc-toolchain %S/Inputs/mips_cs_tree \ +// RUN: | FileCheck --check-prefix=CHECK-EL-SF-32 %s +// CHECK-EL-SF-32: "{{.*}}ld{{(.exe)?}}" +// CHECK-EL-SF-32: "--sysroot=[[TC:[^"]+]]/../../../../mips-linux-gnu/libc/soft-float/el" +// CHECK-EL-SF-32: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/../../../../mips-linux-gnu/libc/soft-float/el/usr/lib/../lib/crt1.o" +// CHECK-EL-SF-32: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/../../../../mips-linux-gnu/libc/soft-float/el/usr/lib/../lib/crti.o" +// CHECK-EL-SF-32: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/soft-float/el/crtbegin.o" +// CHECK-EL-SF-32: "-L[[TC]]/soft-float/el" +// CHECK-EL-SF-32: "-L[[TC]]/../../../../mips-linux-gnu/lib/../lib/soft-float/el" +// CHECK-EL-SF-32: "-L[[TC]]" +// CHECK-EL-SF-32: "-L[[TC]]/../../../../mips-linux-gnu/libc/soft-float/el/lib" +// CHECK-EL-SF-32: "-L[[TC]]/../../../../mips-linux-gnu/libc/soft-float/el/usr/lib" +// CHECK-EL-SF-32: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/soft-float/el/crtend.o" +// CHECK-EL-SF-32: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/../../../../mips-linux-gnu/libc/soft-float/el/usr/lib/../lib/crtn.o" +// +// = Little-endian, soft float, mips16 +// RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \ +// RUN: -target mipsel-linux-gnu -mips16 -msoft-float \ +// RUN: -gcc-toolchain %S/Inputs/mips_cs_tree \ +// RUN: | FileCheck --check-prefix=CHECK-EL-SF-16 %s +// CHECK-EL-SF-16: "{{.*}}ld{{(.exe)?}}" +// CHECK-EL-SF-16: "--sysroot=[[TC:[^"]+]]/../../../../mips-linux-gnu/libc/mips16/soft-float/el" +// CHECK-EL-SF-16: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/../../../../mips-linux-gnu/libc/mips16/soft-float/el/usr/lib/../lib/crt1.o" +// CHECK-EL-SF-16: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/../../../../mips-linux-gnu/libc/mips16/soft-float/el/usr/lib/../lib/crti.o" +// CHECK-EL-SF-16: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/mips16/soft-float/el/crtbegin.o" +// CHECK-EL-SF-16: "-L[[TC]]/mips16/soft-float/el" +// CHECK-EL-SF-16: "-L[[TC]]/../../../../mips-linux-gnu/lib/../lib/mips16/soft-float/el" +// CHECK-EL-SF-16: "-L[[TC]]" +// CHECK-EL-SF-16: "-L[[TC]]/../../../../mips-linux-gnu/libc/mips16/soft-float/el/lib" +// CHECK-EL-SF-16: "-L[[TC]]/../../../../mips-linux-gnu/libc/mips16/soft-float/el/usr/lib" +// CHECK-EL-SF-16: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/mips16/soft-float/el/crtend.o" +// CHECK-EL-SF-16: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/../../../../mips-linux-gnu/libc/mips16/soft-float/el/usr/lib/../lib/crtn.o" +// +// = Little-endian, soft float, micromips +// RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \ +// RUN: -target mipsel-linux-gnu -mmicromips -msoft-float \ +// RUN: -gcc-toolchain %S/Inputs/mips_cs_tree \ +// RUN: | FileCheck --check-prefix=CHECK-EL-SF-MICRO %s +// CHECK-EL-SF-MICRO: "{{.*}}ld{{(.exe)?}}" +// CHECK-EL-SF-MICRO: "--sysroot=[[TC:[^"]+]]/../../../../mips-linux-gnu/libc/micromips/soft-float/el" +// CHECK-EL-SF-MICRO: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/../../../../mips-linux-gnu/libc/micromips/soft-float/el/usr/lib/../lib/crt1.o" +// CHECK-EL-SF-MICRO: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/../../../../mips-linux-gnu/libc/micromips/soft-float/el/usr/lib/../lib/crti.o" +// CHECK-EL-SF-MICRO: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/micromips/soft-float/el/crtbegin.o" +// CHECK-EL-SF-MICRO: "-L[[TC]]/micromips/soft-float/el" +// CHECK-EL-SF-MICRO: "-L[[TC]]/../../../../mips-linux-gnu/lib/../lib/micromips/soft-float/el" +// CHECK-EL-SF-MICRO: "-L[[TC]]" +// CHECK-EL-SF-MICRO: "-L[[TC]]/../../../../mips-linux-gnu/libc/micromips/soft-float/el/lib" +// CHECK-EL-SF-MICRO: "-L[[TC]]/../../../../mips-linux-gnu/libc/micromips/soft-float/el/usr/lib" +// CHECK-EL-SF-MICRO: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/micromips/soft-float/el/crtend.o" +// CHECK-EL-SF-MICRO: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/../../../../mips-linux-gnu/libc/micromips/soft-float/el/usr/lib/../lib/crtn.o" +// +// = Little-endian, hard float, 64-bit +// RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \ +// RUN: -target mips64el-linux-gnu \ +// RUN: -gcc-toolchain %S/Inputs/mips_cs_tree \ +// RUN: | FileCheck --check-prefix=CHECK-EL-HF-64 %s +// CHECK-EL-HF-64: "{{.*}}ld{{(.exe)?}}" +// CHECK-EL-HF-64: "--sysroot=[[TC:[^"]+]]/../../../../mips-linux-gnu/libc/el" +// CHECK-EL-HF-64: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/../../../../mips-linux-gnu/libc/el/usr/lib/../lib64/crt1.o" +// CHECK-EL-HF-64: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/../../../../mips-linux-gnu/libc/el/usr/lib/../lib64/crti.o" +// CHECK-EL-HF-64: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/el/64/crtbegin.o" +// CHECK-EL-HF-64: "-L[[TC]]/el/64" +// CHECK-EL-HF-64: "-L[[TC]]/../../../../mips-linux-gnu/lib/../lib64/el" +// CHECK-EL-HF-64: "-L[[TC]]/../../../../mips-linux-gnu/libc/el/lib/../lib64" +// CHECK-EL-HF-64: "-L[[TC]]/../../../../mips-linux-gnu/libc/el/usr/lib/../lib64" +// CHECK-EL-HF-64: "-L[[TC]]" +// CHECK-EL-HF-64: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/el/64/crtend.o" +// CHECK-EL-HF-64: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/../../../../mips-linux-gnu/libc/el/usr/lib/../lib64/crtn.o" +// +// = Little-endian, soft float, 64-bit +// RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \ +// RUN: -target mips64el-linux-gnu -msoft-float \ +// RUN: -gcc-toolchain %S/Inputs/mips_cs_tree \ +// RUN: | FileCheck --check-prefix=CHECK-EL-SF-64 %s +// CHECK-EL-SF-64: "{{.*}}ld{{(.exe)?}}" +// CHECK-EL-SF-64: "--sysroot=[[TC:[^"]+]]/../../../../mips-linux-gnu/libc/soft-float/el" +// CHECK-EL-SF-64: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/../../../../mips-linux-gnu/libc/soft-float/el/usr/lib/../lib64/crt1.o" +// CHECK-EL-SF-64: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/../../../../mips-linux-gnu/libc/soft-float/el/usr/lib/../lib64/crti.o" +// CHECK-EL-SF-64: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/soft-float/el/64/crtbegin.o" +// CHECK-EL-SF-64: "-L[[TC]]/soft-float/el/64" +// CHECK-EL-SF-64: "-L[[TC]]/../../../../mips-linux-gnu/lib/../lib64/soft-float/el" +// CHECK-EL-SF-64: "-L[[TC]]/../../../../mips-linux-gnu/libc/soft-float/el/lib/../lib64" +// CHECK-EL-SF-64: "-L[[TC]]/../../../../mips-linux-gnu/libc/soft-float/el/usr/lib/../lib64" +// CHECK-EL-SF-64: "-L[[TC]]" +// CHECK-EL-SF-64: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/soft-float/el/64/crtend.o" +// CHECK-EL-SF-64: "{{.*}}/lib/gcc/mips-linux-gnu/4.6.3/../../../../mips-linux-gnu/libc/soft-float/el/usr/lib/../lib64/crtn.o" diff --git a/test/Driver/mips-eleb.c b/test/Driver/mips-eleb.c index 8afe44f51c9..6ea49be3c35 100644 --- a/test/Driver/mips-eleb.c +++ b/test/Driver/mips-eleb.c @@ -1,29 +1,27 @@ -// REQUIRES: mips-registered-target -// // Check that -EL/-EB options adjust the toolchain flags. // -// RUN: %clang -target mips-unknown-linux-gnu -### \ +// RUN: %clang -no-canonical-prefixes -target mips-unknown-linux-gnu -### \ // RUN: -EL -no-integrated-as %s 2>&1 \ // RUN: | FileCheck -check-prefix=MIPS32-EL %s // MIPS32-EL: "{{.*}}clang{{.*}}" "-cc1" "-triple" "mipsel-unknown-linux-gnu" // MIPS32-EL: "{{.*}}as{{(.exe)?}}" "-march" "mips32" "-mabi" "32" "-EL" // MIPS32-EL: "{{.*}}ld{{(.exe)?}}" {{.*}} "-m" "elf32ltsmip" // -// RUN: %clang -target mips64-unknown-linux-gnu -### \ +// RUN: %clang -no-canonical-prefixes -target mips64-unknown-linux-gnu -### \ // RUN: -EL -no-integrated-as %s 2>&1 \ // RUN: | FileCheck -check-prefix=MIPS64-EL %s // MIPS64-EL: "{{.*}}clang{{.*}}" "-cc1" "-triple" "mips64el-unknown-linux-gnu" // MIPS64-EL: "{{.*}}as{{(.exe)?}}" "-march" "mips64" "-mabi" "64" "-EL" // MIPS64-EL: "{{.*}}ld{{(.exe)?}}" {{.*}} "-m" "elf64ltsmip" // -// RUN: %clang -target mipsel-unknown-linux-gnu -### \ +// RUN: %clang -no-canonical-prefixes -target mipsel-unknown-linux-gnu -### \ // RUN: -EB -no-integrated-as %s 2>&1 \ // RUN: | FileCheck -check-prefix=MIPS32-EB %s // MIPS32-EB: "{{.*}}clang{{.*}}" "-cc1" "-triple" "mips-unknown-linux-gnu" // MIPS32-EB: "{{.*}}as{{(.exe)?}}" "-march" "mips32" "-mabi" "32" "-EB" // MIPS32-EB: "{{.*}}ld{{(.exe)?}}" {{.*}} "-m" "elf32btsmip" // -// RUN: %clang -target mips64el-unknown-linux-gnu -### \ +// RUN: %clang -no-canonical-prefixes -target mips64el-unknown-linux-gnu -### \ // RUN: -EB -no-integrated-as %s 2>&1 \ // RUN: | FileCheck -check-prefix=MIPS64-EB %s // MIPS64-EB: "{{.*}}clang{{.*}}" "-cc1" "-triple" "mips64-unknown-linux-gnu" diff --git a/test/Driver/mips-features.c b/test/Driver/mips-features.c index 3bebffc11bb..31bf1935ea0 100644 --- a/test/Driver/mips-features.c +++ b/test/Driver/mips-features.c @@ -1,5 +1,3 @@ -// REQUIRES: mips-registered-target -// // Check handling MIPS specific features options. // // -mips16 @@ -14,6 +12,18 @@ // RUN: | FileCheck --check-prefix=CHECK-NOMIPS16 %s // CHECK-NOMIPS16: "-target-feature" "-mips16" // +// -mmicromips +// RUN: %clang -target mips-linux-gnu -### -c %s \ +// RUN: -mno-micromips -mmicromips 2>&1 \ +// RUN: | FileCheck --check-prefix=CHECK-MICROMIPS %s +// CHECK-MICROMIPS: "-target-feature" "+micromips" +// +// -mno-micromips +// RUN: %clang -target mips-linux-gnu -### -c %s \ +// RUN: -mmicromips -mno-micromips 2>&1 \ +// RUN: | FileCheck --check-prefix=CHECK-NOMICROMIPS %s +// CHECK-NOMICROMIPS: "-target-feature" "-micromips" +// // -mdsp // RUN: %clang -target mips-linux-gnu -### -c %s \ // RUN: -mno-dsp -mdsp 2>&1 \ diff --git a/test/Driver/mips-float.c b/test/Driver/mips-float.c index 5c16b9b0635..9e62c0a95e0 100644 --- a/test/Driver/mips-float.c +++ b/test/Driver/mips-float.c @@ -1,4 +1,3 @@ -// REQUIRES: mips-registered-target // Check handling -mhard-float / -msoft-float / -mfloat-abi options // when build for MIPS platforms. // @@ -36,12 +35,27 @@ // CHECK-ABI-SOFT: "-mfloat-abi" "soft" // CHECK-ABI-SOFT: "-target-feature" "+soft-float" // -// -mfloat-abi=single +// -mdouble-float // RUN: %clang -c %s -### -o %t.o 2>&1 \ -// RUN: -target mips-linux-gnu -mfloat-abi=single \ +// RUN: -target mips-linux-gnu -msingle-float -mdouble-float \ +// RUN: | FileCheck --check-prefix=CHECK-ABI-DOUBLE %s +// CHECK-ABI-DOUBLE: "-mfloat-abi" "hard" +// CHECK-ABI-DOUBLE-NOT: "+single-float" +// +// -msingle-float +// RUN: %clang -c %s -### -o %t.o 2>&1 \ +// RUN: -target mips-linux-gnu -mdouble-float -msingle-float \ // RUN: | FileCheck --check-prefix=CHECK-ABI-SINGLE %s +// CHECK-ABI-SINGLE: "-mfloat-abi" "hard" // CHECK-ABI-SINGLE: "-target-feature" "+single-float" // +// -msoft-float -msingle-float +// RUN: %clang -c %s -### -o %t.o 2>&1 \ +// RUN: -target mips-linux-gnu -msoft-float -msingle-float \ +// RUN: | FileCheck --check-prefix=CHECK-ABI-SOFT-SINGLE %s +// CHECK-ABI-SOFT-SINGLE: "-mfloat-abi" "soft" +// CHECK-ABI-SOFT-SINGLE: "-target-feature" "+single-float" +// // Default -mips16 // RUN: %clang -c %s -### -o %t.o 2>&1 \ // RUN: -target mips-linux-gnu -mips16 \ diff --git a/test/Driver/modules.m b/test/Driver/modules.m index 69c79fca724..b93054dbf87 100644 --- a/test/Driver/modules.m +++ b/test/Driver/modules.m @@ -4,9 +4,3 @@ // RUN: %clang -fmodules -fno-modules -fmodules -### %s 2>&1 | FileCheck -check-prefix=CHECK-HAS-MODULES %s // CHECK-HAS-MODULES: -fmodules -// RUN: %clang -target x86_64-apple-darwin10 -fmodules -fno-modules -fmodules -### %s 2>&1 | FileCheck -check-prefix=CHECK-HAS-AUTOLINK %s -// CHECK-HAS-AUTOLINK: -fmodules-autolink - -// RUN: %clang -fmodules -fno-modules -fno-modules-autolink -fmodules -### %s 2>&1 | FileCheck -check-prefix=CHECK-NO-AUTOLINK %s -// CHECK-NO-AUTOLINK-NOT: -fmodules-autolink - diff --git a/test/Driver/modules_integrated_as.c b/test/Driver/modules_integrated_as.c deleted file mode 100644 index 0abd18fc5fe..00000000000 --- a/test/Driver/modules_integrated_as.c +++ /dev/null @@ -1,6 +0,0 @@ -// RUN: %clang -fsyntax-only modules_integrated_as.c -fmodules -no-integrated-as -### 2>&1 | FileCheck %s - -// Test that the autolinking feature is disabled with *not* using the -// integrated assembler. - -// CHECK-NOT: -fmodules-autolink diff --git a/test/Driver/objc++-cpp-output.mm b/test/Driver/objc++-cpp-output.mm index 63b15d8c18b..a42f7b25578 100644 --- a/test/Driver/objc++-cpp-output.mm +++ b/test/Driver/objc++-cpp-output.mm @@ -1,5 +1,5 @@ -// RUN: %clang -x objc++-cpp-output -c %s -o /dev/null -// RUN: %clang -x objc++-cpp-output -c %s -o /dev/null -### 2>&1 | FileCheck %s +// RUN: %clang -emit-llvm -x objc++-cpp-output -S %s -o /dev/null +// RUN: %clang -emit-llvm -x objc++-cpp-output -S %s -o /dev/null -### 2>&1 | FileCheck %s // PR13820 // REQUIRES: LP64 diff --git a/test/Driver/objc-cpp-output.m b/test/Driver/objc-cpp-output.m index 8c174f77320..293bbc7ef2f 100644 --- a/test/Driver/objc-cpp-output.m +++ b/test/Driver/objc-cpp-output.m @@ -1,4 +1,4 @@ -// RUN: %clang -x objc-cpp-output -c %s -o /dev/null +// RUN: %clang -emit-llvm -x objc-cpp-output -S %s -o /dev/null // PR13820 // REQUIRES: LP64 diff --git a/test/Driver/output-file-is-dir.c b/test/Driver/output-file-is-dir.c index c1fec56eac0..042ae3d40cd 100644 --- a/test/Driver/output-file-is-dir.c +++ b/test/Driver/output-file-is-dir.c @@ -1,7 +1,6 @@ // RUN: rm -rf %t.dir -// RUN: mkdir -p %t.dir/a.out -// RUN: cd %t.dir && not %clang %s -// RUN: test -d %t.dir/a.out -// REQUIRES: shell +// RUN: mkdir -p %t.dir +// RUN: not %clang %s -c -emit-llvm -o %t.dir +// RUN: test -d %t.dir int main() { return 0; } diff --git a/test/Driver/pic.c b/test/Driver/pic.c index 8ba931954b1..3faed2d18fa 100644 --- a/test/Driver/pic.c +++ b/test/Driver/pic.c @@ -36,6 +36,8 @@ // // CHECK-NO-PIE-NOT: "-pie" // +// CHECK-NO-UNUSED-ARG-NOT: argument unused during compilation +// // RUN: %clang -c %s -target i386-unknown-unknown -### 2>&1 \ // RUN: | FileCheck %s --check-prefix=CHECK-NO-PIC // RUN: %clang -c %s -target i386-unknown-unknown -fpic -### 2>&1 \ @@ -164,6 +166,8 @@ // RUN: | FileCheck %s --check-prefix=CHECK-PIC2 // RUN: %clang -c %s -target x86_64-apple-darwin -fPIE -### 2>&1 \ // RUN: | FileCheck %s --check-prefix=CHECK-PIC2 +// RUN: %clang -c %s -target x86_64-apple-darwin -fPIC -### 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-NO-UNUSED-ARG // // Darwin gets even more special with '-mdynamic-no-pic'. This flag is only // valid on Darwin, and it's behavior is very strange but needs to remain diff --git a/test/Driver/r600-mcpu.cl b/test/Driver/r600-mcpu.cl index 70e811650e2..1c5e76225b5 100644 --- a/test/Driver/r600-mcpu.cl +++ b/test/Driver/r600-mcpu.cl @@ -1,12 +1,12 @@ // Check that -mcpu works for all supported GPUs // RUN: %clang -### -target r600 -x cl -S -emit-llvm -mcpu=r600 %s -o - 2>&1 | FileCheck --check-prefix=R600-CHECK %s -// RUN: %clang -### -target r600 -x cl -S -emit-llvm -mcpu=rv610 %s -o - 2>&1 | FileCheck --check-prefix=R600-CHECK %s -// RUN: %clang -### -target r600 -x cl -S -emit-llvm -mcpu=rv620 %s -o - 2>&1 | FileCheck --check-prefix=R600-CHECK %s // RUN: %clang -### -target r600 -x cl -S -emit-llvm -mcpu=rv630 %s -o - 2>&1 | FileCheck --check-prefix=R600-CHECK %s // RUN: %clang -### -target r600 -x cl -S -emit-llvm -mcpu=rv635 %s -o - 2>&1 | FileCheck --check-prefix=R600-CHECK %s -// RUN: %clang -### -target r600 -x cl -S -emit-llvm -mcpu=rs780 %s -o - 2>&1 | FileCheck --check-prefix=R600-CHECK %s -// RUN: %clang -### -target r600 -x cl -S -emit-llvm -mcpu=rs880 %s -o - 2>&1 | FileCheck --check-prefix=R600-CHECK %s +// RUN: %clang -### -target r600 -x cl -S -emit-llvm -mcpu=rv610 %s -o - 2>&1 | FileCheck --check-prefix=RS880-CHECK %s +// RUN: %clang -### -target r600 -x cl -S -emit-llvm -mcpu=rv620 %s -o - 2>&1 | FileCheck --check-prefix=RS880-CHECK %s +// RUN: %clang -### -target r600 -x cl -S -emit-llvm -mcpu=rs780 %s -o - 2>&1 | FileCheck --check-prefix=RS880-CHECK %s +// RUN: %clang -### -target r600 -x cl -S -emit-llvm -mcpu=rs880 %s -o - 2>&1 | FileCheck --check-prefix=RS880-CHECK %s // RUN: %clang -### -target r600 -x cl -S -emit-llvm -mcpu=rv670 %s -o - 2>&1 | FileCheck --check-prefix=RV670-CHECK %s // RUN: %clang -### -target r600 -x cl -S -emit-llvm -mcpu=rv710 %s -o - 2>&1 | FileCheck --check-prefix=RV710-CHECK %s // RUN: %clang -### -target r600 -x cl -S -emit-llvm -mcpu=rv730 %s -o - 2>&1 | FileCheck --check-prefix=RV730-CHECK %s @@ -14,8 +14,8 @@ // RUN: %clang -### -target r600 -x cl -S -emit-llvm -mcpu=rv770 %s -o - 2>&1 | FileCheck --check-prefix=RV770-CHECK %s // RUN: %clang -### -target r600 -x cl -S -emit-llvm -mcpu=palm %s -o - 2>&1 | FileCheck --check-prefix=CEDAR-CHECK %s // RUN: %clang -### -target r600 -x cl -S -emit-llvm -mcpu=cedar %s -o - 2>&1 | FileCheck --check-prefix=CEDAR-CHECK %s -// RUN: %clang -### -target r600 -x cl -S -emit-llvm -mcpu=sumo %s -o - 2>&1 | FileCheck --check-prefix=REDWOOD-CHECK %s -// RUN: %clang -### -target r600 -x cl -S -emit-llvm -mcpu=sumo2 %s -o - 2>&1 | FileCheck --check-prefix=REDWOOD-CHECK %s +// RUN: %clang -### -target r600 -x cl -S -emit-llvm -mcpu=sumo %s -o - 2>&1 | FileCheck --check-prefix=SUMO-CHECK %s +// RUN: %clang -### -target r600 -x cl -S -emit-llvm -mcpu=sumo2 %s -o - 2>&1 | FileCheck --check-prefix=SUMO-CHECK %s // RUN: %clang -### -target r600 -x cl -S -emit-llvm -mcpu=redwood %s -o - 2>&1 | FileCheck --check-prefix=REDWOOD-CHECK %s // RUN: %clang -### -target r600 -x cl -S -emit-llvm -mcpu=juniper %s -o - 2>&1 | FileCheck --check-prefix=JUNIPER-CHECK %s // RUN: %clang -### -target r600 -x cl -S -emit-llvm -mcpu=juniper %s -o - 2>&1 | FileCheck --check-prefix=JUNIPER-CHECK %s @@ -32,12 +32,14 @@ // RUN: %clang -### -target r600 -x cl -S -emit-llvm -mcpu=oland %s -o - 2>&1 | FileCheck --check-prefix=OLAND-CHECK %s // R600-CHECK: "-target-cpu" "r600" +// RS880-CHECK: "-target-cpu" "rs880" // RV670-CHECK: "-target-cpu" "rv670" // RV710-CHECK: "-target-cpu" "rv710" // RV730-CHECK: "-target-cpu" "rv730" // RV770-CHECK: "-target-cpu" "rv770" // CEDAR-CHECK: "-target-cpu" "cedar" // REDWOOD-CHECK: "-target-cpu" "redwood" +// SUMO-CHECK: "-target-cpu" "sumo" // JUNIPER-CHECK: "-target-cpu" "juniper" // CYPRESS-CHECK: "-target-cpu" "cypress" // BARTS-CHECK: "-target-cpu" "barts" diff --git a/test/Driver/sanitizer-ld.c b/test/Driver/sanitizer-ld.c index fd68b579a10..fd7e97fc20f 100644 --- a/test/Driver/sanitizer-ld.c +++ b/test/Driver/sanitizer-ld.c @@ -10,6 +10,7 @@ // CHECK-ASAN-LINUX-NOT: "-lc" // CHECK-ASAN-LINUX: libclang_rt.asan-i386.a" // CHECK-ASAN-LINUX: "-lpthread" +// CHECK-ASAN-LINUX: "-lrt" // CHECK-ASAN-LINUX: "-ldl" // CHECK-ASAN-LINUX-NOT: "-export-dynamic" // CHECK-ASAN-LINUX: "--dynamic-list={{.*}}libclang_rt.asan-i386.a.syms" @@ -24,6 +25,7 @@ // CHECK-ASAN-LINUX-CXX-NOT: "-lc" // CHECK-ASAN-LINUX-CXX: "-whole-archive" "{{.*}}libclang_rt.asan-i386.a" "-no-whole-archive" // CHECK-ASAN-LINUX-CXX: "-lpthread" +// CHECK-ASAN-LINUX-CXX: "-lrt" // CHECK-ASAN-LINUX-CXX: "-ldl" // CHECK-ASAN-LINUX-CXX: "-export-dynamic" // CHECK-ASAN-LINUX-CXX-NOT: "--dynamic-list" @@ -70,6 +72,7 @@ // CHECK-TSAN-LINUX-CXX-NOT: stdc++ // CHECK-TSAN-LINUX-CXX: "-whole-archive" "{{.*}}libclang_rt.tsan-x86_64.a" "-no-whole-archive" // CHECK-TSAN-LINUX-CXX: "-lpthread" +// CHECK-TSAN-LINUX-CXX: "-lrt" // CHECK-TSAN-LINUX-CXX: "-ldl" // CHECK-TSAN-LINUX-CXX-NOT: "-export-dynamic" // CHECK-TSAN-LINUX-CXX: "--dynamic-list={{.*}}libclang_rt.tsan-x86_64.a.syms" @@ -85,6 +88,7 @@ // CHECK-MSAN-LINUX-CXX-NOT: stdc++ // CHECK-MSAN-LINUX-CXX: "-whole-archive" "{{.*}}libclang_rt.msan-x86_64.a" "-no-whole-archive" // CHECK-MSAN-LINUX-CXX: "-lpthread" +// CHECK-MSAN-LINUX-CXX: "-lrt" // CHECK-MSAN-LINUX-CXX: "-ldl" // CHECK-MSAN-LINUX-CXX-NOT: "-export-dynamic" // CHECK-MSAN-LINUX-CXX: "--dynamic-list={{.*}}libclang_rt.msan-x86_64.a.syms" diff --git a/test/Driver/save-temps.c b/test/Driver/save-temps.c new file mode 100644 index 00000000000..a4ca3b26649 --- /dev/null +++ b/test/Driver/save-temps.c @@ -0,0 +1,19 @@ +// RUN: %clang -target x86_64-apple-darwin -save-temps -arch x86_64 %s -### 2>&1 \ +// RUN: | FileCheck %s +// CHECK: "-o" "save-temps.i" +// CHECK: "-o" "save-temps.s" +// CHECK: "-o" "save-temps.o" +// CHECK: "-o" "a.out" + +// RUN: %clang -target x86_64-apple-darwin -save-temps -arch i386 -arch x86_64 %s -### 2>&1 \ +// RUN: | FileCheck %s -check-prefix=MULT-ARCH +// MULT-ARCH: "-o" "save-temps-i386.i" +// MULT-ARCH: "-o" "save-temps-i386.s" +// MULT-ARCH: "-o" "save-temps-i386.o" +// MULT-ARCH: "-o" "a.out-i386" +// MULT-ARCH: "-o" "save-temps-x86_64.i" +// MULT-ARCH: "-o" "save-temps-x86_64.s" +// MULT-ARCH: "-o" "save-temps-x86_64.o" +// MULT-ARCH: "-o" "a.out-x86_64" +// MULT-ARCH: lipo +// MULT-ARCH: "-create" "-output" "a.out" "a.out-i386" "a.out-x86_64" diff --git a/test/Driver/split-debug.s b/test/Driver/split-debug.s new file mode 100644 index 00000000000..d5f077af134 --- /dev/null +++ b/test/Driver/split-debug.s @@ -0,0 +1,21 @@ +// Check that we split debug output properly +// +// REQUIRES: asserts +// RUN: %clang -target x86_64-unknown-linux-gnu -gsplit-dwarf -c -### %s 2> %t +// RUN: FileCheck -check-prefix=CHECK-ACTIONS < %t %s +// +// CHECK-ACTIONS: objcopy{{.*}}--extract-dwo{{.*}}"split-debug.dwo" +// CHECK-ACTIONS: objcopy{{.*}}--strip-dwo{{.*}}"split-debug.o" + + +// RUN: %clang -target x86_64-macosx -gsplit-dwarf -c -### %s 2> %t +// RUN: FileCheck -check-prefix=CHECK-NO-ACTIONS < %t %s +// +// CHECK-NO-ACTIONS-NOT: -split-dwarf + + +// RUN: %clang -target x86_64-unknown-linux-gnu -gsplit-dwarf -o Bad.x -### %s 2> %t +// RUN: FileCheck -check-prefix=CHECK-BAD < %t %s +// +// CHECK-BAD-NOT: "Bad.dwo" + diff --git a/test/FixIt/fixit-cxx1y-compat.cpp b/test/FixIt/fixit-cxx1y-compat.cpp new file mode 100644 index 00000000000..9fd5ff26e55 --- /dev/null +++ b/test/FixIt/fixit-cxx1y-compat.cpp @@ -0,0 +1,12 @@ +// RUN: %clang_cc1 -verify -std=c++11 %s +// RUN: cp %s %t +// RUN: %clang_cc1 -x c++ -std=c++11 -fixit %t +// RUN: %clang_cc1 -Wall -pedantic-errors -Werror -x c++ -std=c++11 %t +// RUN: %clang_cc1 -Wall -pedantic-errors -Werror -x c++ -std=c++1y %t + +// This is a test of the code modification hints for C++1y-compatibility problems. + +struct S { + constexpr int &f(); // expected-warning {{'constexpr' non-static member function will not be implicitly 'const' in C++1y; add 'const' to avoid a change in behavior}} + int &f(); +}; diff --git a/test/Format/basic.cpp b/test/Format/basic.cpp index 375bbd2ec71..a12866b9c1e 100644 --- a/test/Format/basic.cpp +++ b/test/Format/basic.cpp @@ -1,5 +1,5 @@ // RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp -// RUN: clang-format -i %t.cpp +// RUN: clang-format -style=LLVM -i %t.cpp // RUN: FileCheck -strict-whitespace -input-file=%t.cpp %s // CHECK: {{^int\ \*i;}} diff --git a/test/Format/multiple-inputs-error.cpp b/test/Format/multiple-inputs-error.cpp new file mode 100644 index 00000000000..71f87e0b15b --- /dev/null +++ b/test/Format/multiple-inputs-error.cpp @@ -0,0 +1,6 @@ +// RUN: cp %s %t-1.cpp +// RUN: cp %s %t-2.cpp +// RUN: clang-format 2>&1 >/dev/null -offset=1 -length=0 %t-1.cpp %t-2.cpp |FileCheck %s +// CHECK: error: "-offset" and "-length" can only be used for single file. + +int i ; diff --git a/test/Format/multiple-inputs-inplace.cpp b/test/Format/multiple-inputs-inplace.cpp new file mode 100644 index 00000000000..74c30db71eb --- /dev/null +++ b/test/Format/multiple-inputs-inplace.cpp @@ -0,0 +1,8 @@ +// RUN: cp %s %t-1.cpp +// RUN: cp %s %t-2.cpp +// RUN: clang-format -style=LLVM -i %t-1.cpp %t-2.cpp +// RUN: FileCheck -strict-whitespace -input-file=%t-1.cpp %s +// RUN: FileCheck -strict-whitespace -input-file=%t-2.cpp %s + +// CHECK: {{^int\ \*i;}} + int * i ; diff --git a/test/Format/multiple-inputs.cpp b/test/Format/multiple-inputs.cpp new file mode 100644 index 00000000000..df267149fe5 --- /dev/null +++ b/test/Format/multiple-inputs.cpp @@ -0,0 +1,7 @@ +// RUN: cp %s %t-1.cpp +// RUN: cp %s %t-2.cpp +// RUN: clang-format -style=LLVM %t-1.cpp %t-2.cpp|FileCheck -strict-whitespace %s + +// CHECK: {{^int\ \*i;}} +// CHECK: {{^int\ \*i;}} + int * i ; diff --git a/test/Format/ranges.cpp b/test/Format/ranges.cpp index 0244fc195ca..c7fdd4b97a4 100644 --- a/test/Format/ranges.cpp +++ b/test/Format/ranges.cpp @@ -1,5 +1,5 @@ // RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp -// RUN: clang-format -offset=2 -length=0 -offset=28 -length=0 -i %t.cpp +// RUN: clang-format -style=LLVM -offset=2 -length=0 -offset=28 -length=0 -i %t.cpp // RUN: FileCheck -strict-whitespace -input-file=%t.cpp %s // CHECK: {{^int\ \*i;$}} int*i; diff --git a/test/Frontend/Inputs/rewrite-includes8.h b/test/Frontend/Inputs/rewrite-includes8.h new file mode 100644 index 00000000000..e827ad99cf3 --- /dev/null +++ b/test/Frontend/Inputs/rewrite-includes8.h @@ -0,0 +1,5 @@ +#if __has_include_next() +#elif __has_include() +#endif +#if !__has_include("rewrite-includes8.h") +#endif diff --git a/test/Frontend/rewrite-includes-invalid-hasinclude.c b/test/Frontend/rewrite-includes-invalid-hasinclude.c new file mode 100644 index 00000000000..e32d6ad8a3e --- /dev/null +++ b/test/Frontend/rewrite-includes-invalid-hasinclude.c @@ -0,0 +1,17 @@ +// RUN: %clang_cc1 -E -frewrite-includes -DFIRST -I %S/Inputs %s -o - | FileCheck -strict-whitespace %s + +#if __has_include bar.h +#endif + +#if __has_include(bar.h) +#endif + +#if __has_include( +int foo(); +#include + +// CHECK: int bar();{{$}} +// CHECK-NEXT: #if 0 /* expanded by -frewrite-includes */{{$}} +// CHECK-NEXT: #include {{$}} +// CHECK-NEXT: #endif /* expanded by -frewrite-includes */{{$}} +// CHECK-NEXT: @import Module; /* clang -frewrite-includes: implicit import */{{$}} +// CHECK-NEXT: # 6 "{{.*[/\\]}}rewrite-includes-modules.c"{{$}} +// CHECK-NEXT: int foo();{{$}} +// CHECK-NEXT: #if 0 /* expanded by -frewrite-includes */{{$}} +// CHECK-NEXT: #include {{$}} +// CHECK-NEXT: #endif /* expanded by -frewrite-includes */{{$}} +// CHECK-NEXT: @import Module; /* clang -frewrite-includes: implicit import */{{$}} +// CHECK-NEXT: # 8 "{{.*[/\\]}}rewrite-includes-modules.c"{{$}} diff --git a/test/Frontend/rewrite-includes.c b/test/Frontend/rewrite-includes.c index 546a2c44af2..bf330a60a3d 100644 --- a/test/Frontend/rewrite-includes.c +++ b/test/Frontend/rewrite-includes.c @@ -18,6 +18,7 @@ A(1,2) continues */ #include "rewrite-includes7.h" #include "rewrite-includes7.h" +#include "rewrite-includes8.h" // ENDCOMPARE // CHECK: {{^}}// STARTCOMPARE{{$}} // CHECK-NEXT: {{^}}#define A(a,b) a ## b{{$}} @@ -88,6 +89,16 @@ A(1,2) // CHECK-NEXT: {{^}}#include "rewrite-includes7.h"{{$}} // CHECK-NEXT: {{^}}#endif /* expanded by -frewrite-includes */{{$}} // CHECK-NEXT: {{^}}# 21 "{{.*}}rewrite-includes.c"{{$}} +// CHECK-NEXT: {{^}}#if 0 /* expanded by -frewrite-includes */{{$}} +// CHECK-NEXT: {{^}}#include "rewrite-includes8.h"{{$}} +// CHECK-NEXT: {{^}}#endif /* expanded by -frewrite-includes */{{$}} +// CHECK-NEXT: {{^}}# 1 "{{.*[/\\]Inputs[/\\]}}rewrite-includes8.h" 1{{$}} +// CHECK-NEXT: {{^}}#if (1)/*__has_include_next()*/{{$}} +// CHECK-NEXT: {{^}}#elif (0)/*__has_include()*/{{$}} +// CHECK-NEXT: {{^}}#endif{{$}} +// CHECK-NEXT: {{^}}#if !(1)/*__has_include("rewrite-includes8.h")*/{{$}} +// CHECK-NEXT: {{^}}#endif{{$}} +// CHECK-NEXT: {{^}}# 22 "{{.*}}rewrite-includes.c" 2{{$}} // CHECK-NEXT: {{^}}// ENDCOMPARE{{$}} // CHECKNL: {{^}}// STARTCOMPARE{{$}} @@ -142,4 +153,12 @@ A(1,2) // CHECKNL-NEXT: {{^}}#if 0 /* expanded by -frewrite-includes */{{$}} // CHECKNL-NEXT: {{^}}#include "rewrite-includes7.h"{{$}} // CHECKNL-NEXT: {{^}}#endif /* expanded by -frewrite-includes */{{$}} +// CHECKNL-NEXT: {{^}}#if 0 /* expanded by -frewrite-includes */{{$}} +// CHECKNL-NEXT: {{^}}#include "rewrite-includes8.h"{{$}} +// CHECKNL-NEXT: {{^}}#endif /* expanded by -frewrite-includes */{{$}} +// CHECKNL-NEXT: {{^}}#if (1)/*__has_include_next()*/{{$}} +// CHECKNL-NEXT: {{^}}#elif (0)/*__has_include()*/{{$}} +// CHECKNL-NEXT: {{^}}#endif{{$}} +// CHECKNL-NEXT: {{^}}#if !(1)/*__has_include("rewrite-includes8.h")*/{{$}} +// CHECKNL-NEXT: {{^}}#endif{{$}} // CHECKNL-NEXT: {{^}}// ENDCOMPARE{{$}} diff --git a/test/Frontend/rewrite-macros.c b/test/Frontend/rewrite-macros.c index bc7479693bf..eab66571b8d 100644 --- a/test/Frontend/rewrite-macros.c +++ b/test/Frontend/rewrite-macros.c @@ -1,17 +1,21 @@ -// RUN: %clang_cc1 -verify -rewrite-macros -o %t %s +// RUN: %clang_cc1 %s -verify -rewrite-macros -o %t +// RUN: FileCheck %s < %t + +// Any CHECK line comments are included in the output, so we use some extra +// regex brackets to make sure we don't match the CHECK lines themselves. #define A(a,b) a ## b -// RUN: grep '12 */\*A\*/ /\*(1,2)\*/' %t +// CHECK: {{^}} 12 /*A*/ /*(1,2)*/{{$}} A(1,2) -// RUN: grep '/\*_Pragma("mark")\*/' %t +// CHECK: {{^}} /*_Pragma("mark")*/{{$}} _Pragma("mark") -// RUN: grep "//#warning eek" %t +// CHECK: /*#warning eek*/{{$}} /* expected-warning {{eek}} */ #warning eek -// RUN: grep "//#pragma mark mark" %t +// CHECK: {{^}}//#pragma mark mark{{$}} #pragma mark mark diff --git a/test/Frontend/verify.c b/test/Frontend/verify.c index 062e6bd8618..3d71e04980c 100644 --- a/test/Frontend/verify.c +++ b/test/Frontend/verify.c @@ -124,3 +124,19 @@ unexpected b; // expected-error@33 1-1 {{unknown type}} // CHECK7-NEXT: Line 2: 2 // CHECK7-NEXT: 2 errors generated. #endif + +#ifdef TEST8 +// RUN: %clang_cc1 -DTEST8 -verify %s 2>&1 | FileCheck -check-prefix=CHECK8 %s + +// expected-warning@nonexistant-file:1 {{ }} +// expected-error@-1 {{file 'nonexistant-file' could not be located}} + +// expected-warning@verify-directive.h: {{ }} +// expected-error@-1 {{missing or invalid line number}} + +// expected-warning@verify-directive.h:1 {{diagnostic}} + +// CHECK8: error: 'warning' diagnostics expected but not seen: +// CHECK8-NEXT: File {{.*}}verify-directive.h Line 1 (directive at {{.*}}verify.c:137): diagnostic +// CHECK8-NEXT: 1 error generated. +#endif diff --git a/test/Headers/c11.c b/test/Headers/c11.c index f65164d15c2..21f2e4f2224 100644 --- a/test/Headers/c11.c +++ b/test/Headers/c11.c @@ -1,5 +1,6 @@ // RUN: %clang -fsyntax-only -Xclang -verify -std=c11 %s // RUN: %clang -fsyntax-only -Xclang -verify -std=c11 -fmodules %s +// RUN: %clang -fsyntax-only -Xclang -verify -std=c11 -ffreestanding %s noreturn int f(); // expected-error 1+{{}} @@ -17,3 +18,15 @@ _Static_assert(__alignas_is_defined, ""); _Static_assert(__alignof_is_defined, ""); alignas(alignof(int)) char c[4]; _Static_assert(__alignof(c) == 4, ""); + +#define __STDC_WANT_LIB_EXT1__ 1 +#include +rsize_t x = 0; + +// If we are freestanding, then also check RSIZE_MAX (in a hosted implementation +// we will use the host stdint.h, which may not yet have C11 support). +#ifndef __STDC_HOSTED__ +#include +rsize_t x2 = RSIZE_MAX; +#endif + diff --git a/test/Headers/cxx11.cpp b/test/Headers/cxx11.cpp index 41bdc76fdae..54fe350ea5b 100644 --- a/test/Headers/cxx11.cpp +++ b/test/Headers/cxx11.cpp @@ -13,3 +13,10 @@ static_assert(__alignas_is_defined, ""); static_assert(__alignof_is_defined, ""); + + +#include + +#ifndef SIZE_MAX +#error SIZE_MAX should be defined in C++ +#endif diff --git a/test/Headers/ms-wchar.c b/test/Headers/ms-wchar.c new file mode 100644 index 00000000000..f015fc77ee5 --- /dev/null +++ b/test/Headers/ms-wchar.c @@ -0,0 +1,15 @@ +// RUN: %clang -fsyntax-only -target i386-pc-win32 %s + +#if defined(_WCHAR_T_DEFINED) +#error "_WCHAR_T_DEFINED should not be defined in C99" +#endif + +#include + +#if !defined(_WCHAR_T_DEFINED) +#error "_WCHAR_T_DEFINED should have been set by stddef.h" +#endif + +#if defined(_NATIVE_WCHAR_T_DEFINED) +#error "_NATIVE_WCHAR_T_DEFINED should not be defined" +#endif diff --git a/test/Index/annotate-module.m b/test/Index/annotate-module.m index 33ca3f83249..55e21d235e7 100644 --- a/test/Index/annotate-module.m +++ b/test/Index/annotate-module.m @@ -40,3 +40,10 @@ int glob; // CHECK-MOD-NEXT: Punctuation: "*" [2:5 - 2:6] VarDecl=Module_Sub:2:6 // CHECK-MOD-NEXT: Identifier: "Module_Sub" [2:6 - 2:16] VarDecl=Module_Sub:2:6 // CHECK-MOD-NEXT: Punctuation: ";" [2:16 - 2:17] + +// RUN: c-index-test -cursor-at=%s:3:11 %s -fmodules-cache-path=%t.cache -fmodules -F %S/../Modules/Inputs \ +// RUN: | FileCheck %s -check-prefix=CHECK-CURSOR + +// CHECK-CURSOR: 3:1 ModuleImport=DependsOnModule:3:1 (Definition) Extent=[3:1 - 3:24] Spelling=DependsOnModule ([3:9 - 3:24]) ModuleName=DependsOnModule ({{.*}}DependsOnModule.pcm) Headers(2): +// CHECK-CURSOR-NEXT: {{.*}}other.h +// CHECK-CURSOR-NEXT: {{.*}}DependsOnModule.h diff --git a/test/Index/annotate-tokens.cpp b/test/Index/annotate-tokens.cpp index 3062901b7c2..16726547a2e 100644 --- a/test/Index/annotate-tokens.cpp +++ b/test/Index/annotate-tokens.cpp @@ -20,7 +20,15 @@ void test3(S2 s2) { X foo; } -// RUN: c-index-test -test-annotate-tokens=%s:1:1:21:1 %s | FileCheck %s +template +struct TS { + void foo(); +}; + +template +void TS::foo() {} + +// RUN: c-index-test -test-annotate-tokens=%s:1:1:30:1 %s -fno-delayed-template-parsing | FileCheck %s // CHECK: Keyword: "struct" [1:1 - 1:7] StructDecl=bonk:1:8 (Definition) // CHECK: Identifier: "bonk" [1:8 - 1:12] StructDecl=bonk:1:8 (Definition) // CHECK: Punctuation: "{" [1:13 - 1:14] StructDecl=bonk:1:8 (Definition) @@ -120,3 +128,48 @@ void test3(S2 s2) { // CHECK: Identifier: "foo" [20:5 - 20:8] VarDecl=foo:20:5 (Definition) // CHECK: Punctuation: ";" [20:8 - 20:9] DeclStmt= // CHECK: Punctuation: "}" [21:1 - 21:2] CompoundStmt= +// CHECK: Keyword: "template" [23:1 - 23:9] ClassTemplate=TS:24:8 (Definition) +// CHECK: Punctuation: "<" [23:10 - 23:11] ClassTemplate=TS:24:8 (Definition) +// CHECK: Keyword: "bool" [23:11 - 23:15] NonTypeTemplateParameter=tfn:23:18 (Definition) +// CHECK: Punctuation: "(" [23:16 - 23:17] NonTypeTemplateParameter=tfn:23:18 (Definition) +// CHECK: Punctuation: "*" [23:17 - 23:18] NonTypeTemplateParameter=tfn:23:18 (Definition) +// CHECK: Identifier: "tfn" [23:18 - 23:21] NonTypeTemplateParameter=tfn:23:18 (Definition) +// CHECK: Punctuation: ")" [23:21 - 23:22] NonTypeTemplateParameter=tfn:23:18 (Definition) +// CHECK: Punctuation: "(" [23:22 - 23:23] NonTypeTemplateParameter=tfn:23:18 (Definition) +// CHECK: Identifier: "X" [23:23 - 23:24] TypeRef=struct X:7:8 +// CHECK: Punctuation: "*" [23:24 - 23:25] ParmDecl=:23:25 (Definition) +// CHECK: Punctuation: ")" [23:25 - 23:26] ParmDecl=:23:25 (Definition) +// CHECK: Punctuation: ">" [23:26 - 23:27] ClassTemplate=TS:24:8 (Definition) +// CHECK: Keyword: "struct" [24:1 - 24:7] ClassTemplate=TS:24:8 (Definition) +// CHECK: Identifier: "TS" [24:8 - 24:10] ClassTemplate=TS:24:8 (Definition) +// CHECK: Punctuation: "{" [24:11 - 24:12] ClassTemplate=TS:24:8 (Definition) +// CHECK: Keyword: "void" [25:3 - 25:7] CXXMethod=foo:25:8 +// CHECK: Identifier: "foo" [25:8 - 25:11] CXXMethod=foo:25:8 +// CHECK: Punctuation: "(" [25:11 - 25:12] CXXMethod=foo:25:8 +// CHECK: Punctuation: ")" [25:12 - 25:13] CXXMethod=foo:25:8 +// CHECK: Punctuation: ";" [25:13 - 25:14] ClassTemplate=TS:24:8 (Definition) +// CHECK: Punctuation: "}" [26:1 - 26:2] ClassTemplate=TS:24:8 (Definition) +// CHECK: Punctuation: ";" [26:2 - 26:3] +// CHECK: Keyword: "template" [28:1 - 28:9] CXXMethod=foo:29:15 (Definition) +// CHECK: Punctuation: "<" [28:10 - 28:11] CXXMethod=foo:29:15 (Definition) +// CHECK: Keyword: "bool" [28:11 - 28:15] NonTypeTemplateParameter=tfn:28:18 (Definition) +// CHECK: Punctuation: "(" [28:16 - 28:17] NonTypeTemplateParameter=tfn:28:18 (Definition) +// CHECK: Punctuation: "*" [28:17 - 28:18] NonTypeTemplateParameter=tfn:28:18 (Definition) +// CHECK: Identifier: "tfn" [28:18 - 28:21] NonTypeTemplateParameter=tfn:28:18 (Definition) +// CHECK: Punctuation: ")" [28:21 - 28:22] NonTypeTemplateParameter=tfn:28:18 (Definition) +// CHECK: Punctuation: "(" [28:22 - 28:23] NonTypeTemplateParameter=tfn:28:18 (Definition) +// CHECK: Identifier: "X" [28:23 - 28:24] TypeRef=struct X:7:8 +// CHECK: Punctuation: "*" [28:24 - 28:25] ParmDecl=:28:25 (Definition) +// CHECK: Punctuation: ")" [28:25 - 28:26] ParmDecl=:28:25 (Definition) +// CHECK: Punctuation: ">" [28:26 - 28:27] CXXMethod=foo:29:15 (Definition) +// CHECK: Keyword: "void" [29:1 - 29:5] CXXMethod=foo:29:15 (Definition) +// CHECK: Identifier: "TS" [29:6 - 29:8] TemplateRef=TS:24:8 +// CHECK: Punctuation: "<" [29:8 - 29:9] CXXMethod=foo:29:15 (Definition) +// CHECK: Identifier: "tfn" [29:9 - 29:12] DeclRefExpr=tfn:28:18 +// CHECK: Punctuation: ">" [29:12 - 29:13] CXXMethod=foo:29:15 (Definition) +// CHECK: Punctuation: "::" [29:13 - 29:15] CXXMethod=foo:29:15 (Definition) +// CHECK: Identifier: "foo" [29:15 - 29:18] CXXMethod=foo:29:15 (Definition) +// CHECK: Punctuation: "(" [29:18 - 29:19] CXXMethod=foo:29:15 (Definition) +// CHECK: Punctuation: ")" [29:19 - 29:20] CXXMethod=foo:29:15 (Definition) +// CHECK: Punctuation: "{" [29:21 - 29:22] CompoundStmt= +// CHECK: Punctuation: "}" [29:22 - 29:23] CompoundStmt= diff --git a/test/Index/annotate-tokens.m b/test/Index/annotate-tokens.m index 7e888e394cb..40c66a18b8f 100644 --- a/test/Index/annotate-tokens.m +++ b/test/Index/annotate-tokens.m @@ -281,7 +281,7 @@ static Rdar8595462_A * Rdar8595462_staticVar; // CHECK: Punctuation: ")" [40:19 - 40:20] CallExpr=ibaction_test:36:12 // CHECK: Punctuation: ";" [40:20 - 40:21] CompoundStmt= // CHECK: Punctuation: "[" [41:5 - 41:6] ObjCMessageExpr=foo::34:9 -// CHECK: Identifier: "self" [41:6 - 41:10] DeclRefExpr=self:0:0 +// CHECK: Identifier: "self" [41:6 - 41:10] ObjCSelfExpr=self:0:0 // CHECK: Identifier: "foo" [41:11 - 41:14] ObjCMessageExpr=foo::34:9 // CHECK: Punctuation: ":" [41:14 - 41:15] ObjCMessageExpr=foo::34:9 // CHECK: Literal: "0" [41:15 - 41:16] IntegerLiteral= @@ -391,7 +391,7 @@ static Rdar8595462_A * Rdar8595462_staticVar; // CHECK: Identifier: "local" [76:9 - 76:14] VarDecl=local:76:9 (Definition) // CHECK: Punctuation: "=" [76:15 - 76:16] VarDecl=local:76:9 (Definition) // CHECK: Punctuation: "[" [76:17 - 76:18] ObjCMessageExpr=foo::66:9 -// CHECK: Identifier: "self" [76:18 - 76:22] DeclRefExpr=self:0:0 +// CHECK: Identifier: "self" [76:18 - 76:22] ObjCSelfExpr=self:0:0 // CHECK: Identifier: "foo" [76:23 - 76:26] ObjCMessageExpr=foo::66:9 // CHECK: Punctuation: ":" [76:26 - 76:27] ObjCMessageExpr=foo::66:9 // CHECK: Identifier: "VAL" [76:27 - 76:30] macro expansion=VAL:63:9 @@ -401,7 +401,7 @@ static Rdar8595462_A * Rdar8595462_staticVar; // CHECK: Identifier: "second" [77:9 - 77:15] VarDecl=second:77:9 (Definition) // CHECK: Punctuation: "=" [77:16 - 77:17] VarDecl=second:77:9 (Definition) // CHECK: Punctuation: "[" [77:18 - 77:19] ObjCMessageExpr=foo::66:9 -// CHECK: Identifier: "self" [77:19 - 77:23] DeclRefExpr=self:0:0 +// CHECK: Identifier: "self" [77:19 - 77:23] ObjCSelfExpr=self:0:0 // CHECK: Identifier: "foo" [77:24 - 77:27] ObjCMessageExpr=foo::66:9 // CHECK: Punctuation: ":" [77:27 - 77:28] ObjCMessageExpr=foo::66:9 // CHECK: Literal: "0" [77:28 - 77:29] IntegerLiteral= @@ -518,7 +518,7 @@ static Rdar8595462_A * Rdar8595462_staticVar; // CHECK-INSIDE_BLOCK: Identifier: "result" [127:9 - 127:15] VarDecl=result:127:9 (Definition) // CHECK-INSIDE_BLOCK: Punctuation: "=" [127:16 - 127:17] VarDecl=result:127:9 (Definition) // CHECK-INSIDE_BLOCK: Punctuation: "[" [127:18 - 127:19] ObjCMessageExpr=blah::124:8 -// CHECK-INSIDE_BLOCK: Identifier: "self" [127:19 - 127:23] DeclRefExpr=self:0:0 +// CHECK-INSIDE_BLOCK: Identifier: "self" [127:19 - 127:23] ObjCSelfExpr=self:0:0 // CHECK-INSIDE_BLOCK: Identifier: "blah" [127:24 - 127:28] ObjCMessageExpr=blah::124:8 // CHECK-INSIDE_BLOCK: Punctuation: ":" [127:28 - 127:29] ObjCMessageExpr=blah::124:8 // CHECK-INSIDE_BLOCK: Literal: "5" [127:29 - 127:30] IntegerLiteral= @@ -530,7 +530,7 @@ static Rdar8595462_A * Rdar8595462_staticVar; // CHECK-INSIDE_BLOCK: Punctuation: "*" [128:17 - 128:18] VarDecl=a:128:18 (Definition) // CHECK-INSIDE_BLOCK: Identifier: "a" [128:18 - 128:19] VarDecl=a:128:18 (Definition) // CHECK-INSIDE_BLOCK: Punctuation: "=" [128:20 - 128:21] VarDecl=a:128:18 (Definition) -// CHECK-INSIDE_BLOCK: Identifier: "self" [128:22 - 128:26] DeclRefExpr=self:0:0 +// CHECK-INSIDE_BLOCK: Identifier: "self" [128:22 - 128:26] ObjCSelfExpr=self:0:0 // RUN: c-index-test -test-annotate-tokens=%s:134:1:138:1 %s -DIBOutlet='__attribute__((iboutlet))' -DIBAction='void)__attribute__((ibaction)' | FileCheck -check-prefix=CHECK-PROP-AFTER-METHOD %s // CHECK-PROP-AFTER-METHOD: Punctuation: "@" [134:1 - 134:2] ObjCInterfaceDecl=Rdar8062781:134:12 diff --git a/test/Index/c-index-api-loadTU-test.m b/test/Index/c-index-api-loadTU-test.m index b0fb71e419c..61d82a6cde5 100644 --- a/test/Index/c-index-api-loadTU-test.m +++ b/test/Index/c-index-api-loadTU-test.m @@ -169,7 +169,7 @@ struct X0 {}; // CHECK: c-index-api-loadTU-test.m:71:8: StructDecl=X0:71:8 (Definition) Extent=[71:1 - 71:14] // CHECK: c-index-api-loadTU-test.m:73:12: ObjCCategoryDecl=:73:12 Extent=[73:1 - 76:5] // CHECK: c-index-api-loadTU-test.m:73:12: ObjCClassRef=TestAttributes:62:12 Extent=[73:12 - 73:26] -// CHECK: c-index-api-loadTU-test.m:75:32: ObjCPropertyDecl=anotherOutlet:75:32 Extent=[75:1 - 75:45] +// CHECK: c-index-api-loadTU-test.m:75:32: ObjCPropertyDecl=anotherOutlet:75:32 [retain,] Extent=[75:1 - 75:45] // CHECK: :0:0: attribute(iboutlet)= Extent=[75:20 - 75:28] // CHECK: c-index-api-loadTU-test.m:75:29: TypeRef=id:0:0 Extent=[75:29 - 75:31] // CHECK: c-index-api-loadTU-test.m:75:32: ObjCInstanceMethodDecl=anotherOutlet:75:32 Extent=[75:32 - 75:45] diff --git a/test/Index/comment-cplus11-specific.cpp b/test/Index/comment-cplus11-specific.cpp new file mode 100644 index 00000000000..fa0db914cd7 --- /dev/null +++ b/test/Index/comment-cplus11-specific.cpp @@ -0,0 +1,27 @@ +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: c-index-test -test-load-source all -comments-xml-schema=%S/../../bindings/xml/comment-xml-schema.rng std=c++11 %s > %t/out +// RUN: FileCheck %s < %t/out +// rdar://13752382 + +namespace inner { + //! This documentation should be inherited. + struct Opaque; +} +// CHECK: (CXComment_Text Text=[ This documentation should be inherited.])))] + +namespace borrow { + //! This is documentation for the typedef (which shows up). + typedef inner::Opaque Typedef; +// CHECK: (CXComment_Text Text=[ This is documentation for the typedef (which shows up).])))] + + //! This is documentation for the alias (which shows up). + using Alias = inner::Opaque; +// CHECK: (CXComment_Text Text=[ This is documentation for the alias (which shows up).])))] + + typedef inner::Opaque NoDocTypedef; +// CHECK: (CXComment_Text Text=[ This documentation should be inherited.])))] + + using NoDocAlias = inner::Opaque; +// CHECK: (CXComment_Text Text=[ This documentation should be inherited.])))] +} diff --git a/test/Index/comment-misc-tags.m b/test/Index/comment-misc-tags.m new file mode 100644 index 00000000000..9eae5489fc0 --- /dev/null +++ b/test/Index/comment-misc-tags.m @@ -0,0 +1,110 @@ +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: c-index-test -test-load-source all -comments-xml-schema=%S/../../bindings/xml/comment-xml-schema.rng %s > %t/out +// RUN: FileCheck %s < %t/out +// rdar://12379114 + +/*! + @interface IOCommandGate + @brief This is a brief + @abstract Single-threaded work-loop client request mechanism. + @discussion An IOCommandGate instance is an extremely light weight mechanism that + executes an action on the driver's work-loop... + @textblock + Many discussions about text + Many1 discussions about text + Many2 discussions about text + @/textblock + @link //un_ref/c/func/function_name link text goes here @/link + @see //un_ref/doc/uid/XX0000011 I/O Kit Fundamentals + @seealso //k_ref/doc/uid/XX30000905-CH204 Programming + */ +@interface IOCommandGate +@end + +// CHECK: (CXComment_BlockCommand CommandName=[abstract] +// CHECK-NEXT: (CXComment_Paragraph +// CHECK-NEXT: (CXComment_Text Text=[ Single-threaded work-loop client request mechanism.] HasTrailingNewline) +// CHECK: (CXComment_BlockCommand CommandName=[discussion] +// CHECK-NEXT: (CXComment_Paragraph +// CHECK-NEXT: (CXComment_Text Text=[ An IOCommandGate instance is an extremely light weight mechanism that] HasTrailingNewline) +// CHECK-NEXT: (CXComment_Text Text=[ executes an action on the driver's work-loop...] HasTrailingNewline) +// CHECK: (CXComment_VerbatimBlockCommand CommandName=[textblock] +// CHECK-NEXT: (CXComment_VerbatimBlockLine Text=[ Many discussions about text]) +// CHECK-NEXT: (CXComment_VerbatimBlockLine Text=[ Many1 discussions about text]) +// CHECK-NEXT: (CXComment_VerbatimBlockLine Text=[ Many2 discussions about text])) +// CHECK-NEXT: (CXComment_Paragraph IsWhitespace + +// CHECK: (CXComment_VerbatimBlockCommand CommandName=[link] +// CHECK-NEXT: (CXComment_VerbatimBlockLine Text=[ //un_ref/c/func/function_name link text goes here ])) +// CHECK-NEXT: (CXComment_Paragraph IsWhitespace +// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace)) +// CHECK: (CXComment_BlockCommand CommandName=[see] +// CHECK-NEXT: (CXComment_Paragraph +// CHECK-NEXT: (CXComment_Text Text=[ //un_ref/doc/uid/XX0000011 I/O Kit Fundamentals] HasTrailingNewline) +// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace))) +// CHECK: (CXComment_BlockCommand CommandName=[seealso] +// CHECK-NEXT: (CXComment_Paragraph +// CHECK-NEXT: (CXComment_Text Text=[ //k_ref/doc/uid/XX30000905-CH204 Programming] HasTrailingNewline) + +// rdar://12379053 +/*! +\arg \c AlignLeft left alignment. +\li \c AlignRight right alignment. + + No other types of alignment are supported. +*/ +struct S { + int AlignLeft; + int AlignRight; +}; + +// CHECK: (CXComment_BlockCommand CommandName=[arg] +// CHECK-NEXT: (CXComment_Paragraph +// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace) +// CHECK-NEXT: (CXComment_InlineCommand CommandName=[c] RenderMonospaced Arg[0]=AlignLeft) +// CHECK-NEXT: (CXComment_Text Text=[ left alignment.] HasTrailingNewline))) +// CHECK: (CXComment_BlockCommand CommandName=[li] +// CHECK-NEXT: (CXComment_Paragraph +// CHECK-NEXT: (CXComment_Text Text=[ ] IsWhitespace) +// CHECK-NEXT: (CXComment_InlineCommand CommandName=[c] RenderMonospaced Arg[0]=AlignRight) +// CHECK-NEXT: (CXComment_Text Text=[ right alignment.]))) +// CHECK: (CXComment_Paragraph +// CHECK-NEXT: (CXComment_Text Text=[ No other types of alignment are supported.])) + +// rdar://12379053 +/*! \struct Test + * Normal text. + * + * \par User defined paragraph: + * Contents of the paragraph. + * + * \par + * New paragraph under the same heading. + * + * \note + * This note consists of two paragraphs. + * This is the first paragraph. + * + * \par + * And this is the second paragraph. + * + * More normal text. + */ + +struct Test {int filler;}; + +// CHECK: (CXComment_BlockCommand CommandName=[par] +// CHECK-NEXT: (CXComment_Paragraph +// CHECK-NEXT: (CXComment_Text Text=[ User defined paragraph:] HasTrailingNewline) +// CHECK-NEXT: (CXComment_Text Text=[ Contents of the paragraph.]))) +// CHECK: (CXComment_BlockCommand CommandName=[par] +// CHECK-NEXT: (CXComment_Paragraph +// CHECK-NEXT: (CXComment_Text Text=[ New paragraph under the same heading.]))) +// CHECK: (CXComment_BlockCommand CommandName=[note] +// CHECK-NEXT: (CXComment_Paragraph +// CHECK-NEXT: (CXComment_Text Text=[ This note consists of two paragraphs.] HasTrailingNewline) +// CHECK-NEXT: (CXComment_Text Text=[ This is the first paragraph.]))) +// CHECK: (CXComment_BlockCommand CommandName=[par] +// CHECK-NEXT: (CXComment_Paragraph +// CHECK-NEXT: (CXComment_Text Text=[ And this is the second paragraph.]))) diff --git a/test/Index/comment-unqualified-objc-pointer.m b/test/Index/comment-unqualified-objc-pointer.m new file mode 100644 index 00000000000..546d4fa9f07 --- /dev/null +++ b/test/Index/comment-unqualified-objc-pointer.m @@ -0,0 +1,36 @@ +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: c-index-test -test-load-source all -comments-xml-schema=%S/../../bindings/xml/comment-xml-schema.rng -target x86_64-apple-darwin10 -fobjc-default-synthesize-properties -fobjc-arc %s > %t/out +// RUN: FileCheck %s < %t/out +// rdar://13757500 + +@class NSString; + +@interface NSArray @end + +@interface NSMutableArray : NSArray +{ +//! This is the name. + NSString *Name; +} +//! This is WithLabel comment. +- (NSString *)WithLabel:(NSString * const)label; +// CHECK: - (NSString *)WithLabel:(NSString *const)label; + +//! This is a property to get the Name. +@property (copy) NSString *Name; +// CHECK: @property(readwrite, copy, atomic) NSString *Name; +@end + +@implementation NSMutableArray +{ +//! This is private ivar + NSString *NickName; +// CHECK: NSString *NickName +} + +- (NSString *)WithLabel:(NSString * const)label { + return 0; +} +@synthesize Name = Name; +@end diff --git a/test/Index/comment-with-preamble.c b/test/Index/comment-with-preamble.c new file mode 100644 index 00000000000..72e6140d129 --- /dev/null +++ b/test/Index/comment-with-preamble.c @@ -0,0 +1,13 @@ +// Make sure the preable does not truncate comments. + +#ifndef BAZ +#define BAZ 3 +#endif + +//! Foo’s description. +void Foo(); + +// RUN: c-index-test -test-load-source-reparse 1 local %s | FileCheck %s +// RUN: env CINDEXTEST_EDITING=1 c-index-test -test-load-source-reparse 1 local %s | FileCheck %s + +// CHECK: FunctionDecl=Foo:8:6 RawComment=[//! Foo’s description.] RawCommentRange=[7:1 - 7:25] BriefComment=[Foo’s description.] diff --git a/test/Index/get-cursor.cpp b/test/Index/get-cursor.cpp index 8b70216dd16..996ecc25ef4 100644 --- a/test/Index/get-cursor.cpp +++ b/test/Index/get-cursor.cpp @@ -47,6 +47,24 @@ void test() { }; } +template +struct TS { + void foo(); +}; + +template +void TS::foo() {} + +template +class TC { + void init(); +}; + +template<> void TC::init(); + +#define EXTERN_TEMPLATE(...) extern template __VA_ARGS__; +EXTERN_TEMPLATE(class TC) + // RUN: c-index-test -cursor-at=%s:6:4 %s | FileCheck -check-prefix=CHECK-COMPLETION-1 %s // CHECK-COMPLETION-1: CXXConstructor=X:6:3 // CHECK-COMPLETION-1-NEXT: Completion string: {TypedText X}{LeftParen (}{Placeholder int}{Comma , }{Placeholder int}{RightParen )} @@ -103,3 +121,10 @@ void test() { // RUN: c-index-test -cursor-at=%s:45:9 %s | FileCheck -check-prefix=CHECK-LOCALCLASS %s // CHECK-LOCALCLASS: 45:9 DeclRefExpr=x:44:11 Extent=[45:9 - 45:10] Spelling=x ([45:9 - 45:10]) + +// RUN: c-index-test -cursor-at=%s:50:23 -cursor-at=%s:55:23 %s | FileCheck -check-prefix=CHECK-TEMPLPARAM %s +// CHECK-TEMPLPARAM: 50:23 TypeRef=struct X:3:8 Extent=[50:23 - 50:24] Spelling=struct X ([50:23 - 50:24]) +// CHECK-TEMPLPARAM: 55:23 TypeRef=struct X:3:8 Extent=[55:23 - 55:24] Spelling=struct X ([55:23 - 55:24]) + +// RUN: c-index-test -cursor-at=%s:66:23 %s | FileCheck -check-prefix=CHECK-TEMPLSPEC %s +// CHECK-TEMPLSPEC: 66:23 ClassDecl=TC:66:23 (Definition) [Specialization of TC:59:7] Extent=[66:1 - 66:31] Spelling=TC ([66:23 - 66:25]) diff --git a/test/Index/index-refs.m b/test/Index/index-refs.m index b82345f9c68..f25013b882f 100644 --- a/test/Index/index-refs.m +++ b/test/Index/index-refs.m @@ -13,6 +13,15 @@ void foo() { @encode(struct FooS); } +@interface I ++(void)clsMeth; +@end + +void foo2() { + [I clsMeth]; +} + // RUN: c-index-test -index-file %s | FileCheck %s // CHECK: [indexEntityReference]: kind: objc-protocol | name: Prot | {{.*}} | loc: 12:27 // CHECK: [indexEntityReference]: kind: struct | name: FooS | {{.*}} | loc: 13:18 +// CHECK: [indexEntityReference]: kind: objc-class | name: I | {{.*}} | loc: 21:4 diff --git a/test/Index/load-classes.cpp b/test/Index/load-classes.cpp index 58770191ea9..db7b48f7efc 100644 --- a/test/Index/load-classes.cpp +++ b/test/Index/load-classes.cpp @@ -3,7 +3,9 @@ struct X { X(int value); X(const X& x); +protected: ~X(); +private: operator X*(); }; @@ -11,18 +13,18 @@ X::X(int value) { } // RUN: c-index-test -test-load-source all %s | FileCheck %s -// CHECK: load-classes.cpp:3:8: StructDecl=X:3:8 (Definition) Extent=[3:1 - 8:2] -// CHECK: load-classes.cpp:4:3: CXXConstructor=X:4:3 Extent=[4:3 - 4:15] +// CHECK: load-classes.cpp:3:8: StructDecl=X:3:8 (Definition) Extent=[3:1 - 10:2] +// CHECK: load-classes.cpp:4:3: CXXConstructor=X:4:3 Extent=[4:3 - 4:15] [access=public] // FIXME: missing TypeRef in the constructor name // CHECK: load-classes.cpp:4:9: ParmDecl=value:4:9 (Definition) Extent=[4:5 - 4:14] -// CHECK: load-classes.cpp:5:3: CXXConstructor=X:5:3 Extent=[5:3 - 5:16] +// CHECK: load-classes.cpp:5:3: CXXConstructor=X:5:3 Extent=[5:3 - 5:16] [access=public] // FIXME: missing TypeRef in the constructor name // CHECK: load-classes.cpp:5:14: ParmDecl=x:5:14 (Definition) Extent=[5:5 - 5:15] // CHECK: load-classes.cpp:5:11: TypeRef=struct X:3:8 Extent=[5:11 - 5:12] -// CHECK: load-classes.cpp:6:3: CXXDestructor=~X:6:3 Extent=[6:3 - 6:7] +// CHECK: load-classes.cpp:7:3: CXXDestructor=~X:7:3 Extent=[7:3 - 7:7] [access=protected] // FIXME: missing TypeRef in the destructor name -// CHECK: load-classes.cpp:7:3: CXXConversion=operator struct X *:7:3 Extent=[7:3 - 7:16] -// CHECK: load-classes.cpp:7:12: TypeRef=struct X:3:8 Extent=[7:12 - 7:13] -// CHECK: load-classes.cpp:10:4: CXXConstructor=X:10:4 (Definition) Extent=[10:1 - 11:2] -// CHECK: load-classes.cpp:10:1: TypeRef=struct X:3:8 Extent=[10:1 - 10:2] -// CHECK: load-classes.cpp:10:10: ParmDecl=value:10:10 (Definition) Extent=[10:6 - 10:15] +// CHECK: load-classes.cpp:9:3: CXXConversion=operator struct X *:9:3 Extent=[9:3 - 9:16] [access=private] +// CHECK: load-classes.cpp:9:12: TypeRef=struct X:3:8 Extent=[9:12 - 9:13] +// CHECK: load-classes.cpp:12:4: CXXConstructor=X:12:4 (Definition) Extent=[12:1 - 13:2] [access=public] +// CHECK: load-classes.cpp:12:1: TypeRef=struct X:3:8 Extent=[12:1 - 12:2] +// CHECK: load-classes.cpp:12:10: ParmDecl=value:12:10 (Definition) Extent=[12:6 - 12:15] diff --git a/test/Index/parse-all-comments.c b/test/Index/parse-all-comments.c new file mode 100644 index 00000000000..f8b0449f200 --- /dev/null +++ b/test/Index/parse-all-comments.c @@ -0,0 +1,62 @@ +// Run lines are sensitive to line numbers and come below the code. + +#ifndef HEADER +#define HEADER + +// Not a Doxygen comment. notdoxy1 NOT_DOXYGEN +void notdoxy1(void); + +/* Not a Doxygen comment. notdoxy2 NOT_DOXYGEN */ +void notdoxy2(void); + +/*/ Not a Doxygen comment. notdoxy3 NOT_DOXYGEN */ +void notdoxy3(void); + +/** Doxygen comment. isdoxy4 IS_DOXYGEN_SINGLE */ +void isdoxy4(void); + +/*! Doxygen comment. isdoxy5 IS_DOXYGEN_SINGLE */ +void isdoxy5(void); + +/// Doxygen comment. isdoxy6 IS_DOXYGEN_SINGLE +void isdoxy6(void); + +/* BLOCK_ORDINARY_COMMENT */ +// ORDINARY COMMENT +/// This is a BCPL comment. IS_DOXYGEN_START +/// It has only two lines. +/** But there are other blocks that are part of the comment, too. IS_DOXYGEN_END */ +void multi_line_comment_plus_ordinary(int); + +// MULTILINE COMMENT +// +// WITH EMPTY LINE +void multi_line_comment_empty_line(int); + +#endif + +// RUN: rm -rf %t +// RUN: mkdir %t + +// RUN: %clang_cc1 -fparse-all-comments -x c++ -std=c++11 -emit-pch -o %t/out.pch %s + +// RUN: c-index-test -test-load-source all -comments-xml-schema=%S/../../bindings/xml/comment-xml-schema.rng %s -std=c++11 -fparse-all-comments > %t/out.c-index-direct +// RUN: c-index-test -test-load-tu %t/out.pch all > %t/out.c-index-pch + +// RUN: FileCheck %s -check-prefix=WRONG < %t/out.c-index-direct +// RUN: FileCheck %s -check-prefix=WRONG < %t/out.c-index-pch + +// Ensure that XML is not invalid +// WRONG-NOT: CommentXMLInvalid + +// RUN: FileCheck %s < %t/out.c-index-direct +// RUN: FileCheck %s < %t/out.c-index-pch + +// CHECK: parse-all-comments.c:7:6: FunctionDecl=notdoxy1:{{.*}} notdoxy1 NOT_DOXYGEN +// CHECK: parse-all-comments.c:10:6: FunctionDecl=notdoxy2:{{.*}} notdoxy2 NOT_DOXYGEN +// CHECK: parse-all-comments.c:13:6: FunctionDecl=notdoxy3:{{.*}} notdoxy3 NOT_DOXYGEN +// CHECK: parse-all-comments.c:16:6: FunctionDecl=isdoxy4:{{.*}} isdoxy4 IS_DOXYGEN_SINGLE +// CHECK: parse-all-comments.c:19:6: FunctionDecl=isdoxy5:{{.*}} isdoxy5 IS_DOXYGEN_SINGLE +// CHECK: parse-all-comments.c:22:6: FunctionDecl=isdoxy6:{{.*}} isdoxy6 IS_DOXYGEN_SINGLE +// CHECK: parse-all-comments.c:29:6: FunctionDecl=multi_line_comment_plus_ordinary:{{.*}} BLOCK_ORDINARY_COMMENT {{.*}} ORDINARY COMMENT {{.*}} IS_DOXYGEN_START {{.*}} IS_DOXYGEN_END +// CHECK: parse-all-comments.c:34:6: FunctionDecl=multi_line_comment_empty_line:{{.*}} MULTILINE COMMENT{{.*}}\n{{.*}}\n{{.*}} WITH EMPTY LINE diff --git a/test/Index/print-type-size.cpp b/test/Index/print-type-size.cpp new file mode 100644 index 00000000000..698d96705bc --- /dev/null +++ b/test/Index/print-type-size.cpp @@ -0,0 +1,428 @@ +// from SemaCXX/class-layout.cpp +// RUN: c-index-test -test-print-type-size %s -target x86_64-pc-linux-gnu | FileCheck -check-prefix=CHECK64 %s +// RUN: c-index-test -test-print-type-size %s -target i386-apple-darwin9 | FileCheck -check-prefix=CHECK32 %s + +namespace basic { + +// CHECK64: VarDecl=v:[[@LINE+2]]:6 (Definition) [type=void] [typekind=Void] +// CHECK32: VarDecl=v:[[@LINE+1]]:6 (Definition) [type=void] [typekind=Void] +void v; + +// CHECK64: VarDecl=v1:[[@LINE+2]]:7 (Definition) [type=void *] [typekind=Pointer] [sizeof=8] [alignof=8] +// CHECK32: VarDecl=v1:[[@LINE+1]]:7 (Definition) [type=void *] [typekind=Pointer] [sizeof=4] [alignof=4] +void *v1; + +// offsetof +// CHECK64: StructDecl=simple:[[@LINE+2]]:8 (Definition) [type=basic::simple] [typekind=Record] [sizeof=48] [alignof=8] +// CHECK32: StructDecl=simple:[[@LINE+1]]:8 (Definition) [type=basic::simple] [typekind=Record] [sizeof=36] [alignof=4] +struct simple { + int a; + char b; +// CHECK64: FieldDecl=c:[[@LINE+1]]:7 (Definition) [type=int] [typekind=Int] [sizeof=4] [alignof=4] [offsetof=40] [BitFieldSize=3] + int c:3; + long d; + int e:5; +// CHECK64: FieldDecl=f:[[@LINE+1]]:7 (Definition) [type=int] [typekind=Int] [sizeof=4] [alignof=4] [offsetof=133] [BitFieldSize=4] + int f:4; +// CHECK64: FieldDecl=g:[[@LINE+2]]:13 (Definition) [type=long long] [typekind=LongLong] [sizeof=8] [alignof=8] [offsetof=192] +// CHECK32: FieldDecl=g:[[@LINE+1]]:13 (Definition) [type=long long] [typekind=LongLong] [sizeof=8] [alignof=4] [offsetof=128] + long long g; +// CHECK64: FieldDecl=h:[[@LINE+1]]:8 (Definition) [type=char] [typekind=Char_S] [sizeof=1] [alignof=1] [offsetof=256] [BitFieldSize=3] + char h:3; + char i:3; + float j; +// CHECK64: FieldDecl=k:[[@LINE+2]]:10 (Definition) [type=char *] [typekind=Pointer] [sizeof=8] [alignof=8] [offsetof=320] +// CHECK32: FieldDecl=k:[[@LINE+1]]:10 (Definition) [type=char *] [typekind=Pointer] [sizeof=4] [alignof=4] [offsetof=256] + char * k; +}; + + +// CHECK64: UnionDecl=u:[[@LINE+2]]:7 (Definition) [type=basic::u] [typekind=Record] [sizeof=48] [alignof=8] +// CHECK32: UnionDecl=u:[[@LINE+1]]:7 (Definition) [type=basic::u] [typekind=Record] [sizeof=36] [alignof=4] +union u { + int u1; + long long u2; + struct simple s1; +}; + +// CHECK64: VarDecl=s1:[[@LINE+2]]:8 (Definition) [type=basic::simple] [typekind=Record] [sizeof=48] [alignof=8] +// CHECK32: VarDecl=s1:[[@LINE+1]]:8 (Definition) [type=basic::simple] [typekind=Record] [sizeof=36] [alignof=4] +simple s1; + +struct Test { + struct { + union { +//CHECK64: FieldDecl=foo:[[@LINE+1]]:11 (Definition) [type=int] [typekind=Int] [sizeof=4] [alignof=4] [offsetof=0] + int foo; + }; + }; +}; + +struct Test2 { + struct { + struct { +//CHECK64: FieldDecl=foo:[[@LINE+1]]:11 (Definition) [type=int] [typekind=Int] [sizeof=4] [alignof=4] [offsetof=0] + int foo; + }; + struct { +//CHECK64: FieldDecl=bar:[[@LINE+1]]:11 (Definition) [type=int] [typekind=Int] [sizeof=4] [alignof=4] [offsetof=32] + int bar; + }; + struct { + struct { +//CHECK64: FieldDecl=foobar:[[@LINE+1]]:15 (Definition) [type=int] [typekind=Int] [sizeof=4] [alignof=4] [offsetof=64] + int foobar; + }; + }; + struct inner { + struct { +//CHECK64: FieldDecl=mybar:[[@LINE+1]]:15 (Definition) [type=int] [typekind=Int] [sizeof=4] [alignof=4] [offsetof=0] + int mybar; + }; +//CHECK64: FieldDecl=mole:[[@LINE+1]]:7 (Definition) [type=struct inner] [typekind=Unexposed] [sizeof=4] [alignof=4] [offsetof=96] + } mole; + }; +}; + +} + +// these are test crash. Offsetof return values are not important. +namespace Incomplete { +// test that fields in incomplete named record do not crash +union named { + struct forward_decl f1; +//CHECK64: FieldDecl=f2:[[@LINE+1]]:7 (Definition) [type=int] [typekind=Int] [sizeof=4] [alignof=4] [offsetof=-2] + int f2; + struct x { +//CHECK64: FieldDecl=g1:[[@LINE+1]]:9 (Definition) [type=int] [typekind=Int] [sizeof=4] [alignof=4] [offsetof=0] + int g1; +//CHECK64: FieldDecl=f3:[[@LINE+1]]:5 (Definition) [type=struct x] [typekind=Unexposed] [sizeof=4] [alignof=4] [offsetof=-2] + } f3; + struct forward_decl f4; + struct x2{ + int g2; + struct forward_decl g3; + } f5; +}; + +// test that fields in incomplete anonymous record do not crash +union f { +//CHECK64: FieldDecl=f1:[[@LINE+1]]:23 (Definition) [type=struct forward_decl] [typekind=Unexposed] [sizeof=-2] [alignof=-2] [offsetof=-2] + struct forward_decl f1; +//CHECK64: FieldDecl=f2:[[@LINE+1]]:7 (Definition) [type=int] [typekind=Int] [sizeof=4] [alignof=4] [offsetof=-2] + int f2; + struct { +//CHECK64: FieldDecl=e1:[[@LINE+1]]:9 (Definition) [type=int] [typekind=Int] [sizeof=4] [alignof=4] [offsetof=-2] + int e1; + struct { +//CHECK64: FieldDecl=g1:[[@LINE+1]]:28 (Definition) [type=struct forward_decl2] [typekind=Unexposed] [sizeof=-2] [alignof=-2] [offsetof=-2] + struct forward_decl2 g1; + }; +//CHECK64: FieldDecl=e3:[[@LINE+1]]:9 (Definition) [type=int] [typekind=Int] [sizeof=4] [alignof=4] [offsetof=-2] + int e3; + }; +}; + + +// incomplete not in root level, in named record +struct s1 { + struct { + struct forward_decl2 s1_g1; +//CHECK64: FieldDecl=s1_e1:[[@LINE+1]]:9 (Definition) [type=int] [typekind=Int] [sizeof=4] [alignof=4] [offsetof=-2] + int s1_e1; + } s1_x; // named record shows in s1->field_iterator +//CHECK64: FieldDecl=s1_e3:[[@LINE+1]]:7 (Definition) [type=int] [typekind=Int] [sizeof=4] [alignof=4] [offsetof=-2] + int s1_e3; +}; + +// incomplete not in root level, in anonymous record +struct s1b { + struct { + struct forward_decl2 s1b_g1; + }; // erroneous anonymous record does not show in s1b->field_iterator +//CHECK64: FieldDecl=s1b_e2:[[@LINE+1]]:7 (Definition) [type=int] [typekind=Int] [sizeof=4] [alignof=4] [offsetof=0] + int s1b_e2; +}; + +struct s2 { + struct { + struct forward_decl2 s2_g1; +//CHECK64: FieldDecl=s2_e1:[[@LINE+1]]:9 (Definition) [type=int] [typekind=Int] [sizeof=4] [alignof=4] [offsetof=-5] + int s2_e1; + }; // erroneous anonymous record does not show in s1b->field_iterator +//CHECK64: FieldDecl=s2_e3:[[@LINE+1]]:7 (Definition) [type=int] [typekind=Int] [sizeof=4] [alignof=4] [offsetof=0] + int s2_e3; +}; + +//deep anonymous with deep level incomplete +struct s3 { + struct { + int s3_e1; + struct { + struct { + struct { + struct { + struct forward_decl2 s3_g1; + }; + }; + }; + }; +//CHECK64: FieldDecl=s3_e3:[[@LINE+1]]:9 (Definition) [type=int] [typekind=Int] [sizeof=4] [alignof=4] [offsetof=64] + int s3_e3; + }; +}; + +//deep anonymous with first level incomplete +struct s4a { + struct forward_decl2 g1; + struct { + struct forward_decl2 g2; + struct { + struct { + struct { + struct { +//CHECK64: FieldDecl=s4_e1:[[@LINE+1]]:17 (Definition) [type=int] [typekind=Int] [sizeof=4] [alignof=4] [offsetof=-2] + int s4_e1; + }; + }; + }; + }; +//CHECK64: FieldDecl=s4_e3:[[@LINE+1]]:9 (Definition) [type=int] [typekind=Int] [sizeof=4] [alignof=4] [offsetof=-2] + int s4_e3; + }; +}; + +//deep anonymous with sub-first-level incomplete +struct s4b { + struct { + struct forward_decl2 g1; + struct { + struct { + struct { + struct { +//CHECK64: FieldDecl=s4b_e1:[[@LINE+1]]:17 (Definition) [type=int] [typekind=Int] [sizeof=4] [alignof=4] [offsetof=-5] + int s4b_e1; + }; + }; + }; + }; +//CHECK64: FieldDecl=s4b_e3:[[@LINE+1]]:9 (Definition) [type=int] [typekind=Int] [sizeof=4] [alignof=4] [offsetof=-5] + int s4b_e3; + }; +}; + +// CHECK64: StructDecl=As:[[@LINE+1]]:8 [type=Incomplete::As] [typekind=Record] +struct As; + +// undefined class. Should not crash +// CHECK64: ClassDecl=A:[[@LINE+1]]:7 [type=Incomplete::A] [typekind=Record] +class A; +// CHECK64: ClassDecl=B:[[@LINE+1]]:7 (Definition) [type=Incomplete::B] [typekind=Record] [sizeof=16] [alignof=8] +class B { +// CHECK64: FieldDecl=a1:[[@LINE+2]]:6 (Definition) [type=Incomplete::A *] [typekind=Pointer] [sizeof=8] [alignof=8] [offsetof=0] +// CHECK32: FieldDecl=a1:[[@LINE+1]]:6 (Definition) [type=Incomplete::A *] [typekind=Pointer] [sizeof=4] [alignof=4] [offsetof=0] + A* a1; +// CHECK64: FieldDecl=a2:[[@LINE+2]]:6 (Definition) [type=Incomplete::A &] [typekind=LValueReference] [sizeof=-2] [alignof=-2] [offsetof=64] +// CHECK32: FieldDecl=a2:[[@LINE+1]]:6 (Definition) [type=Incomplete::A &] [typekind=LValueReference] [sizeof=-2] [alignof=-2] [offsetof=32] + A& a2; +}; + +} + +namespace Sizes { + +// CHECK64: StructDecl=A:[[@LINE+2]]:8 (Definition) [type=Sizes::A] [typekind=Record] [sizeof=8] [alignof=4] +// CHECK32: StructDecl=A:[[@LINE+1]]:8 (Definition) [type=Sizes::A] [typekind=Record] [sizeof=8] [alignof=4] +struct A { + int a; + char b; +}; + +// CHECK64: StructDecl=B:[[@LINE+2]]:8 (Definition) [type=Sizes::B] [typekind=Record] [sizeof=12] [alignof=4] +// CHECK32: StructDecl=B:[[@LINE+1]]:8 (Definition) [type=Sizes::B] [typekind=Record] [sizeof=12] [alignof=4] +struct B : A { + char c; +}; + +// CHECK64: StructDecl=C:[[@LINE+2]]:8 (Definition) [type=Sizes::C] [typekind=Record] [sizeof=8] [alignof=4] +// CHECK32: StructDecl=C:[[@LINE+1]]:8 (Definition) [type=Sizes::C] [typekind=Record] [sizeof=8] [alignof=4] +struct C { +// Make fields private so C won't be a POD type. +private: + int a; + char b; +}; + +// CHECK64: StructDecl=D:[[@LINE+2]]:8 (Definition) [type=Sizes::D] [typekind=Record] [sizeof=8] [alignof=4] +// CHECK32: StructDecl=D:[[@LINE+1]]:8 (Definition) [type=Sizes::D] [typekind=Record] [sizeof=8] [alignof=4] +struct D : C { + char c; +}; + +// CHECK64: StructDecl=E:[[@LINE+2]]:32 (Definition) [type=Sizes::E] [typekind=Record] [sizeof=5] [alignof=1] +// CHECK32: StructDecl=E:[[@LINE+1]]:32 (Definition) [type=Sizes::E] [typekind=Record] [sizeof=5] [alignof=1] +struct __attribute__((packed)) E { + char b; + int a; +}; + +// CHECK64: StructDecl=F:[[@LINE+2]]:32 (Definition) [type=Sizes::F] [typekind=Record] [sizeof=6] [alignof=1] +// CHECK32: StructDecl=F:[[@LINE+1]]:32 (Definition) [type=Sizes::F] [typekind=Record] [sizeof=6] [alignof=1] +struct __attribute__((packed)) F : E { + char d; +}; + +struct G { G(); }; +// CHECK64: StructDecl=H:[[@LINE+2]]:8 (Definition) [type=Sizes::H] [typekind=Record] [sizeof=1] [alignof=1] +// CHECK32: StructDecl=H:[[@LINE+1]]:8 (Definition) [type=Sizes::H] [typekind=Record] [sizeof=1] [alignof=1] +struct H : G { }; + +// CHECK64: StructDecl=I:[[@LINE+2]]:8 (Definition) [type=Sizes::I] [typekind=Record] [sizeof=5] [alignof=1] +// CHECK32: StructDecl=I:[[@LINE+1]]:8 (Definition) [type=Sizes::I] [typekind=Record] [sizeof=5] [alignof=1] +struct I { + char b; + int a; +} __attribute__((packed)); + +} + +namespace Test1 { + +// Test complex class hierarchy +struct A { }; +struct B : A { virtual void b(); }; +class C : virtual A { int c; }; +struct D : virtual B { }; +struct E : C, virtual D { }; +class F : virtual E { }; +// CHECK64: StructDecl=G:[[@LINE+2]]:8 (Definition) [type=Test1::G] [typekind=Record] [sizeof=24] [alignof=8] +// CHECK32: StructDecl=G:[[@LINE+1]]:8 (Definition) [type=Test1::G] [typekind=Record] [sizeof=16] [alignof=4] +struct G : virtual E, F { }; + +} + +namespace Test2 { + +// Test that this somewhat complex class structure is laid out correctly. +struct A { }; +struct B : A { virtual void b(); }; +struct C : virtual B { }; +struct D : virtual A { }; +struct E : virtual B, D { }; +struct F : E, virtual C { }; +struct G : virtual F, A { }; +// CHECK64: StructDecl=H:[[@LINE+2]]:8 (Definition) [type=Test2::H] [typekind=Record] [sizeof=24] [alignof=8] +// CHECK32: StructDecl=H:[[@LINE+1]]:8 (Definition) [type=Test2::H] [typekind=Record] [sizeof=12] [alignof=4] +struct H { G g; }; + +} + +namespace Test3 { +// CHECK64: ClassDecl=B:[[@LINE+2]]:7 (Definition) [type=Test3::B] [typekind=Record] [sizeof=16] [alignof=8] +// CHECK32: ClassDecl=B:[[@LINE+1]]:7 (Definition) [type=Test3::B] [typekind=Record] [sizeof=8] [alignof=4] +class B { +public: + virtual void b(){} +// CHECK64: FieldDecl=b_field:[[@LINE+2]]:8 (Definition) [type=long] [typekind=Long] [sizeof=8] [alignof=8] [offsetof=64] +// CHECK32: FieldDecl=b_field:[[@LINE+1]]:8 (Definition) [type=long] [typekind=Long] [sizeof=4] [alignof=4] [offsetof=32] + long b_field; +protected: +private: +}; + +// CHECK32: ClassDecl=A:[[@LINE+1]]:7 (Definition) [type=Test3::A] [typekind=Record] [sizeof=16] [alignof=4] +class A : public B { +public: +// CHECK64: FieldDecl=a_field:[[@LINE+2]]:7 (Definition) [type=int] [typekind=Int] [sizeof=4] [alignof=4] [offsetof=128] +// CHECK32: FieldDecl=a_field:[[@LINE+1]]:7 (Definition) [type=int] [typekind=Int] [sizeof=4] [alignof=4] [offsetof=64] + int a_field; + virtual void a(){} +// CHECK64: FieldDecl=one:[[@LINE+2]]:8 (Definition) [type=char] [typekind=Char_S] [sizeof=1] [alignof=1] [offsetof=160] +// CHECK32: FieldDecl=one:[[@LINE+1]]:8 (Definition) [type=char] [typekind=Char_S] [sizeof=1] [alignof=1] [offsetof=96] + char one; +protected: +private: +}; + +// CHECK64: ClassDecl=D:[[@LINE+2]]:7 (Definition) [type=Test3::D] [typekind=Record] [sizeof=16] [alignof=8] +// CHECK32: ClassDecl=D:[[@LINE+1]]:7 (Definition) [type=Test3::D] [typekind=Record] [sizeof=12] [alignof=4] +class D { +public: + virtual void b(){} +// CHECK64: FieldDecl=a:[[@LINE+2]]:10 (Definition) [type=double] [typekind=Double] [sizeof=8] [alignof=8] [offsetof=64] +// CHECK32: FieldDecl=a:[[@LINE+1]]:10 (Definition) [type=double] [typekind=Double] [sizeof=8] [alignof=4] [offsetof=32] + double a; +}; + +// CHECK64: ClassDecl=C:[[@LINE+2]]:7 (Definition) [type=Test3::C] [typekind=Record] [sizeof=88] [alignof=8] +// CHECK32: ClassDecl=C:[[@LINE+1]]:7 (Definition) [type=Test3::C] [typekind=Record] [sizeof=60] [alignof=4] +class C : public virtual A, + public D, public B { +public: + double c1_field; + int c2_field; + double c3_field; + int c4_field; + virtual void foo(){} + virtual void bar(){} +protected: +private: +}; + +struct BaseStruct +{ + BaseStruct(){} + double v0; + float v1; +// CHECK64: FieldDecl=fg:[[@LINE+2]]:7 (Definition) [type=Test3::C] [typekind=Record] [sizeof=88] [alignof=8] [offsetof=128] +// CHECK32: FieldDecl=fg:[[@LINE+1]]:7 (Definition) [type=Test3::C] [typekind=Record] [sizeof=60] [alignof=4] [offsetof=96] + C fg; +// CHECK64: FieldDecl=rg:[[@LINE+2]]:8 (Definition) [type=Test3::C &] [typekind=LValueReference] [sizeof=88] [alignof=8] [offsetof=832] +// CHECK32: FieldDecl=rg:[[@LINE+1]]:8 (Definition) [type=Test3::C &] [typekind=LValueReference] [sizeof=60] [alignof=4] [offsetof=576] + C &rg; + int x; +}; + +} + +namespace NotConstantSize { + +void f(int i) { +// CHECK32: VarDecl=v2:[[@LINE+1]]:8 (Definition) [type=int [i]] [typekind=Unexposed] [sizeof=-4] [alignof=4] + int v2[i]; + { + struct CS1 { +// FIXME: should libclang return [offsetof=0] ? +//CHECK32: FieldDecl=f1:[[@LINE+1]]:9 (Definition) [type=int [i]] [typekind=Unexposed] [sizeof=-4] [alignof=4] [offsetof=0] + int f1[i]; +//CHECK32: FieldDecl=f2:[[@LINE+1]]:11 (Definition) [type=float] [typekind=Float] [sizeof=4] [alignof=4] [offsetof=0] + float f2; + }; + } +} + +} + +namespace CrashTest { +// test crash scenarios on dependent types. +template +struct Foo { +//CHECK32: FieldDecl=t:[[@LINE+1]]:5 (Definition) [type=T] [typekind=Unexposed] [sizeof=-3] [alignof=-3] [offsetof=-1] + T t; +//CHECK32: FieldDecl=a:[[@LINE+1]]:7 (Definition) [type=int] [typekind=Int] [sizeof=4] [alignof=4] [offsetof=-1] + int a; +}; + +Foo t1; +Foo t2; + +void c; + +plopplop; + +// CHECK64: StructDecl=lastValid:[[@LINE+2]]:8 (Definition) [type=CrashTest::lastValid] [typekind=Record] [sizeof=1] [alignof=1] +// CHECK32: StructDecl=lastValid:[[@LINE+1]]:8 (Definition) [type=CrashTest::lastValid] [typekind=Record] [sizeof=1] [alignof=1] +struct lastValid { +}; + +} diff --git a/test/Index/print-type.c b/test/Index/print-type.c index 8594994e77e..4805f59f3fe 100644 --- a/test/Index/print-type.c +++ b/test/Index/print-type.c @@ -11,12 +11,12 @@ int __attribute__((vector_size(16))) x; typedef int __attribute__((vector_size(16))) int4_t; // RUN: c-index-test -test-print-type %s | FileCheck %s -// CHECK: FunctionDecl=f:3:6 (Definition) [type=int *(int *, char *, FooType, int *, void (*)(int))] [typekind=FunctionProto] [canonicaltype=int *(int *, char *, int, int *, void (*)(int))] [canonicaltypekind=FunctionProto] [resulttype=int *] [resulttypekind=Pointer] [args= [int *] [Pointer] [char *] [Pointer] [FooType] [Typedef] [int *] [Pointer] [void (*)(int)] [Pointer]] [isPOD=0] +// CHECK: FunctionDecl=f:3:6 (Definition) [type=int *(int *, char *, FooType, int *, void (*)(int))] [typekind=FunctionProto] [canonicaltype=int *(int *, char *, int, int *, void (*)(int))] [canonicaltypekind=FunctionProto] [resulttype=int *] [resulttypekind=Pointer] [args= [int *] [Pointer] [char *] [Pointer] [FooType] [Typedef] [int [5]] [ConstantArray] [void (*)(int)] [Pointer]] [isPOD=0] // CHECK: ParmDecl=p:3:13 (Definition) [type=int *] [typekind=Pointer] [isPOD=1] // CHECK: ParmDecl=x:3:22 (Definition) [type=char *] [typekind=Pointer] [isPOD=1] // CHECK: ParmDecl=z:3:33 (Definition) [type=FooType] [typekind=Typedef] [canonicaltype=int] [canonicaltypekind=Int] [isPOD=1] // CHECK: TypeRef=FooType:1:13 [type=FooType] [typekind=Typedef] [canonicaltype=int] [canonicaltypekind=Int] [isPOD=1] -// CHECK: ParmDecl=arr:3:40 (Definition) [type=int *] [typekind=Pointer] [isPOD=1] +// CHECK: ParmDecl=arr:3:40 (Definition) [type=int [5]] [typekind=ConstantArray] [isPOD=1] // CHECK: IntegerLiteral= [type=int] [typekind=Int] [isPOD=1] // CHECK: ParmDecl=fn:3:55 (Definition) [type=void (*)(int)] [typekind=Pointer] [canonicaltype=void (*)(int)] [canonicaltypekind=Pointer] [isPOD=1] // CHECK: ParmDecl=:3:62 (Definition) [type=int] [typekind=Int] [isPOD=1] diff --git a/test/Index/print-type.cpp b/test/Index/print-type.cpp index b99d1cb02bb..49a05fbbdbd 100644 --- a/test/Index/print-type.cpp +++ b/test/Index/print-type.cpp @@ -26,6 +26,9 @@ struct Bar { template T tbar(int); +template +T tbar(int[5]); + // RUN: c-index-test -test-print-type %s | FileCheck %s // CHECK: Namespace=outer:1:11 (Definition) [type=] [typekind=Invalid] [isPOD=0] // CHECK: ClassTemplate=Foo:4:8 (Definition) [type=] [typekind=Invalid] [isPOD=0] @@ -59,3 +62,5 @@ T tbar(int); // CHECK: TypedefDecl=ArrayType:20:15 (Definition) [type=ArrayType] [typekind=Typedef] [canonicaltype=int [5]] [canonicaltypekind=ConstantArray] [isPOD=1] // CHECK: FunctionTemplate=tbar:27:3 [type=T (int)] [typekind=FunctionProto] [canonicaltype=type-parameter-0-0 (int)] [canonicaltypekind=FunctionProto] [resulttype=T] [resulttypekind=Unexposed] [isPOD=0] // CHECK: TemplateTypeParameter=T:26:20 (Definition) [type=T] [typekind=Unexposed] [canonicaltype=type-parameter-0-0] [canonicaltypekind=Unexposed] [isPOD=0] +// CHECK: FunctionTemplate=tbar:30:3 [type=T (int *)] [typekind=FunctionProto] [canonicaltype=type-parameter-0-0 (int *)] [canonicaltypekind=FunctionProto] [resulttype=T] [resulttypekind=Unexposed] [isPOD=0] +// CHECK: ParmDecl=:30:11 (Definition) [type=int [5]] [typekind=ConstantArray] [isPOD=1] diff --git a/test/Index/print-type.m b/test/Index/print-type.m index 9325c3fbddf..6f146f80202 100644 --- a/test/Index/print-type.m +++ b/test/Index/print-type.m @@ -2,9 +2,14 @@ @property (readonly) id x; -(int) mymethod; -(const id) mymethod2:(id)x blah:(Class)y boo:(SEL)z; +-(bycopy)methodIn:(in int)i andOut:(out short *)j , ...; @end // RUN: c-index-test -test-print-type %s | FileCheck %s -// CHECK: ObjCPropertyDecl=x:2:25 [type=id] [typekind=ObjCId] [canonicaltype=id] [canonicaltypekind=ObjCObjectPointer] [isPOD=1] +// CHECK: ObjCPropertyDecl=x:2:25 [readonly,] [type=id] [typekind=ObjCId] [canonicaltype=id] [canonicaltypekind=ObjCObjectPointer] [isPOD=1] // CHECK: ObjCInstanceMethodDecl=mymethod:3:8 [type=] [typekind=Invalid] [resulttype=int] [resulttypekind=Int] [isPOD=0] // CHECK: ObjCInstanceMethodDecl=mymethod2:blah:boo::4:13 [type=] [typekind=Invalid] [resulttype=const id] [resulttypekind=ObjCId] [args= [id] [ObjCId] [Class] [ObjCClass] [SEL] [ObjCSel]] [isPOD=0] +// CHECK: ParmDecl=z:4:52 (Definition) [type=SEL] [typekind=ObjCSel] [canonicaltype=SEL *] [canonicaltypekind=Pointer] [isPOD=1] +// CHECK: ObjCInstanceMethodDecl=methodIn:andOut::5:10 (variadic) [Bycopy,] [type=] [typekind=Invalid] [resulttype=id] [resulttypekind=ObjCId] [args= [int] [Int] [short *] [Pointer]] [isPOD=0] +// CHECK: ParmDecl=i:5:27 (Definition) [In,] [type=int] [typekind=Int] [isPOD=1] +// CHECK: ParmDecl=j:5:49 (Definition) [Out,] [type=short *] [typekind=Pointer] [isPOD=1] diff --git a/test/Index/properties-class-extensions.m b/test/Index/properties-class-extensions.m index aa992075c66..0fa0ecba6b6 100644 --- a/test/Index/properties-class-extensions.m +++ b/test/Index/properties-class-extensions.m @@ -60,12 +60,12 @@ // CHECK: properties-class-extensions.m:9:15: ParmDecl=b:9:15 (Definition) Extent=[9:15 - 9:16] // CHECK: properties-class-extensions.m:10:10: ObjCInstanceMethodDecl=bar:10:10 Extent=[10:1 - 10:14] // CHECK: properties-class-extensions.m:15:12: ObjCInterfaceDecl=Bar:15:12 Extent=[15:1 - 17:5] -// CHECK: properties-class-extensions.m:16:25: ObjCPropertyDecl=bar:16:25 Extent=[16:1 - 16:28] +// CHECK: properties-class-extensions.m:16:25: ObjCPropertyDecl=bar:16:25 [readonly,] Extent=[16:1 - 16:28] // CHECK: properties-class-extensions.m:16:22: TypeRef=id:0:0 Extent=[16:22 - 16:24] // CHECK: properties-class-extensions.m:16:25: ObjCInstanceMethodDecl=bar:16:25 Extent=[16:25 - 16:28] // CHECK: properties-class-extensions.m:18:12: ObjCCategoryDecl=:18:12 Extent=[18:1 - 20:5] // CHECK: properties-class-extensions.m:18:12: ObjCClassRef=Bar:15:12 Extent=[18:12 - 18:15] -// CHECK: properties-class-extensions.m:19:26: ObjCPropertyDecl=bar:19:26 Extent=[19:1 - 19:29] +// CHECK: properties-class-extensions.m:19:26: ObjCPropertyDecl=bar:19:26 [readwrite,] Extent=[19:1 - 19:29] // CHECK: properties-class-extensions.m:19:23: TypeRef=id:0:0 Extent=[19:23 - 19:25] // CHECK-NOT: properties-class-extensions.m:16:25: ObjCInstanceMethodDecl=bar:16:25 Extent=[16:25 - 16:28] // CHECK: properties-class-extensions.m:19:26: ObjCInstanceMethodDecl=setBar::19:26 Extent=[19:26 - 19:29] @@ -73,7 +73,7 @@ // CHECK: properties-class-extensions.m:24:8: ObjCInterfaceDecl=Rdar8467189_Bar:24:8 Extent=[24:1 - 24:23] // CHECK: properties-class-extensions.m:24:8: ObjCClassRef=Rdar8467189_Bar:24:8 Extent=[24:8 - 24:23] // CHECK: properties-class-extensions.m:25:11: ObjCProtocolDecl=Rdar8467189_FooProtocol:25:11 (Definition) Extent=[25:1 - 27:5] -// CHECK: properties-class-extensions.m:26:39: ObjCPropertyDecl=Rdar8467189_Bar:26:39 Extent=[26:1 - 26:54] +// CHECK: properties-class-extensions.m:26:39: ObjCPropertyDecl=Rdar8467189_Bar:26:39 [readonly,] Extent=[26:1 - 26:54] // CHECK: properties-class-extensions.m:26:22: ObjCClassRef=Rdar8467189_Bar:24:8 Extent=[26:22 - 26:37] // CHECK: properties-class-extensions.m:26:39: ObjCInstanceMethodDecl=Rdar8467189_Bar:26:39 Extent=[26:39 - 26:54] // CHECK: properties-class-extensions.m:28:12: ObjCInterfaceDecl=Rdar8467189_Foo:28:12 Extent=[28:1 - 29:5] @@ -82,7 +82,7 @@ // CHECK-NOT: properties-class-extensions.m:31:23: ObjCClassRef=Rdar8467189_Bar:24:8 Extent=[31:23 - 31:38] // CHECK: properties-class-extensions.m:30:12: ObjCCategoryDecl=:30:12 Extent=[30:1 - 32:5] // CHECK: properties-class-extensions.m:30:12: ObjCClassRef=Rdar8467189_Foo:28:12 Extent=[30:12 - 30:27] -// CHECK: properties-class-extensions.m:31:40: ObjCPropertyDecl=Rdar8467189_Bar:31:40 Extent=[31:1 - 31:55] +// CHECK: properties-class-extensions.m:31:40: ObjCPropertyDecl=Rdar8467189_Bar:31:40 [readwrite,] Extent=[31:1 - 31:55] // CHECK: properties-class-extensions.m:31:23: ObjCClassRef=Rdar8467189_Bar:24:8 Extent=[31:23 - 31:38] // CHECK: properties-class-extensions.m:31:40: ObjCInstanceMethodDecl=Rdar8467189_Bar:31:40 [Overrides @26:39] Extent=[31:40 - 31:55] // CHECK: properties-class-extensions.m:31:40: ObjCInstanceMethodDecl=setRdar8467189_Bar::31:40 Extent=[31:40 - 31:55] @@ -90,7 +90,7 @@ // CHECK: properties-class-extensions.m:35:12: ObjCInterfaceDecl=Qux:35:12 Extent=[35:1 - 36:5] // CHECK: properties-class-extensions.m:37:12: ObjCCategoryDecl=:37:12 Extent=[37:1 - 39:5] // CHECK: properties-class-extensions.m:37:12: ObjCClassRef=Qux:35:12 Extent=[37:12 - 37:15] -// CHECK: properties-class-extensions.m:38:34: ObjCPropertyDecl=qux:38:34 Extent=[38:1 - 38:37] +// CHECK: properties-class-extensions.m:38:34: ObjCPropertyDecl=qux:38:34 [assign,readwrite,] Extent=[38:1 - 38:37] // CHECK: properties-class-extensions.m:38:31: TypeRef=id:0:0 Extent=[38:31 - 38:33] // CHECK: properties-class-extensions.m:38:34: ObjCInstanceMethodDecl=qux:38:34 Extent=[38:34 - 38:37] // CHECK: properties-class-extensions.m:38:34: ObjCInstanceMethodDecl=setQux::38:34 Extent=[38:34 - 38:37] diff --git a/test/Index/subclass-comment.mm b/test/Index/subclass-comment.mm new file mode 100644 index 00000000000..9682a9f71d6 --- /dev/null +++ b/test/Index/subclass-comment.mm @@ -0,0 +1,107 @@ +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: c-index-test -test-load-source all -comments-xml-schema=%S/../../bindings/xml/comment-xml-schema.rng %s > %t/out +// RUN: FileCheck %s < %t/out +// rdar://13647476 + +//! NSObject is root of all. +@interface NSObject +@end +// CHECK: CommentAST=[ +// CHECK-NEXT: (CXComment_FullComment +// CHECK-NEXT: (CXComment_Paragraph +// CHECK-NEXT: (CXComment_Text Text=[ NSObject is root of all.])))] + +//! An umbrella class for super classes. +@interface SuperClass +@end +// CHECK: CommentAST=[ +// CHECK-NEXT: (CXComment_FullComment +// CHECK-NEXT: (CXComment_Paragraph +// CHECK-NEXT: (CXComment_Text Text=[ An umbrella class for super classes.])))] + +@interface SubClass : SuperClass +@end +// CHECK: CommentAST=[ +// CHECK-NEXT: (CXComment_FullComment +// CHECK-NEXT: (CXComment_Paragraph +// CHECK-NEXT: (CXComment_Text Text=[ An umbrella class for super classes.])))] + +@interface SubSubClass : SubClass +@end +// CHECK: CommentAST=[ +// CHECK-NEXT: (CXComment_FullComment +// CHECK-NEXT: (CXComment_Paragraph +// CHECK-NEXT: (CXComment_Text Text=[ An umbrella class for super classes.])))] + +@interface SubSubClass (Private) +@end +// CHECK: CommentAST=[ +// CHECK-NEXT: (CXComment_FullComment +// CHECK-NEXT: (CXComment_Paragraph +// CHECK-NEXT: (CXComment_Text Text=[ An umbrella class for super classes.])))] + +//! Something valuable to the organization. +class Asset { +}; +// CHECK: CommentAST=[ +// CHECK-NEXT: (CXComment_FullComment +// CHECK-NEXT: (CXComment_Paragraph +// CHECK-NEXT: (CXComment_Text Text=[ Something valuable to the organization.])))] + +//! An individual human or human individual. +class Person : public Asset { +}; +// CHECK: CommentAST=[ +// CHECK-NEXT: (CXComment_FullComment +// CHECK-NEXT: (CXComment_Paragraph +// CHECK-NEXT: (CXComment_Text Text=[ An individual human or human individual.])))] + +class Student : public Person { +}; +// CHECK: CommentAST=[ +// CHECK-NEXT: (CXComment_FullComment +// CHECK-NEXT: (CXComment_Paragraph +// CHECK-NEXT: (CXComment_Text Text=[ An individual human or human individual.])))] + +//! Every thing is a part +class Parts { +}; +// CHECK: CommentAST=[ +// CHECK-NEXT: (CXComment_FullComment +// CHECK-NEXT: (CXComment_Paragraph +// CHECK-NEXT: (CXComment_Text Text=[ Every thing is a part])))] + +class Window : public virtual Parts { +}; +// CHECK: CommentAST=[ +// CHECK-NEXT: (CXComment_FullComment +// CHECK-NEXT: (CXComment_Paragraph +// CHECK-NEXT: (CXComment_Text Text=[ Every thing is a part])))] + +class Door : public virtual Parts { +}; +// CHECK: CommentAST=[ +// CHECK-NEXT: (CXComment_FullComment +// CHECK-NEXT: (CXComment_Paragraph +// CHECK-NEXT: (CXComment_Text Text=[ Every thing is a part])))] + +class House : public Window, Door { +}; +// CHECK: CommentAST=[ +// CHECK-NEXT: (CXComment_FullComment +// CHECK-NEXT: (CXComment_Paragraph +// CHECK-NEXT: (CXComment_Text Text=[ Every thing is a part])))] + +//! Any Material +class Material : virtual Parts { +}; + +class Building : Window, public Material { +}; +// CHECK: CommentAST=[ +// CHECK-NEXT: (CXComment_FullComment +// CHECK-NEXT: (CXComment_Paragraph +// CHECK-NEXT: (CXComment_Text Text=[ Any Material])))] + + diff --git a/test/Index/targeted-annotation.c b/test/Index/targeted-annotation.c index cfa1046cc8c..022a139d36d 100644 --- a/test/Index/targeted-annotation.c +++ b/test/Index/targeted-annotation.c @@ -82,10 +82,10 @@ int LocalVar2; // TOP: Identifier: "TARGETED_TOP_H" [2:9 - 2:23] preprocessing directive= // TOP: Punctuation: "#" [3:1 - 3:2] preprocessing directive= // TOP: Identifier: "define" [3:2 - 3:8] preprocessing directive= -// TOP: Identifier: "TARGETED_TOP_H" [3:9 - 3:23] preprocessing directive= -// TOP: Punctuation: "#" [5:1 - 5:2] preprocessing directive= -// TOP: Identifier: "include" [5:2 - 5:9] preprocessing directive= -// TOP: Literal: ""targeted-nested1.h"" [5:10 - 5:30] preprocessing directive= +// TOP: Identifier: "TARGETED_TOP_H" [3:9 - 3:23] macro definition=TARGETED_TOP_H +// TOP: Punctuation: "#" [5:1 - 5:2] inclusion directive=targeted-nested1.h +// TOP: Identifier: "include" [5:2 - 5:9] inclusion directive=targeted-nested1.h +// TOP: Literal: ""targeted-nested1.h"" [5:10 - 5:30] inclusion directive=targeted-nested1.h // TOP: Keyword: "enum" [7:1 - 7:5] EnumDecl=:7:1 (Definition) // TOP: Punctuation: "{" [7:6 - 7:7] EnumDecl=:7:1 (Definition) // TOP: Identifier: "VALUE" [8:3 - 8:8] EnumConstantDecl=VALUE:8:3 (Definition) diff --git a/test/Index/usrs.m b/test/Index/usrs.m index be0e323c936..dccfb758727 100644 --- a/test/Index/usrs.m +++ b/test/Index/usrs.m @@ -86,6 +86,7 @@ int test_multi_declaration(void) { id var_ext; } @property (assign) id pro_ext; +-(int)methodWithFn:(void (*)(int *p))fn; @end // RUN: c-index-test -test-load-source-usrs all -target x86_64-apple-macosx10.7 %s | FileCheck %s @@ -146,7 +147,7 @@ int test_multi_declaration(void) { // CHECK: usrs.m c:objc(pl)P1 Extent=[79:1 - 81:5] // CHECK: usrs.m c:objc(pl)P1(im)method Extent=[80:1 - 80:16] // CHECK: usrs.m c:objc(cs)CWithExt2 Extent=[83:1 - 84:5] -// CHECK: usrs.m c:objc(ext)CWithExt2@usrs.m@1111 Extent=[85:1 - 89:5] +// CHECK: usrs.m c:objc(ext)CWithExt2@usrs.m@1111 Extent=[85:1 - 90:5] // CHECK: usrs.m c:objc(cs)CWithExt2@var_ext Extent=[86:3 - 86:13] // CHECK: usrs.m c:objc(cs)CWithExt2(py)pro_ext Extent=[88:1 - 88:30] // CHECK: usrs.m c:objc(cs)CWithExt2(im)pro_ext Extent=[88:23 - 88:30] @@ -279,4 +280,6 @@ int test_multi_declaration(void) { // CHECK-source: usrs.m:76:10: IntegerLiteral= Extent=[76:10 - 76:11] // CHECK-source: usrs.m:79:11: ObjCProtocolDecl=P1:79:11 (Definition) Extent=[79:1 - 81:5] // CHECK-source: usrs.m:80:9: ObjCInstanceMethodDecl=method:80:9 Extent=[80:1 - 80:16] - +// CHECK-source: usrs.m:89:7: ObjCInstanceMethodDecl=methodWithFn::89:7 Extent=[89:1 - 89:41] +// CHECK-source: usrs.m:89:38: ParmDecl=fn:89:38 (Definition) Extent=[89:21 - 89:40] +// CHECK-source: usrs.m:89:35: ParmDecl=p:89:35 (Definition) Extent=[89:30 - 89:36] diff --git a/test/Lexer/cxx1y_binary_literal.cpp b/test/Lexer/cxx1y_binary_literal.cpp new file mode 100644 index 00000000000..96dce3dd443 --- /dev/null +++ b/test/Lexer/cxx1y_binary_literal.cpp @@ -0,0 +1,19 @@ +// RUN: %clang_cc1 -std=c++1y %s -verify + +static_assert(0b1001 == 9, ""); + +using I = int; +using I = decltype(0b101001); +using ULL = unsigned long long; +using ULL = decltype(0b10101001ULL); + +constexpr unsigned long long operator""_foo(unsigned long long n) { + return n * 2; +} +static_assert(0b10001111_foo == 286, ""); + +int k1 = 0b1234; // expected-error {{invalid digit '2' in binary constant}} +// FIXME: If we ever need to support a standard suffix starting with [a-f], +// we'll need to rework our binary literal parsing rules. +int k2 = 0b10010f; // expected-error {{invalid digit 'f' in binary constant}} +int k3 = 0b10010g; // expected-error {{invalid suffix 'g' on integer constant}} diff --git a/test/Lexer/has_extension_cxx.cpp b/test/Lexer/has_extension_cxx.cpp index 6ffeebda1f5..68b542fb297 100644 --- a/test/Lexer/has_extension_cxx.cpp +++ b/test/Lexer/has_extension_cxx.cpp @@ -47,3 +47,9 @@ int no_local_type_template_args(); #endif // CHECK: has_local_type_template_args + +#if __has_extension(cxx_binary_literals) +int has_binary_literals(); +#endif + +// CHECK: has_binary_literals diff --git a/test/Lexer/has_feature_c1x.c b/test/Lexer/has_feature_c1x.c index c9a5f56ddf3..e26e309c037 100644 --- a/test/Lexer/has_feature_c1x.c +++ b/test/Lexer/has_feature_c1x.c @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -E -std=c1x %s -o - | FileCheck --check-prefix=CHECK-1X %s +// RUN: %clang_cc1 -E -triple x86_64-linux-gnu -std=c1x %s -o - | FileCheck --check-prefix=CHECK-1X %s // RUN: %clang_cc1 -E %s -o - | FileCheck --check-prefix=CHECK-NO-1X %s #if __has_feature(c_atomic) @@ -37,6 +37,15 @@ int no_alignas(); // CHECK-1X: has_alignas // CHECK-NO-1X: no_alignas +#if __has_feature(c_thread_local) +int has_thread_local(); +#else +int no_thread_local(); +#endif + +// CHECK-1X: has_thread_local +// CHECK-NO-1X: no_thread_local + #if __STDC_VERSION__ > 199901L int is_c1x(); #else diff --git a/test/Lexer/has_feature_cxx0x.cpp b/test/Lexer/has_feature_cxx0x.cpp index 8e0222dcecd..62a965caacb 100644 --- a/test/Lexer/has_feature_cxx0x.cpp +++ b/test/Lexer/has_feature_cxx0x.cpp @@ -1,5 +1,7 @@ -// RUN: %clang_cc1 -E -std=c++11 %s -o - | FileCheck --check-prefix=CHECK-0X %s -// RUN: %clang_cc1 -E %s -o - | FileCheck --check-prefix=CHECK-NO-0X %s +// RUN: %clang_cc1 -E -triple x86_64-linux-gnu -std=c++11 %s -o - | FileCheck --check-prefix=CHECK-11 %s +// RUN: %clang_cc1 -E -triple armv7-apple-darwin -std=c++11 %s -o - | FileCheck --check-prefix=CHECK-NO-TLS %s +// RUN: %clang_cc1 -E -triple x86_64-linux-gnu %s -o - | FileCheck --check-prefix=CHECK-NO-11 %s +// RUN: %clang_cc1 -E -triple x86_64-linux-gnu -std=c++1y %s -o - | FileCheck --check-prefix=CHECK-1Y %s #if __has_feature(cxx_atomic) int has_atomic(); @@ -7,8 +9,9 @@ int has_atomic(); int no_atomic(); #endif -// CHECK-0X: has_atomic -// CHECK-NO-0X: no_atomic +// CHECK-1Y: has_atomic +// CHECK-11: has_atomic +// CHECK-NO-11: no_atomic #if __has_feature(cxx_lambdas) int has_lambdas(); @@ -16,8 +19,9 @@ int has_lambdas(); int no_lambdas(); #endif -// CHECK-0X: has_lambdas -// CHECK-NO-0X: no_lambdas +// CHECK-1Y: has_lambdas +// CHECK-11: has_lambdas +// CHECK-NO-11: no_lambdas #if __has_feature(cxx_nullptr) @@ -26,8 +30,9 @@ int has_nullptr(); int no_nullptr(); #endif -// CHECK-0X: has_nullptr -// CHECK-NO-0X: no_nullptr +// CHECK-1Y: has_nullptr +// CHECK-11: has_nullptr +// CHECK-NO-11: no_nullptr #if __has_feature(cxx_decltype) @@ -36,8 +41,9 @@ int has_decltype(); int no_decltype(); #endif -// CHECK-0X: has_decltype -// CHECK-NO-0X: no_decltype +// CHECK-1Y: has_decltype +// CHECK-11: has_decltype +// CHECK-NO-11: no_decltype #if __has_feature(cxx_decltype_incomplete_return_types) @@ -46,8 +52,9 @@ int has_decltype_incomplete_return_types(); int no_decltype_incomplete_return_types(); #endif -// CHECK-0X: has_decltype_incomplete_return_types -// CHECK-NO-0X: no_decltype_incomplete_return_types +// CHECK-1Y: has_decltype_incomplete_return_types +// CHECK-11: has_decltype_incomplete_return_types +// CHECK-NO-11: no_decltype_incomplete_return_types #if __has_feature(cxx_auto_type) @@ -56,8 +63,9 @@ int has_auto_type(); int no_auto_type(); #endif -// CHECK-0X: has_auto_type -// CHECK-NO-0X: no_auto_type +// CHECK-1Y: has_auto_type +// CHECK-11: has_auto_type +// CHECK-NO-11: no_auto_type #if __has_feature(cxx_trailing_return) @@ -66,8 +74,9 @@ int has_trailing_return(); int no_trailing_return(); #endif -// CHECK-0X: has_trailing_return -// CHECK-NO-0X: no_trailing_return +// CHECK-1Y: has_trailing_return +// CHECK-11: has_trailing_return +// CHECK-NO-11: no_trailing_return #if __has_feature(cxx_attributes) @@ -76,8 +85,9 @@ int has_attributes(); int no_attributes(); #endif -// CHECK-0X: has_attributes -// CHECK-NO-0X: no_attributes +// CHECK-1Y: has_attributes +// CHECK-11: has_attributes +// CHECK-NO-11: no_attributes #if __has_feature(cxx_static_assert) @@ -86,8 +96,9 @@ int has_static_assert(); int no_static_assert(); #endif -// CHECK-0X: has_static_assert -// CHECK-NO-0X: no_static_assert +// CHECK-1Y: has_static_assert +// CHECK-11: has_static_assert +// CHECK-NO-11: no_static_assert #if __has_feature(cxx_deleted_functions) int has_deleted_functions(); @@ -95,8 +106,9 @@ int has_deleted_functions(); int no_deleted_functions(); #endif -// CHECK-0X: has_deleted_functions -// CHECK-NO-0X: no_deleted_functions +// CHECK-1Y: has_deleted_functions +// CHECK-11: has_deleted_functions +// CHECK-NO-11: no_deleted_functions #if __has_feature(cxx_defaulted_functions) int has_defaulted_functions(); @@ -104,8 +116,9 @@ int has_defaulted_functions(); int no_defaulted_functions(); #endif -// CHECK-0X: has_defaulted_functions -// CHECK-NO-0X: no_defaulted_functions +// CHECK-1Y: has_defaulted_functions +// CHECK-11: has_defaulted_functions +// CHECK-NO-11: no_defaulted_functions #if __has_feature(cxx_rvalue_references) int has_rvalue_references(); @@ -113,8 +126,9 @@ int has_rvalue_references(); int no_rvalue_references(); #endif -// CHECK-0X: has_rvalue_references -// CHECK-NO-0X: no_rvalue_references +// CHECK-1Y: has_rvalue_references +// CHECK-11: has_rvalue_references +// CHECK-NO-11: no_rvalue_references #if __has_feature(cxx_variadic_templates) @@ -123,8 +137,9 @@ int has_variadic_templates(); int no_variadic_templates(); #endif -// CHECK-0X: has_variadic_templates -// CHECK-NO-0X: no_variadic_templates +// CHECK-1Y: has_variadic_templates +// CHECK-11: has_variadic_templates +// CHECK-NO-11: no_variadic_templates #if __has_feature(cxx_inline_namespaces) @@ -133,8 +148,9 @@ int has_inline_namespaces(); int no_inline_namespaces(); #endif -// CHECK-0X: has_inline_namespaces -// CHECK-NO-0X: no_inline_namespaces +// CHECK-1Y: has_inline_namespaces +// CHECK-11: has_inline_namespaces +// CHECK-NO-11: no_inline_namespaces #if __has_feature(cxx_range_for) @@ -143,8 +159,9 @@ int has_range_for(); int no_range_for(); #endif -// CHECK-0X: has_range_for -// CHECK-NO-0X: no_range_for +// CHECK-1Y: has_range_for +// CHECK-11: has_range_for +// CHECK-NO-11: no_range_for #if __has_feature(cxx_reference_qualified_functions) @@ -153,8 +170,9 @@ int has_reference_qualified_functions(); int no_reference_qualified_functions(); #endif -// CHECK-0X: has_reference_qualified_functions -// CHECK-NO-0X: no_reference_qualified_functions +// CHECK-1Y: has_reference_qualified_functions +// CHECK-11: has_reference_qualified_functions +// CHECK-NO-11: no_reference_qualified_functions #if __has_feature(cxx_default_function_template_args) int has_default_function_template_args(); @@ -162,8 +180,9 @@ int has_default_function_template_args(); int no_default_function_template_args(); #endif -// CHECK-0X: has_default_function_template_args -// CHECK-NO-0X: no_default_function_template_args +// CHECK-1Y: has_default_function_template_args +// CHECK-11: has_default_function_template_args +// CHECK-NO-11: no_default_function_template_args #if __has_feature(cxx_noexcept) int has_noexcept(); @@ -171,8 +190,9 @@ int has_noexcept(); int no_noexcept(); #endif -// CHECK-0X: has_noexcept -// CHECK-NO-0X: no_noexcept +// CHECK-1Y: has_noexcept +// CHECK-11: has_noexcept +// CHECK-NO-11: no_noexcept #if __has_feature(cxx_override_control) int has_override_control(); @@ -180,8 +200,9 @@ int has_override_control(); int no_override_control(); #endif -// CHECK-0X: has_override_control -// CHECK-NO-0X: no_override_control +// CHECK-1Y: has_override_control +// CHECK-11: has_override_control +// CHECK-NO-11: no_override_control #if __has_feature(cxx_alias_templates) int has_alias_templates(); @@ -189,8 +210,9 @@ int has_alias_templates(); int no_alias_templates(); #endif -// CHECK-0X: has_alias_templates -// CHECK-NO-0X: no_alias_templates +// CHECK-1Y: has_alias_templates +// CHECK-11: has_alias_templates +// CHECK-NO-11: no_alias_templates #if __has_feature(cxx_implicit_moves) int has_implicit_moves(); @@ -198,8 +220,9 @@ int has_implicit_moves(); int no_implicit_moves(); #endif -// CHECK-0X: has_implicit_moves -// CHECK-NO-0X: no_implicit_moves +// CHECK-1Y: has_implicit_moves +// CHECK-11: has_implicit_moves +// CHECK-NO-11: no_implicit_moves #if __has_feature(cxx_alignas) int has_alignas(); @@ -207,8 +230,9 @@ int has_alignas(); int no_alignas(); #endif -// CHECK-0X: has_alignas -// CHECK-NO-0X: no_alignas +// CHECK-1Y: has_alignas +// CHECK-11: has_alignas +// CHECK-NO-11: no_alignas #if __has_feature(cxx_raw_string_literals) int has_raw_string_literals(); @@ -216,8 +240,9 @@ int has_raw_string_literals(); int no_raw_string_literals(); #endif -// CHECK-0X: has_raw_string_literals -// CHECK-NO-0X: no_raw_string_literals +// CHECK-1Y: has_raw_string_literals +// CHECK-11: has_raw_string_literals +// CHECK-NO-11: no_raw_string_literals #if __has_feature(cxx_unicode_literals) int has_unicode_literals(); @@ -225,8 +250,9 @@ int has_unicode_literals(); int no_unicode_literals(); #endif -// CHECK-0X: has_unicode_literals -// CHECK-NO-0X: no_unicode_literals +// CHECK-1Y: has_unicode_literals +// CHECK-11: has_unicode_literals +// CHECK-NO-11: no_unicode_literals #if __has_feature(cxx_constexpr) int has_constexpr(); @@ -234,8 +260,9 @@ int has_constexpr(); int no_constexpr(); #endif -// CHECK-0X: has_constexpr -// CHECK-NO-0X: no_constexpr +// CHECK-1Y: has_constexpr +// CHECK-11: has_constexpr +// CHECK-NO-11: no_constexpr #if __has_feature(cxx_generalized_initializers) int has_generalized_initializers(); @@ -243,8 +270,9 @@ int has_generalized_initializers(); int no_generalized_initializers(); #endif -// CHECK-0X: has_generalized_initializers -// CHECK-NO-0X: no_generalized_initializers +// CHECK-1Y: has_generalized_initializers +// CHECK-11: has_generalized_initializers +// CHECK-NO-11: no_generalized_initializers #if __has_feature(cxx_unrestricted_unions) int has_unrestricted_unions(); @@ -252,8 +280,9 @@ int has_unrestricted_unions(); int no_unrestricted_unions(); #endif -// CHECK-0X: has_unrestricted_unions -// CHECK-NO-0X: no_unrestricted_unions +// CHECK-1Y: has_unrestricted_unions +// CHECK-11: has_unrestricted_unions +// CHECK-NO-11: no_unrestricted_unions #if __has_feature(cxx_user_literals) int has_user_literals(); @@ -261,8 +290,9 @@ int has_user_literals(); int no_user_literals(); #endif -// CHECK-0X: has_user_literals -// CHECK-NO-0X: no_user_literals +// CHECK-1Y: has_user_literals +// CHECK-11: has_user_literals +// CHECK-NO-11: no_user_literals #if __has_feature(cxx_local_type_template_args) int has_local_type_template_args(); @@ -270,5 +300,49 @@ int has_local_type_template_args(); int no_local_type_template_args(); #endif -// CHECK-0X: has_local_type_template_args -// CHECK-NO-0X: no_local_type_template_args +// CHECK-1Y: has_local_type_template_args +// CHECK-11: has_local_type_template_args +// CHECK-NO-11: no_local_type_template_args + +#if __has_feature(cxx_inheriting_constructors) +int has_inheriting_constructors(); +#else +int no_inheriting_constructors(); +#endif + +// CHECK-1Y: has_inheriting_constructors +// CHECK-11: has_inheriting_constructors +// CHECK-NO-11: no_inheriting_constructors + +#if __has_feature(cxx_thread_local) +int has_thread_local(); +#else +int no_thread_local(); +#endif + +// CHECK-1Y: has_thread_local +// CHECK-11: has_thread_local +// CHECK-NO-11: no_thread_local +// CHECK-NO-TLS: no_thread_local + +// === C++1y features === + +#if __has_feature(cxx_binary_literals) +int has_binary_literals(); +#else +int no_binary_literals(); +#endif + +// CHECK-1Y: has_binary_literals +// CHECK-11: no_binary_literals +// CHECK-NO-11: no_binary_literals + +#if __has_feature(cxx_aggregate_nsdmi) +int has_aggregate_nsdmi(); +#else +int no_aggregate_nsdmi(); +#endif + +// CHECK-1Y: has_aggregate_nsdmi +// CHECK-11: no_aggregate_nsdmi +// CHECK-NO-11: no_aggregate_nsdmi diff --git a/test/Lexer/pragma-message.c b/test/Lexer/pragma-message.c index b67886fa330..d0bbe9ea3a6 100644 --- a/test/Lexer/pragma-message.c +++ b/test/Lexer/pragma-message.c @@ -14,3 +14,19 @@ #pragma message ":O gcc accepts this! " STRING(__LINE__) // expected-warning {{:O gcc accepts this! 14}} #pragma message(invalid) // expected-error {{expected string literal in pragma message}} + +// GCC supports a similar pragma, #pragma GCC warning (which generates a warning +// message) and #pragma GCC error (which generates an error message). + +#pragma GCC warning(":O I'm a message! " STRING(__LINE__)) // expected-warning {{:O I'm a message! 21}} +#pragma GCC warning ":O gcc accepts this! " STRING(__LINE__) // expected-warning {{:O gcc accepts this! 22}} + +#pragma GCC error(":O I'm a message! " STRING(__LINE__)) // expected-error {{:O I'm a message! 24}} +#pragma GCC error ":O gcc accepts this! " STRING(__LINE__) // expected-error {{:O gcc accepts this! 25}} + +#define COMPILE_ERROR(x) _Pragma(STRING2(GCC error(x))) +COMPILE_ERROR("Compile error at line " STRING(__LINE__) "!"); // expected-error {{Compile error at line 28!}} + +#pragma message // expected-error {{pragma message requires parenthesized string}} +#pragma GCC warning("" // expected-error {{pragma warning requires parenthesized string}} +#pragma GCC error(1) // expected-error {{expected string literal in pragma error}} diff --git a/test/Lexer/pragma-message2.c b/test/Lexer/pragma-message2.c new file mode 100644 index 00000000000..224ccfbbf8f --- /dev/null +++ b/test/Lexer/pragma-message2.c @@ -0,0 +1,19 @@ +// RUN: %clang_cc1 -E -Werror -verify %s 2>&1 | FileCheck %s + +#pragma message "\\test" // expected-warning {{\test}} +// CHECK: #pragma message("\134test") + +#pragma message("\\test") // expected-warning {{\test}} +// CHECK: #pragma message("\134test") + +#pragma GCC warning "\"" "te" "st" "\"" // expected-warning {{"test"}} +// CHECK: #pragma GCC warning "\042test\042" + +#pragma GCC warning("\"" "te" "st" "\"") // expected-warning {{"test"}} +// CHECK: #pragma GCC warning "\042test\042" + +#pragma GCC error "" "[ ]" "" // expected-error {{[ ]}} +// CHECK: #pragma GCC error "[\011]" + +#pragma GCC error("" "[ ]" "") // expected-error {{[ ]}} +// CHECK: #pragma GCC error "[\011]" diff --git a/test/Misc/ast-dump-decl.c b/test/Misc/ast-dump-decl.c index c74da29f6d6..94335b825c8 100644 --- a/test/Misc/ast-dump-decl.c +++ b/test/Misc/ast-dump-decl.c @@ -139,7 +139,7 @@ extern int TestVarDeclSC; // CHECK: VarDecl{{.*}} TestVarDeclSC 'int' extern __thread int TestVarDeclThread; -// CHECK: VarDecl{{.*}} TestVarDeclThread 'int' __thread +// CHECK: VarDecl{{.*}} TestVarDeclThread 'int' tls{{$}} __module_private__ int TestVarDeclPrivate; // CHECK: VarDecl{{.*}} TestVarDeclPrivate 'int' __module_private__ diff --git a/test/Misc/ast-dump-decl.cpp b/test/Misc/ast-dump-decl.cpp index c8f7d2fe6cc..31715cd15e4 100644 --- a/test/Misc/ast-dump-decl.cpp +++ b/test/Misc/ast-dump-decl.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -std=c++11 -fms-extensions -ast-dump -ast-dump-filter Test %s | FileCheck -check-prefix CHECK -strict-whitespace %s +// RUN: %clang_cc1 -std=c++11 -triple x86_64-linux-gnu -fms-extensions -ast-dump -ast-dump-filter Test %s | FileCheck -check-prefix CHECK -strict-whitespace %s class testEnumDecl { enum class TestEnumDeclScoped; @@ -92,6 +92,9 @@ class TestCXXRecordDeclPack : public T... { // CHECK-NEXT: public 'T'... // CHECK-NEXT: CXXRecordDecl{{.*}} class TestCXXRecordDeclPack +thread_local int TestThreadLocalInt; +// CHECK: TestThreadLocalInt {{.*}} tls_dynamic + __module_private__ class TestCXXRecordDeclPrivate; // CHECK: CXXRecordDecl{{.*}} class TestCXXRecordDeclPrivate __module_private__ diff --git a/test/Misc/warn-in-system-header.c b/test/Misc/warn-in-system-header.c index 6e0237d0dcd..132f083af7b 100644 --- a/test/Misc/warn-in-system-header.c +++ b/test/Misc/warn-in-system-header.c @@ -1,4 +1,4 @@ // RUN: %clang_cc1 -isystem %S %s -fsyntax-only -verify #include -// expected-warning {{the cake is a lie}} +// expected-warning@warn-in-system-header.h:4 {{the cake is a lie}} diff --git a/test/Modules/Inputs/ModuleDiags/has_errors.h b/test/Modules/Inputs/ModuleDiags/has_errors.h new file mode 100644 index 00000000000..2c0929a6f58 --- /dev/null +++ b/test/Modules/Inputs/ModuleDiags/has_errors.h @@ -0,0 +1,2 @@ +static void foo(void) { } +static void foo(void) { } diff --git a/test/Modules/Inputs/ModuleDiags/has_warnings.h b/test/Modules/Inputs/ModuleDiags/has_warnings.h new file mode 100644 index 00000000000..87112be6952 --- /dev/null +++ b/test/Modules/Inputs/ModuleDiags/has_warnings.h @@ -0,0 +1,3 @@ + +int int_val; +float *float_ptr = &int_val; diff --git a/test/Modules/Inputs/ModuleDiags/module.map b/test/Modules/Inputs/ModuleDiags/module.map new file mode 100644 index 00000000000..09b25088e67 --- /dev/null +++ b/test/Modules/Inputs/ModuleDiags/module.map @@ -0,0 +1,7 @@ +module HasWarnings { + header "has_warnings.h" +} + +module HasErrors { + header "has_errors.h" +} diff --git a/test/Modules/Inputs/System/usr/include/dbl_max.h b/test/Modules/Inputs/System/usr/include/dbl_max.h new file mode 100644 index 00000000000..9a020d1bf53 --- /dev/null +++ b/test/Modules/Inputs/System/usr/include/dbl_max.h @@ -0,0 +1 @@ +#define DBL_MAX __DBL_MAX__ diff --git a/test/Modules/Inputs/System/usr/include/module.map b/test/Modules/Inputs/System/usr/include/module.map index 884b59c80cd..9b2f3af2bac 100644 --- a/test/Modules/Inputs/System/usr/include/module.map +++ b/test/Modules/Inputs/System/usr/include/module.map @@ -19,3 +19,14 @@ module cstd [system] { header "stdint.h" } } + +module other_constants { + explicit module dbl_max { + header "dbl_max.h" + } +} + +module uses_other_constants { + header "uses_other_constants.h" + export * +} diff --git a/test/Modules/Inputs/System/usr/include/uses_other_constants.h b/test/Modules/Inputs/System/usr/include/uses_other_constants.h new file mode 100644 index 00000000000..f6d4cca9f8f --- /dev/null +++ b/test/Modules/Inputs/System/usr/include/uses_other_constants.h @@ -0,0 +1,3 @@ +@import other_constants; +#include + diff --git a/test/Modules/auto-module-import.m b/test/Modules/auto-module-import.m index 4bd3c5279ce..73518281820 100644 --- a/test/Modules/auto-module-import.m +++ b/test/Modules/auto-module-import.m @@ -1,10 +1,10 @@ -// other file: expected-note{{'no_umbrella_A_private' declared here}} - // RUN: rm -rf %t // RUN: %clang_cc1 -Wauto-import -fmodules-cache-path=%t -fmodules -F %S/Inputs %s -verify #include // expected-warning{{treating #include as an import of module 'DependsOnModule'}} +// expected-note@Inputs/NoUmbrella.framework/PrivateHeaders/A_Private.h:1{{'no_umbrella_A_private' declared here}} + #ifdef MODULE_H_MACRO # error MODULE_H_MACRO should have been hidden #endif diff --git a/test/Modules/autolink.m b/test/Modules/autolink.m index 7f75473cbbd..4bf9d592a8f 100644 --- a/test/Modules/autolink.m +++ b/test/Modules/autolink.m @@ -1,5 +1,6 @@ // RUN: rm -rf %t -// RUN: %clang_cc1 -emit-llvm -o - -fmodules-cache-path=%t -fmodules -fmodules-autolink -F %S/Inputs -I %S/Inputs %s | FileCheck %s +// RUN: %clang_cc1 -emit-llvm -o - -fmodules-cache-path=%t -fmodules -F %S/Inputs -I %S/Inputs %s | FileCheck %s +// RUN: %clang_cc1 -emit-llvm -fno-autolink -o - -fmodules-cache-path=%t -fmodules -F %S/Inputs -I %S/Inputs %s | FileCheck --check-prefix=CHECK-AUTOLINK-DISABLED %s @import autolink.sub2; @@ -38,3 +39,6 @@ int use_no_umbrella() { // CHECK: ![[DEPENDSONMODULE]] = metadata !{metadata !"-framework", metadata !"DependsOnModule"} // CHECK: ![[MODULE]] = metadata !{metadata !"-framework", metadata !"Module"} // CHECK: ![[NOUMBRELLA]] = metadata !{metadata !"-framework", metadata !"NoUmbrella"} + +// CHECK-AUTOLINK-DISABLED: !llvm.module.flags +// CHECK-AUTOLINK-DISABLED-NOT: "Linker Options" diff --git a/test/Modules/compiler_builtins.m b/test/Modules/compiler_builtins.m index 5ea7d795c73..4b8cb5bdc5d 100644 --- a/test/Modules/compiler_builtins.m +++ b/test/Modules/compiler_builtins.m @@ -2,7 +2,6 @@ // RUN: %clang -fsyntax-only -fmodules -fmodules-cache-path=%t -D__need_wint_t %s -Xclang -verify // RUN: %clang -fsyntax-only -std=c99 -fmodules -fmodules-cache-path=%t -D__need_wint_t %s -Xclang -verify // expected-no-diagnostics -// XFAIL: win32 #ifdef __SSE__ @import _Builtin_intrinsics.intel.sse; diff --git a/test/Modules/cstd.m b/test/Modules/cstd.m index 6d896a91557..3d1dcf38e33 100644 --- a/test/Modules/cstd.m +++ b/test/Modules/cstd.m @@ -1,6 +1,9 @@ // RUN: rm -rf %t // RUN: %clang -fsyntax-only -isystem %S/Inputs/System/usr/include -fmodules -fmodules-cache-path=%t -D__need_wint_t -Werror=implicit-function-declaration %s +@import uses_other_constants; +const double other_value = DBL_MAX; + // Supplied by compiler, but referenced from the "/usr/include" module map. @import cstd.float_constants; @@ -16,7 +19,7 @@ void test_fprintf(FILE *file) { // Supplied by compiler, which forwards to the "/usr/include" version. @import cstd.stdint; -my_awesome_nonstandard_integer_type value; +my_awesome_nonstandard_integer_type value2; // Supplied by the compiler; that version wins. @import cstd.stdbool; @@ -25,5 +28,3 @@ my_awesome_nonstandard_integer_type value; # error "bool was not defined!" #endif - - diff --git a/test/Modules/cycles.c b/test/Modules/cycles.c index 4326e76a75f..5f83092c95f 100644 --- a/test/Modules/cycles.c +++ b/test/Modules/cycles.c @@ -6,8 +6,8 @@ // CHECK: While building module 'MutuallyRecursive1' imported from // CHECK: While building module 'MutuallyRecursive2' imported from // CHECK: MutuallyRecursive2.h:3:9: fatal error: cyclic dependency in module 'MutuallyRecursive1': MutuallyRecursive1 -> MutuallyRecursive2 -> MutuallyRecursive1 -// CHECK: While building module 'MutuallyRecursive1' imported from // CHECK: MutuallyRecursive1.h:2:9: fatal error: could not build module 'MutuallyRecursive2' // CHECK: cycles.c:4:9: fatal error: could not build module 'MutuallyRecursive1' -// CHECK-NOT: error: +// CHECK: 3 errors generated + diff --git a/test/Modules/decldef.m b/test/Modules/decldef.m index 7fb8a61386e..7ed82b57e9c 100644 --- a/test/Modules/decldef.m +++ b/test/Modules/decldef.m @@ -1,8 +1,7 @@ // RUN: rm -rf %t // RUN: %clang_cc1 -fmodules -fobjc-arc -I %S/Inputs -fmodules-cache-path=%t %s -verify - -// In other file: expected-note {{previous definition is here}} +// expected-note@Inputs/def.h:5 {{previous definition is here}} @class Def; Def *def; diff --git a/test/Modules/decldef.mm b/test/Modules/decldef.mm index 732c2a27e23..593f53b2c6c 100644 --- a/test/Modules/decldef.mm +++ b/test/Modules/decldef.mm @@ -1,8 +1,7 @@ // RUN: rm -rf %t // RUN: %clang_cc1 -fmodules -fobjc-arc -I %S/Inputs -fmodules-cache-path=%t %s -verify - -// In other file: expected-note {{previous definition is here}} +// expected-note@Inputs/def.h:5 {{previous definition is here}} @class Def; Def *def; diff --git a/test/Modules/diamond-pch.c b/test/Modules/diamond-pch.c index 079f6afa9c7..e7ad02dbe48 100644 --- a/test/Modules/diamond-pch.c +++ b/test/Modules/diamond-pch.c @@ -1,14 +1,20 @@ - - - -// in diamond-bottom.h: expected-note{{passing argument to parameter 'x' here}} +// RUN: rm -rf %t +// RUN: %clang_cc1 -fmodules -x objective-c -emit-module -fmodules-cache-path=%t -fmodule-name=diamond_top %S/Inputs/module.map +// RUN: %clang_cc1 -fmodules -x objective-c -emit-module -fmodules-cache-path=%t -fmodule-name=diamond_left %S/Inputs/module.map +// RUN: %clang_cc1 -fmodules -x objective-c -emit-module -fmodules-cache-path=%t -fmodule-name=diamond_right %S/Inputs/module.map +// RUN: %clang_cc1 -fmodules -x objective-c -emit-module -fmodules-cache-path=%t -fmodule-name=diamond_bottom %S/Inputs/module.map +// RUN: %clang_cc1 -fmodules -x objective-c -emit-pch -fmodules-cache-path=%t -o %t.pch %S/Inputs/diamond.h +// RUN: %clang_cc1 -fmodules -x objective-c -fmodules-cache-path=%t -include-pch %t.pch %s -verify +// FIXME: When we have a syntax for modules in C, use that. void test_diamond(int i, float f, double d, char c) { top(&i); left(&f); right(&d); bottom(&c); - bottom(&d); // expected-warning{{incompatible pointer types passing 'double *' to parameter of type 'char *'}} + bottom(&d); + // expected-warning@-1{{incompatible pointer types passing 'double *' to parameter of type 'char *'}} + // expected-note@Inputs/diamond_bottom.h:4{{passing argument to parameter 'x' here}} // Names in multiple places in the diamond. top_left(&c); @@ -17,12 +23,3 @@ void test_diamond(int i, float f, double d, char c) { struct left_and_right lr; lr.left = 17; } - -// RUN: rm -rf %t -// RUN: %clang_cc1 -fmodules -x objective-c -emit-module -fmodules-cache-path=%t -fmodule-name=diamond_top %S/Inputs/module.map -// RUN: %clang_cc1 -fmodules -x objective-c -emit-module -fmodules-cache-path=%t -fmodule-name=diamond_left %S/Inputs/module.map -// RUN: %clang_cc1 -fmodules -x objective-c -emit-module -fmodules-cache-path=%t -fmodule-name=diamond_right %S/Inputs/module.map -// RUN: %clang_cc1 -fmodules -x objective-c -emit-module -fmodules-cache-path=%t -fmodule-name=diamond_bottom %S/Inputs/module.map -// RUN: %clang_cc1 -fmodules -x objective-c -emit-pch -fmodules-cache-path=%t -o %t.pch %S/Inputs/diamond.h -// RUN: %clang_cc1 -fmodules -x objective-c -fmodules-cache-path=%t -include-pch %t.pch %s -verify -// FIXME: When we have a syntax for modules in C, use that. diff --git a/test/Modules/diamond.c b/test/Modules/diamond.c index 0bac1b7596a..89d5bc0dda5 100644 --- a/test/Modules/diamond.c +++ b/test/Modules/diamond.c @@ -1,7 +1,10 @@ - - - -// in diamond-bottom.h: expected-note{{passing argument to parameter 'x' here}} +// RUN: rm -rf %t +// RUN: %clang_cc1 -fmodules -x objective-c -emit-module -fmodules-cache-path=%t -fmodule-name=diamond_top %S/Inputs/module.map +// RUN: %clang_cc1 -fmodules -x objective-c -emit-module -fmodules-cache-path=%t -fmodule-name=diamond_left %S/Inputs/module.map +// RUN: %clang_cc1 -fmodules -x objective-c -emit-module -fmodules-cache-path=%t -fmodule-name=diamond_right %S/Inputs/module.map +// RUN: %clang_cc1 -fmodules -x objective-c -emit-module -fmodules-cache-path=%t -fmodule-name=diamond_bottom %S/Inputs/module.map +// RUN: %clang_cc1 -fmodules -x objective-c -fmodules-cache-path=%t %s -verify +// FIXME: When we have a syntax for modules in C, use that. @import diamond_bottom; @@ -10,7 +13,9 @@ void test_diamond(int i, float f, double d, char c) { left(&f); right(&d); bottom(&c); - bottom(&d); // expected-warning{{incompatible pointer types passing 'double *' to parameter of type 'char *'}} + bottom(&d); + // expected-warning@-1{{incompatible pointer types passing 'double *' to parameter of type 'char *'}} + // expected-note@Inputs/diamond_bottom.h:4{{passing argument to parameter 'x' here}} // Names in multiple places in the diamond. top_left(&c); @@ -20,10 +25,3 @@ void test_diamond(int i, float f, double d, char c) { lr.left = 17; } -// RUN: rm -rf %t -// RUN: %clang_cc1 -fmodules -x objective-c -emit-module -fmodules-cache-path=%t -fmodule-name=diamond_top %S/Inputs/module.map -// RUN: %clang_cc1 -fmodules -x objective-c -emit-module -fmodules-cache-path=%t -fmodule-name=diamond_left %S/Inputs/module.map -// RUN: %clang_cc1 -fmodules -x objective-c -emit-module -fmodules-cache-path=%t -fmodule-name=diamond_right %S/Inputs/module.map -// RUN: %clang_cc1 -fmodules -x objective-c -emit-module -fmodules-cache-path=%t -fmodule-name=diamond_bottom %S/Inputs/module.map -// RUN: %clang_cc1 -fmodules -x objective-c -fmodules-cache-path=%t %s -verify -// FIXME: When we have a syntax for modules in C, use that. diff --git a/test/Modules/linkage-merge.cpp b/test/Modules/linkage-merge.cpp index 4e2ecef7d97..9cc9ae64bf9 100644 --- a/test/Modules/linkage-merge.cpp +++ b/test/Modules/linkage-merge.cpp @@ -1,13 +1,12 @@ -// FIXME: we should be able to put these in the .h file :-( -// expected-note {{target of using declaration}} -// expected-note {{using declaration}} +// RUN: rm -rf %t +// RUN: %clang_cc1 -verify -fmodules -fmodules-cache-path=%t -I %S/Inputs %s #include "linkage-merge-bar.h" static int f(int); int f(int); -static void g(int); // expected-error {{declaration conflicts with target of using declaration already in scope}} - -// RUN: rm -rf %t -// RUN: %clang_cc1 -verify -fmodules -fmodules-cache-path=%t -I %S/Inputs %s +static void g(int); +// expected-error@-1 {{declaration conflicts with target of using declaration already in scope}} +// expected-note@Inputs/linkage-merge-foo.h:2 {{target of using declaration}} +// expected-note@Inputs/linkage-merge-bar.h:3 {{using declaration}} diff --git a/test/Modules/linkage-merge.m b/test/Modules/linkage-merge.m index 16e22050783..e838ca10183 100644 --- a/test/Modules/linkage-merge.m +++ b/test/Modules/linkage-merge.m @@ -1,27 +1,26 @@ -// In module: expected-note{{previous declaration}} - - - - -// In module: expected-note{{previous definition is here}} +// RUN: rm -rf %t +// RUN: %clang_cc1 -fmodules -x objective-c -fmodules-cache-path=%t -emit-module -fmodule-name=linkage_merge_left %S/Inputs/module.map +// RUN: %clang_cc1 -fmodules -fmodules-cache-path=%t -w %s -verify // Test redeclarations of functions where the original declaration is // still hidden. @import linkage_merge_left; // excludes "sub" -extern int f0(float); // expected-error{{conflicting types for 'f0'}} +extern int f0(float); +// expected-error@-1{{conflicting types for 'f0'}} +// expected-note@Inputs/linkage-merge-sub.h:1{{previous declaration}} + static int f1(float); // okay: considered distinct static int f2(float); // okay: considered distinct extern int f3(float); // okay: considered distinct -extern float v0; // expected-error{{redefinition of 'v0' with a different type: 'float' vs 'int'}} +extern float v0; +// expected-error@-1{{redefinition of 'v0' with a different type: 'float' vs 'int'}} +// expected-note@Inputs/linkage-merge-sub.h:6{{previous definition is here}} + static float v1; static float v2; extern float v3; typedef float T0; - -// RUN: rm -rf %t -// RUN: %clang_cc1 -fmodules -x objective-c -fmodules-cache-path=%t -emit-module -fmodule-name=linkage_merge_left %S/Inputs/module.map -// RUN: %clang_cc1 -fmodules -fmodules-cache-path=%t -w %s -verify diff --git a/test/Modules/lookup.cpp b/test/Modules/lookup.cpp index 002b6d15566..efd88f47e35 100644 --- a/test/Modules/lookup.cpp +++ b/test/Modules/lookup.cpp @@ -5,7 +5,7 @@ import lookup_left_cxx; #define IMPORT(X) @import X IMPORT(lookup_right_cxx); -// in lookup_left.hpp: expected-warning@3 {{weak identifier 'weak_identifier' never declared}} +// expected-warning@Inputs/lookup_left.hpp:3 {{weak identifier 'weak_identifier' never declared}} void test(int i, float f) { // unqualified lookup diff --git a/test/Modules/lookup.m b/test/Modules/lookup.m index abe95420d4a..54c74913907 100644 --- a/test/Modules/lookup.m +++ b/test/Modules/lookup.m @@ -1,19 +1,19 @@ +// RUN: rm -rf %t +// RUN: %clang_cc1 -fmodules -fmodules-cache-path=%t -emit-module -x objective-c -fmodule-name=lookup_left_objc %S/Inputs/module.map +// RUN: %clang_cc1 -fmodules -fmodules-cache-path=%t -emit-module -x objective-c -fmodule-name=lookup_right_objc %S/Inputs/module.map +// RUN: %clang_cc1 -fmodules -x objective-c -fmodules-cache-path=%t -verify %s +// RUN: %clang_cc1 -fmodules -ast-print -x objective-c -fmodules-cache-path=%t %s | FileCheck -check-prefix=CHECK-PRINT %s -// lookup_left.h: expected-note{{using}} -// lookup_right.h: expected-note{{also found}} @import lookup_left_objc; @import lookup_right_objc; void test(id x) { - [x method]; // expected-warning{{multiple methods named 'method' found}} + [x method]; +// expected-warning@-1{{multiple methods named 'method' found}} +// expected-note@Inputs/lookup_left.h:2{{using}} +// expected-note@Inputs/lookup_right.h:3{{also found}} } -// RUN: rm -rf %t -// RUN: %clang_cc1 -fmodules -fmodules-cache-path=%t -emit-module -x objective-c -fmodule-name=lookup_left_objc %S/Inputs/module.map -// RUN: %clang_cc1 -fmodules -fmodules-cache-path=%t -emit-module -x objective-c -fmodule-name=lookup_right_objc %S/Inputs/module.map -// RUN: %clang_cc1 -fmodules -x objective-c -fmodules-cache-path=%t -verify %s -// RUN: %clang_cc1 -fmodules -ast-print -x objective-c -fmodules-cache-path=%t %s | FileCheck -check-prefix=CHECK-PRINT %s - // CHECK-PRINT: - (int) method; // CHECK-PRINT: - (double) method // CHECK-PRINT: void test(id x) diff --git a/test/Modules/macros.c b/test/Modules/macros.c index fc448d99890..433e03324bc 100644 --- a/test/Modules/macros.c +++ b/test/Modules/macros.c @@ -8,13 +8,13 @@ // FIXME: When we have a syntax for modules in C, use that. // These notes come from headers in modules, and are bogus. -// FIXME: expected-note{{previous definition is here}} -// FIXME: expected-note{{previous definition is here}} expected-note{{expanding this definition of 'LEFT_RIGHT_DIFFERENT'}} -// expected-note{{other definition of 'TOP_RIGHT_REDEF'}} expected-note{{expanding this definition of 'LEFT_RIGHT_DIFFERENT2'}} -// expected-note{{other definition of 'LEFT_RIGHT_DIFFERENT'}} - - -// expected-note{{expanding this definition of 'TOP_RIGHT_REDEF'}} +// FIXME: expected-note@Inputs/macros_left.h:11{{previous definition is here}} +// FIXME: expected-note@Inputs/macros_right.h:12{{previous definition is here}} +// expected-note@Inputs/macros_right.h:12{{expanding this definition of 'LEFT_RIGHT_DIFFERENT'}} +// expected-note@Inputs/macros_top.h:13{{other definition of 'TOP_RIGHT_REDEF'}} +// expected-note@Inputs/macros_right.h:13{{expanding this definition of 'LEFT_RIGHT_DIFFERENT2'}} +// expected-note@Inputs/macros_left.h:14{{other definition of 'LEFT_RIGHT_DIFFERENT'}} +// expected-note@Inputs/macros_right.h:17{{expanding this definition of 'TOP_RIGHT_REDEF'}} @import macros; diff --git a/test/Modules/method_pool.m b/test/Modules/method_pool.m index 9a8897b3830..6fd74b0885f 100644 --- a/test/Modules/method_pool.m +++ b/test/Modules/method_pool.m @@ -8,8 +8,8 @@ - (void)method5:(D*)obj; @end -// in other file: // expected-note@7{{using}} -// in other file: expected-note@12{{also found}} +// expected-note@Inputs/MethodPoolA.h:7{{using}} +// expected-note@Inputs/MethodPoolB.h:12{{also found}} void testMethod1(id object) { [object method1]; @@ -51,8 +51,8 @@ void testMethod3Again(id object) { void testMethod3AgainAgain(id object) { [object method3]; // expected-warning{{multiple methods named 'method3' found}} - // expected-note@2{{using}} - // expected-note@2{{also found}} + // expected-note@Inputs/MethodPoolBSub.h:2{{using}} + // expected-note@Inputs/MethodPoolASub.h:2{{also found}} } void testMethod4Again(id object) { diff --git a/test/Modules/module-private.cpp b/test/Modules/module-private.cpp index d4e73b53968..438dcab9841 100644 --- a/test/Modules/module-private.cpp +++ b/test/Modules/module-private.cpp @@ -15,7 +15,7 @@ int test_broken() { HiddenStruct hidden; // \ // expected-error{{must use 'struct' tag to refer to type 'HiddenStruct' in this scope}} \ // expected-error{{definition of 'struct HiddenStruct' must be imported}} - // expected-note@3 {{previous definition is here}} + // expected-note@Inputs/module_private_left.h:3 {{previous definition is here}} Integer i; // expected-error{{unknown type name 'Integer'}} diff --git a/test/Modules/namespaces.cpp b/test/Modules/namespaces.cpp index 0e9dbffcbb9..426e0025f9f 100644 --- a/test/Modules/namespaces.cpp +++ b/test/Modules/namespaces.cpp @@ -73,5 +73,5 @@ void testAnonymousNotMerged() { N12::consumeFoo(N12::getFoo()); // expected-error{{cannot initialize a parameter of type 'N12::::Foo *' with an rvalue of type 'N12::::Foo *'}} } -// namespaces-right.h: expected-note@60 {{passing argument to parameter here}} -// namespaces-right.h: expected-note@67 {{passing argument to parameter here}} +// expected-note@Inputs/namespaces-right.h:60 {{passing argument to parameter here}} +// expected-note@Inputs/namespaces-right.h:67 {{passing argument to parameter here}} diff --git a/test/Modules/normal-module-map.cpp b/test/Modules/normal-module-map.cpp index 423e808bcab..8155318fb31 100644 --- a/test/Modules/normal-module-map.cpp +++ b/test/Modules/normal-module-map.cpp @@ -1,5 +1,3 @@ -// Note: inside the module. expected-note{{'nested_umbrella_a' declared here}} - // RUN: rm -rf %t // RUN: %clang_cc1 -x objective-c -fmodules-cache-path=%t -fmodules -I %S/Inputs/normal-module-map %s -verify #include "Umbrella/umbrella_sub.h" @@ -25,7 +23,9 @@ int testNestedUmbrellaA() { } int testNestedUmbrellaBFail() { - return nested_umbrella_b; // expected-error{{use of undeclared identifier 'nested_umbrella_b'; did you mean 'nested_umbrella_a'?}} + return nested_umbrella_b; + // expected-error@-1{{use of undeclared identifier 'nested_umbrella_b'; did you mean 'nested_umbrella_a'?}} + // expected-note@Inputs/normal-module-map/nested_umbrella/a.h:1{{'nested_umbrella_a' declared here}} } @import nested_umbrella.b; diff --git a/test/Modules/objc-categories.m b/test/Modules/objc-categories.m index d3ebcb75279..81fb28bafb2 100644 --- a/test/Modules/objc-categories.m +++ b/test/Modules/objc-categories.m @@ -8,11 +8,8 @@ @import category_bottom; - - - -// in category_left.h: expected-note {{previous definition}} -// in category_right.h: expected-warning@11 {{duplicate definition of category}} +// expected-note@Inputs/category_left.h:14 {{previous definition}} +// expected-warning@Inputs/category_right.h:11 {{duplicate definition of category}} @interface Foo(Source) -(void)source; @@ -75,7 +72,7 @@ void test_hidden_right_errors(Foo *foo) { [p4 p4_method]; // expected-warning{{instance method '-p4_method' not found (return type defaults to 'id')}} id p4p = p4.p4_prop; // expected-error{{property 'p4_prop' not found on object of type 'id'}} p4p = foo.p4_prop; // expected-error{{property 'p4_prop' not found on object of type 'Foo *'; did you mean 'p3_prop'?}} - // expected-note@7{{'p3_prop' declared here}} + // expected-note@Inputs/category_left_sub.h:7{{'p3_prop' declared here}} } @import category_right.sub; diff --git a/test/Modules/on-demand-build.m b/test/Modules/on-demand-build.m index 31742f7e03a..e9587594202 100644 --- a/test/Modules/on-demand-build.m +++ b/test/Modules/on-demand-build.m @@ -7,7 +7,7 @@ @interface OtherClass @end -// in module: expected-note@17{{class method 'alloc' is assumed to return an instance of its receiver type ('Module *')}} +// expected-note@Inputs/Module.framework/Headers/Module.h:17{{class method 'alloc' is assumed to return an instance of its receiver type ('Module *')}} void test_getModuleVersion() { const char *version = getModuleVersion(); const char *version2 = [Module version]; diff --git a/test/Modules/redecl-merge.m b/test/Modules/redecl-merge.m index e37366748d0..37e5967f4e3 100644 --- a/test/Modules/redecl-merge.m +++ b/test/Modules/redecl-merge.m @@ -27,8 +27,8 @@ int *call_eventually_noreturn_again(void) { int *call_eventually_noreturn2_again(void) { // noreturn and non-noreturn functions have different types eventually_noreturn2(); // expected-error{{call to 'eventually_noreturn2' is ambiguous}} - // expected-note@93{{candidate function}} - // expected-note@90{{candidate function}} + // expected-note@Inputs/redecl-merge-left.h:93{{candidate function}} + // expected-note@Inputs/redecl-merge-right.h:90{{candidate function}} } @implementation A @@ -79,24 +79,26 @@ void testTypedefMerge(int i, double d) { T1 *ip = &i; // FIXME: Typedefs aren't actually merged in the sense of other merges, because // we should only merge them when the types are identical. - // in other file: expected-note@60{{candidate found by name lookup is 'T2'}} - // in other file: expected-note@63{{candidate found by name lookup is 'T2'}} + // expected-note@Inputs/redecl-merge-left.h:60{{candidate found by name lookup is 'T2'}} + // expected-note@Inputs/redecl-merge-right.h:63{{candidate found by name lookup is 'T2'}} T2 *dp = &d; // expected-error{{reference to 'T2' is ambiguous}} } void testFuncMerge(int i) { func0(i); func1(i); - // in other file: expected-note@64{{candidate function}} - // in other file: expected-note@70{{candidate function}} + // expected-note@Inputs/redecl-merge-left.h:64{{candidate function}} + // expected-note@Inputs/redecl-merge-right.h:70{{candidate function}} func2(i); // expected-error{{call to 'func2' is ambiguous}} } void testVarMerge(int i) { var1 = i; - // in other files: expected-note@77 2{{candidate found by name lookup is 'var2'}} + // expected-note@Inputs/redecl-merge-left.h:77{{candidate found by name lookup is 'var2'}} + // expected-note@Inputs/redecl-merge-right.h:77{{candidate found by name lookup is 'var2'}} var2 = i; // expected-error{{reference to 'var2' is ambiguous}} - // in other files: expected-note@79 2{{candidate found by name lookup is 'var3'}} + // expected-note@Inputs/redecl-merge-left.h:79{{candidate found by name lookup is 'var3'}} + // expected-note@Inputs/redecl-merge-right.h:79{{candidate found by name lookup is 'var3'}} var3 = i; // expected-error{{reference to 'var3' is ambiguous}} } diff --git a/test/Modules/redecls/a.h b/test/Modules/redecls/a.h new file mode 100644 index 00000000000..1647f86606a --- /dev/null +++ b/test/Modules/redecls/a.h @@ -0,0 +1,3 @@ +@interface AA +@end +@class AA; diff --git a/test/Modules/redecls/b.h b/test/Modules/redecls/b.h new file mode 100644 index 00000000000..d41573ddc78 --- /dev/null +++ b/test/Modules/redecls/b.h @@ -0,0 +1 @@ +@class AA; diff --git a/test/Modules/redecls/main.m b/test/Modules/redecls/main.m new file mode 100644 index 00000000000..9ec02b03f2c --- /dev/null +++ b/test/Modules/redecls/main.m @@ -0,0 +1,27 @@ +// RUN: rm -rf %t.mcp +// RUN: %clang_cc1 -fmodules -x objective-c -emit-module -fmodule-name=a %S/module.map -fmodules-cache-path=%t.mcp +// RUN: %clang_cc1 -fmodules -x objective-c -emit-module -fmodule-name=b %S/module.map -fmodules-cache-path=%t.mcp +// RUN: %clang_cc1 -fmodules %s -emit-pch -o %t1.pch -fmodules-cache-path=%t.mcp +// RUN: %clang_cc1 -fmodules %s -emit-pch -o %t2.pch -include-pch %t1.pch -fmodules-cache-path=%t.mcp +// RUN: %clang_cc1 -fmodules %s -fsyntax-only -include-pch %t2.pch -fmodules-cache-path=%t.mcp -verify + +#ifndef HEADER1 +#define HEADER1 + +@import a; + +#elif !defined(HEADER2) +#define HEADER2 + +@class AA; +@import b; + +#else + +// rdar://13712705 +@interface SS : AA +@end + +#warning parsed this +#endif +// expected-warning@-2{{parsed this}} diff --git a/test/Modules/redecls/module.map b/test/Modules/redecls/module.map new file mode 100644 index 00000000000..a36568207b6 --- /dev/null +++ b/test/Modules/redecls/module.map @@ -0,0 +1,2 @@ +module a { header "a.h" } +module b { header "b.h" } diff --git a/test/Modules/serialized-diags.m b/test/Modules/serialized-diags.m new file mode 100644 index 00000000000..18bce06047e --- /dev/null +++ b/test/Modules/serialized-diags.m @@ -0,0 +1,32 @@ +@import HasWarnings; + +#ifdef WITH_ERRORS +@import HasErrors; +#endif + +float float_val; +double *double_ptr = &float_val; + +// RUN: rm -rf %t %t.diag %t.out +// RUN: %clang -fmodules -fmodules-cache-path=%t/ModuleCache -I %S/Inputs/ModuleDiags -fsyntax-only %s --serialize-diagnostics %t.diag > /dev/null 2>&1 +// RUN: c-index-test -read-diagnostics %t.diag > %t.out 2>&1 +// RUN: FileCheck --input-file=%t.out %s + +// CHECK: has_warnings.h:3:8: warning: incompatible pointer types initializing 'float *' +// CHECK: serialized-diags.m:1:9: note: while building module 'HasWarnings' imported from +// CHECK: serialized-diags.m:8:9: warning: incompatible pointer types initializing 'double *' +// CHECK: Number of diagnostics: 2 + +// RUN: rm -rf %t %t.diag_errors %t.out_errors +// RUN: not %clang -fmodules -fmodules-cache-path=%t/ModuleCache -I %S/Inputs/ModuleDiags -fsyntax-only -DWITH_ERRORS %s --serialize-diagnostics %t.diag_errors > /dev/null 2>&1 +// RUN: c-index-test -read-diagnostics %t.diag_errors > %t.out_errors 2>&1 +// RUN: FileCheck -check-prefix=CHECK-WITH-ERRORS --input-file=%t.out_errors %s + +// CHECK-WITH-ERRORS: has_warnings.h:3:8: warning: incompatible pointer types initializing 'float *' +// CHECK-WITH-ERRORS: serialized-diags.m:1:9: note: while building module 'HasWarnings' +// CHECK-WITH-ERRORS: has_errors.h:2:13: error: redefinition of 'foo' +// CHECK-WITH-ERRORS: serialized-diags.m:4:9: note: while building module 'HasErrors' +// CHECK-WITH-ERRORS: has_errors.h:1:13: note: previous definition is here +// CHECK-WITH-ERRORS: serialized-diags.m:4:9: fatal: could not build module 'HasErrors' +// CHECK-WITH-ERRORS: Number of diagnostics: 3 + diff --git a/test/Modules/subframeworks.m b/test/Modules/subframeworks.m index 22dfcca3657..ad70cc2b22f 100644 --- a/test/Modules/subframeworks.m +++ b/test/Modules/subframeworks.m @@ -23,7 +23,7 @@ CXXOnly cxxonly; @import HasSubModules; -// expected-warning@1{{treating #include as an import of module 'HasSubModules.Sub.Types'}} +// expected-warning@Inputs/HasSubModules.framework/Frameworks/Sub.framework/PrivateHeaders/SubPriv.h:1{{treating #include as an import of module 'HasSubModules.Sub.Types'}} #import struct FrameworkSubStruct ss; diff --git a/test/Modules/system_version.m b/test/Modules/system_version.m new file mode 100644 index 00000000000..85b3263f725 --- /dev/null +++ b/test/Modules/system_version.m @@ -0,0 +1,32 @@ +// Test checking that we're hashing a system version file in the +// module hash. +// REQUIRES: shell + +// First, build a system root. +// RUN: rm -rf %t +// RUN: mkdir -p %t/usr/include +// RUN: cp %S/Inputs/Modified/A.h %t/usr/include +// RUN: cp %S/Inputs/Modified/B.h %t/usr/include +// RUN: cp %S/Inputs/Modified/module.map %t/usr/include + +// Run once with no system version file. We should end up with one module. +// RUN: %clang_cc1 -fmodules-cache-path=%t/cache -fmodules -isysroot %t -I %t/usr/include %s -verify +// RUN: ls -R %t | grep -c ModA.pcm| grep 1 + +// Add a system version file and run again. We should now have two +// module variants. +// RUN: mkdir -p %t/System/Library/CoreServices +// RUN: echo "hello" > %t/System/Library/CoreServices/SystemVersion.plist +// RUN: %clang_cc1 -fmodules-cache-path=%t/cache -fmodules -isysroot %t -I %t/usr/include %s -verify +// RUN: ls -R %t | grep -c ModA.pcm| grep 2 + +// Change the system version file and run again. We should now have three +// module variants. +// RUN: mkdir -p %t/System/Library/CoreServices +// RUN: echo "modules" > %t/System/Library/CoreServices/SystemVersion.plist +// RUN: %clang_cc1 -fmodules-cache-path=%t/cache -fmodules -isysroot %t -I %t/usr/include %s -verify +// RUN: ls -R %t | grep -c ModA.pcm| grep 3 + +// expected-no-diagnostics +@import ModA; + diff --git a/test/PCH/captured-stmt.cpp b/test/PCH/captured-stmt.cpp new file mode 100644 index 00000000000..6f5ca3836e0 --- /dev/null +++ b/test/PCH/captured-stmt.cpp @@ -0,0 +1,42 @@ +// RUN: %clang_cc1 -x c++-header -emit-pch %s -o %t +// RUN: %clang_cc1 -include-pch %t -fsyntax-only -verify %s + +// expected-no-diagnostics + +#ifndef HEADER_INCLUDED +#define HEADER_INCLUDED + +static inline void foo(int &x, int y) { + // Capturing x and y + #pragma clang __debug captured + { + x += y; + } +} + +struct C { + int val; + + explicit C(int v) : val(v) { } + + void bar(int &x) { + // Capturing x and this + #pragma clang __debug captured + { + x += val; + } + } +}; + +#else + +void test_foo(int &x) { + foo(x, 10); +} + +void test_bar(int &x) { + C Obj(10); + Obj.bar(x); +} + +#endif diff --git a/test/PCH/cxx-typeid.cpp b/test/PCH/cxx-typeid.cpp index d1e0f9ded75..534863af21b 100644 --- a/test/PCH/cxx-typeid.cpp +++ b/test/PCH/cxx-typeid.cpp @@ -1,8 +1,8 @@ // Test this without pch. -// RUN: %clang -include %S/cxx-typeid.h -fsyntax-only -Xclang -verify %s +// RUN: %clang_cc1 -include %S/cxx-typeid.h -fsyntax-only -verify %s -// RUN: %clang -ccc-pch-is-pch -x c++-header -o %t.gch %S/cxx-typeid.h -// RUN: %clang -ccc-pch-is-pch -include %t -fsyntax-only -Xclang -verify %s +// RUN: %clang_cc1 -x c++-header -emit-pch -o %t.pch %S/cxx-typeid.h +// RUN: %clang_cc1 -include-pch %t.pch -fsyntax-only -verify %s // expected-no-diagnostics diff --git a/test/PCH/cxx-typeid.h b/test/PCH/cxx-typeid.h index aa3b16aa0b4..f10f4de87c0 100644 --- a/test/PCH/cxx-typeid.h +++ b/test/PCH/cxx-typeid.h @@ -1,3 +1,44 @@ // Header for PCH test cxx-typeid.cpp -#include +#ifndef CXX_TYPEID_H +#define CXX_TYPEID_H + +namespace std { + +class type_info +{ +public: + virtual ~type_info(); + + bool operator==(const type_info& rhs) const; + bool operator!=(const type_info& rhs) const; + + bool before(const type_info& rhs) const; + unsigned long hash_code() const; + const char* name() const; + + type_info(const type_info& rhs); + type_info& operator=(const type_info& rhs); +}; + +class bad_cast +{ +public: + bad_cast(); + bad_cast(const bad_cast&); + bad_cast& operator=(const bad_cast&); + virtual const char* what() const; +}; + +class bad_typeid +{ +public: + bad_typeid(); + bad_typeid(const bad_typeid&); + bad_typeid& operator=(const bad_typeid&); + virtual const char* what() const; +}; + +} // std + +#endif diff --git a/test/PCH/cxx-using.cpp b/test/PCH/cxx-using.cpp index 2ca7dad0dd1..2cab1f031a9 100644 --- a/test/PCH/cxx-using.cpp +++ b/test/PCH/cxx-using.cpp @@ -6,10 +6,9 @@ // RUN: %clang_cc1 -include-pch %t -fsyntax-only -verify %s void m() { - D s; // expected-note {{candidate function}} + D s; s.f(); // expected-error {{no matching member}} } - - -// expected-note {{candidate function}} +// expected-note@cxx-using.h:9 {{candidate function}} +// expected-note@cxx-using.h:15 {{candidate function}} diff --git a/test/PCH/cxx11-statement-attributes.cpp b/test/PCH/cxx11-statement-attributes.cpp index 3bb7b40aa96..722ca6e9ffa 100644 --- a/test/PCH/cxx11-statement-attributes.cpp +++ b/test/PCH/cxx11-statement-attributes.cpp @@ -4,8 +4,7 @@ // RUN: %clang_cc1 -x c++-header -emit-pch -std=c++11 -o %t %S/Inputs/cxx11-statement-attributes.h // RUN: %clang_cc1 -include-pch %t -std=c++11 -Wimplicit-fallthrough -fsyntax-only %s -o - -verify -// Warning from Inputs/cxx11-statement-attributes.h: -// expected-warning@10 {{fallthrough annotation does not directly precede switch label}} +// expected-warning@Inputs/cxx11-statement-attributes.h:10 {{fallthrough annotation does not directly precede switch label}} void g(int n) { f<1>(n); // expected-note {{in instantiation of function template specialization 'f<1>' requested here}} diff --git a/test/PCH/cxx1y-decltype-auto.cpp b/test/PCH/cxx1y-decltype-auto.cpp new file mode 100644 index 00000000000..a1c9339cb66 --- /dev/null +++ b/test/PCH/cxx1y-decltype-auto.cpp @@ -0,0 +1,24 @@ +// RUN: %clang_cc1 -pedantic -std=c++1y -emit-pch %s -o %t +// RUN: %clang_cc1 -pedantic -std=c++1y -include-pch %t -verify %s + +#ifndef HEADER_INCLUDED + +#define HEADER_INCLUDED + +template void f(T t) { + auto a = t.x; + decltype(auto) b = t.x; + auto c = (t.x); + decltype(auto) d = (t.x); +} + +#else + +struct Z { + int x : 5; // expected-note {{bit-field}} +}; + +// expected-error@12 {{non-const reference cannot bind to bit-field 'x'}} +template void f(Z); // expected-note {{in instantiation of}} + +#endif diff --git a/test/PCH/cxx1y-default-initializer.cpp b/test/PCH/cxx1y-default-initializer.cpp new file mode 100644 index 00000000000..5a68f270451 --- /dev/null +++ b/test/PCH/cxx1y-default-initializer.cpp @@ -0,0 +1,30 @@ +// RUN: %clang_cc1 -pedantic -std=c++1y -emit-pch %s -o %t +// RUN: %clang_cc1 -pedantic -std=c++1y -include-pch %t -verify %s + +#ifndef HEADER_INCLUDED + +#define HEADER_INCLUDED + +struct A { + int x; + int y = 3; + int z = x + y; +}; +template constexpr A make() { return A {}; } +template constexpr A make(T t) { return A { t }; } + +struct B { + int z1, z2 = z1; + constexpr B(int k) : z1(k) {} +}; + +#else + +static_assert(A{}.z == 3, ""); +static_assert(A{1}.z == 4, ""); +static_assert(A{.y = 5}.z == 5, ""); // expected-warning {{C99}} +static_assert(A{3, .y = 1}.z == 4, ""); // expected-warning {{C99}} +static_assert(make().z == 3, ""); +static_assert(make(12).z == 15, ""); + +#endif diff --git a/test/PCH/functions.c b/test/PCH/functions.c index 35e39210585..fa2ba8d29c0 100644 --- a/test/PCH/functions.c +++ b/test/PCH/functions.c @@ -4,18 +4,20 @@ // Test with pch. // RUN: %clang_cc1 -emit-pch -o %t %S/functions.h // RUN: %clang_cc1 -include-pch %t -fsyntax-only -verify %s -// expected-note{{'f1' declared here}} + int f0(int x0, int y0, ...) { return x0 + y0; } -// expected-note{{passing argument to parameter here}} + float *test_f1(int val, double x, double y) { if (val > 5) return f1(x, y); else return f1(x); // expected-error{{too few arguments to function call}} + // expected-note@functions.h:7{{'f1' declared here}} } void test_g0(int *x, float * y) { g0(y); // expected-warning{{incompatible pointer types passing 'float *' to parameter of type 'int *'}} + // expected-note@functions.h:9{{passing argument to parameter here}} g0(x); } diff --git a/test/PCH/headersearch.cpp b/test/PCH/headersearch.cpp index 8ca0c361c4d..4b24ac6b407 100644 --- a/test/PCH/headersearch.cpp +++ b/test/PCH/headersearch.cpp @@ -20,15 +20,15 @@ // RUN: cp -pR %t_orig %t_moved // Check diagnostic with location in original source: -// RUN: %clang_cc1 -include-pch all.h.pch -I%t_moved -I%t_moved/sub2 -Wpadded -emit-obj -o %t.o %s 2> %t.stderr +// RUN: %clang_cc1 -include-pch all.h.pch -I%t_moved -I%t_moved/sub2 -Wpadded -emit-llvm-only %s 2> %t.stderr // RUN: grep 'struct orig_sub' %t.stderr // Check diagnostic with 2nd location in original source: -// RUN: not %clang_cc1 -DREDECL -include-pch all.h.pch -I%t_moved -I%t_moved/sub2 -emit-obj -o %t.o %s 2> %t.stderr +// RUN: not %clang_cc1 -DREDECL -include-pch all.h.pch -I%t_moved -I%t_moved/sub2 -emit-llvm-only %s 2> %t.stderr // RUN: grep 'void foo' %t.stderr // Check diagnostic with instantiation location in original source: -// RUN: not %clang_cc1 -DINSTANTIATION -include-pch all.h.pch -I%t_moved -I%t_moved/sub2 -emit-obj -o %t.o %s 2> %t.stderr +// RUN: not %clang_cc1 -DINSTANTIATION -include-pch all.h.pch -I%t_moved -I%t_moved/sub2 -emit-llvm-only %s 2> %t.stderr // RUN: grep 'orig_sub2_1' %t.stderr void qq(orig_sub*) {all();} diff --git a/test/PCH/method_pool.m b/test/PCH/method_pool.m index 20010fbd1c5..139f8ba4a69 100644 --- a/test/PCH/method_pool.m +++ b/test/PCH/method_pool.m @@ -9,13 +9,5 @@ int message_id(id x) { return [x instMethod:17]; // expected-warning{{multiple methods}} } - - - - -/* Whitespace below is significant */ -/* expected-note{{using}} */ - - - -/* expected-note{{also}} */ +/* expected-note@method_pool.h:17{{using}} */ +/* expected-note@method_pool.h:21{{also}} */ diff --git a/test/PCH/nonvisible-external-defs.c b/test/PCH/nonvisible-external-defs.c index 49392ca2fef..092e2c9d9bf 100644 --- a/test/PCH/nonvisible-external-defs.c +++ b/test/PCH/nonvisible-external-defs.c @@ -7,4 +7,4 @@ int g(int, float); // expected-error{{conflicting types}} -// expected-note{{previous declaration}} +// expected-note@nonvisible-external-defs.h:10{{previous declaration}} diff --git a/test/PCH/reloc.c b/test/PCH/reloc.c index 4c426e4f773..8dabb8b03d0 100644 --- a/test/PCH/reloc.c +++ b/test/PCH/reloc.c @@ -10,5 +10,5 @@ int x = 2; // expected-error{{redefinition}} int y = 5; // expected-error{{redefinition}} -// expected-note{{previous definition}} -// expected-note{{previous definition}} +// expected-note@libroot/usr/include/reloc.h:13{{previous definition}} +// expected-note@libroot/usr/include/reloc2.h:14{{previous definition}} diff --git a/test/PCH/tentative-defs.c b/test/PCH/tentative-defs.c index 0072818a1a2..42882307dc7 100644 --- a/test/PCH/tentative-defs.c +++ b/test/PCH/tentative-defs.c @@ -5,5 +5,4 @@ // RUN: grep "@variable = common global i32 0" %t | count 1 // RUN: grep "@incomplete_array = common global .*1 x i32" %t | count 1 - -// FIXME: tentative-defs.h expected-warning{{tentative}} +// FIXME: expected-warning@tentative-defs.h:9{{tentative}} diff --git a/test/PCH/thread-local.cpp b/test/PCH/thread-local.cpp new file mode 100644 index 00000000000..f65c12af092 --- /dev/null +++ b/test/PCH/thread-local.cpp @@ -0,0 +1,20 @@ +// RUN: %clang_cc1 -pedantic-errors -std=c++11 -triple x86_64-linux-gnu -emit-pch %s -o %t +// RUN: %clang_cc1 -pedantic-errors -std=c++11 -triple x86_64-linux-gnu -include-pch %t -verify %s + +#ifndef HEADER_INCLUDED + +#define HEADER_INCLUDED +extern thread_local int a; +extern _Thread_local int b; +extern int c; + +#else + +_Thread_local int a; // expected-error {{thread-local declaration of 'a' with static initialization follows declaration with dynamic initialization}} +// expected-note@7 {{previous declaration is here}} +thread_local int b; // expected-error {{thread-local declaration of 'b' with dynamic initialization follows declaration with static initialization}} +// expected-note@8 {{previous declaration is here}} +thread_local int c; // expected-error {{thread-local declaration of 'c' follows non-thread-local declaration}} +// expected-note@9 {{previous declaration is here}} + +#endif diff --git a/test/PCH/typo.cpp b/test/PCH/typo.cpp index f8161d17df4..6ab66c2cabf 100644 --- a/test/PCH/typo.cpp +++ b/test/PCH/typo.cpp @@ -1,17 +1,14 @@ - -// In header: expected-note{{'boost::function' declared here}} - - -// In header: expected-note{{'boost::graph::adjacency_list' declared here}} - - - -adjacent_list g; // expected-error{{no template named 'adjacent_list'; did you mean 'boost::graph::adjacency_list'?}} -Function f; // expected-error{{no template named 'Function'; did you mean 'boost::function'?}} - // Without PCH // RUN: %clang_cc1 -include %S/Inputs/typo.hpp -verify %s // With PCH // RUN: %clang_cc1 -x c++-header -emit-pch -o %t %S/Inputs/typo.hpp // RUN: %clang_cc1 -include-pch %t -verify %s + +adjacent_list g; +// expected-error@-1{{no template named 'adjacent_list'; did you mean 'boost::graph::adjacency_list'?}} +// expected-note@Inputs/typo.hpp:5{{'boost::graph::adjacency_list' declared here}} + +Function f; +// expected-error@-1{{no template named 'Function'; did you mean 'boost::function'?}} +// expected-note@Inputs/typo.hpp:2{{'boost::function' declared here}} diff --git a/test/PCH/typo.m b/test/PCH/typo.m index c6f0275bc2f..876b9438d1b 100644 --- a/test/PCH/typo.m +++ b/test/PCH/typo.m @@ -1,6 +1,7 @@ // RUN: %clang_cc1 -x objective-c-header -emit-pch -o %t %S/Inputs/typo.h // RUN: %clang_cc1 -include-pch %t -verify %s -// In header: expected-note{{declared here}} + void f() { [NSstring alloc]; // expected-error{{unknown receiver 'NSstring'; did you mean 'NSString'?}} + // expected-note@Inputs/typo.h:3{{declared here}} } diff --git a/test/Parser/MicrosoftExtensions.c b/test/Parser/MicrosoftExtensions.c index 4c6f4f891da..35c63d4b552 100644 --- a/test/Parser/MicrosoftExtensions.c +++ b/test/Parser/MicrosoftExtensions.c @@ -6,7 +6,7 @@ void (*__fastcall fastpfunc)(); struct __declspec(uuid("00000000-0000-0000-C000-000000000046")) __declspec(novtable) IUnknown {}; /* expected-warning{{__declspec attribute 'novtable' is not supported}} */ extern __declspec(dllimport) void __stdcall VarR4FromDec(); __declspec(deprecated) __declspec(deprecated) char * __cdecl ltoa( long _Val, char * _DstBuf, int _Radix); -__declspec(noalias) __declspec(restrict) void * __cdecl xxx( void * _Memory ); /* expected-warning{{__declspec attribute 'noalias' is not supported}} expected-warning{{__declspec attribute 'restrict' is not supported}} */ +__declspec(safebuffers) __declspec(noalias) __declspec(restrict) void * __cdecl xxx( void * _Memory ); /* expected-warning{{__declspec attribute 'safebuffers' is not supported}} expected-warning{{__declspec attribute 'noalias' is not supported}} expected-warning{{__declspec attribute 'restrict' is not supported}} */ typedef __w64 unsigned long ULONG_PTR, *PULONG_PTR; void * __ptr64 PtrToPtr64(const void *p) diff --git a/test/Parser/MicrosoftExtensions.cpp b/test/Parser/MicrosoftExtensions.cpp index fd38dca1941..d8a597a8cc5 100644 --- a/test/Parser/MicrosoftExtensions.cpp +++ b/test/Parser/MicrosoftExtensions.cpp @@ -331,3 +331,32 @@ namespace Inheritance { class __multiple_inheritance B; class __virtual_inheritance C; } + +struct StructWithProperty { + __declspec(property) int V0; // expected-error {{expected '(' after 'property'}} + __declspec(property()) int V1; // expected-error {{property does not specify a getter or a putter}} + __declspec(property(set)) int V2; // expected-error {{putter for property must be specified as 'put', not 'set'}} expected-error {{expected '=' after 'set'}} + __declspec(property(ptu)) int V3; // expected-error {{missing 'get=' or 'put='}} + __declspec(property(ptu=PutV)) int V4; // expected-error {{expected 'get' or 'put' in property declaration}} + __declspec(property(get)) int V5; // expected-error {{expected '=' after 'get'}} + __declspec(property(get&)) int V6; // expected-error {{expected '=' after 'get'}} + __declspec(property(get=)) int V7; // expected-error {{expected name of accessor method}} + __declspec(property(get=GetV)) int V8; // no-warning + __declspec(property(get=GetV=)) int V9; // expected-error {{expected ',' or ')' at end of property accessor list}} + __declspec(property(get=GetV,)) int V10; // expected-error {{expected 'get' or 'put' in property declaration}} + __declspec(property(get=GetV,put=SetV)) int V11; // no-warning + __declspec(property(get=GetV,put=SetV,get=GetV)) int V12; // expected-error {{property declaration specifies 'get' accessor twice}} + + int GetV() { return 123; } + void SetV(int v) {} +}; +void TestProperty() { + StructWithProperty sp; + sp.V8; + sp.V8 = 0; // expected-error {{no setter defined for property 'V8'}} + int i = sp.V11; + sp.V11 = i++; + sp.V11 += 8; + sp.V11++; + ++sp.V11; +} diff --git a/test/Parser/captured-statements.c b/test/Parser/captured-statements.c new file mode 100644 index 00000000000..30dddb549c9 --- /dev/null +++ b/test/Parser/captured-statements.c @@ -0,0 +1,14 @@ +// RUN: %clang_cc1 -verify %s + +void test1() +{ + #pragma clang __debug captured x // expected-warning {{extra tokens at end of #pragma clang __debug captured directive}} + { + } +} + +void test2() +{ + #pragma clang __debug captured + int x; // expected-error {{expected '{'}} +} diff --git a/test/Parser/cxx0x-ambig.cpp b/test/Parser/cxx0x-ambig.cpp index 3b864f98019..4c22ed3a9bb 100644 --- a/test/Parser/cxx0x-ambig.cpp +++ b/test/Parser/cxx0x-ambig.cpp @@ -38,8 +38,8 @@ namespace bitfield { constexpr T() {} constexpr T(int) {} constexpr T(T, T, T, T) {} - constexpr T operator=(T) { return *this; } - constexpr operator int() { return 4; } + constexpr T operator=(T) const { return *this; } + constexpr operator int() const { return 4; } }; constexpr T a, b, c, d; @@ -68,7 +68,7 @@ namespace bitfield { }; struct U { - constexpr operator T() { return T(); } // expected-note 2{{candidate}} + constexpr operator T() const { return T(); } // expected-note 2{{candidate}} }; // This could be a bit-field. struct S7 { diff --git a/test/Parser/cxx0x-decl.cpp b/test/Parser/cxx0x-decl.cpp index b9441fd6813..e6cba726ab6 100644 --- a/test/Parser/cxx0x-decl.cpp +++ b/test/Parser/cxx0x-decl.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -verify -fsyntax-only -std=c++11 -pedantic-errors %s +// RUN: %clang_cc1 -verify -fsyntax-only -std=c++11 -pedantic-errors -triple x86_64-linux-gnu %s // Make sure we know these are legitimate commas and not typos for ';'. namespace Commas { @@ -46,16 +46,15 @@ using PR14855 = int S::; // expected-error {{expected ';' after alias declaratio // a constexpr function. struct ConstexprTrailingReturn { int n; - constexpr auto f() -> decltype((n)); + constexpr auto f() const -> decltype((n)); }; constexpr const int &ConstexprTrailingReturn::f() const { return n; } namespace TestIsValidAfterTypeSpecifier { struct s {} v; -// FIXME: We should accept this once we support thread_local. struct s -thread_local tl; // expected-error {{expected unqualified-id}} +thread_local tl; struct s &r0 = v; diff --git a/test/Parser/objc-boxing.m b/test/Parser/objc-boxing.m index a16a137b8f6..a6bb0243cf0 100644 --- a/test/Parser/objc-boxing.m +++ b/test/Parser/objc-boxing.m @@ -24,3 +24,11 @@ id missing_parentheses() { return @(5; // expected-error {{expected ')'}} \ // expected-note {{to match this '('}} } + +// rdar://10679157 +void bar(id p); +void foo(id p) { + bar(@{p, p}); // expected-error {{expected ':'}} + bar(0); + bar(0); +} diff --git a/test/Parser/objc-error-qualified-implementation.m b/test/Parser/objc-error-qualified-implementation.m new file mode 100644 index 00000000000..444fb5dab44 --- /dev/null +++ b/test/Parser/objc-error-qualified-implementation.m @@ -0,0 +1,21 @@ +// RUN: %clang_cc1 -fsyntax-only -Wno-objc-root-class -verify %s +// rdar://12233858 + +@protocol P +@end + +@interface I @end + +@implementation I

@end // expected-error {{@implementation declaration can not be protocol qualified}} + +@interface J < P,P > +@end + + +@implementation J < P,P > // expected-error {{@implementation declaration can not be protocol qualified}} +@end + +@interface K @end + +@implementation K

'}} diff --git a/test/Parser/objcxx11-initialized-temps.mm b/test/Parser/objcxx11-initialized-temps.mm new file mode 100644 index 00000000000..96f19fe6a5e --- /dev/null +++ b/test/Parser/objcxx11-initialized-temps.mm @@ -0,0 +1,38 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s +// expected-no-diagnostics +// rdar://12788429 + +struct CGPoint { + double x; + double y; +}; +typedef struct CGPoint CGPoint; + +struct CGSize { + double width; + double height; +}; +typedef struct CGSize CGSize; + +struct CGRect { + CGPoint origin; + CGSize size; +}; +typedef struct CGRect CGRect; + +typedef CGRect NSRect; + +void HappySetFrame(NSRect frame) {} + +__attribute__((objc_root_class)) +@interface NSObject @end + +@implementation NSObject +- (void) sadSetFrame: (NSRect)frame {} + +- (void) nothing +{ + HappySetFrame({{0,0}, {13,14}}); + [self sadSetFrame: {{0,0}, {13,14}}]; +} +@end diff --git a/test/Parser/pragma-options.c b/test/Parser/pragma-options.c index 7844e710806..d168a2751a2 100644 --- a/test/Parser/pragma-options.c +++ b/test/Parser/pragma-options.c @@ -20,3 +20,15 @@ #pragma align=reset #pragma align=mac68k #pragma align=power + +// PR13580 +struct S +{ + char a[3]; +#pragma align=packed + struct T + { + char b; + int c; + } d; +}; diff --git a/test/Parser/pragma-pack.c b/test/Parser/pragma-pack.c index 84778cd501d..172a332510a 100644 --- a/test/Parser/pragma-pack.c +++ b/test/Parser/pragma-pack.c @@ -30,3 +30,17 @@ _Pragma("pack(push)") /* expected-warning {{expected integer or identifier in '#pragma pack'}}*/ _Pragma("pack(push,)") + +// PR13580 +struct S +{ + char a[3]; +#pragma pack(1) + struct T + { + char b; + int c; + } d; +#pragma pack() + int e; +}; diff --git a/test/Preprocessor/aarch64-target-features.c b/test/Preprocessor/aarch64-target-features.c index 65104e33117..8bb8427c0d2 100644 --- a/test/Preprocessor/aarch64-target-features.c +++ b/test/Preprocessor/aarch64-target-features.c @@ -1,30 +1,32 @@ // RUN: %clang -target aarch64-none-linux-gnu -x c -E -dM %s -o - | FileCheck %s -// CHECK: __AARCH 8 // CHECK: __AARCH64EL__ -// CHECK: __AARCH_ACLE 101 // CHECK-NOT: __AARCH_ADVSIMD_FP // CHECK-NOT: __AARCH_FEATURE_ADVSIMD -// CHECK-NOT: __AARCH_FEATURE_BIG_ENDIAN -// CHECK: __AARCH_FEATURE_CLZ 1 -// CHECK: __AARCH_FEATURE_FMA 1 -// CHECK: __AARCH_FEATURE_LDREX 0xf -// CHECK: __AARCH_FEATURE_UNALIGNED 1 -// CHECK: __AARCH_FP 0xe -// CHECK-NOT: __AARCH_FP_FAST -// CHECK: __AARCH_FP16_FORMAT_IEEE 1 -// CHECK: __AARCH_FP_FENV_ROUNDING 1 -// CHECK: __AARCH_PROFILE 'A' -// CHECK: __AARCH_SIZEOF_MINIMAL_ENUM 4 -// CHECK: __AARCH_SIZEOF_WCHAR_T 4 +// CHECK: __ARM_ACLE 101 +// CHECK: __ARM_ARCH 8 +// CHECK: __ARM_ARCH_PROFILE 'A' +// CHECK-NOT: __ARM_FEATURE_BIG_ENDIAN +// CHECK: __ARM_FEATURE_CLZ 1 +// CHECK: __ARM_FEATURE_FMA 1 +// CHECK: __ARM_FEATURE_LDREX 0xf +// CHECK: __ARM_FEATURE_UNALIGNED 1 +// CHECK: __ARM_FP 0xe +// CHECK-NOT: __ARM_FP_FAST +// CHECK: __ARM_FP16_FORMAT_IEEE 1 +// CHECK: __ARM_FP_FENV_ROUNDING 1 +// CHECK-NOT: __ARM_NEON_FP +// CHECK-NOT: __ARM_NEON +// CHECK: __ARM_SIZEOF_MINIMAL_ENUM 4 +// CHECK: __ARM_SIZEOF_WCHAR_T 4 // CHECK: __aarch64__ // RUN: %clang -target aarch64-none-linux-gnu -ffast-math -x c -E -dM %s -o - | FileCheck --check-prefix=CHECK-FASTMATH %s -// CHECK-FASTMATH: __AARCH_FP_FAST +// CHECK-FASTMATH: __ARM_FP_FAST // RUN: %clang -target aarch64-none-linux-gnu -fshort-wchar -x c -E -dM %s -o - | FileCheck --check-prefix=CHECK-SHORTWCHAR %s -// CHECK-SHORTWCHAR: __AARCH_SIZEOF_WCHAR_T 2 +// CHECK-SHORTWCHAR: __ARM_SIZEOF_WCHAR_T 2 // RUN: %clang -target aarch64-none-linux-gnu -fshort-enums -x c -E -dM %s -o - | FileCheck --check-prefix=CHECK-SHORTENUMS %s -// CHECK-SHORTENUMS: __AARCH_SIZEOF_MINIMAL_ENUM 1 +// CHECK-SHORTENUMS: __ARM_SIZEOF_MINIMAL_ENUM 1 diff --git a/test/Preprocessor/cxx_oper_spelling.cpp b/test/Preprocessor/cxx_oper_spelling.cpp index 0ae9afd7eea..5152977ba2c 100644 --- a/test/Preprocessor/cxx_oper_spelling.cpp +++ b/test/Preprocessor/cxx_oper_spelling.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -E %s | grep 'a: "and"' +// RUN: %clang_cc1 -E %s | FileCheck %s #define X(A) #A @@ -8,4 +8,5 @@ // // This should be spelled as 'and', not '&&' a: X(and) +// CHECK: a: "and" diff --git a/test/Preprocessor/dependencies-and-pp.c b/test/Preprocessor/dependencies-and-pp.c index 7877df3f786..fb496380409 100644 --- a/test/Preprocessor/dependencies-and-pp.c +++ b/test/Preprocessor/dependencies-and-pp.c @@ -3,29 +3,34 @@ // RUN: %clang -E -o %t.1 %s // RUN: %clang -E -MD -MF %t.d -MT foo -o %t.2 %s // RUN: diff %t.1 %t.2 -// RUN: grep "foo:" %t.d -// RUN: grep "dependencies-and-pp.c" %t.d +// RUN: FileCheck -check-prefix=TEST1 %s < %t.d +// TEST1: foo: +// TEST1: dependencies-and-pp.c // Test -MQ flag without quoting // RUN: %clang -E -MD -MF %t.d -MQ foo -o %t %s -// RUN: grep "foo:" %t.d +// RUN: FileCheck -check-prefix=TEST2 %s < %t.d +// TEST2: foo: // Test -MQ flag with quoting // RUN: %clang -E -MD -MF %t.d -MQ '$fo\ooo ooo\ ooo\\ ooo#oo' -o %t %s -// RUN: fgrep '$$fo\ooo\ ooo\\\ ooo\\\\\ ooo\#oo:' %t.d +// RUN: FileCheck -check-prefix=TEST3 %s < %t.d +// TEST3: $$fo\ooo\ ooo\\\ ooo\\\\\ ooo\#oo: // Test consecutive -MT flags // RUN: %clang -E -MD -MF %t.d -MT foo -MT bar -MT baz -o %t %s // RUN: diff %t.1 %t -// RUN: fgrep "foo bar baz:" %t.d +// RUN: FileCheck -check-prefix=TEST4 %s < %t.d +// TEST4: foo bar baz: // Test consecutive -MT and -MQ flags // RUN: %clang -E -MD -MF %t.d -MT foo -MQ '$(bar)' -MT 'b az' -MQ 'qu ux' -MQ ' space' -o %t %s -// RUN: fgrep 'foo $$(bar) b az qu\ ux \ space:' %t.d +// RUN: FileCheck -check-prefix=TEST5 %s < %t.d +// TEST5: foo $$(bar) b az qu\ ux \ space: // TODO: Test default target without quoting // TODO: Test default target with quoting diff --git a/test/Preprocessor/init.c b/test/Preprocessor/init.c index 90b84663464..9671f7e2329 100644 --- a/test/Preprocessor/init.c +++ b/test/Preprocessor/init.c @@ -8,7 +8,17 @@ // BLOCKS:#define __BLOCKS__ 1 // BLOCKS:#define __block __attribute__((__blocks__(byref))) // -// +// +// RUN: %clang_cc1 -x c++ -std=c++1y -E -dM < /dev/null | FileCheck -check-prefix CXX1Y %s +// +// CXX1Y:#define __GNUG__ +// CXX1Y:#define __GXX_EXPERIMENTAL_CXX0X__ 1 +// CXX1Y:#define __GXX_RTTI 1 +// CXX1Y:#define __GXX_WEAK__ 1 +// CXX1Y:#define __cplusplus 201305L +// CXX1Y:#define __private_extern__ extern +// +// // RUN: %clang_cc1 -x c++ -std=c++11 -E -dM < /dev/null | FileCheck -check-prefix CXX11 %s // // CXX11:#define __GNUG__ @@ -67,6 +77,14 @@ // FREESTANDING:#define __STDC_HOSTED__ 0 // // +// RUN: %clang_cc1 -x c++ -std=gnu++1y -E -dM < /dev/null | FileCheck -check-prefix GXX1Y %s +// +// GXX1Y:#define __GNUG__ +// GXX1Y:#define __GXX_WEAK__ 1 +// GXX1Y:#define __cplusplus 201305L +// GXX1Y:#define __private_extern__ extern +// +// // RUN: %clang_cc1 -x c++ -std=gnu++11 -E -dM < /dev/null | FileCheck -check-prefix GXX11 %s // // GXX11:#define __GNUG__ @@ -88,10 +106,24 @@ // C94:#define __STDC_VERSION__ 199409L // // -// RUN: %clang_cc1 -fms-extensions -triple i686-pc-win32 -fobjc-runtime=gcc -E -dM < /dev/null | FileCheck -check-prefix MSEXT %s +// RUN: %clang_cc1 -fms-extensions -triple i686-pc-win32 -E -dM < /dev/null | FileCheck -check-prefix MSEXT %s // // MSEXT-NOT:#define __STDC__ // MSEXT:#define _INTEGRAL_MAX_BITS 64 +// MSEXT-NOT:#define _NATIVE_WCHAR_T_DEFINED 1 +// MSEXT-NOT:#define _WCHAR_T_DEFINED 1 +// +// +// RUN: %clang_cc1 -x c++ -fms-extensions -triple i686-pc-win32 -E -dM < /dev/null | FileCheck -check-prefix MSEXT-CXX %s +// +// MSEXT-CXX:#define _NATIVE_WCHAR_T_DEFINED 1 +// MSEXT-CXX:#define _WCHAR_T_DEFINED 1 +// +// +// RUN: %clang_cc1 -x c++ -fno-wchar -fms-extensions -triple i686-pc-win32 -E -dM < /dev/null | FileCheck -check-prefix MSEXT-CXX-NOWCHAR %s +// +// MSEXT-CXX-NOWCHAR-NOT:#define _NATIVE_WCHAR_T_DEFINED 1 +// MSEXT-CXX-NOWCHAR-NOT:#define _WCHAR_T_DEFINED 1 // // // RUN: %clang_cc1 -x objective-c -E -dM < /dev/null | FileCheck -check-prefix OBJC %s @@ -1150,6 +1182,12 @@ // MIPS-FABI-SINGLE:#define __mips_hard_float 1 // MIPS-FABI-SINGLE:#define __mips_single_float 1 // +// RUN: %clang_cc1 -target-feature +soft-float -target-feature +single-float \ +// RUN: -E -dM -ffreestanding -triple=mips-none-none < /dev/null \ +// RUN: | FileCheck -check-prefix MIPS-FABI-SINGLE-SOFT %s +// MIPS-FABI-SINGLE-SOFT:#define __mips_single_float 1 +// MIPS-FABI-SINGLE-SOFT:#define __mips_soft_float 1 +// // Check MIPS features macros // // RUN: %clang_cc1 -target-feature +mips16 \ @@ -1162,6 +1200,16 @@ // RUN: | FileCheck -check-prefix NOMIPS16 %s // NOMIPS16-NOT:#define __mips16 1 // +// RUN: %clang_cc1 -target-feature +micromips \ +// RUN: -E -dM -triple=mips-none-none < /dev/null \ +// RUN: | FileCheck -check-prefix MICROMIPS %s +// MICROMIPS:#define __mips_micromips 1 +// +// RUN: %clang_cc1 -target-feature -micromips \ +// RUN: -E -dM -triple=mips-none-none < /dev/null \ +// RUN: | FileCheck -check-prefix NOMICROMIPS %s +// NOMICROMIPS-NOT:#define __mips_micromips 1 +// // RUN: %clang_cc1 -target-feature +dsp \ // RUN: -E -dM -triple=mips-none-none < /dev/null \ // RUN: | FileCheck -check-prefix MIPS-DSP %s @@ -2147,6 +2195,98 @@ // PPC-LINUX:#define __powerpc__ 1 // PPC-LINUX:#define __ppc__ 1 // +// RUN: %clang_cc1 -E -dM -ffreestanding -triple=s390x-none-none -fno-signed-char < /dev/null | FileCheck -check-prefix S390X %s +// +// S390X:#define __CHAR16_TYPE__ unsigned short +// S390X:#define __CHAR32_TYPE__ unsigned int +// S390X:#define __CHAR_BIT__ 8 +// S390X:#define __CHAR_UNSIGNED__ 1 +// S390X:#define __DBL_DENORM_MIN__ 4.9406564584124654e-324 +// S390X:#define __DBL_DIG__ 15 +// S390X:#define __DBL_EPSILON__ 2.2204460492503131e-16 +// S390X:#define __DBL_HAS_DENORM__ 1 +// S390X:#define __DBL_HAS_INFINITY__ 1 +// S390X:#define __DBL_HAS_QUIET_NAN__ 1 +// S390X:#define __DBL_MANT_DIG__ 53 +// S390X:#define __DBL_MAX_10_EXP__ 308 +// S390X:#define __DBL_MAX_EXP__ 1024 +// S390X:#define __DBL_MAX__ 1.7976931348623157e+308 +// S390X:#define __DBL_MIN_10_EXP__ (-307) +// S390X:#define __DBL_MIN_EXP__ (-1021) +// S390X:#define __DBL_MIN__ 2.2250738585072014e-308 +// S390X:#define __DECIMAL_DIG__ 36 +// S390X:#define __FLT_DENORM_MIN__ 1.40129846e-45F +// S390X:#define __FLT_DIG__ 6 +// S390X:#define __FLT_EPSILON__ 1.19209290e-7F +// S390X:#define __FLT_EVAL_METHOD__ 0 +// S390X:#define __FLT_HAS_DENORM__ 1 +// S390X:#define __FLT_HAS_INFINITY__ 1 +// S390X:#define __FLT_HAS_QUIET_NAN__ 1 +// S390X:#define __FLT_MANT_DIG__ 24 +// S390X:#define __FLT_MAX_10_EXP__ 38 +// S390X:#define __FLT_MAX_EXP__ 128 +// S390X:#define __FLT_MAX__ 3.40282347e+38F +// S390X:#define __FLT_MIN_10_EXP__ (-37) +// S390X:#define __FLT_MIN_EXP__ (-125) +// S390X:#define __FLT_MIN__ 1.17549435e-38F +// S390X:#define __FLT_RADIX__ 2 +// S390X:#define __INT16_TYPE__ short +// S390X:#define __INT32_TYPE__ int +// S390X:#define __INT64_C_SUFFIX__ L +// S390X:#define __INT64_TYPE__ long long int +// S390X:#define __INT8_TYPE__ char +// S390X:#define __INTMAX_MAX__ 9223372036854775807LL +// S390X:#define __INTMAX_TYPE__ long long int +// S390X:#define __INTMAX_WIDTH__ 64 +// S390X:#define __INTPTR_TYPE__ long int +// S390X:#define __INTPTR_WIDTH__ 64 +// S390X:#define __INT_MAX__ 2147483647 +// S390X:#define __LDBL_DENORM_MIN__ 6.47517511943802511092443895822764655e-4966L +// S390X:#define __LDBL_DIG__ 33 +// S390X:#define __LDBL_EPSILON__ 1.92592994438723585305597794258492732e-34L +// S390X:#define __LDBL_HAS_DENORM__ 1 +// S390X:#define __LDBL_HAS_INFINITY__ 1 +// S390X:#define __LDBL_HAS_QUIET_NAN__ 1 +// S390X:#define __LDBL_MANT_DIG__ 113 +// S390X:#define __LDBL_MAX_10_EXP__ 4932 +// S390X:#define __LDBL_MAX_EXP__ 16384 +// S390X:#define __LDBL_MAX__ 1.18973149535723176508575932662800702e+4932L +// S390X:#define __LDBL_MIN_10_EXP__ (-4931) +// S390X:#define __LDBL_MIN_EXP__ (-16381) +// S390X:#define __LDBL_MIN__ 3.36210314311209350626267781732175260e-4932L +// S390X:#define __LONG_LONG_MAX__ 9223372036854775807LL +// S390X:#define __LONG_MAX__ 9223372036854775807L +// S390X:#define __NO_INLINE__ 1 +// S390X:#define __POINTER_WIDTH__ 64 +// S390X:#define __PTRDIFF_TYPE__ long int +// S390X:#define __PTRDIFF_WIDTH__ 64 +// S390X:#define __SCHAR_MAX__ 127 +// S390X:#define __SHRT_MAX__ 32767 +// S390X:#define __SIG_ATOMIC_WIDTH__ 32 +// S390X:#define __SIZEOF_DOUBLE__ 8 +// S390X:#define __SIZEOF_FLOAT__ 4 +// S390X:#define __SIZEOF_INT__ 4 +// S390X:#define __SIZEOF_LONG_DOUBLE__ 16 +// S390X:#define __SIZEOF_LONG_LONG__ 8 +// S390X:#define __SIZEOF_LONG__ 8 +// S390X:#define __SIZEOF_POINTER__ 8 +// S390X:#define __SIZEOF_PTRDIFF_T__ 8 +// S390X:#define __SIZEOF_SHORT__ 2 +// S390X:#define __SIZEOF_SIZE_T__ 8 +// S390X:#define __SIZEOF_WCHAR_T__ 4 +// S390X:#define __SIZEOF_WINT_T__ 4 +// S390X:#define __SIZE_TYPE__ long unsigned int +// S390X:#define __SIZE_WIDTH__ 64 +// S390X:#define __UINTMAX_TYPE__ long long unsigned int +// S390X:#define __USER_LABEL_PREFIX__ _ +// S390X:#define __WCHAR_MAX__ 2147483647 +// S390X:#define __WCHAR_TYPE__ int +// S390X:#define __WCHAR_WIDTH__ 32 +// S390X:#define __WINT_TYPE__ int +// S390X:#define __WINT_WIDTH__ 32 +// S390X:#define __s390__ 1 +// S390X:#define __s390x__ 1 +// // RUN: %clang_cc1 -E -dM -ffreestanding -triple=sparc-none-none < /dev/null | FileCheck -check-prefix SPARC %s // // SPARC-NOT:#define _LP64 diff --git a/test/Preprocessor/line-directive.c b/test/Preprocessor/line-directive.c index ffa7c5a4197..ea0a36fca35 100644 --- a/test/Preprocessor/line-directive.c +++ b/test/Preprocessor/line-directive.c @@ -29,7 +29,7 @@ # 42 "foo" 3 1 // expected-error {{invalid flag line marker directive}} # 42 "foo" 42 // expected-error {{invalid flag line marker directive}} # 42 "foo" 1 2 // expected-error {{invalid flag line marker directive}} - +# 42a33 // expected-error {{GNU line marker directive requires a simple digit sequence}} // These are checked by the RUN line. #line 92 "blonk.c" @@ -85,12 +85,20 @@ typedef int q; // original definition in system header, should not diagnose. #line 010 // expected-warning {{#line directive interprets number as decimal, not octal}} extern int array[__LINE__ == 10 ? 1:-1]; +# 020 // expected-warning {{GNU line marker directive interprets number as decimal, not octal}} +extern int array_gnuline[__LINE__ == 20 ? 1:-1]; + /* PR3917 */ #line 41 extern char array2[\ _\ _LINE__ == 42 ? 1: -1]; /* line marker is location of first _ */ +# 51 +extern char array2_gnuline[\ +_\ +_LINE__ == 52 ? 1: -1]; /* line marker is location of first _ */ + // rdar://11550996 #line 0 "line-directive.c" // expected-warning {{#line directive with zero argument is a GNU extension}} undefined t; // expected-error {{unknown type name 'undefined'}} diff --git a/test/Preprocessor/pp-modules.c b/test/Preprocessor/pp-modules.c new file mode 100644 index 00000000000..213a5fd23c8 --- /dev/null +++ b/test/Preprocessor/pp-modules.c @@ -0,0 +1,15 @@ +// RUN: rm -rf %t +// RUN: %clang_cc1 -fmodules -fmodules-cache-path=%t -x objective-c %s -F %S/../Modules/Inputs -E -o - | FileCheck %s + +// CHECK: int bar(); +int bar(); +// CHECK: @import Module; /* clang -E: implicit import for "{{.*Headers[/\\]Module.h}}" */ +#include +// CHECK: int foo(); +int foo(); +// CHECK: @import Module; /* clang -E: implicit import for "{{.*Headers[/\\]Module.h}}" */ +#include + +#include "pp-modules.h" // CHECK: # 1 "{{.*}}pp-modules.h" 1 +// CHECK: @import Module; /* clang -E: implicit import for "{{.*}}Module.h" */{{$}} +// CHECK: # 14 "{{.*}}pp-modules.c" 2 diff --git a/test/Preprocessor/pp-modules.h b/test/Preprocessor/pp-modules.h new file mode 100644 index 00000000000..e4ccacf1436 --- /dev/null +++ b/test/Preprocessor/pp-modules.h @@ -0,0 +1 @@ +#include diff --git a/test/Preprocessor/pragma-captured.c b/test/Preprocessor/pragma-captured.c new file mode 100644 index 00000000000..be2a62b5e47 --- /dev/null +++ b/test/Preprocessor/pragma-captured.c @@ -0,0 +1,13 @@ +// RUN: %clang_cc1 -E %s | FileCheck %s + +// Test pragma clang __debug captured, for Captured Statements + +void test1() +{ + #pragma clang __debug captured + { + } +// CHECK: void test1() +// CHECK: { +// CHECK: #pragma clang __debug captured +} diff --git a/test/Preprocessor/pragma_sysheader.c b/test/Preprocessor/pragma_sysheader.c index 075c9803a50..3c94363152a 100644 --- a/test/Preprocessor/pragma_sysheader.c +++ b/test/Preprocessor/pragma_sysheader.c @@ -7,7 +7,7 @@ // PR9861: Verify that line markers are not messed up in -E mode. // CHECK: # 1 "{{.*}}pragma_sysheader.h" 1 -// CHECK-NEXT: # 1 "{{.*}}pragma_sysheader.h" 3 +// CHECK-NEXT: # 2 "{{.*}}pragma_sysheader.h" 3 // CHECK-NEXT: typedef int x; // CHECK-NEXT: typedef int x; // CHECK-NEXT: # 6 "{{.*}}pragma_sysheader.c" 2 diff --git a/test/Preprocessor/predefined-arch-macros.c b/test/Preprocessor/predefined-arch-macros.c index 680f39af716..d0125cd0ed5 100644 --- a/test/Preprocessor/predefined-arch-macros.c +++ b/test/Preprocessor/predefined-arch-macros.c @@ -1094,6 +1094,52 @@ // CHECK_BTVER1_M64: #define __tune_btver1__ 1 // CHECK_BTVER1_M64: #define __x86_64 1 // CHECK_BTVER1_M64: #define __x86_64__ 1 +// RUN: %clang -march=btver2 -m32 -E -dM %s -o - 2>&1 \ +// RUN: -target i386-unknown-linux \ +// RUN: | FileCheck %s -check-prefix=CHECK_BTVER2_M32 +// CHECK_BTVER2_M32-NOT: #define __3dNOW_A__ 1 +// CHECK_BTVER2_M32-NOT: #define __3dNOW__ 1 +// CHECK_BTVER2_M32: #define __AES__ 1 +// CHECK_BTVER2_M32: #define __AVX__ 1 +// CHECK_BTVER2_M32: #define __LZCNT__ 1 +// CHECK_BTVER2_M32: #define __MMX__ 1 +// CHECK_BTVER2_M32: #define __POPCNT__ 1 +// CHECK_BTVER2_M32: #define __SSE2_MATH__ 1 +// CHECK_BTVER2_M32: #define __SSE2__ 1 +// CHECK_BTVER2_M32: #define __SSE3__ 1 +// CHECK_BTVER2_M32: #define __SSE4A__ 1 +// CHECK_BTVER2_M32: #define __SSE_MATH__ 1 +// CHECK_BTVER2_M32: #define __SSE__ 1 +// CHECK_BTVER2_M32: #define __SSSE3__ 1 +// CHECK_BTVER2_M32: #define __btver2 1 +// CHECK_BTVER2_M32: #define __btver2__ 1 +// CHECK_BTVER2_M32: #define __i386 1 +// CHECK_BTVER2_M32: #define __i386__ 1 +// CHECK_BTVER2_M32: #define __tune_btver2__ 1 +// RUN: %clang -march=btver2 -m64 -E -dM %s -o - 2>&1 \ +// RUN: -target i386-unknown-linux \ +// RUN: | FileCheck %s -check-prefix=CHECK_BTVER2_M64 +// CHECK_BTVER2_M64-NOT: #define __3dNOW_A__ 1 +// CHECK_BTVER2_M64-NOT: #define __3dNOW__ 1 +// CHECK_BTVER2_M64: #define __AES__ 1 +// CHECK_BTVER2_M64: #define __AVX__ 1 +// CHECK_BTVER2_M64: #define __LZCNT__ 1 +// CHECK_BTVER2_M64: #define __MMX__ 1 +// CHECK_BTVER2_M64: #define __POPCNT__ 1 +// CHECK_BTVER2_M64: #define __SSE2_MATH__ 1 +// CHECK_BTVER2_M64: #define __SSE2__ 1 +// CHECK_BTVER2_M64: #define __SSE3__ 1 +// CHECK_BTVER2_M64: #define __SSE4A__ 1 +// CHECK_BTVER2_M64: #define __SSE_MATH__ 1 +// CHECK_BTVER2_M64: #define __SSE__ 1 +// CHECK_BTVER2_M64: #define __SSSE3__ 1 +// CHECK_BTVER2_M64: #define __amd64 1 +// CHECK_BTVER2_M64: #define __amd64__ 1 +// CHECK_BTVER2_M64: #define __btver2 1 +// CHECK_BTVER2_M64: #define __btver2__ 1 +// CHECK_BTVER2_M64: #define __tune_btver2__ 1 +// CHECK_BTVER2_M64: #define __x86_64 1 +// CHECK_BTVER2_M64: #define __x86_64__ 1 // RUN: %clang -march=bdver1 -m32 -E -dM %s -o - 2>&1 \ // RUN: -target i386-unknown-linux \ // RUN: | FileCheck %s -check-prefix=CHECK_BDVER1_M32 diff --git a/test/Preprocessor/stdint.c b/test/Preprocessor/stdint.c index 70c106bf797..c3be05d97cd 100644 --- a/test/Preprocessor/stdint.c +++ b/test/Preprocessor/stdint.c @@ -528,6 +528,113 @@ // PPC:INTMAX_C_(0) 0LL // PPC:UINTMAX_C_(0) 0ULL // +// RUN: %clang_cc1 -E -ffreestanding -triple=s390x-none-none %s | FileCheck -check-prefix S390X %s +// +// S390X:typedef signed long long int int64_t; +// S390X:typedef unsigned long long int uint64_t; +// S390X:typedef int64_t int_least64_t; +// S390X:typedef uint64_t uint_least64_t; +// S390X:typedef int64_t int_fast64_t; +// S390X:typedef uint64_t uint_fast64_t; +// +// S390X:typedef signed int int32_t; +// S390X:typedef unsigned int uint32_t; +// S390X:typedef int32_t int_least32_t; +// S390X:typedef uint32_t uint_least32_t; +// S390X:typedef int32_t int_fast32_t; +// S390X:typedef uint32_t uint_fast32_t; +// +// S390X:typedef signed short int16_t; +// S390X:typedef unsigned short uint16_t; +// S390X:typedef int16_t int_least16_t; +// S390X:typedef uint16_t uint_least16_t; +// S390X:typedef int16_t int_fast16_t; +// S390X:typedef uint16_t uint_fast16_t; +// +// S390X:typedef signed char int8_t; +// S390X:typedef unsigned char uint8_t; +// S390X:typedef int8_t int_least8_t; +// S390X:typedef uint8_t uint_least8_t; +// S390X:typedef int8_t int_fast8_t; +// S390X:typedef uint8_t uint_fast8_t; +// +// S390X:typedef int64_t intptr_t; +// S390X:typedef uint64_t uintptr_t; +// +// S390X:typedef long long int intmax_t; +// S390X:typedef long long unsigned int uintmax_t; +// +// S390X:INT8_MAX_ 127 +// S390X:INT8_MIN_ (-127 -1) +// S390X:UINT8_MAX_ 255 +// S390X:INT_LEAST8_MIN_ (-127 -1) +// S390X:INT_LEAST8_MAX_ 127 +// S390X:UINT_LEAST8_MAX_ 255 +// S390X:INT_FAST8_MIN_ (-127 -1) +// S390X:INT_FAST8_MAX_ 127 +// S390X:UINT_FAST8_MAX_ 255 +// +// S390X:INT16_MAX_ 32767 +// S390X:INT16_MIN_ (-32767 -1) +// S390X:UINT16_MAX_ 65535 +// S390X:INT_LEAST16_MIN_ (-32767 -1) +// S390X:INT_LEAST16_MAX_ 32767 +// S390X:UINT_LEAST16_MAX_ 65535 +// S390X:INT_FAST16_MIN_ (-32767 -1) +// S390X:INT_FAST16_MAX_ 32767 +// S390X:UINT_FAST16_MAX_ 65535 +// +// S390X:INT32_MAX_ 2147483647 +// S390X:INT32_MIN_ (-2147483647 -1) +// S390X:UINT32_MAX_ 4294967295U +// S390X:INT_LEAST32_MIN_ (-2147483647 -1) +// S390X:INT_LEAST32_MAX_ 2147483647 +// S390X:UINT_LEAST32_MAX_ 4294967295U +// S390X:INT_FAST32_MIN_ (-2147483647 -1) +// S390X:INT_FAST32_MAX_ 2147483647 +// S390X:UINT_FAST32_MAX_ 4294967295U +// +// S390X:INT64_MAX_ 9223372036854775807L +// S390X:INT64_MIN_ (-9223372036854775807LL -1) +// S390X:UINT64_MAX_ 18446744073709551615UL +// S390X:INT_LEAST64_MIN_ (-9223372036854775807LL -1) +// S390X:INT_LEAST64_MAX_ 9223372036854775807L +// S390X:UINT_LEAST64_MAX_ 18446744073709551615UL +// S390X:INT_FAST64_MIN_ (-9223372036854775807LL -1) +// S390X:INT_FAST64_MAX_ 9223372036854775807L +// S390X:UINT_FAST64_MAX_ 18446744073709551615UL +// +// S390X:INTPTR_MIN_ (-9223372036854775807LL -1) +// S390X:INTPTR_MAX_ 9223372036854775807L +// S390X:UINTPTR_MAX_ 18446744073709551615UL +// S390X:PTRDIFF_MIN_ (-9223372036854775807LL -1) +// S390X:PTRDIFF_MAX_ 9223372036854775807L +// S390X:SIZE_MAX_ 18446744073709551615UL +// +// S390X:INTMAX_MIN_ (-9223372036854775807LL -1) +// S390X:INTMAX_MAX_ 9223372036854775807L +// S390X:UINTMAX_MAX_ 18446744073709551615UL +// +// S390X:SIG_ATOMIC_MIN_ (-2147483647 -1) +// S390X:SIG_ATOMIC_MAX_ 2147483647 +// S390X:WINT_MIN_ (-2147483647 -1) +// S390X:WINT_MAX_ 2147483647 +// +// S390X:WCHAR_MAX_ 2147483647 +// S390X:WCHAR_MIN_ (-2147483647 -1) +// +// S390X:INT8_C_(0) 0 +// S390X:UINT8_C_(0) 0U +// S390X:INT16_C_(0) 0 +// S390X:UINT16_C_(0) 0U +// S390X:INT32_C_(0) 0 +// S390X:UINT32_C_(0) 0U +// S390X:INT64_C_(0) 0L +// S390X:UINT64_C_(0) 0UL +// +// S390X:INTMAX_C_(0) 0L +// S390X:UINTMAX_C_(0) 0UL +// // RUN: %clang_cc1 -E -ffreestanding -triple=sparc-none-none %s | FileCheck -check-prefix SPARC %s // // SPARC:typedef signed long long int int64_t; diff --git a/test/Rewriter/rewrite-byref-in-nested-blocks.mm b/test/Rewriter/rewrite-byref-in-nested-blocks.mm index 022bb5f4e35..f416b6622f4 100644 --- a/test/Rewriter/rewrite-byref-in-nested-blocks.mm +++ b/test/Rewriter/rewrite-byref-in-nested-blocks.mm @@ -19,7 +19,7 @@ void f(void (^block)(void)); - (void)foo { __block int kerfluffle; // radar 7692183 - __block x; + __block int x; f(^{ f(^{ y = 42; diff --git a/test/Sema/MicrosoftCompatibility.cpp b/test/Sema/MicrosoftCompatibility.cpp new file mode 100644 index 00000000000..15c25586c47 --- /dev/null +++ b/test/Sema/MicrosoftCompatibility.cpp @@ -0,0 +1,4 @@ +// RUN: %clang_cc1 %s -fsyntax-only -Wno-unused-value -Wmicrosoft -verify -fms-compatibility + +// PR15845 +int foo(xxx); // expected-error{{unknown type name}} diff --git a/test/Sema/arm-neon-types.c b/test/Sema/arm-neon-types.c index 1a170dbb7e0..a49de12d448 100644 --- a/test/Sema/arm-neon-types.c +++ b/test/Sema/arm-neon-types.c @@ -1,4 +1,5 @@ // RUN: %clang_cc1 -triple thumbv7-apple-darwin10 -target-cpu cortex-a8 -fsyntax-only -Wvector-conversion -ffreestanding -verify %s +#ifndef INCLUDE #include @@ -33,3 +34,14 @@ int16x8_t test5(int *p) { void test6(float *p, int32x2_t v) { return vst1_s32(p, v); // expected-warning {{incompatible pointer types}} } + +#define INCLUDE +#include "arm-neon-types.c" +#else + +// Make sure we don't get a warning about using a static function in an +// extern inline function from a header. +extern inline uint8x8_t test7(uint8x8_t a, uint8x8_t b) { + return vadd_u8(a, b); +} +#endif diff --git a/test/Sema/array-init.c b/test/Sema/array-init.c index b3cae60495c..f92852f341b 100644 --- a/test/Sema/array-init.c +++ b/test/Sema/array-init.c @@ -187,7 +187,7 @@ char r6[sizeof r5 == 15 ? 1 : -1]; const char r7[] = "zxcv"; char r8[5] = "5char"; char r9[5] = "6chars"; //expected-warning{{initializer-string for char array is too long}} - +unsigned char r10[] = __extension__ (_Generic(0, int: (__extension__ "foo" ))); int r11[0] = {}; //expected-warning{{zero size arrays are an extension}} expected-warning{{use of GNU empty initializer extension}} // Some struct tests diff --git a/test/Sema/asm.c b/test/Sema/asm.c index 2c600854bf8..c81f16a3875 100644 --- a/test/Sema/asm.c +++ b/test/Sema/asm.c @@ -130,3 +130,19 @@ void test14(struct S *s) { __asm("": : "a"(*s)); // expected-error {{dereference of pointer to incomplete type 'struct S'}} __asm("": "=a" (*s) :); // expected-error {{dereference of pointer to incomplete type 'struct S'}} } + +// PR15759. +double test15() { + double ret = 0; + __asm("0.0":"="(ret)); // expected-error {{invalid output constraint '=' in asm}} + __asm("0.0":"=&"(ret)); // expected-error {{invalid output constraint '=&' in asm}} + __asm("0.0":"+?"(ret)); // expected-error {{invalid output constraint '+?' in asm}} + __asm("0.0":"+!"(ret)); // expected-error {{invalid output constraint '+!' in asm}} + __asm("0.0":"+#"(ret)); // expected-error {{invalid output constraint '+#' in asm}} + __asm("0.0":"+*"(ret)); // expected-error {{invalid output constraint '+*' in asm}} + __asm("0.0":"=%"(ret)); // expected-error {{invalid output constraint '=%' in asm}} + __asm("0.0":"=,="(ret)); // expected-error {{invalid output constraint '=,=' in asm}} + __asm("0.0":"=,g"(ret)); // no-error + __asm("0.0":"=g"(ret)); // no-error + return ret; +} diff --git a/test/Sema/atomic-expr.c b/test/Sema/atomic-expr.c new file mode 100644 index 00000000000..ecc04c4c68d --- /dev/null +++ b/test/Sema/atomic-expr.c @@ -0,0 +1,47 @@ +// RUN: %clang_cc1 %s -verify -fsyntax-only +// expected-no-diagnostics + +_Atomic(unsigned int) data1; +int _Atomic data2; + +// Shift operations + +int func_01 (int x) { + return data1 << x; +} + +int func_02 (int x) { + return x << data1; +} + +int func_03 (int x) { + return data2 << x; +} + +int func_04 (int x) { + return x << data2; +} + +int func_05 () { + return data2 << data1; +} + +int func_06 () { + return data1 << data2; +} + +void func_07 (int x) { + data1 <<= x; +} + +void func_08 (int x) { + data2 <<= x; +} + +void func_09 (int* xp) { + *xp <<= data1; +} + +void func_10 (int* xp) { + *xp <<= data2; +} diff --git a/test/Sema/bitfield.c b/test/Sema/bitfield.c index a1ce894037f..ab05a7773d9 100644 --- a/test/Sema/bitfield.c +++ b/test/Sema/bitfield.c @@ -39,3 +39,18 @@ int y; struct PR8025 { double : 2; // expected-error{{anonymous bit-field has non-integral type 'double'}} }; + +struct Test4 { + unsigned bitX : 4; + unsigned bitY : 4; + unsigned var; +}; +void test4(struct Test4 *t) { + (void) sizeof(t->bitX); // expected-error {{invalid application of 'sizeof' to bit-field}} + (void) sizeof((t->bitY)); // expected-error {{invalid application of 'sizeof' to bit-field}} + (void) sizeof(t->bitX = 4); // not a bitfield designator in C + (void) sizeof(t->bitX += 4); // not a bitfield designator in C + (void) sizeof((void) 0, t->bitX); // not a bitfield designator in C + (void) sizeof(t->var ? t->bitX : t->bitY); // not a bitfield designator in C + (void) sizeof(t->var ? t->bitX : t->bitX); // not a bitfield designator in C +} diff --git a/test/Sema/builtins-aarch64.c b/test/Sema/builtins-aarch64.c new file mode 100644 index 00000000000..03e03343eb9 --- /dev/null +++ b/test/Sema/builtins-aarch64.c @@ -0,0 +1,16 @@ +// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -fsyntax-only -verify %s + +void test_clear_cache_chars(char *start, char *end) { + __clear_cache(start, end); +} + +void test_clear_cache_voids(void *start, void *end) { + __clear_cache(start, end); +} + +void test_clear_cache_no_args() { + // AArch32 version of this is variadic (at least syntactically). + // However, on AArch64 GCC does not permit this call and the + // implementation I've seen would go disastrously wrong. + __clear_cache(); // expected-error {{too few arguments to function call}} +} diff --git a/test/Sema/captured-statements.c b/test/Sema/captured-statements.c new file mode 100644 index 00000000000..9285a7802d5 --- /dev/null +++ b/test/Sema/captured-statements.c @@ -0,0 +1,78 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s -fblocks + +void test_gotos() { + goto L1; // expected-error {{use of undeclared label 'L1'}} + goto L3; // OK + #pragma clang __debug captured + { +L1: + goto L2; // OK +L2: + goto L3; // expected-error {{use of undeclared label 'L3'}} + } +L3: ; +} + +void test_break_continue() { + while (1) { + #pragma clang __debug captured + { + break; // expected-error {{'break' statement not in loop or switch statement}} + continue; // expected-error {{'continue' statement not in loop statement}} + } + } +} + +void test_return() { + while (1) { + #pragma clang __debug captured + { + return; // expected-error {{cannot return from default captured statement}} + } + } +} + +void test_nest() { + int x; + #pragma clang __debug captured + { + int y; + #pragma clang __debug captured + { + int z; + #pragma clang __debug captured + { + x = z = y; // OK + } + } + } +} + +void test_nest_block() { + __block int x; + int y; + ^{ + int z; + #pragma clang __debug captured + { + x = y; // OK + y = z; // expected-error{{variable is not assignable (missing __block type specifier)}} + z = y; // OK + } + }(); + + __block int a; + int b; + #pragma clang __debug captured + { + __block int c; + int d; + ^{ + a = b; // OK + a = c; // OK + b = d; // OK - Consistent with block inside a lambda + c = a; // OK + d = b; // expected-error{{variable is not assignable (missing __block type specifier)}} + }(); + } +} diff --git a/test/Sema/crash-invalid-array.c b/test/Sema/crash-invalid-array.c index a3bc03b70b5..eeac39148ca 100644 --- a/test/Sema/crash-invalid-array.c +++ b/test/Sema/crash-invalid-array.c @@ -15,3 +15,10 @@ int main() p[i][i] = i; } } + +// rdar://13705391 +void foo(int a[*][2]) {(void)a[0][1]; } // expected-error {{variable length array must be bound in function definition}} +void foo1(int a[2][*]) {(void)a[0][1]; } // expected-error {{variable length array must be bound in function definition}} +void foo2(int a[*][*]) {(void)a[0][1]; } // expected-error {{variable length array must be bound in function definition}} +void foo3(int a[2][*][2]) {(void)a[0][1][1]; } // expected-error {{variable length array must be bound in function definition}} +void foo4(int a[2][*][*]) {(void)a[0][1][1]; } // expected-error {{variable length array must be bound in function definition}} diff --git a/test/Sema/extern-redecl.c b/test/Sema/extern-redecl.c index 9a085de0c00..e9a4c571bd2 100644 --- a/test/Sema/extern-redecl.c +++ b/test/Sema/extern-redecl.c @@ -33,3 +33,32 @@ void test3declarer() { extern int test3_array[]; int x = sizeof(test3_array); // expected-error {{invalid application of 'sizeof' to an incomplete type 'int []'}} } + +void test4() { + extern int test4_array[]; + { + extern int test4_array[100]; + int x = sizeof(test4_array); // fine + } + int x = sizeof(test4_array); // expected-error {{invalid application of 'sizeof' to an incomplete type 'int []'}} +} + +// Test that invalid local extern declarations of library +// builtins behave reasonably. +extern void abort(void); // expected-note 2 {{previous declaration is here}} +extern float *calloc(); // expected-warning {{incompatible redeclaration of library function}} expected-note {{is a builtin}} expected-note 2 {{previous declaration is here}} +void test5a() { + int abort(); // expected-error {{conflicting types}} + float *malloc(); // expected-warning {{incompatible redeclaration of library function}} expected-note 2 {{is a builtin}} + int *calloc(); // expected-error {{conflicting types}} +} +void test5b() { + int abort(); // expected-error {{conflicting types}} + float *malloc(); // expected-warning {{incompatible redeclaration of library function}} + int *calloc(); // expected-error {{conflicting types}} +} +void test5c() { + void (*_abort)(void) = &abort; + void *(*_malloc)() = &malloc; + float *(*_calloc)() = &calloc; +} diff --git a/test/Sema/function-redecl.c b/test/Sema/function-redecl.c index 3ee8763a563..561f7fae6b9 100644 --- a/test/Sema/function-redecl.c +++ b/test/Sema/function-redecl.c @@ -62,7 +62,7 @@ void test2() { // int outer1(int); // expected-note{{previous declaration is here}} struct outer3 { }; -int outer4(int); +int outer4(int); // expected-note{{previous declaration is here}} int outer5; // expected-note{{previous definition is here}} int *outer7(int); @@ -70,7 +70,7 @@ void outer_test() { int outer1(float); // expected-error{{conflicting types for 'outer1'}} int outer2(int); // expected-note{{previous declaration is here}} int outer3(int); // expected-note{{previous declaration is here}} - int outer4(int); // expected-note{{previous declaration is here}} + int outer4(int); int outer5(int); // expected-error{{redefinition of 'outer5' as different kind of symbol}} int* outer6(int); // expected-note{{previous declaration is here}} int *outer7(int); diff --git a/test/Sema/function.c b/test/Sema/function.c index 1b0dc2adeb7..bbf81a56cbb 100644 --- a/test/Sema/function.c +++ b/test/Sema/function.c @@ -92,3 +92,14 @@ void t20(int i...) { } // expected-error {{requires a comma}} int n; void t21(int n, int (*array)[n]); + +int func_e(int x) { + int func_n(int y) { // expected-error {{function definition is not allowed here}} + if (y > 22) { + return y+2; + } else { + return y-2; + } + } + return x + 3; +} diff --git a/test/Sema/no-documentation-warn-tagdecl-specifier.c b/test/Sema/no-documentation-warn-tagdecl-specifier.c new file mode 100644 index 00000000000..a0702ad7dfa --- /dev/null +++ b/test/Sema/no-documentation-warn-tagdecl-specifier.c @@ -0,0 +1,85 @@ +// RUN: %clang_cc1 -fsyntax-only -Wdocumentation -verify %s +// rdar://12390371 + +/** @return s Test*/ +struct s* f(void); +struct s; + +struct s1; +/** @return s1 Test 1*/ +struct s1* f1(void); + +struct s2; +/** @return s2 Test 2*/ +struct s2* f2(void); +struct s2; + +// expected-warning@+1 {{'@return' command used in a comment that is not attached to a function or method declaration}} +/** @return s3 Test 3 - expected warning here */ +struct s3; +struct s3* f3(void); + +/** @return s4 Test 4 */ +struct s4* f4(void); +struct s4 { int is; }; + +// expected-warning@+1 {{'@return' command used in a comment that is not attached to a function or method declaration}} +/** @return s5 Test 5 - expected warning here */ +struct s5 { int is; }; +struct s5* f5(void); + +// expected-warning@+1 {{'@return' command used in a comment that is not attached to a function or method declaration}} +/** @return s6 Test 6 - expected warning here */ +struct s6 *ps6; +struct s6* f6(void); + +// expected-warning@+1 {{'@return' command used in a comment that is not attached to a function or method declaration}} +/** @return s7 Test 7 - expected warning here */ +struct s7; +struct s7* f7(void); + +struct s8 { int is8; }; +/** @return s8 Test 8 */ +struct s4 *f8(struct s8 *p); + + +/** @return e Test*/ +enum e* g(void); +enum e; + +enum e1; +/** @return e1 Test 1*/ +enum e1* g1(void); + +enum e2; +/** @return e2 Test 2*/ +enum e2* g2(void); +enum e2; + +// expected-warning@+1 {{'@return' command used in a comment that is not attached to a function or method declaration}} +/** @return e3 Test 3 - expected warning here */ +enum e3; +enum e3* g3(void); + +/** @return e4 Test 4 */ +enum e4* g4(void); +enum e4 { one }; + +// expected-warning@+1 {{'@return' command used in a comment that is not attached to a function or method declaration}} +/** @return e5 Test 5 - expected warning here */ +enum e5 { two }; +enum e5* g5(void); + +// expected-warning@+1 {{'@return' command used in a comment that is not attached to a function or method declaration}} +/** @return e6 Test 6 - expected warning here */ +enum e6 *pe6; +enum e6* g6(void); + +// expected-warning@+1 {{'@return' command used in a comment that is not attached to a function or method declaration}} +/** @return e7 Test 7 - expected warning here */ +enum e7; +enum e7* g7(void); + +enum e8 { three }; +/** @return e8 Test 8 */ +enum e4 *g8(enum e8 *p); diff --git a/test/Sema/parentheses.c b/test/Sema/parentheses.c index c3b3aa77c5e..300e5855886 100644 --- a/test/Sema/parentheses.c +++ b/test/Sema/parentheses.c @@ -1,25 +1,44 @@ // RUN: %clang_cc1 -Wparentheses -fsyntax-only -verify %s -// RUN: %clang_cc1 -Wparentheses -fixit %s -o - | %clang_cc1 -Wparentheses -Werror - +// RUN: %clang_cc1 -Wparentheses -fsyntax-only -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s // Test the various warnings under -Wparentheses void if_assign(void) { int i; if (i = 4) {} // expected-warning {{assignment as a condition}} \ - // expected-note{{use '==' to turn this assignment into an equality comparison}} \ - // expected-note{{place parentheses around the assignment to silence this warning}} + // expected-note{{place parentheses around the assignment to silence this warning}} \ + // expected-note{{use '==' to turn this assignment into an equality comparison}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:7-[[@LINE-3]]:7}:"(" + // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:12-[[@LINE-4]]:12}:")" + // CHECK: fix-it:"{{.*}}":{[[@LINE-5]]:9-[[@LINE-5]]:10}:"==" + if ((i = 4)) {} } void bitwise_rel(unsigned i) { (void)(i & 0x2 == 0); // expected-warning {{& has lower precedence than ==}} \ - // expected-note{{place parentheses around the & expression to evaluate it first}} \ - // expected-note{{place parentheses around the '==' expression to silence this warning}} + // expected-note{{place parentheses around the '==' expression to silence this warning}} \ + // expected-note{{place parentheses around the & expression to evaluate it first}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:14-[[@LINE-3]]:14}:"(" + // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:22-[[@LINE-4]]:22}:")" + // CHECK: fix-it:"{{.*}}":{[[@LINE-5]]:10-[[@LINE-5]]:10}:"(" + // CHECK: fix-it:"{{.*}}":{[[@LINE-6]]:17-[[@LINE-6]]:17}:")" + (void)(0 == i & 0x2); // expected-warning {{& has lower precedence than ==}} \ - // expected-note{{place parentheses around the & expression to evaluate it first}} \ - // expected-note{{place parentheses around the '==' expression to silence this warning}} + // expected-note{{place parentheses around the '==' expression to silence this warning}} \ + // expected-note{{place parentheses around the & expression to evaluate it first}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:10-[[@LINE-3]]:10}:"(" + // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:16-[[@LINE-4]]:16}:")" + // CHECK: fix-it:"{{.*}}":{[[@LINE-5]]:15-[[@LINE-5]]:15}:"(" + // CHECK: fix-it:"{{.*}}":{[[@LINE-6]]:22-[[@LINE-6]]:22}:")" + (void)(i & 0xff < 30); // expected-warning {{& has lower precedence than <}} \ - // expected-note{{place parentheses around the & expression to evaluate it first}} \ - // expected-note{{place parentheses around the '<' expression to silence this warning}} + // expected-note{{place parentheses around the '<' expression to silence this warning}} \ + // expected-note{{place parentheses around the & expression to evaluate it first}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:14-[[@LINE-3]]:14}:"(" + // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:23-[[@LINE-4]]:23}:")" + // CHECK: fix-it:"{{.*}}":{[[@LINE-5]]:10-[[@LINE-5]]:10}:"(" + // CHECK: fix-it:"{{.*}}":{[[@LINE-6]]:18-[[@LINE-6]]:18}:")" + (void)((i & 0x2) == 0); (void)(i & (0x2 == 0)); // Eager logical op @@ -28,19 +47,33 @@ void bitwise_rel(unsigned i) { (void)(i & i | i); // expected-warning {{'&' within '|'}} \ // expected-note {{place parentheses around the '&' expression to silence this warning}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:10-[[@LINE-2]]:10}:"(" + // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:15-[[@LINE-3]]:15}:")" (void)(i | i & i); // expected-warning {{'&' within '|'}} \ // expected-note {{place parentheses around the '&' expression to silence this warning}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:14-[[@LINE-2]]:14}:"(" + // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:19-[[@LINE-3]]:19}:")" (void)(i || i && i); // expected-warning {{'&&' within '||'}} \ - // expected-note {{place parentheses around the '&&' expression to silence this warning}} + // expected-note {{place parentheses around the '&&' expression to silence this warning}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:14-[[@LINE-2]]:14}:"(" + // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:20-[[@LINE-3]]:20}:")" + (void)(i || i && "w00t"); // no warning. (void)("w00t" && i || i); // no warning. + (void)(i || i && "w00t" || i); // expected-warning {{'&&' within '||'}} \ // expected-note {{place parentheses around the '&&' expression to silence this warning}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:15-[[@LINE-2]]:15}:"(" + // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:26-[[@LINE-3]]:26}:")" + (void)(i || "w00t" && i || i); // expected-warning {{'&&' within '||'}} \ // expected-note {{place parentheses around the '&&' expression to silence this warning}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:15-[[@LINE-2]]:15}:"(" + // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:26-[[@LINE-3]]:26}:")" + (void)(i && i || 0); // no warning. (void)(0 || i && i); // no warning. } @@ -49,25 +82,41 @@ _Bool someConditionFunc(); void conditional_op(int x, int y, _Bool b) { (void)(x + someConditionFunc() ? 1 : 2); // expected-warning {{operator '?:' has lower precedence than '+'}} \ - // expected-note {{place parentheses around the '?:' expression to evaluate it first}} \ - // expected-note {{place parentheses around the '+' expression to silence this warning}} + // expected-note {{place parentheses around the '+' expression to silence this warning}} \ + // expected-note {{place parentheses around the '?:' expression to evaluate it first}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:10-[[@LINE-3]]:10}:"(" + // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:33-[[@LINE-4]]:33}:")" + // CHECK: fix-it:"{{.*}}":{[[@LINE-5]]:14-[[@LINE-5]]:14}:"(" + // CHECK: fix-it:"{{.*}}":{[[@LINE-6]]:41-[[@LINE-6]]:41}:")" (void)((x + someConditionFunc()) ? 1 : 2); // no warning (void)(x - b ? 1 : 2); // expected-warning {{operator '?:' has lower precedence than '-'}} \ - // expected-note {{place parentheses around the '?:' expression to evaluate it first}} \ - // expected-note {{place parentheses around the '-' expression to silence this warning}} + // expected-note {{place parentheses around the '-' expression to silence this warning}} \ + // expected-note {{place parentheses around the '?:' expression to evaluate it first}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:10-[[@LINE-3]]:10}:"(" + // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:15-[[@LINE-4]]:15}:")" + // CHECK: fix-it:"{{.*}}":{[[@LINE-5]]:14-[[@LINE-5]]:14}:"(" + // CHECK: fix-it:"{{.*}}":{[[@LINE-6]]:23-[[@LINE-6]]:23}:")" (void)(x * (x == y) ? 1 : 2); // expected-warning {{operator '?:' has lower precedence than '*'}} \ - // expected-note {{place parentheses around the '?:' expression to evaluate it first}} \ - // expected-note {{place parentheses around the '*' expression to silence this warning}} + // expected-note {{place parentheses around the '*' expression to silence this warning}} \ + // expected-note {{place parentheses around the '?:' expression to evaluate it first}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:10-[[@LINE-3]]:10}:"(" + // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:22-[[@LINE-4]]:22}:")" + // CHECK: fix-it:"{{.*}}":{[[@LINE-5]]:14-[[@LINE-5]]:14}:"(" + // CHECK: fix-it:"{{.*}}":{[[@LINE-6]]:30-[[@LINE-6]]:30}:")" (void)(x / !x ? 1 : 2); // expected-warning {{operator '?:' has lower precedence than '/'}} \ - // expected-note {{place parentheses around the '?:' expression to evaluate it first}} \ - // expected-note {{place parentheses around the '/' expression to silence this warning}} + // expected-note {{place parentheses around the '/' expression to silence this warning}} \ + // expected-note {{place parentheses around the '?:' expression to evaluate it first}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:10-[[@LINE-3]]:10}:"(" + // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:16-[[@LINE-4]]:16}:")" + // CHECK: fix-it:"{{.*}}":{[[@LINE-5]]:14-[[@LINE-5]]:14}:"(" + // CHECK: fix-it:"{{.*}}":{[[@LINE-6]]:24-[[@LINE-6]]:24}:")" (void)(x % 2 ? 1 : 2); // no warning } -// RUN: %clang_cc1 -fsyntax-only -Wparentheses -Werror -fdiagnostics-show-option %s 2>&1 | FileCheck %s -// CHECK: error: using the result of an assignment as a condition without parentheses [-Werror,-Wparentheses] +// RUN: %clang_cc1 -fsyntax-only -Wparentheses -Werror -fdiagnostics-show-option %s 2>&1 | FileCheck %s -check-prefix=CHECK-FLAG +// CHECK-FLAG: error: using the result of an assignment as a condition without parentheses [-Werror,-Wparentheses] diff --git a/test/Sema/parentheses.cpp b/test/Sema/parentheses.cpp index da37dd397bb..ac2694f72e1 100644 --- a/test/Sema/parentheses.cpp +++ b/test/Sema/parentheses.cpp @@ -1,20 +1,32 @@ // RUN: %clang_cc1 -Wparentheses -fsyntax-only -verify %s -// RUN: %clang_cc1 -Wparentheses -fixit %s -o - | %clang_cc1 -Wparentheses -Werror - +// RUN: %clang_cc1 -Wparentheses -fsyntax-only -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s bool someConditionFunc(); void conditional_op(int x, int y, bool b) { (void)(x + someConditionFunc() ? 1 : 2); // expected-warning {{operator '?:' has lower precedence than '+'}} \ - // expected-note {{place parentheses around the '?:' expression to evaluate it first}} \ - // expected-note {{place parentheses around the '+' expression to silence this warning}} + // expected-note {{place parentheses around the '+' expression to silence this warning}} \ + // expected-note {{place parentheses around the '?:' expression to evaluate it first}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:10-[[@LINE-3]]:10}:"(" + // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:33-[[@LINE-4]]:33}:")" + // CHECK: fix-it:"{{.*}}":{[[@LINE-5]]:14-[[@LINE-5]]:14}:"(" + // CHECK: fix-it:"{{.*}}":{[[@LINE-6]]:41-[[@LINE-6]]:41}:")" (void)(x - b ? 1 : 2); // expected-warning {{operator '?:' has lower precedence than '-'}} \ - // expected-note {{place parentheses around the '?:' expression to evaluate it first}} \ - // expected-note {{place parentheses around the '-' expression to silence this warning}} + // expected-note {{place parentheses around the '-' expression to silence this warning}} \ + // expected-note {{place parentheses around the '?:' expression to evaluate it first}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:10-[[@LINE-3]]:10}:"(" + // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:15-[[@LINE-4]]:15}:")" + // CHECK: fix-it:"{{.*}}":{[[@LINE-5]]:14-[[@LINE-5]]:14}:"(" + // CHECK: fix-it:"{{.*}}":{[[@LINE-6]]:23-[[@LINE-6]]:23}:")" (void)(x * (x == y) ? 1 : 2); // expected-warning {{operator '?:' has lower precedence than '*'}} \ - // expected-note {{place parentheses around the '?:' expression to evaluate it first}} \ - // expected-note {{place parentheses around the '*' expression to silence this warning}} + // expected-note {{place parentheses around the '*' expression to silence this warning}} \ + // expected-note {{place parentheses around the '?:' expression to evaluate it first}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:10-[[@LINE-3]]:10}:"(" + // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:22-[[@LINE-4]]:22}:")" + // CHECK: fix-it:"{{.*}}":{[[@LINE-5]]:14-[[@LINE-5]]:14}:"(" + // CHECK: fix-it:"{{.*}}":{[[@LINE-6]]:30-[[@LINE-6]]:30}:")" } class Stream { @@ -28,8 +40,28 @@ class Stream { void f(Stream& s, bool b) { (void)(s << b ? "foo" : "bar"); // expected-warning {{operator '?:' has lower precedence than '<<'}} \ - // expected-note {{place parentheses around the '?:' expression to evaluate it first}} \ - // expected-note {{place parentheses around the '<<' expression to silence this warning}} + // expected-note {{place parentheses around the '<<' expression to silence this warning}} \ + // expected-note {{place parentheses around the '?:' expression to evaluate it first}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:10-[[@LINE-3]]:10}:"(" + // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:16-[[@LINE-4]]:16}:")" + // CHECK: fix-it:"{{.*}}":{[[@LINE-5]]:15-[[@LINE-5]]:15}:"(" + // CHECK: fix-it:"{{.*}}":{[[@LINE-6]]:32-[[@LINE-6]]:32}:")" + + (void)(s << 5 == 1); // expected-warning {{overloaded operator << has lower precedence than comparison operator}} \ + // expected-note {{place parentheses around the '<<' expression to silence this warning}} \ + // expected-note {{place parentheses around comparison expression to evaluate it first}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:10-[[@LINE-3]]:10}:"(" + // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:16-[[@LINE-4]]:16}:")" + // CHECK: fix-it:"{{.*}}":{[[@LINE-5]]:15-[[@LINE-5]]:15}:"(" + // CHECK: fix-it:"{{.*}}":{[[@LINE-6]]:21-[[@LINE-6]]:21}:")" + + (void)(s >> 5 == 1); // expected-warning {{overloaded operator >> has lower precedence than comparison operator}} \ + // expected-note {{place parentheses around the '>>' expression to silence this warning}} \ + // expected-note {{place parentheses around comparison expression to evaluate it first}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:10-[[@LINE-3]]:10}:"(" + // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:16-[[@LINE-4]]:16}:")" + // CHECK: fix-it:"{{.*}}":{[[@LINE-5]]:15-[[@LINE-5]]:15}:"(" + // CHECK: fix-it:"{{.*}}":{[[@LINE-6]]:21-[[@LINE-6]]:21}:")" } struct S { @@ -39,8 +71,12 @@ struct S { void test(S *s, bool (S::*m_ptr)()) { (void)(*s + true ? "foo" : "bar"); // expected-warning {{operator '?:' has lower precedence than '+'}} \ - // expected-note {{place parentheses around the '?:' expression to evaluate it first}} \ - // expected-note {{place parentheses around the '+' expression to silence this warning}} + // expected-note {{place parentheses around the '+' expression to silence this warning}} \ + // expected-note {{place parentheses around the '?:' expression to evaluate it first}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:10-[[@LINE-3]]:10}:"(" + // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:19-[[@LINE-4]]:19}:")" + // CHECK: fix-it:"{{.*}}":{[[@LINE-5]]:15-[[@LINE-5]]:15}:"(" + // CHECK: fix-it:"{{.*}}":{[[@LINE-6]]:35-[[@LINE-6]]:35}:")" (void)((*s + true) ? "foo" : "bar"); // No warning. @@ -51,11 +87,19 @@ void test(S *s, bool (S::*m_ptr)()) { void test(int a, int b, int c) { (void)(a >> b + c); // expected-warning {{operator '>>' has lower precedence than '+'; '+' will be evaluated first}} \ expected-note {{place parentheses around the '+' expression to silence this warning}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:15-[[@LINE-2]]:15}:"(" + // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:20-[[@LINE-3]]:20}:")" + (void)(a - b << c); // expected-warning {{operator '<<' has lower precedence than '-'; '-' will be evaluated first}} \ expected-note {{place parentheses around the '-' expression to silence this warning}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:10-[[@LINE-2]]:10}:"(" + // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:15-[[@LINE-3]]:15}:")" + Stream() << b + c; Stream() >> b + c; // expected-warning {{operator '>>' has lower precedence than '+'; '+' will be evaluated first}} \ expected-note {{place parentheses around the '+' expression to silence this warning}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:15-[[@LINE-2]]:15}:"(" + // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:20-[[@LINE-3]]:20}:")" } namespace PR15628 { diff --git a/test/Sema/pragma-arc-cf-code-audited.c b/test/Sema/pragma-arc-cf-code-audited.c index b646e8966c6..c1aa804557a 100644 --- a/test/Sema/pragma-arc-cf-code-audited.c +++ b/test/Sema/pragma-arc-cf-code-audited.c @@ -13,6 +13,6 @@ #include "Inputs/pragma-arc-cf-code-audited.h" // expected-error {{cannot #include files inside '#pragma clang arc_cf_code_audited'}} // This is actually on the #pragma line in the header. -// expected-error {{'#pragma clang arc_cf_code_audited' was not ended within this file}} +// expected-error@Inputs/pragma-arc-cf-code-audited.h:16 {{'#pragma clang arc_cf_code_audited' was not ended within this file}} #pragma clang arc_cf_code_audited begin // expected-error {{'#pragma clang arc_cf_code_audited' was not ended within this file}} diff --git a/test/Sema/return.c b/test/Sema/return.c index e231e81b097..7e7c8b7b844 100644 --- a/test/Sema/return.c +++ b/test/Sema/return.c @@ -197,8 +197,14 @@ int test29() { exit(1); } -#include +// Include these declarations here explicitly so we don't depend on system headers. +typedef struct __jmp_buf_tag{} jmp_buf[1]; + +extern void longjmp (struct __jmp_buf_tag __env[1], int __val) __attribute__ ((noreturn)); +extern void _longjmp (struct __jmp_buf_tag __env[1], int __val) __attribute__ ((noreturn)); + jmp_buf test30_j; + int test30() { if (j) longjmp(test30_j, 1); diff --git a/test/Sema/thread-specifier.c b/test/Sema/thread-specifier.c index 8c40fcd0a64..9d516e8f141 100644 --- a/test/Sema/thread-specifier.c +++ b/test/Sema/thread-specifier.c @@ -1,30 +1,110 @@ -// RUN: %clang_cc1 -triple i686-pc-linux-gnu -fsyntax-only -Wno-private-extern -verify -pedantic %s +// RUN: %clang_cc1 -triple i686-pc-linux-gnu -fsyntax-only -Wno-private-extern -verify -pedantic %s -DGNU +// RUN: %clang_cc1 -triple i686-pc-linux-gnu -fsyntax-only -Wno-private-extern -verify -pedantic -x c++ %s -DGNU +// RUN: %clang_cc1 -triple i686-pc-linux-gnu -fsyntax-only -Wno-private-extern -verify -pedantic %s -DC11 -D__thread=_Thread_local +// RUN: %clang_cc1 -triple i686-pc-linux-gnu -fsyntax-only -Wno-private-extern -verify -pedantic -x c++ %s -DC11 -D__thread=_Thread_local +// RUN: %clang_cc1 -triple i686-pc-linux-gnu -fsyntax-only -Wno-private-extern -verify -pedantic -x c++ %s -DCXX11 -D__thread=thread_local -std=c++11 +// RUN: %clang_cc1 -triple i686-pc-linux-gnu -fsyntax-only -Wno-private-extern -verify -pedantic -x c++ %s -DC11 -D__thread=_Thread_local -std=c++11 + +#ifdef __cplusplus +// In C++, we define __private_extern__ to extern. +#undef __private_extern__ +#endif __thread int t1; -__thread extern int t2; // expected-warning {{'__thread' before 'extern'}} -__thread static int t3; // expected-warning {{'__thread' before 'static'}} +__thread extern int t2; +__thread static int t3; +#ifdef GNU +// expected-warning@-3 {{'__thread' before 'extern'}} +// expected-warning@-3 {{'__thread' before 'static'}} +#endif + __thread __private_extern__ int t4; -struct t5 { __thread int x; }; // expected-error {{type name does not allow storage class to be specified}} -__thread int t6(); // expected-error {{'__thread' is only allowed on variable declarations}} +struct t5 { __thread int x; }; +#ifdef __cplusplus +// expected-error-re@-2 {{'(__thread|_Thread_local|thread_local)' is only allowed on variable declarations}} +#else +// FIXME: The 'is only allowed on variable declarations' diagnostic is better here. +// expected-error@-5 {{type name does not allow storage class to be specified}} +#endif -int f(__thread int t7) { // expected-error {{'__thread' is only allowed on variable declarations}} - __thread int t8; // expected-error {{'__thread' variables must have global storage}} +__thread int t6(); +#if defined(GNU) +// expected-error@-2 {{'__thread' is only allowed on variable declarations}} +#elif defined(C11) +// expected-error@-4 {{'_Thread_local' is only allowed on variable declarations}} +#else +// expected-error@-6 {{'thread_local' is only allowed on variable declarations}} +#endif + +int f(__thread int t7) { // expected-error {{' is only allowed on variable declarations}} + __thread int t8; +#if defined(GNU) + // expected-error@-2 {{'__thread' variables must have global storage}} +#elif defined(C11) + // expected-error@-4 {{'_Thread_local' variables must have global storage}} +#endif extern __thread int t9; static __thread int t10; __thread __private_extern__ int t11; - __thread auto int t12; // expected-error {{'__thread' variables must have global storage}} - __thread register int t13; // expected-error {{'__thread' variables must have global storage}} +#if __cplusplus < 201103L + __thread auto int t12a; // expected-error-re {{cannot combine with previous '(__thread|_Thread_local)' declaration specifier}} + auto __thread int t12b; // expected-error {{cannot combine with previous 'auto' declaration specifier}} +#elif !defined(CXX11) + __thread auto t12a = 0; // expected-error-re {{'_Thread_local' variables must have global storage}} + auto __thread t12b = 0; // expected-error-re {{'_Thread_local' variables must have global storage}} +#endif + __thread register int t13a; // expected-error-re {{cannot combine with previous '(__thread|_Thread_local|thread_local)' declaration specifier}} + register __thread int t13b; // expected-error {{cannot combine with previous 'register' declaration specifier}} } -__thread typedef int t14; // expected-error {{'__thread' is only allowed on variable declarations}} -__thread int t15; // expected-note {{previous definition is here}} -int t15; // expected-error {{non-thread-local declaration of 't15' follows thread-local declaration}} -int t16; // expected-note {{previous definition is here}} +__thread typedef int t14; // expected-error-re {{cannot combine with previous '(__thread|_Thread_local|thread_local)' declaration specifier}} +__thread int t15; // expected-note {{previous declaration is here}} +extern int t15; // expected-error {{non-thread-local declaration of 't15' follows thread-local declaration}} +extern int t16; // expected-note {{previous declaration is here}} __thread int t16; // expected-error {{thread-local declaration of 't16' follows non-thread-local declaration}} +#ifdef CXX11 +extern thread_local int t17; // expected-note {{previous declaration is here}} +_Thread_local int t17; // expected-error {{thread-local declaration of 't17' with static initialization follows declaration with dynamic initialization}} +extern _Thread_local int t18; // expected-note {{previous declaration is here}} +thread_local int t18; // expected-error {{thread-local declaration of 't18' with dynamic initialization follows declaration with static initialization}} +#endif + // PR13720 __thread int thread_int; -int *thread_int_ptr = &thread_int; // expected-error{{initializer element is not a compile-time constant}} +int *thread_int_ptr = &thread_int; +#ifndef __cplusplus +// expected-error@-2 {{initializer element is not a compile-time constant}} +#endif void g() { int *p = &thread_int; // This is perfectly fine, though. } +#if __cplusplus >= 201103L +constexpr int *thread_int_ptr_2 = &thread_int; // expected-error {{must be initialized by a constant expression}} +#endif + +int non_const(); +__thread int non_const_init = non_const(); +#if !defined(__cplusplus) +// expected-error@-2 {{initializer element is not a compile-time constant}} +#elif !defined(CXX11) +// expected-error@-4 {{initializer for thread-local variable must be a constant expression}} +#if __cplusplus >= 201103L +// expected-note@-6 {{use 'thread_local' to allow this}} +#endif +#endif + +#ifdef __cplusplus +struct S { + ~S(); +}; +__thread S s; +#if !defined(CXX11) +// expected-error@-2 {{type of thread-local variable has non-trivial destruction}} +#if __cplusplus >= 201103L +// expected-note@-4 {{use 'thread_local' to allow this}} +#endif +#endif +#endif + +__thread int aggregate[10] = {0}; diff --git a/test/Sema/var-redecl.c b/test/Sema/var-redecl.c index f7576b6c025..363458b2017 100644 --- a/test/Sema/var-redecl.c +++ b/test/Sema/var-redecl.c @@ -4,7 +4,7 @@ int outer1; // expected-note{{previous definition is here}} extern int outer2; // expected-note{{previous definition is here}} int outer4; int outer4; // expected-note{{previous definition is here}} -int outer5; +int outer5; // expected-note{{previous definition is here}} int outer6(float); // expected-note{{previous definition is here}} int outer7(float); @@ -13,7 +13,7 @@ void outer_test() { extern float outer2; // expected-error{{redefinition of 'outer2' with a different type}} extern float outer3; // expected-note{{previous definition is here}} double outer4; - extern int outer5; // expected-note{{previous definition is here}} + extern int outer5; extern int outer6; // expected-error{{redefinition of 'outer6' as different kind of symbol}} int outer7; extern int outer8; // expected-note{{previous definition is here}} diff --git a/test/Sema/warn-documentation.cpp b/test/Sema/warn-documentation.cpp index 0132ef280c4..b3ab0199dcb 100644 --- a/test/Sema/warn-documentation.cpp +++ b/test/Sema/warn-documentation.cpp @@ -892,10 +892,10 @@ typedef const struct test_nocrash7 * test_nocrash8; // We used to crash on this. +// expected-warning@+1 {{unknown command tag name}} /// aaa \unknown aaa \unknown aaa int test_nocrash9; - // We used to crash on this. PR15068 // expected-warning@+2 {{empty paragraph passed to '@param' command}} diff --git a/test/Sema/warn-documentation.m b/test/Sema/warn-documentation.m index 1e3acf1d723..0737a8dedd0 100644 --- a/test/Sema/warn-documentation.m +++ b/test/Sema/warn-documentation.m @@ -149,6 +149,7 @@ struct S; @class NSArray; @interface NSArray @end +// expected-warning@+3 {{unknown command tag name}} /*! @interface NSMutableArray @super NSArray diff --git a/test/Sema/warn-duplicate-enum.c b/test/Sema/warn-duplicate-enum.c index 239f6f1995c..f108b3aa6c3 100644 --- a/test/Sema/warn-duplicate-enum.c +++ b/test/Sema/warn-duplicate-enum.c @@ -90,3 +90,12 @@ enum { NMax = N2, NCount = NMax + 1 }; + +// PR15693 +enum enum1 { + VALUE // expected-note{{previous definition is here}} +}; + +enum enum2 { + VALUE // expected-error{{redefinition of enumerator 'VALUE'}} +}; diff --git a/test/SemaCXX/Inputs/warn-unused-variables.h b/test/SemaCXX/Inputs/warn-unused-variables.h new file mode 100644 index 00000000000..5fac45922c9 --- /dev/null +++ b/test/SemaCXX/Inputs/warn-unused-variables.h @@ -0,0 +1,11 @@ +// Verify that we don't warn about variables of internal-linkage type in +// headers, as the use may be in another TU. +namespace PR15558 { +namespace { +class A {}; +} + +class B { + static A a; +}; +} diff --git a/test/SemaCXX/MicrosoftExtensions.cpp b/test/SemaCXX/MicrosoftExtensions.cpp index 449e24b03b6..ab3ff69f27b 100644 --- a/test/SemaCXX/MicrosoftExtensions.cpp +++ b/test/SemaCXX/MicrosoftExtensions.cpp @@ -208,3 +208,128 @@ void ::f(); // expected-warning{{extra qualification on member 'f'}} class C { C::C(); // expected-warning{{extra qualification on member 'C'}} }; + +struct StructWithProperty { + __declspec(property(get=GetV)) int V1; + __declspec(property(put=SetV)) int V2; + __declspec(property(get=GetV, put=SetV_NotExist)) int V3; + __declspec(property(get=GetV_NotExist, put=SetV)) int V4; + __declspec(property(get=GetV, put=SetV)) int V5; + + int GetV() { return 123; } + void SetV(int i) {} +}; +void TestProperty() { + StructWithProperty sp; + int i = sp.V2; // expected-error{{no getter defined for property 'V2'}} + sp.V1 = 12; // expected-error{{no setter defined for property 'V1'}} + int j = sp.V4; // expected-error{{no member named 'GetV_NotExist' in 'StructWithProperty'}} expected-error{{cannot find suitable getter for property 'V4'}} + sp.V3 = 14; // expected-error{{no member named 'SetV_NotExist' in 'StructWithProperty'}} expected-error{{cannot find suitable setter for property 'V3'}} + int k = sp.V5; + sp.V5 = k++; +} + +/* 4 tests for PseudoObject, begin */ +struct SP1 +{ + bool operator()() { return true; } +}; +struct SP2 +{ + __declspec(property(get=GetV)) SP1 V; + SP1 GetV() { return SP1(); } +}; +void TestSP2() { + SP2 sp2; + bool b = sp2.V(); +} + +struct SP3 { + template + void f(T t) {} +}; +template +struct SP4 +{ + __declspec(property(get=GetV)) int V; + int GetV() { return 123; } + void f() { SP3 s2; s2.f(V); } +}; +void TestSP4() { + SP4 s; + s.f(); +} + +template +struct SP5 +{ + __declspec(property(get=GetV)) T V; + int GetV() { return 123; } + void f() { int *p = new int[V]; } +}; + +template +struct SP6 +{ +public: + __declspec(property(get=GetV)) T V; + T GetV() { return 123; } + void f() { int t = V; } +}; +void TestSP6() { + SP6 c; + c.f(); +} +/* 4 tests for PseudoObject, end */ + +// Property access: explicit, implicit, with Qualifier +struct SP7 { + __declspec(property(get=GetV, put=SetV)) int V; + int GetV() { return 123; } + void SetV(int v) {} + + void ImplicitAccess() { int i = V; V = i; } + void ExplicitAccess() { int i = this->V; this->V = i; } +}; +struct SP8: public SP7 { + void AccessWithQualifier() { int i = SP7::V; SP7::V = i; } +}; + +// Property usage +template +struct SP9 { + __declspec(property(get=GetV, put=SetV)) T V; + T GetV() { return 0; } + void SetV(T v) {} + void f() { V = this->V; V < this->V; } + void g() { V++; } + void h() { V*=2; } +}; +struct SP10 { + SP10(int v) {} + bool operator<(const SP10& v) { return true; } + SP10 operator*(int v) { return *this; } + SP10 operator+(int v) { return *this; } + SP10& operator=(const SP10& v) { return *this; } +}; +void TestSP9() { + SP9 c; + int i = c.V; // Decl initializer + i = c.V; // Binary op operand + c.SetV(c.V); // CallExpr arg + int *p = new int[c.V + 1]; // Array size + p[c.V] = 1; // Array index + + c.V = 123; // Setter + + c.V++; // Unary op operand + c.V *= 2; // Unary op operand + + SP9 c2; + c2.V[0] = 123; // Array + + SP9 c3; + c3.f(); // Overloaded binary op operand + c3.g(); // Overloaded incdec op operand + c3.h(); // Overloaded unary op operand +} diff --git a/test/SemaCXX/access.cpp b/test/SemaCXX/access.cpp index 18ad301b5fe..50f2eff87bf 100644 --- a/test/SemaCXX/access.cpp +++ b/test/SemaCXX/access.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -fsyntax-only -verify %s +// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify %s class C { struct S; // expected-note {{previously declared 'private' here}} @@ -32,3 +32,77 @@ namespace test1 { class X {}; }; } + +// PR15209 +namespace PR15209 { + namespace alias_templates { + template struct U { }; + template using W = U; + + class A { + typedef int I; + static constexpr I x = 0; // expected-note {{implicitly declared private here}} + static constexpr I y = 42; // expected-note {{implicitly declared private here}} + friend W; + }; + + template + struct U { + int v_; + // the following will trigger for U instantiation, via W + U() : v_(A::x) { } // expected-error {{'x' is a private member of 'PR15209::alias_templates::A'}} + }; + + template + struct U { + int v_; + U() : v_(A::y) { } // expected-error {{'y' is a private member of 'PR15209::alias_templates::A'}} + }; + + template struct U; // expected-note {{in instantiation of member function 'PR15209::alias_templates::U::U' requested here}} + + void f() + { + W(); + // we should issue diagnostics for the following + W(); // expected-note {{in instantiation of member function 'PR15209::alias_templates::U::U' requested here}} + } + } + + namespace templates { + class A { + typedef int I; // expected-note {{implicitly declared private here}} + static constexpr I x = 0; // expected-note {{implicitly declared private here}} + + template friend struct B; + template struct C; + template class T> friend struct TT; + template friend void funct(T); + }; + template struct B { }; + + template struct A::C { }; + + template class T> struct TT { + T t; + }; + + template struct TT; + template struct D { }; // expected-error {{'I' is a private member of 'PR15209::templates::A'}} + template struct TT; + + // function template case + template + void funct(T) + { + (void)A::x; + } + + template void funct(int); + + void f() + { + (void)A::x; // expected-error {{'x' is a private member of 'PR15209::templates::A'}} + } + } +} diff --git a/test/SemaCXX/alignof.cpp b/test/SemaCXX/alignof.cpp new file mode 100644 index 00000000000..a9de1ad07c9 --- /dev/null +++ b/test/SemaCXX/alignof.cpp @@ -0,0 +1,52 @@ +// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify %s + +// rdar://13784901 + +struct S0 { + int x; + static const int test0 = __alignof__(x); // expected-error {{invalid application of 'alignof' to a field of a class still being defined}} + static const int test1 = __alignof__(S0::x); // expected-error {{invalid application of 'alignof' to a field of a class still being defined}} + auto test2() -> char(&)[__alignof__(x)]; // expected-error {{invalid application of 'alignof' to a field of a class still being defined}} +}; + +struct S1; // expected-note 5 {{forward declaration}} +extern S1 s1; +const int test3 = __alignof__(s1); // expected-error {{invalid application of 'alignof' to an incomplete type 'S1'}} + +struct S2 { + S2(); + S1 &s; + int x; + + int test4 = __alignof__(x); // ok + int test5 = __alignof__(s); // expected-error {{invalid application of 'alignof' to an incomplete type 'S1'}} +}; + +const int test6 = __alignof__(S2::x); +const int test7 = __alignof__(S2::s); // expected-error {{invalid application of 'alignof' to an incomplete type 'S1'}} + +// Arguably, these should fail like the S1 cases do: the alignment of +// 's2.x' should depend on the alignment of both x-within-S2 and +// s2-within-S3 and thus require 'S3' to be complete. If we start +// doing the appropriate recursive walk to do that, we should make +// sure that these cases don't explode. +struct S3 { + S2 s2; + + static const int test8 = __alignof__(s2.x); + static const int test9 = __alignof__(s2.s); // expected-error {{invalid application of 'alignof' to an incomplete type 'S1'}} + auto test10() -> char(&)[__alignof__(s2.x)]; + static const int test11 = __alignof__(S3::s2.x); + static const int test12 = __alignof__(S3::s2.s); // expected-error {{invalid application of 'alignof' to an incomplete type 'S1'}} + auto test13() -> char(&)[__alignof__(s2.x)]; +}; + +// Same reasoning as S3. +struct S4 { + union { + int x; + }; + static const int test0 = __alignof__(x); + static const int test1 = __alignof__(S0::x); + auto test2() -> char(&)[__alignof__(x)]; +}; diff --git a/test/SemaCXX/ast-print.cpp b/test/SemaCXX/ast-print.cpp index 5de8c4b51b9..921f7d8baaa 100644 --- a/test/SemaCXX/ast-print.cpp +++ b/test/SemaCXX/ast-print.cpp @@ -137,3 +137,14 @@ void test12() { ConstrWithCleanupsClass cwcExplicitArg(VirualDestrClass(56)); } +// CHECK: void test13() { +// CHECK: _Atomic(int) i; +// CHECK: __c11_atomic_init(&i, 0); +// CHECK: __c11_atomic_load(&i, 0); +// CHECK: } +void test13() { + _Atomic(int) i; + __c11_atomic_init(&i, 0); + __c11_atomic_load(&i, 0); +} + diff --git a/test/SemaCXX/attr-noreturn.cpp b/test/SemaCXX/attr-noreturn.cpp index f3d548b793b..41214c4f4b4 100644 --- a/test/SemaCXX/attr-noreturn.cpp +++ b/test/SemaCXX/attr-noreturn.cpp @@ -80,3 +80,86 @@ namespace PR12948 { template void wibble() __attribute__((__noreturn__)); template voidfn wibble; } + +// PR15291 +// Overload resolution per over.over should allow implicit noreturn adjustment. +namespace PR15291 { + __attribute__((noreturn)) void foo(int) {} + __attribute__((noreturn)) void foo(double) {} + + template + __attribute__((noreturn)) void bar(T) {} + + void baz(int) {} + void baz(double) {} + + template + void qux(T) {} + + // expected-note@+5 {{candidate function [with T = void (*)(int) __attribute__((noreturn))] not viable: no overload of 'baz' matching 'void (*)(int) __attribute__((noreturn))' for 1st argument}} + // expected-note@+4 {{candidate function [with T = void (*)(int) __attribute__((noreturn))] not viable: no overload of 'qux' matching 'void (*)(int) __attribute__((noreturn))' for 1st argument}} + // expected-note@+3 {{candidate function [with T = void (*)(int) __attribute__((noreturn))] not viable: no overload of 'bar' matching 'void (*)(int) __attribute__((noreturn))' for 1st argument}} + // expected-note@+2 {{candidate function [with T = void (*)(int)] not viable: no overload of 'bar' matching 'void (*)(int)' for 1st argument}} + // expected-note@+1 {{candidate function [with T = void (int)] not viable: no overload of 'bar' matching 'void (*)(int)' for 1st argument}} + template void accept_T(T) {} + + // expected-note@+1 {{candidate function not viable: no overload of 'bar' matching 'void (*)(int)' for 1st argument}} + void accept_fptr(void (*f)(int)) { + f(42); + } + + // expected-note@+2 {{candidate function not viable: no overload of 'baz' matching 'void (*)(int) __attribute__((noreturn))' for 1st argument}} + // expected-note@+1 {{candidate function not viable: no overload of 'qux' matching 'void (*)(int) __attribute__((noreturn))' for 1st argument}} + void accept_noreturn_fptr(void __attribute__((noreturn)) (*f)(int)) { + f(42); + } + + typedef void (*fptr_t)(int); + typedef void __attribute__((noreturn)) (*fptr_noreturn_t)(int); + + // expected-note@+1 {{candidate function not viable: no overload of 'bar' matching 'fptr_t' (aka 'void (*)(int)') for 1st argument}} + void accept_fptr_t(fptr_t f) { + f(42); + } + + // expected-note@+2 {{candidate function not viable: no overload of 'baz' matching 'fptr_noreturn_t' (aka 'void (*)(int) __attribute__((noreturn))') for 1st argument}} + // expected-note@+1 {{candidate function not viable: no overload of 'qux' matching 'fptr_noreturn_t' (aka 'void (*)(int) __attribute__((noreturn))') for 1st argument}} + void accept_fptr_noreturn_t(fptr_noreturn_t f) { + f(42); + } + + // Stripping noreturn should work if everything else is correct. + void strip_noreturn() { + accept_fptr(foo); + accept_fptr(bar); + accept_fptr(bar); // expected-error {{no matching function for call to 'accept_fptr'}} + + accept_fptr_t(foo); + accept_fptr_t(bar); + accept_fptr_t(bar); // expected-error {{no matching function for call to 'accept_fptr_t'}} + + accept_T(foo); + accept_T(bar); + accept_T(bar); // expected-error {{no matching function for call to 'accept_T'}} + + accept_T(foo); + accept_T(bar); + accept_T(bar); // expected-error {{no matching function for call to 'accept_T'}} + + accept_T(foo); + accept_T(bar); + accept_T(bar); // expected-error {{no matching function for call to 'accept_T'}} + } + + // Introducing noreturn should not work. + void introduce_noreturn() { + accept_noreturn_fptr(baz); // expected-error {{no matching function for call to 'accept_noreturn_fptr'}} + accept_noreturn_fptr(qux); // expected-error {{no matching function for call to 'accept_noreturn_fptr'}} + + accept_fptr_noreturn_t(baz); // expected-error {{no matching function for call to 'accept_fptr_noreturn_t'}} + accept_fptr_noreturn_t(qux); // expected-error {{no matching function for call to 'accept_fptr_noreturn_t'}} + + accept_T(baz); // expected-error {{no matching function for call to 'accept_T'}} + accept_T(qux); // expected-error {{no matching function for call to 'accept_T'}} + } +} diff --git a/test/SemaCXX/captured-statements.cpp b/test/SemaCXX/captured-statements.cpp new file mode 100644 index 00000000000..dbb18a7b676 --- /dev/null +++ b/test/SemaCXX/captured-statements.cpp @@ -0,0 +1,166 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s -fblocks + +void test_nest_lambda() { + int x; + int y; + [&,y]() { + int z; + #pragma clang __debug captured + { + x = y; // OK + y = z; // expected-error{{cannot assign to a variable captured by copy in a non-mutable lambda}} + z = y; // OK + } + }(); + + int a; + #pragma clang __debug captured + { + int b; + int c; + [&,c]() { + a = b; // OK + b = c; // OK + c = a; // expected-error{{cannot assign to a variable captured by copy in a non-mutable lambda}} + }(); + } +} + +class test_obj_capture { + int a; + void b(); + static void test() { + test_obj_capture c; + #pragma clang __debug captured + { (void)c.a; } // OK + #pragma clang __debug captured + { c.b(); } // OK + } +}; + +class test_this_capture { + int a; + void b(); + void test() { + #pragma clang __debug captured + { (void)this; } // OK + #pragma clang __debug captured + { (void)a; } // OK + #pragma clang __debug captured + { b(); } // OK + } +}; + +template +void template_capture_var() { + T x; // expected-error{{declaration of reference variable 'x' requires an initializer}} + #pragma clang _debug captured + { + (void)x; + } +} + +template +class Val { + T v; +public: + void set(const T &v0) { + #pragma clang __debug captured + { + v = v0; + } + } +}; + +void test_capture_var() { + template_capture_var(); // OK + template_capture_var(); // expected-note{{in instantiation of function template specialization 'template_capture_var' requested here}} + + Val Obj; + Obj.set(0.0f); // OK +} + +template +S template_capture_var(S x, T y) { + #pragma clang _debug captured + { + x++; + y++; // expected-error{{read-only variable is not assignable}} + } + + return x; +} + +// Check if can recover from a template error. +void test_capture_var_error() { + template_capture_var(0, 1); // OK + template_capture_var(0, 1); // expected-note{{in instantiation of function template specialization 'template_capture_var' requested here}} + template_capture_var(0, 1); // OK +} + +template +void template_capture_in_lambda() { + T x, y; + [=, &y]() { + #pragma clang __debug captured + { + y += x; + } + }(); +} + +void test_lambda() { + template_capture_in_lambda(); // OK +} + +struct Foo { + void foo() { } + static void bar() { } +}; + +template +void template_capture_func(T &t) { + #pragma clang __debug captured + { + t.foo(); + } + + #pragma clang __debug captured + { + T::bar(); + } +} + +void test_template_capture_func() { + Foo Obj; + template_capture_func(Obj); +} + +template +T captured_sum(const T &a, const T &b) { + T result; + + #pragma clang __debug captured + { + result = a + b; + } + + return result; +} + +template +T captured_sum(const T &a, const Args&... args) { + T result; + + #pragma clang __debug captured + { + result = a + captured_sum(args...); + } + + return result; +} + +void test_capture_variadic() { + (void)captured_sum(1, 2, 3); // OK + (void)captured_sum(1, 2, 3, 4, 5); // OK +} diff --git a/test/SemaCXX/compound-literal.cpp b/test/SemaCXX/compound-literal.cpp index fe0e45d3c6a..595747e40ce 100644 --- a/test/SemaCXX/compound-literal.cpp +++ b/test/SemaCXX/compound-literal.cpp @@ -1,4 +1,8 @@ -// RUN: %clang_cc1 -fsyntax-only -verify %s +// RUN: %clang_cc1 -fsyntax-only -std=c++03 -verify -ast-dump %s > %t-03 +// RUN: FileCheck --input-file=%t-03 %s +// RUN: %clang_cc1 -fsyntax-only -std=c++11 -verify -ast-dump %s > %t-11 +// RUN: FileCheck --input-file=%t-11 %s +// RUN: FileCheck --input-file=%t-11 %s --check-prefix=CHECK-CXX11 // http://llvm.org/PR7905 namespace PR7905 { @@ -12,3 +16,63 @@ void foo2() { (void)(M []) {{3}}; } } + +// Check compound literals mixed with C++11 list-initialization. +namespace brace_initializers { + struct POD { + int x, y; + }; + struct HasCtor { + HasCtor(int x, int y); + }; + struct HasDtor { + int x, y; + ~HasDtor(); + }; + struct HasCtorDtor { + HasCtorDtor(int x, int y); + ~HasCtorDtor(); + }; + + void test() { + (void)(POD){1, 2}; + // CHECK-NOT: CXXBindTemporaryExpr {{.*}} 'struct brace_initializers::POD' + // CHECK: CompoundLiteralExpr {{.*}} 'struct brace_initializers::POD' + // CHECK-NEXT: InitListExpr {{.*}} 'struct brace_initializers::POD' + // CHECK-NEXT: IntegerLiteral {{.*}} 1{{$}} + // CHECK-NEXT: IntegerLiteral {{.*}} 2{{$}} + + (void)(HasDtor){1, 2}; + // CHECK: CXXBindTemporaryExpr {{.*}} 'struct brace_initializers::HasDtor' + // CHECK-NEXT: CompoundLiteralExpr {{.*}} 'struct brace_initializers::HasDtor' + // CHECK-NEXT: InitListExpr {{.*}} 'struct brace_initializers::HasDtor' + // CHECK-NEXT: IntegerLiteral {{.*}} 1{{$}} + // CHECK-NEXT: IntegerLiteral {{.*}} 2{{$}} + +#if __cplusplus >= 201103L + (void)(HasCtor){1, 2}; + // CHECK-CXX11-NOT: CXXBindTemporaryExpr {{.*}} 'struct brace_initializers::HasCtor' + // CHECK-CXX11: CompoundLiteralExpr {{.*}} 'struct brace_initializers::HasCtor' + // CHECK-CXX11-NEXT: CXXTemporaryObjectExpr {{.*}} 'struct brace_initializers::HasCtor' + // CHECK-CXX11-NEXT: IntegerLiteral {{.*}} 1{{$}} + // CHECK-CXX11-NEXT: IntegerLiteral {{.*}} 2{{$}} + + (void)(HasCtorDtor){1, 2}; + // CHECK-CXX11: CXXBindTemporaryExpr {{.*}} 'struct brace_initializers::HasCtorDtor' + // CHECK-CXX11-NEXT: CompoundLiteralExpr {{.*}} 'struct brace_initializers::HasCtorDtor' + // CHECK-CXX11-NEXT: CXXTemporaryObjectExpr {{.*}} 'struct brace_initializers::HasCtorDtor' + // CHECK-CXX11-NEXT: IntegerLiteral {{.*}} 1{{$}} + // CHECK-CXX11-NEXT: IntegerLiteral {{.*}} 2{{$}} +#endif + } + + struct PrivateDtor { + int x, y; + private: + ~PrivateDtor(); // expected-note {{declared private here}} + }; + + void testPrivateDtor() { + (void)(PrivateDtor){1, 2}; // expected-error {{temporary of type 'brace_initializers::PrivateDtor' has private destructor}} + } +} diff --git a/test/SemaCXX/condition.cpp b/test/SemaCXX/condition.cpp index d8058811948..73f3dceef64 100644 --- a/test/SemaCXX/condition.cpp +++ b/test/SemaCXX/condition.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -fsyntax-only -verify %s +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s void test() { int x; @@ -6,7 +6,7 @@ void test() { if (int x=0) ++x; typedef int arr[10]; - while (arr x=0) ; // expected-error {{an array type is not allowed here}} expected-error {{array initializer must be an initializer list}} + while (arr x={0}) ; // expected-error {{an array type is not allowed here}} while (int f()=0) ; // expected-error {{a function type is not allowed here}} struct S {} s; @@ -19,9 +19,7 @@ void test() { while (struct NewS *x=0) ; while (struct S {} *x=0) ; // expected-error {{types may not be defined in conditions}} while (struct {} *x=0) ; // expected-error {{types may not be defined in conditions}} - switch (enum {E} x=0) ; // expected-error {{types may not be defined in conditions}} \ - // expected-warning{{enumeration value 'E' not handled in switch}} expected-warning {{switch statement has empty body}} \ - // expected-note{{put the semicolon on a separate line}} + switch (enum {E} x=0) ; // expected-error {{types may not be defined in conditions}} if (int x=0) { // expected-note 2 {{previous definition is here}} int x; // expected-error {{redefinition of 'x'}} diff --git a/test/SemaCXX/constant-expression-cxx11.cpp b/test/SemaCXX/constant-expression-cxx11.cpp index 30aa7d7b0b3..09a9cb5dd8d 100644 --- a/test/SemaCXX/constant-expression-cxx11.cpp +++ b/test/SemaCXX/constant-expression-cxx11.cpp @@ -21,7 +21,7 @@ template constexpr T *begin(T (&xs)[N]) { return xs; } template constexpr T *end(T (&xs)[N]) { return xs + N; } struct MemberZero { - constexpr int zero() { return 0; } + constexpr int zero() const { return 0; } }; namespace DerivedToVBaseCast { @@ -304,16 +304,16 @@ struct Str { expected-note {{reinterpret_cast is not allowed in a constant expression}} int c : (S*)(long)(sptr) == (S*)(long)(sptr); // \ expected-warning {{not an integral constant expression}} \ - expected-note {{cast which performs the conversions of a reinterpret_cast is not allowed in a constant expression}} + expected-note {{cast that performs the conversions of a reinterpret_cast is not allowed in a constant expression}} int d : (S*)(42) == (S*)(42); // \ expected-warning {{not an integral constant expression}} \ - expected-note {{cast which performs the conversions of a reinterpret_cast is not allowed in a constant expression}} + expected-note {{cast that performs the conversions of a reinterpret_cast is not allowed in a constant expression}} int e : (Str*)(sptr) == (Str*)(sptr); // \ expected-warning {{not an integral constant expression}} \ - expected-note {{cast which performs the conversions of a reinterpret_cast is not allowed in a constant expression}} + expected-note {{cast that performs the conversions of a reinterpret_cast is not allowed in a constant expression}} int f : &(U&)(*sptr) == &(U&)(*sptr); // \ expected-warning {{not an integral constant expression}} \ - expected-note {{cast which performs the conversions of a reinterpret_cast is not allowed in a constant expression}} + expected-note {{cast that performs the conversions of a reinterpret_cast is not allowed in a constant expression}} int g : (S*)(void*)(sptr) == sptr; // \ expected-warning {{not an integral constant expression}} \ expected-note {{cast from 'void *' is not allowed in a constant expression}} @@ -362,7 +362,7 @@ constexpr char c0 = "nought index"[0]; constexpr char c1 = "nice index"[10]; constexpr char c2 = "nasty index"[12]; // expected-error {{must be initialized by a constant expression}} expected-warning {{is past the end}} expected-note {{read of dereferenced one-past-the-end pointer}} constexpr char c3 = "negative index"[-1]; // expected-error {{must be initialized by a constant expression}} expected-warning {{is before the beginning}} expected-note {{cannot refer to element -1 of array of 15 elements}} -constexpr char c4 = ((char*)(int*)"no reinterpret_casts allowed")[14]; // expected-error {{must be initialized by a constant expression}} expected-note {{cast which performs the conversions of a reinterpret_cast}} +constexpr char c4 = ((char*)(int*)"no reinterpret_casts allowed")[14]; // expected-error {{must be initialized by a constant expression}} expected-note {{cast that performs the conversions of a reinterpret_cast}} constexpr const char *p = "test" + 2; static_assert(*p == 's', ""); @@ -414,6 +414,19 @@ struct V { }; static_assert(V().c[1] == "i"[0], ""); +namespace Parens { + constexpr unsigned char a[] = ("foo"), b[] = {"foo"}, c[] = {("foo")}, + d[4] = ("foo"), e[5] = {"foo"}, f[6] = {("foo")}; + static_assert(a[0] == 'f', ""); + static_assert(b[1] == 'o', ""); + static_assert(c[2] == 'o', ""); + static_assert(d[0] == 'f', ""); + static_assert(e[1] == 'o', ""); + static_assert(f[2] == 'o', ""); + static_assert(f[5] == 0, ""); + static_assert(f[6] == 0, ""); // expected-error {{constant expression}} expected-note {{one-past-the-end}} +} + } namespace Array { @@ -486,7 +499,7 @@ static_assert(CountZero(arr, arr + 40) == 36, ""); struct ArrayElem { constexpr ArrayElem() : n(0) {} int n; - constexpr int f() { return n; } + constexpr int f() const { return n; } }; struct ArrayRVal { constexpr ArrayRVal() {} @@ -731,14 +744,14 @@ namespace ConversionOperators { struct T { constexpr T(int n) : k(5*n - 3) {} - constexpr operator int() { return k; } + constexpr operator int() const { return k; } int k; }; struct S { constexpr S(int n) : k(2*n + 1) {} - constexpr operator int() { return k; } - constexpr operator T() { return T(k); } + constexpr operator int() const { return k; } + constexpr operator T() const { return T(k); } int k; }; @@ -750,7 +763,7 @@ static_assert(check(S(5), 11), ""); namespace PR14171 { struct X { - constexpr (operator int)() { return 0; } + constexpr (operator int)() const { return 0; } }; static_assert(X() == 0, ""); @@ -764,13 +777,13 @@ namespace Temporaries { struct S { constexpr S() {} - constexpr int f(); + constexpr int f() const; }; struct T : S { constexpr T(int n) : S(), n(n) {} int n; }; -constexpr int S::f() { +constexpr int S::f() const { // 'this' must be the postfix-expression in a class member access expression, // so we can't just use // return static_cast(this)->n; @@ -825,7 +838,7 @@ namespace MemberPointer { struct A { constexpr A(int n) : n(n) {} int n; - constexpr int f() { return n + 3; } + constexpr int f() const { return n + 3; } }; constexpr A a(7); static_assert(A(5).*&A::n == 5, ""); @@ -836,7 +849,7 @@ namespace MemberPointer { struct B : A { constexpr B(int n, int m) : A(n), m(m) {} int m; - constexpr int g() { return n + m + 1; } + constexpr int g() const { return n + m + 1; } }; constexpr B b(9, 13); static_assert(B(4, 11).*&A::n == 4, ""); @@ -857,7 +870,7 @@ namespace MemberPointer { m(m), n(n), pf(pf), pn(pn) {} constexpr S() : m(), n(), pf(&S::f), pn(&S::n) {} - constexpr int f() { return this->*pn; } + constexpr int f() const { return this->*pn; } virtual int g() const; int m, n; @@ -938,7 +951,7 @@ namespace ArrayBaseDerived { }; struct Derived : Base { constexpr Derived() {} - constexpr const int *f() { return &n; } + constexpr const int *f() const { return &n; } }; constexpr Derived a[10]; @@ -1038,7 +1051,7 @@ static_assert(makeComplexWrap(1,0) != complex(0, 1), ""); } namespace PR11595 { - struct A { constexpr bool operator==(int x) { return true; } }; + struct A { constexpr bool operator==(int x) const { return true; } }; struct B { B(); A& x; }; static_assert(B().x == 3, ""); // expected-error {{constant expression}} expected-note {{non-literal type 'PR11595::B' cannot be used in a constant expression}} @@ -1312,6 +1325,13 @@ namespace InvalidClasses { } } +namespace NamespaceAlias { + constexpr int f() { + namespace NS = NamespaceAlias; // expected-warning {{use of this statement in a constexpr function is a C++1y extension}} + return &NS::f != nullptr; + } +} + // Constructors can be implicitly constexpr, even for a non-literal type. namespace ImplicitConstexpr { struct Q { Q() = default; Q(const Q&) = default; Q(Q&&) = default; ~Q(); }; // expected-note 3{{here}} @@ -1455,3 +1475,31 @@ namespace PR14203 { // constructor here. int n = sizeof(short{duration(duration())}); } + +namespace ArrayEltInit { + struct A { + constexpr A() : p(&p) {} + void *p; + }; + constexpr A a[10]; + static_assert(a[0].p == &a[0].p, ""); + static_assert(a[9].p == &a[9].p, ""); + static_assert(a[0].p != &a[9].p, ""); + static_assert(a[9].p != &a[0].p, ""); + + constexpr A b[10] = {}; + static_assert(b[0].p == &b[0].p, ""); + static_assert(b[9].p == &b[9].p, ""); + static_assert(b[0].p != &b[9].p, ""); + static_assert(b[9].p != &b[0].p, ""); +} + +namespace PR15884 { + struct S {}; + constexpr S f() { return {}; } + constexpr S *p = &f(); + // expected-error@-1 {{taking the address of a temporary}} + // expected-error@-2 {{constexpr variable 'p' must be initialized by a constant expression}} + // expected-note@-3 {{pointer to temporary is not a constant expression}} + // expected-note@-4 {{temporary created here}} +} diff --git a/test/SemaCXX/constant-expression-cxx1y.cpp b/test/SemaCXX/constant-expression-cxx1y.cpp new file mode 100644 index 00000000000..60ec82062de --- /dev/null +++ b/test/SemaCXX/constant-expression-cxx1y.cpp @@ -0,0 +1,459 @@ +// RUN: %clang_cc1 -std=c++1y -verify %s -fcxx-exceptions -triple=x86_64-linux-gnu + +struct S { + // dummy ctor to make this a literal type + constexpr S(int); + + S(); + + int arr[10]; + + constexpr int &get(int n) { return arr[n]; } + constexpr const int &get(int n) const { return arr[n]; } +}; + +S s = S(); +const S &sr = s; +static_assert(&s.get(4) - &sr.get(2) == 2, ""); + +// Compound-statements can be used in constexpr functions. +constexpr int e() {{{{}} return 5; }} +static_assert(e() == 5, ""); + +// Types can be defined in constexpr functions. +constexpr int f() { + enum E { e1, e2, e3 }; + + struct S { + constexpr S(E e) : e(e) {} + constexpr int get() { return e; } + E e; + }; + + return S(e2).get(); +} +static_assert(f() == 1, ""); + +// Variables can be declared in constexpr functions. +constexpr int g(int k) { + const int n = 9; + int k2 = k * k; + int k3 = k2 * k; + return 3 * k3 + 5 * k2 + n * k - 20; +} +static_assert(g(2) == 42, ""); +constexpr int h(int n) { + static const int m = n; // expected-error {{static variable not permitted in a constexpr function}} + return m; +} +constexpr int i(int n) { + thread_local const int m = n; // expected-error {{thread_local variable not permitted in a constexpr function}} + return m; +} + +// if-statements can be used in constexpr functions. +constexpr int j(int k) { + if (k == 5) + return 1; + if (k == 1) + return 5; + else { + if (int n = 2 * k - 4) { + return n + 1; + return 2; + } + } +} // expected-note 2{{control reached end of constexpr function}} +static_assert(j(0) == -3, ""); +static_assert(j(1) == 5, ""); +static_assert(j(2), ""); // expected-error {{constant expression}} expected-note {{in call to 'j(2)'}} +static_assert(j(3) == 3, ""); +static_assert(j(4) == 5, ""); +static_assert(j(5) == 1, ""); + +// There can be 0 return-statements. +constexpr void k() { +} + +// If the return type is not 'void', no return statements => never a constant +// expression, so still diagnose that case. +[[noreturn]] constexpr int fn() { // expected-error {{no return statement in constexpr function}} + fn(); +} + +// We evaluate the body of a constexpr constructor, to check for side-effects. +struct U { + constexpr U(int n) { + if (j(n)) {} // expected-note {{in call to 'j(2)'}} + } +}; +constexpr U u1{1}; +constexpr U u2{2}; // expected-error {{constant expression}} expected-note {{in call to 'U(2)'}} + +// We allow expression-statements. +constexpr int l(bool b) { + if (b) + throw "invalid value for b!"; // expected-note {{subexpression not valid}} + return 5; +} +static_assert(l(false) == 5, ""); +static_assert(l(true), ""); // expected-error {{constant expression}} expected-note {{in call to 'l(true)'}} + +// Potential constant expression checking is still applied where possible. +constexpr int htonl(int x) { // expected-error {{never produces a constant expression}} + typedef unsigned char uchar; + uchar arr[4] = { uchar(x >> 24), uchar(x >> 16), uchar(x >> 8), uchar(x) }; + return *reinterpret_cast(arr); // expected-note {{reinterpret_cast is not allowed in a constant expression}} +} + +constexpr int maybe_htonl(bool isBigEndian, int x) { + if (isBigEndian) + return x; + + typedef unsigned char uchar; + uchar arr[4] = { uchar(x >> 24), uchar(x >> 16), uchar(x >> 8), uchar(x) }; + return *reinterpret_cast(arr); // expected-note {{reinterpret_cast is not allowed in a constant expression}} +} + +constexpr int swapped = maybe_htonl(false, 123); // expected-error {{constant expression}} expected-note {{in call}} + +namespace NS { + constexpr int n = 0; +} +constexpr int namespace_alias() { + namespace N = NS; + return N::n; +} + +namespace assign { + constexpr int a = 0; + const int b = 0; + int c = 0; // expected-note 2{{here}} + + constexpr void set(const int &a, int b) { + const_cast(a) = b; // expected-note 2{{constant expression cannot modify an object that is visible outside that expression}} + } + constexpr int wrap(int a, int b) { + set(a, b); + return a; + } + + static_assert((set(a, 1), a) == 1, ""); // expected-error {{constant expression}} expected-note {{in call to 'set(a, 1)'}} + static_assert((set(b, 1), b) == 1, ""); // expected-error {{constant expression}} expected-note {{in call to 'set(b, 1)'}} + static_assert((set(c, 1), c) == 1, ""); // expected-error {{constant expression}} expected-note {{read of non-const variable 'c'}} + + static_assert(wrap(a, 1) == 1, ""); + static_assert(wrap(b, 1) == 1, ""); + static_assert(wrap(c, 1) == 1, ""); // expected-error {{constant expression}} expected-note {{read of non-const variable 'c'}} +} + +namespace string_assign { + template + constexpr void swap(T &a, T &b) { + T tmp = a; + a = b; + b = tmp; + } + template + constexpr void reverse(Iterator begin, Iterator end) { + while (begin != end && begin != --end) + swap(*begin++, *end); + } + template + constexpr bool equal(Iterator1 a, Iterator1 ae, Iterator2 b, Iterator2 be) { + while (a != ae && b != be) + if (*a++ != *b++) + return false; + return a == ae && b == be; + } + constexpr bool test1(int n) { + char stuff[100] = "foobarfoo"; + const char stuff2[100] = "oofraboof"; + reverse(stuff, stuff + n); // expected-note {{cannot refer to element 101 of array of 100 elements}} + return equal(stuff, stuff + n, stuff2, stuff2 + n); + } + static_assert(!test1(1), ""); + static_assert(test1(3), ""); + static_assert(!test1(6), ""); + static_assert(test1(9), ""); + static_assert(!test1(100), ""); + static_assert(!test1(101), ""); // expected-error {{constant expression}} expected-note {{in call to 'test1(101)'}} + + // FIXME: We should be able to reject this before it's called + constexpr void f() { + char foo[10] = { "z" }; // expected-note {{here}} + foo[10] = 'x'; // expected-warning {{past the end}} expected-note {{assignment to dereferenced one-past-the-end pointer}} + } + constexpr int k = (f(), 0); // expected-error {{constant expression}} expected-note {{in call}} +} + +namespace array_resize { + constexpr int do_stuff(int k1, int k2) { + int arr[1234] = { 1, 2, 3, 4 }; + arr[k1] = 5; // expected-note {{past-the-end}} expected-note {{cannot refer to element 1235}} expected-note {{cannot refer to element -1}} + return arr[k2]; + } + static_assert(do_stuff(1, 2) == 3, ""); + static_assert(do_stuff(0, 0) == 5, ""); + static_assert(do_stuff(1233, 1233) == 5, ""); + static_assert(do_stuff(1233, 0) == 1, ""); + static_assert(do_stuff(1234, 0) == 1, ""); // expected-error {{constant expression}} expected-note {{in call}} + static_assert(do_stuff(1235, 0) == 1, ""); // expected-error {{constant expression}} expected-note {{in call}} + static_assert(do_stuff(-1, 0) == 1, ""); // expected-error {{constant expression}} expected-note {{in call}} +} + +namespace potential_const_expr { + constexpr void set(int &n) { n = 1; } + constexpr int div_zero_1() { int z = 0; set(z); return 100 / z; } // no error + constexpr int div_zero_2() { // expected-error {{never produces a constant expression}} + int z = 0; + return 100 / (set(z), 0); // expected-note {{division by zero}} + } + int n; // expected-note {{declared here}} + constexpr int ref() { // expected-error {{never produces a constant expression}} + int &r = n; + return r; // expected-note {{read of non-const variable 'n'}} + } +} + +namespace subobject { + union A { constexpr A() : y(5) {} int x, y; }; + struct B { A a; }; + struct C : B {}; + union D { constexpr D() : c() {} constexpr D(int n) : n(n) {} C c; int n; }; + constexpr void f(D &d) { + d.c.a.y = 3; + // expected-note@-1 {{cannot modify an object that is visible outside}} + // expected-note@-2 {{assignment to member 'c' of union with active member 'n'}} + } + constexpr bool check(D &d) { return d.c.a.y == 3; } + + constexpr bool g() { D d; f(d); return d.c.a.y == 3; } + static_assert(g(), ""); + + D d; + constexpr bool h() { f(d); return check(d); } // expected-note {{in call}} + static_assert(h(), ""); // expected-error {{constant expression}} expected-note {{in call}} + + constexpr bool i() { D d(0); f(d); return check(d); } // expected-note {{in call}} + static_assert(i(), ""); // expected-error {{constant expression}} expected-note {{in call}} + + constexpr bool j() { D d; d.c.a.x = 3; return check(d); } // expected-note {{assignment to member 'x' of union with active member 'y'}} + static_assert(j(), ""); // expected-error {{constant expression}} expected-note {{in call}} +} + +namespace lifetime { + constexpr int &&id(int &&n) { return static_cast(n); } + constexpr int &&dead() { return id(0); } // expected-note {{temporary created here}} + constexpr int bad() { int &&n = dead(); n = 1; return n; } // expected-note {{assignment to temporary whose lifetime has ended}} + static_assert(bad(), ""); // expected-error {{constant expression}} expected-note {{in call}} +} + +namespace const_modify { + constexpr int modify(int &n) { return n = 1; } // expected-note {{modification of object of const-qualified type 'const int'}} + constexpr int test1() { int k = 0; return modify(k); } + constexpr int test2() { const int k = 0; return modify(const_cast(k)); } // expected-note {{in call}} + static_assert(test1() == 1, ""); + static_assert(test2() == 1, ""); // expected-error {{constant expression}} expected-note {{in call}} +} + +namespace null { + constexpr int test(int *p) { + return *p = 123; // expected-note {{assignment to dereferenced null pointer}} + } + static_assert(test(0), ""); // expected-error {{constant expression}} expected-note {{in call}} +} + +namespace incdec { + template constexpr T &ref(T &&r) { return r; } + template constexpr T postinc(T &&r) { return (r++, r); } + template constexpr T postdec(T &&r) { return (r--, r); } + + static_assert(++ref(0) == 1, ""); + static_assert(ref(0)++ == 0, ""); + static_assert(postinc(0) == 1, ""); + static_assert(--ref(0) == -1, ""); + static_assert(ref(0)-- == 0, ""); + static_assert(postdec(0) == -1, ""); + + constexpr int overflow_int_inc_1 = ref(0x7fffffff)++; // expected-error {{constant}} expected-note {{2147483648}} + constexpr int overflow_int_inc_1_ok = ref(0x7ffffffe)++; + constexpr int overflow_int_inc_2 = ++ref(0x7fffffff); // expected-error {{constant}} expected-note {{2147483648}} + constexpr int overflow_int_inc_2_ok = ++ref(0x7ffffffe); + + // inc/dec on short can't overflow because we promote to int first + static_assert(++ref(0x7fff) == (int)0xffff8000u, ""); + static_assert(--ref(0x8000) == 0x7fff, ""); + + // inc on bool sets to true + static_assert(++ref(false), ""); // expected-warning {{deprecated}} + static_assert(++ref(true), ""); // expected-warning {{deprecated}} + + int arr[10]; + static_assert(++ref(&arr[0]) == &arr[1], ""); + static_assert(++ref(&arr[9]) == &arr[10], ""); + static_assert(++ref(&arr[10]) == &arr[11], ""); // expected-error {{constant}} expected-note {{cannot refer to element 11}} + static_assert(ref(&arr[0])++ == &arr[0], ""); + static_assert(ref(&arr[10])++ == &arr[10], ""); // expected-error {{constant}} expected-note {{cannot refer to element 11}} + static_assert(postinc(&arr[0]) == &arr[1], ""); + static_assert(--ref(&arr[10]) == &arr[9], ""); + static_assert(--ref(&arr[1]) == &arr[0], ""); + static_assert(--ref(&arr[0]) != &arr[0], ""); // expected-error {{constant}} expected-note {{cannot refer to element -1}} + static_assert(ref(&arr[1])-- == &arr[1], ""); + static_assert(ref(&arr[0])-- == &arr[0], ""); // expected-error {{constant}} expected-note {{cannot refer to element -1}} + static_assert(postdec(&arr[1]) == &arr[0], ""); + + int x; + static_assert(++ref(&x) == &x + 1, ""); + + static_assert(++ref(0.0) == 1.0, ""); + static_assert(ref(0.0)++ == 0.0, ""); + static_assert(postinc(0.0) == 1.0, ""); + static_assert(--ref(0.0) == -1.0, ""); + static_assert(ref(0.0)-- == 0.0, ""); + static_assert(postdec(0.0) == -1.0, ""); + + static_assert(++ref(1e100) == 1e100, ""); + static_assert(--ref(1e100) == 1e100, ""); + + union U { + int a, b; + }; + constexpr int f(U u) { + return ++u.b; // expected-note {{increment of member 'b' of union with active member 'a'}} + } + constexpr int wrong_member = f({0}); // expected-error {{constant}} expected-note {{in call to 'f({.a = 0})'}} + constexpr int vol = --ref(0); // expected-error {{constant}} expected-note {{decrement of volatile-qualified}} + + constexpr int incr(int k) { + int x = k; + if (x++ == 100) + return x; + return incr(x); + } + static_assert(incr(0) == 101, ""); +} + +namespace loops { + constexpr int fib_loop(int a) { + int f_k = 0, f_k_plus_one = 1; + for (int k = 1; k != a; ++k) { + int f_k_plus_two = f_k + f_k_plus_one; + f_k = f_k_plus_one; + f_k_plus_one = f_k_plus_two; + } + return f_k_plus_one; + } + static_assert(fib_loop(46) == 1836311903, ""); + + constexpr bool breaks_work() { + int a = 0; + for (int n = 0; n != 100; ++n) { + ++a; + if (a == 5) continue; + if ((a % 5) == 0) break; + } + + int b = 0; + while (b != 17) { + ++b; + if (b == 6) continue; + if ((b % 6) == 0) break; + } + + int c = 0; + do { + ++c; + if (c == 7) continue; + if ((c % 7) == 0) break; + } while (c != 21); + + return a == 10 && b == 12 & c == 14; + } + static_assert(breaks_work(), ""); + + void not_constexpr(); + constexpr bool no_cont_after_break() { + for (;;) { + break; + not_constexpr(); + } + while (true) { + break; + not_constexpr(); + } + do { + break; + not_constexpr(); + } while (true); + return true; + } + static_assert(no_cont_after_break(), ""); + + constexpr bool cond() { + for (int a = 1; bool b = a != 3; ++a) { + if (!b) + return false; + } + while (bool b = true) { + b = false; + break; + } + return true; + } + static_assert(cond(), ""); + + constexpr int range_for() { + int arr[] = { 1, 2, 3, 4, 5 }; + int sum = 0; + for (int x : arr) + sum = sum + x; + return sum; + } + static_assert(range_for() == 15, ""); + + template struct ints {}; + template struct join_ints; + template struct join_ints, ints> { + using type = ints; + }; + template struct make_ints { + using type = typename join_ints::type, typename make_ints<(N+1)/2>::type>::type; + }; + template<> struct make_ints<0> { using type = ints<>; }; + template<> struct make_ints<1> { using type = ints<0>; }; + + struct ignore { template constexpr ignore(Ts &&...) {} }; + + template struct array { + constexpr array() : arr{} {} + template + constexpr array(X ...x) : arr{} { + init(typename make_ints::type{}, x...); + } + template constexpr void init(ints, X ...x) { + ignore{arr[I] = x ...}; + } + T arr[N]; + struct iterator { + T *p; + constexpr explicit iterator(T *p) : p(p) {} + constexpr bool operator!=(iterator o) { return p != o.p; } + constexpr iterator &operator++() { ++p; return *this; } + constexpr T &operator*() { return *p; } + }; + constexpr iterator begin() { return iterator(arr); } + constexpr iterator end() { return iterator(arr + N); } + }; + + constexpr int range_for_2() { + array arr { 1, 2, 3, 4, 5 }; + int sum = 0; + for (int k : arr) { + sum = sum + k; + if (sum > 8) break; + } + return sum; + } + static_assert(range_for_2() == 10, ""); +} diff --git a/test/SemaCXX/constexpr-printing.cpp b/test/SemaCXX/constexpr-printing.cpp index 9170fa1ec24..e545f45d601 100644 --- a/test/SemaCXX/constexpr-printing.cpp +++ b/test/SemaCXX/constexpr-printing.cpp @@ -9,7 +9,7 @@ struct S { int n, m; }; -constexpr int extract(const S &s) { return s.n; } // expected-note {{read of uninitialized object is not allowed in a constant expression}} +constexpr int extract(const S &s) { return s.n; } // expected-note {{read of object outside its lifetime is not allowed in a constant expression}} constexpr S s1; // ok void f() { diff --git a/test/SemaCXX/constexpr-value-init.cpp b/test/SemaCXX/constexpr-value-init.cpp index e459f097b9d..d137bd88db0 100644 --- a/test/SemaCXX/constexpr-value-init.cpp +++ b/test/SemaCXX/constexpr-value-init.cpp @@ -1,7 +1,7 @@ // RUN: %clang_cc1 %s -std=c++11 -fsyntax-only -verify struct A { - constexpr A() : a(b + 1), b(a + 1) {} // expected-note {{uninitialized}} + constexpr A() : a(b + 1), b(a + 1) {} // expected-note {{outside its lifetime}} int a; int b; }; diff --git a/test/SemaCXX/cxx11-ast-print.cpp b/test/SemaCXX/cxx11-ast-print.cpp index f95eeb50fef..f7bfc1123a7 100644 --- a/test/SemaCXX/cxx11-ast-print.cpp +++ b/test/SemaCXX/cxx11-ast-print.cpp @@ -41,3 +41,5 @@ const char *p10 = 3.300e+15_fritz; // CHECK: ; ; // CHECK-NOT: ; + + diff --git a/test/SemaCXX/cxx11-crashes.cpp b/test/SemaCXX/cxx11-crashes.cpp index bd51af1da2f..a4d4829f3fc 100644 --- a/test/SemaCXX/cxx11-crashes.cpp +++ b/test/SemaCXX/cxx11-crashes.cpp @@ -70,7 +70,9 @@ namespace b6981007 { for (auto x : s) { // We used to attempt to evaluate the initializer of this variable, // and crash because it has an undeduced type. - const int &n(x); + // FIXME: We should set the loop variable to be invalid if we can't build + // the loop, to suppress this follow-on error. + const int &n(x); // expected-error {{could not bind to an lvalue of type 'auto'}} } } } diff --git a/test/SemaCXX/cxx11-inheriting-ctors.cpp b/test/SemaCXX/cxx11-inheriting-ctors.cpp new file mode 100644 index 00000000000..67d55213a08 --- /dev/null +++ b/test/SemaCXX/cxx11-inheriting-ctors.cpp @@ -0,0 +1,28 @@ +// RUN: %clang_cc1 -std=c++11 %s -verify + +// expected-no-diagnostics + +namespace PR15757 { + struct S { + }; + + template struct T { + template T(X x, A &&a) {} + + template explicit T(A &&a) + noexcept(noexcept(T(X(), static_cast(a)))) + : T(X(), static_cast(a)) {} + }; + + template struct U : T { + using T::T; + }; + + U foo(char ch) { return U(ch); } + + int main() { + U a(42); + U b('4'); + return 0; + } +} diff --git a/test/SemaCXX/cxx11-thread-local-print.cpp b/test/SemaCXX/cxx11-thread-local-print.cpp new file mode 100644 index 00000000000..9d9a82b7e6a --- /dev/null +++ b/test/SemaCXX/cxx11-thread-local-print.cpp @@ -0,0 +1,9 @@ +// RUN: %clang_cc1 -std=c++11 -triple=x86_64-linux-gnu -ast-print %s | FileCheck %s + +// CHECK: __thread int gnu_tl; +// CHECK: _Thread_local int c11_tl; +// CHECK: thread_local int cxx11_tl; +__thread int gnu_tl; +_Thread_local int c11_tl; +thread_local int cxx11_tl; + diff --git a/test/SemaCXX/cxx11-thread-local.cpp b/test/SemaCXX/cxx11-thread-local.cpp new file mode 100644 index 00000000000..f1dddc1c3bf --- /dev/null +++ b/test/SemaCXX/cxx11-thread-local.cpp @@ -0,0 +1,23 @@ +// RUN: %clang_cc1 -std=c++11 -triple=x86_64-linux-gnu -verify %s + +struct S { + static thread_local int a; + static int b; // expected-note {{here}} + thread_local int c; // expected-error {{'thread_local' is only allowed on variable declarations}} + static thread_local int d; // expected-note {{here}} +}; + +thread_local int S::a; +thread_local int S::b; // expected-error {{thread-local declaration of 'b' follows non-thread-local declaration}} +thread_local int S::c; // expected-error {{non-static data member defined out-of-line}} +int S::d; // expected-error {{non-thread-local declaration of 'd' follows thread-local declaration}} + +thread_local int x[3]; +thread_local int y[3]; +thread_local int z[3]; // expected-note {{previous}} + +void f() { + thread_local int x; + static thread_local int y; + extern thread_local int z; // expected-error {{redefinition of 'z' with a different type}} +} diff --git a/test/SemaCXX/cxx11-user-defined-literals-unused.cpp b/test/SemaCXX/cxx11-user-defined-literals-unused.cpp new file mode 100644 index 00000000000..cd93ffbf21e --- /dev/null +++ b/test/SemaCXX/cxx11-user-defined-literals-unused.cpp @@ -0,0 +1,13 @@ +// RUN: %clang_cc1 -std=c++11 -verify %s -Wunused + +namespace { +double operator"" _x(long double value) { return double(value); } +int operator"" _ii(long double value) { return int(value); } // expected-warning {{not needed and will not be emitted}} +} + +namespace rdar13589856 { + template double value() { return 3.2_x; } + template int valuei() { return 3.2_ii; } + + double get_value() { return value(); } +} diff --git a/test/SemaCXX/cxx1y-array-runtime-bound.cpp b/test/SemaCXX/cxx1y-array-runtime-bound.cpp new file mode 100644 index 00000000000..1643adbe0b0 --- /dev/null +++ b/test/SemaCXX/cxx1y-array-runtime-bound.cpp @@ -0,0 +1,68 @@ +// RUN: %clang_cc1 -std=c++1y %s -verify -triple=x86_64-linux-gnu -pedantic-errors + +// FIXME: many diagnostics here say 'variably modified type'. +// catch this case and say 'array of runtime bound' instead. + +namespace std { struct type_info; } + +struct S { + int arr[__SIZE_MAX__ / 32]; +}; +S s[32]; // expected-error {{array is too large}} + +int n; +int a[n]; // expected-error {{not allowed at file scope}} + +struct T { + int a[n]; // expected-error {{fields must have a constant size}} + static int b[n]; // expected-error {{not allowed at file scope}} +}; + +int g(int n, int a[n]); + +template struct X {}; +template struct Y {}; +template struct Z {}; // expected-error {{of variably modified type}} + +int f(int n) { + int arb[n]; // expected-note 3{{here}} + [arb] {} (); // expected-error {{cannot be captured}} + + // FIXME: an array of runtime bound can be captured by reference. + [&arb] { // expected-error {{cannot be captured}} + // Capturing the array implicitly captures the bound, if we need it + // in a range-based for loop. + for (auto &n : arb) { } // expected-error {{cannot be captured}} + } (); + + X x; // expected-error {{variably modified type}} + + int arb_neg[-1]; // expected-error {{negative size}} + int arb_of_array[n][2]; + int arr[3] = { 1, 2, 3, 4 }; // expected-error {{excess elements}} + char foo[4] = "fool"; // expected-error {{initializer-string for char array is too long}} + + static int not_auto1[n]; // expected-error {{can not have 'static'}} + extern int not_auto2[n]; // expected-error {{can not have 'extern'}} + // FIXME: say 'thread_local' not 'static'. + thread_local int not_auto1[n]; // expected-error {{can not have 'static'}} + + // FIXME: these should all be invalid. + auto &&ti1 = typeid(arb); + auto &&ti2 = typeid(int[n]); + auto &&so1 = sizeof(arb); + auto &&so2 = sizeof(int[n]); + auto *p = &arb; + decltype(arb) arb2; + int (*arbp)[n] = 0; + const int (&arbr)[n] = arbr; // expected-warning {{not yet bound}} + typedef int arbty[n]; + int array_of_arb[2][n]; + + struct Dyn { Dyn() {} Dyn(int) {} ~Dyn() {} }; + + // FIXME: these should be valid. + int arb_dynamic[n] = { 1, 2, 3, 4 }; // expected-error {{may not be initialized}} + Dyn dyn[n]; // expected-error {{non-POD}} + Dyn dyn_init[n] = { 1, 2, 3, 4 }; // expected-error {{non-POD}} +} diff --git a/test/SemaCXX/cxx1y-constexpr-not-const.cpp b/test/SemaCXX/cxx1y-constexpr-not-const.cpp new file mode 100644 index 00000000000..3f100b8b691 --- /dev/null +++ b/test/SemaCXX/cxx1y-constexpr-not-const.cpp @@ -0,0 +1,18 @@ +// RUN: %clang_cc1 -std=c++11 %s -verify +// RUN: %clang_cc1 -std=c++1y %s -verify -DCXX1Y + +struct X { + constexpr int f(); // @5 + int f(); // @6 +}; + +#ifdef CXX1Y +// FIXME: Detect this situation and provide a better recovery. + +// expected-error@6 {{class member cannot be redeclared}} +// expected-note@5 {{previous}} +// expected-error@6 {{non-constexpr declaration of 'f' follows constexpr declaration}} +// expected-note@5 {{previous}} +#else +// expected-warning@5 {{'constexpr' non-static member function will not be implicitly 'const' in C++1y; add 'const' to avoid a change in behavior}} +#endif diff --git a/test/SemaCXX/cxx1y-deduced-return-type.cpp b/test/SemaCXX/cxx1y-deduced-return-type.cpp new file mode 100644 index 00000000000..f0146f88d68 --- /dev/null +++ b/test/SemaCXX/cxx1y-deduced-return-type.cpp @@ -0,0 +1,338 @@ +// RUN: %clang_cc1 -std=c++1y -verify -fsyntax-only %s + +auto f(); // expected-note {{previous}} +int f(); // expected-error {{differ only in their return type}} + +auto &g(); +auto g() -> auto &; + +auto h() -> auto *; +auto *h(); + +struct Conv1 { + operator auto(); // expected-note {{declared here}} +} conv1; +int conv1a = conv1; // expected-error {{function 'operator auto' with deduced return type cannot be used before it is defined}} +// expected-error@-1 {{no viable conversion}} +Conv1::operator auto() { return 123; } +int conv1b = conv1; +int conv1c = conv1.operator auto(); +int conv1d = conv1.operator int(); // expected-error {{no member named 'operator int'}} + +struct Conv2 { + operator auto() { return 0; } // expected-note 2{{previous}} + operator auto() { return 0.; } // expected-error {{cannot be redeclared}} expected-error {{redefinition of 'operator auto'}} +}; + +struct Conv3 { + operator auto() { int *p = nullptr; return p; } // expected-note {{candidate}} + operator auto*() { int *p = nullptr; return p; } // expected-note {{candidate}} +} conv3; +int *conv3a = conv3; // expected-error {{ambiguous}} +int *conv3b = conv3.operator auto(); +int *conv3c = conv3.operator auto*(); + +template +struct Conv4 { + operator auto() { return T(); } +}; +Conv4 conv4int; +int conv4a = conv4int; +int conv4b = conv4int.operator auto(); + +auto a(); +auto a() { return 0; } +using T = decltype(a()); +using T = int; +auto a(); // expected-note {{previous}} +using T = decltype(a()); +auto *a(); // expected-error {{differ only in their return type}} + +auto b(bool k) { + if (k) + return "hello"; + return "goodbye"; +} + +auto *ptr_1() { + return 100; // expected-error {{cannot deduce return type 'auto *' from returned value of type 'int'}} +} + +const auto &ref_1() { + return 0; // expected-warning {{returning reference to local temporary}} +} + +auto init_list() { + return { 1, 2, 3 }; // expected-error {{cannot deduce return type from initializer list}} +} + +auto fwd_decl(); // expected-note 2{{here}} + +int n = fwd_decl(); // expected-error {{function 'fwd_decl' with deduced return type cannot be used before it is defined}} +int k = sizeof(fwd_decl()); // expected-error {{used before it is defined}} + +auto fac(int n) { + if (n <= 2) + return n; + return n * fac(n-1); // ok +} + +auto fac_2(int n) { // expected-note {{declared here}} + if (n > 2) + return n * fac_2(n-1); // expected-error {{cannot be used before it is defined}} + return n; +} + +auto void_ret() {} +using Void = void; +using Void = decltype(void_ret()); + +auto &void_ret_2() {} // expected-error {{cannot deduce return type 'auto &' for function with no return statements}} +const auto void_ret_3() {} // ok, return type 'const void' is adjusted to 'void' + +const auto void_ret_4() { + if (false) + return void(); + if (false) + return; + return 0; // expected-error {{'auto' in return type deduced as 'int' here but deduced as 'void' in earlier return statement}} +} + +namespace Templates { + template auto f1() { + return T() + 1; + } + template auto &f2(T &&v) { return v; } + int a = f1(); + const int &b = f2(0); + double d; + float &c = f2(0.0); // expected-error {{non-const lvalue reference to type 'float' cannot bind to a value of unrelated type 'double'}} + + template auto fwd_decl(); // expected-note {{declared here}} + int e = fwd_decl(); // expected-error {{cannot be used before it is defined}} + template auto fwd_decl() { return 0; } + int f = fwd_decl(); + template auto fwd_decl(); + int g = fwd_decl(); + + auto (*p)() = f1; // expected-error {{incompatible initializer}} + auto (*q)() = f1; // ok + + typedef decltype(f2(1.2)) dbl; // expected-note {{previous}} + typedef float dbl; // expected-error {{typedef redefinition with different types ('float' vs 'decltype(f2(1.2))' (aka 'double &'))}} + + extern template auto fwd_decl(); + int k1 = fwd_decl(); + extern template int fwd_decl(); // expected-error {{does not refer to a function template}} + int k2 = fwd_decl(); + + template auto instantiate() { T::error; } // expected-error {{has no members}} + extern template auto instantiate(); // ok + int k = instantiate(); // expected-note {{in instantiation of}} + template<> auto instantiate() {} // ok + template<> void instantiate() {} // expected-error {{no function template matches}} + + template auto arg_single() { return 0; } + template auto arg_multi() { return 0l; } + template auto arg_multi(int) { return "bad"; } + template struct Outer { + static auto arg_single() { return 0.f; } + static auto arg_multi() { return 0.; } + static auto arg_multi(int) { return "bad"; } + }; + template T &take_fn(T (*p)()); + + int &check1 = take_fn(arg_single); // expected-error {{no matching}} expected-note@-2 {{couldn't infer}} + int &check2 = take_fn(arg_single); + int &check3 = take_fn(arg_single); // expected-error {{no matching}} expected-note@-4{{no overload of 'arg_single'}} + int &check4 = take_fn(arg_single); + long &check5 = take_fn(arg_multi); // expected-error {{no matching}} expected-note@-6 {{couldn't infer}} + long &check6 = take_fn(arg_multi); + long &check7 = take_fn(arg_multi); // expected-error {{no matching}} expected-note@-8{{no overload of 'arg_multi'}} + long &check8 = take_fn(arg_multi); + + float &mem_check1 = take_fn(Outer::arg_single); + float &mem_check2 = take_fn(Outer::arg_single); + double &mem_check3 = take_fn(Outer::arg_multi); + double &mem_check4 = take_fn(Outer::arg_multi); + + namespace Deduce1 { + template auto f() { return 0; } // expected-note {{candidate}} + template void g(T(*)()); // expected-note 2{{candidate}} + void h() { + auto p = f; + auto (*q)() = f; + int (*r)() = f; // expected-error {{does not match}} + g(f); + g(f); // expected-error {{no matching function}} + g(f); // expected-error {{no matching function}} + } + } + + namespace Deduce2 { + template auto f(int) { return 0; } // expected-note {{candidate}} + template void g(T(*)(int)); // expected-note 2{{candidate}} + void h() { + auto p = f; + auto (*q)(int) = f; + int (*r)(int) = f; // expected-error {{does not match}} + g(f); + g(f); // expected-error {{no matching function}} + g(f); // expected-error {{no matching function}} + } + } + + namespace Deduce3 { + template auto f(T) { return 0; } + template void g(T(*)(int)); // expected-note {{couldn't infer}} + void h() { + auto p = f; + auto (*q)(int) = f; + int (*r)(int) = f; // ok + g(f); + g(f); // ok + g(f); // expected-error {{no matching function}} + } + } + + namespace DeduceInDeducedReturnType { + template auto f() -> auto (T::*)(U) { + int (T::*result)(U) = nullptr; + return result; + } + struct S {}; + int (S::*(*p)())(double) = f; + int (S::*(*q)())(double) = f; + } +} + +auto fwd_decl_using(); +namespace N { using ::fwd_decl_using; } +auto fwd_decl_using() { return 0; } +namespace N { int k = N::fwd_decl_using(); } + +namespace OverloadResolutionNonTemplate { + auto f(); + auto f(int); // expected-note {{here}} + + int &g(int (*f)()); // expected-note {{not viable: no overload of 'f' matching 'int (*)()'}} + char &g(int (*f)(int)); // expected-note {{not viable: no overload of 'f' matching 'int (*)(int)'}} + + int a = g(f); // expected-error {{no matching function}} + + auto f() { return 0; } + + // FIXME: It's not completely clear whether this should be ill-formed. + int &b = g(f); // expected-error {{used before it is defined}} + + auto f(int) { return 0.0; } + + int &c = g(f); // ok +} + +namespace OverloadResolutionTemplate { + auto f(); + template auto f(T); + + int &g(int (*f)()); // expected-note {{not viable: no overload of 'f' matching 'int (*)()'}} expected-note {{candidate}} + char &g(int (*f)(int)); // expected-note {{not viable: no overload of 'f' matching 'int (*)(int)'}} expected-note {{candidate}} + + int a = g(f); // expected-error {{no matching function}} + + auto f() { return 0; } + + int &b = g(f); // ok (presumably), due to deduction failure forming type of 'f' + + template auto f(T) { return 0; } + + int &c = g(f); // expected-error {{ambiguous}} +} + +namespace DefaultedMethods { + struct A { + auto operator=(const A&) = default; // expected-error {{must return 'DefaultedMethods::A &'}} + A &operator=(A&&); // expected-note {{previous}} + }; + auto A::operator=(A&&) = default; // expected-error {{differs from the declaration in the return type}} +} + +namespace Constexpr { + constexpr auto f1(int n) { return n; } + struct NonLiteral { ~NonLiteral(); } nl; // expected-note {{user-provided destructor}} + constexpr auto f2(int n) { return nl; } // expected-error {{return type 'Constexpr::NonLiteral' is not a literal type}} +} + +// It's not really clear whether these are valid, but this matches g++. +using size_t = decltype(sizeof(0)); +auto operator new(size_t n, const char*); // expected-error {{must return type 'void *'}} +auto operator delete(void *, const char*); // expected-error {{must return type 'void'}} + +namespace Virtual { + struct S { + virtual auto f() { return 0; } // expected-error {{function with deduced return type cannot be virtual}} expected-note {{here}} + }; + // Allow 'auto' anyway for error recovery. + struct T : S { + int f(); + }; + struct U : S { + auto f(); // expected-error {{different return}} + }; + + // And here's why... + struct V { virtual auto f(); }; // expected-error {{cannot be virtual}} + struct W : V { virtual auto f(); }; // expected-error {{cannot be virtual}} + auto V::f() { return 0; } // in tu1.cpp + auto W::f() { return 0.0; } // in tu2.cpp + W w; + int k1 = w.f(); + int k2 = ((V&)w).f(); +} + +namespace std_examples { + +namespace NoReturn { + auto f() {} + void (*p)() = &f; + + auto *g() {} // expected-error {{cannot deduce return type 'auto *' for function with no return statements}} +} + +namespace UseBeforeComplete { + auto n = n; // expected-error {{variable 'n' declared with 'auto' type cannot appear in its own initializer}} + auto f(); // expected-note {{declared here}} + void g() { &f; } // expected-error {{function 'f' with deduced return type cannot be used before it is defined}} + auto sum(int i) { + if (i == 1) + return i; + else + return sum(i - 1) + i; + } +} + +namespace Redecl { + auto f(); + auto f() { return 42; } + auto f(); // expected-note 2{{previous}} + int f(); // expected-error {{functions that differ only in their return type cannot be overloaded}} + decltype(auto) f(); // expected-error {{cannot be overloaded}} + + template auto g(T t) { return t; } // expected-note {{candidate}} + template auto g(int); + template char g(char); // expected-error {{does not refer to a function}} + template<> auto g(double); + + template T g(T t) { return t; } // expected-note {{candidate}} + template char g(char); + template auto g(float); + + void h() { return g(42); } // expected-error {{ambiguous}} +} + +namespace ExplicitInstantiationDecl { + template auto f(T t) { return t; } + extern template auto f(int); + int (*p)(int) = f; +} + +} diff --git a/test/SemaCXX/cxx1y-initializer-aggregates.cpp b/test/SemaCXX/cxx1y-initializer-aggregates.cpp new file mode 100644 index 00000000000..9b542403def --- /dev/null +++ b/test/SemaCXX/cxx1y-initializer-aggregates.cpp @@ -0,0 +1,63 @@ +// RUN: %clang_cc1 -std=c++1y %s -verify + +namespace in_class_init { + union U { char c; double d = 4.0; }; + constexpr U u1 = U(); + constexpr U u2 {}; + constexpr U u3 { 'x' }; + static_assert(u1.d == 4.0, ""); + static_assert(u2.d == 4.0, ""); + static_assert(u3.c == 'x', ""); + + struct A { + int n = 5; + int m = n * 3; + union { + char c; + double d = 4.0; + }; + }; + constexpr A a1 {}; + constexpr A a2 { 8 }; + constexpr A a3 { 1, 2, { 3 } }; + constexpr A a4 { 1, 2, { .d = 3.0 } }; + static_assert(a1.d == 4.0, ""); + static_assert(a2.m == 24, ""); + static_assert(a2.d == 4.0, ""); + static_assert(a3.c == 3, ""); + static_assert(a3.d == 4.0, ""); // expected-error {{constant expression}} expected-note {{active member 'c'}} + static_assert(a4.d == 3.0, ""); + + struct B { + int n; + constexpr int f() { return n * 5; } + int m = f(); + }; + B b1 {}; + constexpr B b2 { 2 }; + B b3 { 1, 2 }; + static_assert(b2.m == 10, ""); + + struct C { + int k; + union { + int l = k; // expected-error {{invalid use of non-static}} + }; + }; +} + +namespace nested_aggregate_init { + struct A { + int n = 5; + int b = n * 3; + }; + struct B { + constexpr B(int k) : d(1.23), k(k) {} + // Within this aggregate, both this object's 'this' and the temporary's + // 'this' are used. + constexpr int f() const { return A{k}.b; } + double d; + int k; + }; + static_assert(B(6).f() == 18, ""); +} diff --git a/test/SemaCXX/cxx98-compat-pedantic.cpp b/test/SemaCXX/cxx98-compat-pedantic.cpp index 18fd1520ec4..208ea4ce923 100644 --- a/test/SemaCXX/cxx98-compat-pedantic.cpp +++ b/test/SemaCXX/cxx98-compat-pedantic.cpp @@ -1,3 +1,5 @@ +// RUN: %clang_cc1 -fsyntax-only -std=c++1y -DCXX1Y -Wc++98-compat-pedantic -verify %s +// RUN: %clang_cc1 -fsyntax-only -std=c++1y -DCXX1Y -Wc++98-compat -Werror %s // RUN: %clang_cc1 -fsyntax-only -std=c++11 -Wc++98-compat-pedantic -verify %s // RUN: %clang_cc1 -fsyntax-only -std=c++11 -Wc++98-compat -Werror %s // RUN: %clang_cc1 -fsyntax-only -std=c++98 -Werror %s @@ -38,3 +40,12 @@ long long ll1 = // expected-warning {{'long long' is incompatible with C++98}} unsigned long long ull1 = // expected-warning {{'long long' is incompatible with C++98}} 42ULL; // expected-warning {{'long long' is incompatible with C++98}} +int k = 0b1001; +#ifdef CXX1Y +// expected-warning@-2 {{binary integer literals are incompatible with C++ standards before C++1y}} +#endif + +void f(int n) { int a[n]; } +#ifdef CXX1Y +// expected-warning@-2 {{arrays of runtime bound are incompatible with C++ standards before C++1y}} +#endif diff --git a/test/SemaCXX/enum-unscoped-nonexistent.cpp b/test/SemaCXX/enum-unscoped-nonexistent.cpp index d49800caa61..e9da38f558d 100644 --- a/test/SemaCXX/enum-unscoped-nonexistent.cpp +++ b/test/SemaCXX/enum-unscoped-nonexistent.cpp @@ -5,8 +5,8 @@ struct Base { }; template struct S : Base { enum E : int; - constexpr int f(); - constexpr int g(); // expected-note {{declared here}} + constexpr int f() const; + constexpr int g() const; // expected-note {{declared here}} void h(); }; template<> enum S::E : int {}; // expected-note {{enum 'S::E' was explicitly specialized here}} @@ -16,13 +16,13 @@ template enum S::E : int { b = 8 }; // The unqualified-id here names a member of the non-dependent base class Base // and not the injected enumerator name 'a' from the specialization. -template constexpr int S::f() { return a; } +template constexpr int S::f() const { return a; } static_assert(S().f() == 1, ""); static_assert(S().f() == 1, ""); // The unqualified-id here names a member of the current instantiation, which // bizarrely might not exist in some instantiations. -template constexpr int S::g() { return b; } // expected-error {{enumerator 'b' does not exist in instantiation of 'S'}} +template constexpr int S::g() const { return b; } // expected-error {{enumerator 'b' does not exist in instantiation of 'S'}} static_assert(S().g() == 1, ""); // expected-note {{here}} expected-error {{not an integral constant expression}} expected-note {{undefined}} static_assert(S().g() == 2, ""); static_assert(S().g() == 8, ""); diff --git a/test/SemaCXX/for-range-unused.cpp b/test/SemaCXX/for-range-unused.cpp index ce6b379cc19..ec11015552e 100644 --- a/test/SemaCXX/for-range-unused.cpp +++ b/test/SemaCXX/for-range-unused.cpp @@ -7,7 +7,7 @@ template void doIt() { int a; // expected-warning {{unused variable 'a'}} - for (auto& e : elements) + for (auto& e : elements) // expected-warning {{unused variable 'e'}} ; } @@ -17,5 +17,5 @@ template int main(int, char**) { Vector vector; - vector.doIt(); + vector.doIt(); // expected-note {{here}} } diff --git a/test/SemaCXX/i-c-e-cxx.cpp b/test/SemaCXX/i-c-e-cxx.cpp index 5631577e0fa..c80323ccd78 100644 --- a/test/SemaCXX/i-c-e-cxx.cpp +++ b/test/SemaCXX/i-c-e-cxx.cpp @@ -60,7 +60,7 @@ int* y = reinterpret_cast(x); // expected-error {{cannot initialize // This isn't an integral constant expression, but make sure it folds anyway. struct PR8836 { char _; long long a; }; // expected-warning {{long long}} -int PR8836test[(__typeof(sizeof(int)))&reinterpret_cast((((PR8836*)0)->a))]; // expected-warning {{folded to constant array as an extension}} expected-note {{cast which performs the conversions of a reinterpret_cast is not allowed in a constant expression}} +int PR8836test[(__typeof(sizeof(int)))&reinterpret_cast((((PR8836*)0)->a))]; // expected-warning {{folded to constant array as an extension}} expected-note {{cast that performs the conversions of a reinterpret_cast is not allowed in a constant expression}} const int nonconst = 1.0; // expected-note {{declared here}} int arr[nonconst]; // expected-warning {{folded to constant array as an extension}} expected-note {{initializer of 'nonconst' is not a constant expression}} diff --git a/test/SemaCXX/linkage-spec.cpp b/test/SemaCXX/linkage-spec.cpp index 0ba95081245..504df0d35c2 100644 --- a/test/SemaCXX/linkage-spec.cpp +++ b/test/SemaCXX/linkage-spec.cpp @@ -106,3 +106,11 @@ namespace PR9162 { return sizeof(ArtsSink); } } + +namespace pr14958 { + namespace js { extern int ObjectClass; } + extern "C" { + namespace js {} + } + int js::ObjectClass; +} diff --git a/test/SemaCXX/linkage.cpp b/test/SemaCXX/linkage.cpp index 6b73d596e01..13d295a5d59 100644 --- a/test/SemaCXX/linkage.cpp +++ b/test/SemaCXX/linkage.cpp @@ -94,3 +94,12 @@ extern "C" { // CHECK: define linkonce_odr i8* @_ZN5test21A1BILj0EE3fooEv( // CHECK: define linkonce_odr i8* @_ZN5test11A3fooILj0EEEPvv( + +namespace test5 { + struct foo { + }; + extern "C" { + const foo bar[] = { + }; + } +} diff --git a/test/SemaCXX/linkage2.cpp b/test/SemaCXX/linkage2.cpp index ddf4064215d..3cfa98138ba 100644 --- a/test/SemaCXX/linkage2.cpp +++ b/test/SemaCXX/linkage2.cpp @@ -152,3 +152,15 @@ namespace test15 { const int a = 5; // expected-note {{previous definition is here}} static const int a; // expected-error {{redefinition of 'a'}} } + +namespace test16 { + extern "C" { + class Foo { + int x; + friend int bar(Foo *y); + }; + int bar(Foo *y) { + return y->x; + } + } +} diff --git a/test/SemaCXX/pascal-strings.cpp b/test/SemaCXX/pascal-strings.cpp index 89194b54aa8..f4c692db58f 100644 --- a/test/SemaCXX/pascal-strings.cpp +++ b/test/SemaCXX/pascal-strings.cpp @@ -4,3 +4,5 @@ const wchar_t *pascalString = L"\pThis is a Pascal string"; unsigned char a[3] = "\pa"; unsigned char b[3] = "\pab"; unsigned char c[3] = "\pabc"; // expected-error {{initializer-string for char array is too long}} +unsigned char d[3] = ("\pab"); +unsigned char e[3] = ("\pabc"); // expected-error {{initializer-string for char array is too long}} diff --git a/test/SemaCXX/trailing-return-0x.cpp b/test/SemaCXX/trailing-return-0x.cpp index 462b4fa3da0..bd601db2ac1 100644 --- a/test/SemaCXX/trailing-return-0x.cpp +++ b/test/SemaCXX/trailing-return-0x.cpp @@ -85,3 +85,12 @@ namespace PR12053 { f2(0); // expected-error{{no matching function for call to 'f2'}} } } + +namespace DR1608 { + struct S { + void operator+(); + int operator[](int); + auto f() -> decltype(+*this); // expected-note {{here}} + auto f() -> decltype((*this)[0]); // expected-error {{cannot be overloaded}} + }; +} diff --git a/test/SemaCXX/undefined-internal.cpp b/test/SemaCXX/undefined-internal.cpp index 839fdafb341..1b76a86dcb5 100644 --- a/test/SemaCXX/undefined-internal.cpp +++ b/test/SemaCXX/undefined-internal.cpp @@ -323,3 +323,10 @@ namespace test13 { } } +namespace test14 { + extern "C" const int foo; + + int f() { + return foo; + } +} diff --git a/test/SemaCXX/uninitialized.cpp b/test/SemaCXX/uninitialized.cpp index 2aa56623f69..665cfe7e911 100644 --- a/test/SemaCXX/uninitialized.cpp +++ b/test/SemaCXX/uninitialized.cpp @@ -511,3 +511,15 @@ namespace operators { int x = x = 5; } + +namespace lambdas { + struct A { + template A(T) {} + int x; + }; + A a0([] { return a0.x; }); // ok + void f() { + A a1([=] { return a1.x; }); // expected-warning{{variable 'a1' is uninitialized when used within its own initialization}} + A a2([&] { return a2.x; }); // ok + } +} diff --git a/test/SemaCXX/warn-c++11-extensions.cpp b/test/SemaCXX/warn-c++11-extensions.cpp index 8f351711195..bf8612aa398 100644 --- a/test/SemaCXX/warn-c++11-extensions.cpp +++ b/test/SemaCXX/warn-c++11-extensions.cpp @@ -5,3 +5,5 @@ long long ll1 = // expected-warning {{'long long' is a C++11 extension}} unsigned long long ull1 = // expected-warning {{'long long' is a C++11 extension}} 42ULL; // expected-warning {{'long long' is a C++11 extension}} +enum struct E1 { A, B }; // expected-warning {{scoped enumerations are a C++11 extension}} +enum class E2 { C, D }; // expected-warning {{scoped enumerations are a C++11 extension}} diff --git a/test/SemaCXX/warn-overloaded-virtual.cpp b/test/SemaCXX/warn-overloaded-virtual.cpp index 9b0f5aa9f33..629d59dee53 100644 --- a/test/SemaCXX/warn-overloaded-virtual.cpp +++ b/test/SemaCXX/warn-overloaded-virtual.cpp @@ -120,3 +120,21 @@ struct MostDerived: Derived3, Derived2 { void func(); }; } + +namespace { + class A { + virtual int foo(bool) const; + // expected-note@-1{{type mismatch at 1st parameter ('bool' vs 'int')}} + virtual int foo(int, int) const; + // expected-note@-1{{different number of parameters (2 vs 1)}} + virtual int foo(int*) const; + // expected-note@-1{{type mismatch at 1st parameter ('int *' vs 'int')}} + virtual int foo(int) volatile; + // expected-note@-1{{different qualifiers (volatile vs const)}} + }; + + class B : public A { + virtual int foo(int) const; + // expected-warning@-1{{hides overloaded virtual functions}} + }; +} diff --git a/test/SemaCXX/warn-thread-safety-analysis.cpp b/test/SemaCXX/warn-thread-safety-analysis.cpp index 3f41124d47a..bc4b40ead73 100644 --- a/test/SemaCXX/warn-thread-safety-analysis.cpp +++ b/test/SemaCXX/warn-thread-safety-analysis.cpp @@ -1475,8 +1475,8 @@ namespace substitution_test { public: Mutex mu; - void lockData() __attribute__((exclusive_lock_function(mu))) { } - void unlockData() __attribute__((unlock_function(mu))) { } + void lockData() __attribute__((exclusive_lock_function(mu))); + void unlockData() __attribute__((unlock_function(mu))); void doSomething() __attribute__((exclusive_locks_required(mu))) { } }; @@ -1484,8 +1484,8 @@ namespace substitution_test { class DataLocker { public: - void lockData (MyData *d) __attribute__((exclusive_lock_function(d->mu))) { } - void unlockData(MyData *d) __attribute__((unlock_function(d->mu))) { } + void lockData (MyData *d) __attribute__((exclusive_lock_function(d->mu))); + void unlockData(MyData *d) __attribute__((unlock_function(d->mu))); }; @@ -2858,7 +2858,7 @@ void Foo::lock1() EXCLUSIVE_LOCK_FUNCTION(mu1_) { } void Foo::slock1() SHARED_LOCK_FUNCTION(mu1_) { - mu1_.Lock(); + mu1_.ReaderLock(); } void Foo::lock3() EXCLUSIVE_LOCK_FUNCTION(mu1_, mu2_, mu3_) { @@ -3640,8 +3640,8 @@ class Foo { LOCKS_EXCLUDED(mu2_); void lock() EXCLUSIVE_LOCK_FUNCTION(mu1_) EXCLUSIVE_LOCK_FUNCTION(mu2_); - void readerlock() EXCLUSIVE_LOCK_FUNCTION(mu1_) - EXCLUSIVE_LOCK_FUNCTION(mu2_); + void readerlock() SHARED_LOCK_FUNCTION(mu1_) + SHARED_LOCK_FUNCTION(mu2_); void unlock() UNLOCK_FUNCTION(mu1_) UNLOCK_FUNCTION(mu2_); bool trylock() EXCLUSIVE_TRYLOCK_FUNCTION(true, mu1_) @@ -3663,9 +3663,9 @@ void Foo::foo2() { } void Foo::foo3() { } -void Foo::lock() { } -void Foo::readerlock() { } -void Foo::unlock() { } +void Foo::lock() { mu1_.Lock(); mu2_.Lock(); } +void Foo::readerlock() { mu1_.ReaderLock(); mu2_.ReaderLock(); } +void Foo::unlock() { mu1_.Unlock(); mu2_.Unlock(); } bool Foo::trylock() { return true; } bool Foo::readertrylock() { return true; } @@ -3915,3 +3915,73 @@ void bar2() EXCLUSIVE_LOCKS_REQUIRED(getMutex1(), getMutex2()); } // end namespace UnevaluatedContextTest + +namespace LockUnlockFunctionTest { + +// Check built-in lock functions +class LOCKABLE MyLockable { +public: + void lock() EXCLUSIVE_LOCK_FUNCTION() { mu_.Lock(); } + void readerLock() SHARED_LOCK_FUNCTION() { mu_.ReaderLock(); } + void unlock() UNLOCK_FUNCTION() { mu_.Unlock(); } + +private: + Mutex mu_; +}; + + +class Foo { +public: + // Correct lock/unlock functions + void lock() EXCLUSIVE_LOCK_FUNCTION(mu_) { + mu_.Lock(); + } + + void readerLock() SHARED_LOCK_FUNCTION(mu_) { + mu_.ReaderLock(); + } + + void unlock() UNLOCK_FUNCTION(mu_) { + mu_.Unlock(); + } + + // Check failure to lock. + void lockBad() EXCLUSIVE_LOCK_FUNCTION(mu_) { // expected-note {{mutex acquired here}} + mu2_.Lock(); + mu2_.Unlock(); + } // expected-warning {{expecting mutex 'mu_' to be locked at the end of function}} + + void readerLockBad() SHARED_LOCK_FUNCTION(mu_) { // expected-note {{mutex acquired here}} + mu2_.Lock(); + mu2_.Unlock(); + } // expected-warning {{expecting mutex 'mu_' to be locked at the end of function}} + + void unlockBad() UNLOCK_FUNCTION(mu_) { // expected-note {{mutex acquired here}} + mu2_.Lock(); + mu2_.Unlock(); + } // expected-warning {{mutex 'mu_' is still locked at the end of function}} + + // Check locking the wrong thing. + void lockBad2() EXCLUSIVE_LOCK_FUNCTION(mu_) { // expected-note {{mutex acquired here}} + mu2_.Lock(); // expected-note {{mutex acquired here}} + } // expected-warning {{expecting mutex 'mu_' to be locked at the end of function}} \ + // expected-warning {{mutex 'mu2_' is still locked at the end of function}} + + + void readerLockBad2() SHARED_LOCK_FUNCTION(mu_) { // expected-note {{mutex acquired here}} + mu2_.ReaderLock(); // expected-note {{mutex acquired here}} + } // expected-warning {{expecting mutex 'mu_' to be locked at the end of function}} \ + // expected-warning {{mutex 'mu2_' is still locked at the end of function}} + + + void unlockBad2() UNLOCK_FUNCTION(mu_) { // expected-note {{mutex acquired here}} + mu2_.Unlock(); // expected-warning {{unlocking 'mu2_' that was not locked}} + } // expected-warning {{mutex 'mu_' is still locked at the end of function}} + +private: + Mutex mu_; + Mutex mu2_; +}; + +} // end namespace LockUnlockFunctionTest + diff --git a/test/SemaCXX/warn-unused-filescoped.cpp b/test/SemaCXX/warn-unused-filescoped.cpp index e12668bf2a4..9fb601130d3 100644 --- a/test/SemaCXX/warn-unused-filescoped.cpp +++ b/test/SemaCXX/warn-unused-filescoped.cpp @@ -133,6 +133,27 @@ namespace test6 { }; } +namespace test7 +{ + template + static inline void foo(T) { } + + // This should not emit an unused-function warning since it inherits + // the static storage type from the base template. + template<> + inline void foo(int) { } + + // Partial specialization + template + static inline void bar(T, U) { } + + template + inline void bar(int, U) { } + + template<> + inline void bar(int, int) { } +}; + namespace pr14776 { namespace { struct X {}; diff --git a/test/SemaCXX/warn-unused-variables-error.cpp b/test/SemaCXX/warn-unused-variables-error.cpp new file mode 100644 index 00000000000..6386c4b6824 --- /dev/null +++ b/test/SemaCXX/warn-unused-variables-error.cpp @@ -0,0 +1,10 @@ +// RUN: %clang_cc1 -fsyntax-only -Wunused-variable -verify %s + +namespace PR6948 { + template class X; // expected-note{{template is declared here}} + + void f() { + X str (read_from_file()); // expected-error{{use of undeclared identifier 'read_from_file'}} \ + expected-error{{implicit instantiation of undefined template 'PR6948::X'}} + } +} diff --git a/test/SemaCXX/warn-unused-variables.cpp b/test/SemaCXX/warn-unused-variables.cpp index 4e8d51d319e..00597f929b2 100644 --- a/test/SemaCXX/warn-unused-variables.cpp +++ b/test/SemaCXX/warn-unused-variables.cpp @@ -41,15 +41,6 @@ void test_dependent_init(T *p) { (void)i; } -namespace PR6948 { - template class X; // expected-note{{template is declared here}} - - void f() { - X str (read_from_file()); // expected-error{{use of undeclared identifier 'read_from_file'}} \ - expected-error{{implicit instantiation of undefined template 'PR6948::X'}} - } -} - void unused_local_static() { static int x = 0; static int y = 0; // expected-warning{{unused variable 'y'}} @@ -135,3 +126,5 @@ namespace ctor_with_cleanups { S2 s((S1())); } } + +#include "Inputs/warn-unused-variables.h" diff --git a/test/SemaObjC/arc-repeated-weak.mm b/test/SemaObjC/arc-repeated-weak.mm index e652bee82d5..b5d9002130d 100644 --- a/test/SemaObjC/arc-repeated-weak.mm +++ b/test/SemaObjC/arc-repeated-weak.mm @@ -327,6 +327,32 @@ void doWhileLoop(Test *a) { } @end +@interface Base1 +@end +@interface Sub1 : Base1 +@end +@interface Sub1(cat) +-(id)prop; +@end + +void test1(Sub1 *s) { + use([s prop]); + use([s prop]); +} + +@interface Base1(cat) +@property (weak) id prop; +@end + +void test2(Sub1 *s) { + // This does not warn because the "prop" in "Base1(cat)" was introduced + // after the method declaration and we don't find it as overridden. + // Always looking for overridden methods after the method declaration is expensive + // and it's not clear it is worth it currently. + use([s prop]); + use([s prop]); +} + class Wrapper { Test *a; diff --git a/test/SemaObjC/arc-system-header.m b/test/SemaObjC/arc-system-header.m index 3443bda99bb..d9392ed73f1 100644 --- a/test/SemaObjC/arc-system-header.m +++ b/test/SemaObjC/arc-system-header.m @@ -1,30 +1,30 @@ -// silly workaround expected-note {{marked unavailable here}} // RUN: %clang_cc1 -fobjc-arc -isystem %S/Inputs %s -DNO_USE // RUN: %clang_cc1 -fobjc-arc -isystem %S/Inputs %s -verify -// another silly workaround expected-note {{marked unavailable here}} #include #ifndef NO_USE void test(id op, void *cp) { cp = test0(op); // expected-error {{'test0' is unavailable: converts between Objective-C and C pointers in -fobjc-arc}} cp = *test1(&op); // expected-error {{'test1' is unavailable: converts between Objective-C and C pointers in -fobjc-arc}} +// expected-note@arc-system-header.h:1 {{marked unavailable here}} +// expected-note@arc-system-header.h:5 {{marked unavailable here}} } -// workaround expected-note {{marked unavailable here}} void test3(struct Test3 *p) { p->field = 0; // expected-error {{'field' is unavailable: this system declaration uses an unsupported type}} + // expected-note@arc-system-header.h:14 {{marked unavailable here}} } -// workaround expected-note {{marked unavailable here}} void test4(Test4 *p) { p->field1 = 0; // expected-error {{'field1' is unavailable: this system declaration uses an unsupported type}} + // expected-note@arc-system-header.h:19 {{marked unavailable here}} p->field2 = 0; } -// workaround expected-note {{marked unavailable here}} void test5(struct Test5 *p) { p->field = 0; // expected-error {{'field' is unavailable: this system field has retaining ownership}} + // expected-note@arc-system-header.h:25 {{marked unavailable here}} } id test6() { @@ -38,12 +38,13 @@ id test6() { x = (id) (test6_helper(), kMagicConstant); } -// workaround expected-note 4 {{marked unavailable here}} expected-note 2 {{property 'prop' is declared unavailable here}} void test7(Test7 *p) { *p.prop = 0; // expected-error {{'prop' is unavailable: this system declaration uses an unsupported type}} p.prop = 0; // expected-error {{'prop' is unavailable: this system declaration uses an unsupported type}} *[p prop] = 0; // expected-error {{'prop' is unavailable: this system declaration uses an unsupported type}} [p setProp: 0]; // expected-error {{'setProp:' is unavailable: this system declaration uses an unsupported type}} +// expected-note@arc-system-header.h:41 4 {{marked unavailable here}} +// expected-note@arc-system-header.h:41 2 {{property 'prop' is declared unavailable here}} } #endif diff --git a/test/SemaObjC/arc-unavailable-for-weakref.m b/test/SemaObjC/arc-unavailable-for-weakref.m index b140c64da71..b9b5cc516de 100644 --- a/test/SemaObjC/arc-unavailable-for-weakref.m +++ b/test/SemaObjC/arc-unavailable-for-weakref.m @@ -56,9 +56,33 @@ __attribute__((objc_arc_weak_reference_unavailable)) @interface I { } -@property (weak) NSFont *font; // expected-note {{property declared here}} +@property (weak) NSFont *font; // expected-error {{synthesizing __weak instance variable of type 'NSFont *', which does not support weak references}} @end -@implementation I -@synthesize font = _font; // expected-error {{synthesis of a weak-unavailable property is disallowed because it requires synthesis of an instance variable of the __weak object}} +@implementation I // expected-note {{when implemented by class I}} +@synthesize font = _font; +@end + +// rdar://13676793 +@protocol MyProtocol +@property (weak) NSFont *font; // expected-error {{synthesizing __weak instance variable of type 'NSFont *', which does not support weak references}} +@end + +@interface I1 +@end + +@implementation I1 // expected-note {{when implemented by class I1}} +@synthesize font = _font; +@end + +@interface Super +@property (weak) NSFont *font; // expected-error {{synthesizing __weak instance variable of type 'NSFont *', which does not support weak references}} +@end + + +@interface I2 : Super +@end + +@implementation I2 // expected-note {{when implemented by class I2}} +@synthesize font = _font; @end diff --git a/test/SemaObjC/arc.m b/test/SemaObjC/arc.m index d89d035fca2..1d4e42de649 100644 --- a/test/SemaObjC/arc.m +++ b/test/SemaObjC/arc.m @@ -756,3 +756,14 @@ void rdar12569201(id key, id value) { @interface C - (void)method:(id[])objects; // expected-error{{must explicitly describe intended ownership of an object array parameter}} @end + +// rdar://13752880 +@interface NSMutableArray : NSArray @end + +typedef __strong NSMutableArray * PSNS; + +void test(NSArray *x) { + NSMutableArray *y = x; // expected-warning {{incompatible pointer types initializing 'NSMutableArray *' with an expression of type 'NSArray *'}} + __strong NSMutableArray *y1 = x; // expected-warning {{incompatible pointer types initializing 'NSMutableArray *' with an expression of type 'NSArray *'}} + PSNS y2 = x; // expected-warning {{incompatible pointer types initializing 'NSMutableArray *' with an expression of type 'NSArray *'}} +} diff --git a/test/SemaObjC/attr-availability.m b/test/SemaObjC/attr-availability.m index bf7ef19bead..fddcd506d44 100644 --- a/test/SemaObjC/attr-availability.m +++ b/test/SemaObjC/attr-availability.m @@ -17,7 +17,7 @@ // rdar://11475360 @interface B : A -- (void)method; // expected-note {{method 'method' declared here}} +- (void)method; // NOTE: we expect 'method' to *not* inherit availability. - (void)overridden __attribute__((availability(macosx,introduced=10.4))); // expected-warning{{overriding method introduced after overridden method on OS X (10.4 vs. 10.3)}} - (void)overridden2 __attribute__((availability(macosx,introduced=10.2))); - (void)overridden3 __attribute__((availability(macosx,deprecated=10.4))); @@ -28,7 +28,35 @@ void f(A *a, B *b) { [a method]; // expected-warning{{'method' is deprecated: first deprecated in OS X 10.2}} - [b method]; // expected-warning {{'method' is deprecated: first deprecated in OS X 10.2}} + [b method]; // no-warning [a proto_method]; // expected-warning{{'proto_method' is deprecated: first deprecated in OS X 10.2}} [b proto_method]; // expected-warning{{'proto_method' is deprecated: first deprecated in OS X 10.2}} } + +// Test case for . Warn about +// using a deprecated method when that method is re-implemented in a +// subclass where the redeclared method is not deprecated. +@interface C +- (void) method __attribute__((availability(macosx,introduced=10.1,deprecated=10.2))); // expected-note {{method 'method' declared here}} +@end + +@interface D : C +- (void) method; +@end + +@interface E : D +- (void) method; +@end + +@implementation D +- (void) method { + [super method]; // expected-warning {{'method' is deprecated: first deprecated in OS X 10.2}} +} +@end + +@implementation E +- (void) method { + [super method]; // no-warning +} +@end + diff --git a/test/SemaObjC/warn-isa-ref.m b/test/SemaObjC/deprecated-objc-introspection.m similarity index 75% rename from test/SemaObjC/warn-isa-ref.m rename to test/SemaObjC/deprecated-objc-introspection.m index b1ffb4fc900..faaef254d59 100644 --- a/test/SemaObjC/warn-isa-ref.m +++ b/test/SemaObjC/deprecated-objc-introspection.m @@ -1,4 +1,10 @@ -// RUN: %clang_cc1 -fsyntax-only -verify %s +// RUN: %clang_cc1 -triple=x86_64-apple-darwin -fsyntax-only -verify %s + +//====------------------------------------------------------------====// +// Test deprecated direct usage of the 'isa' pointer. +//====------------------------------------------------------------====// + +typedef unsigned long NSUInteger; typedef struct objc_object { struct objc_class *isa; @@ -81,3 +87,11 @@ static void func() { } @end +// Test for introspection of Objective-C pointers via bitmasking. + +void testBitmasking(NSObject *p) { + (void) (((NSUInteger) p) & 0x1); // expected-warning {{bitmasking for introspection of Objective-C object pointers is strongly discouraged}} + (void) (0x1 & ((NSUInteger) p)); // expected-warning {{bitmasking for introspection of Objective-C object pointers is strongly discouraged}} + (void) (((NSUInteger) p) ^ 0x1); // no-warning + (void) (0x1 ^ ((NSUInteger) p)); // no-warning +} \ No newline at end of file diff --git a/test/SemaObjC/format-arg-attribute.m b/test/SemaObjC/format-arg-attribute.m index 6edb8fd99b3..dede433f37a 100644 --- a/test/SemaObjC/format-arg-attribute.m +++ b/test/SemaObjC/format-arg-attribute.m @@ -14,7 +14,7 @@ union u1 { int i; } __attribute__((format_arg(1))); // expected-warning {{'form enum e1 { E1V0 } __attribute__((format_arg(1))); // expected-warning {{'format_arg' attribute only applies to functions}} extern NSString *ff3 (const NSString *) __attribute__((format_arg(3-2))); -extern NSString *ff4 (const NSString *) __attribute__((format_arg(foo))); // expected-error {{attribute takes one argument}} +extern NSString *ff4 (const NSString *) __attribute__((format_arg(foo))); // expected-error {{use of undeclared identifier 'foo'}} /* format_arg formats must take and return a string. */ extern NSString *fi0 (int) __attribute__((format_arg(1))); // expected-error {{format argument not a string type}} diff --git a/test/SemaObjC/format-strings-objc.m b/test/SemaObjC/format-strings-objc.m index bd33ad41a56..84901302243 100644 --- a/test/SemaObjC/format-strings-objc.m +++ b/test/SemaObjC/format-strings-objc.m @@ -13,6 +13,7 @@ typedef signed char BOOL; typedef unsigned int NSUInteger; +typedef long NSInteger; @class NSString, Protocol; extern void NSLog(NSString *format, ...); extern void NSLogv(NSString *format, va_list args); @@ -235,3 +236,8 @@ void testByValueObjectInFormat(Foo *obj) { [Bar log2:@"%d", *obj]; // expected-error {{cannot pass object with interface type 'Foo' by value to variadic method; expected type from format string was 'int'}} } +// +void testTypeOf(NSInteger dW, NSInteger dH) { + NSLog(@"dW %d dH %d",({ __typeof__(dW) __a = (dW); __a < 0 ? -__a : __a; }),({ __typeof__(dH) __a = (dH); __a < 0 ? -__a : __a; })); // expected-warning 2 {{values of type 'NSInteger' should not be used as format arguments; add an explicit cast to 'long' instead}} +} + diff --git a/test/SemaObjC/message.m b/test/SemaObjC/message.m index f43bdf98852..40fa102f35d 100644 --- a/test/SemaObjC/message.m +++ b/test/SemaObjC/message.m @@ -106,3 +106,15 @@ void foo5(id p) { // expected-note {{to match this '['}} \ // expected-warning {{instance method '-bar' not found}} } + +@interface I1 +-(void)unavail_meth __attribute__((unavailable)); // expected-note {{marked unavailable here}} +@end + +// rdar://13620447 +void foo6(I1 *p) { + [p + bar]; // expected-warning {{instance method '-bar' not found}} + [p + unavail_meth]; // expected-error {{unavailable}} +} diff --git a/test/SemaObjC/method-conflict-2.m b/test/SemaObjC/method-conflict-2.m index df59f242ce7..ec80a433cc7 100644 --- a/test/SemaObjC/method-conflict-2.m +++ b/test/SemaObjC/method-conflict-2.m @@ -1,4 +1,5 @@ // RUN: %clang_cc1 -Wmethod-signatures -fsyntax-only -verify -Wno-objc-root-class %s +// RUN: %clang_cc1 -x objective-c++ -Wmethod-signatures -fsyntax-only -verify -Wno-objc-root-class %s @interface A @end @interface B : A @end @@ -42,3 +43,24 @@ - (A*) test1 { return 0; } // id -> A* is rdar://problem/8596987 - (id) test2 { return 0; } @end + +// rdar://12522752 +typedef int int32_t; +typedef long long int64_t; + +@interface NSObject @end + +@protocol CKMessage +@property (nonatomic,readonly,assign) int64_t sequenceNumber; // expected-note {{previous definition is here}} +@end + +@protocol CKMessage; + +@interface CKIMMessage : NSObject +@end + +@implementation CKIMMessage +- (int32_t)sequenceNumber { // expected-warning {{conflicting return type in implementation of 'sequenceNumber': 'int64_t' (aka 'long long') vs 'int32_t' (aka 'int')}} + return 0; +} +@end diff --git a/test/SemaObjC/property-category-4.m b/test/SemaObjC/property-category-4.m index e7939b32c11..ccf5e9b2a86 100644 --- a/test/SemaObjC/property-category-4.m +++ b/test/SemaObjC/property-category-4.m @@ -16,3 +16,108 @@ @dynamic d_selectedObjects; // expected-error {{property declared in category 'CAT' cannot be implemented in class implementation}} @end + +// rdar://13713098 +// Test1 +@interface NSArray +- (int)count; +@end + +@protocol MyCountable +@property (readonly) int count; +@end + + +@interface NSArray(Additions) +@end + +@implementation NSArray(Additions) +@end + +// Test2 +@protocol NSProtocol +- (int)count; +@end + +@interface NSArray1 +@end + +@interface NSArray1(Additions) +@end + +@implementation NSArray1(Additions) +@end + +// Test3 +@interface Super +@end + +@interface NSArray2 : Super @end + +@interface NSArray2(Additions) +@end + +@implementation NSArray2(Additions) +@end + +// Test3 +@interface Super1 +@property (readonly) int count; +@end + +@protocol MyCountable1 +@end + +@interface NSArray3 : Super1 +@end + +@implementation NSArray3 +@end + +// Test4 +@interface I +@property int d1; +@end + +@interface I(CAT) +@property int d1; +@end + +@implementation I(CAT) +@end + +// Test5 +@interface C @end + +@interface C (CAT) +- (int) p; +@end + + +@interface C (Category) +@property (readonly) int p; // no warning for this property - a getter is declared in another category +@property (readonly) int p1; // expected-note {{property declared here}} +@property (readonly) int p2; // no warning for this property - a getter is declared in this category +- (int) p2; +@end + +@implementation C (Category) // expected-warning {{property 'p1' requires method 'p1' to be defined - use @dynamic or provide a method implementation in this category}} +@end + +// Test6 +@protocol MyProtocol +@property (readonly) float anotherFloat; // expected-note {{property declared here}} +@property (readonly) float Float; // no warning for this property - a getter is declared in this protocol +- (float) Float; +@end + +@interface MyObject +{ float anotherFloat; } +@end + +@interface MyObject (CAT) +@end + +@implementation MyObject (CAT) // expected-warning {{property 'anotherFloat' requires method 'anotherFloat' to be defined - use @dynamic or provide a method implementation in this category}} +@end + diff --git a/test/SemaObjC/property-category-impl.m b/test/SemaObjC/property-category-impl.m index be42deaf909..135b0057618 100644 --- a/test/SemaObjC/property-category-impl.m +++ b/test/SemaObjC/property-category-impl.m @@ -1,4 +1,5 @@ // RUN: %clang_cc1 -fsyntax-only -verify -Wno-objc-root-class %s +// expected-no-diagnostics /* This test is for categories which don't implement the accessors but some accessors are implemented in their base class implementation. In this case,no warning must be issued. @@ -24,10 +25,10 @@ @end @interface MyClass (public) -@property(readwrite) int foo; // expected-note {{property declared here}} +@property(readwrite) int foo; @end -@implementation MyClass (public)// expected-warning {{property 'foo' requires method 'setFoo:' to be defined }} +@implementation MyClass (public) @end // rdar://12568064 diff --git a/test/SemaObjC/property-deprecated-warning.m b/test/SemaObjC/property-deprecated-warning.m index aa7b764fab1..7e10356ac57 100644 --- a/test/SemaObjC/property-deprecated-warning.m +++ b/test/SemaObjC/property-deprecated-warning.m @@ -5,37 +5,51 @@ typedef signed char BOOL; @protocol P -@property(nonatomic,assign) id ptarget __attribute__((availability(ios,introduced=2.0,deprecated=3.0))); // expected-note 2 {{property 'ptarget' is declared deprecated here}} +@property(nonatomic,assign) id ptarget __attribute__((availability(ios,introduced=2.0,deprecated=3.0))); // expected-note {{property 'ptarget' is declared deprecated here}} expected-note {{method 'ptarget' declared here}} @end @protocol P1

-- (void)setPtarget:(id)arg; // expected-note {{method 'setPtarget:' declared here}} +- (void)setPtarget:(id)arg; @end @interface UITableViewCell -@property(nonatomic,assign) id target __attribute__((availability(ios,introduced=2.0,deprecated=3.0))); // expected-note {{property 'target' is declared deprecated here}} +@property(nonatomic,assign) id target __attribute__((availability(ios,introduced=2.0,deprecated=3.0))); // expected-note {{property 'target' is declared deprecated here}} expected-note {{method 'setTarget:' declared here}} @end @interface PSTableCell : UITableViewCell - - (void)setTarget:(id)target; // expected-note {{method 'setTarget:' declared here}} + - (void)setTarget:(id)target; @end @interface UITableViewCell(UIDeprecated) -@property(nonatomic,assign) id dep_target __attribute__((availability(ios,introduced=2.0,deprecated=3.0))); // expected-note {{method 'dep_target' declared here}} \ - // expected-note 2 {{property 'dep_target' is declared deprecated here}} \ - // expected-note {{method 'setDep_target:' declared here}} +@property(nonatomic,assign) id dep_target __attribute__((availability(ios,introduced=2.0,deprecated=3.0))); // expected-note 2 {{method 'dep_target' declared here}} \ + // expected-note 4 {{property 'dep_target' is declared deprecated here}} \ + // expected-note 2 {{method 'setDep_target:' declared here}} @end @implementation PSTableCell - (void)setTarget:(id)target {}; - (void)setPtarget:(id)val {}; +- (void) Meth { + [self setTarget: (id)0]; // no-warning + [self setDep_target: [self dep_target]]; // expected-warning {{'dep_target' is deprecated: first deprecated in iOS 3.0}} \ + // expected-warning {{'setDep_target:' is deprecated: first deprecated in iOS 3.0}} + + [self setPtarget: (id)0]; // no-warning +} +@end + +@implementation UITableViewCell +@synthesize target; +@synthesize ptarget; +- (void)setPtarget:(id)val {}; +- (void)setTarget:(id)target {}; - (void) Meth { [self setTarget: (id)0]; // expected-warning {{'setTarget:' is deprecated: first deprecated in iOS 3.0}} [self setDep_target: [self dep_target]]; // expected-warning {{'dep_target' is deprecated: first deprecated in iOS 3.0}} \ // expected-warning {{'setDep_target:' is deprecated: first deprecated in iOS 3.0}} - [self setPtarget: (id)0]; // expected-warning {{setPtarget:' is deprecated: first deprecated in iOS 3.0}} + [self setPtarget: (id)0]; // no-warning } @end @@ -56,9 +70,11 @@ void testCustomAccessorNames(CustomAccessorNames *obj) { @end @interface ProtocolInCategory (TheCategory) -- (id)ptarget; // expected-note {{method 'ptarget' declared here}} +- (id)ptarget; @end -id useDeprecatedProperty(ProtocolInCategory *obj) { - return [obj ptarget]; // expected-warning {{'ptarget' is deprecated: first deprecated in iOS 3.0}} +id useDeprecatedProperty(ProtocolInCategory *obj, id

obj2, int flag) { + if (flag) + return [obj ptarget]; // no-warning + return [obj2 ptarget]; // expected-warning {{'ptarget' is deprecated: first deprecated in iOS 3.0}} } diff --git a/test/SemaObjC/property-noninherited-availability-attr.m b/test/SemaObjC/property-noninherited-availability-attr.m index 79cdd3e476f..0c2a5d38530 100644 --- a/test/SemaObjC/property-noninherited-availability-attr.m +++ b/test/SemaObjC/property-noninherited-availability-attr.m @@ -5,7 +5,8 @@ @interface NSObject @end @protocol myProtocol -@property int myProtocolProperty __attribute__((availability(macosx,introduced=10.7,deprecated=10.8))); +@property int myProtocolProperty __attribute__((availability(macosx,introduced=10.7,deprecated=10.8))); // expected-note {{method 'myProtocolProperty' declared here}} \ + // expected-note {{property 'myProtocolProperty' is declared deprecated here}} @end @interface Foo : NSObject @@ -15,18 +16,19 @@ @end @interface Bar : Foo -@property int myProperty; // expected-note {{'myProperty' declared here}} -@property int myProtocolProperty; // expected-note {{'myProtocolProperty' declared here}} +@property int myProperty; +@property int myProtocolProperty; @end -void test(Foo *y, Bar *x) { +void test(Foo *y, Bar *x, id z) { y.myProperty = 0; // expected-warning {{'myProperty' is deprecated: first deprecated in OS X 10.8}} [y myProperty]; // expected-warning {{'myProperty' is deprecated: first deprecated in OS X 10.8}} x.myProperty = 1; // no-warning - [x myProperty]; // expected-warning {{'myProperty' is deprecated: first deprecated in OS X 10.8}} + [x myProperty]; // no-warning x.myProtocolProperty = 0; // no-warning - [x myProtocolProperty]; // expected-warning {{'myProtocolProperty' is deprecated: first deprecated in OS X 10.8}} + [x myProtocolProperty]; // no-warning + [z myProtocolProperty]; // expected-warning {{'myProtocolProperty' is deprecated: first deprecated in OS X 10.8}} } diff --git a/test/SemaObjC/property-user-setter.m b/test/SemaObjC/property-user-setter.m index cda983c9ec0..e84fad2394a 100644 --- a/test/SemaObjC/property-user-setter.m +++ b/test/SemaObjC/property-user-setter.m @@ -85,7 +85,7 @@ static int g_val; - (void)setFoo:(int)value; @end -void g(int); // expected-note {{passing argument to parameter here}} +void g(int); void f(C *c) { c.Foo = 17; // OK diff --git a/test/SemaObjC/property.m b/test/SemaObjC/property.m index 76fdf5b242a..74854471738 100644 --- a/test/SemaObjC/property.m +++ b/test/SemaObjC/property.m @@ -11,7 +11,7 @@ @end @interface I(CAT) -@property int d1; // expected-note 2 {{property declared here}} +@property int d1; @end @implementation I @@ -22,8 +22,7 @@ @synthesize name; // OK! property with same name as an accessible ivar of same name @end -@implementation I(CAT) // expected-warning {{property 'd1' requires method 'd1' to be defined }} \ - // expected-warning {{property 'd1' requires method 'setD1:' to be defined }} +@implementation I(CAT) @synthesize d1; // expected-error {{@synthesize not allowed in a category's implementation}} @dynamic bad; // expected-error {{property implementation must have its declaration in the category 'CAT'}} @end diff --git a/test/SemaObjC/protocol-lookup-2.m b/test/SemaObjC/protocol-lookup-2.m index 9e8ed8a627b..90f6db0c84f 100644 --- a/test/SemaObjC/protocol-lookup-2.m +++ b/test/SemaObjC/protocol-lookup-2.m @@ -32,3 +32,26 @@ } @end + + +@protocol ProtC +-document; +@end + +@interface I1 : NSObject +@end + +@interface I1(cat) +-document; +@end + +@interface I2 : NSObject +-document; +@end + +@interface I2() +@end + +@implementation I2 +- document { return 0; } +@end diff --git a/test/SemaObjC/typo-correction.m b/test/SemaObjC/typo-correction.m index 3fd61e2ecea..893e31294ad 100644 --- a/test/SemaObjC/typo-correction.m +++ b/test/SemaObjC/typo-correction.m @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 %s -verify -fsyntax-only -fwarn-on-spellcheck +// RUN: %clang_cc1 %s -verify -fsyntax-only @interface B @property int x; @@ -8,8 +8,7 @@ @end // Spell-checking 'undefined' is ok. -undefined var; // expected-warning {{spell-checking initiated}} \ - // expected-error {{unknown type name}} +undefined var; // expected-error {{unknown type name}} typedef int super1; @implementation S diff --git a/test/SemaObjC/warn-missing-super.m b/test/SemaObjC/warn-missing-super.m index 02b81651d7a..e9769a9db17 100644 --- a/test/SemaObjC/warn-missing-super.m +++ b/test/SemaObjC/warn-missing-super.m @@ -54,5 +54,5 @@ __attribute__((objc_root_class)) // CHECK-GC-ONLY: 1 warning generated. // RUN: %clang_cc1 -fsyntax-only -triple x86_64-apple-darwin10 -fobjc-arc %s 2>&1 | FileCheck --check-prefix=CHECK-ARC %s -// CHECK-ARC: warn-missing-super.m:36:4: error: ARC forbids explicit message send of 'dealloc' +// CHECK-ARC: warn-missing-super.m:36:10: error: ARC forbids explicit message send of 'dealloc' // CHECK-ARC: 1 error generated. diff --git a/test/SemaObjCXX/arc-system-header.mm b/test/SemaObjCXX/arc-system-header.mm index 107b5b5a53d..3358e5fc127 100644 --- a/test/SemaObjCXX/arc-system-header.mm +++ b/test/SemaObjCXX/arc-system-header.mm @@ -6,5 +6,4 @@ void f(A* a) { a->data.void_ptr = 0; a->data.a_b.b = 0; // expected-error{{'a_b' is unavailable: this system field has retaining ownership}} } -// Silly location below -// expected-note{{declaration has been explicitly marked unavailable here}} +// expected-note@arc-system-header.h:10{{declaration has been explicitly marked unavailable here}} diff --git a/test/SemaObjCXX/foreach.mm b/test/SemaObjCXX/foreach.mm index 3c4b908eab9..d1302c19a58 100644 --- a/test/SemaObjCXX/foreach.mm +++ b/test/SemaObjCXX/foreach.mm @@ -12,8 +12,18 @@ void f(NSArray *a) { // expected-warning {{expression result unused}} for (id thisKey : keys); + + for (auto thisKey : keys) { } // expected-warning{{'auto' deduced as 'id' in declaration of 'thisKey'}} +} + +template +void ft(Collection col) { + for (id x : col) { } + for (auto x : col) { } } +template void ft(NSArray *); + /* // rdar://9072298 */ @protocol NSObject @end @@ -59,3 +69,9 @@ void test2(NSObject *collection) { // expected-warning {{property access result unused - getters should not be used for side effects}} } } + +void testErrors(NSArray *array) { + typedef int fn(int); + + for (fn x in array) { } // expected-error{{non-variable declaration in 'for' loop}} +} diff --git a/test/SemaObjCXX/property-reference.mm b/test/SemaObjCXX/property-reference.mm index b86ae5e9f51..cfac9f30dbd 100644 --- a/test/SemaObjCXX/property-reference.mm +++ b/test/SemaObjCXX/property-reference.mm @@ -57,3 +57,21 @@ template void f() { } template void f(); + +// rdar://13602832 +// +// Make sure that the default-argument checker looks through +// pseudo-object expressions correctly. The default argument +// needs to force l2r to test this effectively because the checker +// is syntactic and runs before placeholders are handled. +@interface Test13602832 +- (int) x; +@end +namespace test13602832 { + template void foo(Test13602832 *a, int limit = a.x + N) {} // expected-error {{default argument references parameter 'a'}} + + void test(Test13602832 *a) { + // FIXME: this is a useless cascade error. + foo<1024>(a); // expected-error {{no matching function}} + } +} diff --git a/test/SemaObjCXX/references.mm b/test/SemaObjCXX/references.mm index f63e17d98ef..fa552076fb9 100644 --- a/test/SemaObjCXX/references.mm +++ b/test/SemaObjCXX/references.mm @@ -1,5 +1,7 @@ -// RUN: %clang_cc1 -verify -emit-llvm -o - %s -// expected-no-diagnostics +// RUN: %clang_cc1 -verify -o - %s + +__attribute__((objc_root_class)) +@interface Root @end // Test reference binding. @@ -8,7 +10,7 @@ typedef struct { int f1; } T; -@interface A +@interface A : Root @property (assign) T p0; @property (assign) T& p1; @end @@ -61,3 +63,14 @@ void f6(baz* x) { f5d(ToBar()); (void)((foo&)ToBar()); } + +// rdar://13794269 +@interface B : Root @end +@implementation B { + unsigned bf : 4; // expected-note {{declared here}} +} + +- (void) foo { + unsigned &i = bf; // expected-error {{non-const reference cannot bind to bit-field 'bf'}} +} +@end diff --git a/test/SemaOpenCL/event_t.cl b/test/SemaOpenCL/event_t.cl index 57a0981cf13..06197d0c179 100644 --- a/test/SemaOpenCL/event_t.cl +++ b/test/SemaOpenCL/event_t.cl @@ -2,7 +2,7 @@ event_t glb_evt; // expected-error {{the event_t type cannot be used to declare a program scope variable}} -struct evt_s { +constant struct evt_s { event_t evt; // expected-error {{the event_t type cannot be used to declare a structure or union field}} } evt_str; diff --git a/test/SemaOpenCL/storageclass.cl b/test/SemaOpenCL/storageclass.cl index fdfe1346218..d2678f2010d 100644 --- a/test/SemaOpenCL/storageclass.cl +++ b/test/SemaOpenCL/storageclass.cl @@ -2,6 +2,8 @@ static constant int A = 0; +int X = 0; // expected-error{{global variables must have a constant address space qualifier}} + // static is not allowed at local scope. void kernel foo() { static int X = 5; // expected-error{{variables in function scope cannot be declared static}} diff --git a/test/SemaTemplate/attributes.cpp b/test/SemaTemplate/attributes.cpp index 495f4a7ad38..5a06a706aa8 100644 --- a/test/SemaTemplate/attributes.cpp +++ b/test/SemaTemplate/attributes.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -fsyntax-only -verify %s +// RUN: %clang_cc1 -std=gnu++11 -fsyntax-only -verify %s namespace attribute_aligned { template @@ -18,6 +18,26 @@ namespace attribute_aligned { check_alignment<2>::t c2; check_alignment<3>::t c3; // expected-note 2 {{in instantiation}} check_alignment<4>::t c4; + + template + class my_aligned_storage + { + __attribute__((align(Align))) char storage[Size]; + }; + + template + class C { + public: + C() { + static_assert(sizeof(t) == sizeof(T), "my_aligned_storage size wrong"); + static_assert(alignof(t) == alignof(T), "my_aligned_storage align wrong"); // expected-warning{{'alignof' applied to an expression is a GNU extension}} + } + + private: + my_aligned_storage t; + }; + + C cd; } namespace PR9049 { diff --git a/test/SemaTemplate/dependent-names.cpp b/test/SemaTemplate/dependent-names.cpp index eb75e69ef4d..fa47ef53581 100644 --- a/test/SemaTemplate/dependent-names.cpp +++ b/test/SemaTemplate/dependent-names.cpp @@ -264,7 +264,7 @@ namespace PR10053 { } namespace PR10187 { - namespace A { + namespace A1 { template struct S { void f() { @@ -278,6 +278,25 @@ namespace PR10187 { } } + namespace A2 { + template + struct S { + void f() { + for (auto &a : e) + __range(a); // expected-error {{undeclared identifier '__range'}} + } + T e[10]; + }; + void g() { + S().f(); // expected-note {{here}} + } + struct X {}; + void __range(X); + void h() { + S().f(); + } + } + namespace B { template void g(); // expected-note {{not viable}} template void f() { diff --git a/test/SemaTemplate/example-dynarray.cpp b/test/SemaTemplate/example-dynarray.cpp deleted file mode 100644 index 266d2d4998e..00000000000 --- a/test/SemaTemplate/example-dynarray.cpp +++ /dev/null @@ -1,178 +0,0 @@ -// RUN: %clangxx -emit-llvm -c -o - %s -// XFAIL: hexagon -#include -#include -#include - -// Placement new requires to be included, but we don't support that yet. -void* operator new(size_t, void* ptr) throw() { - return ptr; -} -void operator delete(void*, void*) throw() { -} - -template -class dynarray { -public: - dynarray() { Start = Last = End = 0; } - - dynarray(const dynarray &other) { - Start = (T*)malloc(sizeof(T) * other.size()); - Last = End = Start + other.size(); - - for (unsigned I = 0, N = other.size(); I != N; ++I) - new (Start + I) T(other[I]); - } - - ~dynarray() { - for (unsigned I = 0, N = size(); I != N; ++I) - Start[I].~T(); - - free(Start); - } - - dynarray &operator=(const dynarray &other) { - T* NewStart = (T*)malloc(sizeof(T) * other.size()); - - for (unsigned I = 0, N = other.size(); I != N; ++I) - new (NewStart + I) T(other[I]); - - for (unsigned I = 0, N = size(); I != N; ++I) - Start[I].~T(); - - free(Start); - Start = NewStart; - Last = End = NewStart + other.size(); - return *this; - } - - unsigned size() const { return Last - Start; } - unsigned capacity() const { return End - Start; } - - void push_back(const T& value); - - void pop_back() { - --Last; - Last->~T(); - } - - T& operator[](unsigned Idx) { - return Start[Idx]; - } - - const T& operator[](unsigned Idx) const { - return Start[Idx]; - } - - typedef T* iterator; - typedef const T* const_iterator; - - iterator begin() { return Start; } - const_iterator begin() const { return Start; } - - iterator end() { return Last; } - const_iterator end() const { return Last; } - - bool operator==(const dynarray &other) const { - if (size() != other.size()) - return false; - - for (unsigned I = 0, N = size(); I != N; ++I) - if ((*this)[I] != other[I]) - return false; - - return true; - } - - bool operator!=(const dynarray &other) const { - return !(*this == other); - } - -public: - T* Start, *Last, *End; -}; - -template -void dynarray::push_back(const T& value) { - if (Last == End) { - unsigned NewCapacity = capacity() * 2; - if (NewCapacity == 0) - NewCapacity = 4; - - T* NewStart = (T*)malloc(sizeof(T) * NewCapacity); - - unsigned Size = size(); - for (unsigned I = 0; I != Size; ++I) - new (NewStart + I) T(Start[I]); - - for (unsigned I = 0, N = size(); I != N; ++I) - Start[I].~T(); - free(Start); - - Start = NewStart; - Last = Start + Size; - End = Start + NewCapacity; - } - - new (Last) T(value); - ++Last; -} - -struct Point { - Point() { x = y = z = 0.0; } - Point(const Point& other) : x(other.x), y(other.y), z(other.z) { } - - float x, y, z; -}; - -int main() { - dynarray di; - di.push_back(0); - di.push_back(1); - di.push_back(2); - di.push_back(3); - di.push_back(4); - assert(di.size() == 5); - for (dynarray::iterator I = di.begin(), IEnd = di.end(); I != IEnd; ++I) - assert(*I == I - di.begin()); - - for (int I = 0, N = di.size(); I != N; ++I) - assert(di[I] == I); - - di.pop_back(); - assert(di.size() == 4); - di.push_back(4); - - dynarray di2 = di; - assert(di2.size() == 5); - assert(di.begin() != di2.begin()); - for (dynarray::iterator I = di2.begin(), IEnd = di2.end(); - I != IEnd; ++I) - assert(*I == I - di2.begin()); - - dynarray di3(di); - assert(di3.size() == 5); - assert(di.begin() != di3.begin()); - for (dynarray::iterator I = di3.begin(), IEnd = di3.end(); - I != IEnd; ++I) - assert(*I == I - di3.begin()); - - dynarray di4; - assert(di4.size() == 0); - di4 = di; - assert(di4.size() == 5); - assert(di.begin() != di4.begin()); - for (dynarray::iterator I = di4.begin(), IEnd = di4.end(); - I != IEnd; ++I) - assert(*I == I - di4.begin()); - - assert(di4 == di); - di4[3] = 17; - assert(di4 != di); - - dynarray dp; - dp.push_back(Point()); - assert(dp.size() == 1); - - return 0; -} diff --git a/test/SemaTemplate/local-member-templates.cpp b/test/SemaTemplate/local-member-templates.cpp new file mode 100644 index 00000000000..3cdf5df8d14 --- /dev/null +++ b/test/SemaTemplate/local-member-templates.cpp @@ -0,0 +1,76 @@ +// RUN: %clang_cc1 -std=c++1y -verify %s +// RUN: %clang_cc1 -std=c++1y -verify %s -fdelayed-template-parsing + +namespace nested_local_templates_1 { + +template struct Outer { + template int outer_mem(T t, U u) { + struct Inner { + template int inner_mem(T t, U u, V v) { + struct InnerInner { + template int inner_inner_mem(W w, T t, U u, V v) { + return 0; + } + }; + InnerInner().inner_inner_mem("abc", t, u, v); + return 0; + } + }; + Inner i; + i.inner_mem(t, u, 3.14); + return 0; + } + + template int outer_mem(T t, U *u); +}; + +template int Outer::outer_mem(int, char); + +template template int Outer::outer_mem(T t, U *u) { + struct Inner { + template + int inner_mem(T t, U u, V v) { //expected-note{{candidate function}} + struct InnerInner { + template int inner_inner_mem(W w, T t, U u, V v) { return 0; } + }; + InnerInner().inner_inner_mem("abc", t, u, v); + return 0; + } + }; + Inner i; + i.inner_mem(t, U{}, i); + i.inner_mem(t, u, 3.14); //expected-error{{no matching member function for call to 'inner}} + return 0; +} + +template int Outer::outer_mem(int, char *); //expected-note{{in instantiation of function}} + +} // end ns + +namespace nested_local_templates_2 { + +template struct Outer { + template void outer_mem(T t, U u) { + struct Inner { + template struct InnerTemplateClass { + template + void itc_mem(T t, U u, V v, W w) { //expected-note{{candidate function}} + struct InnerInnerInner { + template void iii_mem(X x) {} + }; + InnerInnerInner i; + i.iii_mem("abc"); + } + }; + }; + Inner i; + typename Inner::template InnerTemplateClass ii; + ii.itc_mem(t, u, i, "jim"); + ii.itc_mem(t, u, 0, "abd"); //expected-error{{no matching member function}} + } +}; + +template void +Outer::outer_mem(int, char); //expected-note{{in instantiation of}} + +} diff --git a/test/SemaTemplate/ms-function-specialization-class-scope.cpp b/test/SemaTemplate/ms-function-specialization-class-scope.cpp index 131922bbfba..9efb02ce5f5 100644 --- a/test/SemaTemplate/ms-function-specialization-class-scope.cpp +++ b/test/SemaTemplate/ms-function-specialization-class-scope.cpp @@ -1,4 +1,5 @@ // RUN: %clang_cc1 -fms-extensions -fsyntax-only -verify %s +// RUN: %clang_cc1 -fms-extensions -fdelayed-template-parsing -fsyntax-only -verify %s class A { diff --git a/test/SemaTemplate/ms-lookup-template-base-classes.cpp b/test/SemaTemplate/ms-lookup-template-base-classes.cpp index 8f80cb59e8a..cb1a7f50b71 100644 --- a/test/SemaTemplate/ms-lookup-template-base-classes.cpp +++ b/test/SemaTemplate/ms-lookup-template-base-classes.cpp @@ -8,7 +8,6 @@ class A { void g();// expected-note {{must qualify identifier to find this declaration in dependent base class}} }; - template class B : public A { public: @@ -28,6 +27,31 @@ void test() b.z(3); } +struct A2 { + template void f(T) { + XX; //expected-error {{use of undeclared identifier 'XX'}} + A2::XX; //expected-error {{no member named 'XX' in 'A2'}} + } +}; +template void A2::f(int); + +template +struct A3 { + template void f(T1) { + XX; //expected-error {{use of undeclared identifier 'XX'}} + } +}; +template void A3::f(int); + +template +struct A4 { + void f(char) { + XX; //expected-error {{use of undeclared identifier 'XX'}} + } +}; +template class A4; + + namespace lookup_dependent_bases_id_expr { template class A { diff --git a/test/SemaTemplate/overload-candidates.cpp b/test/SemaTemplate/overload-candidates.cpp index dc6d2a51ec2..ad65397865a 100644 --- a/test/SemaTemplate/overload-candidates.cpp +++ b/test/SemaTemplate/overload-candidates.cpp @@ -62,3 +62,20 @@ template struct NonTemplateFunction { typename boost::enable_if::type f(); // expected-error{{no type named 'type' in 'boost::enable_if'; 'enable_if' cannot be used to disable this declaration}} }; NonTemplateFunction NTFC; // expected-note{{here}} + +namespace NS1 { + template + class array {}; +} + +namespace NS2 { + template + class array {}; +} + +template +void foo(NS2::array); // expected-note{{candidate template ignored: could not match 'NS2::array' against 'NS1::array'}} + +void test() { + foo(NS1::array()); // expected-error{{no matching function for call to 'foo'}} +} diff --git a/test/SemaTemplate/temp_arg_nontype.cpp b/test/SemaTemplate/temp_arg_nontype.cpp index 210b5e463f6..24509524b29 100644 --- a/test/SemaTemplate/temp_arg_nontype.cpp +++ b/test/SemaTemplate/temp_arg_nontype.cpp @@ -337,3 +337,12 @@ namespace rdar13000548 { } } + +namespace rdar13806270 { + template class X { }; + const unsigned value = 32; + struct Y { + X x; + }; + void foo() {} +} diff --git a/test/Tooling/clang-check-args.cpp b/test/Tooling/clang-check-args.cpp index 9ba5d45f508..66950ae1039 100644 --- a/test/Tooling/clang-check-args.cpp +++ b/test/Tooling/clang-check-args.cpp @@ -2,6 +2,3 @@ // CHECK: C++ requires invalid; - -// FIXME: This is incompatible to -fms-compatibility. -// XFAIL: win32 diff --git a/test/Tooling/clang-check-builtin-headers.cpp b/test/Tooling/clang-check-builtin-headers.cpp index 504d197ea9a..ed2bea0d656 100644 --- a/test/Tooling/clang-check-builtin-headers.cpp +++ b/test/Tooling/clang-check-builtin-headers.cpp @@ -10,6 +10,3 @@ // CHECK: C++ requires invalid; - -// FIXME: This is incompatible to -fms-compatibility. -// XFAIL: win32 diff --git a/test/Tooling/clang-check-chdir.cpp b/test/Tooling/clang-check-chdir.cpp index 29b5abb4c94..c8113233fa2 100644 --- a/test/Tooling/clang-check-chdir.cpp +++ b/test/Tooling/clang-check-chdir.cpp @@ -12,6 +12,3 @@ // CHECK: C++ requires invalid; - -// FIXME: This is incompatible to -fms-compatibility. -// XFAIL: win32 diff --git a/test/Tooling/clang-check.cpp b/test/Tooling/clang-check.cpp index 91ab01b01be..56835b3bf98 100644 --- a/test/Tooling/clang-check.cpp +++ b/test/Tooling/clang-check.cpp @@ -7,6 +7,3 @@ // CHECK: C++ requires invalid; - -// FIXME: This is incompatible to -fms-compatibility. -// XFAIL: win32 diff --git a/test/Tooling/multi-jobs.cpp b/test/Tooling/multi-jobs.cpp index a3eb7039c04..1e6bce113fd 100644 --- a/test/Tooling/multi-jobs.cpp +++ b/test/Tooling/multi-jobs.cpp @@ -2,6 +2,3 @@ // CHECK: C++ requires invalid; - -// FIXME: This is incompatible to -fms-compatibility. -// XFAIL: win32 diff --git a/test/lit.cfg b/test/lit.cfg index 4466f0fc074..6b0ad59c9f1 100644 --- a/test/lit.cfg +++ b/test/lit.cfg @@ -24,12 +24,21 @@ if platform.system() == 'Windows': config.environment['PATH'])) config.environment['PATH'] = path +# Choose between lit's internal shell pipeline runner and a real shell. If +# LIT_USE_INTERNAL_SHELL is in the environment, we use that as an override. +use_lit_shell = os.environ.get("LIT_USE_INTERNAL_SHELL") +if use_lit_shell: + # 0 is external, "" is default, and everything else is internal. + execute_external = (use_lit_shell == "0") +else: + # Otherwise we default to internal on Windows and external elsewhere, as + # bash on Windows is usually very slow. + execute_external = (not sys.platform in ['win32']) + # testFormat: The test format to use to interpret tests. # # For now we require '&&' between commands, until they get globally killed and # the test runner updated. -execute_external = (platform.system() != 'Windows' - or lit.getBashPath() not in [None, ""]) config.test_format = lit.formats.ShTest(execute_external) # suffixes: A list of file extensions to treat as test files. @@ -219,17 +228,13 @@ if platform.system() not in ['FreeBSD']: config.available_features.add('crash-recovery') # Shell execution -if platform.system() not in ['Windows'] or lit.getBashPath() != '': +if execute_external: config.available_features.add('shell') # Exclude MSYS due to transforming '/' to 'X:/mingwroot/'. if not platform.system() in ['Windows'] or lit.getBashPath() == '': config.available_features.add('shell-preserves-root') -# For tests that require Darwin to run. -if platform.system() in ['Darwin']: - config.available_features.add('system-darwin') - # ANSI escape sequences in non-dumb terminal if platform.system() not in ['Windows']: config.available_features.add('ansi-escape-sequences') diff --git a/tools/c-index-test/c-index-test.c b/tools/c-index-test/c-index-test.c index 88b49edaaf3..e5752341679 100644 --- a/tools/c-index-test/c-index-test.c +++ b/tools/c-index-test/c-index-test.c @@ -694,6 +694,9 @@ static void PrintCursor(CXCursor Cursor, printf(" (static)"); if (clang_CXXMethod_isVirtual(Cursor)) printf(" (virtual)"); + + if (clang_Cursor_isVariadic(Cursor)) + printf(" (variadic)"); if (Cursor.kind == CXCursor_IBOutletCollectionAttr) { CXType T = @@ -787,6 +790,44 @@ static void PrintCursor(CXCursor Cursor, } PrintCursorComments(Cursor, ValidationData); + + { + unsigned PropAttrs = clang_Cursor_getObjCPropertyAttributes(Cursor, 0); + if (PropAttrs != CXObjCPropertyAttr_noattr) { + printf(" ["); + #define PRINT_PROP_ATTR(A) \ + if (PropAttrs & CXObjCPropertyAttr_##A) printf(#A ",") + PRINT_PROP_ATTR(readonly); + PRINT_PROP_ATTR(getter); + PRINT_PROP_ATTR(assign); + PRINT_PROP_ATTR(readwrite); + PRINT_PROP_ATTR(retain); + PRINT_PROP_ATTR(copy); + PRINT_PROP_ATTR(nonatomic); + PRINT_PROP_ATTR(setter); + PRINT_PROP_ATTR(atomic); + PRINT_PROP_ATTR(weak); + PRINT_PROP_ATTR(strong); + PRINT_PROP_ATTR(unsafe_unretained); + printf("]"); + } + } + + { + unsigned QT = clang_Cursor_getObjCDeclQualifiers(Cursor); + if (QT != CXObjCDeclQualifier_None) { + printf(" ["); + #define PRINT_OBJC_QUAL(A) \ + if (QT & CXObjCDeclQualifier_##A) printf(#A ",") + PRINT_OBJC_QUAL(In); + PRINT_OBJC_QUAL(Inout); + PRINT_OBJC_QUAL(Out); + PRINT_OBJC_QUAL(Bycopy); + PRINT_OBJC_QUAL(Byref); + PRINT_OBJC_QUAL(Oneway); + printf("]"); + } + } } } @@ -934,6 +975,23 @@ enum CXChildVisitResult FilteredPrintingVisitor(CXCursor Cursor, GetCursorSource(Cursor), line, column); PrintCursor(Cursor, &Data->ValidationData); PrintCursorExtent(Cursor); + if (clang_isDeclaration(Cursor.kind)) { + enum CX_CXXAccessSpecifier access = clang_getCXXAccessSpecifier(Cursor); + const char *accessStr = 0; + + switch (access) { + case CX_CXXInvalidAccessSpecifier: break; + case CX_CXXPublic: + accessStr = "public"; break; + case CX_CXXProtected: + accessStr = "protected"; break; + case CX_CXXPrivate: + accessStr = "private"; break; + } + + if (accessStr) + printf(" [access=%s]", accessStr); + } printf("\n"); return CXChildVisit_Recurse; } @@ -1144,6 +1202,61 @@ static enum CXChildVisitResult PrintType(CXCursor cursor, CXCursor p, return CXChildVisit_Recurse; } +static enum CXChildVisitResult PrintTypeSize(CXCursor cursor, CXCursor p, + CXClientData d) { + CXType T; + enum CXCursorKind K = clang_getCursorKind(cursor); + if (clang_isInvalid(K)) + return CXChildVisit_Recurse; + T = clang_getCursorType(cursor); + PrintCursor(cursor, NULL); + PrintTypeAndTypeKind(T, " [type=%s] [typekind=%s]"); + /* Print the type sizeof if applicable. */ + { + long long Size = clang_Type_getSizeOf(T); + if (Size >= 0 || Size < -1 ) { + printf(" [sizeof=%lld]", Size); + } + } + /* Print the type alignof if applicable. */ + { + long long Align = clang_Type_getAlignOf(T); + if (Align >= 0 || Align < -1) { + printf(" [alignof=%lld]", Align); + } + } + /* Print the record field offset if applicable. */ + { + const char *FieldName = clang_getCString(clang_getCursorSpelling(cursor)); + /* recurse to get the root anonymous record parent */ + CXCursor Parent, Root; + if (clang_getCursorKind(cursor) == CXCursor_FieldDecl ) { + const char *RootParentName; + Root = Parent = p; + do { + Root = Parent; + RootParentName = clang_getCString(clang_getCursorSpelling(Root)); + Parent = clang_getCursorSemanticParent(Root); + } while ( clang_getCursorType(Parent).kind == CXType_Record && + !strcmp(RootParentName, "") ); + /* if RootParentName is "", record is anonymous. */ + { + long long Offset = clang_Type_getOffsetOf(clang_getCursorType(Root), + FieldName); + printf(" [offsetof=%lld]", Offset); + } + } + } + /* Print if its a bitfield */ + { + int IsBitfield = clang_Cursor_isBitField(cursor); + if (IsBitfield) + printf(" [BitFieldSize=%d]", clang_getFieldDeclBitWidth(cursor)); + } + printf("\n"); + return CXChildVisit_Recurse; +} + /******************************************************************************/ /* Bitwidth testing. */ /******************************************************************************/ @@ -1256,7 +1369,7 @@ int perform_test_load_source(int argc, const char **argv, Idx = clang_createIndex(/* excludeDeclsFromPCH */ (!strcmp(filter, "local") || !strcmp(filter, "local-display"))? 1 : 0, - /* displayDiagnostics=*/0); + /* displayDiagnostics=*/1); if ((CommentSchemaFile = parse_comments_schema(argc, argv))) { argc--; @@ -1301,7 +1414,7 @@ int perform_test_reparse_source(int argc, const char **argv, int trials, Idx = clang_createIndex(/* excludeDeclsFromPCH */ !strcmp(filter, "local") ? 1 : 0, - /* displayDiagnostics=*/0); + /* displayDiagnostics=*/1); if (parse_remapped_files(argc, argv, 0, &unsaved_files, &num_unsaved_files)) { clang_disposeIndex(Idx); @@ -1992,14 +2105,19 @@ static int inspect_cursor_at(int argc, const char **argv) { { CXModule mod = clang_Cursor_getModule(Cursor); - CXString name; + CXFile astFile; + CXString name, astFilename; unsigned i, numHeaders; if (mod) { + astFile = clang_Module_getASTFile(mod); + astFilename = clang_getFileName(astFile); name = clang_Module_getFullName(mod); numHeaders = clang_Module_getNumTopLevelHeaders(TU, mod); - printf(" ModuleName=%s Headers(%d):", - clang_getCString(name), numHeaders); + printf(" ModuleName=%s (%s) Headers(%d):", + clang_getCString(name), clang_getCString(astFilename), + numHeaders); clang_disposeString(name); + clang_disposeString(astFilename); for (i = 0; i < numHeaders; ++i) { CXFile file = clang_Module_getTopLevelHeader(TU, mod, i); CXString filename = clang_getFileName(file); @@ -3395,6 +3513,7 @@ int write_pch_file(const char *filename, int argc, const char *argv[]) { unsaved_files, num_unsaved_files, CXTranslationUnit_Incomplete | + CXTranslationUnit_DetailedPreprocessingRecord| CXTranslationUnit_ForSerialization); if (!TU) { fprintf(stderr, "Unable to load translation unit!\n"); @@ -3642,6 +3761,7 @@ static void print_usage(void) { fprintf(stderr, " c-index-test -test-print-linkage-source {}*\n" " c-index-test -test-print-type {}*\n" + " c-index-test -test-print-type-size {}*\n" " c-index-test -test-print-bitwidth {}*\n" " c-index-test -print-usr [ {}]*\n" " c-index-test -print-usr-file \n" @@ -3728,6 +3848,9 @@ int cindextest_main(int argc, const char **argv) { else if (argc > 2 && strcmp(argv[1], "-test-print-type") == 0) return perform_test_load_source(argc - 2, argv + 2, "all", PrintType, 0); + else if (argc > 2 && strcmp(argv[1], "-test-print-type-size") == 0) + return perform_test_load_source(argc - 2, argv + 2, "all", + PrintTypeSize, 0); else if (argc > 2 && strcmp(argv[1], "-test-print-bitwidth") == 0) return perform_test_load_source(argc - 2, argv + 2, "all", PrintBitWidth, 0); diff --git a/tools/clang-format/ClangFormat.cpp b/tools/clang-format/ClangFormat.cpp index c4969b2c086..57833edf0f3 100644 --- a/tools/clang-format/ClangFormat.cpp +++ b/tools/clang-format/ClangFormat.cpp @@ -27,22 +27,25 @@ using namespace llvm; static cl::opt Help("h", cl::desc("Alias for -help"), cl::Hidden); -static cl::list Offsets( - "offset", cl::desc("Format a range starting at this file offset.")); -static cl::list Lengths( - "length", cl::desc("Format a range of this length, -1 for end of file.")); +static cl::list +Offsets("offset", cl::desc("Format a range starting at this file offset. Can " + "only be used with one input file.")); +static cl::list +Lengths("length", cl::desc("Format a range of this length. " + "When it's not specified, end of file is used. " + "Can only be used with one input file.")); static cl::opt Style( "style", - cl::desc("Coding style, currently supports: LLVM, Google, Chromium."), + cl::desc("Coding style, currently supports: LLVM, Google, Chromium, Mozilla."), cl::init("LLVM")); static cl::opt Inplace("i", - cl::desc("Inplace edit , if specified.")); + cl::desc("Inplace edit s, if specified.")); static cl::opt OutputXML( "output-replacements-xml", cl::desc("Output replacements as XML.")); -static cl::opt FileName(cl::Positional, cl::desc("[]"), - cl::init("-")); +static cl::list FileNames(cl::Positional, + cl::desc("[ ...]")); namespace clang { namespace format { @@ -60,12 +63,18 @@ static FormatStyle getStyle() { FormatStyle TheStyle = getGoogleStyle(); if (Style == "LLVM") TheStyle = getLLVMStyle(); - if (Style == "Chromium") + else if (Style == "Chromium") TheStyle = getChromiumStyle(); + else if (Style == "Mozilla") + TheStyle = getMozillaStyle(); + else if (Style != "Google") + llvm::errs() << "Unknown style " << Style << ", using Google style.\n"; + return TheStyle; } -static void format() { +// Returns true on error. +static bool format(std::string FileName) { FileManager Files((FileSystemOptions())); DiagnosticsEngine Diagnostics( IntrusiveRefCntPtr(new DiagnosticIDs), @@ -74,7 +83,7 @@ static void format() { OwningPtr Code; if (error_code ec = MemoryBuffer::getFileOrSTDIN(FileName, Code)) { llvm::errs() << ec.message() << "\n"; - return; + return true; } FileID ID = createInMemoryFile(FileName, Code.get(), Sources, Files); Lexer Lex(ID, Sources.getBuffer(ID), Sources, getFormattingLangOpts()); @@ -82,15 +91,27 @@ static void format() { Offsets.push_back(0); if (Offsets.size() != Lengths.size() && !(Offsets.size() == 1 && Lengths.empty())) { - llvm::errs() << "Number of -offset and -length arguments must match.\n"; - return; + llvm::errs() + << "error: number of -offset and -length arguments must match.\n"; + return true; } std::vector Ranges; - for (cl::list::size_type i = 0, e = Offsets.size(); i != e; ++i) { + for (unsigned i = 0, e = Offsets.size(); i != e; ++i) { + if (Offsets[i] >= Code->getBufferSize()) { + llvm::errs() << "error: offset " << Offsets[i] + << " is outside the file\n"; + return true; + } SourceLocation Start = Sources.getLocForStartOfFile(ID).getLocWithOffset(Offsets[i]); SourceLocation End; if (i < Lengths.size()) { + if (Offsets[i] + Lengths[i] > Code->getBufferSize()) { + llvm::errs() << "error: invalid length " << Lengths[i] + << ", offset + length (" << Offsets[i] + Lengths[i] + << ") is outside the file.\n"; + return true; + } End = Start.getLocWithOffset(Lengths[i]); } else { End = Sources.getLocForEndOfFile(ID); @@ -99,7 +120,8 @@ static void format() { } tooling::Replacements Replaces = reformat(getStyle(), Lex, Sources, Ranges); if (OutputXML) { - llvm::outs() << "\n\n"; + llvm::outs() + << "\n\n"; for (tooling::Replacements::const_iterator I = Replaces.begin(), E = Replaces.end(); I != E; ++I) { @@ -114,14 +136,14 @@ static void format() { tooling::applyAllReplacements(Replaces, Rewrite); if (Inplace) { if (Replaces.size() == 0) - return; // Nothing changed, don't touch the file. + return false; // Nothing changed, don't touch the file. std::string ErrorInfo; llvm::raw_fd_ostream FileStream(FileName.c_str(), ErrorInfo, llvm::raw_fd_ostream::F_Binary); if (!ErrorInfo.empty()) { llvm::errs() << "Error while writing file: " << ErrorInfo << "\n"; - return; + return true; } Rewrite.getEditBuffer(ID).write(FileStream); FileStream.flush(); @@ -129,6 +151,7 @@ static void format() { Rewrite.getEditBuffer(ID).write(outs()); } } + return false; } } // namespace format @@ -139,14 +162,32 @@ int main(int argc, const char **argv) { cl::ParseCommandLineOptions( argc, argv, "A tool to format C/C++/Obj-C code.\n\n" - "Currently supports LLVM and Google style guides.\n" "If no arguments are specified, it formats the code from standard input\n" "and writes the result to the standard output.\n" - "If is given, it reformats the file. If -i is specified together\n" - "with , the file is edited in-place. Otherwise, the result is\n" - "written to the standard output.\n"); + "If s are given, it reformats the files. If -i is specified \n" + "together with s, the files are edited in-place. Otherwise, the \n" + "result is written to the standard output.\n"); + if (Help) cl::PrintHelpMessage(); - clang::format::format(); - return 0; + + bool Error = false; + switch (FileNames.size()) { + case 0: + Error = clang::format::format("-"); + break; + case 1: + Error = clang::format::format(FileNames[0]); + break; + default: + if (!Offsets.empty() || !Lengths.empty()) { + llvm::errs() << "error: \"-offset\" and \"-length\" can only be used for " + "single file.\n"; + return 1; + } + for (unsigned i = 0; i < FileNames.size(); ++i) + Error |= clang::format::format(FileNames[i]); + break; + } + return Error ? 1 : 0; } diff --git a/tools/clang-format/clang-format-bbedit.applescript b/tools/clang-format/clang-format-bbedit.applescript new file mode 100644 index 00000000000..fa88fe90048 --- /dev/null +++ b/tools/clang-format/clang-format-bbedit.applescript @@ -0,0 +1,27 @@ +-- In this file, change "/path/to/" to the path where you installed clang-format +-- and save it to ~/Library/Application Support/BBEdit/Scripts. You can then +-- select the script from the Script menu and clang-format will format the +-- selection. Note that you can rename the menu item by renaming the script, and +-- can assign the menu item a keyboard shortcut in the BBEdit preferences, under +-- Menus & Shortcuts. +on urlToPOSIXPath(theURL) + return do shell script "python -c \"import urllib, urlparse, sys; print urllib.unquote(urlparse.urlparse(sys.argv[1])[2])\" " & quoted form of theURL +end urlToPOSIXPath + +tell application "BBEdit" + set selectionOffset to characterOffset of selection + set selectionLength to length of selection + set fileURL to URL of text document 1 +end tell + +set filePath to urlToPOSIXPath(fileURL) +set newContents to do shell script "/path/to/clang-format -offset=" & selectionOffset & " -length=" & selectionLength & " " & quoted form of filePath + +tell application "BBEdit" + -- "set contents of text document 1 to newContents" scrolls to the bottom while + -- replacing a selection flashes a bit but doesn't affect the scroll position. + set currentLength to length of contents of text document 1 + select characters 1 thru currentLength of text document 1 + set text of selection to newContents + select characters selectionOffset thru (selectionOffset + selectionLength - 1) of text document 1 +end tell diff --git a/tools/clang-format/clang-format-diff.py b/tools/clang-format/clang-format-diff.py index ab5f1b1bc63..68b5113d92f 100755 --- a/tools/clang-format/clang-format-diff.py +++ b/tools/clang-format/clang-format-diff.py @@ -63,9 +63,10 @@ def formatRange(r, style): offset, length = getOffsetLength(filename, line_number, line_count) with open(filename, 'r') as f: text = f.read() - p = subprocess.Popen([binary, '-offset', str(offset), '-length', str(length), - '-style', style], - stdout=subprocess.PIPE, stderr=subprocess.PIPE, + command = [binary, '-offset', str(offset), '-length', str(length)] + if style: + command.extend(['-style', style]) + p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE) stdout, stderr = p.communicate(input=text) if stderr: @@ -84,8 +85,8 @@ def main(): 'Reformat changed lines in diff') parser.add_argument('-p', default=1, help='strip the smallest prefix containing P slashes') - parser.add_argument('-style', default='LLVM', - help='formatting style to apply (LLVM, Google)') + parser.add_argument('-style', + help='formatting style to apply (LLVM, Google, Chromium)') args = parser.parse_args() filename = None diff --git a/tools/clang-format/clang-format.el b/tools/clang-format/clang-format.el new file mode 100644 index 00000000000..2c5546b241c --- /dev/null +++ b/tools/clang-format/clang-format.el @@ -0,0 +1,31 @@ +;;; Clang-format emacs integration for use with C/Objective-C/C++. + +;; This defines a function clang-format-region that you can bind to a key. +;; A minimal .emacs would contain: +;; +;; (load "/tools/clang-format/clang-format.el") +;; (global-set-key [C-M-tab] 'clang-format-region) +;; +;; Depending on your configuration and coding style, you might need to modify +;; 'style' and 'binary' below. +(defun clang-format-region () + (interactive) + + (let* ((orig-windows (get-buffer-window-list (current-buffer))) + (orig-window-starts (mapcar #'window-start orig-windows)) + (orig-point (point)) + (binary "clang-format") + (style "LLVM")) + (if mark-active + (setq beg (region-beginning) + end (region-end)) + (setq beg (min (line-beginning-position) (1- (point-max))) + end (min (line-end-position) (1- (point-max))))) + (call-process-region (point-min) (point-max) binary t t nil + "-offset" (number-to-string (1- beg)) + "-length" (number-to-string (- end beg)) + "-style" style) + (goto-char orig-point) + (dotimes (index (length orig-windows)) + (set-window-start (nth index orig-windows) + (nth index orig-window-starts))))) diff --git a/tools/clang-format/clang-format.py b/tools/clang-format/clang-format.py index de922574070..d90c62a5bf6 100644 --- a/tools/clang-format/clang-format.py +++ b/tools/clang-format/clang-format.py @@ -23,6 +23,10 @@ # Change this to the full path if clang-format is not on the path. binary = 'clang-format' +# Change this to format according to other formatting styles (see +# clang-format -help) +style = 'LLVM' + # Get the current text. buf = vim.current.buffer text = "\n".join(buf) @@ -34,7 +38,8 @@ str(vim.current.range.end + 2) + ')')) - offset - 2 # Call formatter. -p = subprocess.Popen([binary, '-offset', str(offset), '-length', str(length)], +p = subprocess.Popen([binary, '-offset', str(offset), '-length', str(length), + '-style', style], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE) stdout, stderr = p.communicate(input=text) diff --git a/tools/libclang/CIndex.cpp b/tools/libclang/CIndex.cpp index a81f1e437d2..a43c1ab7e45 100644 --- a/tools/libclang/CIndex.cpp +++ b/tools/libclang/CIndex.cpp @@ -723,6 +723,13 @@ bool CursorVisitor::VisitEnumConstantDecl(EnumConstantDecl *D) { } bool CursorVisitor::VisitDeclaratorDecl(DeclaratorDecl *DD) { + unsigned NumParamList = DD->getNumTemplateParameterLists(); + for (unsigned i = 0; i < NumParamList; i++) { + TemplateParameterList* Params = DD->getTemplateParameterList(i); + if (VisitTemplateParameters(Params)) + return true; + } + if (TypeSourceInfo *TSInfo = DD->getTypeSourceInfo()) if (Visit(TSInfo->getTypeLoc())) return true; @@ -751,6 +758,13 @@ static int CompareCXXCtorInitializers(const void* Xp, const void *Yp) { } bool CursorVisitor::VisitFunctionDecl(FunctionDecl *ND) { + unsigned NumParamList = ND->getNumTemplateParameterLists(); + for (unsigned i = 0; i < NumParamList; i++) { + TemplateParameterList* Params = ND->getTemplateParameterList(i); + if (VisitTemplateParameters(Params)) + return true; + } + if (TypeSourceInfo *TSInfo = ND->getTypeSourceInfo()) { // Visit the function declaration's syntactic components in the order // written. This requires a bit of work. @@ -3574,6 +3588,8 @@ CXString clang_getCursorKindSpelling(enum CXCursorKind Kind) { return cxstring::createRef("ObjCStringLiteral"); case CXCursor_ObjCBoolLiteralExpr: return cxstring::createRef("ObjCBoolLiteralExpr"); + case CXCursor_ObjCSelfExpr: + return cxstring::createRef("ObjCSelfExpr"); case CXCursor_ObjCEncodeExpr: return cxstring::createRef("ObjCEncodeExpr"); case CXCursor_ObjCSelectorExpr: @@ -4444,6 +4460,7 @@ CXCursor clang_getCursorDefinition(CXCursor C) { case Decl::TemplateTypeParm: case Decl::EnumConstant: case Decl::Field: + case Decl::MSProperty: case Decl::IndirectField: case Decl::ObjCIvar: case Decl::ObjCAtDefsField: @@ -4459,6 +4476,7 @@ CXCursor clang_getCursorDefinition(CXCursor C) { case Decl::FileScopeAsm: case Decl::StaticAssert: case Decl::Block: + case Decl::Captured: case Decl::Label: // FIXME: Is this right?? case Decl::ClassScopeFunctionSpecialization: case Decl::Import: @@ -5903,6 +5921,72 @@ CXFile clang_getIncludedFile(CXCursor cursor) { return const_cast(ID->getFile()); } +unsigned clang_Cursor_getObjCPropertyAttributes(CXCursor C, unsigned reserved) { + if (C.kind != CXCursor_ObjCPropertyDecl) + return CXObjCPropertyAttr_noattr; + + unsigned Result = CXObjCPropertyAttr_noattr; + const ObjCPropertyDecl *PD = dyn_cast(getCursorDecl(C)); + ObjCPropertyDecl::PropertyAttributeKind Attr = + PD->getPropertyAttributesAsWritten(); + +#define SET_CXOBJCPROP_ATTR(A) \ + if (Attr & ObjCPropertyDecl::OBJC_PR_##A) \ + Result |= CXObjCPropertyAttr_##A + SET_CXOBJCPROP_ATTR(readonly); + SET_CXOBJCPROP_ATTR(getter); + SET_CXOBJCPROP_ATTR(assign); + SET_CXOBJCPROP_ATTR(readwrite); + SET_CXOBJCPROP_ATTR(retain); + SET_CXOBJCPROP_ATTR(copy); + SET_CXOBJCPROP_ATTR(nonatomic); + SET_CXOBJCPROP_ATTR(setter); + SET_CXOBJCPROP_ATTR(atomic); + SET_CXOBJCPROP_ATTR(weak); + SET_CXOBJCPROP_ATTR(strong); + SET_CXOBJCPROP_ATTR(unsafe_unretained); +#undef SET_CXOBJCPROP_ATTR + + return Result; +} + +unsigned clang_Cursor_getObjCDeclQualifiers(CXCursor C) { + if (!clang_isDeclaration(C.kind)) + return CXObjCDeclQualifier_None; + + Decl::ObjCDeclQualifier QT = Decl::OBJC_TQ_None; + const Decl *D = getCursorDecl(C); + if (const ObjCMethodDecl *MD = dyn_cast(D)) + QT = MD->getObjCDeclQualifier(); + else if (const ParmVarDecl *PD = dyn_cast(D)) + QT = PD->getObjCDeclQualifier(); + if (QT == Decl::OBJC_TQ_None) + return CXObjCDeclQualifier_None; + + unsigned Result = CXObjCDeclQualifier_None; + if (QT & Decl::OBJC_TQ_In) Result |= CXObjCDeclQualifier_In; + if (QT & Decl::OBJC_TQ_Inout) Result |= CXObjCDeclQualifier_Inout; + if (QT & Decl::OBJC_TQ_Out) Result |= CXObjCDeclQualifier_Out; + if (QT & Decl::OBJC_TQ_Bycopy) Result |= CXObjCDeclQualifier_Bycopy; + if (QT & Decl::OBJC_TQ_Byref) Result |= CXObjCDeclQualifier_Byref; + if (QT & Decl::OBJC_TQ_Oneway) Result |= CXObjCDeclQualifier_Oneway; + + return Result; +} + +unsigned clang_Cursor_isVariadic(CXCursor C) { + if (!clang_isDeclaration(C.kind)) + return 0; + + const Decl *D = getCursorDecl(C); + if (const FunctionDecl *FD = dyn_cast(D)) + return FD->isVariadic(); + if (const ObjCMethodDecl *MD = dyn_cast(D)) + return MD->isVariadic(); + + return 0; +} + CXSourceRange clang_Cursor_getCommentRange(CXCursor C) { if (!clang_isDeclaration(C.kind)) return clang_getNullRange(); @@ -5971,6 +6055,13 @@ CXModule clang_Cursor_getModule(CXCursor C) { return 0; } +CXFile clang_Module_getASTFile(CXModule CXMod) { + if (!CXMod) + return 0; + Module *Mod = static_cast(CXMod); + return const_cast(Mod->getASTFile()); +} + CXModule clang_Module_getParent(CXModule CXMod) { if (!CXMod) return 0; diff --git a/tools/libclang/CIndexCXX.cpp b/tools/libclang/CIndexCXX.cpp index c68dde7c2c2..a3d236446f8 100644 --- a/tools/libclang/CIndexCXX.cpp +++ b/tools/libclang/CIndexCXX.cpp @@ -33,7 +33,7 @@ unsigned clang_isVirtualBase(CXCursor C) { enum CX_CXXAccessSpecifier clang_getCXXAccessSpecifier(CXCursor C) { AccessSpecifier spec = AS_none; - if (C.kind == CXCursor_CXXAccessSpecifier) + if (C.kind == CXCursor_CXXAccessSpecifier || clang_isDeclaration(C.kind)) spec = getCursorDecl(C)->getAccess(); else if (C.kind == CXCursor_CXXBaseSpecifier) spec = getCursorCXXBaseSpecifier(C)->getAccessSpecifier(); diff --git a/tools/libclang/CXCursor.cpp b/tools/libclang/CXCursor.cpp index 7b01ec2de0c..2cdb71bb022 100644 --- a/tools/libclang/CXCursor.cpp +++ b/tools/libclang/CXCursor.cpp @@ -215,6 +215,7 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl *Parent, case Stmt::TypeTraitExprClass: case Stmt::CXXBindTemporaryExprClass: case Stmt::CXXDefaultArgExprClass: + case Stmt::CXXDefaultInitExprClass: case Stmt::CXXScalarValueInitExprClass: case Stmt::CXXUuidofExprClass: case Stmt::ChooseExprClass: @@ -270,6 +271,10 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl *Parent, K = CXCursor_DeclStmt; break; + case Stmt::CapturedStmtClass: + K = CXCursor_UnexposedStmt; + break; + case Stmt::IntegerLiteralClass: K = CXCursor_IntegerLiteral; break; @@ -430,7 +435,21 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl *Parent, K = CXCursor_SizeOfPackExpr; break; - case Stmt::DeclRefExprClass: + case Stmt::DeclRefExprClass: + if (const ImplicitParamDecl *IPD = + dyn_cast_or_null(cast(S)->getDecl())) { + if (const ObjCMethodDecl *MD = + dyn_cast(IPD->getDeclContext())) { + if (MD->getSelfDecl() == IPD) { + K = CXCursor_ObjCSelfExpr; + break; + } + } + } + + K = CXCursor_DeclRefExpr; + break; + case Stmt::DependentScopeDeclRefExprClass: case Stmt::SubstNonTypeTemplateParmExprClass: case Stmt::SubstNonTypeTemplateParmPackExprClass: @@ -442,6 +461,7 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl *Parent, case Stmt::CXXDependentScopeMemberExprClass: case Stmt::CXXPseudoDestructorExprClass: case Stmt::MemberExprClass: + case Stmt::MSPropertyRefExprClass: case Stmt::ObjCIsaExprClass: case Stmt::ObjCIvarRefExprClass: case Stmt::ObjCPropertyRefExprClass: @@ -1025,7 +1045,7 @@ unsigned clang_CXCursorSet_contains(CXCursorSet set, CXCursor cursor) { CXCursorSet_Impl *setImpl = unpackCXCursorSet(set); if (!setImpl) return 0; - return setImpl->find(cursor) == setImpl->end(); + return setImpl->find(cursor) != setImpl->end(); } unsigned clang_CXCursorSet_insert(CXCursorSet set, CXCursor cursor) { diff --git a/tools/libclang/CXSourceLocation.cpp b/tools/libclang/CXSourceLocation.cpp index bc8d5751903..b7c7622c66b 100644 --- a/tools/libclang/CXSourceLocation.cpp +++ b/tools/libclang/CXSourceLocation.cpp @@ -198,6 +198,17 @@ static void createNullLocation(CXString *filename, unsigned *line, extern "C" { +int clang_Location_isInSystemHeader(CXSourceLocation location) { + const SourceLocation Loc = + SourceLocation::getFromRawEncoding(location.int_data); + if (Loc.isInvalid()) + return 0; + + const SourceManager &SM = + *static_cast(location.ptr_data[0]); + return SM.isInSystemHeader(Loc); +} + void clang_getExpansionLocation(CXSourceLocation location, CXFile *file, unsigned *line, diff --git a/tools/libclang/CXTranslationUnit.h b/tools/libclang/CXTranslationUnit.h index 699b74a642d..bdc171cff66 100644 --- a/tools/libclang/CXTranslationUnit.h +++ b/tools/libclang/CXTranslationUnit.h @@ -39,6 +39,8 @@ namespace cxtu { CXTranslationUnitImpl *MakeCXTranslationUnit(CIndexer *CIdx, ASTUnit *AU); static inline ASTUnit *getASTUnit(CXTranslationUnit TU) { + if (!TU) + return 0; return TU->TheASTUnit; } diff --git a/tools/libclang/CXType.cpp b/tools/libclang/CXType.cpp index 6f87fc51a41..148241597a3 100644 --- a/tools/libclang/CXType.cpp +++ b/tools/libclang/CXType.cpp @@ -149,14 +149,20 @@ CXType clang_getCursorType(CXCursor C) { return MakeCXType(Context.getTypeDeclType(TD), TU); if (const ObjCInterfaceDecl *ID = dyn_cast(D)) return MakeCXType(Context.getObjCInterfaceType(ID), TU); + if (const DeclaratorDecl *DD = dyn_cast(D)) { + if (TypeSourceInfo *TSInfo = DD->getTypeSourceInfo()) + return MakeCXType(TSInfo->getType(), TU); + return MakeCXType(DD->getType(), TU); + } if (const ValueDecl *VD = dyn_cast(D)) return MakeCXType(VD->getType(), TU); if (const ObjCPropertyDecl *PD = dyn_cast(D)) return MakeCXType(PD->getType(), TU); - if (const FunctionDecl *FD = dyn_cast(D)) - return MakeCXType(FD->getType(), TU); - if (const FunctionTemplateDecl *FTD = dyn_cast(D)) + if (const FunctionTemplateDecl *FTD = dyn_cast(D)) { + if (TypeSourceInfo *TSInfo = FTD->getTemplatedDecl()->getTypeSourceInfo()) + return MakeCXType(TSInfo->getType(), TU); return MakeCXType(FTD->getTemplatedDecl()->getType(), TU); + } return MakeCXType(QualType(), TU); } @@ -651,6 +657,126 @@ long long clang_getArraySize(CXType CT) { return result; } +long long clang_Type_getAlignOf(CXType T) { + if (T.kind == CXType_Invalid) + return CXTypeLayoutError_Invalid; + ASTContext &Ctx = cxtu::getASTUnit(GetTU(T))->getASTContext(); + QualType QT = GetQualType(T); + // [expr.alignof] p1: return size_t value for complete object type, reference + // or array. + // [expr.alignof] p3: if reference type, return size of referenced type + if (QT->isReferenceType()) + QT = QT.getNonReferenceType(); + if (QT->isIncompleteType()) + return CXTypeLayoutError_Incomplete; + if (QT->isDependentType()) + return CXTypeLayoutError_Dependent; + // Exceptions by GCC extension - see ASTContext.cpp:1313 getTypeInfoImpl + // if (QT->isFunctionType()) return 4; // Bug #15511 - should be 1 + // if (QT->isVoidType()) return 1; + return Ctx.getTypeAlignInChars(QT).getQuantity(); +} + +long long clang_Type_getSizeOf(CXType T) { + if (T.kind == CXType_Invalid) + return CXTypeLayoutError_Invalid; + ASTContext &Ctx = cxtu::getASTUnit(GetTU(T))->getASTContext(); + QualType QT = GetQualType(T); + // [expr.sizeof] p2: if reference type, return size of referenced type + if (QT->isReferenceType()) + QT = QT.getNonReferenceType(); + // [expr.sizeof] p1: return -1 on: func, incomplete, bitfield, incomplete + // enumeration + // Note: We get the cxtype, not the cxcursor, so we can't call + // FieldDecl->isBitField() + // [expr.sizeof] p3: pointer ok, function not ok. + // [gcc extension] lib/AST/ExprConstant.cpp:1372 HandleSizeof : vla == error + if (QT->isIncompleteType()) + return CXTypeLayoutError_Incomplete; + if (QT->isDependentType()) + return CXTypeLayoutError_Dependent; + if (!QT->isConstantSizeType()) + return CXTypeLayoutError_NotConstantSize; + // [gcc extension] lib/AST/ExprConstant.cpp:1372 + // HandleSizeof : {voidtype,functype} == 1 + // not handled by ASTContext.cpp:1313 getTypeInfoImpl + if (QT->isVoidType() || QT->isFunctionType()) + return 1; + return Ctx.getTypeSizeInChars(QT).getQuantity(); +} + +static long long visitRecordForValidation(const RecordDecl *RD) { + for (RecordDecl::field_iterator I = RD->field_begin(), E = RD->field_end(); + I != E; ++I){ + QualType FQT = (*I)->getType(); + if (FQT->isIncompleteType()) + return CXTypeLayoutError_Incomplete; + if (FQT->isDependentType()) + return CXTypeLayoutError_Dependent; + // recurse + if (const RecordType *ChildType = (*I)->getType()->getAs()) { + if (const RecordDecl *Child = ChildType->getDecl()) { + long long ret = visitRecordForValidation(Child); + if (ret < 0) + return ret; + } + } + // else try next field + } + return 0; +} + +long long clang_Type_getOffsetOf(CXType PT, const char *S) { + // check that PT is not incomplete/dependent + CXCursor PC = clang_getTypeDeclaration(PT); + if (clang_isInvalid(PC.kind)) + return CXTypeLayoutError_Invalid; + const RecordDecl *RD = + dyn_cast_or_null(cxcursor::getCursorDecl(PC)); + if (!RD) + return CXTypeLayoutError_Invalid; + RD = RD->getDefinition(); + if (!RD) + return CXTypeLayoutError_Incomplete; + QualType RT = GetQualType(PT); + if (RT->isIncompleteType()) + return CXTypeLayoutError_Incomplete; + if (RT->isDependentType()) + return CXTypeLayoutError_Dependent; + // We recurse into all record fields to detect incomplete and dependent types. + long long Error = visitRecordForValidation(RD); + if (Error < 0) + return Error; + if (!S) + return CXTypeLayoutError_InvalidFieldName; + // lookup field + ASTContext &Ctx = cxtu::getASTUnit(GetTU(PT))->getASTContext(); + IdentifierInfo *II = &Ctx.Idents.get(S); + DeclarationName FieldName(II); + RecordDecl::lookup_const_result Res = RD->lookup(FieldName); + // If a field of the parent record is incomplete, lookup will fail. + // and we would return InvalidFieldName instead of Incomplete. + // But this erroneous results does protects again a hidden assertion failure + // in the RecordLayoutBuilder + if (Res.size() != 1) + return CXTypeLayoutError_InvalidFieldName; + if (const FieldDecl *FD = dyn_cast(Res.front())) + return Ctx.getFieldOffset(FD); + if (const IndirectFieldDecl *IFD = dyn_cast(Res.front())) + return Ctx.getFieldOffset(IFD); + // we don't want any other Decl Type. + return CXTypeLayoutError_InvalidFieldName; +} + +unsigned clang_Cursor_isBitField(CXCursor C) { + if (!clang_isDeclaration(C.kind)) + return 0; + const FieldDecl *FD = dyn_cast_or_null(cxcursor::getCursorDecl(C)); + if (!FD) + return 0; + return FD->isBitField(); +} + CXString clang_getDeclObjCTypeEncoding(CXCursor C) { if (!clang_isDeclaration(C.kind)) return cxstring::createEmpty(); diff --git a/tools/libclang/IndexBody.cpp b/tools/libclang/IndexBody.cpp index 95d74efc04e..02ab885e62f 100644 --- a/tools/libclang/IndexBody.cpp +++ b/tools/libclang/IndexBody.cpp @@ -68,9 +68,6 @@ class BodyIndexer : public cxindex::RecursiveASTVisitor { } bool VisitObjCMessageExpr(ObjCMessageExpr *E) { - if (TypeSourceInfo *Cls = E->getClassReceiverTypeInfo()) - IndexCtx.indexTypeSourceInfo(Cls, Parent, ParentDC); - if (ObjCMethodDecl *MD = E->getMethodDecl()) IndexCtx.handleReference(MD, E->getSelectorStartLoc(), Parent, ParentDC, E, @@ -89,6 +86,12 @@ class BodyIndexer : public cxindex::RecursiveASTVisitor { return true; } + bool VisitMSPropertyRefExpr(MSPropertyRefExpr *E) { + IndexCtx.handleReference(E->getPropertyDecl(), E->getMemberLoc(), Parent, + ParentDC, E, CXIdxEntityRef_Direct); + return true; + } + bool VisitObjCProtocolExpr(ObjCProtocolExpr *E) { IndexCtx.handleReference(E->getProtocol(), E->getProtocolIdLoc(), Parent, ParentDC, E, CXIdxEntityRef_Direct); diff --git a/tools/libclang/IndexDecl.cpp b/tools/libclang/IndexDecl.cpp index d7fb959b1ed..756001c3c12 100644 --- a/tools/libclang/IndexDecl.cpp +++ b/tools/libclang/IndexDecl.cpp @@ -105,6 +105,11 @@ class IndexingDeclVisitor : public ConstDeclVisitor { return true; } + bool VisitMSPropertyDecl(const MSPropertyDecl *D) { + handleDeclarator(D); + return true; + } + bool VisitEnumConstantDecl(const EnumConstantDecl *D) { IndexCtx.handleEnumerator(D); IndexCtx.indexBody(D->getInitExpr(), D); diff --git a/tools/libclang/Indexing.cpp b/tools/libclang/Indexing.cpp index 2a504db2a49..15786acb8b3 100644 --- a/tools/libclang/Indexing.cpp +++ b/tools/libclang/Indexing.cpp @@ -292,7 +292,7 @@ class IndexPPCallbacks : public PPCallbacks { /// MacroExpands - This is called by when a macro invocation is found. virtual void MacroExpands(const Token &MacroNameTok, const MacroDirective *MD, - SourceRange Range) { + SourceRange Range, const MacroArgs *Args) { } /// SourceRangeSkipped - This hook is called when a source range is skipped. @@ -398,10 +398,6 @@ class CaptureDiagnosticConsumer : public DiagnosticConsumer { if (level >= DiagnosticsEngine::Error) Errors.push_back(StoredDiagnostic(level, Info)); } - - DiagnosticConsumer *clone(DiagnosticsEngine &Diags) const { - return new IgnoringDiagConsumer(); - } }; //===----------------------------------------------------------------------===// @@ -549,8 +545,7 @@ static void clang_indexSourceFile_Impl(void *UserData) { IntrusiveRefCntPtr Diags(CompilerInstance::createDiagnostics(new DiagnosticOptions, CaptureDiag, - /*ShouldOwnClient=*/true, - /*ShouldCloneClient=*/false)); + /*ShouldOwnClient=*/true)); // Recover resources if we crash before exiting this function. llvm::CrashRecoveryContextCleanupRegistrargetLocation(), getCursor(D), DInfo); } +bool IndexingContext::handleMSProperty(const MSPropertyDecl *D) { + DeclInfo DInfo(/*isRedeclaration=*/false, /*isDefinition=*/true, + /*isContainer=*/false); + return handleDecl(D, D->getLocation(), getCursor(D), DInfo); +} + bool IndexingContext::handleEnumerator(const EnumConstantDecl *D) { DeclInfo DInfo(/*isRedeclaration=*/false, /*isDefinition=*/true, /*isContainer=*/false); diff --git a/tools/libclang/IndexingContext.h b/tools/libclang/IndexingContext.h index c9097c5e6ee..62873bedb27 100644 --- a/tools/libclang/IndexingContext.h +++ b/tools/libclang/IndexingContext.h @@ -16,6 +16,7 @@ namespace clang { class FileEntry; + class MSPropertyDecl; class ObjCPropertyDecl; class ClassTemplateDecl; class FunctionTemplateDecl; @@ -404,6 +405,8 @@ class IndexingContext { bool handleField(const FieldDecl *D); + bool handleMSProperty(const MSPropertyDecl *D); + bool handleEnumerator(const EnumConstantDecl *D); bool handleTagDecl(const TagDecl *D); diff --git a/tools/libclang/RecursiveASTVisitor.h b/tools/libclang/RecursiveASTVisitor.h index 5862e12cd1b..e45545ed1ad 100644 --- a/tools/libclang/RecursiveASTVisitor.h +++ b/tools/libclang/RecursiveASTVisitor.h @@ -1170,8 +1170,9 @@ bool RecursiveASTVisitor::TraverseDeclContextHelper(DeclContext *DC) { for (DeclContext::decl_iterator Child = DC->decls_begin(), ChildEnd = DC->decls_end(); Child != ChildEnd; ++Child) { - // BlockDecls are traversed through BlockExprs. - if (!isa(*Child)) + // BlockDecls and CapturedDecls are traversed through BlockExprs and + // CapturedStmts respectively. + if (!isa(*Child) && !isa(*Child)) TRY_TO(TraverseDecl(*Child)); } @@ -1200,6 +1201,14 @@ DEF_TRAVERSE_DECL(BlockDecl, { return true; }) +DEF_TRAVERSE_DECL(CapturedDecl, { + TRY_TO(TraverseStmt(D->getBody())); + // This return statement makes sure the traversal of nodes in + // decls_begin()/decls_end() (done in the DEF_TRAVERSE_DECL macro) + // is skipped - don't remove it. + return true; + }) + DEF_TRAVERSE_DECL(EmptyDecl, { }) DEF_TRAVERSE_DECL(FileScopeAsmDecl, { @@ -1614,6 +1623,10 @@ DEF_TRAVERSE_DECL(FieldDecl, { TRY_TO(TraverseStmt(D->getInClassInitializer())); }) +DEF_TRAVERSE_DECL(MSPropertyDecl, { + TRY_TO(TraverseDeclaratorHelper(D)); + }) + DEF_TRAVERSE_DECL(ObjCAtDefsFieldDecl, { TRY_TO(TraverseDeclaratorHelper(D)); if (D->isBitField()) @@ -1836,7 +1849,6 @@ DEF_TRAVERSE_STMT(ReturnStmt, { }) DEF_TRAVERSE_STMT(SwitchStmt, { }) DEF_TRAVERSE_STMT(WhileStmt, { }) - DEF_TRAVERSE_STMT(CXXDependentScopeMemberExpr, { TRY_TO(TraverseNestedNameSpecifierLoc(S->getQualifierLoc())); TRY_TO(TraverseDeclarationNameInfo(S->getMemberNameInfo())); @@ -2079,6 +2091,7 @@ DEF_TRAVERSE_STMT(CompoundLiteralExpr, { }) DEF_TRAVERSE_STMT(CXXBindTemporaryExpr, { }) DEF_TRAVERSE_STMT(CXXBoolLiteralExpr, { }) DEF_TRAVERSE_STMT(CXXDefaultArgExpr, { }) +DEF_TRAVERSE_STMT(CXXDefaultInitExpr, { }) DEF_TRAVERSE_STMT(CXXDeleteExpr, { }) DEF_TRAVERSE_STMT(ExprWithCleanups, { }) DEF_TRAVERSE_STMT(CXXNullPtrLiteralExpr, { }) @@ -2103,7 +2116,10 @@ DEF_TRAVERSE_STMT(ObjCEncodeExpr, { }) DEF_TRAVERSE_STMT(ObjCIsaExpr, { }) DEF_TRAVERSE_STMT(ObjCIvarRefExpr, { }) -DEF_TRAVERSE_STMT(ObjCMessageExpr, { }) +DEF_TRAVERSE_STMT(ObjCMessageExpr, { + if (TypeSourceInfo *TInfo = S->getClassReceiverTypeInfo()) + TRY_TO(TraverseTypeLoc(TInfo->getTypeLoc())); +}) DEF_TRAVERSE_STMT(ObjCPropertyRefExpr, { }) DEF_TRAVERSE_STMT(ObjCSubscriptRefExpr, { }) DEF_TRAVERSE_STMT(ObjCProtocolExpr, { }) @@ -2133,9 +2149,13 @@ DEF_TRAVERSE_STMT(UnresolvedMemberExpr, { } }) +DEF_TRAVERSE_STMT(MSPropertyRefExpr, {}) DEF_TRAVERSE_STMT(SEHTryStmt, {}) DEF_TRAVERSE_STMT(SEHExceptStmt, {}) DEF_TRAVERSE_STMT(SEHFinallyStmt,{}) +DEF_TRAVERSE_STMT(CapturedStmt, { + TRY_TO(TraverseDecl(S->getCapturedDecl())); +}) DEF_TRAVERSE_STMT(CXXOperatorCallExpr, { }) DEF_TRAVERSE_STMT(OpaqueValueExpr, { }) diff --git a/tools/libclang/libclang.exports b/tools/libclang/libclang.exports index d99f24ef038..0c9912e5202 100644 --- a/tools/libclang/libclang.exports +++ b/tools/libclang/libclang.exports @@ -10,13 +10,18 @@ clang_Cursor_getCommentRange clang_Cursor_getParsedComment clang_Cursor_getRawCommentText clang_Cursor_getNumArguments +clang_Cursor_getObjCDeclQualifiers +clang_Cursor_getObjCPropertyAttributes clang_Cursor_getObjCSelectorIndex clang_Cursor_getSpellingNameRange clang_Cursor_getTranslationUnit clang_Cursor_getReceiverType +clang_Cursor_isBitField clang_Cursor_isDynamicCall clang_Cursor_isNull +clang_Cursor_isVariadic clang_Cursor_getModule +clang_Module_getASTFile clang_Module_getParent clang_Module_getName clang_Module_getFullName @@ -53,6 +58,9 @@ clang_TParamCommandComment_getParamName clang_TParamCommandComment_isParamPositionValid clang_TParamCommandComment_getDepth clang_TParamCommandComment_getIndex +clang_Type_getAlignOf +clang_Type_getSizeOf +clang_Type_getOffsetOf clang_VerbatimBlockLineComment_getText clang_VerbatimLineComment_getText clang_HTMLTagComment_getAsString @@ -244,6 +252,7 @@ clang_isUnexposed clang_isVirtualBase clang_isVolatileQualifiedType clang_loadDiagnostics +clang_Location_isInSystemHeader clang_parseTranslationUnit clang_remap_dispose clang_remap_getFilenames diff --git a/tools/scan-build/c++-analyzer b/tools/scan-build/c++-analyzer deleted file mode 120000 index ca10bf547dc..00000000000 --- a/tools/scan-build/c++-analyzer +++ /dev/null @@ -1 +0,0 @@ -ccc-analyzer \ No newline at end of file diff --git a/tools/scan-build/c++-analyzer b/tools/scan-build/c++-analyzer new file mode 100755 index 00000000000..dda5db9c7d9 --- /dev/null +++ b/tools/scan-build/c++-analyzer @@ -0,0 +1,8 @@ +#!/usr/bin/env perl + +use Cwd qw/ abs_path /; +use File::Basename qw/ dirname /; +# Add scan-build dir to the list of places where perl looks for modules. +use lib dirname(abs_path($0)); + +do 'ccc-analyzer'; diff --git a/tools/scan-build/ccc-analyzer b/tools/scan-build/ccc-analyzer index bb6dd9563b3..60b0185e6fb 100755 --- a/tools/scan-build/ccc-analyzer +++ b/tools/scan-build/ccc-analyzer @@ -491,7 +491,7 @@ foreach (my $i = 0; $i < scalar(@ARGV); ++$i) { while ($Cnt > 0) { ++$i; --$Cnt; push @CompileOpts, $ARGV[$i]; } next; } - if ($Arg =~ /-msse.*/) { + if ($Arg =~ /-m.*/) { push @CompileOpts,$Arg; next; } diff --git a/tools/scan-build/scan-build b/tools/scan-build/scan-build index 32eecc079e6..35f852e70b2 100755 --- a/tools/scan-build/scan-build +++ b/tools/scan-build/scan-build @@ -931,11 +931,13 @@ sub RunXcodebuild { if ($oldBehavior == 0) { my $OutputDir = $Options->{"OUTPUT_DIR"}; my $CLANG = $Options->{"CLANG"}; + my $OtherFlags = $Options->{"CCC_ANALYZER_ANALYSIS"}; push @$Args, "RUN_CLANG_STATIC_ANALYZER=YES", "CLANG_ANALYZER_OUTPUT=plist-html", "CLANG_ANALYZER_EXEC=$CLANG", - "CLANG_ANALYZER_OUTPUT_DIR=$OutputDir"; + "CLANG_ANALYZER_OUTPUT_DIR=$OutputDir", + "CLANG_ANALYZER_OTHER_FLAGS=$OtherFlags"; return (system(@$Args) >> 8); } @@ -1560,8 +1562,16 @@ if ($ForceDisplayHelp || $RequestDisplayHelp) { } $ClangCXX = $Clang; -$ClangCXX =~ s/\-\d+\.\d+$//; -$ClangCXX .= "++"; +# Determine operating system under which this copy of Perl was built. +my $IsWinBuild = ($^O =~/msys|cygwin|MSWin32/); +if($IsWinBuild) { + $ClangCXX =~ s/.exe$/++.exe/; +} +else { + $ClangCXX =~ s/\-\d+\.\d+$//; + $ClangCXX .= "++"; +} + # Make sure to use "" to handle paths with spaces. $ClangVersion = HtmlEscape(`"$Clang" --version`); diff --git a/tools/scan-build/scan-build.bat b/tools/scan-build/scan-build.bat new file mode 100644 index 00000000000..77be6746318 --- /dev/null +++ b/tools/scan-build/scan-build.bat @@ -0,0 +1 @@ +perl -S scan-build %* diff --git a/tools/scan-view/ScanView.py b/tools/scan-view/ScanView.py index 32570b98583..ee08baa8bad 100644 --- a/tools/scan-view/ScanView.py +++ b/tools/scan-view/ScanView.py @@ -686,16 +686,8 @@ def send_head(self, fields=None): if components[-1] == '': components[-1] = 'index.html' - suffix = '/'.join(components) - - # The summary may reference source files on disk using rooted - # paths. Make sure these resolve correctly for now. - # FIXME: This isn't a very good idea... we should probably - # mark rooted paths somehow. - if os.path.exists(posixpath.join('/', suffix)): - path = posixpath.join('/', suffix) - else: - path = posixpath.join(self.server.root, suffix) + relpath = '/'.join(components) + path = posixpath.join(self.server.root, relpath) if self.server.options.debug > 1: print >>sys.stderr, '%s: SERVER: sending path "%s"'%(sys.argv[0], @@ -708,8 +700,8 @@ def send_404(self): def send_path(self, path): # If the requested path is outside the root directory, do not open it - rel = os.path.abspath(os.path.join(self.server.root, path)) - if not rel.startswith(os.path.abspath(self.server.root) ): + rel = os.path.abspath(path) + if not rel.startswith(os.path.abspath(self.server.root)): return self.send_404() ctype = self.guess_type(path) diff --git a/unittests/AST/CommentLexer.cpp b/unittests/AST/CommentLexer.cpp index 507daf83913..fc0fd772750 100644 --- a/unittests/AST/CommentLexer.cpp +++ b/unittests/AST/CommentLexer.cpp @@ -64,7 +64,7 @@ void CommentLexerTest::lexString(const char *Source, FileID File = SourceMgr.createFileIDForMemBuffer(Buf); SourceLocation Begin = SourceMgr.getLocForStartOfFile(File); - Lexer L(Allocator, Traits, Begin, Source, Source + strlen(Source)); + Lexer L(Allocator, Diags, Traits, Begin, Source, Source + strlen(Source)); while (1) { Token Tok; diff --git a/unittests/AST/CommentParser.cpp b/unittests/AST/CommentParser.cpp index 3dce60ab731..d05fb58fafa 100644 --- a/unittests/AST/CommentParser.cpp +++ b/unittests/AST/CommentParser.cpp @@ -58,7 +58,7 @@ FullComment *CommentParserTest::parseString(const char *Source) { FileID File = SourceMgr.createFileIDForMemBuffer(Buf); SourceLocation Begin = SourceMgr.getLocForStartOfFile(File); - Lexer L(Allocator, Traits, Begin, Source, Source + strlen(Source)); + Lexer L(Allocator, Diags, Traits, Begin, Source, Source + strlen(Source)); Sema S(Allocator, SourceMgr, Diags, Traits, /*PP=*/ NULL); Parser P(L, S, Allocator, SourceMgr, Diags, Traits); diff --git a/unittests/AST/SourceLocationTest.cpp b/unittests/AST/SourceLocationTest.cpp index b8d8b02d031..f6c0edc9271 100644 --- a/unittests/AST/SourceLocationTest.cpp +++ b/unittests/AST/SourceLocationTest.cpp @@ -132,10 +132,10 @@ TEST(CompoundLiteralExpr, CompoundVectorLiteralRange) { TEST(CompoundLiteralExpr, ParensCompoundVectorLiteralRange) { RangeVerifier Verifier; - Verifier.expectRange(2, 11, 2, 22); + Verifier.expectRange(2, 20, 2, 31); EXPECT_TRUE(Verifier.match( "typedef int int2 __attribute__((ext_vector_type(2)));\n" - "int2 i2 = (int2)(1, 2);", + "constant int2 i2 = (int2)(1, 2);", compoundLiteralExpr(), Lang_OpenCL)); } @@ -149,10 +149,10 @@ TEST(InitListExpr, VectorLiteralListBraceRange) { TEST(InitListExpr, VectorLiteralInitListParens) { RangeVerifier Verifier; - Verifier.expectRange(2, 17, 2, 22); + Verifier.expectRange(2, 26, 2, 31); EXPECT_TRUE(Verifier.match( "typedef int int2 __attribute__((ext_vector_type(2)));\n" - "int2 i2 = (int2)(1, 2);", initListExpr(), Lang_OpenCL)); + "constant int2 i2 = (int2)(1, 2);", initListExpr(), Lang_OpenCL)); } } // end namespace ast_matchers diff --git a/unittests/ASTMatchers/ASTMatchersTest.cpp b/unittests/ASTMatchers/ASTMatchersTest.cpp index 301b4f7c8a8..cfa53868ed2 100644 --- a/unittests/ASTMatchers/ASTMatchersTest.cpp +++ b/unittests/ASTMatchers/ASTMatchersTest.cpp @@ -337,14 +337,22 @@ TEST(DeclarationMatcher, hasDeclContext) { " class D {};" " }" "}", - recordDecl(hasDeclContext(namedDecl(hasName("M")))))); + recordDecl(hasDeclContext(namespaceDecl(hasName("M")))))); EXPECT_TRUE(notMatches( "namespace N {" " namespace M {" " class D {};" " }" "}", - recordDecl(hasDeclContext(namedDecl(hasName("N")))))); + recordDecl(hasDeclContext(namespaceDecl(hasName("N")))))); + + EXPECT_TRUE(matches("namespace {" + " namespace M {" + " class D {};" + " }" + "}", + recordDecl(hasDeclContext(namespaceDecl( + hasName("M"), hasDeclContext(namespaceDecl())))))); } TEST(ClassTemplate, DoesNotMatchClass) { @@ -1492,6 +1500,27 @@ TEST(Matcher, MatchesAccessSpecDecls) { EXPECT_TRUE(notMatches("class C { int i; };", accessSpecDecl())); } +TEST(Matcher, MatchesVirtualMethod) { + EXPECT_TRUE(matches("class X { virtual int f(); };", + methodDecl(isVirtual(), hasName("::X::f")))); + EXPECT_TRUE(notMatches("class X { int f(); };", + methodDecl(isVirtual()))); +} + +TEST(Matcher, MatchesOverridingMethod) { + EXPECT_TRUE(matches("class X { virtual int f(); }; " + "class Y : public X { int f(); };", + methodDecl(isOverride(), hasName("::Y::f")))); + EXPECT_TRUE(notMatches("class X { virtual int f(); }; " + "class Y : public X { int f(); };", + methodDecl(isOverride(), hasName("::X::f")))); + EXPECT_TRUE(notMatches("class X { int f(); }; " + "class Y : public X { int f(); };", + methodDecl(isOverride()))); + EXPECT_TRUE(notMatches("class X { int f(); int f(int); }; ", + methodDecl(isOverride()))); +} + TEST(Matcher, ConstructorCall) { StatementMatcher Constructor = constructExpr(); @@ -3387,10 +3416,12 @@ TEST(TypeMatching, MatchesAutoTypes) { EXPECT_TRUE(matches("int v[] = { 2, 3 }; void f() { for (int i : v) {} }", autoType())); - EXPECT_TRUE(matches("auto a = 1;", - autoType(hasDeducedType(isInteger())))); - EXPECT_TRUE(notMatches("auto b = 2.0;", - autoType(hasDeducedType(isInteger())))); + // FIXME: Matching against the type-as-written can't work here, because the + // type as written was not deduced. + //EXPECT_TRUE(matches("auto a = 1;", + // autoType(hasDeducedType(isInteger())))); + //EXPECT_TRUE(notMatches("auto b = 2.0;", + // autoType(hasDeducedType(isInteger())))); } TEST(TypeMatching, MatchesFunctionTypes) { diff --git a/unittests/Basic/SourceManagerTest.cpp b/unittests/Basic/SourceManagerTest.cpp index 3f09cbb0f9e..8e7f4a0371a 100644 --- a/unittests/Basic/SourceManagerTest.cpp +++ b/unittests/Basic/SourceManagerTest.cpp @@ -257,7 +257,7 @@ class MacroTracker : public PPCallbacks { true)); } virtual void MacroExpands(const Token &MacroNameTok, const MacroDirective *MD, - SourceRange Range) { + SourceRange Range, const MacroArgs *Args) { Macros.push_back(MacroAction(MacroNameTok.getLocation(), MacroNameTok.getIdentifierInfo()->getName(), false)); diff --git a/unittests/Format/FormatTest.cpp b/unittests/Format/FormatTest.cpp index e01034b09a7..4c948e89e98 100644 --- a/unittests/Format/FormatTest.cpp +++ b/unittests/Format/FormatTest.cpp @@ -302,6 +302,7 @@ TEST_F(FormatTest, FormatsForLoop) { verifyFormat("for (;;) {\n" " f();\n" "}"); + verifyFormat("for (int i = 0; (i < 10); ++i) {\n}"); verifyFormat( "for (std::vector::iterator I = UnwrappedLines.begin(),\n" @@ -430,8 +431,10 @@ TEST_F(FormatTest, FormatsSwitchStatement) { verifyFormat("switch (x) {\n" "default: {\n" " // Do nothing.\n" + "}\n" "}"); verifyFormat("switch (x) {\n" + "// comment\n" "// if 1, do f()\n" "case 1:\n" " f();\n" @@ -619,6 +622,13 @@ TEST_F(FormatTest, CanFormatCommentsLocally) { " // line 2\n" "int b;", 28, 0, getLLVMStyle())); + EXPECT_EQ("int aaaaaa; // comment\n" + "int b;\n" + "int c; // unrelated comment", + format("int aaaaaa; // comment\n" + "int b;\n" + "int c; // unrelated comment", + 31, 0, getLLVMStyle())); } TEST_F(FormatTest, RemovesTrailingWhitespaceOfComments) { @@ -633,7 +643,7 @@ TEST_F(FormatTest, UnderstandsMultiLineComments) { EXPECT_EQ( "f(aaaaaaaaaaaaaaaaaaaaaaaaa, /* Trailing comment for aa... */\n" " bbbbbbbbbbbbbbbbbbbbbbbbb);", - format("f(aaaaaaaaaaaaaaaaaaaaaaaaa , /* Trailing comment for aa... */\n" + format("f(aaaaaaaaaaaaaaaaaaaaaaaaa , \\\n/* Trailing comment for aa... */\n" " bbbbbbbbbbbbbbbbbbbbbbbbb);")); EXPECT_EQ( "f(aaaaaaaaaaaaaaaaaaaaaaaaa,\n" @@ -699,6 +709,16 @@ TEST_F(FormatTest, SplitsLongCxxComments) { "// one line", format("// A comment that doesn't fit on one line", getLLVMStyleWithColumns(20))); + EXPECT_EQ("// a b c d\n" + "// e f g\n" + "// h i j k", + format("// a b c d e f g h i j k", + getLLVMStyleWithColumns(10))); + EXPECT_EQ("// a b c d\n" + "// e f g\n" + "// h i j k", + format("\\\n// a b c d e f g h i j k", + getLLVMStyleWithColumns(10))); EXPECT_EQ("if (true) // A comment that\n" " // doesn't fit on\n" " // one line", @@ -741,6 +761,18 @@ TEST_F(FormatTest, SplitsLongLinesInComments) { "doesn't " "fit on one line. */", getLLVMStyleWithColumns(20))); + EXPECT_EQ("/* a b c d\n" + " * e f g\n" + " * h i j k\n" + " */", + format("/* a b c d e f g h i j k */", + getLLVMStyleWithColumns(10))); + EXPECT_EQ("/* a b c d\n" + " * e f g\n" + " * h i j k\n" + " */", + format("\\\n/* a b c d e f g h i j k */", + getLLVMStyleWithColumns(10))); EXPECT_EQ("/*\n" "This is a long\n" "comment that doesn't\n" @@ -843,10 +875,7 @@ TEST_F(FormatTest, SplitsLongLinesInCommentsInPreprocessor) { " Macro comment \\\n" " with a long \\\n" " line \\\n" - // FIXME: We should look at the length of the last line of the token - // instead of the full token's length. - //" */ \\\n" - " */\\\n" + " */ \\\n" " A + B", format("#define X \\\n" " /*\n" @@ -858,10 +887,7 @@ TEST_F(FormatTest, SplitsLongLinesInCommentsInPreprocessor) { EXPECT_EQ("#define X \\\n" " /* Macro comment \\\n" " with a long \\\n" - // FIXME: We should look at the length of the last line of the token - // instead of the full token's length. - //" line */ \\\n" - " line */\\\n" + " line */ \\\n" " A + B", format("#define X \\\n" " /* Macro comment with a long\n" @@ -871,10 +897,7 @@ TEST_F(FormatTest, SplitsLongLinesInCommentsInPreprocessor) { EXPECT_EQ("#define X \\\n" " /* Macro comment \\\n" " * with a long \\\n" - // FIXME: We should look at the length of the last line of the token - // instead of the full token's length. - //" * line */ \\\n" - " * line */\\\n" + " * line */ \\\n" " A + B", format("#define X \\\n" " /* Macro comment with a long line */ \\\n" @@ -953,6 +976,7 @@ TEST_F(FormatTest, DoesNotBreakSemiAfterClassDecl) { TEST_F(FormatTest, UnderstandsAccessSpecifiers) { verifyFormat("class A {\n" "public:\n" + "public: // comment\n" "protected:\n" "private:\n" " void f() {}\n" @@ -987,7 +1011,7 @@ TEST_F(FormatTest, SeparatesLogicalBlocks) { "};")); } -TEST_F(FormatTest, FormatsDerivedClass) { +TEST_F(FormatTest, FormatsClasses) { verifyFormat("class A : public B {\n};"); verifyFormat("class A : public ::B {\n};"); @@ -995,9 +1019,9 @@ TEST_F(FormatTest, FormatsDerivedClass) { "class AAAAAAAAAAAAAAAAAAAA : public BBBBBBBBBBBBBBBBBBBBBBBBBBBBBB,\n" " public CCCCCCCCCCCCCCCCCCCCCCCCCCCCCC {\n" "};\n"); - verifyFormat("class AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA :\n" - " public BBBBBBBBBBBBBBBBBBBBBBBBBBBBBB,\n" - " public CCCCCCCCCCCCCCCCCCCCCCCCCCCCCC {\n" + verifyFormat("class AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n" + " : public BBBBBBBBBBBBBBBBBBBBBBBBBBBBBB,\n" + " public CCCCCCCCCCCCCCCCCCCCCCCCCCCCCC {\n" "};\n"); verifyFormat( "class A : public B, public C, public D, public E, public F, public G {\n" @@ -1009,6 +1033,10 @@ TEST_F(FormatTest, FormatsDerivedClass) { " public F,\n" " public G {\n" "};"); + + verifyFormat("class\n" + " ReallyReallyLongClassName {\n};", + getLLVMStyleWithColumns(32)); } TEST_F(FormatTest, FormatsVariableDeclarationsAfterStructOrClass) { @@ -1193,8 +1221,8 @@ TEST_F(FormatTest, FormatsSmallMacroDefinitionsInSingleLine) { TEST_F(FormatTest, DoesNotBreakPureVirtualFunctionDefinition) { verifyFormat( - "virtual void\n" - "write(ELFWriter *writerrr, OwningPtr &buffer) = 0;"); + "virtual void write(ELFWriter *writerrr,\n" + " OwningPtr &buffer) = 0;"); } TEST_F(FormatTest, LayoutUnknownPPDirective) { @@ -1335,6 +1363,115 @@ TEST_F(FormatTest, MacroDefinitionsWithIncompleteCode) { "f(STR(this_is_a_string_literal{));"); } +TEST_F(FormatTest, MacroCallsWithoutTrailingSemicolon) { + EXPECT_EQ("INITIALIZE_PASS_BEGIN(ScopDetection, \"polly-detect\")\n" + "INITIALIZE_AG_DEPENDENCY(AliasAnalysis)\n" + "INITIALIZE_PASS_DEPENDENCY(DominatorTree)\n" + "class X {\n" + "};\n" + "INITIALIZE_PASS_END(ScopDetection, \"polly-detect\")\n" + "int *createScopDetectionPass() { return 0; }", + format(" INITIALIZE_PASS_BEGIN(ScopDetection, \"polly-detect\")\n" + " INITIALIZE_AG_DEPENDENCY(AliasAnalysis)\n" + " INITIALIZE_PASS_DEPENDENCY(DominatorTree)\n" + " class X {};\n" + " INITIALIZE_PASS_END(ScopDetection, \"polly-detect\")\n" + " int *createScopDetectionPass() { return 0; }")); + // FIXME: We could probably treat IPC_BEGIN_MESSAGE_MAP/IPC_END_MESSAGE_MAP as + // braces, so that inner block is indented one level more. + EXPECT_EQ("int q() {\n" + " IPC_BEGIN_MESSAGE_MAP(WebKitTestController, message)\n" + " IPC_MESSAGE_HANDLER(xxx, qqq)\n" + " IPC_END_MESSAGE_MAP()\n" + "}", + format("int q() {\n" + " IPC_BEGIN_MESSAGE_MAP(WebKitTestController, message)\n" + " IPC_MESSAGE_HANDLER(xxx, qqq)\n" + " IPC_END_MESSAGE_MAP()\n" + "}")); + EXPECT_EQ("int q() {\n" + " f(x);\n" + " f(x) {}\n" + " f(x)->g();\n" + " f(x)->*g();\n" + " f(x).g();\n" + " f(x) = x;\n" + " f(x) += x;\n" + " f(x) -= x;\n" + " f(x) *= x;\n" + " f(x) /= x;\n" + " f(x) %= x;\n" + " f(x) &= x;\n" + " f(x) |= x;\n" + " f(x) ^= x;\n" + " f(x) >>= x;\n" + " f(x) <<= x;\n" + " f(x)[y].z();\n" + " LOG(INFO) << x;\n" + " ifstream(x) >> x;\n" + "}\n", + format("int q() {\n" + " f(x)\n;\n" + " f(x)\n {}\n" + " f(x)\n->g();\n" + " f(x)\n->*g();\n" + " f(x)\n.g();\n" + " f(x)\n = x;\n" + " f(x)\n += x;\n" + " f(x)\n -= x;\n" + " f(x)\n *= x;\n" + " f(x)\n /= x;\n" + " f(x)\n %= x;\n" + " f(x)\n &= x;\n" + " f(x)\n |= x;\n" + " f(x)\n ^= x;\n" + " f(x)\n >>= x;\n" + " f(x)\n <<= x;\n" + " f(x)\n[y].z();\n" + " LOG(INFO)\n << x;\n" + " ifstream(x)\n >> x;\n" + "}\n")); + EXPECT_EQ("int q() {\n" + " f(x)\n" + " if (1) {\n" + " }\n" + " f(x)\n" + " while (1) {\n" + " }\n" + " f(x)\n" + " g(x);\n" + " f(x)\n" + " try {\n" + " q();\n" + " }\n" + " catch (...) {\n" + " }\n" + "}\n", + format("int q() {\n" + "f(x)\n" + "if (1) {}\n" + "f(x)\n" + "while (1) {}\n" + "f(x)\n" + "g(x);\n" + "f(x)\n" + "try { q(); } catch (...) {}\n" + "}\n")); + EXPECT_EQ("class A {\n" + " A() : t(0) {}\n" + " A(X x)\n" // FIXME: function-level try blocks are broken. + " try : t(0) {\n" + " }\n" + " catch (...) {\n" + " }\n" + "};", + format("class A {\n" + " A()\n : t(0) {}\n" + " A(X x)\n" + " try : t(0) {} catch (...) {}\n" + "};")); +} + TEST_F(FormatTest, IndentPreprocessorDirectivesAtZero) { EXPECT_EQ("{\n {\n#define A\n }\n}", format("{{\n#define A\n}}")); } @@ -1383,7 +1520,7 @@ TEST_F(FormatTest, MixingPreprocessorDirectivesAndNormalCode) { TEST_F(FormatTest, LayoutStatementsAroundPreprocessorDirectives) { EXPECT_EQ("int\n" "#define A\n" - "a;", + " a;", format("int\n#define A\na;")); verifyFormat("functionCallTo(\n" " someOtherFunction(\n" @@ -1469,6 +1606,30 @@ TEST_F(FormatTest, PreventConfusingIndents) { " ddd);"); } +TEST_F(FormatTest, ExpressionIndentation) { + verifyFormat("bool value = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +\n" + " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +\n" + " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ==\n" + " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa *\n" + " bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb +\n" + " bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb &&\n" + " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa *\n" + " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa >\n" + " ccccccccccccccccccccccccccccccccccccccccc;"); + verifyFormat("if (aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa *\n" + " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +\n" + " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ==\n" + " bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb) {\n}"); + verifyFormat("if (aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +\n" + " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa *\n" + " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ==\n" + " bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb) {\n}"); + verifyFormat("if (aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ==\n" + " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa *\n" + " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +\n" + " bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb) {\n}"); +} + TEST_F(FormatTest, ConstructorInitializers) { verifyFormat("Constructor() : Initializer(FitsOnTheLine) {}"); verifyFormat("Constructor() : Inttializer(FitsOnTheLine) {}", @@ -1538,8 +1699,34 @@ TEST_F(FormatTest, ConstructorInitializers) { " aaaaa(aaaaaa),\n" " aaaaa(aaaaaa) {}", OnePerLine); + verifyFormat("Constructor()\n" + " : aaaaa(aaaaaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaaaaaaaa,\n" + " aaaaaaaaaaaaaaaaaaaaaa) {}", + OnePerLine); +} + +TEST_F(FormatTest, MemoizationTests) { + // This breaks if the memoization lookup does not take \c Indent and + // \c LastSpace into account. + verifyFormat( + "extern CFRunLoopTimerRef\n" + "CFRunLoopTimerCreate(CFAllocatorRef allocato, CFAbsoluteTime fireDate,\n" + " CFTimeInterval interval, CFOptionFlags flags,\n" + " CFIndex order, CFRunLoopTimerCallBack callout,\n" + " CFRunLoopTimerContext *context) {}"); + + // Deep nesting somewhat works around our memoization. + verifyFormat( + "aaaaa(aaaaa(aaaaa(aaaaa(aaaaa(aaaaa(aaaaa(aaaaa(aaaaa(aaaaa(aaaaa(\n" + " aaaaa(aaaaa(aaaaa(aaaaa(aaaaa(aaaaa(aaaaa(aaaaa(aaaaa(aaaaa(\n" + " aaaaa(aaaaa(aaaaa(aaaaa(aaaaa(aaaaa(aaaaa(aaaaa(aaaaa(\n" + " aaaaa(aaaaa(aaaaa(aaaaa(aaaaa(aaaaa(aaaaa(aaaaa(aaaaa(\n" + " aaaaa())))))))))))))))))))))))))))))))))))))));", + getLLVMStyleWithColumns(65)); // This test takes VERY long when memoization is broken. + FormatStyle OnePerLine = getLLVMStyle(); + OnePerLine.ConstructorInitializerAllOnOneLineOrOnePerLine = true; OnePerLine.BinPackParameters = false; std::string input = "Constructor()\n" " : aaaa(a,\n"; @@ -1558,7 +1745,64 @@ TEST_F(FormatTest, BreaksAsHighAsPossible) { " f();\n" "}"); verifyFormat("if (Intervals[i].getRange().getFirst() <\n" - " Intervals[i - 1].getRange().getLast()) {\n}"); + " Intervals[i - 1].getRange().getLast()) {\n}"); +} + +TEST_F(FormatTest, BreaksFunctionDeclarations) { + // Principially, we break function declarations in a certain order: + // 1) break amongst arguments. + verifyFormat("Aaaaaaaaaaaaaa bbbbbbbbbbbbbb(Cccccccccccccc cccccccccccccc,\n" + " Cccccccccccccc cccccccccccccc);"); + + // 2) break after return type. + verifyFormat( + "Aaaaaaaaaaaaaaaaaaaaaaaa\n" + " bbbbbbbbbbbbbb(Cccccccccccccc cccccccccccccccccccccccccc);"); + + // 3) break after (. + verifyFormat( + "Aaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbb(\n" + " Cccccccccccccccccccccccccccccc cccccccccccccccccccccccccccccccc);"); + + // 4) break before after nested name specifiers. + verifyFormat( + "Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" + " SomeClasssssssssssssssssssssssssssssssssssssss::\n" + " bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb(Cccccccccccccc cccccccccc);"); + + // However, there are exceptions, if a sufficient amount of lines can be + // saved. + // FIXME: The precise cut-offs wrt. the number of saved lines might need some + // more adjusting. + verifyFormat("Aaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbb(Cccccccccccccc cccccccccc,\n" + " Cccccccccccccc cccccccccc,\n" + " Cccccccccccccc cccccccccc,\n" + " Cccccccccccccc cccccccccc,\n" + " Cccccccccccccc cccccccccc);"); + verifyFormat( + "Aaaaaaaaaaaaaaaaaa\n" + " bbbbbbbbbbb(Cccccccccccccc cccccccccc, Cccccccccccccc cccccccccc,\n" + " Cccccccccccccc cccccccccc, Cccccccccccccc cccccccccc,\n" + " Cccccccccccccc cccccccccc, Cccccccccccccc cccccccccc);"); + verifyFormat( + "Aaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb(Cccccccccccccc cccccccccc,\n" + " Cccccccccccccc cccccccccc,\n" + " Cccccccccccccc cccccccccc,\n" + " Cccccccccccccc cccccccccc,\n" + " Cccccccccccccc cccccccccc,\n" + " Cccccccccccccc cccccccccc,\n" + " Cccccccccccccc cccccccccc);"); + verifyFormat("Aaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb(\n" + " Cccccccccccccc cccccccccc, Cccccccccccccc cccccccccc,\n" + " Cccccccccccccc cccccccccc, Cccccccccccccc cccccccccc,\n" + " Cccccccccccccc cccccccccc, Cccccccccccccc cccccccccc,\n" + " Cccccccccccccc cccccccccc, Cccccccccccccc cccccccccc);"); + + // Break after multi-line parameters. + verifyFormat("void aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(\n" + " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" + " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,\n" + " bbbb bbbb);"); } TEST_F(FormatTest, BreaksDesireably) { @@ -1681,9 +1925,9 @@ TEST_F(FormatTest, FormatsBuilderPattern) { " .StartsWith(\".eh_frame\", ORDER_EH_FRAME).StartsWith(\".init\", ORDER_INIT)\n" " .StartsWith(\".fini\", ORDER_FINI).StartsWith(\".hash\", ORDER_HASH)\n" " .Default(ORDER_TEXT);\n"); - + verifyFormat("return aaaaaaaaaaaaaaaaa->aaaaa().aaaaaaaaaaaaa().aaaaaa() <\n" - " aaaaaaaaaaaaaaa->aaaaa().aaaaaaaaaaaaa().aaaaaa();"); + " aaaaaaaaaaaaaaa->aaaaa().aaaaaaaaaaaaa().aaaaaa();"); verifyFormat( "aaaaaaa->aaaaaaa\n" " ->aaaaaaaaaaaaaaaa(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa)\n" @@ -1711,12 +1955,9 @@ TEST_F(FormatTest, DoesNotBreakTrailingAnnotation) { " aaaaaaaaaaaaaaaaaaaaaaaaa));"); verifyFormat("bool aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" " __attribute__((unused));"); - - // FIXME: This is bad indentation, but generally hard to distinguish from a - // function declaration. verifyFormat( "bool aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" - "GUARDED_BY(aaaaaaaaaaaa);"); + " GUARDED_BY(aaaaaaaaaaaa);"); } TEST_F(FormatTest, BreaksAccordingToOperatorPrecedence) { @@ -1780,10 +2021,15 @@ TEST_F(FormatTest, AlignsAfterReturn) { " aaaaaaaaaaaaaaaaaaaaaaaaa);"); verifyFormat( "return aaaaaaaaaaaaaaaaaaaaaa + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa >=\n" - " aaaaaaaaaaaaaaaaaaaaaa();"); + " aaaaaaaaaaaaaaaaaaaaaa();"); verifyFormat( "return (aaaaaaaaaaaaaaaaaaaaaa + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa >=\n" - " aaaaaaaaaaaaaaaaaaaaaa());"); + " aaaaaaaaaaaaaaaaaaaaaa());"); + verifyFormat("return aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(\n" + " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa);"); + verifyFormat("return aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(\n" + " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa) &&\n" + " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa;"); } TEST_F(FormatTest, BreaksConditionalExpressions) { @@ -1827,7 +2073,7 @@ TEST_F(FormatTest, BreaksConditionalExpressions) { " ? aaaaaaaaaaaaaaa\n" " : aaaaaaaaaaaaaaa;"); verifyFormat("f(aaaaaaaaaaaaaaaa == // force break\n" - " aaaaaaaaa\n" + " aaaaaaaaa\n" " ? b\n" " : c);"); verifyFormat( @@ -1873,8 +2119,8 @@ TEST_F(FormatTest, DeclarationsOfMultipleVariables) { // FIXME: If multiple variables are defined, the "*" needs to move to the new // line. Also fix indent for breaking after the type, this looks bad. verifyFormat("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa *\n" - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaa = aaaaaaaaaaaaaaaaaaa,\n" - " *b = bbbbbbbbbbbbbbbbbbb;"); + " aaaaaaaaaaaaaaaaaaaaaaaaaaaaa = aaaaaaaaaaaaaaaaaaa,\n" + " *b = bbbbbbbbbbbbbbbbbbb;"); // Not ideal, but pointer-with-type does not allow much here. verifyGoogleFormat( @@ -2226,6 +2472,11 @@ TEST_F(FormatTest, UndestandsOverloadedOperators) { verifyFormat( "ostream &operator<<(ostream &OutputStream,\n" " SomeReallyLongType WithSomeReallyLongValue);"); + verifyFormat("bool operator<(const aaaaaaaaaaaaaaaaaaaaa &left,\n" + " const aaaaaaaaaaaaaaaaaaaaa &right) {\n" + " return left.group < right.group;\n" + "}"); + verifyFormat("SomeType &operator=(const SomeType &S);"); verifyGoogleFormat("operator void*();"); verifyGoogleFormat("operator SomeType>();"); @@ -2295,6 +2546,7 @@ TEST_F(FormatTest, UnderstandsUsesOfStarAndAmp) { verifyIndependentOfContext("A a;"); verifyIndependentOfContext("void f(int *a = d * e, int *b = c * d);"); verifyFormat("for (char **a = b; *a; ++a) {\n}"); + verifyFormat("for (; a && b;) {\n}"); verifyFormat( "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(\n" @@ -2348,6 +2600,11 @@ TEST_F(FormatTest, UnderstandsUsesOfStarAndAmp) { verifyGoogleFormat("A = new SomeType* [Length];"); } +TEST_F(FormatTest, UnderstandsEllipsis) { + verifyFormat("int printf(const char *fmt, ...);"); + verifyFormat("template void Foo(Ts... ts) { Foo(ts...); }"); +} + TEST_F(FormatTest, AdaptivelyFormatsPointersAndReferences) { EXPECT_EQ("int *a;\n" "int *a;\n" @@ -2390,7 +2647,7 @@ TEST_F(FormatTest, UnderstandsRvalueReferences) { TEST_F(FormatTest, FormatsBinaryOperatorsPrecedingEquals) { verifyFormat("void f() {\n" " x[aaaaaaaaa -\n" - " b] = 23;\n" + " b] = 23;\n" "}", getLLVMStyleWithColumns(15)); } @@ -2448,12 +2705,25 @@ TEST_F(FormatTest, FormatsFunctionTypes) { } TEST_F(FormatTest, BreaksLongDeclarations) { + verifyFormat("typedef LoooooooooooooooooooooooooooooooooooooooongType\n" + " AnotherNameForTheLongType;"); + verifyFormat("LoooooooooooooooooooooooooooooooooooooooongType\n" + " LoooooooooooooooooooooooooooooooooooooooongVariable;"); + verifyFormat("LoooooooooooooooooooooooooooooooooooooooongReturnType\n" + " LoooooooooooooooooooooooooooooooongFunctionDeclaration();"); + verifyFormat("LoooooooooooooooooooooooooooooooooooooooongReturnType\n" + "LooooooooooooooooooooooooooooooooooongFunctionDefinition() {}"); + + // FIXME: Without the comment, this breaks after "(". + verifyFormat("LoooooooooooooooooooooooooooooooooooooooongType // break\n" + " (*LoooooooooooooooooooooooooooongFunctionTypeVarialbe)();"); + verifyFormat("int *someFunction(int LoooooooooooooooooooongParam1,\n" " int LoooooooooooooooooooongParam2) {}"); verifyFormat( - "TypeSpecDecl *\n" - "TypeSpecDecl::Create(ASTContext &C, DeclContext *DC, SourceLocation L,\n" - " IdentifierIn *II, Type *T) {}"); + "TypeSpecDecl *TypeSpecDecl::Create(ASTContext &C, DeclContext *DC,\n" + " SourceLocation L, IdentifierIn *II,\n" + " Type *T) {}"); verifyFormat("ReallyLongReturnType\n" "ReallyReallyLongFunctionName(\n" " const std::string &SomeParameter,\n" @@ -2463,7 +2733,7 @@ TEST_F(FormatTest, BreaksLongDeclarations) { " AnotherLongParameterName) {}"); verifyFormat( "aaaaaaaaaaaaaaaa::aaaaaaaaaaaaaaaa\n" - "aaaaaaaaaaaaaaaaaaaaaaa;"); + " aaaaaaaaaaaaaaaaaaaaaaa;"); verifyGoogleFormat( "TypeSpecDecl* TypeSpecDecl::Create(ASTContext& C, DeclContext* DC,\n" @@ -2493,6 +2763,7 @@ TEST_F(FormatTest, HandlesIncludeDirectives) { "#include \"string.h\"\n" "#include \n" "#include < path with space >\n" + "#include \"abc.h\" // this is included for ABC\n" "#include \"some very long include paaaaaaaaaaaaaaaaaaaaaaath\"", getLLVMStyleWithColumns(35)); @@ -2723,6 +2994,17 @@ TEST_F(FormatTest, DoNotInterfereWithErrorAndWarning) { EXPECT_EQ("#warning 1", format(" # warning 1")); } +TEST_F(FormatTest, FormatHashIfExpressions) { + // FIXME: Come up with a better indentation for #elif. + verifyFormat( + "#if !defined(AAAAAAA) && (defined CCCCCC || defined DDDDDD) && \\\n" + " defined(BBBBBBBB)\n" + "#elif !defined(AAAAAA) && (defined CCCCC || defined DDDDDD) && \\\n" + " defined(BBBBBBBB)\n" + "#endif", + getLLVMStyleWithColumns(65)); +} + TEST_F(FormatTest, MergeHandlingInTheFaceOfPreprocessorDirectives) { FormatStyle AllowsMergedIf = getGoogleStyle(); AllowsMergedIf.AllowShortIfStatementsOnASingleLine = true; @@ -2733,16 +3015,16 @@ TEST_F(FormatTest, MergeHandlingInTheFaceOfPreprocessorDirectives) { format("if (true)\nreturn 42;", AllowsMergedIf)); FormatStyle ShortMergedIf = AllowsMergedIf; ShortMergedIf.ColumnLimit = 25; - verifyFormat("#define A \\\n" + verifyFormat("#define A \\\n" " if (true) return 42;", ShortMergedIf); - verifyFormat("#define A \\\n" - " f(); \\\n" + verifyFormat("#define A \\\n" + " f(); \\\n" " if (true)\n" "#define B", ShortMergedIf); - verifyFormat("#define A \\\n" - " f(); \\\n" + verifyFormat("#define A \\\n" + " f(); \\\n" " if (true)\n" "g();", ShortMergedIf); @@ -3364,8 +3646,18 @@ TEST_F(FormatTest, ObjCLiterals) { verifyFormat("@{ @\"one\" : @1 }"); verifyFormat("return @{ @\"one\" : @1 };"); verifyFormat("@{ @\"one\" : @1, }"); - verifyFormat("@{ @\"one\" : @{ @2 : @1 } }"); - verifyFormat("@{ @\"one\" : @{ @2 : @1 }, }"); + + // FIXME: Breaking in cases where we think there's a structural error + // showed that we're incorrectly parsing this code. We need to fix the + // parsing here. + verifyFormat("@{ @\"one\" : @\n" + "{ @2 : @1 }\n" + "}"); + verifyFormat("@{ @\"one\" : @\n" + "{ @2 : @1 }\n" + ",\n" + "}"); + verifyFormat("@{ 1 > 2 ? @\"one\" : @\"two\" : 1 > 2 ? @1 : @2 }"); verifyFormat("[self setDict:@{}"); verifyFormat("[self setDict:@{ @1 : @2 }"); @@ -3463,6 +3755,9 @@ TEST_F(FormatTest, BreakStringLiterals) { EXPECT_EQ("\"some text \"\n" "\"other\";", format("\"some text other\";", getLLVMStyleWithColumns(12))); + EXPECT_EQ("\"some text \"\n" + "\"other\";", + format("\\\n\"some text other\";", getLLVMStyleWithColumns(12))); EXPECT_EQ( "#define A \\\n" " \"some \" \\\n" @@ -3487,7 +3782,8 @@ TEST_F(FormatTest, BreakStringLiterals) { "\"text\"", format("\"some text\"", getLLVMStyleWithColumns(7))); EXPECT_EQ("\"some\"\n" - "\" text\"", + "\" tex\"\n" + "\"t\"", format("\"some text\"", getLLVMStyleWithColumns(6))); EXPECT_EQ("\"some\"\n" "\" tex\"\n" @@ -3545,6 +3841,15 @@ TEST_F(FormatTest, BreakStringLiterals) { "\"pathat/\"\n" "\"slashes\"", format("\"split/pathat/slashes\"", getLLVMStyleWithColumns(10))); + + FormatStyle AlignLeft = getLLVMStyleWithColumns(12); + AlignLeft.AlignEscapedNewlinesLeft = true; + EXPECT_EQ( + "#define A \\\n" + " \"some \" \\\n" + " \"text \" \\\n" + " \"other\";", + format("#define A \"some text other\";", AlignLeft)); } TEST_F(FormatTest, DoNotBreakStringLiteralsInEscapeSequence) { @@ -3580,11 +3885,26 @@ TEST_F(FormatTest, DoNotBreakStringLiteralsInEscapeSequence) { "\"000001\"", format("\"test\\000000000001\"", getLLVMStyleWithColumns(9))); EXPECT_EQ("\"test\\000\"\n" - "\"000000001\"", + "\"00000000\"\n" + "\"1\"", format("\"test\\000000000001\"", getLLVMStyleWithColumns(10))); EXPECT_EQ("R\"(\\x\\x00)\"\n", format("R\"(\\x\\x00)\"\n", getLLVMStyleWithColumns(7))); } +TEST_F(FormatTest, DoNotCreateUnreasonableUnwrappedLines) { + verifyFormat("void f() {\n" + " return g() {}\n" + " void h() {}"); + verifyFormat("if (foo)\n" + " return { forgot_closing_brace();\n" + "test();"); + verifyFormat("int a[] = { void forgot_closing_brace()\n" + "{\n" + " f();\n" + " g();\n" + "}"); +} + } // end namespace tooling } // end namespace clang diff --git a/utils/TableGen/ClangAttrEmitter.cpp b/utils/TableGen/ClangAttrEmitter.cpp index 7c8603fc6c3..eaf10a64a4f 100644 --- a/utils/TableGen/ClangAttrEmitter.cpp +++ b/utils/TableGen/ClangAttrEmitter.cpp @@ -971,6 +971,48 @@ void EmitClangAttrClass(RecordKeeper &Records, raw_ostream &OS) { OS << "#endif\n"; } +// Emits the all-arguments-are-expressions property for attributes. +void EmitClangAttrExprArgsList(RecordKeeper &Records, raw_ostream &OS) { + emitSourceFileHeader("llvm::StringSwitch code to match attributes with " + "expression arguments", OS); + + std::vector Attrs = Records.getAllDerivedDefinitions("Attr"); + + for (std::vector::iterator I = Attrs.begin(), E = Attrs.end(); + I != E; ++I) { + Record &Attr = **I; + + // Determine whether the first argument is something that is always + // an expression. + std::vector Args = Attr.getValueAsListOfDefs("Args"); + if (Args.empty() || Args[0]->getSuperClasses().empty()) + continue; + + // Check whether this is one of the argument kinds that implies an + // expression. + // FIXME: Aligned is weird. + if (!llvm::StringSwitch(Args[0]->getSuperClasses().back()->getName()) + .Case("AlignedArgument", true) + .Case("BoolArgument", true) + .Case("DefaultIntArgument", true) + .Case("IntArgument", true) + .Case("ExprArgument", true) + .Case("UnsignedArgument", true) + .Case("VariadicUnsignedArgument", true) + .Case("VariadicExprArgument", true) + .Default(false)) + continue; + + std::vector Spellings = Attr.getValueAsListOfDefs("Spellings"); + + for (std::vector::const_iterator I = Spellings.begin(), + E = Spellings.end(); I != E; ++I) { + OS << ".Case(\"" << (*I)->getValueAsString("Name") << "\", " + << "true" << ")\n"; + } + } +} + // Emits the class method definitions for attributes. void EmitClangAttrImpl(RecordKeeper &Records, raw_ostream &OS) { emitSourceFileHeader("Attribute classes' member function definitions", OS); diff --git a/utils/TableGen/ClangCommentCommandInfoEmitter.cpp b/utils/TableGen/ClangCommentCommandInfoEmitter.cpp index ebb0427d7c5..cab1c2b9b26 100644 --- a/utils/TableGen/ClangCommentCommandInfoEmitter.cpp +++ b/utils/TableGen/ClangCommentCommandInfoEmitter.cpp @@ -97,6 +97,9 @@ static std::string MangleName(StringRef Str) { case '$': Mangled += "dollar"; break; + case '/': + Mangled += "slash"; + break; } } return Mangled; diff --git a/utils/TableGen/ClangDiagnosticsEmitter.cpp b/utils/TableGen/ClangDiagnosticsEmitter.cpp index 291eb754091..da15c934359 100644 --- a/utils/TableGen/ClangDiagnosticsEmitter.cpp +++ b/utils/TableGen/ClangDiagnosticsEmitter.cpp @@ -245,7 +245,7 @@ static void groupDiagnostics(const std::vector &Diags, SourceMgr::DK_Error, Twine("group '") + Name + "' is referred to anonymously", - ArrayRef(), + None, InGroupRange.isValid() ? FixIt : ArrayRef()); SrcMgr.PrintMessage((*I)->ExplicitDef->getLoc().front(), diff --git a/utils/TableGen/NeonEmitter.cpp b/utils/TableGen/NeonEmitter.cpp index d453ededd5e..34b955e8e9d 100644 --- a/utils/TableGen/NeonEmitter.cpp +++ b/utils/TableGen/NeonEmitter.cpp @@ -98,7 +98,12 @@ enum ClassKind { ClassI, // generic integer instruction, e.g., "i8" suffix ClassS, // signed/unsigned/poly, e.g., "s8", "u8" or "p8" suffix ClassW, // width-specific instruction, e.g., "8" suffix - ClassB // bitcast arguments with enum argument to specify type + ClassB, // bitcast arguments with enum argument to specify type + ClassL, // Logical instructions which are op instructions + // but we need to not emit any suffix for in our + // tests. + ClassNoTest // Instructions which we do not test since they are + // not TRUE instructions. }; /// NeonTypeFlags - Flags to identify the types for overloaded Neon @@ -204,9 +209,20 @@ class NeonEmitter { Record *SI = R.getClass("SInst"); Record *II = R.getClass("IInst"); Record *WI = R.getClass("WInst"); + Record *SOpI = R.getClass("SOpInst"); + Record *IOpI = R.getClass("IOpInst"); + Record *WOpI = R.getClass("WOpInst"); + Record *LOpI = R.getClass("LOpInst"); + Record *NoTestOpI = R.getClass("NoTestOpInst"); + ClassMap[SI] = ClassS; ClassMap[II] = ClassI; ClassMap[WI] = ClassW; + ClassMap[SOpI] = ClassS; + ClassMap[IOpI] = ClassI; + ClassMap[WOpI] = ClassW; + ClassMap[LOpI] = ClassL; + ClassMap[NoTestOpI] = ClassNoTest; } // run - Emit arm_neon.h.inc @@ -572,73 +588,89 @@ static std::string BuiltinTypeString(const char mod, StringRef typestr, return quad ? "V16Sc" : "V8Sc"; } -/// MangleName - Append a type or width suffix to a base neon function name, -/// and insert a 'q' in the appropriate location if the operation works on -/// 128b rather than 64b. E.g. turn "vst2_lane" into "vst2q_lane_f32", etc. -static std::string MangleName(const std::string &name, StringRef typestr, - ClassKind ck) { - if (name == "vcvt_f32_f16") - return name; - - bool quad = false; +/// InstructionTypeCode - Computes the ARM argument character code and +/// quad status for a specific type string and ClassKind. +static void InstructionTypeCode(const StringRef &typeStr, + const ClassKind ck, + bool &quad, + std::string &typeCode) { bool poly = false; bool usgn = false; - char type = ClassifyType(typestr, quad, poly, usgn); - - std::string s = name; + char type = ClassifyType(typeStr, quad, poly, usgn); switch (type) { case 'c': switch (ck) { - case ClassS: s += poly ? "_p8" : usgn ? "_u8" : "_s8"; break; - case ClassI: s += "_i8"; break; - case ClassW: s += "_8"; break; + case ClassS: typeCode = poly ? "p8" : usgn ? "u8" : "s8"; break; + case ClassI: typeCode = "i8"; break; + case ClassW: typeCode = "8"; break; default: break; } break; case 's': switch (ck) { - case ClassS: s += poly ? "_p16" : usgn ? "_u16" : "_s16"; break; - case ClassI: s += "_i16"; break; - case ClassW: s += "_16"; break; + case ClassS: typeCode = poly ? "p16" : usgn ? "u16" : "s16"; break; + case ClassI: typeCode = "i16"; break; + case ClassW: typeCode = "16"; break; default: break; } break; case 'i': switch (ck) { - case ClassS: s += usgn ? "_u32" : "_s32"; break; - case ClassI: s += "_i32"; break; - case ClassW: s += "_32"; break; + case ClassS: typeCode = usgn ? "u32" : "s32"; break; + case ClassI: typeCode = "i32"; break; + case ClassW: typeCode = "32"; break; default: break; } break; case 'l': switch (ck) { - case ClassS: s += usgn ? "_u64" : "_s64"; break; - case ClassI: s += "_i64"; break; - case ClassW: s += "_64"; break; + case ClassS: typeCode = usgn ? "u64" : "s64"; break; + case ClassI: typeCode = "i64"; break; + case ClassW: typeCode = "64"; break; default: break; } break; case 'h': switch (ck) { case ClassS: - case ClassI: s += "_f16"; break; - case ClassW: s += "_16"; break; + case ClassI: typeCode = "f16"; break; + case ClassW: typeCode = "16"; break; default: break; } break; case 'f': switch (ck) { case ClassS: - case ClassI: s += "_f32"; break; - case ClassW: s += "_32"; break; + case ClassI: typeCode = "f32"; break; + case ClassW: typeCode = "32"; break; default: break; } break; default: PrintFatalError("unhandled type!"); } +} + +/// MangleName - Append a type or width suffix to a base neon function name, +/// and insert a 'q' in the appropriate location if the operation works on +/// 128b rather than 64b. E.g. turn "vst2_lane" into "vst2q_lane_f32", etc. +static std::string MangleName(const std::string &name, StringRef typestr, + ClassKind ck) { + if (name == "vcvt_f32_f16") + return name; + + bool quad = false; + std::string typeCode = ""; + + InstructionTypeCode(typestr, ck, quad, typeCode); + + std::string s = name; + + if (typeCode.size() > 0) { + s += "_" + typeCode; + } + if (ck == ClassB) s += "_v"; @@ -648,9 +680,457 @@ static std::string MangleName(const std::string &name, StringRef typestr, size_t pos = s.find('_'); s = s.insert(pos, "q"); } + return s; } +static void PreprocessInstruction(const StringRef &Name, + const std::string &InstName, + std::string &Prefix, + bool &HasNPostfix, + bool &HasLanePostfix, + bool &HasDupPostfix, + bool &IsSpecialVCvt, + size_t &TBNumber) { + // All of our instruction name fields from arm_neon.td are of the form + // _... + // Thus we grab our instruction name via computation of said Prefix. + const size_t PrefixEnd = Name.find_first_of('_'); + // If InstName is passed in, we use that instead of our name Prefix. + Prefix = InstName.size() == 0? Name.slice(0, PrefixEnd).str() : InstName; + + const StringRef Postfix = Name.slice(PrefixEnd, Name.size()); + + HasNPostfix = Postfix.count("_n"); + HasLanePostfix = Postfix.count("_lane"); + HasDupPostfix = Postfix.count("_dup"); + IsSpecialVCvt = Postfix.size() != 0 && Name.count("vcvt"); + + if (InstName.compare("vtbl") == 0 || + InstName.compare("vtbx") == 0) { + // If we have a vtblN/vtbxN instruction, use the instruction's ASCII + // encoding to get its true value. + TBNumber = Name[Name.size()-1] - 48; + } +} + +/// GenerateRegisterCheckPatternsForLoadStores - Given a bunch of data we have +/// extracted, generate a FileCheck pattern for a Load Or Store +static void +GenerateRegisterCheckPatternForLoadStores(const StringRef &NameRef, + const std::string& OutTypeCode, + const bool &IsQuad, + const bool &HasDupPostfix, + const bool &HasLanePostfix, + const size_t Count, + std::string &RegisterSuffix) { + const bool IsLDSTOne = NameRef.count("vld1") || NameRef.count("vst1"); + // If N == 3 || N == 4 and we are dealing with a quad instruction, Clang + // will output a series of v{ld,st}1s, so we have to handle it specially. + if ((Count == 3 || Count == 4) && IsQuad) { + RegisterSuffix += "{"; + for (size_t i = 0; i < Count; i++) { + RegisterSuffix += "d{{[0-9]+}}"; + if (HasDupPostfix) { + RegisterSuffix += "[]"; + } + if (HasLanePostfix) { + RegisterSuffix += "[{{[0-9]+}}]"; + } + if (i < Count-1) { + RegisterSuffix += ", "; + } + } + RegisterSuffix += "}"; + } else { + + // Handle normal loads and stores. + RegisterSuffix += "{"; + for (size_t i = 0; i < Count; i++) { + RegisterSuffix += "d{{[0-9]+}}"; + if (HasDupPostfix) { + RegisterSuffix += "[]"; + } + if (HasLanePostfix) { + RegisterSuffix += "[{{[0-9]+}}]"; + } + if (IsQuad && !HasLanePostfix) { + RegisterSuffix += ", d{{[0-9]+}}"; + if (HasDupPostfix) { + RegisterSuffix += "[]"; + } + } + if (i < Count-1) { + RegisterSuffix += ", "; + } + } + RegisterSuffix += "}, [r{{[0-9]+}}"; + + // We only include the alignment hint if we have a vld1.*64 or + // a dup/lane instruction. + if (IsLDSTOne) { + if ((HasLanePostfix || HasDupPostfix) && OutTypeCode != "8") { + RegisterSuffix += ", :" + OutTypeCode; + } else if (OutTypeCode == "64") { + RegisterSuffix += ", :64"; + } + } + + RegisterSuffix += "]"; + } +} + +static bool HasNPostfixAndScalarArgs(const StringRef &NameRef, + const bool &HasNPostfix) { + return (NameRef.count("vmla") || + NameRef.count("vmlal") || + NameRef.count("vmlsl") || + NameRef.count("vmull") || + NameRef.count("vqdmlal") || + NameRef.count("vqdmlsl") || + NameRef.count("vqdmulh") || + NameRef.count("vqdmull") || + NameRef.count("vqrdmulh")) && HasNPostfix; +} + +static bool IsFiveOperandLaneAccumulator(const StringRef &NameRef, + const bool &HasLanePostfix) { + return (NameRef.count("vmla") || + NameRef.count("vmls") || + NameRef.count("vmlal") || + NameRef.count("vmlsl") || + (NameRef.count("vmul") && NameRef.size() == 3)|| + NameRef.count("vqdmlal") || + NameRef.count("vqdmlsl") || + NameRef.count("vqdmulh") || + NameRef.count("vqrdmulh")) && HasLanePostfix; +} + +static bool IsSpecialLaneMultiply(const StringRef &NameRef, + const bool &HasLanePostfix, + const bool &IsQuad) { + const bool IsVMulOrMulh = (NameRef.count("vmul") || NameRef.count("mulh")) + && IsQuad; + const bool IsVMull = NameRef.count("mull") && !IsQuad; + return (IsVMulOrMulh || IsVMull) && HasLanePostfix; +} + +static void NormalizeProtoForRegisterPatternCreation(const std::string &Name, + const std::string &Proto, + const bool &HasNPostfix, + const bool &IsQuad, + const bool &HasLanePostfix, + const bool &HasDupPostfix, + std::string &NormedProto) { + // Handle generic case. + const StringRef NameRef(Name); + for (size_t i = 0, end = Proto.size(); i < end; i++) { + switch (Proto[i]) { + case 'u': + case 'f': + case 'd': + case 's': + case 'x': + case 't': + case 'n': + NormedProto += IsQuad? 'q' : 'd'; + break; + case 'w': + case 'k': + NormedProto += 'q'; + break; + case 'g': + case 'h': + case 'e': + NormedProto += 'd'; + break; + case 'i': + NormedProto += HasLanePostfix? 'a' : 'i'; + break; + case 'a': + if (HasLanePostfix) { + NormedProto += 'a'; + } else if (HasNPostfixAndScalarArgs(NameRef, HasNPostfix)) { + NormedProto += IsQuad? 'q' : 'd'; + } else { + NormedProto += 'i'; + } + break; + } + } + + // Handle Special Cases. + const bool IsNotVExt = !NameRef.count("vext"); + const bool IsVPADAL = NameRef.count("vpadal"); + const bool Is5OpLaneAccum = IsFiveOperandLaneAccumulator(NameRef, + HasLanePostfix); + const bool IsSpecialLaneMul = IsSpecialLaneMultiply(NameRef, HasLanePostfix, + IsQuad); + + if (IsSpecialLaneMul) { + // If + NormedProto[2] = NormedProto[3]; + NormedProto.erase(3); + } else if (NormedProto.size() == 4 && + NormedProto[0] == NormedProto[1] && + IsNotVExt) { + // If NormedProto.size() == 4 and the first two proto characters are the + // same, ignore the first. + NormedProto = NormedProto.substr(1, 3); + } else if (Is5OpLaneAccum) { + // If we have a 5 op lane accumulator operation, we take characters 1,2,4 + std::string tmp = NormedProto.substr(1,2); + tmp += NormedProto[4]; + NormedProto = tmp; + } else if (IsVPADAL) { + // If we have VPADAL, ignore the first character. + NormedProto = NormedProto.substr(0, 2); + } else if (NameRef.count("vdup") && NormedProto.size() > 2) { + // If our instruction is a dup instruction, keep only the first and + // last characters. + std::string tmp = ""; + tmp += NormedProto[0]; + tmp += NormedProto[NormedProto.size()-1]; + NormedProto = tmp; + } +} + +/// GenerateRegisterCheckPatterns - Given a bunch of data we have +/// extracted, generate a FileCheck pattern to check that an +/// instruction's arguments are correct. +static void GenerateRegisterCheckPattern(const std::string &Name, + const std::string &Proto, + const std::string &OutTypeCode, + const bool &HasNPostfix, + const bool &IsQuad, + const bool &HasLanePostfix, + const bool &HasDupPostfix, + const size_t &TBNumber, + std::string &RegisterSuffix) { + + RegisterSuffix = ""; + + const StringRef NameRef(Name); + const StringRef ProtoRef(Proto); + + if ((NameRef.count("vdup") || NameRef.count("vmov")) && HasNPostfix) { + return; + } + + const bool IsLoadStore = NameRef.count("vld") || NameRef.count("vst"); + const bool IsTBXOrTBL = NameRef.count("vtbl") || NameRef.count("vtbx"); + + if (IsLoadStore) { + // Grab N value from v{ld,st}N using its ascii representation. + const size_t Count = NameRef[3] - 48; + + GenerateRegisterCheckPatternForLoadStores(NameRef, OutTypeCode, IsQuad, + HasDupPostfix, HasLanePostfix, + Count, RegisterSuffix); + } else if (IsTBXOrTBL) { + RegisterSuffix += "d{{[0-9]+}}, {"; + for (size_t i = 0; i < TBNumber-1; i++) { + RegisterSuffix += "d{{[0-9]+}}, "; + } + RegisterSuffix += "d{{[0-9]+}}}, d{{[0-9]+}}"; + } else { + // Handle a normal instruction. + if (NameRef.count("vget") || NameRef.count("vset")) + return; + + // We first normalize our proto, since we only need to emit 4 + // different types of checks, yet have more than 4 proto types + // that map onto those 4 patterns. + std::string NormalizedProto(""); + NormalizeProtoForRegisterPatternCreation(Name, Proto, HasNPostfix, IsQuad, + HasLanePostfix, HasDupPostfix, + NormalizedProto); + + for (size_t i = 0, end = NormalizedProto.size(); i < end; i++) { + const char &c = NormalizedProto[i]; + switch (c) { + case 'q': + RegisterSuffix += "q{{[0-9]+}}, "; + break; + + case 'd': + RegisterSuffix += "d{{[0-9]+}}, "; + break; + + case 'i': + RegisterSuffix += "#{{[0-9]+}}, "; + break; + + case 'a': + RegisterSuffix += "d{{[0-9]+}}[{{[0-9]}}], "; + break; + } + } + + // Remove extra ", ". + RegisterSuffix = RegisterSuffix.substr(0, RegisterSuffix.size()-2); + } +} + +/// GenerateChecksForIntrinsic - Given a specific instruction name + +/// typestr + class kind, generate the proper set of FileCheck +/// Patterns to check for. We could just return a string, but instead +/// use a vector since it provides us with the extra flexibility of +/// emitting multiple checks, which comes in handy for certain cases +/// like mla where we want to check for 2 different instructions. +static void GenerateChecksForIntrinsic(const std::string &Name, + const std::string &Proto, + StringRef &OutTypeStr, + StringRef &InTypeStr, + ClassKind Ck, + const std::string &InstName, + bool IsHiddenLOp, + std::vector& Result) { + + // If Ck is a ClassNoTest instruction, just return so no test is + // emitted. + if(Ck == ClassNoTest) + return; + + if (Name == "vcvt_f32_f16") { + Result.push_back("vcvt.f32.f16"); + return; + } + + + // Now we preprocess our instruction given the data we have to get the + // data that we need. + // Create a StringRef for String Manipulation of our Name. + const StringRef NameRef(Name); + // Instruction Prefix. + std::string Prefix; + // The type code for our out type string. + std::string OutTypeCode; + // To handle our different cases, we need to check for different postfixes. + // Is our instruction a quad instruction. + bool IsQuad = false; + // Our instruction is of the form _n. + bool HasNPostfix = false; + // Our instruction is of the form _lane. + bool HasLanePostfix = false; + // Our instruction is of the form _dup. + bool HasDupPostfix = false; + // Our instruction is a vcvt instruction which requires special handling. + bool IsSpecialVCvt = false; + // If we have a vtbxN or vtblN instruction, this is set to N. + size_t TBNumber = -1; + // Register Suffix + std::string RegisterSuffix; + + PreprocessInstruction(NameRef, InstName, Prefix, + HasNPostfix, HasLanePostfix, HasDupPostfix, + IsSpecialVCvt, TBNumber); + + InstructionTypeCode(OutTypeStr, Ck, IsQuad, OutTypeCode); + GenerateRegisterCheckPattern(Name, Proto, OutTypeCode, HasNPostfix, IsQuad, + HasLanePostfix, HasDupPostfix, TBNumber, + RegisterSuffix); + + // In the following section, we handle a bunch of special cases. You can tell + // a special case by the fact we are returning early. + + // If our instruction is a logical instruction without postfix or a + // hidden LOp just return the current Prefix. + if (Ck == ClassL || IsHiddenLOp) { + Result.push_back(Prefix + " " + RegisterSuffix); + return; + } + + // If we have a vmov, due to the many different cases, some of which + // vary within the different intrinsics generated for a single + // instruction type, just output a vmov. (e.g. given an instruction + // A, A.u32 might be vmov and A.u8 might be vmov.8). + // + // FIXME: Maybe something can be done about this. The two cases that we care + // about are vmov as an LType and vmov as a WType. + if (Prefix == "vmov") { + Result.push_back(Prefix + " " + RegisterSuffix); + return; + } + + // In the following section, we handle special cases. + + if (OutTypeCode == "64") { + // If we have a 64 bit vdup/vext and are handling an uint64x1_t + // type, the intrinsic will be optimized away, so just return + // nothing. On the other hand if we are handling an uint64x2_t + // (i.e. quad instruction), vdup/vmov instructions should be + // emitted. + if (Prefix == "vdup" || Prefix == "vext") { + if (IsQuad) { + Result.push_back("{{vmov|vdup}}"); + } + return; + } + + // v{st,ld}{2,3,4}_{u,s}64 emit v{st,ld}1.64 instructions with + // multiple register operands. + bool MultiLoadPrefix = Prefix == "vld2" || Prefix == "vld3" + || Prefix == "vld4"; + bool MultiStorePrefix = Prefix == "vst2" || Prefix == "vst3" + || Prefix == "vst4"; + if (MultiLoadPrefix || MultiStorePrefix) { + Result.push_back(NameRef.slice(0, 3).str() + "1.64"); + return; + } + + // v{st,ld}1_{lane,dup}_{u64,s64} use vldr/vstr/vmov/str instead of + // emitting said instructions. So return a check for + // vldr/vstr/vmov/str instead. + if (HasLanePostfix || HasDupPostfix) { + if (Prefix == "vst1") { + Result.push_back("{{str|vstr|vmov}}"); + return; + } else if (Prefix == "vld1") { + Result.push_back("{{ldr|vldr|vmov}}"); + return; + } + } + } + + // vzip.32/vuzp.32 are the same instruction as vtrn.32 and are + // sometimes disassembled as vtrn.32. We use a regex to handle both + // cases. + if ((Prefix == "vzip" || Prefix == "vuzp") && OutTypeCode == "32") { + Result.push_back("{{vtrn|" + Prefix + "}}.32 " + RegisterSuffix); + return; + } + + // Currently on most ARM processors, we do not use vmla/vmls for + // quad floating point operations. Instead we output vmul + vadd. So + // check if we have one of those instructions and just output a + // check for vmul. + if (OutTypeCode == "f32") { + if (Prefix == "vmls") { + Result.push_back("vmul." + OutTypeCode + " " + RegisterSuffix); + Result.push_back("vsub." + OutTypeCode); + return; + } else if (Prefix == "vmla") { + Result.push_back("vmul." + OutTypeCode + " " + RegisterSuffix); + Result.push_back("vadd." + OutTypeCode); + return; + } + } + + // If we have vcvt, get the input type from the instruction name + // (which should be of the form instname_inputtype) and append it + // before the output type. + if (Prefix == "vcvt") { + const std::string inTypeCode = NameRef.substr(NameRef.find_last_of("_")+1); + Prefix += "." + inTypeCode; + } + + // Append output type code to get our final mangled instruction. + Prefix += "." + OutTypeCode; + + Result.push_back(Prefix + " " + RegisterSuffix); +} + /// UseMacro - Examine the prototype string to determine if the intrinsic /// should be defined as a preprocessor macro instead of an inline function. static bool UseMacro(const std::string &proto) { @@ -1342,7 +1822,7 @@ void NeonEmitter::run(raw_ostream &OS) { } } - OS<<"#define __ai static __attribute__((__always_inline__, __nodebug__))\n\n"; + OS<<"#define __ai static inline __attribute__((__always_inline__, __nodebug__))\n\n"; std::vector RV = Records.getAllDerivedDefinitions("Inst"); @@ -1668,7 +2148,8 @@ void NeonEmitter::runHeader(raw_ostream &OS) { static std::string GenTest(const std::string &name, const std::string &proto, StringRef outTypeStr, StringRef inTypeStr, - bool isShift) { + bool isShift, bool isHiddenLOp, + ClassKind ck, const std::string &InstName) { assert(!proto.empty() && ""); std::string s; @@ -1683,9 +2164,22 @@ static std::string GenTest(const std::string &name, mangledName = MangleName(mangledName, inTypeNoQuad, ClassS); } + std::vector FileCheckPatterns; + GenerateChecksForIntrinsic(name, proto, outTypeStr, inTypeStr, ck, InstName, + isHiddenLOp, FileCheckPatterns); + // Emit the FileCheck patterns. s += "// CHECK: test_" + mangledName + "\n"; - // s += "// CHECK: \n"; // FIXME: + expected instruction opcode. + // If for any reason we do not want to emit a check, mangledInst + // will be the empty string. + if (FileCheckPatterns.size()) { + for (std::vector::const_iterator i = FileCheckPatterns.begin(), + e = FileCheckPatterns.end(); + i != e; + ++i) { + s += "// CHECK: " + *i + "\n"; + } + } // Emit the start of the test function. s += TypeString(proto[0], outTypeStr) + " test_" + mangledName + "("; @@ -1727,8 +2221,9 @@ static std::string GenTest(const std::string &name, /// intrinsics. void NeonEmitter::runTests(raw_ostream &OS) { OS << - "// RUN: %clang_cc1 -triple thumbv7-apple-darwin \\\n" - "// RUN: -target-cpu cortex-a9 -ffreestanding -S -o - %s | FileCheck %s\n" + "// RUN: %clang_cc1 -triple thumbv7s-apple-darwin -target-abi apcs-gnu\\\n" + "// RUN: -target-cpu swift -ffreestanding -Os -S -o - %s\\\n" + "// RUN: | FileCheck %s\n" "\n" "#include \n" "\n"; @@ -1740,10 +2235,13 @@ void NeonEmitter::runTests(raw_ostream &OS) { std::string Proto = R->getValueAsString("Prototype"); std::string Types = R->getValueAsString("Types"); bool isShift = R->getValueAsBit("isShift"); + std::string InstName = R->getValueAsString("InstName"); + bool isHiddenLOp = R->getValueAsBit("isHiddenLInst"); SmallVector TypeVec; ParseTypes(R, Types, TypeVec); + ClassKind ck = ClassMap[R->getSuperClasses()[1]]; OpKind kind = OpMap[R->getValueAsDef("Operand")->getName()]; if (kind == OpUnavailable) continue; @@ -1758,10 +2256,12 @@ void NeonEmitter::runTests(raw_ostream &OS) { (void)ClassifyType(TypeVec[srcti], inQuad, dummy, dummy); if (srcti == ti || inQuad != outQuad) continue; - OS << GenTest(name, Proto, TypeVec[ti], TypeVec[srcti], isShift); + OS << GenTest(name, Proto, TypeVec[ti], TypeVec[srcti], + isShift, isHiddenLOp, ck, InstName); } } else { - OS << GenTest(name, Proto, TypeVec[ti], TypeVec[ti], isShift); + OS << GenTest(name, Proto, TypeVec[ti], TypeVec[ti], + isShift, isHiddenLOp, ck, InstName); } } OS << "\n"; diff --git a/utils/TableGen/TableGen.cpp b/utils/TableGen/TableGen.cpp index 3df8940b055..12e1c47725e 100644 --- a/utils/TableGen/TableGen.cpp +++ b/utils/TableGen/TableGen.cpp @@ -24,6 +24,7 @@ using namespace clang; enum ActionType { GenClangAttrClasses, + GenClangAttrExprArgsList, GenClangAttrImpl, GenClangAttrList, GenClangAttrPCHRead, @@ -62,6 +63,10 @@ namespace { "Generate option parser implementation"), clEnumValN(GenClangAttrClasses, "gen-clang-attr-classes", "Generate clang attribute clases"), + clEnumValN(GenClangAttrExprArgsList, + "gen-clang-attr-expr-args-list", + "Generate a clang attribute expression " + "arguments list"), clEnumValN(GenClangAttrImpl, "gen-clang-attr-impl", "Generate clang attribute implementations"), clEnumValN(GenClangAttrList, "gen-clang-attr-list", @@ -143,6 +148,9 @@ bool ClangTableGenMain(raw_ostream &OS, RecordKeeper &Records) { case GenClangAttrClasses: EmitClangAttrClass(Records, OS); break; + case GenClangAttrExprArgsList: + EmitClangAttrExprArgsList(Records, OS); + break; case GenClangAttrImpl: EmitClangAttrImpl(Records, OS); break; diff --git a/utils/TableGen/TableGenBackends.h b/utils/TableGen/TableGenBackends.h index 03708b6a766..0ff33d775cc 100644 --- a/utils/TableGen/TableGenBackends.h +++ b/utils/TableGen/TableGenBackends.h @@ -30,6 +30,7 @@ void EmitClangASTNodes(RecordKeeper &RK, raw_ostream &OS, const std::string &N, const std::string &S); void EmitClangAttrClass(RecordKeeper &Records, raw_ostream &OS); +void EmitClangAttrExprArgsList(RecordKeeper &Records, raw_ostream &OS); void EmitClangAttrImpl(RecordKeeper &Records, raw_ostream &OS); void EmitClangAttrList(RecordKeeper &Records, raw_ostream &OS); void EmitClangAttrPCHRead(RecordKeeper &Records, raw_ostream &OS); diff --git a/utils/analyzer/SATestBuild.py b/utils/analyzer/SATestBuild.py index 067be162e27..e119155a9b4 100644 --- a/utils/analyzer/SATestBuild.py +++ b/utils/analyzer/SATestBuild.py @@ -168,8 +168,9 @@ def getSBOutputDirName(IsReferenceBuild) : SBOutputDirReferencePrefix = "Ref" # The list of checkers used during analyzes. -# Currently, consists of all the non experimental checkers. -Checkers="alpha.unix.SimpleStream,alpha.security.taint,core,deadcode,security,unix,osx" +# Currently, consists of all the non experimental checkers, plus a few alpha +# checkers we don't want to regress on. +Checkers="alpha.unix.SimpleStream,alpha.security.taint,alpha.cplusplus.NewDeleteLeaks,core,cplusplus,deadcode,security,unix,osx" Verbose = 1 diff --git a/www/OpenProjects.html b/www/OpenProjects.html index 98d86602fb4..ad1890997bc 100644 --- a/www/OpenProjects.html +++ b/www/OpenProjects.html @@ -21,13 +21,10 @@ intended to be comprehensive. Please ask on cfe-dev for more specifics or to verify that one of these isn't already completed. :)

-
  • Continue work on C++'11 support: - C++'98 is feature complete, but there is still a lot of C++'11 features to +
  • Continue work on C++1y support: + C++98 and C++11 are feature-complete, but there are still several C++1y features to implement. Please see the C++ status report page to find out what is missing.
  • diff --git a/www/analyzer/available_checks.html b/www/analyzer/available_checks.html index be15125a7e8..8c7d9e987a5 100644 --- a/www/analyzer/available_checks.html +++ b/www/analyzer/available_checks.html @@ -30,15 +30,15 @@ core.AdjustedReturnValueCheck to see if the return value of a function call is different than the caller expects (e.g., from calls through function pointers). -core.AttributeNonNullCheck for null pointers passed as arguments to a function whose arguments are marked with the 'nonnull' attribute. - - core.CallAndMessageCheck for logical errors for function calls and Objective-C message expressions (e.g., uninitialized arguments, null function pointers). core.DivideZeroCheck for division by zero. +core.NonNullParamCheckerCheck for null pointers passed as arguments to a function whose arguments are known to be non-null. + + core.NullDereferenceCheck for dereferences of null pointers. @@ -72,6 +72,9 @@ core.uninitialized.UndefReturnCheck for uninitialized values being returned to the caller. +cplusplus.NewDeleteCheck for double-free and use-after-free problems involving C++ delete. + + deadcode.DeadStoresCheck for values stored to variables that are never read afterwards. -osx.APICheck for proper uses of various Mac OS X APIs. - - -osx.AtomicCASEvaluate calls to OSAtomic functions. +osx.APICheck for proper uses of various Apple APIs. osx.SecKeychainAPICheck for proper uses of Secure Keychain APIs. -osx.cocoa.AtSyncCheck for null pointers used as mutexes for @synchronized. +osx.cocoa.AtSyncCheck for nil pointers used as mutexes for @synchronized. osx.cocoa.ClassReleaseCheck for sending 'retain', 'release', or 'autorelease' directly to a Class. @@ -164,12 +164,15 @@ unix.APICheck calls to various UNIX/Posix functions. -unix.MallocCheck for memory leaks, double free, and use-after-free problems. +unix.MallocCheck for memory leaks, double free, and use-after-free problems involving malloc. unix.MallocSizeofCheck for dubious malloc arguments involving sizeof. +unix.MismatchedDeallocatorCheck for mismatched deallocators (e.g. passing a pointer allocating with new to free()). + + unix.cstring.BadSizeArgCheck the size argument passed into C string functions for common erroneous patterns. diff --git a/www/analyzer/checker_dev_manual.html b/www/analyzer/checker_dev_manual.html index 5368eb0e961..a8249530312 100644 --- a/www/analyzer/checker_dev_manual.html +++ b/www/analyzer/checker_dev_manual.html @@ -18,13 +18,18 @@

    Checker Developer Manual

    -

    The static analyzer engine performs symbolic execution of the program and +

    The static analyzer engine performs path-sensitive exploration of the program and relies on a set of checkers to implement the logic for detecting and -constructing bug reports. This page provides hints and guidelines for anyone -who is interested in implementing their own checker. The static analyzer is a +constructing specific bug reports. Anyone who is interested in implementing their own +checker, should check out the Building a Checker in 24 Hours talk +(slides + video) +and refer to this page for additional information on writing a checker. The static analyzer is a part of the Clang project, so consult Hacking on Clang -and LLVM Programmer's Manual -for general developer guidelines and information.

    +and LLVM Programmer's Manual +for developer guidelines and send your questions and proposals to +cfe-dev mailing list. +

    • Getting Started
    • diff --git a/www/analyzer/dev_cxx.html b/www/analyzer/dev_cxx.html deleted file mode 100644 index 4424a9a8b5b..00000000000 --- a/www/analyzer/dev_cxx.html +++ /dev/null @@ -1,57 +0,0 @@ - - - - Analyzer Development: C++ Support - - - - - - -
      - -
      - -

      C++ Support

      - -

      The Clang compiler supports almost all of C++11. -Support in the frontend for C++ language features, however, does not -automatically translate into support for those features in the static analyzer. -Language features need to be specifically modeled in the static analyzer so -their semantics can be properly analyzed. Support for analyzing C++ and -Objective-C++ files is currently fairly basic.

      - -

      Listed here are a set of open tasks that are prerequisites for -decent analysis of C++. This list is also not complete; new tasks -will be added as deemed necessary.

      - -
        -
      • Control-Flow Graph Enhancements: -
          -
        • Model destructors for temporary objects
        • -
        • Model the implicit allocator call to operator new
        • -
        -
      • -
      • Path-Sensitive Analysis Engine (ExprEngine): -
          -
        • Allow constructors to be inlined
        • -
        • Allow destructors to be inlined
        • -
        • Fully model new and delete
        • -
        • Track type info through casts more precisely
        • -
        -
      • -
      • Checkers: - -
      • -
      - -
      -
      - - - diff --git a/www/analyzer/latest_checker.html.incl b/www/analyzer/latest_checker.html.incl index 1355297e7c4..233a104cf12 100644 --- a/www/analyzer/latest_checker.html.incl +++ b/www/analyzer/latest_checker.html.incl @@ -1 +1 @@ -checker-272.tar.bz2 (built March 1, 2013) +checker-274.tar.bz2 (built April 23, 2013) diff --git a/www/analyzer/menu.html.incl b/www/analyzer/menu.html.incl index 72160e69993..c487160c4ae 100644 --- a/www/analyzer/menu.html.incl +++ b/www/analyzer/menu.html.incl @@ -26,9 +26,9 @@
    • Development
    • diff --git a/www/analyzer/open_projects.html b/www/analyzer/open_projects.html new file mode 100644 index 00000000000..c015b486653 --- /dev/null +++ b/www/analyzer/open_projects.html @@ -0,0 +1,180 @@ + + + + Open Projects + + + + + + +
      + +
      + +

      Open Projects

      + +

      This page lists several projects that would boost analyzer's usability and +power. Most of the projects listed here are infrastructure-related so this list +is an addition to the potential checkers +list. If you are interested in tackling one of these, please send an email +to the cfe-dev +mailing list to notify other members of the community.

      + +
        +
      • Core Analyzer Infrastructure +
          +
        • Explicitly model standard library functions with BodyFarm. +

          BodyFarm + allows the analyzer to explicitly model functions whose definitions are + not available during analysis. Modeling more of the widely used functions + (such as the members of std::string) will improve precision of the + analysis. + (Difficulty: Easy)

          +

        • + +
        • Implement generalized loop execution modeling. +

          Currently, the analyzer simply unrolls each loop N times. This + means that it will not execute any code after the loop if the loop is + guaranteed to execute more than N times. This results in lost + basic block coverage. We could continue exploring the path if we could + model a generic i-th iteration of a loop. + (Difficulty: Hard)

          +
        • + +
        • Enhance CFG to model C++ temporaries properly. +

          There is an existing implementation of this, but it's not complete and + is disabled in the analyzer. + (Difficulty: Medium)

          + +
        • Enhance CFG to model exception-handling properly. +

          Currently exceptions are treated as "black holes", and exception-handling + control structures are poorly modeled (to be conservative). This could be + much improved for both C++ and Objective-C exceptions. + (Difficulty: Medium)

          + +
        • Enhance CFG to model C++ new more precisely. +

          The current representation of new does not provide an easy + way for the analyzer to model the call to a memory allocation function + (operator new), then initialize the result with a constructor + call. The problem is discussed at length in + PR12014. + (Difficulty: Easy)

          + +
        • Enhance CFG to model C++ delete more precisely. +

          Similarly, the representation of delete does not include + the call to the destructor, followed by the call to the deallocation + function (operator delete). One particular issue + (noreturn destructors) is discussed in + PR15599 + (Difficulty: Easy)

          + +
        • Track type info through casts more precisely. +

          The DynamicTypePropagation checker is in charge of inferring a region's + dynamic type based on what operations the code is performing. Casts are a + rich source of type information that the analyzer currently ignores. They + are tricky to get right, but might have very useful consequences. + (Difficulty: Medium)

          + +
        • Design and implement alpha-renaming. +

          Implement unifying two symbolic values along a path after they are + determined to be equal via comparison. This would allow us to reduce the + number of false positives and would be a building step to more advanced + analyses, such as summary-based interprocedural and cross-translation-unit + analysis. + (Difficulty: Hard)

          +
        • +
        +
      • + +
      • Bug Reporting +
          +
        • Add support for displaying cross-file diagnostic paths in HTML output + (used by scan-build). +

          Currently scan-build output does not display reports that span + multiple files. The main problem is that we do not have a good format to + display such paths in HTML output. (Difficulty: Medium)

          +
        • + +
        • Relate bugs to checkers / "bug types" +

          We need to come up with an API which will relate bug reports + to the checkers that produce them and refactor the existing code to use the + new API. This would allow us to identify the checker from the bug report, + which paves the way for selective control of certain checks. + (Difficulty: Easy-Medium)

          +
        • + +
        • Refactor path diagnostic generation in BugReporter.cpp. +

          It would be great to have more code reuse between "Minimal" and + "Extensive" PathDiagnostic generation algorithms. One idea is to create an + IR for representing path diagnostics, which would be later be used to + generate minimal or extensive report output. (Difficulty: Medium)

          +
        • +
        +
      • + +
      • Other Infrastructure +
          +
        • Rewrite scan-build (in Python). +

          (Difficulty: Easy)

          +
        • + +
        • Do a better job interposing on a compilation. +

          Currently, scan-build just sets the CC and CXX + environment variables to its wrapper scripts, which then call into an + underlying platform compiler. This is problematic for any project that + doesn't exclusively use CC and CXX to control its + compilers. +

          (Difficulty: Medium-Hard)

          +
        • + +
        • Create an analyzer_annotate attribute for the analyzer + annotations. +

          We would like to put all analyzer attributes behind a fence so that we + could add/remove them without worrying that compiler (not analyzer) users + depend on them. Design and implement such a generic analyzer attribute in + the compiler. (Difficulty: Medium)

          +
        • +
        +
      • + +
      • Enhanced Checks +
          +
        • Implement a production-ready StreamChecker. +

          A SimpleStreamChecker has been presented in the Building a Checker in 24 + Hours talk + (slides + video). + We need to implement a production version of the checker with richer set of + APIs and evaluate it by running on real codebases. + (Difficulty: Easy)

          +
        • + +
        • Extend Malloc checker with reasoning about custom allocator, + deallocator, and ownership-transfer functions. +

          This would require extending the MallocPessimistic checker to reason + about annotated functions. It is strongly desired that one would rely on + the analyzer_annotate attribute, as described above. + (Difficulty: Easy)

          +
        • + +
        • Implement iterators invalidation checker. +

          (Difficulty: Easy)

          +
        • + +
        • Write checkers which catch Copy and Paste errors. +

          Take a look at the + CP-Miner + paper for inspiration. + (Difficulty: Medium-Hard)

          +
        • +
        +
      • +
      + +
      +
      + + + diff --git a/www/analyzer/potential_checkers.html b/www/analyzer/potential_checkers.html index 04bf9fe45d0..c769541e70d 100644 --- a/www/analyzer/potential_checkers.html +++ b/www/analyzer/potential_checkers.html @@ -62,43 +62,6 @@ void test() { PR15238 -memory.MismatchedDelete -
      (C, C++)


      -Mismatched deallocation function is used -
      -#include <stdlib.h>
      -
      -void test() {
      -  int *p1 = new int;
      -  int *p2 = new int[1];
      -  int *p3 = (int*)malloc(sizeof(int));
      -
      -  delete[] p1; // warn
      -  delete p2; // warn
      -  delete p3; // warn
      -}
      -
      PR15238 - - -memory.MultipleDelete -
      (C++)


      -Attempt to deallocate released memory -
      -#include <new>
      -
      -void test() {
      -  int *p1 = new int;
      -  int *p2 = new(p1) int;
      -  int *p3 = p1;
      -  delete p1;
      -  delete p1; // warn
      -  delete p2; // warn
      -  delete p3; // warn
      -}
      -
      PR15237 - - - memory.LeakPtrValChanged
      enhancement to unix.Malloc
      (C, C++)


      Potential memory leak: a pointer to newly allocated data loses its original @@ -124,31 +87,6 @@ void test() { done at r174678 (C case) - -memory.DeallocateNonPtr -
      enhancement to unix.Malloc
      (C, C++)


      -Deallocation function is applied to non-pointer -
      -#include <stdlib.h>
      -
      -class A {
      -  int *p;
      -public:
      -  operator int *() { return p; }  
      -};
      -
      -void test() {
      -  A a;
      -  delete a; // warn
      -  free(a); // warn
      -  const char *s = "text";
      -  delete s; // warn
      -  free(s); // warn
      -}
      -
      PR15237 - - - memory.LeakEvalOrder
      (C, C++)


      Potential memory leak: argument evaluation order is undefined, g() may never be called @@ -232,6 +170,17 @@ class A { }; +ctordtor.PlacementSelfCopy
      +(C++11)


      +For a placement copy or move, it is almost certainly an error if the constructed object is also the object being copied from. +
      +class A {};
      +
      +void test(A *dst, A *src) {
      +  ::new (dst) A(*dst); // warn (should be 'src')
      +}
      +
      + @@ -1276,16 +1225,6 @@ void test() { } -different.UnaryPlusWithUnsigned -
      (C)


      -Using 'unary +' with unsigned is meaningless -
      -void test() {
      -  unsigned a;
      -  a = +a; // warn
      -}
      -
      - different.LogicalOpUselessArg
      (C)


      The second operand of the && operator has no impact on expression result diff --git a/www/analyzer/release_notes.html b/www/analyzer/release_notes.html index 190695ef85f..2d1660dcb52 100644 --- a/www/analyzer/release_notes.html +++ b/www/analyzer/release_notes.html @@ -15,6 +15,30 @@

      Release notes for checker-XXX builds

      +

      checker-274

      +

      built: April 23, 2013
      + download: checker-274.tar.bz2

      +

      highlights:

      +
        +
      • Improved use-after-free and mismatched deallocator checking.
      • +
      • Diagnostic polish.
      • +
      • Fixes crashes found in checker-273.
      • +
      + +

      checker-273

      +

      built: April 8, 2013
      + download: checker-273.tar.bz2

      +

      highlights:

      +
        +
      • Additional checks for misuse of Foundation collection APIs. +
      • New C++ checker for attempting to create a reference to null.
      • +
      • New use-after-free checker for C++ 'delete'.
      • +
      • New checker for simple cases of mismatched allocators and deallocators, e.g. "delete malloc(4);"
      • +
      • Support for basic interprocedural analysis of C++ destructors.
      • +
      • Additional heuristics for suppressing null pointer false positives.
      • +
      • Misc. bug fixes and performance enhancements.
      • +
      +

      checker-272

      built: March 1, 2013
      download: checker-272.tar.bz2

      diff --git a/www/comparison.html b/www/comparison.html index e8b14923b68..afc321e0619 100644 --- a/www/comparison.html +++ b/www/comparison.html @@ -51,7 +51,6 @@
    • GCC supports languages that clang does not aim to, such as Java, Ada, FORTRAN, etc.
    • GCC supports more targets than LLVM.
    • -
    • GCC is popular and widely adopted.

    Pro's of clang vs GCC:

    diff --git a/www/cxx_status.html b/www/cxx_status.html index ac1862a9ce6..ff6ec8a0ce2 100644 --- a/www/cxx_status.html +++ b/www/cxx_status.html @@ -3,11 +3,12 @@ - Clang - C++98 and C++11 Status + Clang - C++98, C++11, and C++14 Status