From 519fc96c475680de2cc49e7811dbbfadb912cbcc Mon Sep 17 00:00:00 2001 From: Dimitry Andric Date: Wed, 23 Oct 2019 17:52:09 +0000 Subject: Vendor import of stripped clang trunk r375505, the last commit before the upstream Subversion repository was made read-only, and the LLVM project migrated to GitHub: https://llvm.org/svn/llvm-project/cfe/trunk@375505 --- include/clang-c/FatalErrorHandler.h | 33 + include/clang-c/Index.h | 22 +- include/clang/AST/APValue.h | 46 +- include/clang/AST/ASTContext.h | 136 +- include/clang/AST/ASTFwd.h | 2 +- include/clang/AST/ASTImporter.h | 43 +- include/clang/AST/ASTImporterSharedState.h | 2 +- include/clang/AST/ASTNodeTraverser.h | 3 + include/clang/AST/ASTStructuralEquivalence.h | 19 +- include/clang/AST/ASTTypeTraits.h | 4 +- include/clang/AST/Attr.h | 151 +- include/clang/AST/CXXRecordDeclDefinitionBits.def | 236 ++ include/clang/AST/CharUnits.h | 5 + include/clang/AST/CommentCommands.td | 1 + include/clang/AST/CommentLexer.h | 3 +- include/clang/AST/Decl.h | 52 +- include/clang/AST/DeclBase.h | 2 +- include/clang/AST/DeclCXX.h | 343 +-- include/clang/AST/DeclTemplate.h | 178 +- include/clang/AST/Expr.h | 16 +- include/clang/AST/ExprCXX.h | 235 +- include/clang/AST/ExternalASTMerger.h | 38 +- include/clang/AST/FormatString.h | 22 +- include/clang/AST/GlobalDecl.h | 1 + include/clang/AST/JSONNodeDumper.h | 2 + include/clang/AST/Mangle.h | 18 +- include/clang/AST/NSAPI.h | 3 - include/clang/AST/OpenMPClause.h | 157 +- include/clang/AST/OperationKinds.def | 5 +- include/clang/AST/OptionalDiagnostic.h | 78 + include/clang/AST/RawCommentList.h | 23 +- include/clang/AST/RecursiveASTVisitor.h | 100 +- include/clang/AST/Stmt.h | 10 + include/clang/AST/StmtOpenMP.h | 302 ++- include/clang/AST/TextNodeDumper.h | 2 - include/clang/AST/Type.h | 105 +- include/clang/AST/TypeLoc.h | 2 +- include/clang/AST/TypeLocNodes.def | 2 +- include/clang/AST/TypeNodes.def | 135 -- include/clang/AST/TypeVisitor.h | 4 +- include/clang/ASTMatchers/ASTMatchers.h | 186 +- include/clang/ASTMatchers/ASTMatchersInternal.h | 25 +- include/clang/Analysis/AnalysisDeclContext.h | 51 +- include/clang/Analysis/CFG.h | 195 +- include/clang/Analysis/CallGraph.h | 1 + include/clang/Analysis/PathDiagnostic.h | 868 +++++++ include/clang/Basic/AArch64SVEACLETypes.def | 70 + include/clang/Basic/Attr.td | 182 +- include/clang/Basic/AttrDocs.td | 210 +- include/clang/Basic/AttributeCommonInfo.h | 190 ++ include/clang/Basic/Builtins.def | 27 +- include/clang/Basic/BuiltinsAArch64.def | 56 +- include/clang/Basic/BuiltinsAMDGPU.def | 9 + include/clang/Basic/BuiltinsARM.def | 48 +- include/clang/Basic/BuiltinsBPF.def | 24 + include/clang/Basic/BuiltinsPPC.def | 14 +- include/clang/Basic/BuiltinsWebAssembly.def | 33 + include/clang/Basic/BuiltinsX86.def | 4 +- include/clang/Basic/BuiltinsX86_64.def | 4 +- include/clang/Basic/CodeGenOptions.def | 14 +- include/clang/Basic/CodeGenOptions.h | 6 + include/clang/Basic/Diagnostic.h | 17 +- include/clang/Basic/DiagnosticASTKinds.td | 139 +- include/clang/Basic/DiagnosticCommentKinds.td | 6 + include/clang/Basic/DiagnosticCommonKinds.td | 26 +- include/clang/Basic/DiagnosticDriverKinds.td | 9 +- include/clang/Basic/DiagnosticFrontendKinds.td | 13 +- include/clang/Basic/DiagnosticGroups.td | 50 +- include/clang/Basic/DiagnosticOptions.def | 1 + include/clang/Basic/DiagnosticParseKinds.td | 55 +- include/clang/Basic/DiagnosticSemaKinds.td | 388 ++- .../clang/Basic/DiagnosticSerializationKinds.td | 15 +- include/clang/Basic/Features.def | 4 +- include/clang/Basic/FileManager.h | 204 +- include/clang/Basic/IdentifierTable.h | 6 + include/clang/Basic/LangOptions.def | 11 +- include/clang/Basic/LangOptions.h | 16 + include/clang/Basic/LangStandard.h | 136 ++ include/clang/Basic/LangStandards.def | 189 ++ include/clang/Basic/Linkage.h | 6 + include/clang/Basic/OpenCLOptions.h | 6 +- include/clang/Basic/OpenMPKinds.def | 103 + include/clang/Basic/OpenMPKinds.h | 13 +- include/clang/Basic/OperatorKinds.h | 19 + include/clang/Basic/SourceManager.h | 83 +- include/clang/Basic/Specifiers.h | 3 +- include/clang/Basic/Stack.h | 29 + include/clang/Basic/StmtNodes.td | 7 + include/clang/Basic/SyncScope.h | 2 +- include/clang/Basic/TargetBuiltins.h | 10 + include/clang/Basic/TargetInfo.h | 23 +- include/clang/Basic/TokenKinds.def | 72 +- include/clang/Basic/TokenKinds.h | 11 +- include/clang/Basic/TypeNodes.td | 106 + include/clang/Basic/X86Target.def | 5 + include/clang/Basic/arm_neon.td | 8 +- include/clang/CodeGen/CGFunctionInfo.h | 10 +- include/clang/CrossTU/CrossTranslationUnit.h | 143 +- include/clang/DirectoryWatcher/DirectoryWatcher.h | 10 +- include/clang/Driver/Action.h | 26 +- include/clang/Driver/CC1Options.td | 16 +- include/clang/Driver/CLCompatOptions.td | 14 +- include/clang/Driver/Driver.h | 14 +- include/clang/Driver/Options.h | 2 +- include/clang/Driver/Options.td | 136 +- include/clang/Driver/Phases.h | 3 +- include/clang/Driver/SanitizerArgs.h | 7 +- include/clang/Driver/ToolChain.h | 9 + include/clang/Driver/Types.def | 123 +- include/clang/Driver/Types.h | 7 +- include/clang/Format/Format.h | 240 +- include/clang/Frontend/ASTUnit.h | 5 +- include/clang/Frontend/CompilerInstance.h | 24 +- include/clang/Frontend/CompilerInvocation.h | 14 +- include/clang/Frontend/FrontendActions.h | 9 +- include/clang/Frontend/FrontendOptions.h | 51 +- include/clang/Frontend/LangStandard.h | 117 - include/clang/Frontend/LangStandards.def | 188 -- include/clang/Frontend/Utils.h | 13 +- include/clang/Index/CodegenNameGenerator.h | 52 - include/clang/Index/IndexDataConsumer.h | 16 +- include/clang/Index/IndexingAction.h | 37 +- include/clang/Index/IndexingOptions.h | 42 + .../Lex/DependencyDirectivesSourceMinimizer.h | 22 + include/clang/Lex/DirectoryLookup.h | 68 +- include/clang/Lex/HeaderMap.h | 5 +- include/clang/Lex/HeaderSearch.h | 14 +- include/clang/Lex/HeaderSearchOptions.h | 27 +- include/clang/Lex/Lexer.h | 15 + include/clang/Lex/MacroArgs.h | 10 - include/clang/Lex/PPCallbacks.h | 12 +- include/clang/Lex/Preprocessor.h | 55 +- ...cessorExcludedConditionalDirectiveSkipMapping.h | 31 + include/clang/Lex/PreprocessorOptions.h | 16 + include/clang/Parse/Parser.h | 50 +- include/clang/Rewrite/Core/Rewriter.h | 11 + include/clang/Sema/Overload.h | 102 +- include/clang/Sema/ParsedAttr.h | 212 +- include/clang/Sema/ScopeInfo.h | 10 +- include/clang/Sema/Sema.h | 577 +++-- include/clang/Sema/SemaInternal.h | 2 +- include/clang/Sema/TypoCorrection.h | 8 +- include/clang/Serialization/ASTBitCodes.h | 17 +- include/clang/Serialization/ASTReader.h | 14 +- include/clang/StaticAnalyzer/Checkers/Checkers.td | 43 +- .../clang/StaticAnalyzer/Core/AnalyzerOptions.def | 32 +- .../clang/StaticAnalyzer/Core/AnalyzerOptions.h | 68 +- .../StaticAnalyzer/Core/BugReporter/BugReporter.h | 656 ++--- .../Core/BugReporter/BugReporterVisitors.h | 217 +- .../StaticAnalyzer/Core/BugReporter/BugType.h | 40 +- .../Core/BugReporter/CommonBugCategories.h | 1 + .../Core/BugReporter/PathDiagnostic.h | 923 ------- include/clang/StaticAnalyzer/Core/Checker.h | 4 +- include/clang/StaticAnalyzer/Core/CheckerManager.h | 24 +- .../StaticAnalyzer/Core/PathDiagnosticConsumers.h | 12 +- .../Core/PathSensitive/AnalysisManager.h | 9 +- .../StaticAnalyzer/Core/PathSensitive/CallEvent.h | 27 +- .../Core/PathSensitive/CheckerContext.h | 17 +- .../Core/PathSensitive/DynamicCastInfo.h | 55 + .../Core/PathSensitive/DynamicType.h | 73 + .../Core/PathSensitive/DynamicTypeInfo.h | 46 +- .../Core/PathSensitive/DynamicTypeMap.h | 63 - .../Core/PathSensitive/ExplodedGraph.h | 54 +- .../StaticAnalyzer/Core/PathSensitive/ExprEngine.h | 10 +- .../StaticAnalyzer/Core/PathSensitive/MemRegion.h | 6 + .../Core/PathSensitive/ProgramState.h | 4 + include/clang/Tooling/ASTDiff/ASTDiff.h | 2 +- include/clang/Tooling/AllTUsExecution.h | 3 +- include/clang/Tooling/ArgumentsAdjusters.h | 4 + .../DependencyScanningFilesystem.h | 188 ++ .../DependencyScanning/DependencyScanningService.h | 65 + .../DependencyScanning/DependencyScanningTool.h | 48 + .../DependencyScanning/DependencyScanningWorker.h | 42 +- include/clang/Tooling/Execution.h | 7 - include/clang/Tooling/Inclusions/HeaderIncludes.h | 1 + include/clang/Tooling/Inclusions/IncludeStyle.h | 2 + .../Tooling/Refactoring/Extract/SourceExtraction.h | 51 + include/clang/Tooling/Refactoring/RangeSelector.h | 89 - .../Tooling/Refactoring/RecursiveSymbolVisitor.h | 15 +- .../Refactoring/RefactoringActionRulesInternal.h | 10 +- include/clang/Tooling/Refactoring/SourceCode.h | 77 - include/clang/Tooling/Refactoring/Stencil.h | 173 -- include/clang/Tooling/Refactoring/Transformer.h | 308 --- include/clang/Tooling/StandaloneExecution.h | 2 - include/clang/Tooling/Syntax/Tokens.h | 10 + include/clang/Tooling/Tooling.h | 30 +- include/clang/Tooling/Transformer/MatchConsumer.h | 62 + include/clang/Tooling/Transformer/RangeSelector.h | 113 + include/clang/Tooling/Transformer/RewriteRule.h | 309 +++ include/clang/Tooling/Transformer/SourceCode.h | 90 + .../clang/Tooling/Transformer/SourceCodeBuilders.h | 86 + include/clang/Tooling/Transformer/Stencil.h | 221 ++ include/clang/Tooling/Transformer/Transformer.h | 52 + include/clang/module.modulemap | 6 +- lib/ARCMigrate/ARCMT.cpp | 6 +- lib/ARCMigrate/FileRemapper.cpp | 24 +- lib/ARCMigrate/ObjCMT.cpp | 15 +- lib/ARCMigrate/PlistReporter.cpp | 2 +- lib/AST/APValue.cpp | 38 +- lib/AST/ASTContext.cpp | 757 +++--- lib/AST/ASTDiagnostic.cpp | 2 +- lib/AST/ASTImporter.cpp | 542 +++-- lib/AST/ASTStructuralEquivalence.cpp | 90 +- lib/AST/ASTTypeTraits.cpp | 16 +- lib/AST/CXXInheritance.cpp | 2 +- lib/AST/Comment.cpp | 15 + lib/AST/CommentLexer.cpp | 7 +- lib/AST/CommentParser.cpp | 6 + lib/AST/CommentSema.cpp | 8 + lib/AST/Decl.cpp | 75 +- lib/AST/DeclBase.cpp | 15 +- lib/AST/DeclCXX.cpp | 84 +- lib/AST/DeclPrinter.cpp | 15 +- lib/AST/DeclTemplate.cpp | 69 +- lib/AST/Expr.cpp | 140 +- lib/AST/ExprCXX.cpp | 176 +- lib/AST/ExprClassification.cpp | 5 + lib/AST/ExprConstant.cpp | 2530 +++++++++++++++----- lib/AST/ExternalASTMerger.cpp | 124 +- lib/AST/FormatString.cpp | 4 + lib/AST/FormatStringParsing.h | 13 + lib/AST/InheritViz.cpp | 4 +- lib/AST/Interp/Block.cpp | 87 + lib/AST/Interp/Block.h | 140 ++ lib/AST/Interp/Boolean.h | 148 ++ lib/AST/Interp/ByteCodeEmitter.cpp | 175 ++ lib/AST/Interp/ByteCodeEmitter.h | 112 + lib/AST/Interp/ByteCodeExprGen.cpp | 580 +++++ lib/AST/Interp/ByteCodeExprGen.h | 331 +++ lib/AST/Interp/ByteCodeGenError.cpp | 14 + lib/AST/Interp/ByteCodeGenError.h | 46 + lib/AST/Interp/ByteCodeStmtGen.cpp | 265 ++ lib/AST/Interp/ByteCodeStmtGen.h | 89 + lib/AST/Interp/Context.cpp | 148 ++ lib/AST/Interp/Context.h | 100 + lib/AST/Interp/Descriptor.cpp | 292 +++ lib/AST/Interp/Descriptor.h | 220 ++ lib/AST/Interp/Disasm.cpp | 69 + lib/AST/Interp/EvalEmitter.cpp | 253 ++ lib/AST/Interp/EvalEmitter.h | 129 + lib/AST/Interp/Frame.cpp | 14 + lib/AST/Interp/Frame.h | 45 + lib/AST/Interp/Function.cpp | 48 + lib/AST/Interp/Function.h | 163 ++ lib/AST/Interp/Integral.h | 269 +++ lib/AST/Interp/Interp.cpp | 417 ++++ lib/AST/Interp/Interp.h | 960 ++++++++ lib/AST/Interp/InterpFrame.cpp | 193 ++ lib/AST/Interp/InterpFrame.h | 153 ++ lib/AST/Interp/InterpStack.cpp | 78 + lib/AST/Interp/InterpStack.h | 113 + lib/AST/Interp/InterpState.cpp | 74 + lib/AST/Interp/InterpState.h | 112 + lib/AST/Interp/Opcode.h | 30 + lib/AST/Interp/Opcodes.td | 422 ++++ lib/AST/Interp/Pointer.cpp | 193 ++ lib/AST/Interp/Pointer.h | 353 +++ lib/AST/Interp/PrimType.cpp | 23 + lib/AST/Interp/PrimType.h | 115 + lib/AST/Interp/Program.cpp | 364 +++ lib/AST/Interp/Program.h | 220 ++ lib/AST/Interp/Record.cpp | 46 + lib/AST/Interp/Record.h | 121 + lib/AST/Interp/Source.cpp | 39 + lib/AST/Interp/Source.h | 118 + lib/AST/Interp/State.cpp | 158 ++ lib/AST/Interp/State.h | 133 + lib/AST/ItaniumCXXABI.cpp | 72 +- lib/AST/ItaniumMangle.cpp | 154 +- lib/AST/JSONNodeDumper.cpp | 49 +- lib/AST/Mangle.cpp | 16 +- lib/AST/MicrosoftCXXABI.cpp | 4 +- lib/AST/MicrosoftMangle.cpp | 32 +- lib/AST/NSAPI.cpp | 14 +- lib/AST/OpenMPClause.cpp | 71 +- lib/AST/PrintfFormatString.cpp | 20 + lib/AST/RawCommentList.cpp | 64 +- lib/AST/Stmt.cpp | 11 + lib/AST/StmtOpenMP.cpp | 246 ++ lib/AST/StmtPrinter.cpp | 46 +- lib/AST/StmtProfile.cpp | 37 + lib/AST/TemplateBase.cpp | 2 +- lib/AST/TextNodeDumper.cpp | 17 +- lib/AST/Type.cpp | 113 +- lib/AST/TypeLoc.cpp | 3 + lib/AST/TypePrinter.cpp | 11 +- lib/AST/VTTBuilder.cpp | 12 +- lib/AST/VTableBuilder.cpp | 12 +- lib/ASTMatchers/ASTMatchFinder.cpp | 73 +- lib/ASTMatchers/Dynamic/Marshallers.h | 14 +- lib/ASTMatchers/Dynamic/Registry.cpp | 3 +- lib/Analysis/AnalysisDeclContext.cpp | 23 +- lib/Analysis/BodyFarm.cpp | 4 +- lib/Analysis/CFG.cpp | 432 +++- lib/Analysis/CallGraph.cpp | 42 +- lib/Analysis/CloneDetection.cpp | 3 +- lib/Analysis/CocoaConventions.cpp | 4 +- lib/Analysis/Consumed.cpp | 12 +- lib/Analysis/PathDiagnostic.cpp | 1219 ++++++++++ lib/Analysis/ProgramPoint.cpp | 6 +- lib/Analysis/ReachableCode.cpp | 2 +- lib/Analysis/RetainSummaryManager.cpp | 2 +- lib/Analysis/ThreadSafety.cpp | 30 +- .../plugins/SampleAnalyzer/MainCallChecker.cpp | 4 +- lib/Basic/Attributes.cpp | 75 +- lib/Basic/FileManager.cpp | 257 +- lib/Basic/IdentifierTable.cpp | 15 + lib/Basic/LangStandards.cpp | 45 + lib/Basic/Module.cpp | 4 +- lib/Basic/OpenMPKinds.cpp | 85 +- lib/Basic/SourceManager.cpp | 203 +- lib/Basic/Stack.cpp | 75 + lib/Basic/TargetInfo.cpp | 6 + lib/Basic/Targets.cpp | 23 +- lib/Basic/Targets/AArch64.cpp | 32 +- lib/Basic/Targets/AArch64.h | 1 + lib/Basic/Targets/AMDGPU.cpp | 8 +- lib/Basic/Targets/ARM.cpp | 103 +- lib/Basic/Targets/BPF.cpp | 12 + lib/Basic/Targets/BPF.h | 4 +- lib/Basic/Targets/OSTargets.h | 15 +- lib/Basic/Targets/PPC.cpp | 10 +- lib/Basic/Targets/PPC.h | 1 + lib/Basic/Targets/RISCV.cpp | 63 +- lib/Basic/Targets/RISCV.h | 20 +- lib/Basic/Targets/SPIR.h | 2 +- lib/Basic/Targets/Sparc.h | 1 + lib/Basic/Targets/SystemZ.cpp | 2 +- lib/Basic/Targets/X86.cpp | 20 +- lib/Basic/Targets/X86.h | 39 +- lib/Basic/TokenKinds.cpp | 20 + lib/CodeGen/BackendUtil.cpp | 85 +- lib/CodeGen/CGAtomic.cpp | 9 +- lib/CodeGen/CGBlocks.cpp | 5 +- lib/CodeGen/CGBuiltin.cpp | 518 ++-- lib/CodeGen/CGCUDANV.cpp | 23 +- lib/CodeGen/CGCXX.cpp | 6 +- lib/CodeGen/CGCXXABI.cpp | 4 +- lib/CodeGen/CGCXXABI.h | 2 +- lib/CodeGen/CGCall.cpp | 84 +- lib/CodeGen/CGClass.cpp | 51 +- lib/CodeGen/CGCleanup.cpp | 11 +- lib/CodeGen/CGDebugInfo.cpp | 97 +- lib/CodeGen/CGDecl.cpp | 39 +- lib/CodeGen/CGDeclCXX.cpp | 15 +- lib/CodeGen/CGException.cpp | 8 +- lib/CodeGen/CGExpr.cpp | 90 +- lib/CodeGen/CGExprAgg.cpp | 26 +- lib/CodeGen/CGExprCXX.cpp | 45 +- lib/CodeGen/CGExprComplex.cpp | 4 + lib/CodeGen/CGExprConstant.cpp | 10 +- lib/CodeGen/CGExprScalar.cpp | 223 +- lib/CodeGen/CGLoopInfo.cpp | 60 +- lib/CodeGen/CGLoopInfo.h | 12 +- lib/CodeGen/CGNonTrivialStruct.cpp | 2 +- lib/CodeGen/CGObjC.cpp | 4 +- lib/CodeGen/CGObjCGNU.cpp | 13 +- lib/CodeGen/CGObjCMac.cpp | 47 +- lib/CodeGen/CGOpenMPRuntime.cpp | 1214 +++++++--- lib/CodeGen/CGOpenMPRuntime.h | 106 +- lib/CodeGen/CGOpenMPRuntimeNVPTX.cpp | 128 +- lib/CodeGen/CGOpenMPRuntimeNVPTX.h | 12 + lib/CodeGen/CGStmt.cpp | 72 +- lib/CodeGen/CGStmtOpenMP.cpp | 160 +- lib/CodeGen/CGVTables.cpp | 92 +- lib/CodeGen/CodeGenAction.cpp | 41 +- lib/CodeGen/CodeGenFunction.cpp | 56 +- lib/CodeGen/CodeGenFunction.h | 15 +- lib/CodeGen/CodeGenModule.cpp | 233 +- lib/CodeGen/CodeGenModule.h | 15 +- lib/CodeGen/CodeGenPGO.cpp | 2 +- lib/CodeGen/CodeGenPGO.h | 4 +- lib/CodeGen/CodeGenTypes.cpp | 25 +- lib/CodeGen/ConstantInitBuilder.cpp | 2 +- lib/CodeGen/CoverageMappingGen.cpp | 27 +- lib/CodeGen/CoverageMappingGen.h | 8 +- lib/CodeGen/EHScopeStack.h | 4 +- lib/CodeGen/ItaniumCXXABI.cpp | 246 +- lib/CodeGen/MicrosoftCXXABI.cpp | 17 +- lib/CodeGen/ModuleBuilder.cpp | 15 +- lib/CodeGen/ObjectFilePCHContainerOperations.cpp | 12 +- lib/CodeGen/TargetInfo.cpp | 362 ++- lib/CrossTU/CrossTranslationUnit.cpp | 261 +- .../default/DirectoryWatcher-not-implemented.cpp | 6 +- .../linux/DirectoryWatcher-linux.cpp | 33 +- lib/DirectoryWatcher/mac/DirectoryWatcher-mac.cpp | 34 +- .../windows/DirectoryWatcher-windows.cpp | 50 + lib/Driver/Action.cpp | 14 + lib/Driver/Compilation.cpp | 2 +- lib/Driver/Driver.cpp | 524 ++-- lib/Driver/DriverOptions.cpp | 17 +- lib/Driver/Phases.cpp | 1 + lib/Driver/SanitizerArgs.cpp | 40 +- lib/Driver/ToolChain.cpp | 32 + lib/Driver/ToolChains/AMDGPU.cpp | 2 +- lib/Driver/ToolChains/AVR.cpp | 2 +- lib/Driver/ToolChains/Ananas.cpp | 4 +- lib/Driver/ToolChains/Arch/AArch64.cpp | 1 + lib/Driver/ToolChains/Arch/ARM.cpp | 50 +- lib/Driver/ToolChains/Arch/Mips.cpp | 25 +- lib/Driver/ToolChains/Arch/Mips.h | 3 +- lib/Driver/ToolChains/Arch/PPC.cpp | 4 +- lib/Driver/ToolChains/Arch/RISCV.cpp | 311 +-- lib/Driver/ToolChains/Arch/RISCV.h | 3 +- lib/Driver/ToolChains/Arch/X86.cpp | 1 + lib/Driver/ToolChains/BareMetal.cpp | 2 +- lib/Driver/ToolChains/Clang.cpp | 618 +++-- lib/Driver/ToolChains/Clang.h | 18 + lib/Driver/ToolChains/CloudABI.cpp | 2 +- lib/Driver/ToolChains/CommonArgs.cpp | 356 +-- lib/Driver/ToolChains/CommonArgs.h | 16 +- lib/Driver/ToolChains/CrossWindows.cpp | 4 +- lib/Driver/ToolChains/Cuda.cpp | 12 +- lib/Driver/ToolChains/Darwin.cpp | 41 +- lib/Driver/ToolChains/DragonFly.cpp | 4 +- lib/Driver/ToolChains/FreeBSD.cpp | 24 +- lib/Driver/ToolChains/FreeBSD.h | 2 +- lib/Driver/ToolChains/Fuchsia.cpp | 4 +- lib/Driver/ToolChains/Fuchsia.h | 4 +- lib/Driver/ToolChains/Gnu.cpp | 23 +- lib/Driver/ToolChains/HIP.cpp | 64 +- lib/Driver/ToolChains/HIP.h | 3 +- lib/Driver/ToolChains/Hexagon.cpp | 4 +- lib/Driver/ToolChains/InterfaceStubs.cpp | 37 + lib/Driver/ToolChains/InterfaceStubs.h | 36 + lib/Driver/ToolChains/Linux.cpp | 15 +- lib/Driver/ToolChains/MSP430.cpp | 2 +- lib/Driver/ToolChains/MSVC.cpp | 17 +- lib/Driver/ToolChains/MSVC.h | 14 +- lib/Driver/ToolChains/MinGW.cpp | 4 +- lib/Driver/ToolChains/MinGW.h | 3 +- lib/Driver/ToolChains/Minix.cpp | 4 +- lib/Driver/ToolChains/Myriad.cpp | 6 +- lib/Driver/ToolChains/NaCl.cpp | 2 +- lib/Driver/ToolChains/NetBSD.cpp | 25 +- lib/Driver/ToolChains/OpenBSD.cpp | 4 +- lib/Driver/ToolChains/PPCLinux.cpp | 5 +- lib/Driver/ToolChains/PS4CPU.cpp | 6 +- lib/Driver/ToolChains/RISCVToolchain.cpp | 9 +- lib/Driver/ToolChains/RISCVToolchain.h | 1 + lib/Driver/ToolChains/Solaris.cpp | 30 +- lib/Driver/ToolChains/WebAssembly.cpp | 31 +- lib/Driver/ToolChains/XCore.cpp | 4 +- lib/Driver/Types.cpp | 131 +- lib/Driver/XRayArgs.cpp | 2 +- lib/Format/BreakableToken.cpp | 11 +- lib/Format/ContinuationIndenter.cpp | 23 +- lib/Format/Encoding.h | 3 +- lib/Format/Format.cpp | 246 +- lib/Format/FormatToken.h | 13 + lib/Format/FormatTokenLexer.cpp | 20 +- lib/Format/FormatTokenLexer.h | 1 + lib/Format/NamespaceEndCommentsFixer.cpp | 16 +- lib/Format/TokenAnnotator.cpp | 132 +- lib/Format/TokenAnnotator.h | 3 +- lib/Format/UnwrappedLineFormatter.cpp | 63 +- lib/Format/UnwrappedLineParser.cpp | 41 +- lib/Format/UnwrappedLineParser.h | 2 +- lib/Format/WhitespaceManager.cpp | 41 +- lib/Frontend/ASTConsumers.cpp | 8 +- lib/Frontend/ASTUnit.cpp | 74 +- lib/Frontend/ChainedIncludesSource.cpp | 2 +- lib/Frontend/CompilerInstance.cpp | 117 +- lib/Frontend/CompilerInvocation.cpp | 376 ++- lib/Frontend/CreateInvocationFromCommandLine.cpp | 16 +- lib/Frontend/DependencyFile.cpp | 25 +- lib/Frontend/DependencyGraph.cpp | 4 +- lib/Frontend/FrontendAction.cpp | 29 +- lib/Frontend/FrontendActions.cpp | 61 +- lib/Frontend/FrontendOptions.cpp | 37 +- lib/Frontend/HeaderIncludeGen.cpp | 5 +- lib/Frontend/InitHeaderSearch.cpp | 19 +- lib/Frontend/InitPreprocessor.cpp | 79 +- lib/Frontend/InterfaceStubFunctionsConsumer.cpp | 106 +- lib/Frontend/LangStandards.cpp | 42 - lib/Frontend/ModuleDependencyCollector.cpp | 8 +- lib/Frontend/MultiplexConsumer.cpp | 4 +- lib/Frontend/PrecompiledPreamble.cpp | 24 +- lib/Frontend/PrintPreprocessedOutput.cpp | 2 +- lib/Frontend/Rewrite/FixItRewriter.cpp | 2 +- lib/Frontend/Rewrite/FrontendActions.cpp | 15 +- lib/Frontend/Rewrite/HTMLPrint.cpp | 2 +- lib/Frontend/Rewrite/InclusionRewriter.cpp | 180 +- lib/Frontend/Rewrite/RewriteModernObjC.cpp | 45 +- lib/Frontend/Rewrite/RewriteObjC.cpp | 23 +- lib/Frontend/SerializedDiagnosticPrinter.cpp | 8 +- lib/Frontend/TextDiagnostic.cpp | 11 +- lib/Frontend/VerifyDiagnosticConsumer.cpp | 11 +- lib/FrontendTool/ExecuteCompilerInvocation.cpp | 99 +- lib/Headers/__clang_cuda_intrinsics.h | 10 +- lib/Headers/altivec.h | 85 +- lib/Headers/arm_acle.h | 24 +- lib/Headers/avx512fintrin.h | 27 +- lib/Headers/bmiintrin.h | 175 +- lib/Headers/cpuid.h | 4 +- lib/Headers/emmintrin.h | 6 +- lib/Headers/ia32intrin.h | 68 + lib/Headers/immintrin.h | 3 +- lib/Headers/opencl-c-base.h | 19 +- lib/Headers/opencl-c.h | 212 +- lib/Headers/ppc_wrappers/emmintrin.h | 6 + lib/Headers/ppc_wrappers/mm_malloc.h | 6 + lib/Headers/ppc_wrappers/mmintrin.h | 7 + lib/Headers/ppc_wrappers/pmmintrin.h | 150 ++ lib/Headers/ppc_wrappers/smmintrin.h | 85 + lib/Headers/ppc_wrappers/tmmintrin.h | 495 ++++ lib/Headers/ppc_wrappers/xmmintrin.h | 6 + lib/Index/CodegenNameGenerator.cpp | 36 - lib/Index/IndexSymbol.cpp | 2 +- lib/Index/IndexingAction.cpp | 178 +- lib/Index/USRGeneration.cpp | 3 + lib/Lex/DependencyDirectivesSourceMinimizer.cpp | 250 +- lib/Lex/HeaderMap.cpp | 8 +- lib/Lex/HeaderSearch.cpp | 348 +-- lib/Lex/Lexer.cpp | 9 + lib/Lex/MacroArgs.cpp | 20 - lib/Lex/ModuleMap.cpp | 73 +- lib/Lex/PPDirectives.cpp | 391 +-- lib/Lex/PPLexerChange.cpp | 25 +- lib/Lex/PPMacroExpansion.cpp | 52 +- lib/Lex/Pragma.cpp | 85 +- lib/Lex/Preprocessor.cpp | 13 +- lib/Lex/TokenLexer.cpp | 16 +- lib/Lex/UnicodeCharSets.h | 2 +- lib/Parse/ParseCXXInlineMethods.cpp | 2 +- lib/Parse/ParseDecl.cpp | 130 +- lib/Parse/ParseDeclCXX.cpp | 16 +- lib/Parse/ParseExpr.cpp | 39 +- lib/Parse/ParseExprCXX.cpp | 77 +- lib/Parse/ParseInit.cpp | 27 + lib/Parse/ParseObjc.cpp | 4 - lib/Parse/ParseOpenMP.cpp | 478 +++- lib/Parse/ParsePragma.cpp | 136 +- lib/Parse/ParseStmt.cpp | 26 +- lib/Parse/ParseTemplate.cpp | 8 +- lib/Parse/ParseTentative.cpp | 7 +- lib/Parse/Parser.cpp | 47 +- lib/Rewrite/Rewriter.cpp | 11 + lib/Sema/AnalysisBasedWarnings.cpp | 82 +- lib/Sema/DeclSpec.cpp | 15 +- lib/Sema/OpenCLBuiltins.td | 744 ++++-- lib/Sema/ParsedAttr.cpp | 65 - lib/Sema/Sema.cpp | 91 +- lib/Sema/SemaAccess.cpp | 6 +- lib/Sema/SemaAttr.cpp | 135 +- lib/Sema/SemaCUDA.cpp | 93 +- lib/Sema/SemaCXXScopeSpec.cpp | 2 +- lib/Sema/SemaCast.cpp | 17 +- lib/Sema/SemaChecking.cpp | 612 ++++- lib/Sema/SemaCodeComplete.cpp | 3 + lib/Sema/SemaConcept.cpp | 125 + lib/Sema/SemaCoroutine.cpp | 2 +- lib/Sema/SemaDecl.cpp | 884 ++++--- lib/Sema/SemaDeclAttr.cpp | 1101 ++++----- lib/Sema/SemaDeclCXX.cpp | 577 +++-- lib/Sema/SemaDeclObjC.cpp | 14 +- lib/Sema/SemaExceptionSpec.cpp | 23 +- lib/Sema/SemaExpr.cpp | 991 +++++--- lib/Sema/SemaExprCXX.cpp | 295 ++- lib/Sema/SemaExprMember.cpp | 20 +- lib/Sema/SemaExprObjC.cpp | 4 +- lib/Sema/SemaInit.cpp | 1210 ++++++---- lib/Sema/SemaLambda.cpp | 127 +- lib/Sema/SemaLookup.cpp | 853 ++++--- lib/Sema/SemaModule.cpp | 2 + lib/Sema/SemaObjCProperty.cpp | 14 +- lib/Sema/SemaOpenMP.cpp | 1942 ++++++++++++--- lib/Sema/SemaOverload.cpp | 653 +++-- lib/Sema/SemaStmt.cpp | 151 +- lib/Sema/SemaStmtAsm.cpp | 32 +- lib/Sema/SemaStmtAttr.cpp | 31 +- lib/Sema/SemaTemplate.cpp | 274 ++- lib/Sema/SemaTemplateDeduction.cpp | 47 +- lib/Sema/SemaTemplateInstantiate.cpp | 69 +- lib/Sema/SemaTemplateInstantiateDecl.cpp | 213 +- lib/Sema/SemaTemplateVariadic.cpp | 74 +- lib/Sema/SemaType.cpp | 214 +- lib/Sema/TreeTransform.h | 220 +- lib/Sema/TypeLocBuilder.cpp | 2 +- lib/Sema/TypeLocBuilder.h | 12 +- lib/Serialization/ASTCommon.cpp | 5 + lib/Serialization/ASTReader.cpp | 223 +- lib/Serialization/ASTReaderDecl.cpp | 171 +- lib/Serialization/ASTReaderStmt.cpp | 86 + lib/Serialization/ASTWriter.cpp | 174 +- lib/Serialization/ASTWriterDecl.cpp | 15 +- lib/Serialization/ASTWriterStmt.cpp | 50 + lib/Serialization/GlobalModuleIndex.cpp | 49 +- lib/Serialization/ModuleManager.cpp | 35 +- lib/Serialization/PCHContainerOperations.cpp | 6 +- lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp | 3 +- .../Checkers/ArrayBoundCheckerV2.cpp | 4 +- .../Checkers/BasicObjCFoundationChecks.cpp | 11 +- .../Checkers/BlockInCriticalSectionChecker.cpp | 5 +- .../Checkers/BoolAssignmentChecker.cpp | 4 +- lib/StaticAnalyzer/Checkers/CStringChecker.cpp | 72 +- .../Checkers/CStringSyntaxChecker.cpp | 27 +- .../Checkers/CallAndMessageChecker.cpp | 23 +- lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp | 3 +- lib/StaticAnalyzer/Checkers/CastValueChecker.cpp | 445 +++- lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp | 15 +- .../Checkers/CheckObjCInstMethSignature.cpp | 2 +- .../Checkers/CheckSecuritySyntaxOnly.cpp | 43 +- lib/StaticAnalyzer/Checkers/ChrootChecker.cpp | 2 +- lib/StaticAnalyzer/Checkers/CloneChecker.cpp | 6 +- lib/StaticAnalyzer/Checkers/ConversionChecker.cpp | 2 +- lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp | 106 +- lib/StaticAnalyzer/Checkers/DebugCheckers.cpp | 3 +- .../Checkers/DeleteWithNonVirtualDtorChecker.cpp | 21 +- lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp | 6 +- lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp | 4 +- lib/StaticAnalyzer/Checkers/DynamicTypeChecker.cpp | 25 +- .../Checkers/DynamicTypePropagation.cpp | 54 +- .../Checkers/EnumCastOutOfRangeChecker.cpp | 18 +- .../Checkers/ExprInspectionChecker.cpp | 4 +- .../Checkers/FixedAddressChecker.cpp | 3 +- .../Checkers/GenericTaintChecker.cpp | 264 +- .../Checkers/InnerPointerChecker.cpp | 19 +- lib/StaticAnalyzer/Checkers/IteratorChecker.cpp | 51 +- .../Checkers/IvarInvalidationChecker.cpp | 14 +- .../Checkers/LocalizationChecker.cpp | 42 +- lib/StaticAnalyzer/Checkers/MIGChecker.cpp | 5 +- .../Checkers/MPI-Checker/MPIBugReporter.cpp | 24 +- .../Checkers/MPI-Checker/MPIBugReporter.h | 6 +- .../Checkers/MacOSKeychainAPIChecker.cpp | 43 +- lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp | 3 +- lib/StaticAnalyzer/Checkers/MallocChecker.cpp | 1287 ++++++---- .../Checkers/MallocSizeofChecker.cpp | 2 +- .../Checkers/MmapWriteExecChecker.cpp | 2 +- lib/StaticAnalyzer/Checkers/MoveChecker.cpp | 23 +- .../Checkers/NSAutoreleasePoolChecker.cpp | 8 +- lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp | 3 +- .../Checkers/NonNullParamChecker.cpp | 31 +- lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp | 36 +- lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp | 8 +- .../Checkers/ObjCContainersChecker.cpp | 7 +- .../Checkers/ObjCMissingSuperCallChecker.cpp | 2 +- .../Checkers/ObjCSelfInitChecker.cpp | 2 +- .../Checkers/ObjCSuperDeallocChecker.cpp | 20 +- .../Checkers/ObjCUnusedIVarsChecker.cpp | 6 +- lib/StaticAnalyzer/Checkers/PaddingChecker.cpp | 5 +- .../Checkers/PointerArithChecker.cpp | 18 +- lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp | 3 +- lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp | 14 +- .../RetainCountChecker/RetainCountChecker.cpp | 10 +- .../RetainCountChecker/RetainCountChecker.h | 2 +- .../RetainCountChecker/RetainCountDiagnostics.cpp | 61 +- .../RetainCountChecker/RetainCountDiagnostics.h | 18 +- .../Checkers/ReturnPointerRangeChecker.cpp | 3 +- lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp | 3 +- .../Checkers/SimpleStreamChecker.cpp | 9 +- .../Checkers/StackAddrEscapeChecker.cpp | 22 +- lib/StaticAnalyzer/Checkers/StreamChecker.cpp | 8 +- lib/StaticAnalyzer/Checkers/Taint.cpp | 8 +- lib/StaticAnalyzer/Checkers/Taint.h | 6 +- lib/StaticAnalyzer/Checkers/TaintTesterChecker.cpp | 2 +- .../Checkers/TestAfterDivZeroChecker.cpp | 16 +- lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp | 3 +- .../Checkers/UndefCapturedBlockVarChecker.cpp | 7 +- lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp | 2 +- .../Checkers/UndefinedArraySubscriptChecker.cpp | 2 +- .../Checkers/UndefinedAssignmentChecker.cpp | 4 +- .../UninitializedObjectChecker.cpp | 6 +- .../UninitializedObject/UninitializedPointee.cpp | 11 +- lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp | 8 +- .../Checkers/UnreachableCodeChecker.cpp | 2 +- lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp | 4 +- lib/StaticAnalyzer/Checkers/ValistChecker.cpp | 44 +- lib/StaticAnalyzer/Checkers/VforkChecker.cpp | 2 +- lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp | 203 +- lib/StaticAnalyzer/Checkers/Yaml.h | 59 + lib/StaticAnalyzer/Core/AnalysisManager.cpp | 4 +- lib/StaticAnalyzer/Core/AnalyzerOptions.cpp | 19 - lib/StaticAnalyzer/Core/BugReporter.cpp | 1858 +++++++------- lib/StaticAnalyzer/Core/BugReporterVisitors.cpp | 660 ++--- lib/StaticAnalyzer/Core/CallEvent.cpp | 24 +- lib/StaticAnalyzer/Core/Checker.cpp | 8 +- lib/StaticAnalyzer/Core/CheckerHelpers.cpp | 2 +- lib/StaticAnalyzer/Core/CheckerManager.cpp | 2 +- lib/StaticAnalyzer/Core/CommonBugCategories.cpp | 1 + lib/StaticAnalyzer/Core/DynamicType.cpp | 229 ++ lib/StaticAnalyzer/Core/DynamicTypeMap.cpp | 97 - lib/StaticAnalyzer/Core/Environment.cpp | 1 + lib/StaticAnalyzer/Core/ExplodedGraph.cpp | 120 +- lib/StaticAnalyzer/Core/ExprEngine.cpp | 91 +- lib/StaticAnalyzer/Core/ExprEngineC.cpp | 3 +- lib/StaticAnalyzer/Core/ExprEngineCXX.cpp | 36 +- .../Core/ExprEngineCallAndReturn.cpp | 5 +- lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp | 50 +- lib/StaticAnalyzer/Core/LoopUnrolling.cpp | 4 +- lib/StaticAnalyzer/Core/MemRegion.cpp | 5 +- lib/StaticAnalyzer/Core/PathDiagnostic.cpp | 1466 ------------ lib/StaticAnalyzer/Core/PlistDiagnostics.cpp | 157 +- lib/StaticAnalyzer/Core/ProgramState.cpp | 2 +- lib/StaticAnalyzer/Core/RangeConstraintManager.cpp | 2 +- lib/StaticAnalyzer/Core/RegionStore.cpp | 89 +- lib/StaticAnalyzer/Core/SMTConstraintManager.cpp | 2 +- lib/StaticAnalyzer/Core/SarifDiagnostics.cpp | 123 +- lib/StaticAnalyzer/Core/Store.cpp | 2 +- lib/StaticAnalyzer/Core/WorkList.cpp | 12 +- lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp | 106 +- .../Frontend/CheckerRegistration.cpp | 23 +- lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp | 24 +- lib/StaticAnalyzer/Frontend/FrontendActions.cpp | 2 +- lib/StaticAnalyzer/Frontend/ModelInjector.cpp | 3 +- lib/Tooling/ASTDiff/ASTDiff.cpp | 16 +- lib/Tooling/AllTUsExecution.cpp | 5 +- lib/Tooling/ArgumentsAdjusters.cpp | 16 + lib/Tooling/CommonOptionsParser.cpp | 2 +- lib/Tooling/CompilationDatabase.cpp | 4 +- lib/Tooling/Core/Replacement.cpp | 11 +- .../DependencyScanningFilesystem.cpp | 234 ++ .../DependencyScanningService.cpp | 19 + .../DependencyScanning/DependencyScanningTool.cpp | 71 + .../DependencyScanningWorker.cpp | 140 +- .../GuessTargetAndModeCompilationDatabase.cpp | 2 +- lib/Tooling/Inclusions/HeaderIncludes.cpp | 14 + lib/Tooling/Inclusions/IncludeStyle.cpp | 1 + lib/Tooling/InterpolatingCompilationDatabase.cpp | 33 +- lib/Tooling/Refactoring.cpp | 5 +- .../Refactoring/ASTSelectionRequirements.cpp | 2 +- lib/Tooling/Refactoring/Extract/Extract.cpp | 2 +- .../Refactoring/Extract/SourceExtraction.cpp | 8 +- lib/Tooling/Refactoring/Extract/SourceExtraction.h | 51 - lib/Tooling/Refactoring/RangeSelector.cpp | 296 --- lib/Tooling/Refactoring/RefactoringActions.cpp | 4 +- lib/Tooling/Refactoring/Rename/RenamingAction.cpp | 4 +- .../Refactoring/Rename/SymbolOccurrences.cpp | 2 +- .../Refactoring/Rename/USRFindingAction.cpp | 6 +- lib/Tooling/Refactoring/SourceCode.cpp | 31 - lib/Tooling/Refactoring/Stencil.cpp | 175 -- lib/Tooling/Refactoring/Transformer.cpp | 263 -- lib/Tooling/RefactoringCallbacks.cpp | 2 +- lib/Tooling/StandaloneExecution.cpp | 2 +- lib/Tooling/Syntax/BuildTree.cpp | 12 +- lib/Tooling/Syntax/Tokens.cpp | 17 +- lib/Tooling/Tooling.cpp | 84 +- lib/Tooling/Transformer/CMakeLists.txt | 18 + lib/Tooling/Transformer/RangeSelector.cpp | 314 +++ lib/Tooling/Transformer/RewriteRule.cpp | 178 ++ lib/Tooling/Transformer/SourceCode.cpp | 65 + lib/Tooling/Transformer/SourceCodeBuilders.cpp | 160 ++ lib/Tooling/Transformer/Stencil.cpp | 318 +++ lib/Tooling/Transformer/Transformer.cpp | 72 + tools/clang-format/ClangFormat.cpp | 284 ++- tools/clang-offload-wrapper/CMakeLists.txt | 23 + .../clang-offload-wrapper/ClangOffloadWrapper.cpp | 371 +++ tools/driver/cc1_main.cpp | 44 +- tools/driver/cc1as_main.cpp | 38 +- tools/driver/driver.cpp | 8 +- utils/TableGen/ClangASTNodesEmitter.cpp | 10 +- utils/TableGen/ClangAttrEmitter.cpp | 264 +- utils/TableGen/ClangCommentCommandInfoEmitter.cpp | 8 +- ...ngCommentHTMLNamedCharacterReferenceEmitter.cpp | 9 +- utils/TableGen/ClangDataCollectorsEmitter.cpp | 5 +- utils/TableGen/ClangDiagnosticsEmitter.cpp | 18 +- utils/TableGen/ClangOpcodesEmitter.cpp | 357 +++ utils/TableGen/ClangOpenCLBuiltinEmitter.cpp | 540 ++++- utils/TableGen/ClangOptionDocEmitter.cpp | 9 +- utils/TableGen/ClangSACheckersEmitter.cpp | 5 +- utils/TableGen/ClangTypeNodesEmitter.cpp | 220 ++ utils/TableGen/NeonEmitter.cpp | 60 +- utils/TableGen/TableGen.cpp | 12 + utils/TableGen/TableGenBackends.h | 2 + 764 files changed, 49781 insertions(+), 20543 deletions(-) create mode 100644 include/clang-c/FatalErrorHandler.h create mode 100644 include/clang/AST/CXXRecordDeclDefinitionBits.def create mode 100644 include/clang/AST/OptionalDiagnostic.h delete mode 100644 include/clang/AST/TypeNodes.def create mode 100644 include/clang/Analysis/PathDiagnostic.h create mode 100644 include/clang/Basic/AArch64SVEACLETypes.def create mode 100644 include/clang/Basic/AttributeCommonInfo.h create mode 100644 include/clang/Basic/BuiltinsBPF.def create mode 100644 include/clang/Basic/LangStandard.h create mode 100644 include/clang/Basic/LangStandards.def create mode 100644 include/clang/Basic/TypeNodes.td delete mode 100644 include/clang/Frontend/LangStandard.h delete mode 100644 include/clang/Frontend/LangStandards.def delete mode 100644 include/clang/Index/CodegenNameGenerator.h create mode 100644 include/clang/Index/IndexingOptions.h create mode 100644 include/clang/Lex/PreprocessorExcludedConditionalDirectiveSkipMapping.h delete mode 100644 include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h create mode 100644 include/clang/StaticAnalyzer/Core/PathSensitive/DynamicCastInfo.h create mode 100644 include/clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h delete mode 100644 include/clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h create mode 100644 include/clang/Tooling/DependencyScanning/DependencyScanningFilesystem.h create mode 100644 include/clang/Tooling/DependencyScanning/DependencyScanningService.h create mode 100644 include/clang/Tooling/DependencyScanning/DependencyScanningTool.h create mode 100755 include/clang/Tooling/Refactoring/Extract/SourceExtraction.h delete mode 100644 include/clang/Tooling/Refactoring/RangeSelector.h delete mode 100644 include/clang/Tooling/Refactoring/SourceCode.h delete mode 100644 include/clang/Tooling/Refactoring/Stencil.h delete mode 100644 include/clang/Tooling/Refactoring/Transformer.h create mode 100644 include/clang/Tooling/Transformer/MatchConsumer.h create mode 100644 include/clang/Tooling/Transformer/RangeSelector.h create mode 100644 include/clang/Tooling/Transformer/RewriteRule.h create mode 100644 include/clang/Tooling/Transformer/SourceCode.h create mode 100644 include/clang/Tooling/Transformer/SourceCodeBuilders.h create mode 100644 include/clang/Tooling/Transformer/Stencil.h create mode 100644 include/clang/Tooling/Transformer/Transformer.h create mode 100644 lib/AST/Interp/Block.cpp create mode 100644 lib/AST/Interp/Block.h create mode 100644 lib/AST/Interp/Boolean.h create mode 100644 lib/AST/Interp/ByteCodeEmitter.cpp create mode 100644 lib/AST/Interp/ByteCodeEmitter.h create mode 100644 lib/AST/Interp/ByteCodeExprGen.cpp create mode 100644 lib/AST/Interp/ByteCodeExprGen.h create mode 100644 lib/AST/Interp/ByteCodeGenError.cpp create mode 100644 lib/AST/Interp/ByteCodeGenError.h create mode 100644 lib/AST/Interp/ByteCodeStmtGen.cpp create mode 100644 lib/AST/Interp/ByteCodeStmtGen.h create mode 100644 lib/AST/Interp/Context.cpp create mode 100644 lib/AST/Interp/Context.h create mode 100644 lib/AST/Interp/Descriptor.cpp create mode 100644 lib/AST/Interp/Descriptor.h create mode 100644 lib/AST/Interp/Disasm.cpp create mode 100644 lib/AST/Interp/EvalEmitter.cpp create mode 100644 lib/AST/Interp/EvalEmitter.h create mode 100644 lib/AST/Interp/Frame.cpp create mode 100644 lib/AST/Interp/Frame.h create mode 100644 lib/AST/Interp/Function.cpp create mode 100644 lib/AST/Interp/Function.h create mode 100644 lib/AST/Interp/Integral.h create mode 100644 lib/AST/Interp/Interp.cpp create mode 100644 lib/AST/Interp/Interp.h create mode 100644 lib/AST/Interp/InterpFrame.cpp create mode 100644 lib/AST/Interp/InterpFrame.h create mode 100644 lib/AST/Interp/InterpStack.cpp create mode 100644 lib/AST/Interp/InterpStack.h create mode 100644 lib/AST/Interp/InterpState.cpp create mode 100644 lib/AST/Interp/InterpState.h create mode 100644 lib/AST/Interp/Opcode.h create mode 100644 lib/AST/Interp/Opcodes.td create mode 100644 lib/AST/Interp/Pointer.cpp create mode 100644 lib/AST/Interp/Pointer.h create mode 100644 lib/AST/Interp/PrimType.cpp create mode 100644 lib/AST/Interp/PrimType.h create mode 100644 lib/AST/Interp/Program.cpp create mode 100644 lib/AST/Interp/Program.h create mode 100644 lib/AST/Interp/Record.cpp create mode 100644 lib/AST/Interp/Record.h create mode 100644 lib/AST/Interp/Source.cpp create mode 100644 lib/AST/Interp/Source.h create mode 100644 lib/AST/Interp/State.cpp create mode 100644 lib/AST/Interp/State.h create mode 100644 lib/Analysis/PathDiagnostic.cpp create mode 100644 lib/Basic/LangStandards.cpp create mode 100644 lib/Basic/Stack.cpp create mode 100644 lib/DirectoryWatcher/windows/DirectoryWatcher-windows.cpp create mode 100644 lib/Driver/ToolChains/InterfaceStubs.cpp create mode 100644 lib/Driver/ToolChains/InterfaceStubs.h delete mode 100644 lib/Frontend/LangStandards.cpp create mode 100644 lib/Headers/ppc_wrappers/pmmintrin.h create mode 100644 lib/Headers/ppc_wrappers/smmintrin.h create mode 100644 lib/Headers/ppc_wrappers/tmmintrin.h delete mode 100644 lib/Index/CodegenNameGenerator.cpp create mode 100644 lib/Sema/SemaConcept.cpp create mode 100755 lib/StaticAnalyzer/Checkers/Yaml.h create mode 100644 lib/StaticAnalyzer/Core/DynamicType.cpp delete mode 100644 lib/StaticAnalyzer/Core/DynamicTypeMap.cpp delete mode 100644 lib/StaticAnalyzer/Core/PathDiagnostic.cpp create mode 100644 lib/Tooling/DependencyScanning/DependencyScanningFilesystem.cpp create mode 100644 lib/Tooling/DependencyScanning/DependencyScanningService.cpp create mode 100644 lib/Tooling/DependencyScanning/DependencyScanningTool.cpp delete mode 100644 lib/Tooling/Refactoring/Extract/SourceExtraction.h delete mode 100644 lib/Tooling/Refactoring/RangeSelector.cpp delete mode 100644 lib/Tooling/Refactoring/SourceCode.cpp delete mode 100644 lib/Tooling/Refactoring/Stencil.cpp delete mode 100644 lib/Tooling/Refactoring/Transformer.cpp create mode 100644 lib/Tooling/Transformer/CMakeLists.txt create mode 100644 lib/Tooling/Transformer/RangeSelector.cpp create mode 100644 lib/Tooling/Transformer/RewriteRule.cpp create mode 100644 lib/Tooling/Transformer/SourceCode.cpp create mode 100644 lib/Tooling/Transformer/SourceCodeBuilders.cpp create mode 100644 lib/Tooling/Transformer/Stencil.cpp create mode 100644 lib/Tooling/Transformer/Transformer.cpp create mode 100644 tools/clang-offload-wrapper/CMakeLists.txt create mode 100644 tools/clang-offload-wrapper/ClangOffloadWrapper.cpp create mode 100644 utils/TableGen/ClangOpcodesEmitter.cpp create mode 100644 utils/TableGen/ClangTypeNodesEmitter.cpp diff --git a/include/clang-c/FatalErrorHandler.h b/include/clang-c/FatalErrorHandler.h new file mode 100644 index 0000000000000..ce8ff2cae735f --- /dev/null +++ b/include/clang-c/FatalErrorHandler.h @@ -0,0 +1,33 @@ +/*===-- clang-c/FatalErrorHandler.h - Fatal Error Handling --------*- C -*-===*\ +|* *| +|* Part of the LLVM Project, under the Apache License v2.0 with LLVM *| +|* Exceptions. *| +|* See https://llvm.org/LICENSE.txt for license information. *| +|* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception *| +|* *| +\*===----------------------------------------------------------------------===*/ + +#ifndef LLVM_CLANG_C_FATAL_ERROR_HANDLER_H +#define LLVM_CLANG_C_FATAL_ERROR_HANDLER_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Installs error handler that prints error message to stderr and calls abort(). + * Replaces currently installed error handler (if any). + */ +void clang_install_aborting_llvm_fatal_error_handler(void); + +/** + * Removes currently installed error handler (if any). + * If no error handler is intalled, the default strategy is to print error + * message to stderr and call exit(1). + */ +void clang_uninstall_llvm_fatal_error_handler(void); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/include/clang-c/Index.h b/include/clang-c/Index.h index 74badac740b64..226893505437d 100644 --- a/include/clang-c/Index.h +++ b/include/clang-c/Index.h @@ -1356,7 +1356,12 @@ enum CXTranslationUnit_Flags { * the case where these warnings are not of interest, as for an IDE for * example, which typically shows only the diagnostics in the main file. */ - CXTranslationUnit_IgnoreNonErrorsFromIncludedFiles = 0x4000 + CXTranslationUnit_IgnoreNonErrorsFromIncludedFiles = 0x4000, + + /** + * Tells the preprocessor not to skip excluded conditional blocks. + */ + CXTranslationUnit_RetainExcludedConditionalBlocks = 0x8000 }; /** @@ -2550,7 +2555,20 @@ enum CXCursorKind { */ CXCursor_BuiltinBitCastExpr = 280, - CXCursor_LastStmt = CXCursor_BuiltinBitCastExpr, + /** OpenMP master taskloop directive. + */ + CXCursor_OMPMasterTaskLoopDirective = 281, + + /** OpenMP parallel master taskloop directive. + */ + CXCursor_OMPParallelMasterTaskLoopDirective = 282, + + /** OpenMP master taskloop simd directive. + */ + CXCursor_OMPMasterTaskLoopSimdDirective = 283, + + + CXCursor_LastStmt = CXCursor_OMPMasterTaskLoopSimdDirective, /** * Cursor that represents the translation unit itself. diff --git a/include/clang/AST/APValue.h b/include/clang/AST/APValue.h index 6943479831ecf..63359294ef632 100644 --- a/include/clang/AST/APValue.h +++ b/include/clang/AST/APValue.h @@ -53,6 +53,34 @@ public: void print(llvm::raw_ostream &Out, const PrintingPolicy &Policy) const; }; + +/// Symbolic representation of a dynamic allocation. +class DynamicAllocLValue { + unsigned Index; + +public: + DynamicAllocLValue() : Index(0) {} + explicit DynamicAllocLValue(unsigned Index) : Index(Index + 1) {} + unsigned getIndex() { return Index - 1; } + + explicit operator bool() const { return Index != 0; } + + void *getOpaqueValue() { + return reinterpret_cast(static_cast(Index) + << NumLowBitsAvailable); + } + static DynamicAllocLValue getFromOpaqueValue(void *Value) { + DynamicAllocLValue V; + V.Index = reinterpret_cast(Value) >> NumLowBitsAvailable; + return V; + } + + static unsigned getMaxIndex() { + return (std::numeric_limits::max() >> NumLowBitsAvailable) - 1; + } + + static constexpr int NumLowBitsAvailable = 3; +}; } namespace llvm { @@ -67,6 +95,17 @@ template<> struct PointerLikeTypeTraits { // to include Type.h. static constexpr int NumLowBitsAvailable = 3; }; + +template<> struct PointerLikeTypeTraits { + static void *getAsVoidPointer(clang::DynamicAllocLValue V) { + return V.getOpaqueValue(); + } + static clang::DynamicAllocLValue getFromVoidPointer(void *P) { + return clang::DynamicAllocLValue::getFromOpaqueValue(P); + } + static constexpr int NumLowBitsAvailable = + clang::DynamicAllocLValue::NumLowBitsAvailable; +}; } namespace clang { @@ -97,13 +136,15 @@ public: }; class LValueBase { - typedef llvm::PointerUnion + typedef llvm::PointerUnion PtrTy; public: LValueBase() : Local{} {} LValueBase(const ValueDecl *P, unsigned I = 0, unsigned V = 0); LValueBase(const Expr *P, unsigned I = 0, unsigned V = 0); + static LValueBase getDynamicAlloc(DynamicAllocLValue LV, QualType Type); static LValueBase getTypeInfo(TypeInfoLValue LV, QualType TypeInfo); template @@ -124,6 +165,7 @@ public: unsigned getCallIndex() const; unsigned getVersion() const; QualType getTypeInfoType() const; + QualType getDynamicAllocType() const; friend bool operator==(const LValueBase &LHS, const LValueBase &RHS); friend bool operator!=(const LValueBase &LHS, const LValueBase &RHS) { @@ -140,6 +182,8 @@ public: LocalState Local; /// The type std::type_info, if this is a TypeInfoLValue. void *TypeInfoType; + /// The QualType, if this is a DynamicAllocLValue. + void *DynamicAllocType; }; }; diff --git a/include/clang/AST/ASTContext.h b/include/clang/AST/ASTContext.h index 1d1aaf4fb1157..5e2f4031d96cc 100644 --- a/include/clang/AST/ASTContext.h +++ b/include/clang/AST/ASTContext.h @@ -139,6 +139,12 @@ class FullComment; } // namespace comments +namespace interp { + +class Context; + +} // namespace interp + struct TypeInfo { uint64_t Width = 0; unsigned Align = 0; @@ -179,7 +185,8 @@ private: mutable llvm::FoldingSet LValueReferenceTypes; mutable llvm::FoldingSet RValueReferenceTypes; mutable llvm::FoldingSet MemberPointerTypes; - mutable llvm::FoldingSet ConstantArrayTypes; + mutable llvm::ContextualFoldingSet + ConstantArrayTypes; mutable llvm::FoldingSet IncompleteArrayTypes; mutable std::vector VariableArrayTypes; mutable llvm::FoldingSet DependentSizedArrayTypes; @@ -507,6 +514,8 @@ private: /// need to be consistently numbered for the mangler). llvm::DenseMap> MangleNumberingContexts; + llvm::DenseMap> + ExtraMangleNumberingContexts; /// Side-table of mangling numbers for declarations which rarely /// need them (like static local vars). @@ -564,6 +573,7 @@ private: const TargetInfo *Target = nullptr; const TargetInfo *AuxTarget = nullptr; clang::PrintingPolicy PrintingPolicy; + std::unique_ptr InterpContext; public: IdentifierTable &Idents; @@ -573,6 +583,9 @@ public: IntrusiveRefCntPtr ExternalSource; ASTMutationListener *Listener = nullptr; + /// Returns the clang bytecode interpreter context. + interp::Context &getInterpContext(); + /// Container for either a single DynTypedNode or for an ArrayRef to /// DynTypedNode. For use with ParentMap. class DynTypedNodeList { @@ -729,71 +742,49 @@ public: /// True if comments are already loaded from ExternalASTSource. mutable bool CommentsLoaded = false; - class RawCommentAndCacheFlags { - public: - enum Kind { - /// We searched for a comment attached to the particular declaration, but - /// didn't find any. - /// - /// getRaw() == 0. - NoCommentInDecl = 0, - - /// We have found a comment attached to this particular declaration. - /// - /// getRaw() != 0. - FromDecl, - - /// This declaration does not have an attached comment, and we have - /// searched the redeclaration chain. - /// - /// If getRaw() == 0, the whole redeclaration chain does not have any - /// comments. - /// - /// If getRaw() != 0, it is a comment propagated from other - /// redeclaration. - FromRedecl - }; - - Kind getKind() const LLVM_READONLY { - return Data.getInt(); - } - - void setKind(Kind K) { - Data.setInt(K); - } - - const RawComment *getRaw() const LLVM_READONLY { - return Data.getPointer(); - } - - void setRaw(const RawComment *RC) { - Data.setPointer(RC); - } - - const Decl *getOriginalDecl() const LLVM_READONLY { - return OriginalDecl; - } - - void setOriginalDecl(const Decl *Orig) { - OriginalDecl = Orig; - } - - private: - llvm::PointerIntPair Data; - const Decl *OriginalDecl; - }; + /// Mapping from declaration to directly attached comment. + /// + /// Raw comments are owned by Comments list. This mapping is populated + /// lazily. + mutable llvm::DenseMap DeclRawComments; - /// Mapping from declarations to comments attached to any - /// redeclaration. + /// Mapping from canonical declaration to the first redeclaration in chain + /// that has a comment attached. /// /// Raw comments are owned by Comments list. This mapping is populated /// lazily. - mutable llvm::DenseMap RedeclComments; + mutable llvm::DenseMap RedeclChainComments; + + /// Keeps track of redeclaration chains that don't have any comment attached. + /// Mapping from canonical declaration to redeclaration chain that has no + /// comments attached to any redeclaration. Specifically it's mapping to + /// the last redeclaration we've checked. + /// + /// Shall not contain declarations that have comments attached to any + /// redeclaration in their chain. + mutable llvm::DenseMap CommentlessRedeclChains; /// Mapping from declarations to parsed comments attached to any /// redeclaration. mutable llvm::DenseMap ParsedComments; + /// Attaches \p Comment to \p OriginalD and to its redeclaration chain + /// and removes the redeclaration chain from the set of commentless chains. + /// + /// Don't do anything if a comment has already been attached to \p OriginalD + /// or its redeclaration chain. + void cacheRawCommentForDecl(const Decl &OriginalD, + const RawComment &Comment) const; + + /// \returns searches \p CommentsInFile for doc comment for \p D. + /// + /// \p RepresentativeLocForDecl is used as a location for searching doc + /// comments. \p CommentsInFile is a mapping offset -> comment of files in the + /// same file where \p RepresentativeLocForDecl is. + RawComment *getRawCommentForDeclNoCacheImpl( + const Decl *D, const SourceLocation RepresentativeLocForDecl, + const std::map &CommentsInFile) const; + /// Return the documentation comment attached to a given declaration, /// without looking into cache. RawComment *getRawCommentForDeclNoCache(const Decl *D) const; @@ -818,6 +809,16 @@ public: getRawCommentForAnyRedecl(const Decl *D, const Decl **OriginalDecl = nullptr) const; + /// Searches existing comments for doc comments that should be attached to \p + /// Decls. If any doc comment is found, it is parsed. + /// + /// Requirement: All \p Decls are in the same file. + /// + /// If the last comment in the file is already attached we assume + /// there are not comments left to be attached to \p Decls. + void attachCommentsToJustParsedDecls(ArrayRef Decls, + const Preprocessor *PP); + /// Return parsed documentation comment attached to a given declaration. /// Returns nullptr if no comment is attached. /// @@ -1054,6 +1055,9 @@ public: #define EXT_OPAQUE_TYPE(ExtType, Id, Ext) \ CanQualType Id##Ty; #include "clang/Basic/OpenCLExtensionTypes.def" +#define SVE_TYPE(Name, Id, SingletonId) \ + CanQualType SingletonId; +#include "clang/Basic/AArch64SVEACLETypes.def" // Types for deductions in C++0x [stmt.ranged]'s desugaring. Built on demand. mutable QualType AutoDeductTy; // Deduction against 'auto'. @@ -1329,6 +1333,7 @@ public: /// Return the unique reference to the type for a constant array of /// the specified element type. QualType getConstantArrayType(QualType EltTy, const llvm::APInt &ArySize, + const Expr *SizeExpr, ArrayType::ArraySizeModifier ASM, unsigned IndexTypeQuals) const; @@ -1498,8 +1503,7 @@ public: bool isKindOf) const; QualType getObjCTypeParamType(const ObjCTypeParamDecl *Decl, - ArrayRef protocols, - QualType Canonical = QualType()) const; + ArrayRef protocols) const; bool ObjCObjectAdoptsQTypeProtocols(QualType QT, ObjCInterfaceDecl *Decl); @@ -2054,6 +2058,11 @@ public: /// types. bool areCompatibleVectorTypes(QualType FirstVec, QualType SecondVec); + /// Return true if the type has been explicitly qualified with ObjC ownership. + /// A type may be implicitly qualified with ownership under ObjC ARC, and in + /// some cases the compiler treats these differently. + bool hasDirectOwnershipQualifier(QualType Ty) const; + /// Return true if this is an \c NSObject object with its \c NSObject /// attribute set. static bool isObjCNSObjectType(QualType Ty) { @@ -2577,10 +2586,12 @@ public: return T == getObjCSelType(); } - bool ObjCQualifiedIdTypesAreCompatible(QualType LHS, QualType RHS, + bool ObjCQualifiedIdTypesAreCompatible(const ObjCObjectPointerType *LHS, + const ObjCObjectPointerType *RHS, bool ForCompare); - bool ObjCQualifiedClassTypesAreCompatible(QualType LHS, QualType RHS); + bool ObjCQualifiedClassTypesAreCompatible(const ObjCObjectPointerType *LHS, + const ObjCObjectPointerType *RHS); // Check the safety of assignment from LHS to RHS bool canAssignObjCInterfaces(const ObjCObjectPointerType *LHSOPT, @@ -2802,6 +2813,9 @@ public: /// Retrieve the context for computing mangling numbers in the given /// DeclContext. MangleNumberingContext &getManglingNumberContext(const DeclContext *DC); + enum NeedExtraManglingDecl_t { NeedExtraManglingDecl }; + MangleNumberingContext &getManglingNumberContext(NeedExtraManglingDecl_t, + const Decl *D); std::unique_ptr createMangleNumberingContext() const; diff --git a/include/clang/AST/ASTFwd.h b/include/clang/AST/ASTFwd.h index 93919bbdd52f5..25c3214854437 100644 --- a/include/clang/AST/ASTFwd.h +++ b/include/clang/AST/ASTFwd.h @@ -24,7 +24,7 @@ class Stmt; #include "clang/AST/StmtNodes.inc" class Type; #define TYPE(DERIVED, BASE) class DERIVED##Type; -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" class CXXCtorInitializer; } // end namespace clang diff --git a/include/clang/AST/ASTImporter.h b/include/clang/AST/ASTImporter.h index 4a55c120a4579..c82dcab35db57 100644 --- a/include/clang/AST/ASTImporter.h +++ b/include/clang/AST/ASTImporter.h @@ -87,6 +87,10 @@ class TypeSourceInfo; using NonEquivalentDeclSet = llvm::DenseSet>; using ImportedCXXBaseSpecifierMap = llvm::DenseMap; + using FileIDImportHandlerType = + std::function; + + enum class ODRHandlingType { Conservative, Liberal }; // An ImportPath is the list of the AST nodes which we visit during an // Import call. @@ -210,6 +214,8 @@ class TypeSourceInfo; }; private: + FileIDImportHandlerType FileIDImportHandler; + std::shared_ptr SharedState = nullptr; /// The path which we go through during the import of a given AST node. @@ -232,6 +238,8 @@ class TypeSourceInfo; /// Whether to perform a minimal import. bool Minimal; + ODRHandlingType ODRHandling; + /// Whether the last diagnostic came from the "from" context. bool LastDiagFromFrom = false; @@ -310,10 +318,20 @@ class TypeSourceInfo; virtual ~ASTImporter(); + /// Set a callback function for FileID import handling. + /// The function is invoked when a FileID is imported from the From context. + /// The imported FileID in the To context and the original FileID in the + /// From context is passed to it. + void setFileIDImportHandler(FileIDImportHandlerType H) { + FileIDImportHandler = H; + } + /// Whether the importer will perform a minimal import, creating /// to-be-completed forward declarations when possible. bool isMinimalImport() const { return Minimal; } + void setODRHandling(ODRHandlingType T) { ODRHandling = T; } + /// \brief Import the given object, returns the result. /// /// \param To Import the object into this variable. @@ -366,6 +384,20 @@ class TypeSourceInfo; /// imported. If it does not exist nullptr is returned. TranslationUnitDecl *GetFromTU(Decl *ToD); + /// Return the declaration in the "from" context from which the declaration + /// in the "to" context was imported. If it was not imported or of the wrong + /// type a null value is returned. + template + llvm::Optional getImportedFromDecl(const DeclT *ToD) const { + auto FromI = ImportedFromDecls.find(ToD); + if (FromI == ImportedFromDecls.end()) + return {}; + auto *FromD = dyn_cast(FromI->second); + if (!FromD) + return {}; + return FromD; + } + /// Import the given declaration context from the "from" /// AST context into the "to" AST context. /// @@ -491,12 +523,11 @@ class TypeSourceInfo; /// /// \param NumDecls the number of conflicting declarations in \p Decls. /// - /// \returns the name that the newly-imported declaration should have. - virtual DeclarationName HandleNameConflict(DeclarationName Name, - DeclContext *DC, - unsigned IDNS, - NamedDecl **Decls, - unsigned NumDecls); + /// \returns the name that the newly-imported declaration should have. Or + /// an error if we can't handle the name conflict. + virtual Expected + HandleNameConflict(DeclarationName Name, DeclContext *DC, unsigned IDNS, + NamedDecl **Decls, unsigned NumDecls); /// Retrieve the context that AST nodes are being imported into. ASTContext &getToContext() const { return ToContext; } diff --git a/include/clang/AST/ASTImporterSharedState.h b/include/clang/AST/ASTImporterSharedState.h index 3635a62deef00..829eb1c611c38 100644 --- a/include/clang/AST/ASTImporterSharedState.h +++ b/include/clang/AST/ASTImporterSharedState.h @@ -47,7 +47,7 @@ public: ASTImporterSharedState() = default; ASTImporterSharedState(TranslationUnitDecl &ToTU) { - LookupTable = llvm::make_unique(ToTU); + LookupTable = std::make_unique(ToTU); } ASTImporterLookupTable *getLookupTable() { return LookupTable.get(); } diff --git a/include/clang/AST/ASTNodeTraverser.h b/include/clang/AST/ASTNodeTraverser.h index e43eacef86c6a..0bb2aad553fbf 100644 --- a/include/clang/AST/ASTNodeTraverser.h +++ b/include/clang/AST/ASTNodeTraverser.h @@ -237,6 +237,9 @@ public: for (const auto &TP : *TPL) Visit(TP); + + if (const Expr *RC = TPL->getRequiresClause()) + Visit(RC); } void diff --git a/include/clang/AST/ASTStructuralEquivalence.h b/include/clang/AST/ASTStructuralEquivalence.h index 70e0daa08a9ae..36a42070fd281 100644 --- a/include/clang/AST/ASTStructuralEquivalence.h +++ b/include/clang/AST/ASTStructuralEquivalence.h @@ -18,7 +18,7 @@ #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/Optional.h" -#include +#include #include namespace clang { @@ -42,14 +42,13 @@ struct StructuralEquivalenceContext { /// AST contexts for which we are checking structural equivalence. ASTContext &FromCtx, &ToCtx; - /// The set of "tentative" equivalences between two canonical - /// declarations, mapping from a declaration in the first context to the - /// declaration in the second context that we believe to be equivalent. - llvm::DenseMap TentativeEquivalences; + // Queue of from-to Decl pairs that are to be checked to determine the final + // result of equivalence of a starting Decl pair. + std::queue> DeclsToCheck; - /// Queue of declarations in the first context whose equivalence - /// with a declaration in the second context still needs to be verified. - std::deque DeclsToCheck; + // Set of from-to Decl pairs that are already visited during the check + // (are in or were once in \c DeclsToCheck) of a starting Decl pair. + llvm::DenseSet> VisitedDecls; /// Declaration (from, to) pairs that are known not to be equivalent /// (which we have already complained about). @@ -88,14 +87,14 @@ struct StructuralEquivalenceContext { /// Implementation functions (all static functions in /// ASTStructuralEquivalence.cpp) must never call this function because that /// will wreak havoc the internal state (\c DeclsToCheck and - /// \c TentativeEquivalences members) and can cause faulty equivalent results. + /// \c VisitedDecls members) and can cause faulty equivalent results. bool IsEquivalent(Decl *D1, Decl *D2); /// Determine whether the two types are structurally equivalent. /// Implementation functions (all static functions in /// ASTStructuralEquivalence.cpp) must never call this function because that /// will wreak havoc the internal state (\c DeclsToCheck and - /// \c TentativeEquivalences members) and can cause faulty equivalent results. + /// \c VisitedDecls members) and can cause faulty equivalent results. bool IsEquivalent(QualType T1, QualType T2); /// Find the index of the given anonymous struct/union within its diff --git a/include/clang/AST/ASTTypeTraits.h b/include/clang/AST/ASTTypeTraits.h index a29a04e5d242d..dd4ead2f0c2bd 100644 --- a/include/clang/AST/ASTTypeTraits.h +++ b/include/clang/AST/ASTTypeTraits.h @@ -148,7 +148,7 @@ private: #include "clang/AST/StmtNodes.inc" NKI_Type, #define TYPE(DERIVED, BASE) NKI_##DERIVED##Type, -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" NKI_OMPClause, #define OPENMP_CLAUSE(TextualSpelling, Class) NKI_##Class, #include "clang/Basic/OpenMPKinds.def" @@ -205,7 +205,7 @@ KIND_TO_KIND_ID(OMPClause) #define STMT(DERIVED, BASE) KIND_TO_KIND_ID(DERIVED) #include "clang/AST/StmtNodes.inc" #define TYPE(DERIVED, BASE) KIND_TO_KIND_ID(DERIVED##Type) -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" #define OPENMP_CLAUSE(TextualSpelling, Class) KIND_TO_KIND_ID(Class) #include "clang/Basic/OpenMPKinds.def" #undef KIND_TO_KIND_ID diff --git a/include/clang/AST/Attr.h b/include/clang/AST/Attr.h index 1fbed7ceebfad..d315dde6ed454 100644 --- a/include/clang/AST/Attr.h +++ b/include/clang/AST/Attr.h @@ -19,6 +19,7 @@ #include "clang/AST/Expr.h" #include "clang/AST/Type.h" #include "clang/Basic/AttrKinds.h" +#include "clang/Basic/AttributeCommonInfo.h" #include "clang/Basic/LLVM.h" #include "clang/Basic/OpenMPKinds.h" #include "clang/Basic/Sanitizers.h" @@ -32,6 +33,7 @@ namespace clang { class ASTContext; + class AttributeCommonInfo; class IdentifierInfo; class ObjCInterfaceDecl; class Expr; @@ -40,84 +42,79 @@ namespace clang { class TypeSourceInfo; /// Attr - This represents one attribute. -class Attr { -private: - SourceRange Range; - unsigned AttrKind : 16; - -protected: - /// An index into the spelling list of an - /// attribute defined in Attr.td file. - unsigned SpellingListIndex : 4; - unsigned Inherited : 1; - unsigned IsPackExpansion : 1; - unsigned Implicit : 1; - // FIXME: These are properties of the attribute kind, not state for this - // instance of the attribute. - unsigned IsLateParsed : 1; - unsigned InheritEvenIfAlreadyPresent : 1; - - void *operator new(size_t bytes) noexcept { - llvm_unreachable("Attrs cannot be allocated with regular 'new'."); - } - void operator delete(void *data) noexcept { - llvm_unreachable("Attrs cannot be released with regular 'delete'."); - } - -public: - // Forward so that the regular new and delete do not hide global ones. - void *operator new(size_t Bytes, ASTContext &C, - size_t Alignment = 8) noexcept { - return ::operator new(Bytes, C, Alignment); - } - void operator delete(void *Ptr, ASTContext &C, size_t Alignment) noexcept { - return ::operator delete(Ptr, C, Alignment); - } + class Attr : public AttributeCommonInfo { + private: + unsigned AttrKind : 16; + + protected: + /// An index into the spelling list of an + /// attribute defined in Attr.td file. + unsigned Inherited : 1; + unsigned IsPackExpansion : 1; + unsigned Implicit : 1; + // FIXME: These are properties of the attribute kind, not state for this + // instance of the attribute. + unsigned IsLateParsed : 1; + unsigned InheritEvenIfAlreadyPresent : 1; + + void *operator new(size_t bytes) noexcept { + llvm_unreachable("Attrs cannot be allocated with regular 'new'."); + } + void operator delete(void *data) noexcept { + llvm_unreachable("Attrs cannot be released with regular 'delete'."); + } -protected: - Attr(attr::Kind AK, SourceRange R, unsigned SpellingListIndex, - bool IsLateParsed) - : Range(R), AttrKind(AK), SpellingListIndex(SpellingListIndex), - Inherited(false), IsPackExpansion(false), Implicit(false), - IsLateParsed(IsLateParsed), InheritEvenIfAlreadyPresent(false) {} + public: + // Forward so that the regular new and delete do not hide global ones. + void *operator new(size_t Bytes, ASTContext &C, + size_t Alignment = 8) noexcept { + return ::operator new(Bytes, C, Alignment); + } + void operator delete(void *Ptr, ASTContext &C, size_t Alignment) noexcept { + return ::operator delete(Ptr, C, Alignment); + } -public: + protected: + Attr(ASTContext &Context, const AttributeCommonInfo &CommonInfo, + attr::Kind AK, bool IsLateParsed) + : AttributeCommonInfo(CommonInfo), AttrKind(AK), Inherited(false), + IsPackExpansion(false), Implicit(false), IsLateParsed(IsLateParsed), + InheritEvenIfAlreadyPresent(false) {} - attr::Kind getKind() const { - return static_cast(AttrKind); - } + public: + attr::Kind getKind() const { return static_cast(AttrKind); } - unsigned getSpellingListIndex() const { return SpellingListIndex; } - const char *getSpelling() const; + unsigned getSpellingListIndex() const { + return getAttributeSpellingListIndex(); + } + const char *getSpelling() const; - SourceLocation getLocation() const { return Range.getBegin(); } - SourceRange getRange() const { return Range; } - void setRange(SourceRange R) { Range = R; } + SourceLocation getLocation() const { return getRange().getBegin(); } - bool isInherited() const { return Inherited; } + bool isInherited() const { return Inherited; } - /// Returns true if the attribute has been implicitly created instead - /// of explicitly written by the user. - bool isImplicit() const { return Implicit; } - void setImplicit(bool I) { Implicit = I; } + /// Returns true if the attribute has been implicitly created instead + /// of explicitly written by the user. + bool isImplicit() const { return Implicit; } + void setImplicit(bool I) { Implicit = I; } - void setPackExpansion(bool PE) { IsPackExpansion = PE; } - bool isPackExpansion() const { return IsPackExpansion; } + void setPackExpansion(bool PE) { IsPackExpansion = PE; } + bool isPackExpansion() const { return IsPackExpansion; } - // Clone this attribute. - Attr *clone(ASTContext &C) const; + // Clone this attribute. + Attr *clone(ASTContext &C) const; - bool isLateParsed() const { return IsLateParsed; } + bool isLateParsed() const { return IsLateParsed; } - // Pretty print this attribute. - void printPretty(raw_ostream &OS, const PrintingPolicy &Policy) const; -}; + // Pretty print this attribute. + void printPretty(raw_ostream &OS, const PrintingPolicy &Policy) const; + }; class TypeAttr : public Attr { protected: - TypeAttr(attr::Kind AK, SourceRange R, unsigned SpellingListIndex, - bool IsLateParsed) - : Attr(AK, R, SpellingListIndex, IsLateParsed) {} + TypeAttr(ASTContext &Context, const AttributeCommonInfo &CommonInfo, + attr::Kind AK, bool IsLateParsed) + : Attr(Context, CommonInfo, AK, IsLateParsed) {} public: static bool classof(const Attr *A) { @@ -128,9 +125,9 @@ public: class StmtAttr : public Attr { protected: - StmtAttr(attr::Kind AK, SourceRange R, unsigned SpellingListIndex, - bool IsLateParsed) - : Attr(AK, R, SpellingListIndex, IsLateParsed) {} + StmtAttr(ASTContext &Context, const AttributeCommonInfo &CommonInfo, + attr::Kind AK, bool IsLateParsed) + : Attr(Context, CommonInfo, AK, IsLateParsed) {} public: static bool classof(const Attr *A) { @@ -141,9 +138,10 @@ public: class InheritableAttr : public Attr { protected: - InheritableAttr(attr::Kind AK, SourceRange R, unsigned SpellingListIndex, - bool IsLateParsed, bool InheritEvenIfAlreadyPresent) - : Attr(AK, R, SpellingListIndex, IsLateParsed) { + InheritableAttr(ASTContext &Context, const AttributeCommonInfo &CommonInfo, + attr::Kind AK, bool IsLateParsed, + bool InheritEvenIfAlreadyPresent) + : Attr(Context, CommonInfo, AK, IsLateParsed) { this->InheritEvenIfAlreadyPresent = InheritEvenIfAlreadyPresent; } @@ -165,9 +163,10 @@ public: class InheritableParamAttr : public InheritableAttr { protected: - InheritableParamAttr(attr::Kind AK, SourceRange R, unsigned SpellingListIndex, + InheritableParamAttr(ASTContext &Context, + const AttributeCommonInfo &CommonInfo, attr::Kind AK, bool IsLateParsed, bool InheritEvenIfAlreadyPresent) - : InheritableAttr(AK, R, SpellingListIndex, IsLateParsed, + : InheritableAttr(Context, CommonInfo, AK, IsLateParsed, InheritEvenIfAlreadyPresent) {} public: @@ -182,11 +181,11 @@ public: /// for the parameter. class ParameterABIAttr : public InheritableParamAttr { protected: - ParameterABIAttr(attr::Kind AK, SourceRange R, - unsigned SpellingListIndex, bool IsLateParsed, + ParameterABIAttr(ASTContext &Context, const AttributeCommonInfo &CommonInfo, + attr::Kind AK, bool IsLateParsed, bool InheritEvenIfAlreadyPresent) - : InheritableParamAttr(AK, R, SpellingListIndex, IsLateParsed, - InheritEvenIfAlreadyPresent) {} + : InheritableParamAttr(Context, CommonInfo, AK, IsLateParsed, + InheritEvenIfAlreadyPresent) {} public: ParameterABI getABI() const { diff --git a/include/clang/AST/CXXRecordDeclDefinitionBits.def b/include/clang/AST/CXXRecordDeclDefinitionBits.def new file mode 100644 index 0000000000000..bd4d8247aeca5 --- /dev/null +++ b/include/clang/AST/CXXRecordDeclDefinitionBits.def @@ -0,0 +1,236 @@ +//===-- CXXRecordDeclDefinitionBits.def - Class definition bits -*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file enumerates the various bitfields that we want to store on C++ class +// definitions. +// +//===----------------------------------------------------------------------===// +// +/// @file CXXRecordDeclDefinitionBits.def +/// +/// In this file, each of the bitfields representing data about a C++ class +/// results in an expansion of the FIELD macro, which should be defined before +/// including this file. +/// +/// The macro have three operands: +/// +/// Name: The name of the field, as a member of CXXRecordDecl::DefinitionData. +/// +/// BitWidth: The width of the field in bits. +/// +/// MergePolicy: How to behave when the value of the field is different in +/// multiple translation units, one of: +/// NO_MERGE: It is an ODR violation if the fields do not match. +/// MERGE_OR: Merge the fields by ORing them together. + +#ifndef FIELD +#error define FIELD before including this file +#endif + +/// True if this class has any user-declared constructors. +FIELD(UserDeclaredConstructor, 1, NO_MERGE) + +/// The user-declared special members which this class has. +FIELD(UserDeclaredSpecialMembers, 6, NO_MERGE) + +/// True when this class is an aggregate. +FIELD(Aggregate, 1, NO_MERGE) + +/// True when this class is a POD-type. +FIELD(PlainOldData, 1, NO_MERGE) + +/// True when this class is empty for traits purposes, that is: +/// * has no data members other than 0-width bit-fields and empty fields +/// marked [[no_unique_address]] +/// * has no virtual function/base, and +/// * doesn't inherit from a non-empty class. +/// Doesn't take union-ness into account. +FIELD(Empty, 1, NO_MERGE) + +/// True when this class is polymorphic, i.e., has at +/// least one virtual member or derives from a polymorphic class. +FIELD(Polymorphic, 1, NO_MERGE) + +/// True when this class is abstract, i.e., has at least +/// one pure virtual function, (that can come from a base class). +FIELD(Abstract, 1, NO_MERGE) + +/// True when this class is standard-layout, per the applicable +/// language rules (including DRs). +FIELD(IsStandardLayout, 1, NO_MERGE) + +/// True when this class was standard-layout under the C++11 +/// definition. +/// +/// C++11 [class]p7. A standard-layout class is a class that: +/// * has no non-static data members of type non-standard-layout class (or +/// array of such types) or reference, +/// * has no virtual functions (10.3) and no virtual base classes (10.1), +/// * has the same access control (Clause 11) for all non-static data +/// members +/// * has no non-standard-layout base classes, +/// * either has no non-static data members in the most derived class and at +/// most one base class with non-static data members, or has no base +/// classes with non-static data members, and +/// * has no base classes of the same type as the first non-static data +/// member. +FIELD(IsCXX11StandardLayout, 1, NO_MERGE) + +/// True when any base class has any declared non-static data +/// members or bit-fields. +/// This is a helper bit of state used to implement IsStandardLayout more +/// efficiently. +FIELD(HasBasesWithFields, 1, NO_MERGE) + +/// True when any base class has any declared non-static data +/// members. +/// This is a helper bit of state used to implement IsCXX11StandardLayout +/// more efficiently. +FIELD(HasBasesWithNonStaticDataMembers, 1, NO_MERGE) + +/// True when there are private non-static data members. +FIELD(HasPrivateFields, 1, NO_MERGE) + +/// True when there are protected non-static data members. +FIELD(HasProtectedFields, 1, NO_MERGE) + +/// True when there are private non-static data members. +FIELD(HasPublicFields, 1, NO_MERGE) + +/// True if this class (or any subobject) has mutable fields. +FIELD(HasMutableFields, 1, NO_MERGE) + +/// True if this class (or any nested anonymous struct or union) +/// has variant members. +FIELD(HasVariantMembers, 1, NO_MERGE) + +/// True if there no non-field members declared by the user. +FIELD(HasOnlyCMembers, 1, NO_MERGE) + +/// True if any field has an in-class initializer, including those +/// within anonymous unions or structs. +FIELD(HasInClassInitializer, 1, NO_MERGE) + +/// True if any field is of reference type, and does not have an +/// in-class initializer. +/// +/// In this case, value-initialization of this class is illegal in C++98 +/// even if the class has a trivial default constructor. +FIELD(HasUninitializedReferenceMember, 1, NO_MERGE) + +/// True if any non-mutable field whose type doesn't have a user- +/// provided default ctor also doesn't have an in-class initializer. +FIELD(HasUninitializedFields, 1, NO_MERGE) + +/// True if there are any member using-declarations that inherit +/// constructors from a base class. +FIELD(HasInheritedConstructor, 1, NO_MERGE) + +/// True if there are any member using-declarations named +/// 'operator='. +FIELD(HasInheritedAssignment, 1, NO_MERGE) + +/// These flags are \c true if a defaulted corresponding special +/// member can't be fully analyzed without performing overload resolution. +/// @{ +FIELD(NeedOverloadResolutionForCopyConstructor, 1, NO_MERGE) +FIELD(NeedOverloadResolutionForMoveConstructor, 1, NO_MERGE) +FIELD(NeedOverloadResolutionForMoveAssignment, 1, NO_MERGE) +FIELD(NeedOverloadResolutionForDestructor, 1, NO_MERGE) +/// @} + +/// These flags are \c true if an implicit defaulted corresponding +/// special member would be defined as deleted. +/// @{ +FIELD(DefaultedCopyConstructorIsDeleted, 1, NO_MERGE) +FIELD(DefaultedMoveConstructorIsDeleted, 1, NO_MERGE) +FIELD(DefaultedMoveAssignmentIsDeleted, 1, NO_MERGE) +FIELD(DefaultedDestructorIsDeleted, 1, NO_MERGE) +/// @} + +/// The trivial special members which this class has, per +/// C++11 [class.ctor]p5, C++11 [class.copy]p12, C++11 [class.copy]p25, +/// C++11 [class.dtor]p5, or would have if the member were not suppressed. +/// +/// This excludes any user-declared but not user-provided special members +/// which have been declared but not yet defined. +FIELD(HasTrivialSpecialMembers, 6, MERGE_OR) + +/// These bits keep track of the triviality of special functions for the +/// purpose of calls. Only the bits corresponding to SMF_CopyConstructor, +/// SMF_MoveConstructor, and SMF_Destructor are meaningful here. +FIELD(HasTrivialSpecialMembersForCall, 6, MERGE_OR) + +/// The declared special members of this class which are known to be +/// non-trivial. +/// +/// This excludes any user-declared but not user-provided special members +/// which have been declared but not yet defined, and any implicit special +/// members which have not yet been declared. +FIELD(DeclaredNonTrivialSpecialMembers, 6, MERGE_OR) + +/// These bits keep track of the declared special members that are +/// non-trivial for the purpose of calls. +/// Only the bits corresponding to SMF_CopyConstructor, +/// SMF_MoveConstructor, and SMF_Destructor are meaningful here. +FIELD(DeclaredNonTrivialSpecialMembersForCall, 6, MERGE_OR) + +/// True when this class has a destructor with no semantic effect. +FIELD(HasIrrelevantDestructor, 1, NO_MERGE) + +/// True when this class has at least one user-declared constexpr +/// constructor which is neither the copy nor move constructor. +FIELD(HasConstexprNonCopyMoveConstructor, 1, MERGE_OR) + +/// True if this class has a (possibly implicit) defaulted default +/// constructor. +FIELD(HasDefaultedDefaultConstructor, 1, MERGE_OR) + +/// True if a defaulted default constructor for this class would +/// be constexpr. +FIELD(DefaultedDefaultConstructorIsConstexpr, 1, NO_MERGE) + +/// True if this class has a constexpr default constructor. +/// +/// This is true for either a user-declared constexpr default constructor +/// or an implicitly declared constexpr default constructor. +FIELD(HasConstexprDefaultConstructor, 1, MERGE_OR) + +/// True if a defaulted destructor for this class would be constexpr. +FIELD(DefaultedDestructorIsConstexpr, 1, NO_MERGE) + +/// True when this class contains at least one non-static data +/// member or base class of non-literal or volatile type. +FIELD(HasNonLiteralTypeFieldsOrBases, 1, NO_MERGE) + +/// Whether we have a C++11 user-provided default constructor (not +/// explicitly deleted or defaulted). +FIELD(UserProvidedDefaultConstructor, 1, NO_MERGE) + +/// The special members which have been declared for this class, +/// either by the user or implicitly. +FIELD(DeclaredSpecialMembers, 6, MERGE_OR) + +/// Whether an implicit copy constructor could have a const-qualified +/// parameter, for initializing virtual bases and for other subobjects. +FIELD(ImplicitCopyConstructorCanHaveConstParamForVBase, 1, NO_MERGE) +FIELD(ImplicitCopyConstructorCanHaveConstParamForNonVBase, 1, NO_MERGE) + +/// Whether an implicit copy assignment operator would have a +/// const-qualified parameter. +FIELD(ImplicitCopyAssignmentHasConstParam, 1, NO_MERGE) + +/// Whether any declared copy constructor has a const-qualified +/// parameter. +FIELD(HasDeclaredCopyConstructorWithConstParam, 1, MERGE_OR) + +/// Whether any declared copy assignment operator has either a +/// const-qualified reference parameter or a non-reference parameter. +FIELD(HasDeclaredCopyAssignmentWithConstParam, 1, MERGE_OR) + +#undef FIELD diff --git a/include/clang/AST/CharUnits.h b/include/clang/AST/CharUnits.h index 37f489c7708ac..f14d3abf71e5e 100644 --- a/include/clang/AST/CharUnits.h +++ b/include/clang/AST/CharUnits.h @@ -14,6 +14,7 @@ #define LLVM_CLANG_AST_CHARUNITS_H #include "llvm/ADT/DenseMapInfo.h" +#include "llvm/Support/Alignment.h" #include "llvm/Support/DataTypes.h" #include "llvm/Support/MathExtras.h" @@ -177,6 +178,10 @@ namespace clang { /// getQuantity - Get the raw integer representation of this quantity. QuantityType getQuantity() const { return Quantity; } + /// getAsAlign - Returns Quantity as a valid llvm::Align, + /// Beware llvm::Align assumes power of two 8-bit bytes. + llvm::Align getAsAlign() const { return llvm::Align(Quantity); } + /// alignTo - Returns the next integer (mod 2**64) that is /// greater than or equal to this quantity and is a multiple of \p Align. /// Align must be non-zero. diff --git a/include/clang/AST/CommentCommands.td b/include/clang/AST/CommentCommands.td index 958ee032e71fd..3b0d1603d407c 100644 --- a/include/clang/AST/CommentCommands.td +++ b/include/clang/AST/CommentCommands.td @@ -139,6 +139,7 @@ def Post : BlockCommand<"post">; def Pre : BlockCommand<"pre">; def Remark : BlockCommand<"remark">; def Remarks : BlockCommand<"remarks">; +def Retval : BlockCommand<"retval">; def Sa : BlockCommand<"sa">; def See : BlockCommand<"see">; def Since : BlockCommand<"since">; diff --git a/include/clang/AST/CommentLexer.h b/include/clang/AST/CommentLexer.h index 9ddbb7d31d99e..138fdaca0ff61 100644 --- a/include/clang/AST/CommentLexer.h +++ b/include/clang/AST/CommentLexer.h @@ -352,8 +352,7 @@ public: void lex(Token &T); - StringRef getSpelling(const Token &Tok, const SourceManager &SourceMgr, - bool *Invalid = nullptr) const; + StringRef getSpelling(const Token &Tok, const SourceManager &SourceMgr) const; }; } // end namespace comments diff --git a/include/clang/AST/Decl.h b/include/clang/AST/Decl.h index 02742801f37c2..ce674e09c44d4 100644 --- a/include/clang/AST/Decl.h +++ b/include/clang/AST/Decl.h @@ -310,6 +310,14 @@ public: void printQualifiedName(raw_ostream &OS) const; void printQualifiedName(raw_ostream &OS, const PrintingPolicy &Policy) const; + /// Print only the nested name specifier part of a fully-qualified name, + /// including the '::' at the end. E.g. + /// when `printQualifiedName(D)` prints "A::B::i", + /// this function prints "A::B::". + void printNestedNameSpecifier(raw_ostream &OS) const; + void printNestedNameSpecifier(raw_ostream &OS, + const PrintingPolicy &Policy) const; + // FIXME: Remove string version. std::string getQualifiedNameAsString() const; @@ -800,12 +808,19 @@ struct EvaluatedStmt { /// valid if CheckedICE is true. bool IsICE : 1; + /// Whether this variable is known to have constant destruction. That is, + /// whether running the destructor on the initial value is a side-effect + /// (and doesn't inspect any state that might have changed during program + /// execution). This is currently only computed if the destructor is + /// non-trivial. + bool HasConstantDestruction : 1; + Stmt *Value; APValue Evaluated; - EvaluatedStmt() : WasEvaluated(false), IsEvaluating(false), CheckedICE(false), - CheckingICE(false), IsICE(false) {} - + EvaluatedStmt() + : WasEvaluated(false), IsEvaluating(false), CheckedICE(false), + CheckingICE(false), IsICE(false), HasConstantDestruction(false) {} }; /// Represents a variable declaration or definition. @@ -1226,6 +1241,14 @@ public: void setInit(Expr *I); + /// Get the initializing declaration of this variable, if any. This is + /// usually the definition, except that for a static data member it can be + /// the in-class declaration. + VarDecl *getInitializingDeclaration(); + const VarDecl *getInitializingDeclaration() const { + return const_cast(this)->getInitializingDeclaration(); + } + /// Determine whether this variable's value might be usable in a /// constant expression, according to the relevant language standard. /// This only checks properties of the declaration, and does not check @@ -1251,6 +1274,14 @@ public: /// to untyped APValue if the value could not be evaluated. APValue *getEvaluatedValue() const; + /// Evaluate the destruction of this variable to determine if it constitutes + /// constant destruction. + /// + /// \pre isInitICE() + /// \return \c true if this variable has constant destruction, \c false if + /// not. + bool evaluateDestruction(SmallVectorImpl &Notes) const; + /// Determines whether it is already known whether the /// initializer is an integral constant expression or not. bool isInitKnownICE() const; @@ -1489,9 +1520,14 @@ public: // has no definition within this source file. bool isKnownToBeDefined() const; - /// Do we need to emit an exit-time destructor for this variable? + /// Is destruction of this variable entirely suppressed? If so, the variable + /// need not have a usable destructor at all. bool isNoDestroy(const ASTContext &) const; + /// Do we need to emit an exit-time destructor for this variable, and if so, + /// what kind? + QualType::DestructionKind needsDestruction(const ASTContext &Ctx) const; + // Implement isa/cast/dyncast/etc. static bool classof(const Decl *D) { return classofKind(D->getKind()); } static bool classofKind(Kind K) { return K >= firstVar && K <= lastVar; } @@ -4078,13 +4114,9 @@ public: void setCaptures(ASTContext &Context, ArrayRef Captures, bool CapturesCXXThis); - unsigned getBlockManglingNumber() const { - return ManglingNumber; - } + unsigned getBlockManglingNumber() const { return ManglingNumber; } - Decl *getBlockManglingContextDecl() const { - return ManglingContextDecl; - } + Decl *getBlockManglingContextDecl() const { return ManglingContextDecl; } void setBlockMangling(unsigned Number, Decl *Ctx) { ManglingNumber = Number; diff --git a/include/clang/AST/DeclBase.h b/include/clang/AST/DeclBase.h index d64d0cb425db0..01c2f1809771b 100644 --- a/include/clang/AST/DeclBase.h +++ b/include/clang/AST/DeclBase.h @@ -959,7 +959,7 @@ public: /// as this declaration, or NULL if there is no previous declaration. Decl *getPreviousDecl() { return getPreviousDeclImpl(); } - /// Retrieve the most recent declaration that declares the same entity + /// Retrieve the previous declaration that declares the same entity /// as this declaration, or NULL if there is no previous declaration. const Decl *getPreviousDecl() const { return const_cast(this)->getPreviousDeclImpl(); diff --git a/include/clang/AST/DeclCXX.h b/include/clang/AST/DeclCXX.h index 7add83f896244..66212f72b7875 100644 --- a/include/clang/AST/DeclCXX.h +++ b/include/clang/AST/DeclCXX.h @@ -42,6 +42,7 @@ #include "llvm/ADT/PointerUnion.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/iterator_range.h" +#include "llvm/BinaryFormat/Dwarf.h" #include "llvm/Support/Casting.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/PointerLikeTypeTraits.h" @@ -73,52 +74,6 @@ class TemplateDecl; class TemplateParameterList; class UsingDecl; -/// Represents any kind of function declaration, whether it is a -/// concrete function or a function template. -class AnyFunctionDecl { - NamedDecl *Function; - - AnyFunctionDecl(NamedDecl *ND) : Function(ND) {} - -public: - AnyFunctionDecl(FunctionDecl *FD) : Function(FD) {} - AnyFunctionDecl(FunctionTemplateDecl *FTD); - - /// Implicily converts any function or function template into a - /// named declaration. - operator NamedDecl *() const { return Function; } - - /// Retrieve the underlying function or function template. - NamedDecl *get() const { return Function; } - - static AnyFunctionDecl getFromNamedDecl(NamedDecl *ND) { - return AnyFunctionDecl(ND); - } -}; - -} // namespace clang - -namespace llvm { - - // Provide PointerLikeTypeTraits for non-cvr pointers. - template<> - struct PointerLikeTypeTraits< ::clang::AnyFunctionDecl> { - static void *getAsVoidPointer(::clang::AnyFunctionDecl F) { - return F.get(); - } - - static ::clang::AnyFunctionDecl getFromVoidPointer(void *P) { - return ::clang::AnyFunctionDecl::getFromNamedDecl( - static_cast< ::clang::NamedDecl*>(P)); - } - - enum { NumLowBitsAvailable = 2 }; - }; - -} // namespace llvm - -namespace clang { - /// Represents an access specifier followed by colon ':'. /// /// An objects of this class represents sugar for the syntactic occurrence @@ -322,207 +277,9 @@ class CXXRecordDecl : public RecordDecl { }; struct DefinitionData { - /// True if this class has any user-declared constructors. - unsigned UserDeclaredConstructor : 1; - - /// The user-declared special members which this class has. - unsigned UserDeclaredSpecialMembers : 6; - - /// True when this class is an aggregate. - unsigned Aggregate : 1; - - /// True when this class is a POD-type. - unsigned PlainOldData : 1; - - /// True when this class is empty for traits purposes, that is: - /// * has no data members other than 0-width bit-fields and empty fields - /// marked [[no_unique_address]] - /// * has no virtual function/base, and - /// * doesn't inherit from a non-empty class. - /// Doesn't take union-ness into account. - unsigned Empty : 1; - - /// True when this class is polymorphic, i.e., has at - /// least one virtual member or derives from a polymorphic class. - unsigned Polymorphic : 1; - - /// True when this class is abstract, i.e., has at least - /// one pure virtual function, (that can come from a base class). - unsigned Abstract : 1; - - /// True when this class is standard-layout, per the applicable - /// language rules (including DRs). - unsigned IsStandardLayout : 1; - - /// True when this class was standard-layout under the C++11 - /// definition. - /// - /// C++11 [class]p7. A standard-layout class is a class that: - /// * has no non-static data members of type non-standard-layout class (or - /// array of such types) or reference, - /// * has no virtual functions (10.3) and no virtual base classes (10.1), - /// * has the same access control (Clause 11) for all non-static data - /// members - /// * has no non-standard-layout base classes, - /// * either has no non-static data members in the most derived class and at - /// most one base class with non-static data members, or has no base - /// classes with non-static data members, and - /// * has no base classes of the same type as the first non-static data - /// member. - unsigned IsCXX11StandardLayout : 1; - - /// True when any base class has any declared non-static data - /// members or bit-fields. - /// This is a helper bit of state used to implement IsStandardLayout more - /// efficiently. - unsigned HasBasesWithFields : 1; - - /// True when any base class has any declared non-static data - /// members. - /// This is a helper bit of state used to implement IsCXX11StandardLayout - /// more efficiently. - unsigned HasBasesWithNonStaticDataMembers : 1; - - /// True when there are private non-static data members. - unsigned HasPrivateFields : 1; - - /// True when there are protected non-static data members. - unsigned HasProtectedFields : 1; - - /// True when there are private non-static data members. - unsigned HasPublicFields : 1; - - /// True if this class (or any subobject) has mutable fields. - unsigned HasMutableFields : 1; - - /// True if this class (or any nested anonymous struct or union) - /// has variant members. - unsigned HasVariantMembers : 1; - - /// True if there no non-field members declared by the user. - unsigned HasOnlyCMembers : 1; - - /// True if any field has an in-class initializer, including those - /// within anonymous unions or structs. - unsigned HasInClassInitializer : 1; - - /// True if any field is of reference type, and does not have an - /// in-class initializer. - /// - /// In this case, value-initialization of this class is illegal in C++98 - /// even if the class has a trivial default constructor. - unsigned HasUninitializedReferenceMember : 1; - - /// True if any non-mutable field whose type doesn't have a user- - /// provided default ctor also doesn't have an in-class initializer. - unsigned HasUninitializedFields : 1; - - /// True if there are any member using-declarations that inherit - /// constructors from a base class. - unsigned HasInheritedConstructor : 1; - - /// True if there are any member using-declarations named - /// 'operator='. - unsigned HasInheritedAssignment : 1; - - /// These flags are \c true if a defaulted corresponding special - /// member can't be fully analyzed without performing overload resolution. - /// @{ - unsigned NeedOverloadResolutionForCopyConstructor : 1; - unsigned NeedOverloadResolutionForMoveConstructor : 1; - unsigned NeedOverloadResolutionForMoveAssignment : 1; - unsigned NeedOverloadResolutionForDestructor : 1; - /// @} - - /// These flags are \c true if an implicit defaulted corresponding - /// special member would be defined as deleted. - /// @{ - unsigned DefaultedCopyConstructorIsDeleted : 1; - unsigned DefaultedMoveConstructorIsDeleted : 1; - unsigned DefaultedMoveAssignmentIsDeleted : 1; - unsigned DefaultedDestructorIsDeleted : 1; - /// @} - - /// The trivial special members which this class has, per - /// C++11 [class.ctor]p5, C++11 [class.copy]p12, C++11 [class.copy]p25, - /// C++11 [class.dtor]p5, or would have if the member were not suppressed. - /// - /// This excludes any user-declared but not user-provided special members - /// which have been declared but not yet defined. - unsigned HasTrivialSpecialMembers : 6; - - /// These bits keep track of the triviality of special functions for the - /// purpose of calls. Only the bits corresponding to SMF_CopyConstructor, - /// SMF_MoveConstructor, and SMF_Destructor are meaningful here. - unsigned HasTrivialSpecialMembersForCall : 6; - - /// The declared special members of this class which are known to be - /// non-trivial. - /// - /// This excludes any user-declared but not user-provided special members - /// which have been declared but not yet defined, and any implicit special - /// members which have not yet been declared. - unsigned DeclaredNonTrivialSpecialMembers : 6; - - /// These bits keep track of the declared special members that are - /// non-trivial for the purpose of calls. - /// Only the bits corresponding to SMF_CopyConstructor, - /// SMF_MoveConstructor, and SMF_Destructor are meaningful here. - unsigned DeclaredNonTrivialSpecialMembersForCall : 6; - - /// True when this class has a destructor with no semantic effect. - unsigned HasIrrelevantDestructor : 1; - - /// True when this class has at least one user-declared constexpr - /// constructor which is neither the copy nor move constructor. - unsigned HasConstexprNonCopyMoveConstructor : 1; - - /// True if this class has a (possibly implicit) defaulted default - /// constructor. - unsigned HasDefaultedDefaultConstructor : 1; - - /// True if a defaulted default constructor for this class would - /// be constexpr. - unsigned DefaultedDefaultConstructorIsConstexpr : 1; - - /// True if this class has a constexpr default constructor. - /// - /// This is true for either a user-declared constexpr default constructor - /// or an implicitly declared constexpr default constructor. - unsigned HasConstexprDefaultConstructor : 1; - - /// True when this class contains at least one non-static data - /// member or base class of non-literal or volatile type. - unsigned HasNonLiteralTypeFieldsOrBases : 1; - - /// True when visible conversion functions are already computed - /// and are available. - unsigned ComputedVisibleConversions : 1; - - /// Whether we have a C++11 user-provided default constructor (not - /// explicitly deleted or defaulted). - unsigned UserProvidedDefaultConstructor : 1; - - /// The special members which have been declared for this class, - /// either by the user or implicitly. - unsigned DeclaredSpecialMembers : 6; - - /// Whether an implicit copy constructor could have a const-qualified - /// parameter, for initializing virtual bases and for other subobjects. - unsigned ImplicitCopyConstructorCanHaveConstParamForVBase : 1; - unsigned ImplicitCopyConstructorCanHaveConstParamForNonVBase : 1; - - /// Whether an implicit copy assignment operator would have a - /// const-qualified parameter. - unsigned ImplicitCopyAssignmentHasConstParam : 1; - - /// Whether any declared copy constructor has a const-qualified - /// parameter. - unsigned HasDeclaredCopyConstructorWithConstParam : 1; - - /// Whether any declared copy assignment operator has either a - /// const-qualified reference parameter or a non-reference parameter. - unsigned HasDeclaredCopyAssignmentWithConstParam : 1; + #define FIELD(Name, Width, Merge) \ + unsigned Name : Width; + #include "CXXRecordDeclDefinitionBits.def" /// Whether this class describes a C++ lambda. unsigned IsLambda : 1; @@ -530,6 +287,10 @@ class CXXRecordDecl : public RecordDecl { /// Whether we are currently parsing base specifiers. unsigned IsParsingBaseSpecifiers : 1; + /// True when visible conversion functions are already computed + /// and are available. + unsigned ComputedVisibleConversions : 1; + unsigned HasODRHash : 1; /// A hash of parts of the class to help in ODR checking. @@ -628,9 +389,12 @@ class CXXRecordDecl : public RecordDecl { /// The number of explicit captures in this lambda. unsigned NumExplicitCaptures : 13; + /// Has known `internal` linkage. + unsigned HasKnownInternalLinkage : 1; + /// The number used to indicate this lambda expression for name /// mangling in the Itanium C++ ABI. - unsigned ManglingNumber = 0; + unsigned ManglingNumber : 31; /// The declaration that provides context for this lambda, if the /// actual DeclContext does not suffice. This is used for lambdas that @@ -645,12 +409,12 @@ class CXXRecordDecl : public RecordDecl { /// The type of the call method. TypeSourceInfo *MethodTyInfo; - LambdaDefinitionData(CXXRecordDecl *D, TypeSourceInfo *Info, - bool Dependent, bool IsGeneric, - LambdaCaptureDefault CaptureDefault) - : DefinitionData(D), Dependent(Dependent), IsGenericLambda(IsGeneric), - CaptureDefault(CaptureDefault), NumCaptures(0), NumExplicitCaptures(0), - MethodTyInfo(Info) { + LambdaDefinitionData(CXXRecordDecl *D, TypeSourceInfo *Info, bool Dependent, + bool IsGeneric, LambdaCaptureDefault CaptureDefault) + : DefinitionData(D), Dependent(Dependent), IsGenericLambda(IsGeneric), + CaptureDefault(CaptureDefault), NumCaptures(0), + NumExplicitCaptures(0), HasKnownInternalLinkage(0), ManglingNumber(0), + MethodTyInfo(Info) { IsLambda = true; // C++1z [expr.prim.lambda]p4: @@ -1214,6 +978,10 @@ public: /// if this is a closure type. CXXMethodDecl *getLambdaCallOperator() const; + /// Retrieve the dependent lambda call operator of the closure type + /// if this is a templated closure type. + FunctionTemplateDecl *getDependentLambdaCallOperator() const; + /// Retrieve the lambda static invoker, the address of which /// is returned by the conversion operator, and the body of which /// is forwarded to the lambda call operator. @@ -1398,7 +1166,8 @@ public: /// would be constexpr. bool defaultedDefaultConstructorIsConstexpr() const { return data().DefaultedDefaultConstructorIsConstexpr && - (!isUnion() || hasInClassInitializer() || !hasVariantMembers()); + (!isUnion() || hasInClassInitializer() || !hasVariantMembers() || + getASTContext().getLangOpts().CPlusPlus2a); } /// Determine whether this class has a constexpr default constructor. @@ -1486,6 +1255,16 @@ public: !(data().HasTrivialSpecialMembers & SMF_MoveAssignment)); } + /// Determine whether a defaulted default constructor for this class + /// would be constexpr. + bool defaultedDestructorIsConstexpr() const { + return data().DefaultedDestructorIsConstexpr && + getASTContext().getLangOpts().CPlusPlus2a; + } + + /// Determine whether this class has a constexpr destructor. + bool hasConstexprDestructor() const; + /// Determine whether this class has a trivial destructor /// (C++ [class.dtor]p3) bool hasTrivialDestructor() const { @@ -1577,8 +1356,10 @@ public: /// /// Only in C++17 and beyond, are lambdas literal types. bool isLiteral() const { - return hasTrivialDestructor() && - (!isLambda() || getASTContext().getLangOpts().CPlusPlus17) && + ASTContext &Ctx = getASTContext(); + return (Ctx.getLangOpts().CPlusPlus2a ? hasConstexprDestructor() + : hasTrivialDestructor()) && + (!isLambda() || Ctx.getLangOpts().CPlusPlus17) && !hasNonLiteralTypeFieldsOrBases() && (isAggregate() || isLambda() || hasConstexprNonCopyMoveConstructor() || @@ -1927,6 +1708,13 @@ public: return getLambdaData().ManglingNumber; } + /// The lambda is known to has internal linkage no matter whether it has name + /// mangling number. + bool hasKnownLambdaInternalLinkage() const { + assert(isLambda() && "Not a lambda closure type!"); + return getLambdaData().HasKnownInternalLinkage; + } + /// Retrieve the declaration that provides additional context for a /// lambda, when the normal declaration context is not specific enough. /// @@ -1940,9 +1728,12 @@ public: /// Set the mangling number and context declaration for a lambda /// class. - void setLambdaMangling(unsigned ManglingNumber, Decl *ContextDecl) { + void setLambdaMangling(unsigned ManglingNumber, Decl *ContextDecl, + bool HasKnownInternalLinkage = false) { + assert(isLambda() && "Not a lambda closure type!"); getLambdaData().ManglingNumber = ManglingNumber; getLambdaData().ContextDecl = ContextDecl; + getLambdaData().HasKnownInternalLinkage = HasKnownInternalLinkage; } /// Returns the inheritance model used for this record. @@ -2265,7 +2056,7 @@ public: const CXXRecordDecl *Decl); Qualifiers getMethodQualifiers() const { - return getType()->getAs()->getMethodQuals(); + return getType()->castAs()->getMethodQuals(); } /// Retrieve the ref-qualifier associated with this method. @@ -2280,7 +2071,7 @@ public: /// }; /// @endcode RefQualifierKind getRefQualifier() const { - return getType()->getAs()->getRefQualifier(); + return getType()->castAs()->getRefQualifier(); } bool hasInlineBody() const; @@ -2600,9 +2391,9 @@ class CXXConstructorDecl final ExplicitSpecifier getExplicitSpecifierInternal() const { if (CXXConstructorDeclBits.HasTrailingExplicitSpecifier) - return *getCanonicalDecl()->getTrailingObjects(); + return *getTrailingObjects(); return ExplicitSpecifier( - nullptr, getCanonicalDecl()->CXXConstructorDeclBits.IsSimpleExplicit + nullptr, CXXConstructorDeclBits.IsSimpleExplicit ? ExplicitSpecKind::ResolvedTrue : ExplicitSpecKind::ResolvedFalse); } @@ -2643,10 +2434,10 @@ public: InheritedConstructor Inherited = InheritedConstructor()); ExplicitSpecifier getExplicitSpecifier() { - return getExplicitSpecifierInternal(); + return getCanonicalDecl()->getExplicitSpecifierInternal(); } const ExplicitSpecifier getExplicitSpecifier() const { - return getExplicitSpecifierInternal(); + return getCanonicalDecl()->getExplicitSpecifierInternal(); } /// Return true if the declartion is already resolved to be explicit. @@ -2847,9 +2638,9 @@ class CXXDestructorDecl : public CXXMethodDecl { CXXDestructorDecl(ASTContext &C, CXXRecordDecl *RD, SourceLocation StartLoc, const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo, bool isInline, - bool isImplicitlyDeclared) + bool isImplicitlyDeclared, ConstexprSpecKind ConstexprKind) : CXXMethodDecl(CXXDestructor, C, RD, StartLoc, NameInfo, T, TInfo, - SC_None, isInline, CSK_unspecified, SourceLocation()) { + SC_None, isInline, ConstexprKind, SourceLocation()) { setImplicit(isImplicitlyDeclared); } @@ -2859,9 +2650,9 @@ public: static CXXDestructorDecl *Create(ASTContext &C, CXXRecordDecl *RD, SourceLocation StartLoc, const DeclarationNameInfo &NameInfo, - QualType T, TypeSourceInfo* TInfo, - bool isInline, - bool isImplicitlyDeclared); + QualType T, TypeSourceInfo *TInfo, + bool isInline, bool isImplicitlyDeclared, + ConstexprSpecKind ConstexprKind); static CXXDestructorDecl *CreateDeserialized(ASTContext & C, unsigned ID); void setOperatorDelete(FunctionDecl *OD, Expr *ThisArg); @@ -2934,7 +2725,7 @@ public: /// Returns the type that this conversion function is converting to. QualType getConversionType() const { - return getType()->getAs()->getReturnType(); + return getType()->castAs()->getReturnType(); } /// Determine whether this conversion function is a conversion from @@ -2971,8 +2762,10 @@ public: /// ensure a stable ABI for this, we choose the DW_LANG_ encodings /// from the dwarf standard. enum LanguageIDs { - lang_c = /* DW_LANG_C */ 0x0002, - lang_cxx = /* DW_LANG_C_plus_plus */ 0x0004 + lang_c = llvm::dwarf::DW_LANG_C, + lang_cxx = llvm::dwarf::DW_LANG_C_plus_plus, + lang_cxx_11 = llvm::dwarf::DW_LANG_C_plus_plus_11, + lang_cxx_14 = llvm::dwarf::DW_LANG_C_plus_plus_14 }; private: @@ -3469,12 +3262,6 @@ public: return IsVirtual; } - /// Get the constructor or constructor template in the derived class - /// correspnding to this using shadow declaration, if it has been implicitly - /// declared already. - CXXConstructorDecl *getConstructor() const; - void setConstructor(NamedDecl *Ctor); - static bool classof(const Decl *D) { return classofKind(D->getKind()); } static bool classofKind(Kind K) { return K == ConstructorUsingShadow; } }; diff --git a/include/clang/AST/DeclTemplate.h b/include/clang/AST/DeclTemplate.h index 235b31c1c3120..ec14adc7de972 100644 --- a/include/clang/AST/DeclTemplate.h +++ b/include/clang/AST/DeclTemplate.h @@ -168,6 +168,16 @@ public: return HasRequiresClause ? *getTrailingObjects() : nullptr; } + /// \brief All associated constraints derived from this template parameter + /// list, including the requires clause and any constraints derived from + /// constrained-parameters. + /// + /// The constraints in the resulting list are to be treated as if in a + /// conjunction ("and"). + void getAssociatedConstraints(llvm::SmallVectorImpl &AC) const; + + bool hasAssociatedConstraints() const; + SourceLocation getTemplateLoc() const { return TemplateLoc; } SourceLocation getLAngleLoc() const { return LAngleLoc; } SourceLocation getRAngleLoc() const { return RAngleLoc; } @@ -369,33 +379,7 @@ public: // Kinds of Templates //===----------------------------------------------------------------------===// -/// Stores the template parameter list and associated constraints for -/// \c TemplateDecl objects that track associated constraints. -class ConstrainedTemplateDeclInfo { - friend TemplateDecl; - -public: - ConstrainedTemplateDeclInfo() = default; - - TemplateParameterList *getTemplateParameters() const { - return TemplateParams; - } - - Expr *getAssociatedConstraints() const { return AssociatedConstraints; } - -protected: - void setTemplateParameters(TemplateParameterList *TParams) { - TemplateParams = TParams; - } - - void setAssociatedConstraints(Expr *AC) { AssociatedConstraints = AC; } - - TemplateParameterList *TemplateParams = nullptr; - Expr *AssociatedConstraints = nullptr; -}; - - -/// The base class of all kinds of template declarations (e.g., +/// \brief The base class of all kinds of template declarations (e.g., /// class, function, etc.). /// /// The TemplateDecl class stores the list of template parameters and a @@ -404,54 +388,32 @@ class TemplateDecl : public NamedDecl { void anchor() override; protected: + // Construct a template decl with name, parameters, and templated element. + TemplateDecl(Kind DK, DeclContext *DC, SourceLocation L, DeclarationName Name, + TemplateParameterList *Params, NamedDecl *Decl); + // Construct a template decl with the given name and parameters. // Used when there is no templated element (e.g., for tt-params). - TemplateDecl(ConstrainedTemplateDeclInfo *CTDI, Kind DK, DeclContext *DC, - SourceLocation L, DeclarationName Name, - TemplateParameterList *Params) - : NamedDecl(DK, DC, L, Name), TemplatedDecl(nullptr), - TemplateParams(CTDI) { - this->setTemplateParameters(Params); - } - TemplateDecl(Kind DK, DeclContext *DC, SourceLocation L, DeclarationName Name, TemplateParameterList *Params) - : TemplateDecl(nullptr, DK, DC, L, Name, Params) {} - - // Construct a template decl with name, parameters, and templated element. - TemplateDecl(ConstrainedTemplateDeclInfo *CTDI, Kind DK, DeclContext *DC, - SourceLocation L, DeclarationName Name, - TemplateParameterList *Params, NamedDecl *Decl) - : NamedDecl(DK, DC, L, Name), TemplatedDecl(Decl), - TemplateParams(CTDI) { - this->setTemplateParameters(Params); - } - - TemplateDecl(Kind DK, DeclContext *DC, SourceLocation L, DeclarationName Name, - TemplateParameterList *Params, NamedDecl *Decl) - : TemplateDecl(nullptr, DK, DC, L, Name, Params, Decl) {} + : TemplateDecl(DK, DC, L, Name, Params, nullptr) {} public: + friend class ASTDeclReader; + friend class ASTDeclWriter; + /// Get the list of template parameters TemplateParameterList *getTemplateParameters() const { - const auto *const CTDI = - TemplateParams.dyn_cast(); - return CTDI ? CTDI->getTemplateParameters() - : TemplateParams.get(); + return TemplateParams; } - /// Get the constraint-expression from the associated requires-clause (if any) - const Expr *getRequiresClause() const { - const TemplateParameterList *const TP = getTemplateParameters(); - return TP ? TP->getRequiresClause() : nullptr; - } + /// \brief Get the total constraint-expression associated with this template, + /// including constraint-expressions derived from the requires-clause, + /// trailing requires-clause (for functions and methods) and constrained + /// template parameters. + void getAssociatedConstraints(llvm::SmallVectorImpl &AC) const; - Expr *getAssociatedConstraints() const { - const auto *const C = cast(getCanonicalDecl()); - const auto *const CTDI = - C->TemplateParams.dyn_cast(); - return CTDI ? CTDI->getAssociatedConstraints() : nullptr; - } + bool hasAssociatedConstraints() const; /// Get the underlying, templated declaration. NamedDecl *getTemplatedDecl() const { return TemplatedDecl; } @@ -470,29 +432,10 @@ public: protected: NamedDecl *TemplatedDecl; - - /// The template parameter list and optional requires-clause - /// associated with this declaration; alternatively, a - /// \c ConstrainedTemplateDeclInfo if the associated constraints of the - /// template are being tracked by this particular declaration. - llvm::PointerUnion - TemplateParams; + TemplateParameterList *TemplateParams; void setTemplateParameters(TemplateParameterList *TParams) { - if (auto *const CTDI = - TemplateParams.dyn_cast()) { - CTDI->setTemplateParameters(TParams); - } else { - TemplateParams = TParams; - } - } - - void setAssociatedConstraints(Expr *AC) { - assert(isCanonicalDecl() && - "Attaching associated constraints to non-canonical Decl"); - TemplateParams.get() - ->setAssociatedConstraints(AC); + TemplateParams = TParams; } public: @@ -889,17 +832,10 @@ protected: virtual CommonBase *newCommon(ASTContext &C) const = 0; // Construct a template decl with name, parameters, and templated element. - RedeclarableTemplateDecl(ConstrainedTemplateDeclInfo *CTDI, Kind DK, - ASTContext &C, DeclContext *DC, SourceLocation L, - DeclarationName Name, TemplateParameterList *Params, - NamedDecl *Decl) - : TemplateDecl(CTDI, DK, DC, L, Name, Params, Decl), redeclarable_base(C) - {} - RedeclarableTemplateDecl(Kind DK, ASTContext &C, DeclContext *DC, SourceLocation L, DeclarationName Name, TemplateParameterList *Params, NamedDecl *Decl) - : RedeclarableTemplateDecl(nullptr, DK, C, DC, L, Name, Params, Decl) {} + : TemplateDecl(DK, DC, L, Name, Params, Decl), redeclarable_base(C) {} public: friend class ASTDeclReader; @@ -2026,6 +1962,20 @@ public: return TemplateParams; } + /// \brief All associated constraints of this partial specialization, + /// including the requires clause and any constraints derived from + /// constrained-parameters. + /// + /// The constraints in the resulting list are to be treated as if in a + /// conjunction ("and"). + void getAssociatedConstraints(llvm::SmallVectorImpl &AC) const { + TemplateParams->getAssociatedConstraints(AC); + } + + bool hasAssociatedConstraints() const { + return TemplateParams->hasAssociatedConstraints(); + } + /// Get the template arguments as written. const ASTTemplateArgumentListInfo *getTemplateArgsAsWritten() const { return ArgsAsWritten; @@ -2145,16 +2095,10 @@ protected: llvm::FoldingSetVector & getPartialSpecializations(); - ClassTemplateDecl(ConstrainedTemplateDeclInfo *CTDI, ASTContext &C, - DeclContext *DC, SourceLocation L, DeclarationName Name, - TemplateParameterList *Params, NamedDecl *Decl) - : RedeclarableTemplateDecl(CTDI, ClassTemplate, C, DC, L, Name, Params, - Decl) {} - ClassTemplateDecl(ASTContext &C, DeclContext *DC, SourceLocation L, DeclarationName Name, TemplateParameterList *Params, NamedDecl *Decl) - : ClassTemplateDecl(nullptr, C, DC, L, Name, Params, Decl) {} + : RedeclarableTemplateDecl(ClassTemplate, C, DC, L, Name, Params, Decl) {} CommonBase *newCommon(ASTContext &C) const override; @@ -2180,14 +2124,12 @@ public: return getTemplatedDecl()->isThisDeclarationADefinition(); } - // FIXME: remove default argument for AssociatedConstraints - /// Create a class template node. + /// \brief Create a class template node. static ClassTemplateDecl *Create(ASTContext &C, DeclContext *DC, SourceLocation L, DeclarationName Name, TemplateParameterList *Params, - NamedDecl *Decl, - Expr *AssociatedConstraints = nullptr); + NamedDecl *Decl); /// Create an empty class template node. static ClassTemplateDecl *CreateDeserialized(ASTContext &C, unsigned ID); @@ -2527,10 +2469,6 @@ public: } }; -/// Implementation of inline functions that require the template declarations -inline AnyFunctionDecl::AnyFunctionDecl(FunctionTemplateDecl *FTD) - : Function(FTD) {} - /// Represents a variable template specialization, which refers to /// a variable template with a given set of template arguments. /// @@ -2866,7 +2804,21 @@ public: return ArgsAsWritten; } - /// Retrieve the member variable template partial specialization from + /// \brief All associated constraints of this partial specialization, + /// including the requires clause and any constraints derived from + /// constrained-parameters. + /// + /// The constraints in the resulting list are to be treated as if in a + /// conjunction ("and"). + void getAssociatedConstraints(llvm::SmallVectorImpl &AC) const { + TemplateParams->getAssociatedConstraints(AC); + } + + bool hasAssociatedConstraints() const { + return TemplateParams->hasAssociatedConstraints(); + } + + /// \brief Retrieve the member variable template partial specialization from /// which this particular variable template partial specialization was /// instantiated. /// @@ -3095,11 +3047,9 @@ class ConceptDecl : public TemplateDecl, public Mergeable { protected: Expr *ConstraintExpr; - ConceptDecl(DeclContext *DC, - SourceLocation L, DeclarationName Name, - TemplateParameterList *Params, - Expr *ConstraintExpr) - : TemplateDecl(nullptr, Concept, DC, L, Name, Params), + ConceptDecl(DeclContext *DC, SourceLocation L, DeclarationName Name, + TemplateParameterList *Params, Expr *ConstraintExpr) + : TemplateDecl(Concept, DC, L, Name, Params), ConstraintExpr(ConstraintExpr) {}; public: static ConceptDecl *Create(ASTContext &C, DeclContext *DC, diff --git a/include/clang/AST/Expr.h b/include/clang/AST/Expr.h index d44a815c8699e..ffa7d4db96a47 100644 --- a/include/clang/AST/Expr.h +++ b/include/clang/AST/Expr.h @@ -906,6 +906,11 @@ public: return skipRValueSubobjectAdjustments(CommaLHSs, Adjustments); } + /// Checks that the two Expr's will refer to the same value as a comparison + /// operand. The caller must ensure that the values referenced by the Expr's + /// are not modified between E1 and E2 or the result my be invalid. + static bool isSameComparisonOperand(const Expr* E1, const Expr* E2); + static bool classof(const Stmt *T) { return T->getStmtClass() >= firstExprConstant && T->getStmtClass() <= lastExprConstant; @@ -2619,9 +2624,8 @@ public: /// + sizeof(Stmt *) bytes of storage, aligned to alignof(CallExpr): /// /// \code{.cpp} - /// llvm::AlignedCharArray Buffer; - /// CallExpr *TheCall = CallExpr::CreateTemporary(Buffer.buffer, etc); + /// alignas(CallExpr) char Buffer[sizeof(CallExpr) + sizeof(Stmt *)]; + /// CallExpr *TheCall = CallExpr::CreateTemporary(Buffer, etc); /// \endcode static CallExpr *CreateTemporary(void *Mem, Expr *Fn, QualType Ty, ExprValueKind VK, SourceLocation RParenLoc, @@ -4496,6 +4500,8 @@ public: // Explicit InitListExpr's originate from source code (and have valid source // locations). Implicit InitListExpr's are created by the semantic analyzer. + // FIXME: This is wrong; InitListExprs created by semantic analysis have + // valid source locations too! bool isExplicit() const { return LBraceLoc.isValid() && RBraceLoc.isValid(); } @@ -4830,6 +4836,10 @@ public: SourceLocation getEqualOrColonLoc() const { return EqualOrColonLoc; } void setEqualOrColonLoc(SourceLocation L) { EqualOrColonLoc = L; } + /// Whether this designated initializer should result in direct-initialization + /// of the designated subobject (eg, '{.foo{1, 2, 3}}'). + bool isDirectInit() const { return EqualOrColonLoc.isInvalid(); } + /// Determines whether this designated initializer used the /// deprecated GNU syntax for designated initializers. bool usesGNUSyntax() const { return GNUSyntax; } diff --git a/include/clang/AST/ExprCXX.h b/include/clang/AST/ExprCXX.h index 28ed6cdfde14f..2152e108c7cba 100644 --- a/include/clang/AST/ExprCXX.h +++ b/include/clang/AST/ExprCXX.h @@ -17,6 +17,7 @@ #include "clang/AST/Decl.h" #include "clang/AST/DeclBase.h" #include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclTemplate.h" #include "clang/AST/DeclarationName.h" #include "clang/AST/Expr.h" #include "clang/AST/NestedNameSpecifier.h" @@ -185,15 +186,20 @@ public: static CXXMemberCallExpr *CreateEmpty(const ASTContext &Ctx, unsigned NumArgs, EmptyShell Empty); - /// Retrieves the implicit object argument for the member call. + /// Retrieve the implicit object argument for the member call. /// /// For example, in "x.f(5)", this returns the sub-expression "x". Expr *getImplicitObjectArgument() const; - /// Retrieves the declaration of the called method. + /// Retrieve the type of the object argument. + /// + /// Note that this always returns a non-pointer type. + QualType getObjectType() const; + + /// Retrieve the declaration of the called method. CXXMethodDecl *getMethodDecl() const; - /// Retrieves the CXXRecordDecl for the underlying type of + /// Retrieve the CXXRecordDecl for the underlying type of /// the implicit object argument. /// /// Note that this is may not be the same declaration as that of the class @@ -248,6 +254,96 @@ public: } }; +/// A rewritten comparison expression that was originally written using +/// operator syntax. +/// +/// In C++20, the following rewrites are performed: +/// - a == b -> b == a +/// - a != b -> !(a == b) +/// - a != b -> !(b == a) +/// - For \c \@ in \c <, \c <=, \c >, \c >=, \c <=>: +/// - a @ b -> (a <=> b) @ 0 +/// - a @ b -> 0 @ (b <=> a) +/// +/// This expression provides access to both the original syntax and the +/// rewritten expression. +/// +/// Note that the rewritten calls to \c ==, \c <=>, and \c \@ are typically +/// \c CXXOperatorCallExprs, but could theoretically be \c BinaryOperators. +class CXXRewrittenBinaryOperator : public Expr { + friend class ASTStmtReader; + + /// The rewritten semantic form. + Stmt *SemanticForm; + +public: + CXXRewrittenBinaryOperator(Expr *SemanticForm, bool IsReversed) + : Expr(CXXRewrittenBinaryOperatorClass, SemanticForm->getType(), + SemanticForm->getValueKind(), SemanticForm->getObjectKind(), + SemanticForm->isTypeDependent(), SemanticForm->isValueDependent(), + SemanticForm->isInstantiationDependent(), + SemanticForm->containsUnexpandedParameterPack()), + SemanticForm(SemanticForm) { + CXXRewrittenBinaryOperatorBits.IsReversed = IsReversed; + } + CXXRewrittenBinaryOperator(EmptyShell Empty) + : Expr(CXXRewrittenBinaryOperatorClass, Empty), SemanticForm() {} + + /// Get an equivalent semantic form for this expression. + Expr *getSemanticForm() { return cast(SemanticForm); } + const Expr *getSemanticForm() const { return cast(SemanticForm); } + + struct DecomposedForm { + /// The original opcode, prior to rewriting. + BinaryOperatorKind Opcode; + /// The original left-hand side. + const Expr *LHS; + /// The original right-hand side. + const Expr *RHS; + /// The inner \c == or \c <=> operator expression. + const Expr *InnerBinOp; + }; + + /// Decompose this operator into its syntactic form. + DecomposedForm getDecomposedForm() const LLVM_READONLY; + + /// Determine whether this expression was rewritten in reverse form. + bool isReversed() const { return CXXRewrittenBinaryOperatorBits.IsReversed; } + + BinaryOperatorKind getOperator() const { return getDecomposedForm().Opcode; } + const Expr *getLHS() const { return getDecomposedForm().LHS; } + const Expr *getRHS() const { return getDecomposedForm().RHS; } + + SourceLocation getOperatorLoc() const LLVM_READONLY { + return getDecomposedForm().InnerBinOp->getExprLoc(); + } + SourceLocation getExprLoc() const LLVM_READONLY { return getOperatorLoc(); } + + /// Compute the begin and end locations from the decomposed form. + /// The locations of the semantic form are not reliable if this is + /// a reversed expression. + //@{ + SourceLocation getBeginLoc() const LLVM_READONLY { + return getDecomposedForm().LHS->getBeginLoc(); + } + SourceLocation getEndLoc() const LLVM_READONLY { + return getDecomposedForm().RHS->getEndLoc(); + } + SourceRange getSourceRange() const LLVM_READONLY { + DecomposedForm DF = getDecomposedForm(); + return SourceRange(DF.LHS->getBeginLoc(), DF.RHS->getEndLoc()); + } + //@} + + child_range children() { + return child_range(&SemanticForm, &SemanticForm + 1); + } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == CXXRewrittenBinaryOperatorClass; + } +}; + /// Abstract class common to all of the C++ "named"/"keyword" casts. /// /// This abstract class is inherited by all of the classes @@ -1902,6 +1998,10 @@ public: /// lambda expression. CXXMethodDecl *getCallOperator() const; + /// Retrieve the function template call operator associated with this + /// lambda expression. + FunctionTemplateDecl *getDependentCallOperator() const; + /// If this is a generic lambda expression, retrieve the template /// parameter list associated with it, or else return null. TemplateParameterList *getTemplateParameterList() const; @@ -2091,8 +2191,7 @@ public: bool IsParenTypeId); QualType getAllocatedType() const { - assert(getType()->isPointerType()); - return getType()->getAs()->getPointeeType(); + return getType()->castAs()->getPointeeType(); } TypeSourceInfo *getAllocatedTypeSourceInfo() const { @@ -2270,8 +2369,8 @@ public: CXXDeleteExpr(QualType Ty, bool GlobalDelete, bool ArrayForm, bool ArrayFormAsWritten, bool UsualArrayDeleteWantsSize, FunctionDecl *OperatorDelete, Expr *Arg, SourceLocation Loc) - : Expr(CXXDeleteExprClass, Ty, VK_RValue, OK_Ordinary, false, false, - Arg->isInstantiationDependent(), + : Expr(CXXDeleteExprClass, Ty, VK_RValue, OK_Ordinary, false, + Arg->isValueDependent(), Arg->isInstantiationDependent(), Arg->containsUnexpandedParameterPack()), OperatorDelete(OperatorDelete), Argument(Arg) { CXXDeleteExprBits.GlobalDelete = GlobalDelete; @@ -4335,9 +4434,6 @@ private: }; llvm::PointerUnion State; - void initializeExtraState(const ValueDecl *ExtendedBy, - unsigned ManglingNumber); - public: MaterializeTemporaryExpr(QualType T, Expr *Temporary, bool BoundToLvalueReference) @@ -4745,6 +4841,125 @@ public: } }; +/// \brief Represents the specialization of a concept - evaluates to a prvalue +/// of type bool. +/// +/// According to C++2a [expr.prim.id]p3 an id-expression that denotes the +/// specialization of a concept results in a prvalue of type bool. +class ConceptSpecializationExpr final : public Expr, + private llvm::TrailingObjects { + friend class ASTStmtReader; + friend TrailingObjects; + + // \brief The optional nested name specifier used when naming the concept. + NestedNameSpecifierLoc NestedNameSpec; + + /// \brief The location of the template keyword, if specified when naming the + /// concept. + SourceLocation TemplateKWLoc; + + /// \brief The location of the concept name in the expression. + SourceLocation ConceptNameLoc; + + /// \brief The declaration found by name lookup when the expression was + /// created. + /// Can differ from NamedConcept when, for example, the concept was found + /// through a UsingShadowDecl. + NamedDecl *FoundDecl; + + /// \brief The concept named, and whether or not the concept with the given + /// arguments was satisfied when the expression was created. + /// If any of the template arguments are dependent (this expr would then be + /// isValueDependent()), this bit is to be ignored. + llvm::PointerIntPair NamedConcept; + + /// \brief The template argument list source info used to specialize the + /// concept. + const ASTTemplateArgumentListInfo *ArgsAsWritten = nullptr; + + /// \brief The number of template arguments in the tail-allocated list of + /// converted template arguments. + unsigned NumTemplateArgs; + + ConceptSpecializationExpr(ASTContext &C, NestedNameSpecifierLoc NNS, + SourceLocation TemplateKWLoc, + SourceLocation ConceptNameLoc, NamedDecl *FoundDecl, + ConceptDecl *NamedConcept, + const ASTTemplateArgumentListInfo *ArgsAsWritten, + ArrayRef ConvertedArgs, + Optional IsSatisfied); + + ConceptSpecializationExpr(EmptyShell Empty, unsigned NumTemplateArgs); + +public: + + static ConceptSpecializationExpr * + Create(ASTContext &C, NestedNameSpecifierLoc NNS, + SourceLocation TemplateKWLoc, SourceLocation ConceptNameLoc, + NamedDecl *FoundDecl, ConceptDecl *NamedConcept, + const ASTTemplateArgumentListInfo *ArgsAsWritten, + ArrayRef ConvertedArgs, Optional IsSatisfied); + + static ConceptSpecializationExpr * + Create(ASTContext &C, EmptyShell Empty, unsigned NumTemplateArgs); + + const NestedNameSpecifierLoc &getNestedNameSpecifierLoc() const { + return NestedNameSpec; + } + + NamedDecl *getFoundDecl() const { + return FoundDecl; + } + + ConceptDecl *getNamedConcept() const { + return NamedConcept.getPointer(); + } + + ArrayRef getTemplateArguments() const { + return ArrayRef(getTrailingObjects(), + NumTemplateArgs); + } + + const ASTTemplateArgumentListInfo *getTemplateArgsAsWritten() const { + return ArgsAsWritten; + } + + /// \brief Set new template arguments for this concept specialization. + void setTemplateArguments(const ASTTemplateArgumentListInfo *ArgsAsWritten, + ArrayRef Converted); + + /// \brief Whether or not the concept with the given arguments was satisfied + /// when the expression was created. This method assumes that the expression + /// is not dependent! + bool isSatisfied() const { + assert(!isValueDependent() + && "isSatisfied called on a dependent ConceptSpecializationExpr"); + return NamedConcept.getInt(); + } + + SourceLocation getConceptNameLoc() const { return ConceptNameLoc; } + + SourceLocation getTemplateKWLoc() const { return TemplateKWLoc; } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == ConceptSpecializationExprClass; + } + + SourceLocation getBeginLoc() const LLVM_READONLY { return ConceptNameLoc; } + SourceLocation getEndLoc() const LLVM_READONLY { + return ArgsAsWritten->RAngleLoc; + } + + // Iterators + child_range children() { + return child_range(child_iterator(), child_iterator()); + } + const_child_range children() const { + return const_child_range(const_child_iterator(), const_child_iterator()); + } +}; + } // namespace clang #endif // LLVM_CLANG_AST_EXPRCXX_H diff --git a/include/clang/AST/ExternalASTMerger.h b/include/clang/AST/ExternalASTMerger.h index d89189da04f02..0230495a5ef30 100644 --- a/include/clang/AST/ExternalASTMerger.h +++ b/include/clang/AST/ExternalASTMerger.h @@ -14,6 +14,7 @@ #define LLVM_CLANG_AST_EXTERNALASTMERGER_H #include "clang/AST/ASTImporter.h" +#include "clang/AST/ASTImporterSharedState.h" #include "clang/AST/ExternalASTSource.h" #include "llvm/Support/raw_ostream.h" @@ -22,7 +23,7 @@ namespace clang { /// ExternalASTSource implementation that merges information from several /// ASTContexts. /// -/// ExtermalASTMerger maintains a vector of ASTImporters that it uses to import +/// ExternalASTMerger maintains a vector of ASTImporters that it uses to import /// (potentially incomplete) Decls and DeclContexts from the source ASTContexts /// in response to ExternalASTSource API calls. /// @@ -36,7 +37,7 @@ namespace clang { /// lookup. In this case, Origins contains an entry overriding lookup and /// specifying the correct pair of DeclContext/ASTContext. /// -/// - The DeclContext of origin was determined by another ExterenalASTMerger. +/// - The DeclContext of origin was determined by another ExternalASTMerger. /// (This is possible when the source ASTContext for one of the Importers has /// its own ExternalASTMerger). The origin must be properly forwarded in this /// case. @@ -79,20 +80,47 @@ public: /// import SourceLocations properly. Additionally, when import occurs for /// a DeclContext whose origin has been overridden, then this /// ExternalASTMerger must be able to determine that. - struct ImporterSource { + class ImporterSource { ASTContext &AST; FileManager &FM; const OriginMap &OM; + /// True iff the source only exists temporary, i.e., it will be removed from + /// the ExternalASTMerger during the life time of the ExternalASTMerger. + bool Temporary; + /// If the ASTContext of this source has an ExternalASTMerger that imports + /// into this source, then this will point to that other ExternalASTMerger. + ExternalASTMerger *Merger; + + public: + ImporterSource(ASTContext &AST, FileManager &FM, const OriginMap &OM, + bool Temporary = false, ExternalASTMerger *Merger = nullptr) + : AST(AST), FM(FM), OM(OM), Temporary(Temporary), Merger(Merger) {} + ASTContext &getASTContext() const { return AST; } + FileManager &getFileManager() const { return FM; } + const OriginMap &getOriginMap() const { return OM; } + bool isTemporary() const { return Temporary; } + ExternalASTMerger *getMerger() const { return Merger; } }; private: - /// The target for this ExtenralASTMerger. + /// The target for this ExternalASTMerger. ImporterTarget Target; + /// ExternalASTMerger has multiple ASTImporters that import into the same + /// TU. This is the shared state for all ASTImporters of this + /// ExternalASTMerger. + /// See also the CrossTranslationUnitContext that has a similar setup. + std::shared_ptr SharedState; public: ExternalASTMerger(const ImporterTarget &Target, llvm::ArrayRef Sources); + /// Asks all connected ASTImporters if any of them imported the given + /// declaration. If any ASTImporter did import the given declaration, + /// then this function returns the declaration that D was imported from. + /// Returns nullptr if no ASTImporter did import import D. + Decl *FindOriginalDecl(Decl *D); + /// Add a set of ASTContexts as possible origins. /// /// Usually the set will be initialized in the constructor, but long-lived @@ -145,7 +173,7 @@ public: /// OriginContext. bool HasImporterForOrigin(ASTContext &OriginContext); - /// Returns a reference to the ASTRImporter from Importers whose origin + /// Returns a reference to the ASTImporter from Importers whose origin /// is OriginContext. This allows manual import of ASTs while preserving the /// OriginMap correctly. ASTImporter &ImporterForOrigin(ASTContext &OriginContext); diff --git a/include/clang/AST/FormatString.h b/include/clang/AST/FormatString.h index 643fb822f7f40..8c944451f796b 100644 --- a/include/clang/AST/FormatString.h +++ b/include/clang/AST/FormatString.h @@ -251,7 +251,21 @@ public: enum Kind { UnknownTy, InvalidTy, SpecificTy, ObjCPointerTy, CPointerTy, AnyCharTy, CStrTy, WCStrTy, WIntTy }; - enum MatchKind { NoMatch = 0, Match = 1, NoMatchPedantic }; + /// How well a given conversion specifier matches its argument. + enum MatchKind { + /// The conversion specifier and the argument types are incompatible. For + /// instance, "%d" and float. + NoMatch = 0, + /// The conversion specifier and the argument type are compatible. For + /// instance, "%d" and _Bool. + Match = 1, + /// The conversion specifier and the argument type are disallowed by the C + /// standard, but are in practice harmless. For instance, "%p" and int*. + NoMatchPedantic, + /// The conversion specifier and the argument type are compatible, but still + /// seems likely to be an error. For instance, "%hd" and _Bool. + NoMatchTypeConfusion, + }; private: const Kind K; @@ -748,6 +762,12 @@ bool ParseScanfString(FormatStringHandler &H, const char *beg, const char *end, const LangOptions &LO, const TargetInfo &Target); +/// Return true if the given string has at least one formatting specifier. +bool parseFormatStringHasFormattingSpecifiers(const char *Begin, + const char *End, + const LangOptions &LO, + const TargetInfo &Target); + } // end analyze_format_string namespace } // end clang namespace #endif diff --git a/include/clang/AST/GlobalDecl.h b/include/clang/AST/GlobalDecl.h index 86fd0f6aa9076..145e961a23a38 100644 --- a/include/clang/AST/GlobalDecl.h +++ b/include/clang/AST/GlobalDecl.h @@ -59,6 +59,7 @@ public: GlobalDecl(const CapturedDecl *D) { Init(D); } GlobalDecl(const ObjCMethodDecl *D) { Init(D); } GlobalDecl(const OMPDeclareReductionDecl *D) { Init(D); } + GlobalDecl(const OMPDeclareMapperDecl *D) { Init(D); } GlobalDecl(const CXXConstructorDecl *D, CXXCtorType Type) : Value(D, Type) {} GlobalDecl(const CXXDestructorDecl *D, CXXDtorType Type) : Value(D, Type) {} GlobalDecl(const VarDecl *D, DynamicInitKind StubKind) diff --git a/include/clang/AST/JSONNodeDumper.h b/include/clang/AST/JSONNodeDumper.h index 238e43aad78b0..5f34440b8b561 100644 --- a/include/clang/AST/JSONNodeDumper.h +++ b/include/clang/AST/JSONNodeDumper.h @@ -141,6 +141,8 @@ class JSONNodeDumper JOS.attribute(Key, Value); } + void writeIncludeStack(PresumedLoc Loc, bool JustFirst = false); + // Writes the attributes of a SourceLocation object without. void writeBareSourceLocation(SourceLocation Loc, bool IsSpelling); diff --git a/include/clang/AST/Mangle.h b/include/clang/AST/Mangle.h index b1fbe936136a9..5db5c5b977da5 100644 --- a/include/clang/AST/Mangle.h +++ b/include/clang/AST/Mangle.h @@ -56,7 +56,7 @@ private: llvm::DenseMap GlobalBlockIds; llvm::DenseMap LocalBlockIds; - llvm::DenseMap AnonStructIds; + llvm::DenseMap AnonStructIds; public: ManglerKind getKind() const { return Kind; } @@ -82,9 +82,9 @@ public: return Result.first->second; } - uint64_t getAnonymousStructId(const TagDecl *TD) { - std::pair::iterator, bool> - Result = AnonStructIds.insert(std::make_pair(TD, AnonStructIds.size())); + uint64_t getAnonymousStructId(const NamedDecl *D) { + std::pair::iterator, bool> + Result = AnonStructIds.insert(std::make_pair(D, AnonStructIds.size())); return Result.first->second; } @@ -170,6 +170,8 @@ public: virtual void mangleCXXDtorComdat(const CXXDestructorDecl *D, raw_ostream &) = 0; + virtual void mangleLambdaSig(const CXXRecordDecl *Lambda, raw_ostream &) = 0; + static bool classof(const MangleContext *C) { return C->getKind() == MK_Itanium; } @@ -248,8 +250,16 @@ class ASTNameGenerator { public: explicit ASTNameGenerator(ASTContext &Ctx); ~ASTNameGenerator(); + + /// Writes name for \p D to \p OS. + /// \returns true on failure, false on success. bool writeName(const Decl *D, raw_ostream &OS); + + /// \returns name for \p D std::string getName(const Decl *D); + + /// \returns all applicable mangled names. + /// For example C++ constructors/destructors can have multiple. std::vector getAllManglings(const Decl *D); private: diff --git a/include/clang/AST/NSAPI.h b/include/clang/AST/NSAPI.h index 21f0c5458d817..a8bd2d0f17e6f 100644 --- a/include/clang/AST/NSAPI.h +++ b/include/clang/AST/NSAPI.h @@ -55,9 +55,6 @@ public: /// The Objective-C NSString selectors. Selector getNSStringSelector(NSStringMethodKind MK) const; - /// Return NSStringMethodKind if \param Sel is such a selector. - Optional getNSStringMethodKind(Selector Sel) const; - /// Returns true if the expression \param E is a reference of /// "NSUTF8StringEncoding" enum constant. bool isNSUTF8StringEncodingConstant(const Expr *E) const { diff --git a/include/clang/AST/OpenMPClause.h b/include/clang/AST/OpenMPClause.h index eadcc62a34575..b2a2035dcb3cc 100644 --- a/include/clang/AST/OpenMPClause.h +++ b/include/clang/AST/OpenMPClause.h @@ -519,7 +519,7 @@ public: /// \endcode /// In this example directive '#pragma omp task' has simple 'final' /// clause with condition 'a > 5'. -class OMPFinalClause : public OMPClause { +class OMPFinalClause : public OMPClause, public OMPClauseWithPreInit { friend class OMPClauseReader; /// Location of '('. @@ -534,18 +534,25 @@ class OMPFinalClause : public OMPClause { public: /// Build 'final' clause with condition \a Cond. /// + /// \param Cond Condition of the clause. + /// \param HelperCond Helper condition for the construct. + /// \param CaptureRegion Innermost OpenMP region where expressions in this + /// clause must be captured. /// \param StartLoc Starting location of the clause. /// \param LParenLoc Location of '('. - /// \param Cond Condition of the clause. /// \param EndLoc Ending location of the clause. - OMPFinalClause(Expr *Cond, SourceLocation StartLoc, SourceLocation LParenLoc, - SourceLocation EndLoc) - : OMPClause(OMPC_final, StartLoc, EndLoc), LParenLoc(LParenLoc), - Condition(Cond) {} + OMPFinalClause(Expr *Cond, Stmt *HelperCond, + OpenMPDirectiveKind CaptureRegion, SourceLocation StartLoc, + SourceLocation LParenLoc, SourceLocation EndLoc) + : OMPClause(OMPC_final, StartLoc, EndLoc), OMPClauseWithPreInit(this), + LParenLoc(LParenLoc), Condition(Cond) { + setPreInitStmt(HelperCond, CaptureRegion); + } /// Build an empty clause. OMPFinalClause() - : OMPClause(OMPC_final, SourceLocation(), SourceLocation()) {} + : OMPClause(OMPC_final, SourceLocation(), SourceLocation()), + OMPClauseWithPreInit(this) {} /// Sets the location of '('. void setLParenLoc(SourceLocation Loc) { LParenLoc = Loc; } @@ -562,11 +569,10 @@ public: return const_child_range(&Condition, &Condition + 1); } - child_range used_children() { - return child_range(child_iterator(), child_iterator()); - } + child_range used_children(); const_child_range used_children() const { - return const_child_range(const_child_iterator(), const_child_iterator()); + auto Children = const_cast(this)->used_children(); + return const_child_range(Children.begin(), Children.end()); } static bool classof(const OMPClause *T) { @@ -2099,10 +2105,12 @@ public: } child_range used_children() { - return child_range(child_iterator(), child_iterator()); + return child_range(reinterpret_cast(varlist_begin()), + reinterpret_cast(varlist_end())); } const_child_range used_children() const { - return const_child_range(const_child_iterator(), const_child_iterator()); + auto Children = const_cast(this)->used_children(); + return const_child_range(Children.begin(), Children.end()); } static bool classof(const OMPClause *T) { @@ -2616,10 +2624,12 @@ public: } child_range used_children() { - return child_range(child_iterator(), child_iterator()); + return child_range(reinterpret_cast(varlist_begin()), + reinterpret_cast(varlist_end())); } const_child_range used_children() const { - return const_child_range(const_child_iterator(), const_child_iterator()); + auto Children = const_cast(this)->used_children(); + return const_child_range(Children.begin(), Children.end()); } static bool classof(const OMPClause *T) { @@ -3212,6 +3222,14 @@ class OMPLinearClause final return llvm::makeArrayRef(getUpdates().end(), varlist_size()); } + /// Gets the list of used expressions for linear variables. + MutableArrayRef getUsedExprs() { + return MutableArrayRef(getFinals().end() + 2, varlist_size() + 1); + } + ArrayRef getUsedExprs() const { + return llvm::makeArrayRef(getFinals().end() + 2, varlist_size() + 1); + } + /// Sets the list of the copies of original linear variables. /// \param PL List of expressions. void setPrivates(ArrayRef PL); @@ -3291,6 +3309,9 @@ public: /// \param FL List of expressions. void setFinals(ArrayRef FL); + /// Sets the list of used expressions for the linear clause. + void setUsedExprs(ArrayRef UE); + using privates_iterator = MutableArrayRef::iterator; using privates_const_iterator = ArrayRef::iterator; using privates_range = llvm::iterator_range; @@ -3343,6 +3364,21 @@ public: return finals_const_range(getFinals().begin(), getFinals().end()); } + using used_expressions_iterator = MutableArrayRef::iterator; + using used_expressions_const_iterator = ArrayRef::iterator; + using used_expressions_range = + llvm::iterator_range; + using used_expressions_const_range = + llvm::iterator_range; + + used_expressions_range used_expressions() { + return finals_range(getUsedExprs().begin(), getUsedExprs().end()); + } + + used_expressions_const_range used_expressions() const { + return finals_const_range(getUsedExprs().begin(), getUsedExprs().end()); + } + child_range children() { return child_range(reinterpret_cast(varlist_begin()), reinterpret_cast(varlist_end())); @@ -3353,11 +3389,11 @@ public: return const_child_range(Children.begin(), Children.end()); } - child_range used_children() { - return child_range(child_iterator(), child_iterator()); - } + child_range used_children(); + const_child_range used_children() const { - return const_child_range(const_child_iterator(), const_child_iterator()); + auto Children = const_cast(this)->used_children(); + return const_child_range(Children.begin(), Children.end()); } static bool classof(const OMPClause *T) { @@ -4995,12 +5031,17 @@ public: } child_range used_children() { + if (MapType == OMPC_MAP_to || MapType == OMPC_MAP_tofrom) + return child_range(reinterpret_cast(varlist_begin()), + reinterpret_cast(varlist_end())); return child_range(child_iterator(), child_iterator()); } const_child_range used_children() const { - return const_child_range(const_child_iterator(), const_child_iterator()); + auto Children = const_cast(this)->used_children(); + return const_child_range(Children.begin(), Children.end()); } + static bool classof(const OMPClause *T) { return T->getClauseKind() == OMPC_map; } @@ -5165,7 +5206,7 @@ public: /// \endcode /// In this example directive '#pragma omp teams' has clause 'priority' with /// single expression 'n'. -class OMPPriorityClause : public OMPClause { +class OMPPriorityClause : public OMPClause, public OMPClauseWithPreInit { friend class OMPClauseReader; /// Location of '('. @@ -5182,18 +5223,25 @@ class OMPPriorityClause : public OMPClause { public: /// Build 'priority' clause. /// - /// \param E Expression associated with this clause. + /// \param Priority Expression associated with this clause. + /// \param HelperPriority Helper priority for the construct. + /// \param CaptureRegion Innermost OpenMP region where expressions in this + /// clause must be captured. /// \param StartLoc Starting location of the clause. /// \param LParenLoc Location of '('. /// \param EndLoc Ending location of the clause. - OMPPriorityClause(Expr *E, SourceLocation StartLoc, SourceLocation LParenLoc, - SourceLocation EndLoc) - : OMPClause(OMPC_priority, StartLoc, EndLoc), LParenLoc(LParenLoc), - Priority(E) {} + OMPPriorityClause(Expr *Priority, Stmt *HelperPriority, + OpenMPDirectiveKind CaptureRegion, SourceLocation StartLoc, + SourceLocation LParenLoc, SourceLocation EndLoc) + : OMPClause(OMPC_priority, StartLoc, EndLoc), OMPClauseWithPreInit(this), + LParenLoc(LParenLoc), Priority(Priority) { + setPreInitStmt(HelperPriority, CaptureRegion); + } /// Build an empty clause. OMPPriorityClause() - : OMPClause(OMPC_priority, SourceLocation(), SourceLocation()) {} + : OMPClause(OMPC_priority, SourceLocation(), SourceLocation()), + OMPClauseWithPreInit(this) {} /// Sets the location of '('. void setLParenLoc(SourceLocation Loc) { LParenLoc = Loc; } @@ -5213,11 +5261,10 @@ public: return const_child_range(&Priority, &Priority + 1); } - child_range used_children() { - return child_range(child_iterator(), child_iterator()); - } + child_range used_children(); const_child_range used_children() const { - return const_child_range(const_child_iterator(), const_child_iterator()); + auto Children = const_cast(this)->used_children(); + return const_child_range(Children.begin(), Children.end()); } static bool classof(const OMPClause *T) { @@ -5233,7 +5280,7 @@ public: /// \endcode /// In this example directive '#pragma omp taskloop' has clause 'grainsize' /// with single expression '4'. -class OMPGrainsizeClause : public OMPClause { +class OMPGrainsizeClause : public OMPClause, public OMPClauseWithPreInit { friend class OMPClauseReader; /// Location of '('. @@ -5249,16 +5296,23 @@ public: /// Build 'grainsize' clause. /// /// \param Size Expression associated with this clause. + /// \param HelperSize Helper grainsize for the construct. + /// \param CaptureRegion Innermost OpenMP region where expressions in this + /// clause must be captured. /// \param StartLoc Starting location of the clause. /// \param EndLoc Ending location of the clause. - OMPGrainsizeClause(Expr *Size, SourceLocation StartLoc, + OMPGrainsizeClause(Expr *Size, Stmt *HelperSize, + OpenMPDirectiveKind CaptureRegion, SourceLocation StartLoc, SourceLocation LParenLoc, SourceLocation EndLoc) - : OMPClause(OMPC_grainsize, StartLoc, EndLoc), LParenLoc(LParenLoc), - Grainsize(Size) {} + : OMPClause(OMPC_grainsize, StartLoc, EndLoc), OMPClauseWithPreInit(this), + LParenLoc(LParenLoc), Grainsize(Size) { + setPreInitStmt(HelperSize, CaptureRegion); + } /// Build an empty clause. explicit OMPGrainsizeClause() - : OMPClause(OMPC_grainsize, SourceLocation(), SourceLocation()) {} + : OMPClause(OMPC_grainsize, SourceLocation(), SourceLocation()), + OMPClauseWithPreInit(this) {} /// Sets the location of '('. void setLParenLoc(SourceLocation Loc) { LParenLoc = Loc; } @@ -5275,11 +5329,10 @@ public: return const_child_range(&Grainsize, &Grainsize + 1); } - child_range used_children() { - return child_range(child_iterator(), child_iterator()); - } + child_range used_children(); const_child_range used_children() const { - return const_child_range(const_child_iterator(), const_child_iterator()); + auto Children = const_cast(this)->used_children(); + return const_child_range(Children.begin(), Children.end()); } static bool classof(const OMPClause *T) { @@ -5334,7 +5387,7 @@ public: /// \endcode /// In this example directive '#pragma omp taskloop' has clause 'num_tasks' /// with single expression '4'. -class OMPNumTasksClause : public OMPClause { +class OMPNumTasksClause : public OMPClause, public OMPClauseWithPreInit { friend class OMPClauseReader; /// Location of '('. @@ -5350,16 +5403,23 @@ public: /// Build 'num_tasks' clause. /// /// \param Size Expression associated with this clause. + /// \param HelperSize Helper grainsize for the construct. + /// \param CaptureRegion Innermost OpenMP region where expressions in this + /// clause must be captured. /// \param StartLoc Starting location of the clause. /// \param EndLoc Ending location of the clause. - OMPNumTasksClause(Expr *Size, SourceLocation StartLoc, + OMPNumTasksClause(Expr *Size, Stmt *HelperSize, + OpenMPDirectiveKind CaptureRegion, SourceLocation StartLoc, SourceLocation LParenLoc, SourceLocation EndLoc) - : OMPClause(OMPC_num_tasks, StartLoc, EndLoc), LParenLoc(LParenLoc), - NumTasks(Size) {} + : OMPClause(OMPC_num_tasks, StartLoc, EndLoc), OMPClauseWithPreInit(this), + LParenLoc(LParenLoc), NumTasks(Size) { + setPreInitStmt(HelperSize, CaptureRegion); + } /// Build an empty clause. explicit OMPNumTasksClause() - : OMPClause(OMPC_num_tasks, SourceLocation(), SourceLocation()) {} + : OMPClause(OMPC_num_tasks, SourceLocation(), SourceLocation()), + OMPClauseWithPreInit(this) {} /// Sets the location of '('. void setLParenLoc(SourceLocation Loc) { LParenLoc = Loc; } @@ -5376,11 +5436,10 @@ public: return const_child_range(&NumTasks, &NumTasks + 1); } - child_range used_children() { - return child_range(child_iterator(), child_iterator()); - } + child_range used_children(); const_child_range used_children() const { - return const_child_range(const_child_iterator(), const_child_iterator()); + auto Children = const_cast(this)->used_children(); + return const_child_range(Children.begin(), Children.end()); } static bool classof(const OMPClause *T) { diff --git a/include/clang/AST/OperationKinds.def b/include/clang/AST/OperationKinds.def index 9af92c1ae7ff7..f29664e8eb338 100644 --- a/include/clang/AST/OperationKinds.def +++ b/include/clang/AST/OperationKinds.def @@ -66,8 +66,9 @@ CAST_OPERATION(BitCast) /// bool b; reinterpret_cast(b) = 'a'; CAST_OPERATION(LValueBitCast) -/// CK_LValueToRValueBitCast - A conversion that causes us to reinterpret an -/// lvalue as an rvalue of a different type. Created by __builtin_bit_cast. +/// CK_LValueToRValueBitCast - A conversion that causes us to reinterpret the +/// object representation of an lvalue as an rvalue. Created by +/// __builtin_bit_cast. CAST_OPERATION(LValueToRValueBitCast) /// CK_LValueToRValue - A conversion which causes the extraction of diff --git a/include/clang/AST/OptionalDiagnostic.h b/include/clang/AST/OptionalDiagnostic.h new file mode 100644 index 0000000000000..c57199f0fdf12 --- /dev/null +++ b/include/clang/AST/OptionalDiagnostic.h @@ -0,0 +1,78 @@ +//===- OptionalDiagnostic.h - An optional diagnostic ------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +/// \file +/// Implements a partial diagnostic which may not be emitted. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_AST_OPTIONALDIAGNOSTIC_H +#define LLVM_CLANG_AST_OPTIONALDIAGNOSTIC_H + +#include "clang/AST/APValue.h" +#include "clang/Basic/PartialDiagnostic.h" +#include "llvm/ADT/APFloat.h" +#include "llvm/ADT/APSInt.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" + +namespace clang { + +/// A partial diagnostic which we might know in advance that we are not going +/// to emit. +class OptionalDiagnostic { + PartialDiagnostic *Diag; + +public: + explicit OptionalDiagnostic(PartialDiagnostic *Diag = nullptr) : Diag(Diag) {} + + template OptionalDiagnostic &operator<<(const T &v) { + if (Diag) + *Diag << v; + return *this; + } + + OptionalDiagnostic &operator<<(const llvm::APSInt &I) { + if (Diag) { + SmallVector Buffer; + I.toString(Buffer); + *Diag << StringRef(Buffer.data(), Buffer.size()); + } + return *this; + } + + OptionalDiagnostic &operator<<(const llvm::APFloat &F) { + if (Diag) { + // FIXME: Force the precision of the source value down so we don't + // print digits which are usually useless (we don't really care here if + // we truncate a digit by accident in edge cases). Ideally, + // APFloat::toString would automatically print the shortest + // representation which rounds to the correct value, but it's a bit + // tricky to implement. Could use std::to_chars. + unsigned precision = llvm::APFloat::semanticsPrecision(F.getSemantics()); + precision = (precision * 59 + 195) / 196; + SmallVector Buffer; + F.toString(Buffer, precision); + *Diag << StringRef(Buffer.data(), Buffer.size()); + } + return *this; + } + + OptionalDiagnostic &operator<<(const APFixedPoint &FX) { + if (Diag) { + SmallVector Buffer; + FX.toString(Buffer); + *Diag << StringRef(Buffer.data(), Buffer.size()); + } + return *this; + } +}; + +} // namespace clang + +#endif diff --git a/include/clang/AST/RawCommentList.h b/include/clang/AST/RawCommentList.h index 5dc8694e77e94..1eea56dee6223 100644 --- a/include/clang/AST/RawCommentList.h +++ b/include/clang/AST/RawCommentList.h @@ -10,8 +10,11 @@ #define LLVM_CLANG_AST_RAWCOMMENTLIST_H #include "clang/Basic/CommentOptions.h" +#include "clang/Basic/SourceLocation.h" #include "clang/Basic/SourceManager.h" #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseMap.h" +#include namespace clang { @@ -196,17 +199,25 @@ public: void addComment(const RawComment &RC, const CommentOptions &CommentOpts, llvm::BumpPtrAllocator &Allocator); - ArrayRef getComments() const { - return Comments; - } + /// \returns A mapping from an offset of the start of the comment to the + /// comment itself, or nullptr in case there are no comments in \p File. + const std::map *getCommentsInFile(FileID File) const; + + bool empty() const; + + unsigned getCommentBeginLine(RawComment *C, FileID File, + unsigned Offset) const; + unsigned getCommentEndOffset(RawComment *C) const; private: SourceManager &SourceMgr; - std::vector Comments; - - void addDeserializedComments(ArrayRef DeserializedComments); + // mapping: FileId -> comment begin offset -> comment + llvm::DenseMap> OrderedComments; + mutable llvm::DenseMap CommentBeginLine; + mutable llvm::DenseMap CommentEndOffset; friend class ASTReader; + friend class ASTWriter; }; } // end namespace clang diff --git a/include/clang/AST/RecursiveASTVisitor.h b/include/clang/AST/RecursiveASTVisitor.h index 698fba2f4ed16..5b58eab95d60d 100644 --- a/include/clang/AST/RecursiveASTVisitor.h +++ b/include/clang/AST/RecursiveASTVisitor.h @@ -431,7 +431,7 @@ public: // Declare Traverse*() for all concrete Type classes. #define ABSTRACT_TYPE(CLASS, BASE) #define TYPE(CLASS, BASE) bool Traverse##CLASS##Type(CLASS##Type *T); -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" // The above header #undefs ABSTRACT_TYPE and TYPE upon exit. // Define WalkUpFrom*() and empty Visit*() for all Type classes. @@ -444,7 +444,7 @@ public: return true; \ } \ bool Visit##CLASS##Type(CLASS##Type *T) { return true; } -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" // ---- Methods on TypeLocs ---- // FIXME: this currently just calls the matching Type methods @@ -460,7 +460,7 @@ public: bool VisitTypeLoc(TypeLoc TL) { return true; } // QualifiedTypeLoc and UnqualTypeLoc are not declared in - // TypeNodes.def and thus need to be handled specially. + // TypeNodes.inc and thus need to be handled specially. bool WalkUpFromQualifiedTypeLoc(QualifiedTypeLoc TL) { return getDerived().VisitUnqualTypeLoc(TL.getUnqualifiedLoc()); } @@ -478,7 +478,7 @@ public: return true; \ } \ bool Visit##CLASS##TypeLoc(CLASS##TypeLoc TL) { return true; } -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" // ---- Methods on Decls ---- @@ -676,7 +676,7 @@ bool RecursiveASTVisitor::TraverseType(QualType T) { #define TYPE(CLASS, BASE) \ case Type::CLASS: \ DISPATCH(CLASS##Type, CLASS##Type, const_cast(T.getTypePtr())); -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" } return true; @@ -722,12 +722,6 @@ bool RecursiveASTVisitor::TraverseDecl(Decl *D) { break; #include "clang/AST/DeclNodes.inc" } - - // Visit any attributes attached to this declaration. - for (auto *I : D->attrs()) { - if (!getDerived().TraverseAttr(I)) - return false; - } return true; } @@ -965,8 +959,11 @@ DEF_TRAVERSE_TYPE(AdjustedType, { TRY_TO(TraverseType(T->getOriginalType())); }) DEF_TRAVERSE_TYPE(DecayedType, { TRY_TO(TraverseType(T->getOriginalType())); }) -DEF_TRAVERSE_TYPE(ConstantArrayType, - { TRY_TO(TraverseType(T->getElementType())); }) +DEF_TRAVERSE_TYPE(ConstantArrayType, { + TRY_TO(TraverseType(T->getElementType())); + if (T->getSizeExpr()) + TRY_TO(TraverseStmt(const_cast(T->getSizeExpr()))); +}) DEF_TRAVERSE_TYPE(IncompleteArrayType, { TRY_TO(TraverseType(T->getElementType())); }) @@ -1407,6 +1404,11 @@ bool RecursiveASTVisitor::TraverseDeclContextHelper(DeclContext *DC) { { CODE; } \ if (ReturnValue && ShouldVisitChildren) \ TRY_TO(TraverseDeclContextHelper(dyn_cast(D))); \ + if (ReturnValue) { \ + /* Visit any attributes attached to this declaration. */ \ + for (auto *I : D->attrs()) \ + TRY_TO(getDerived().TraverseAttr(I)); \ + } \ if (ReturnValue && getDerived().shouldTraversePostOrder()) \ TRY_TO(WalkUpFrom##DECL(D)); \ return ReturnValue; \ @@ -1631,9 +1633,11 @@ template bool RecursiveASTVisitor::TraverseTemplateParameterListHelper( TemplateParameterList *TPL) { if (TPL) { - for (TemplateParameterList::iterator I = TPL->begin(), E = TPL->end(); - I != E; ++I) { - TRY_TO(TraverseDecl(*I)); + for (NamedDecl *D : *TPL) { + TRY_TO(TraverseDecl(D)); + } + if (Expr *RequiresClause = TPL->getRequiresClause()) { + TRY_TO(TraverseStmt(RequiresClause)); } } return true; @@ -2023,11 +2027,18 @@ bool RecursiveASTVisitor::TraverseFunctionHelper(FunctionDecl *D) { if (CXXConstructorDecl *Ctor = dyn_cast(D)) { // Constructor initializers. for (auto *I : Ctor->inits()) { - TRY_TO(TraverseConstructorInitializer(I)); + if (I->isWritten() || getDerived().shouldVisitImplicitCode()) + TRY_TO(TraverseConstructorInitializer(I)); } } - if (D->isThisDeclarationADefinition()) { + bool VisitBody = D->isThisDeclarationADefinition(); + // If a method is set to default outside the class definition the compiler + // generates the method body and adds it to the AST. + if (const auto *MD = dyn_cast(D)) + VisitBody &= !MD->isDefaulted() || getDerived().shouldVisitImplicitCode(); + + if (VisitBody) { TRY_TO(TraverseStmt(D->getBody())); // Function body. } return true; @@ -2308,19 +2319,30 @@ bool RecursiveASTVisitor::TraverseSynOrSemInitListExpr( return true; } -// This method is called once for each pair of syntactic and semantic -// InitListExpr, and it traverses the subtrees defined by the two forms. This -// may cause some of the children to be visited twice, if they appear both in -// the syntactic and the semantic form. +// If shouldVisitImplicitCode() returns false, this method traverses only the +// syntactic form of InitListExpr. +// If shouldVisitImplicitCode() return true, this method is called once for +// each pair of syntactic and semantic InitListExpr, and it traverses the +// subtrees defined by the two forms. This may cause some of the children to be +// visited twice, if they appear both in the syntactic and the semantic form. // // There is no guarantee about which form \p S takes when this method is called. template bool RecursiveASTVisitor::TraverseInitListExpr( InitListExpr *S, DataRecursionQueue *Queue) { + if (S->isSemanticForm() && S->isSyntacticForm()) { + // `S` does not have alternative forms, traverse only once. + TRY_TO(TraverseSynOrSemInitListExpr(S, Queue)); + return true; + } TRY_TO(TraverseSynOrSemInitListExpr( S->isSemanticForm() ? S->getSyntacticForm() : S, Queue)); - TRY_TO(TraverseSynOrSemInitListExpr( - S->isSemanticForm() ? S : S->getSemanticForm(), Queue)); + if (getDerived().shouldVisitImplicitCode()) { + // Only visit the semantic form if the clients are interested in implicit + // compiler-generated. + TRY_TO(TraverseSynOrSemInitListExpr( + S->isSemanticForm() ? S : S->getSemanticForm(), Queue)); + } return true; } @@ -2584,6 +2606,15 @@ DEF_TRAVERSE_STMT(SEHLeaveStmt, {}) DEF_TRAVERSE_STMT(CapturedStmt, { TRY_TO(TraverseDecl(S->getCapturedDecl())); }) DEF_TRAVERSE_STMT(CXXOperatorCallExpr, {}) +DEF_TRAVERSE_STMT(CXXRewrittenBinaryOperator, { + if (!getDerived().shouldVisitImplicitCode()) { + CXXRewrittenBinaryOperator::DecomposedForm Decomposed = + S->getDecomposedForm(); + TRY_TO(TraverseStmt(const_cast(Decomposed.LHS))); + TRY_TO(TraverseStmt(const_cast(Decomposed.RHS))); + ShouldVisitChildren = false; + } +}) DEF_TRAVERSE_STMT(OpaqueValueExpr, {}) DEF_TRAVERSE_STMT(TypoExpr, {}) DEF_TRAVERSE_STMT(CUDAKernelCallExpr, {}) @@ -2639,6 +2670,12 @@ DEF_TRAVERSE_STMT(CoyieldExpr, { } }) +DEF_TRAVERSE_STMT(ConceptSpecializationExpr, { + TRY_TO(TraverseTemplateArgumentLocsHelper( + S->getTemplateArgsAsWritten()->getTemplateArgs(), + S->getTemplateArgsAsWritten()->NumTemplateArgs)); +}) + // These literals (all of them) do not need any action. DEF_TRAVERSE_STMT(IntegerLiteral, {}) DEF_TRAVERSE_STMT(FixedPointLiteral, {}) @@ -2768,6 +2805,15 @@ DEF_TRAVERSE_STMT(OMPTaskLoopDirective, DEF_TRAVERSE_STMT(OMPTaskLoopSimdDirective, { TRY_TO(TraverseOMPExecutableDirective(S)); }) +DEF_TRAVERSE_STMT(OMPMasterTaskLoopDirective, + { TRY_TO(TraverseOMPExecutableDirective(S)); }) + +DEF_TRAVERSE_STMT(OMPMasterTaskLoopSimdDirective, + { TRY_TO(TraverseOMPExecutableDirective(S)); }) + +DEF_TRAVERSE_STMT(OMPParallelMasterTaskLoopDirective, + { TRY_TO(TraverseOMPExecutableDirective(S)); }) + DEF_TRAVERSE_STMT(OMPDistributeDirective, { TRY_TO(TraverseOMPExecutableDirective(S)); }) @@ -2826,6 +2872,8 @@ bool RecursiveASTVisitor::TraverseOMPClause(OMPClause *C) { #include "clang/Basic/OpenMPKinds.def" case OMPC_threadprivate: case OMPC_uniform: + case OMPC_device_type: + case OMPC_match: case OMPC_unknown: break; } @@ -2870,6 +2918,7 @@ bool RecursiveASTVisitor::VisitOMPIfClause(OMPIfClause *C) { template bool RecursiveASTVisitor::VisitOMPFinalClause(OMPFinalClause *C) { + TRY_TO(VisitOMPClauseWithPreInit(C)); TRY_TO(TraverseStmt(C->getCondition())); return true; } @@ -3240,6 +3289,7 @@ bool RecursiveASTVisitor::VisitOMPThreadLimitClause( template bool RecursiveASTVisitor::VisitOMPPriorityClause( OMPPriorityClause *C) { + TRY_TO(VisitOMPClauseWithPreInit(C)); TRY_TO(TraverseStmt(C->getPriority())); return true; } @@ -3247,6 +3297,7 @@ bool RecursiveASTVisitor::VisitOMPPriorityClause( template bool RecursiveASTVisitor::VisitOMPGrainsizeClause( OMPGrainsizeClause *C) { + TRY_TO(VisitOMPClauseWithPreInit(C)); TRY_TO(TraverseStmt(C->getGrainsize())); return true; } @@ -3254,6 +3305,7 @@ bool RecursiveASTVisitor::VisitOMPGrainsizeClause( template bool RecursiveASTVisitor::VisitOMPNumTasksClause( OMPNumTasksClause *C) { + TRY_TO(VisitOMPClauseWithPreInit(C)); TRY_TO(TraverseStmt(C->getNumTasks())); return true; } diff --git a/include/clang/AST/Stmt.h b/include/clang/AST/Stmt.h index 403b88ac3a3c7..7aebbf2cb6a3a 100644 --- a/include/clang/AST/Stmt.h +++ b/include/clang/AST/Stmt.h @@ -604,6 +604,15 @@ protected: unsigned FPFeatures : 3; }; + class CXXRewrittenBinaryOperatorBitfields { + friend class ASTStmtReader; + friend class CXXRewrittenBinaryOperator; + + unsigned : NumCallExprBits; + + unsigned IsReversed : 1; + }; + class CXXBoolLiteralExprBitfields { friend class CXXBoolLiteralExpr; @@ -978,6 +987,7 @@ protected: // C++ Expressions CXXOperatorCallExprBitfields CXXOperatorCallExprBits; + CXXRewrittenBinaryOperatorBitfields CXXRewrittenBinaryOperatorBits; CXXBoolLiteralExprBitfields CXXBoolLiteralExprBits; CXXNullPtrLiteralExprBitfields CXXNullPtrLiteralExprBits; CXXThisExprBitfields CXXThisExprBits; diff --git a/include/clang/AST/StmtOpenMP.h b/include/clang/AST/StmtOpenMP.h index e37f5b1e00048..ddfb3060b1584 100644 --- a/include/clang/AST/StmtOpenMP.h +++ b/include/clang/AST/StmtOpenMP.h @@ -17,6 +17,7 @@ #include "clang/AST/Expr.h" #include "clang/AST/OpenMPClause.h" #include "clang/AST/Stmt.h" +#include "clang/AST/StmtCXX.h" #include "clang/Basic/OpenMPKinds.h" #include "clang/Basic/SourceLocation.h" @@ -448,7 +449,8 @@ class OMPLoopDirective : public OMPExecutableDirective { PreInitsOffset = 8, // The '...End' enumerators do not correspond to child expressions - they // specify the offset to the end (and start of the following counters/ - // updates/finals arrays). + // updates/finals/dependent_counters/dependent_inits/finals_conditions + // arrays). DefaultEnd = 9, // The following 8 exprs are used by worksharing and distribute loops only. IsLastIterVariableOffset = 9, @@ -474,7 +476,8 @@ class OMPLoopDirective : public OMPExecutableDirective { CombinedNextUpperBoundOffset = 27, CombinedDistConditionOffset = 28, CombinedParForInDistConditionOffset = 29, - // Offset to the end (and start of the following counters/updates/finals + // Offset to the end (and start of the following + // counters/updates/finals/dependent_counters/dependent_inits/finals_conditions // arrays) for combined distribute loop directives. CombinedDistributeEnd = 30, }; @@ -517,6 +520,30 @@ class OMPLoopDirective : public OMPExecutableDirective { return MutableArrayRef(Storage, CollapsedNum); } + /// Get the dependent counters storage. + MutableArrayRef getDependentCounters() { + Expr **Storage = reinterpret_cast( + &*std::next(child_begin(), + getArraysOffset(getDirectiveKind()) + 5 * CollapsedNum)); + return MutableArrayRef(Storage, CollapsedNum); + } + + /// Get the dependent inits storage. + MutableArrayRef getDependentInits() { + Expr **Storage = reinterpret_cast( + &*std::next(child_begin(), + getArraysOffset(getDirectiveKind()) + 6 * CollapsedNum)); + return MutableArrayRef(Storage, CollapsedNum); + } + + /// Get the finals conditions storage. + MutableArrayRef getFinalsConditions() { + Expr **Storage = reinterpret_cast( + &*std::next(child_begin(), + getArraysOffset(getDirectiveKind()) + 7 * CollapsedNum)); + return MutableArrayRef(Storage, CollapsedNum); + } + protected: /// Build instance of loop directive of class \a Kind. /// @@ -551,9 +578,10 @@ protected: /// Children number. static unsigned numLoopChildren(unsigned CollapsedNum, OpenMPDirectiveKind Kind) { - return getArraysOffset(Kind) + 5 * CollapsedNum; // Counters, - // PrivateCounters, Inits, - // Updates and Finals + return getArraysOffset(Kind) + + 8 * CollapsedNum; // Counters, PrivateCounters, Inits, + // Updates, Finals, DependentCounters, + // DependentInits, FinalsConditions. } void setIterationVariable(Expr *IV) { @@ -703,6 +731,9 @@ protected: void setInits(ArrayRef A); void setUpdates(ArrayRef A); void setFinals(ArrayRef A); + void setDependentCounters(ArrayRef A); + void setDependentInits(ArrayRef A); + void setFinalsConditions(ArrayRef A); public: /// The expressions built to support OpenMP loops in combined/composite @@ -798,6 +829,15 @@ public: SmallVector Updates; /// Final loop counter values for GodeGen. SmallVector Finals; + /// List of counters required for the generation of the non-rectangular + /// loops. + SmallVector DependentCounters; + /// List of initializers required for the generation of the non-rectangular + /// loops. + SmallVector DependentInits; + /// List of final conditions required for the generation of the + /// non-rectangular loops. + SmallVector FinalsConditions; /// Init statement for all captured expressions. Stmt *PreInits; @@ -813,7 +853,9 @@ public: } /// Initialize all the fields to null. - /// \param Size Number of elements in the counters/finals/updates arrays. + /// \param Size Number of elements in the + /// counters/finals/updates/dependent_counters/dependent_inits/finals_conditions + /// arrays. void clear(unsigned Size) { IterationVarRef = nullptr; LastIteration = nullptr; @@ -839,12 +881,18 @@ public: Inits.resize(Size); Updates.resize(Size); Finals.resize(Size); + DependentCounters.resize(Size); + DependentInits.resize(Size); + FinalsConditions.resize(Size); for (unsigned i = 0; i < Size; ++i) { Counters[i] = nullptr; PrivateCounters[i] = nullptr; Inits[i] = nullptr; Updates[i] = nullptr; Finals[i] = nullptr; + DependentCounters[i] = nullptr; + DependentInits[i] = nullptr; + FinalsConditions[i] = nullptr; } PreInits = nullptr; DistCombinedFields.LB = nullptr; @@ -1040,10 +1088,22 @@ public: // This relies on the loop form is already checked by Sema. const Stmt *Body = getInnermostCapturedStmt()->getCapturedStmt()->IgnoreContainers(); - Body = cast(Body)->getBody(); + if (auto *For = dyn_cast(Body)) { + Body = For->getBody(); + } else { + assert(isa(Body) && + "Expected canonical for loop or range-based for loop."); + Body = cast(Body)->getBody(); + } for (unsigned Cnt = 1; Cnt < CollapsedNum; ++Cnt) { Body = Body->IgnoreContainers(); - Body = cast(Body)->getBody(); + if (auto *For = dyn_cast(Body)) { + Body = For->getBody(); + } else { + assert(isa(Body) && + "Expected canonical for loop or range-based for loop."); + Body = cast(Body)->getBody(); + } } return Body; } @@ -1078,6 +1138,24 @@ public: return const_cast(this)->getFinals(); } + ArrayRef dependent_counters() { return getDependentCounters(); } + + ArrayRef dependent_counters() const { + return const_cast(this)->getDependentCounters(); + } + + ArrayRef dependent_inits() { return getDependentInits(); } + + ArrayRef dependent_inits() const { + return const_cast(this)->getDependentInits(); + } + + ArrayRef finals_conditions() { return getFinalsConditions(); } + + ArrayRef finals_conditions() const { + return const_cast(this)->getFinalsConditions(); + } + static bool classof(const Stmt *T) { return T->getStmtClass() == OMPSimdDirectiveClass || T->getStmtClass() == OMPForDirectiveClass || @@ -1086,6 +1164,9 @@ public: T->getStmtClass() == OMPParallelForSimdDirectiveClass || T->getStmtClass() == OMPTaskLoopDirectiveClass || T->getStmtClass() == OMPTaskLoopSimdDirectiveClass || + T->getStmtClass() == OMPMasterTaskLoopDirectiveClass || + T->getStmtClass() == OMPMasterTaskLoopSimdDirectiveClass || + T->getStmtClass() == OMPParallelMasterTaskLoopDirectiveClass || T->getStmtClass() == OMPDistributeDirectiveClass || T->getStmtClass() == OMPTargetParallelForDirectiveClass || T->getStmtClass() == OMPDistributeParallelForDirectiveClass || @@ -3041,6 +3122,211 @@ public: } }; +/// This represents '#pragma omp master taskloop' directive. +/// +/// \code +/// #pragma omp master taskloop private(a,b) grainsize(val) num_tasks(num) +/// \endcode +/// In this example directive '#pragma omp master taskloop' has clauses +/// 'private' with the variables 'a' and 'b', 'grainsize' with expression 'val' +/// and 'num_tasks' with expression 'num'. +/// +class OMPMasterTaskLoopDirective : public OMPLoopDirective { + friend class ASTStmtReader; + /// Build directive with the given start and end location. + /// + /// \param StartLoc Starting location of the directive kind. + /// \param EndLoc Ending location of the directive. + /// \param CollapsedNum Number of collapsed nested loops. + /// \param NumClauses Number of clauses. + /// + OMPMasterTaskLoopDirective(SourceLocation StartLoc, SourceLocation EndLoc, + unsigned CollapsedNum, unsigned NumClauses) + : OMPLoopDirective(this, OMPMasterTaskLoopDirectiveClass, + OMPD_master_taskloop, StartLoc, EndLoc, CollapsedNum, + NumClauses) {} + + /// Build an empty directive. + /// + /// \param CollapsedNum Number of collapsed nested loops. + /// \param NumClauses Number of clauses. + /// + explicit OMPMasterTaskLoopDirective(unsigned CollapsedNum, + unsigned NumClauses) + : OMPLoopDirective(this, OMPMasterTaskLoopDirectiveClass, + OMPD_master_taskloop, SourceLocation(), + SourceLocation(), CollapsedNum, NumClauses) {} + +public: + /// Creates directive with a list of \a Clauses. + /// + /// \param C AST context. + /// \param StartLoc Starting location of the directive kind. + /// \param EndLoc Ending Location of the directive. + /// \param CollapsedNum Number of collapsed loops. + /// \param Clauses List of clauses. + /// \param AssociatedStmt Statement, associated with the directive. + /// \param Exprs Helper expressions for CodeGen. + /// + static OMPMasterTaskLoopDirective * + Create(const ASTContext &C, SourceLocation StartLoc, SourceLocation EndLoc, + unsigned CollapsedNum, ArrayRef Clauses, + Stmt *AssociatedStmt, const HelperExprs &Exprs); + + /// Creates an empty directive with the place + /// for \a NumClauses clauses. + /// + /// \param C AST context. + /// \param CollapsedNum Number of collapsed nested loops. + /// \param NumClauses Number of clauses. + /// + static OMPMasterTaskLoopDirective *CreateEmpty(const ASTContext &C, + unsigned NumClauses, + unsigned CollapsedNum, + EmptyShell); + + static bool classof(const Stmt *T) { + return T->getStmtClass() == OMPMasterTaskLoopDirectiveClass; + } +}; + +/// This represents '#pragma omp master taskloop simd' directive. +/// +/// \code +/// #pragma omp master taskloop simd private(a,b) grainsize(val) num_tasks(num) +/// \endcode +/// In this example directive '#pragma omp master taskloop simd' has clauses +/// 'private' with the variables 'a' and 'b', 'grainsize' with expression 'val' +/// and 'num_tasks' with expression 'num'. +/// +class OMPMasterTaskLoopSimdDirective : public OMPLoopDirective { + friend class ASTStmtReader; + /// Build directive with the given start and end location. + /// + /// \param StartLoc Starting location of the directive kind. + /// \param EndLoc Ending location of the directive. + /// \param CollapsedNum Number of collapsed nested loops. + /// \param NumClauses Number of clauses. + /// + OMPMasterTaskLoopSimdDirective(SourceLocation StartLoc, SourceLocation EndLoc, + unsigned CollapsedNum, unsigned NumClauses) + : OMPLoopDirective(this, OMPMasterTaskLoopSimdDirectiveClass, + OMPD_master_taskloop_simd, StartLoc, EndLoc, + CollapsedNum, NumClauses) {} + + /// Build an empty directive. + /// + /// \param CollapsedNum Number of collapsed nested loops. + /// \param NumClauses Number of clauses. + /// + explicit OMPMasterTaskLoopSimdDirective(unsigned CollapsedNum, + unsigned NumClauses) + : OMPLoopDirective(this, OMPMasterTaskLoopSimdDirectiveClass, + OMPD_master_taskloop_simd, SourceLocation(), + SourceLocation(), CollapsedNum, NumClauses) {} + +public: + /// Creates directive with a list of \p Clauses. + /// + /// \param C AST context. + /// \param StartLoc Starting location of the directive kind. + /// \param EndLoc Ending Location of the directive. + /// \param CollapsedNum Number of collapsed loops. + /// \param Clauses List of clauses. + /// \param AssociatedStmt Statement, associated with the directive. + /// \param Exprs Helper expressions for CodeGen. + /// + static OMPMasterTaskLoopSimdDirective * + Create(const ASTContext &C, SourceLocation StartLoc, SourceLocation EndLoc, + unsigned CollapsedNum, ArrayRef Clauses, + Stmt *AssociatedStmt, const HelperExprs &Exprs); + + /// Creates an empty directive with the place for \p NumClauses clauses. + /// + /// \param C AST context. + /// \param CollapsedNum Number of collapsed nested loops. + /// \param NumClauses Number of clauses. + /// + static OMPMasterTaskLoopSimdDirective *CreateEmpty(const ASTContext &C, + unsigned NumClauses, + unsigned CollapsedNum, + EmptyShell); + + static bool classof(const Stmt *T) { + return T->getStmtClass() == OMPMasterTaskLoopSimdDirectiveClass; + } +}; + +/// This represents '#pragma omp parallel master taskloop' directive. +/// +/// \code +/// #pragma omp parallel master taskloop private(a,b) grainsize(val) +/// num_tasks(num) +/// \endcode +/// In this example directive '#pragma omp parallel master taskloop' has clauses +/// 'private' with the variables 'a' and 'b', 'grainsize' with expression 'val' +/// and 'num_tasks' with expression 'num'. +/// +class OMPParallelMasterTaskLoopDirective : public OMPLoopDirective { + friend class ASTStmtReader; + /// Build directive with the given start and end location. + /// + /// \param StartLoc Starting location of the directive kind. + /// \param EndLoc Ending location of the directive. + /// \param CollapsedNum Number of collapsed nested loops. + /// \param NumClauses Number of clauses. + /// + OMPParallelMasterTaskLoopDirective(SourceLocation StartLoc, + SourceLocation EndLoc, + unsigned CollapsedNum, unsigned NumClauses) + : OMPLoopDirective(this, OMPParallelMasterTaskLoopDirectiveClass, + OMPD_parallel_master_taskloop, StartLoc, EndLoc, + CollapsedNum, NumClauses) {} + + /// Build an empty directive. + /// + /// \param CollapsedNum Number of collapsed nested loops. + /// \param NumClauses Number of clauses. + /// + explicit OMPParallelMasterTaskLoopDirective(unsigned CollapsedNum, + unsigned NumClauses) + : OMPLoopDirective(this, OMPParallelMasterTaskLoopDirectiveClass, + OMPD_parallel_master_taskloop, SourceLocation(), + SourceLocation(), CollapsedNum, NumClauses) {} + +public: + /// Creates directive with a list of \a Clauses. + /// + /// \param C AST context. + /// \param StartLoc Starting location of the directive kind. + /// \param EndLoc Ending Location of the directive. + /// \param CollapsedNum Number of collapsed loops. + /// \param Clauses List of clauses. + /// \param AssociatedStmt Statement, associated with the directive. + /// \param Exprs Helper expressions for CodeGen. + /// + static OMPParallelMasterTaskLoopDirective * + Create(const ASTContext &C, SourceLocation StartLoc, SourceLocation EndLoc, + unsigned CollapsedNum, ArrayRef Clauses, + Stmt *AssociatedStmt, const HelperExprs &Exprs); + + /// Creates an empty directive with the place + /// for \a NumClauses clauses. + /// + /// \param C AST context. + /// \param CollapsedNum Number of collapsed nested loops. + /// \param NumClauses Number of clauses. + /// + static OMPParallelMasterTaskLoopDirective *CreateEmpty(const ASTContext &C, + unsigned NumClauses, + unsigned CollapsedNum, + EmptyShell); + + static bool classof(const Stmt *T) { + return T->getStmtClass() == OMPParallelMasterTaskLoopDirectiveClass; + } +}; + /// This represents '#pragma omp distribute' directive. /// /// \code diff --git a/include/clang/AST/TextNodeDumper.h b/include/clang/AST/TextNodeDumper.h index 4c2d0710963b2..0ff5a614a864d 100644 --- a/include/clang/AST/TextNodeDumper.h +++ b/include/clang/AST/TextNodeDumper.h @@ -146,8 +146,6 @@ class TextNodeDumper const comments::CommandTraits *Traits; - const ASTContext *Context; - const char *getCommandName(unsigned CommandID); public: diff --git a/include/clang/AST/Type.h b/include/clang/AST/Type.h index 584655fe789e6..c9238e9521012 100644 --- a/include/clang/AST/Type.h +++ b/include/clang/AST/Type.h @@ -126,7 +126,7 @@ using CanQualType = CanQual; // Provide forward declarations for all of the *Type classes. #define TYPE(Class, Base) class Class##Type; -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" /// The collection of all-type qualifiers we support. /// Clang supports five independent qualifiers: @@ -972,6 +972,9 @@ public: friend bool operator!=(const QualType &LHS, const QualType &RHS) { return LHS.Value != RHS.Value; } + friend bool operator<(const QualType &LHS, const QualType &RHS) { + return LHS.Value < RHS.Value; + } static std::string getAsString(SplitQualType split, const PrintingPolicy &Policy) { @@ -1434,10 +1437,9 @@ class alignas(8) Type : public ExtQualsTypeCommonBase { public: enum TypeClass { #define TYPE(Class, Base) Class, -#define LAST_TYPE(Class) TypeLast = Class, +#define LAST_TYPE(Class) TypeLast = Class #define ABSTRACT_TYPE(Class, Base) -#include "clang/AST/TypeNodes.def" - TagFirst = Record, TagLast = Enum +#include "clang/AST/TypeNodes.inc" }; private: @@ -1511,6 +1513,15 @@ protected: unsigned SizeModifier : 3; }; + class ConstantArrayTypeBitfields { + friend class ConstantArrayType; + + unsigned : NumTypeBits + 3 + 3; + + /// Whether we have a stored size expression. + unsigned HasStoredSizeExpr : 1; + }; + class BuiltinTypeBitfields { friend class BuiltinType; @@ -1732,6 +1743,7 @@ protected: union { TypeBitfields TypeBits; ArrayTypeBitfields ArrayTypeBits; + ConstantArrayTypeBitfields ConstantArrayTypeBits; AttributedTypeBitfields AttributedTypeBits; AutoTypeBitfields AutoTypeBits; BuiltinTypeBitfields BuiltinTypeBits; @@ -2053,6 +2065,7 @@ public: bool isCARCBridgableType() const; bool isTemplateTypeParmType() const; // C++ template type parameter bool isNullPtrType() const; // C++11 std::nullptr_t + bool isNothrowT() const; // C++ std::nothrow_t bool isAlignValT() const; // C++17 std::align_val_t bool isStdByteType() const; // C++17 std::byte bool isAtomicType() const; // C11 _Atomic() @@ -2416,7 +2429,7 @@ template <> inline const Class##Type *Type::getAs() const { \ template <> inline const Class##Type *Type::castAs() const { \ return cast(CanonicalType); \ } -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" /// This class is used for builtin types like 'int'. Builtin /// types are always canonical and have a literal name field. @@ -2429,6 +2442,9 @@ public: // OpenCL extension types #define EXT_OPAQUE_TYPE(ExtType, Id, Ext) Id, #include "clang/Basic/OpenCLExtensionTypes.def" +// SVE Types +#define SVE_TYPE(Name, Id, SingletonId) Id, +#include "clang/Basic/AArch64SVEACLETypes.def" // All other builtin types #define BUILTIN_TYPE(Id, SingletonId) Id, #define LAST_BUILTIN_TYPE(Id) LastKind = Id @@ -2858,22 +2874,8 @@ private: protected: friend class ASTContext; // ASTContext creates these. - // C++ [temp.dep.type]p1: - // A type is dependent if it is... - // - an array type constructed from any dependent type or whose - // size is specified by a constant expression that is - // value-dependent, - ArrayType(TypeClass tc, QualType et, QualType can, - ArraySizeModifier sm, unsigned tq, - bool ContainsUnexpandedParameterPack) - : Type(tc, can, et->isDependentType() || tc == DependentSizedArray, - et->isInstantiationDependentType() || tc == DependentSizedArray, - (tc == VariableArray || et->isVariablyModifiedType()), - ContainsUnexpandedParameterPack), - ElementType(et) { - ArrayTypeBits.IndexTypeQuals = tq; - ArrayTypeBits.SizeModifier = sm; - } + ArrayType(TypeClass tc, QualType et, QualType can, ArraySizeModifier sm, + unsigned tq, const Expr *sz = nullptr); public: QualType getElementType() const { return ElementType; } @@ -2901,25 +2903,35 @@ public: /// Represents the canonical version of C arrays with a specified constant size. /// For example, the canonical type for 'int A[4 + 4*100]' is a /// ConstantArrayType where the element type is 'int' and the size is 404. -class ConstantArrayType : public ArrayType { +class ConstantArrayType final + : public ArrayType, + private llvm::TrailingObjects { + friend class ASTContext; // ASTContext creates these. + friend TrailingObjects; + llvm::APInt Size; // Allows us to unique the type. ConstantArrayType(QualType et, QualType can, const llvm::APInt &size, - ArraySizeModifier sm, unsigned tq) - : ArrayType(ConstantArray, et, can, sm, tq, - et->containsUnexpandedParameterPack()), - Size(size) {} - -protected: - friend class ASTContext; // ASTContext creates these. + const Expr *sz, ArraySizeModifier sm, unsigned tq) + : ArrayType(ConstantArray, et, can, sm, tq, sz), Size(size) { + ConstantArrayTypeBits.HasStoredSizeExpr = sz != nullptr; + if (ConstantArrayTypeBits.HasStoredSizeExpr) { + assert(!can.isNull() && "canonical constant array should not have size"); + *getTrailingObjects() = sz; + } + } - ConstantArrayType(TypeClass tc, QualType et, QualType can, - const llvm::APInt &size, ArraySizeModifier sm, unsigned tq) - : ArrayType(tc, et, can, sm, tq, et->containsUnexpandedParameterPack()), - Size(size) {} + unsigned numTrailingObjects(OverloadToken) const { + return ConstantArrayTypeBits.HasStoredSizeExpr; + } public: const llvm::APInt &getSize() const { return Size; } + const Expr *getSizeExpr() const { + return ConstantArrayTypeBits.HasStoredSizeExpr + ? *getTrailingObjects() + : nullptr; + } bool isSugared() const { return false; } QualType desugar() const { return QualType(this, 0); } @@ -2933,19 +2945,15 @@ public: /// can require, which limits the maximum size of the array. static unsigned getMaxSizeBits(const ASTContext &Context); - void Profile(llvm::FoldingSetNodeID &ID) { - Profile(ID, getElementType(), getSize(), + void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Ctx) { + Profile(ID, Ctx, getElementType(), getSize(), getSizeExpr(), getSizeModifier(), getIndexTypeCVRQualifiers()); } - static void Profile(llvm::FoldingSetNodeID &ID, QualType ET, - const llvm::APInt &ArraySize, ArraySizeModifier SizeMod, - unsigned TypeQuals) { - ID.AddPointer(ET.getAsOpaquePtr()); - ID.AddInteger(ArraySize.getZExtValue()); - ID.AddInteger(SizeMod); - ID.AddInteger(TypeQuals); - } + static void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Ctx, + QualType ET, const llvm::APInt &ArraySize, + const Expr *SizeExpr, ArraySizeModifier SizeMod, + unsigned TypeQuals); static bool classof(const Type *T) { return T->getTypeClass() == ConstantArray; @@ -2960,8 +2968,7 @@ class IncompleteArrayType : public ArrayType { IncompleteArrayType(QualType et, QualType can, ArraySizeModifier sm, unsigned tq) - : ArrayType(IncompleteArray, et, can, sm, tq, - et->containsUnexpandedParameterPack()) {} + : ArrayType(IncompleteArray, et, can, sm, tq) {} public: friend class StmtIteratorBase; @@ -3013,8 +3020,7 @@ class VariableArrayType : public ArrayType { VariableArrayType(QualType et, QualType can, Expr *e, ArraySizeModifier sm, unsigned tq, SourceRange brackets) - : ArrayType(VariableArray, et, can, sm, tq, - et->containsUnexpandedParameterPack()), + : ArrayType(VariableArray, et, can, sm, tq, e), SizeExpr((Stmt*) e), Brackets(brackets) {} public: @@ -4429,7 +4435,7 @@ public: bool isBeingDefined() const; static bool classof(const Type *T) { - return T->getTypeClass() >= TagFirst && T->getTypeClass() <= TagLast; + return T->getTypeClass() == Enum || T->getTypeClass() == Record; } }; @@ -5563,7 +5569,7 @@ class ObjCTypeParamType : public Type, public: bool isSugared() const { return true; } - QualType desugar() const { return getCanonicalTypeInternal(); } + QualType desugar() const; static bool classof(const Type *T) { return T->getTypeClass() == ObjCTypeParam; @@ -6347,6 +6353,7 @@ inline bool QualType::isCForbiddenLValueType() const { /// \returns True for types specified in C++0x [basic.fundamental]. inline bool Type::isFundamentalType() const { return isVoidType() || + isNullPtrType() || // FIXME: It's really annoying that we don't have an // 'isArithmeticType()' which agrees with the standard definition. (isArithmeticType() && !isEnumeralType()); diff --git a/include/clang/AST/TypeLoc.h b/include/clang/AST/TypeLoc.h index 40d17f991f1fb..f305680d775cf 100644 --- a/include/clang/AST/TypeLoc.h +++ b/include/clang/AST/TypeLoc.h @@ -106,7 +106,7 @@ public: #define ABSTRACT_TYPE(Class, Base) #define TYPE(Class, Base) \ Class = Type::Class, -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" Qualified }; diff --git a/include/clang/AST/TypeLocNodes.def b/include/clang/AST/TypeLocNodes.def index c0dfe150d6cc0..81448c7e7ce5b 100644 --- a/include/clang/AST/TypeLocNodes.def +++ b/include/clang/AST/TypeLocNodes.def @@ -31,7 +31,7 @@ TYPELOC(Qualified, TypeLoc) #define TYPE(Class, Base) UNQUAL_TYPELOC(Class, Base##Loc) #define ABSTRACT_TYPE(Class, Base) ABSTRACT_TYPELOC(Class, Base##Loc) -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" #undef DECLARATOR_TYPELOC #undef TYPESPEC_TYPELOC diff --git a/include/clang/AST/TypeNodes.def b/include/clang/AST/TypeNodes.def deleted file mode 100644 index 58a5f880cbe61..0000000000000 --- a/include/clang/AST/TypeNodes.def +++ /dev/null @@ -1,135 +0,0 @@ -//===-- TypeNodes.def - Metadata about Type AST nodes -----------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// This file defines the AST type info database. Each type node is -// enumerated by providing its name (e.g., "Builtin" or "Enum") and -// base class (e.g., "Type" or "TagType"). Depending on where in the -// abstract syntax tree the type will show up, the enumeration uses -// one of five different macros: -// -// TYPE(Class, Base) - A type that can show up anywhere in the AST, -// and might be dependent, canonical, or non-canonical. All clients -// will need to understand these types. -// -// ABSTRACT_TYPE(Class, Base) - An abstract class that shows up in -// the type hierarchy but has no concrete instances. -// -// NON_CANONICAL_TYPE(Class, Base) - A type that can show up -// anywhere in the AST but will never be a part of a canonical -// type. Clients that only need to deal with canonical types -// (ignoring, e.g., typedefs and other type aliases used for -// pretty-printing) can ignore these types. -// -// DEPENDENT_TYPE(Class, Base) - A type that will only show up -// within a C++ template that has not been instantiated, e.g., a -// type that is always dependent. Clients that do not need to deal -// with uninstantiated C++ templates can ignore these types. -// -// NON_CANONICAL_UNLESS_DEPENDENT_TYPE(Class, Base) - A type that -// is non-canonical unless it is dependent. Defaults to TYPE because -// it is neither reliably dependent nor reliably non-canonical. -// -// There is a sixth macro, independent of the others. Most clients -// will not need to use it. -// -// LEAF_TYPE(Class) - A type that never has inner types. Clients -// which can operate on such types more efficiently may wish to do so. -// -//===----------------------------------------------------------------------===// - -#ifndef ABSTRACT_TYPE -# define ABSTRACT_TYPE(Class, Base) TYPE(Class, Base) -#endif - -#ifndef NON_CANONICAL_TYPE -# define NON_CANONICAL_TYPE(Class, Base) TYPE(Class, Base) -#endif - -#ifndef DEPENDENT_TYPE -# define DEPENDENT_TYPE(Class, Base) TYPE(Class, Base) -#endif - -#ifndef NON_CANONICAL_UNLESS_DEPENDENT_TYPE -# define NON_CANONICAL_UNLESS_DEPENDENT_TYPE(Class, Base) TYPE(Class, Base) -#endif - -TYPE(Builtin, Type) -TYPE(Complex, Type) -TYPE(Pointer, Type) -TYPE(BlockPointer, Type) -ABSTRACT_TYPE(Reference, Type) -TYPE(LValueReference, ReferenceType) -TYPE(RValueReference, ReferenceType) -TYPE(MemberPointer, Type) -ABSTRACT_TYPE(Array, Type) -TYPE(ConstantArray, ArrayType) -TYPE(IncompleteArray, ArrayType) -TYPE(VariableArray, ArrayType) -DEPENDENT_TYPE(DependentSizedArray, ArrayType) -DEPENDENT_TYPE(DependentSizedExtVector, Type) -DEPENDENT_TYPE(DependentAddressSpace, Type) -TYPE(Vector, Type) -DEPENDENT_TYPE(DependentVector, Type) -TYPE(ExtVector, VectorType) -ABSTRACT_TYPE(Function, Type) -TYPE(FunctionProto, FunctionType) -TYPE(FunctionNoProto, FunctionType) -DEPENDENT_TYPE(UnresolvedUsing, Type) -NON_CANONICAL_TYPE(Paren, Type) -NON_CANONICAL_TYPE(Typedef, Type) -NON_CANONICAL_TYPE(MacroQualified, Type) -NON_CANONICAL_TYPE(Adjusted, Type) -NON_CANONICAL_TYPE(Decayed, AdjustedType) -NON_CANONICAL_UNLESS_DEPENDENT_TYPE(TypeOfExpr, Type) -NON_CANONICAL_UNLESS_DEPENDENT_TYPE(TypeOf, Type) -NON_CANONICAL_UNLESS_DEPENDENT_TYPE(Decltype, Type) -NON_CANONICAL_UNLESS_DEPENDENT_TYPE(UnaryTransform, Type) -ABSTRACT_TYPE(Tag, Type) -TYPE(Record, TagType) -TYPE(Enum, TagType) -NON_CANONICAL_TYPE(Elaborated, Type) -NON_CANONICAL_TYPE(Attributed, Type) -DEPENDENT_TYPE(TemplateTypeParm, Type) -NON_CANONICAL_TYPE(SubstTemplateTypeParm, Type) -DEPENDENT_TYPE(SubstTemplateTypeParmPack, Type) -NON_CANONICAL_UNLESS_DEPENDENT_TYPE(TemplateSpecialization, Type) -ABSTRACT_TYPE(Deduced, Type) -TYPE(Auto, DeducedType) -TYPE(DeducedTemplateSpecialization, DeducedType) -DEPENDENT_TYPE(InjectedClassName, Type) -DEPENDENT_TYPE(DependentName, Type) -DEPENDENT_TYPE(DependentTemplateSpecialization, Type) -NON_CANONICAL_UNLESS_DEPENDENT_TYPE(PackExpansion, Type) -NON_CANONICAL_TYPE(ObjCTypeParam, Type) -TYPE(ObjCObject, Type) -TYPE(ObjCInterface, ObjCObjectType) -TYPE(ObjCObjectPointer, Type) -TYPE(Pipe, Type) -TYPE(Atomic, Type) - -#ifdef LAST_TYPE -LAST_TYPE(Atomic) -#undef LAST_TYPE -#endif - -// These types are always leaves in the type hierarchy. -#ifdef LEAF_TYPE -LEAF_TYPE(Enum) -LEAF_TYPE(Builtin) -LEAF_TYPE(Record) -LEAF_TYPE(InjectedClassName) -LEAF_TYPE(ObjCInterface) -LEAF_TYPE(TemplateTypeParm) -#undef LEAF_TYPE -#endif - -#undef NON_CANONICAL_UNLESS_DEPENDENT_TYPE -#undef DEPENDENT_TYPE -#undef NON_CANONICAL_TYPE -#undef ABSTRACT_TYPE -#undef TYPE diff --git a/include/clang/AST/TypeVisitor.h b/include/clang/AST/TypeVisitor.h index 8930ec8539497..17301835fb187 100644 --- a/include/clang/AST/TypeVisitor.h +++ b/include/clang/AST/TypeVisitor.h @@ -70,7 +70,7 @@ public: switch (T->getTypeClass()) { #define ABSTRACT_TYPE(CLASS, PARENT) #define TYPE(CLASS, PARENT) case Type::CLASS: DISPATCH(CLASS##Type); -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" } llvm_unreachable("Unknown type class!"); } @@ -80,7 +80,7 @@ public: #define TYPE(CLASS, PARENT) RetTy Visit##CLASS##Type(const CLASS##Type *T) { \ DISPATCH(PARENT); \ } -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" /// Method called if \c ImpClass doesn't provide specific handler /// for some type class. diff --git a/include/clang/ASTMatchers/ASTMatchers.h b/include/clang/ASTMatchers/ASTMatchers.h index 063d8217d9aa5..e34b31cbda880 100644 --- a/include/clang/ASTMatchers/ASTMatchers.h +++ b/include/clang/ASTMatchers/ASTMatchers.h @@ -19,15 +19,15 @@ // // For more complicated match expressions we're often interested in accessing // multiple parts of the matched AST nodes once a match is found. In that case, -// use the id(...) matcher around the match expressions that match the nodes -// you want to access. +// call `.bind("name")` on match expressions that match the nodes you want to +// access. // // For example, when we're interested in child classes of a certain class, we // would write: -// cxxRecordDecl(hasName("MyClass"), has(id("child", recordDecl()))) +// cxxRecordDecl(hasName("MyClass"), has(recordDecl().bind("child"))) // When the match is found via the MatchFinder, a user provided callback will // be called with a BoundNodes instance that contains a mapping from the -// strings that we provided for the id(...) calls to the nodes that were +// strings that we provided for the `.bind()` calls to the nodes that were // matched. // In the given example, each time our matcher finds a match we get a callback // where "child" is bound to the RecordDecl node of the matching child @@ -131,15 +131,6 @@ private: internal::BoundNodesMap MyBoundNodes; }; -/// If the provided matcher matches a node, binds the node to \c ID. -/// -/// FIXME: Do we want to support this now that we have bind()? -template -internal::Matcher id(StringRef ID, - const internal::BindableMatcher &InnerMatcher) { - return InnerMatcher.bind(ID); -} - /// Types of matchers for the top-level classes in the AST class /// hierarchy. /// @{ @@ -2611,8 +2602,9 @@ hasOverloadedOperatorName(StringRef Name) { AST_POLYMORPHIC_SUPPORTED_TYPES(CXXOperatorCallExpr, FunctionDecl)>(Name); } -/// Matches C++ classes that are directly or indirectly derived from -/// a class matching \c Base. +/// Matches C++ classes that are directly or indirectly derived from a class +/// matching \c Base, or Objective-C classes that directly or indirectly +/// subclass a class matching \c Base. /// /// Note that a class is not considered to be derived from itself. /// @@ -2632,33 +2624,128 @@ hasOverloadedOperatorName(StringRef Name) { /// typedef Foo X; /// class Bar : public Foo {}; // derived from a type that X is a typedef of /// \endcode -AST_MATCHER_P(CXXRecordDecl, isDerivedFrom, - internal::Matcher, Base) { - return Finder->classIsDerivedFrom(&Node, Base, Builder); +/// +/// In the following example, Bar matches isDerivedFrom(hasName("NSObject")) +/// \code +/// @interface NSObject @end +/// @interface Bar : NSObject @end +/// \endcode +/// +/// Usable as: Matcher, Matcher +AST_POLYMORPHIC_MATCHER_P( + isDerivedFrom, + AST_POLYMORPHIC_SUPPORTED_TYPES(CXXRecordDecl, ObjCInterfaceDecl), + internal::Matcher, Base) { + // Check if the node is a C++ struct/union/class. + if (const auto *RD = dyn_cast(&Node)) + return Finder->classIsDerivedFrom(RD, Base, Builder, /*Directly=*/false); + + // The node must be an Objective-C class. + const auto *InterfaceDecl = cast(&Node); + return Finder->objcClassIsDerivedFrom(InterfaceDecl, Base, Builder, + /*Directly=*/false); } /// Overloaded method as shortcut for \c isDerivedFrom(hasName(...)). -AST_MATCHER_P_OVERLOAD(CXXRecordDecl, isDerivedFrom, std::string, BaseName, 1) { - assert(!BaseName.empty()); - return isDerivedFrom(hasName(BaseName)).matches(Node, Finder, Builder); +AST_POLYMORPHIC_MATCHER_P_OVERLOAD( + isDerivedFrom, + AST_POLYMORPHIC_SUPPORTED_TYPES(CXXRecordDecl, ObjCInterfaceDecl), + std::string, BaseName, 1) { + if (BaseName.empty()) + return false; + + const auto M = isDerivedFrom(hasName(BaseName)); + + if (const auto *RD = dyn_cast(&Node)) + return Matcher(M).matches(*RD, Finder, Builder); + + const auto *InterfaceDecl = cast(&Node); + return Matcher(M).matches(*InterfaceDecl, Finder, Builder); } /// Similar to \c isDerivedFrom(), but also matches classes that directly /// match \c Base. -AST_MATCHER_P_OVERLOAD(CXXRecordDecl, isSameOrDerivedFrom, - internal::Matcher, Base, 0) { - return Matcher(anyOf(Base, isDerivedFrom(Base))) - .matches(Node, Finder, Builder); +AST_POLYMORPHIC_MATCHER_P_OVERLOAD( + isSameOrDerivedFrom, + AST_POLYMORPHIC_SUPPORTED_TYPES(CXXRecordDecl, ObjCInterfaceDecl), + internal::Matcher, Base, 0) { + const auto M = anyOf(Base, isDerivedFrom(Base)); + + if (const auto *RD = dyn_cast(&Node)) + return Matcher(M).matches(*RD, Finder, Builder); + + const auto *InterfaceDecl = cast(&Node); + return Matcher(M).matches(*InterfaceDecl, Finder, Builder); } /// Overloaded method as shortcut for /// \c isSameOrDerivedFrom(hasName(...)). -AST_MATCHER_P_OVERLOAD(CXXRecordDecl, isSameOrDerivedFrom, std::string, - BaseName, 1) { - assert(!BaseName.empty()); - return isSameOrDerivedFrom(hasName(BaseName)).matches(Node, Finder, Builder); +AST_POLYMORPHIC_MATCHER_P_OVERLOAD( + isSameOrDerivedFrom, + AST_POLYMORPHIC_SUPPORTED_TYPES(CXXRecordDecl, ObjCInterfaceDecl), + std::string, BaseName, 1) { + if (BaseName.empty()) + return false; + + const auto M = isSameOrDerivedFrom(hasName(BaseName)); + + if (const auto *RD = dyn_cast(&Node)) + return Matcher(M).matches(*RD, Finder, Builder); + + const auto *InterfaceDecl = cast(&Node); + return Matcher(M).matches(*InterfaceDecl, Finder, Builder); } +/// Matches C++ or Objective-C classes that are directly derived from a class +/// matching \c Base. +/// +/// Note that a class is not considered to be derived from itself. +/// +/// Example matches Y, C (Base == hasName("X")) +/// \code +/// class X; +/// class Y : public X {}; // directly derived +/// class Z : public Y {}; // indirectly derived +/// typedef X A; +/// typedef A B; +/// class C : public B {}; // derived from a typedef of X +/// \endcode +/// +/// In the following example, Bar matches isDerivedFrom(hasName("X")): +/// \code +/// class Foo; +/// typedef Foo X; +/// class Bar : public Foo {}; // derived from a type that X is a typedef of +/// \endcode +AST_POLYMORPHIC_MATCHER_P_OVERLOAD( + isDirectlyDerivedFrom, + AST_POLYMORPHIC_SUPPORTED_TYPES(CXXRecordDecl, ObjCInterfaceDecl), + internal::Matcher, Base, 0) { + // Check if the node is a C++ struct/union/class. + if (const auto *RD = dyn_cast(&Node)) + return Finder->classIsDerivedFrom(RD, Base, Builder, /*Directly=*/true); + + // The node must be an Objective-C class. + const auto *InterfaceDecl = cast(&Node); + return Finder->objcClassIsDerivedFrom(InterfaceDecl, Base, Builder, + /*Directly=*/true); +} + +/// Overloaded method as shortcut for \c isDirectlyDerivedFrom(hasName(...)). +AST_POLYMORPHIC_MATCHER_P_OVERLOAD( + isDirectlyDerivedFrom, + AST_POLYMORPHIC_SUPPORTED_TYPES(CXXRecordDecl, ObjCInterfaceDecl), + std::string, BaseName, 1) { + if (BaseName.empty()) + return false; + const auto M = isDirectlyDerivedFrom(hasName(BaseName)); + + if (const auto *RD = dyn_cast(&Node)) + return Matcher(M).matches(*RD, Finder, Builder); + + const auto *InterfaceDecl = cast(&Node); + return Matcher(M).matches(*InterfaceDecl, Finder, Builder); +} /// Matches the first method of a class or struct that satisfies \c /// InnerMatcher. /// @@ -6358,10 +6445,9 @@ extern const internal::VariadicDynCastAllOfMatcher /// expr(nullPointerConstant()) /// matches the initializer for v1, v2, v3, cp, and ip. Does not match the /// initializer for i. -AST_MATCHER_FUNCTION(internal::Matcher, nullPointerConstant) { - return anyOf( - gnuNullExpr(), cxxNullPtrLiteralExpr(), - integerLiteral(equals(0), hasParent(expr(hasType(pointerType()))))); +AST_MATCHER(Expr, nullPointerConstant) { + return Node.isNullPointerConstant(Finder->getASTContext(), + Expr::NPC_ValueDependentIsNull); } /// Matches declaration of the function the statement belongs to @@ -6375,7 +6461,7 @@ AST_MATCHER_FUNCTION(internal::Matcher, nullPointerConstant) { /// \endcode /// returnStmt(forFunction(hasName("operator="))) /// matches 'return *this' -/// but does match 'return > 0' +/// but does not match 'return v > 0' AST_MATCHER_P(Stmt, forFunction, internal::Matcher, InnerMatcher) { const auto &Parents = Finder->getASTContext().getParents(Node); @@ -6498,14 +6584,15 @@ AST_MATCHER(FunctionDecl, hasTrailingReturn) { } /// Matches expressions that match InnerMatcher that are possibly wrapped in an -/// elidable constructor. +/// elidable constructor and other corresponding bookkeeping nodes. /// -/// In C++17 copy elidable constructors are no longer being -/// generated in the AST as it is not permitted by the standard. They are -/// however part of the AST in C++14 and earlier. Therefore, to write a matcher -/// that works in all language modes, the matcher has to skip elidable -/// constructor AST nodes if they appear in the AST. This matcher can be used to -/// skip those elidable constructors. +/// In C++17, elidable copy constructors are no longer being generated in the +/// AST as it is not permitted by the standard. They are, however, part of the +/// AST in C++14 and earlier. So, a matcher must abstract over these differences +/// to work in all language modes. This matcher skips elidable constructor-call +/// AST nodes, `ExprWithCleanups` nodes wrapping elidable constructor-calls and +/// various implicit nodes inside the constructor calls, all of which will not +/// appear in the C++17 AST. /// /// Given /// @@ -6517,13 +6604,20 @@ AST_MATCHER(FunctionDecl, hasTrailingReturn) { /// } /// \endcode /// -/// ``varDecl(hasInitializer(any( -/// ignoringElidableConstructorCall(callExpr()), -/// exprWithCleanups(ignoringElidableConstructorCall(callExpr()))))`` -/// matches ``H D = G()`` +/// ``varDecl(hasInitializer(ignoringElidableConstructorCall(callExpr())))`` +/// matches ``H D = G()`` in C++11 through C++17 (and beyond). AST_MATCHER_P(Expr, ignoringElidableConstructorCall, ast_matchers::internal::Matcher, InnerMatcher) { - if (const auto *CtorExpr = dyn_cast(&Node)) { + // E tracks the node that we are examining. + const Expr *E = &Node; + // If present, remove an outer `ExprWithCleanups` corresponding to the + // underlying `CXXConstructExpr`. This check won't cover all cases of added + // `ExprWithCleanups` corresponding to `CXXConstructExpr` nodes (because the + // EWC is placed on the outermost node of the expression, which this may not + // be), but, it still improves the coverage of this matcher. + if (const auto *CleanupsExpr = dyn_cast(&Node)) + E = CleanupsExpr->getSubExpr(); + if (const auto *CtorExpr = dyn_cast(E)) { if (CtorExpr->isElidable()) { if (const auto *MaterializeTemp = dyn_cast(CtorExpr->getArg(0))) { diff --git a/include/clang/ASTMatchers/ASTMatchersInternal.h b/include/clang/ASTMatchers/ASTMatchersInternal.h index b1bb0bfa32183..e9fa920b6bcef 100644 --- a/include/clang/ASTMatchers/ASTMatchersInternal.h +++ b/include/clang/ASTMatchers/ASTMatchersInternal.h @@ -183,7 +183,8 @@ public: /// Note that we're using std::map here, as for memoization: /// - we need a comparison operator /// - we need an assignment operator - using IDToNodeMap = std::map; + using IDToNodeMap = + std::map>; const IDToNodeMap &getMap() const { return NodeMap; @@ -971,13 +972,23 @@ public: virtual ~ASTMatchFinder() = default; - /// Returns true if the given class is directly or indirectly derived + /// Returns true if the given C++ class is directly or indirectly derived /// from a base type matching \c base. /// - /// A class is considered to be also derived from itself. + /// A class is not considered to be derived from itself. virtual bool classIsDerivedFrom(const CXXRecordDecl *Declaration, const Matcher &Base, - BoundNodesTreeBuilder *Builder) = 0; + BoundNodesTreeBuilder *Builder, + bool Directly) = 0; + + /// Returns true if the given Objective-C class is directly or indirectly + /// derived from a base class matching \c base. + /// + /// A class is not considered to be derived from itself. + virtual bool objcClassIsDerivedFrom(const ObjCInterfaceDecl *Declaration, + const Matcher &Base, + BoundNodesTreeBuilder *Builder, + bool Directly) = 0; template bool matchesChildOf(const T &Node, const DynTypedMatcher &Matcher, @@ -1315,7 +1326,7 @@ class ForEachMatcher : public WrapperMatcherInterface { /// /// Input matchers can have any type (including other polymorphic matcher /// types), and the actual Matcher is generated on demand with an implicit -/// coversion operator. +/// conversion operator. template class VariadicOperatorMatcher { public: VariadicOperatorMatcher(DynTypedMatcher::VariadicOperator Op, Ps &&... Params) @@ -1324,14 +1335,14 @@ public: template operator Matcher() const { return DynTypedMatcher::constructVariadic( Op, ast_type_traits::ASTNodeKind::getFromNodeKind(), - getMatchers(llvm::index_sequence_for())) + getMatchers(std::index_sequence_for())) .template unconditionalConvertTo(); } private: // Helper method to unpack the tuple into a vector. template - std::vector getMatchers(llvm::index_sequence) const { + std::vector getMatchers(std::index_sequence) const { return {Matcher(std::get(Params))...}; } diff --git a/include/clang/Analysis/AnalysisDeclContext.h b/include/clang/Analysis/AnalysisDeclContext.h index 1961d571e9e12..9faa78cde89c2 100644 --- a/include/clang/Analysis/AnalysisDeclContext.h +++ b/include/clang/Analysis/AnalysisDeclContext.h @@ -183,9 +183,8 @@ public: const ImplicitParamDecl *getSelfDecl() const; const StackFrameContext *getStackFrame(LocationContext const *Parent, - const Stmt *S, - const CFGBlock *Blk, - unsigned Idx); + const Stmt *S, const CFGBlock *Blk, + unsigned BlockCount, unsigned Idx); const BlockInvocationContext * getBlockInvocationContext(const LocationContext *parent, @@ -258,7 +257,7 @@ public: return getAnalysisDeclContext()->getAnalysis(); } - ParentMap &getParentMap() const { + const ParentMap &getParentMap() const { return getAnalysisDeclContext()->getParentMap(); } @@ -303,15 +302,19 @@ class StackFrameContext : public LocationContext { // The parent block of the callsite. const CFGBlock *Block; + // The number of times the 'Block' has been visited. + // It allows discriminating between stack frames of the same call that is + // called multiple times in a loop. + const unsigned BlockCount; + // The index of the callsite in the CFGBlock. - unsigned Index; + const unsigned Index; StackFrameContext(AnalysisDeclContext *ctx, const LocationContext *parent, - const Stmt *s, const CFGBlock *blk, - unsigned idx, - int64_t ID) - : LocationContext(StackFrame, ctx, parent, ID), CallSite(s), - Block(blk), Index(idx) {} + const Stmt *s, const CFGBlock *blk, unsigned blockCount, + unsigned idx, int64_t ID) + : LocationContext(StackFrame, ctx, parent, ID), CallSite(s), Block(blk), + BlockCount(blockCount), Index(idx) {} public: ~StackFrameContext() override = default; @@ -329,9 +332,10 @@ public: static void Profile(llvm::FoldingSetNodeID &ID, AnalysisDeclContext *ctx, const LocationContext *parent, const Stmt *s, - const CFGBlock *blk, unsigned idx) { + const CFGBlock *blk, unsigned blockCount, unsigned idx) { ProfileCommon(ID, StackFrame, ctx, parent, s); ID.AddPointer(blk); + ID.AddInteger(blockCount); ID.AddInteger(idx); } @@ -410,8 +414,8 @@ public: const StackFrameContext *getStackFrame(AnalysisDeclContext *ctx, const LocationContext *parent, - const Stmt *s, - const CFGBlock *blk, unsigned idx); + const Stmt *s, const CFGBlock *blk, + unsigned blockCount, unsigned idx); const ScopeContext *getScope(AnalysisDeclContext *ctx, const LocationContext *parent, @@ -483,26 +487,25 @@ public: bool synthesizeBodies() const { return SynthesizeBodies; } const StackFrameContext *getStackFrame(AnalysisDeclContext *Ctx, - LocationContext const *Parent, - const Stmt *S, - const CFGBlock *Blk, - unsigned Idx) { - return LocContexts.getStackFrame(Ctx, Parent, S, Blk, Idx); + const LocationContext *Parent, + const Stmt *S, const CFGBlock *Blk, + unsigned BlockCount, unsigned Idx) { + return LocContexts.getStackFrame(Ctx, Parent, S, Blk, BlockCount, Idx); } // Get the top level stack frame. const StackFrameContext *getStackFrame(const Decl *D) { return LocContexts.getStackFrame(getContext(D), nullptr, nullptr, nullptr, - 0); + 0, 0); } // Get a stack frame with parent. StackFrameContext const *getStackFrame(const Decl *D, - LocationContext const *Parent, - const Stmt *S, - const CFGBlock *Blk, - unsigned Idx) { - return LocContexts.getStackFrame(getContext(D), Parent, S, Blk, Idx); + const LocationContext *Parent, + const Stmt *S, const CFGBlock *Blk, + unsigned BlockCount, unsigned Idx) { + return LocContexts.getStackFrame(getContext(D), Parent, S, Blk, BlockCount, + Idx); } /// Get a reference to {@code BodyFarm} instance. diff --git a/include/clang/Analysis/CFG.h b/include/clang/Analysis/CFG.h index 277b2292e5eac..a8301a0e0063f 100644 --- a/include/clang/Analysis/CFG.h +++ b/include/clang/Analysis/CFG.h @@ -121,6 +121,12 @@ public: x |= Data1.getInt(); return (Kind) x; } + + void dumpToStream(llvm::raw_ostream &OS) const; + + void dump() const { + dumpToStream(llvm::errs()); + } }; class CFGStmt : public CFGElement { @@ -610,6 +616,153 @@ class CFGBlock { bool empty() const { return Impl.empty(); } }; + /// A convenience class for comparing CFGElements, since methods of CFGBlock + /// like operator[] return CFGElements by value. This is practically a wrapper + /// around a (CFGBlock, Index) pair. + template class ElementRefImpl { + + template friend class ElementRefImpl; + + using CFGBlockPtr = + typename std::conditional::type; + + using CFGElementPtr = typename std::conditional::type; + + protected: + CFGBlockPtr Parent; + size_t Index; + + public: + ElementRefImpl(CFGBlockPtr Parent, size_t Index) + : Parent(Parent), Index(Index) {} + + template + ElementRefImpl(ElementRefImpl Other) + : ElementRefImpl(Other.Parent, Other.Index) {} + + size_t getIndexInBlock() const { return Index; } + + CFGBlockPtr getParent() { return Parent; } + CFGBlockPtr getParent() const { return Parent; } + + bool operator<(ElementRefImpl Other) const { + return std::make_pair(Parent, Index) < + std::make_pair(Other.Parent, Other.Index); + } + + bool operator==(ElementRefImpl Other) const { + return Parent == Other.Parent && Index == Other.Index; + } + + bool operator!=(ElementRefImpl Other) const { return !(*this == Other); } + CFGElement operator*() const { return (*Parent)[Index]; } + CFGElementPtr operator->() const { return &*(Parent->begin() + Index); } + + void dumpToStream(llvm::raw_ostream &OS) const { + OS << getIndexInBlock() + 1 << ": "; + (*this)->dumpToStream(OS); + } + + void dump() const { + dumpToStream(llvm::errs()); + } + }; + + template class ElementRefIterator { + + template + friend class ElementRefIterator; + + using CFGBlockRef = + typename std::conditional::type; + + using UnderlayingIteratorTy = typename std::conditional< + IsConst, + typename std::conditional::type, + typename std::conditional::type>::type; + + using IteratorTraits = typename std::iterator_traits; + using ElementRef = typename CFGBlock::ElementRefImpl; + + public: + using difference_type = typename IteratorTraits::difference_type; + using value_type = ElementRef; + using pointer = ElementRef *; + using iterator_category = typename IteratorTraits::iterator_category; + + private: + CFGBlockRef Parent; + UnderlayingIteratorTy Pos; + + public: + ElementRefIterator(CFGBlockRef Parent, UnderlayingIteratorTy Pos) + : Parent(Parent), Pos(Pos) {} + + template + ElementRefIterator(ElementRefIterator E) + : ElementRefIterator(E.Parent, E.Pos.base()) {} + + template + ElementRefIterator(ElementRefIterator E) + : ElementRefIterator(E.Parent, llvm::make_reverse_iterator(E.Pos)) {} + + bool operator<(ElementRefIterator Other) const { + assert(Parent == Other.Parent); + return Pos < Other.Pos; + } + + bool operator==(ElementRefIterator Other) const { + return Parent == Other.Parent && Pos == Other.Pos; + } + + bool operator!=(ElementRefIterator Other) const { + return !(*this == Other); + } + + private: + template + static size_t + getIndexInBlock(CFGBlock::ElementRefIterator E) { + return E.Parent->size() - (E.Pos - E.Parent->rbegin()) - 1; + } + + template + static size_t + getIndexInBlock(CFGBlock::ElementRefIterator E) { + return E.Pos - E.Parent->begin(); + } + + public: + value_type operator*() { return {Parent, getIndexInBlock(*this)}; } + + difference_type operator-(ElementRefIterator Other) const { + return Pos - Other.Pos; + } + + ElementRefIterator operator++() { + ++this->Pos; + return *this; + } + ElementRefIterator operator++(int) { + ElementRefIterator Ret = *this; + ++*this; + return Ret; + } + ElementRefIterator operator+(size_t count) { + this->Pos += count; + return *this; + } + ElementRefIterator operator-(size_t count) { + this->Pos -= count; + return *this; + } + }; + +public: /// The set of statements in the basic block. ElementList Elements; @@ -715,6 +868,8 @@ public: using reverse_iterator = ElementList::reverse_iterator; using const_reverse_iterator = ElementList::const_reverse_iterator; + size_t getIndexInCFG() const; + CFGElement front() const { return Elements.front(); } CFGElement back() const { return Elements.back(); } @@ -728,6 +883,38 @@ public: const_reverse_iterator rbegin() const { return Elements.rbegin(); } const_reverse_iterator rend() const { return Elements.rend(); } + using CFGElementRef = ElementRefImpl; + using ConstCFGElementRef = ElementRefImpl; + + using ref_iterator = ElementRefIterator; + using ref_iterator_range = llvm::iterator_range; + using const_ref_iterator = ElementRefIterator; + using const_ref_iterator_range = llvm::iterator_range; + + using reverse_ref_iterator = ElementRefIterator; + using reverse_ref_iterator_range = llvm::iterator_range; + + using const_reverse_ref_iterator = ElementRefIterator; + using const_reverse_ref_iterator_range = + llvm::iterator_range; + + ref_iterator ref_begin() { return {this, begin()}; } + ref_iterator ref_end() { return {this, end()}; } + const_ref_iterator ref_begin() const { return {this, begin()}; } + const_ref_iterator ref_end() const { return {this, end()}; } + + reverse_ref_iterator rref_begin() { return {this, rbegin()}; } + reverse_ref_iterator rref_end() { return {this, rend()}; } + const_reverse_ref_iterator rref_begin() const { return {this, rbegin()}; } + const_reverse_ref_iterator rref_end() const { return {this, rend()}; } + + ref_iterator_range refs() { return {ref_begin(), ref_end()}; } + const_ref_iterator_range refs() const { return {ref_begin(), ref_end()}; } + reverse_ref_iterator_range rrefs() { return {rref_begin(), rref_end()}; } + const_reverse_ref_iterator_range rrefs() const { + return {rref_begin(), rref_end()}; + } + unsigned size() const { return Elements.size(); } bool empty() const { return Elements.empty(); } @@ -855,6 +1042,10 @@ public: void setLoopTarget(const Stmt *loopTarget) { LoopTarget = loopTarget; } void setHasNoReturnElement() { HasNoReturnElement = true; } + /// Returns true if the block would eventually end with a sink (a noreturn + /// node). + bool isInevitablySinking() const; + CFGTerminator getTerminator() const { return Terminator; } Stmt *getTerminatorStmt() { return Terminator.getStmt(); } @@ -894,7 +1085,7 @@ public: void printTerminator(raw_ostream &OS, const LangOptions &LO) const; void printTerminatorJson(raw_ostream &Out, const LangOptions &LO, bool AddQuotes) const; - + void printAsOperand(raw_ostream &OS, bool /*PrintType*/) { OS << "BB#" << getBlockID(); } @@ -1010,7 +1201,6 @@ public: *I = CFGScopeEnd(VD, S); return ++I; } - }; /// CFGCallback defines methods that should be called when a logical @@ -1023,6 +1213,7 @@ public: virtual void compareAlwaysTrue(const BinaryOperator *B, bool isAlwaysTrue) {} virtual void compareBitwiseEquality(const BinaryOperator *B, bool isAlwaysTrue) {} + virtual void compareBitwiseOr(const BinaryOperator *B) {} }; /// Represents a source-level, intra-procedural CFG that represents the diff --git a/include/clang/Analysis/CallGraph.h b/include/clang/Analysis/CallGraph.h index 49c04490fed2c..dae2b58ffc102 100644 --- a/include/clang/Analysis/CallGraph.h +++ b/include/clang/Analysis/CallGraph.h @@ -131,6 +131,7 @@ public: bool shouldWalkTypesOfTypeLocs() const { return false; } bool shouldVisitTemplateInstantiations() const { return true; } + bool shouldVisitImplicitCode() const { return true; } private: /// Add the given declaration to the call graph. diff --git a/include/clang/Analysis/PathDiagnostic.h b/include/clang/Analysis/PathDiagnostic.h new file mode 100644 index 0000000000000..6730057cf0adc --- /dev/null +++ b/include/clang/Analysis/PathDiagnostic.h @@ -0,0 +1,868 @@ +//===- PathDiagnostic.h - Path-Specific Diagnostic Handling -----*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines the PathDiagnostic-related interfaces. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_STATICANALYZER_CORE_BUGREPORTER_PATHDIAGNOSTIC_H +#define LLVM_CLANG_STATICANALYZER_CORE_BUGREPORTER_PATHDIAGNOSTIC_H + +#include "clang/AST/Stmt.h" +#include "clang/Analysis/AnalysisDeclContext.h" +#include "clang/Basic/LLVM.h" +#include "clang/Basic/SourceLocation.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/FoldingSet.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/PointerUnion.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Allocator.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace clang { + +class AnalysisDeclContext; +class BinaryOperator; +class CallEnter; +class CallExitEnd; +class CallExpr; +class ConditionalOperator; +class Decl; +class Expr; +class LocationContext; +class MemberExpr; +class ProgramPoint; +class SourceManager; + +namespace ento { + +//===----------------------------------------------------------------------===// +// High-level interface for handlers of path-sensitive diagnostics. +//===----------------------------------------------------------------------===// + +class PathDiagnostic; + +class PathDiagnosticConsumer { +public: + class PDFileEntry : public llvm::FoldingSetNode { + public: + PDFileEntry(llvm::FoldingSetNodeID &NodeID) : NodeID(NodeID) {} + + using ConsumerFiles = std::vector>; + + /// A vector of pairs. + ConsumerFiles files; + + /// A precomputed hash tag used for uniquing PDFileEntry objects. + const llvm::FoldingSetNodeID NodeID; + + /// Used for profiling in the FoldingSet. + void Profile(llvm::FoldingSetNodeID &ID) { ID = NodeID; } + }; + + class FilesMade { + llvm::BumpPtrAllocator Alloc; + llvm::FoldingSet Set; + + public: + ~FilesMade(); + + bool empty() const { return Set.empty(); } + + void addDiagnostic(const PathDiagnostic &PD, + StringRef ConsumerName, + StringRef fileName); + + PDFileEntry::ConsumerFiles *getFiles(const PathDiagnostic &PD); + }; + +private: + virtual void anchor(); + +public: + PathDiagnosticConsumer() = default; + virtual ~PathDiagnosticConsumer(); + + void FlushDiagnostics(FilesMade *FilesMade); + + virtual void FlushDiagnosticsImpl(std::vector &Diags, + FilesMade *filesMade) = 0; + + virtual StringRef getName() const = 0; + + void HandlePathDiagnostic(std::unique_ptr D); + + enum PathGenerationScheme { + /// Only runs visitors, no output generated. + None, + + /// Used for HTML, SARIF, and text output. + Minimal, + + /// Used for plist output, used for "arrows" generation. + Extensive, + }; + + virtual PathGenerationScheme getGenerationScheme() const { return Minimal; } + + bool shouldGenerateDiagnostics() const { + return getGenerationScheme() != None; + } + + bool shouldAddPathEdges() const { return getGenerationScheme() == Extensive; } + + virtual bool supportsLogicalOpControlFlow() const { return false; } + + /// Return true if the PathDiagnosticConsumer supports individual + /// PathDiagnostics that span multiple files. + virtual bool supportsCrossFileDiagnostics() const { return false; } + +protected: + bool flushed = false; + llvm::FoldingSet Diags; +}; + +//===----------------------------------------------------------------------===// +// Path-sensitive diagnostics. +//===----------------------------------------------------------------------===// + +class PathDiagnosticRange : public SourceRange { +public: + bool isPoint = false; + + PathDiagnosticRange(SourceRange R, bool isP = false) + : SourceRange(R), isPoint(isP) {} + PathDiagnosticRange() = default; +}; + +using LocationOrAnalysisDeclContext = + llvm::PointerUnion; + +class PathDiagnosticLocation { +private: + enum Kind { RangeK, SingleLocK, StmtK, DeclK } K = SingleLocK; + + const Stmt *S = nullptr; + const Decl *D = nullptr; + const SourceManager *SM = nullptr; + FullSourceLoc Loc; + PathDiagnosticRange Range; + + PathDiagnosticLocation(SourceLocation L, const SourceManager &sm, Kind kind) + : K(kind), SM(&sm), Loc(genLocation(L)), Range(genRange()) {} + + FullSourceLoc genLocation( + SourceLocation L = SourceLocation(), + LocationOrAnalysisDeclContext LAC = (AnalysisDeclContext *)nullptr) const; + + PathDiagnosticRange genRange( + LocationOrAnalysisDeclContext LAC = (AnalysisDeclContext *)nullptr) const; + +public: + /// Create an invalid location. + PathDiagnosticLocation() = default; + + /// Create a location corresponding to the given statement. + PathDiagnosticLocation(const Stmt *s, const SourceManager &sm, + LocationOrAnalysisDeclContext lac) + : K(s->getBeginLoc().isValid() ? StmtK : SingleLocK), + S(K == StmtK ? s : nullptr), SM(&sm), + Loc(genLocation(SourceLocation(), lac)), Range(genRange(lac)) { + assert(K == SingleLocK || S); + assert(K == SingleLocK || Loc.isValid()); + assert(K == SingleLocK || Range.isValid()); + } + + /// Create a location corresponding to the given declaration. + PathDiagnosticLocation(const Decl *d, const SourceManager &sm) + : K(DeclK), D(d), SM(&sm), Loc(genLocation()), Range(genRange()) { + assert(D); + assert(Loc.isValid()); + assert(Range.isValid()); + } + + /// Create a location at an explicit offset in the source. + /// + /// This should only be used if there are no more appropriate constructors. + PathDiagnosticLocation(SourceLocation loc, const SourceManager &sm) + : SM(&sm), Loc(loc, sm), Range(genRange()) { + assert(Loc.isValid()); + assert(Range.isValid()); + } + + /// Create a location corresponding to the given declaration. + static PathDiagnosticLocation create(const Decl *D, + const SourceManager &SM) { + return PathDiagnosticLocation(D, SM); + } + + /// Create a location for the beginning of the declaration. + static PathDiagnosticLocation createBegin(const Decl *D, + const SourceManager &SM); + + /// Create a location for the beginning of the declaration. + /// The third argument is ignored, useful for generic treatment + /// of statements and declarations. + static PathDiagnosticLocation + createBegin(const Decl *D, const SourceManager &SM, + const LocationOrAnalysisDeclContext LAC) { + return createBegin(D, SM); + } + + /// Create a location for the beginning of the statement. + static PathDiagnosticLocation createBegin(const Stmt *S, + const SourceManager &SM, + const LocationOrAnalysisDeclContext LAC); + + /// Create a location for the end of the statement. + /// + /// If the statement is a CompoundStatement, the location will point to the + /// closing brace instead of following it. + static PathDiagnosticLocation createEnd(const Stmt *S, + const SourceManager &SM, + const LocationOrAnalysisDeclContext LAC); + + /// Create the location for the operator of the binary expression. + /// Assumes the statement has a valid location. + static PathDiagnosticLocation createOperatorLoc(const BinaryOperator *BO, + const SourceManager &SM); + static PathDiagnosticLocation createConditionalColonLoc( + const ConditionalOperator *CO, + const SourceManager &SM); + + /// For member expressions, return the location of the '.' or '->'. + /// Assumes the statement has a valid location. + static PathDiagnosticLocation createMemberLoc(const MemberExpr *ME, + const SourceManager &SM); + + /// Create a location for the beginning of the compound statement. + /// Assumes the statement has a valid location. + static PathDiagnosticLocation createBeginBrace(const CompoundStmt *CS, + const SourceManager &SM); + + /// Create a location for the end of the compound statement. + /// Assumes the statement has a valid location. + static PathDiagnosticLocation createEndBrace(const CompoundStmt *CS, + const SourceManager &SM); + + /// Create a location for the beginning of the enclosing declaration body. + /// Defaults to the beginning of the first statement in the declaration body. + static PathDiagnosticLocation createDeclBegin(const LocationContext *LC, + const SourceManager &SM); + + /// Constructs a location for the end of the enclosing declaration body. + /// Defaults to the end of brace. + static PathDiagnosticLocation createDeclEnd(const LocationContext *LC, + const SourceManager &SM); + + /// Create a location corresponding to the given valid ProgramPoint. + static PathDiagnosticLocation create(const ProgramPoint &P, + const SourceManager &SMng); + + /// Convert the given location into a single kind location. + static PathDiagnosticLocation createSingleLocation( + const PathDiagnosticLocation &PDL); + + /// Construct a source location that corresponds to either the beginning + /// or the end of the given statement, or a nearby valid source location + /// if the statement does not have a valid source location of its own. + static SourceLocation + getValidSourceLocation(const Stmt *S, LocationOrAnalysisDeclContext LAC, + bool UseEndOfStatement = false); + + bool operator==(const PathDiagnosticLocation &X) const { + return K == X.K && Loc == X.Loc && Range == X.Range; + } + + bool operator!=(const PathDiagnosticLocation &X) const { + return !(*this == X); + } + + bool isValid() const { + return SM != nullptr; + } + + FullSourceLoc asLocation() const { + return Loc; + } + + PathDiagnosticRange asRange() const { + return Range; + } + + const Stmt *asStmt() const { assert(isValid()); return S; } + const Stmt *getStmtOrNull() const { + if (!isValid()) + return nullptr; + return asStmt(); + } + + const Decl *asDecl() const { assert(isValid()); return D; } + + bool hasRange() const { return K == StmtK || K == RangeK || K == DeclK; } + + bool hasValidLocation() const { return asLocation().isValid(); } + + void invalidate() { + *this = PathDiagnosticLocation(); + } + + void flatten(); + + const SourceManager& getManager() const { assert(isValid()); return *SM; } + + void Profile(llvm::FoldingSetNodeID &ID) const; + + void dump() const; +}; + +class PathDiagnosticLocationPair { +private: + PathDiagnosticLocation Start, End; + +public: + PathDiagnosticLocationPair(const PathDiagnosticLocation &start, + const PathDiagnosticLocation &end) + : Start(start), End(end) {} + + 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(); + } + + void Profile(llvm::FoldingSetNodeID &ID) const { + Start.Profile(ID); + End.Profile(ID); + } +}; + +//===----------------------------------------------------------------------===// +// Path "pieces" for path-sensitive diagnostics. +//===----------------------------------------------------------------------===// + +class PathDiagnosticPiece: public llvm::FoldingSetNode { +public: + enum Kind { ControlFlow, Event, Macro, Call, Note, PopUp }; + enum DisplayHint { Above, Below }; + +private: + const std::string str; + const Kind kind; + const DisplayHint Hint; + + /// In the containing bug report, this piece is the last piece from + /// the main source file. + bool LastInMainSourceFile = false; + + /// A constant string that can be used to tag the PathDiagnosticPiece, + /// typically with the identification of the creator. The actual pointer + /// value is meant to be an identifier; the string itself is useful for + /// debugging. + StringRef Tag; + + std::vector ranges; + std::vector fixits; + +protected: + PathDiagnosticPiece(StringRef s, Kind k, DisplayHint hint = Below); + PathDiagnosticPiece(Kind k, DisplayHint hint = Below); + +public: + PathDiagnosticPiece() = delete; + PathDiagnosticPiece(const PathDiagnosticPiece &) = delete; + PathDiagnosticPiece &operator=(const PathDiagnosticPiece &) = delete; + virtual ~PathDiagnosticPiece(); + + StringRef getString() const { return str; } + + /// Tag this PathDiagnosticPiece with the given C-string. + void setTag(const char *tag) { Tag = tag; } + + /// Return the opaque tag (if any) on the PathDiagnosticPiece. + const void *getTag() const { return Tag.data(); } + + /// Return the string representation of the tag. This is useful + /// for debugging. + StringRef getTagStr() const { return Tag; } + + /// getDisplayHint - Return a hint indicating where the diagnostic should + /// be displayed by the PathDiagnosticConsumer. + DisplayHint getDisplayHint() const { return Hint; } + + virtual PathDiagnosticLocation getLocation() const = 0; + virtual void flattenLocations() = 0; + + Kind getKind() const { return kind; } + + void addRange(SourceRange R) { + if (!R.isValid()) + return; + ranges.push_back(R); + } + + void addRange(SourceLocation B, SourceLocation E) { + if (!B.isValid() || !E.isValid()) + return; + ranges.push_back(SourceRange(B,E)); + } + + void addFixit(FixItHint F) { + fixits.push_back(F); + } + + /// Return the SourceRanges associated with this PathDiagnosticPiece. + ArrayRef getRanges() const { return ranges; } + + /// Return the fix-it hints associated with this PathDiagnosticPiece. + ArrayRef getFixits() const { return fixits; } + + virtual void Profile(llvm::FoldingSetNodeID &ID) const; + + void setAsLastInMainSourceFile() { + LastInMainSourceFile = true; + } + + bool isLastInMainSourceFile() const { + return LastInMainSourceFile; + } + + virtual void dump() const = 0; +}; + +using PathDiagnosticPieceRef = std::shared_ptr; + +class PathPieces : public std::list { + void flattenTo(PathPieces &Primary, PathPieces &Current, + bool ShouldFlattenMacros) const; + +public: + PathPieces flatten(bool ShouldFlattenMacros) const { + PathPieces Result; + flattenTo(Result, Result, ShouldFlattenMacros); + return Result; + } + + void dump() const; +}; + +class PathDiagnosticSpotPiece : public PathDiagnosticPiece { +private: + PathDiagnosticLocation Pos; + +public: + PathDiagnosticSpotPiece(const PathDiagnosticLocation &pos, + StringRef s, + PathDiagnosticPiece::Kind k, + bool addPosRange = true) + : PathDiagnosticPiece(s, k), Pos(pos) { + assert(Pos.isValid() && Pos.hasValidLocation() && + "PathDiagnosticSpotPiece's must have a valid location."); + if (addPosRange && Pos.hasRange()) addRange(Pos.asRange()); + } + + PathDiagnosticLocation getLocation() const override { return Pos; } + void flattenLocations() override { Pos.flatten(); } + + void Profile(llvm::FoldingSetNodeID &ID) const override; + + static bool classof(const PathDiagnosticPiece *P) { + return P->getKind() == Event || P->getKind() == Macro || + P->getKind() == Note || P->getKind() == PopUp; + } +}; + +class PathDiagnosticEventPiece : public PathDiagnosticSpotPiece { + Optional IsPrunable; + +public: + PathDiagnosticEventPiece(const PathDiagnosticLocation &pos, + StringRef s, bool addPosRange = true) + : PathDiagnosticSpotPiece(pos, s, Event, addPosRange) {} + ~PathDiagnosticEventPiece() override; + + /// Mark the diagnostic piece as being potentially prunable. This + /// flag may have been previously set, at which point it will not + /// be reset unless one specifies to do so. + void setPrunable(bool isPrunable, bool override = false) { + if (IsPrunable.hasValue() && !override) + return; + IsPrunable = isPrunable; + } + + /// Return true if the diagnostic piece is prunable. + bool isPrunable() const { + return IsPrunable.hasValue() ? IsPrunable.getValue() : false; + } + + void dump() const override; + + static bool classof(const PathDiagnosticPiece *P) { + return P->getKind() == Event; + } +}; + +class PathDiagnosticCallPiece : public PathDiagnosticPiece { + const Decl *Caller; + const Decl *Callee = nullptr; + + // Flag signifying that this diagnostic has only call enter and no matching + // call exit. + bool NoExit; + + // Flag signifying that the callee function is an Objective-C autosynthesized + // property getter or setter. + bool IsCalleeAnAutosynthesizedPropertyAccessor = false; + + // The custom string, which should appear after the call Return Diagnostic. + // TODO: Should we allow multiple diagnostics? + std::string CallStackMessage; + + PathDiagnosticCallPiece(const Decl *callerD, + const PathDiagnosticLocation &callReturnPos) + : PathDiagnosticPiece(Call), Caller(callerD), NoExit(false), + callReturn(callReturnPos) {} + PathDiagnosticCallPiece(PathPieces &oldPath, const Decl *caller) + : PathDiagnosticPiece(Call), Caller(caller), NoExit(true), + path(oldPath) {} + +public: + PathDiagnosticLocation callEnter; + PathDiagnosticLocation callEnterWithin; + PathDiagnosticLocation callReturn; + PathPieces path; + + ~PathDiagnosticCallPiece() override; + + const Decl *getCaller() const { return Caller; } + + const Decl *getCallee() const { return Callee; } + void setCallee(const CallEnter &CE, const SourceManager &SM); + + bool hasCallStackMessage() { return !CallStackMessage.empty(); } + void setCallStackMessage(StringRef st) { CallStackMessage = st; } + + PathDiagnosticLocation getLocation() const override { return callEnter; } + + std::shared_ptr getCallEnterEvent() const; + std::shared_ptr + getCallEnterWithinCallerEvent() const; + std::shared_ptr getCallExitEvent() const; + + void flattenLocations() override { + callEnter.flatten(); + callReturn.flatten(); + for (const auto &I : path) + I->flattenLocations(); + } + + static std::shared_ptr + construct(const CallExitEnd &CE, + const SourceManager &SM); + + static PathDiagnosticCallPiece *construct(PathPieces &pieces, + const Decl *caller); + + void dump() const override; + + void Profile(llvm::FoldingSetNodeID &ID) const override; + + static bool classof(const PathDiagnosticPiece *P) { + return P->getKind() == Call; + } +}; + +class PathDiagnosticControlFlowPiece : public PathDiagnosticPiece { + std::vector LPairs; + +public: + PathDiagnosticControlFlowPiece(const PathDiagnosticLocation &startPos, + const PathDiagnosticLocation &endPos, + StringRef s) + : PathDiagnosticPiece(s, ControlFlow) { + LPairs.push_back(PathDiagnosticLocationPair(startPos, endPos)); + } + + PathDiagnosticControlFlowPiece(const PathDiagnosticLocation &startPos, + const PathDiagnosticLocation &endPos) + : PathDiagnosticPiece(ControlFlow) { + LPairs.push_back(PathDiagnosticLocationPair(startPos, endPos)); + } + + ~PathDiagnosticControlFlowPiece() override; + + PathDiagnosticLocation getStartLocation() const { + assert(!LPairs.empty() && + "PathDiagnosticControlFlowPiece needs at least one location."); + return LPairs[0].getStart(); + } + + PathDiagnosticLocation getEndLocation() const { + assert(!LPairs.empty() && + "PathDiagnosticControlFlowPiece needs at least one location."); + 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); } + + PathDiagnosticLocation getLocation() const override { + return getStartLocation(); + } + + using iterator = std::vector::iterator; + + iterator begin() { return LPairs.begin(); } + iterator end() { return LPairs.end(); } + + void flattenLocations() override { + for (auto &I : *this) + I.flatten(); + } + + using const_iterator = + std::vector::const_iterator; + + const_iterator begin() const { return LPairs.begin(); } + const_iterator end() const { return LPairs.end(); } + + static bool classof(const PathDiagnosticPiece *P) { + return P->getKind() == ControlFlow; + } + + void dump() const override; + + void Profile(llvm::FoldingSetNodeID &ID) const override; +}; + +class PathDiagnosticMacroPiece : public PathDiagnosticSpotPiece { +public: + PathDiagnosticMacroPiece(const PathDiagnosticLocation &pos) + : PathDiagnosticSpotPiece(pos, "", Macro) {} + ~PathDiagnosticMacroPiece() override; + + PathPieces subPieces; + + void flattenLocations() override { + PathDiagnosticSpotPiece::flattenLocations(); + for (const auto &I : subPieces) + I->flattenLocations(); + } + + static bool classof(const PathDiagnosticPiece *P) { + return P->getKind() == Macro; + } + + void dump() const override; + + void Profile(llvm::FoldingSetNodeID &ID) const override; +}; + +class PathDiagnosticNotePiece: public PathDiagnosticSpotPiece { +public: + PathDiagnosticNotePiece(const PathDiagnosticLocation &Pos, StringRef S, + bool AddPosRange = true) + : PathDiagnosticSpotPiece(Pos, S, Note, AddPosRange) {} + ~PathDiagnosticNotePiece() override; + + static bool classof(const PathDiagnosticPiece *P) { + return P->getKind() == Note; + } + + void dump() const override; + + void Profile(llvm::FoldingSetNodeID &ID) const override; +}; + +class PathDiagnosticPopUpPiece: public PathDiagnosticSpotPiece { +public: + PathDiagnosticPopUpPiece(const PathDiagnosticLocation &Pos, StringRef S, + bool AddPosRange = true) + : PathDiagnosticSpotPiece(Pos, S, PopUp, AddPosRange) {} + ~PathDiagnosticPopUpPiece() override; + + static bool classof(const PathDiagnosticPiece *P) { + return P->getKind() == PopUp; + } + + void dump() const override; + + void Profile(llvm::FoldingSetNodeID &ID) const override; +}; + +/// File IDs mapped to sets of line numbers. +using FilesToLineNumsMap = std::map>; + +/// PathDiagnostic - PathDiagnostic objects represent a single path-sensitive +/// diagnostic. It represents an ordered-collection of PathDiagnosticPieces, +/// each which represent the pieces of the path. +class PathDiagnostic : public llvm::FoldingSetNode { + std::string CheckerName; + const Decl *DeclWithIssue; + std::string BugType; + std::string VerboseDesc; + std::string ShortDesc; + std::string Category; + std::deque OtherDesc; + + /// Loc The location of the path diagnostic report. + PathDiagnosticLocation Loc; + + PathPieces pathImpl; + SmallVector pathStack; + + /// Important bug uniqueing location. + /// The location info is useful to differentiate between bugs. + PathDiagnosticLocation UniqueingLoc; + const Decl *UniqueingDecl; + + /// Lines executed in the path. + std::unique_ptr ExecutedLines; + +public: + PathDiagnostic() = delete; + PathDiagnostic(StringRef CheckerName, const Decl *DeclWithIssue, + StringRef bugtype, StringRef verboseDesc, StringRef shortDesc, + StringRef category, PathDiagnosticLocation LocationToUnique, + const Decl *DeclToUnique, + std::unique_ptr ExecutedLines); + ~PathDiagnostic(); + + const PathPieces &path; + + /// Return the path currently used by builders for constructing the + /// PathDiagnostic. + PathPieces &getActivePath() { + if (pathStack.empty()) + return pathImpl; + return *pathStack.back(); + } + + /// Return a mutable version of 'path'. + PathPieces &getMutablePieces() { + return pathImpl; + } + + /// Return the unrolled size of the path. + unsigned full_size(); + + void pushActivePath(PathPieces *p) { pathStack.push_back(p); } + void popActivePath() { if (!pathStack.empty()) pathStack.pop_back(); } + + bool isWithinCall() const { return !pathStack.empty(); } + + void setEndOfPath(PathDiagnosticPieceRef EndPiece) { + assert(!Loc.isValid() && "End location already set!"); + Loc = EndPiece->getLocation(); + assert(Loc.isValid() && "Invalid location for end-of-path piece"); + getActivePath().push_back(std::move(EndPiece)); + } + + void appendToDesc(StringRef S) { + if (!ShortDesc.empty()) + ShortDesc += S; + VerboseDesc += S; + } + + StringRef getVerboseDescription() const { return VerboseDesc; } + + StringRef getShortDescription() const { + return ShortDesc.empty() ? VerboseDesc : ShortDesc; + } + + StringRef getCheckerName() const { return CheckerName; } + StringRef getBugType() const { return BugType; } + StringRef getCategory() const { return Category; } + + using meta_iterator = std::deque::const_iterator; + + meta_iterator meta_begin() const { return OtherDesc.begin(); } + meta_iterator meta_end() const { return OtherDesc.end(); } + void addMeta(StringRef s) { OtherDesc.push_back(s); } + + const FilesToLineNumsMap &getExecutedLines() const { + return *ExecutedLines; + } + + FilesToLineNumsMap &getExecutedLines() { + return *ExecutedLines; + } + + /// Return the semantic context where an issue occurred. If the + /// issue occurs along a path, this represents the "central" area + /// where the bug manifests. + const Decl *getDeclWithIssue() const { return DeclWithIssue; } + + void setDeclWithIssue(const Decl *D) { + DeclWithIssue = D; + } + + PathDiagnosticLocation getLocation() const { + return Loc; + } + + void setLocation(PathDiagnosticLocation NewLoc) { + Loc = NewLoc; + } + + /// Get the location on which the report should be uniqued. + PathDiagnosticLocation getUniqueingLoc() const { + return UniqueingLoc; + } + + /// Get the declaration containing the uniqueing location. + const Decl *getUniqueingDecl() const { + return UniqueingDecl; + } + + void flattenLocations() { + Loc.flatten(); + for (const auto &I : pathImpl) + I->flattenLocations(); + } + + /// Profiles the diagnostic, independent of the path it references. + /// + /// This can be used to merge diagnostics that refer to the same issue + /// along different paths. + void Profile(llvm::FoldingSetNodeID &ID) const; + + /// Profiles the diagnostic, including its path. + /// + /// Two diagnostics with the same issue along different paths will generate + /// different profiles. + void FullProfile(llvm::FoldingSetNodeID &ID) const; +}; + +} // namespace ento +} // namespace clang + +#endif // LLVM_CLANG_STATICANALYZER_CORE_BUGREPORTER_PATHDIAGNOSTIC_H diff --git a/include/clang/Basic/AArch64SVEACLETypes.def b/include/clang/Basic/AArch64SVEACLETypes.def new file mode 100644 index 0000000000000..7d387587dc297 --- /dev/null +++ b/include/clang/Basic/AArch64SVEACLETypes.def @@ -0,0 +1,70 @@ +//===-- AArch64SVEACLETypes.def - Metadata about SVE types ------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines various SVE builtin types. The macros are: +// +// SVE_TYPE(Name, Id, SingletonId) - A builtin type that has not been +// covered by any other #define. Defining this macro covers all +// the builtins. +// +// SVE_VECTOR_TYPE(Name, Id, SingletonId, ElKind, ElBits, IsSigned, IsFP) - +// An SVE scalable vector. +// +// SVE_PREDICATE_TYPE(Name, Id, SingletonId, ElKind) - An SVE scalable +// predicate. +// +// where: +// +// - Name is the name of the builtin type. +// +// - BuiltinType::Id is the enumerator defining the type. +// +// - Context.SingletonId is the global singleton of this type. +// +// - ElKind enumerates the type of the elements. +// +// - ElBits is the size of one element in bits. +// +// - IsSigned is true for vectors of signed integer elements and +// for vectors of floating-point elements. +// +// - IsFP is true for vectors of floating-point elements. +// +//===----------------------------------------------------------------------===// + +#ifndef SVE_VECTOR_TYPE +#define SVE_VECTOR_TYPE(Name, Id, SingletonId, ElKind, ElBits, IsSigned, IsFP)\ + SVE_TYPE(Name, Id, SingletonId) +#endif + +#ifndef SVE_PREDICATE_TYPE +#define SVE_PREDICATE_TYPE(Name, Id, SingletonId, ElKind)\ + SVE_TYPE(Name, Id, SingletonId) +#endif + +//===- Vector point types -----------------------------------------------===// + +SVE_VECTOR_TYPE("__SVInt8_t", SveInt8, SveInt8Ty, SveElSInt8, 8, true, false) +SVE_VECTOR_TYPE("__SVInt16_t", SveInt16, SveInt16Ty, SveElSInt16, 16, true, false) +SVE_VECTOR_TYPE("__SVInt32_t", SveInt32, SveInt32Ty, SveElSInt32, 32, true, false) +SVE_VECTOR_TYPE("__SVInt64_t", SveInt64, SveInt64Ty, SveElSInt64, 64, true, false) + +SVE_VECTOR_TYPE("__SVUint8_t", SveUint8, SveUint8Ty, SveElUInt8, 8, false, false) +SVE_VECTOR_TYPE("__SVUint16_t", SveUint16, SveUint16Ty, SveElUInt16, 16, false, false) +SVE_VECTOR_TYPE("__SVUint32_t", SveUint32, SveUint32Ty, SveElUInt32, 32, false, false) +SVE_VECTOR_TYPE("__SVUint64_t", SveUint64, SveUint64Ty, SveElUInt64, 64, false, false) + +SVE_VECTOR_TYPE("__SVFloat16_t", SveFloat16, SveFloat16Ty, SveElHalf, 16, true, true) +SVE_VECTOR_TYPE("__SVFloat32_t", SveFloat32, SveFloat32Ty, SveElFloat, 32, true, true) +SVE_VECTOR_TYPE("__SVFloat64_t", SveFloat64, SveFloat64Ty, SveElDouble, 64, true, true) + +SVE_PREDICATE_TYPE("__SVBool_t", SveBool, SveBoolTy, SveElBool) + +#undef SVE_VECTOR_TYPE +#undef SVE_PREDICATE_TYPE +#undef SVE_TYPE diff --git a/include/clang/Basic/Attr.td b/include/clang/Basic/Attr.td index d39b16e62b7fa..c3a2ee325dfe4 100644 --- a/include/clang/Basic/Attr.td +++ b/include/clang/Basic/Attr.td @@ -722,9 +722,25 @@ def AVRSignal : InheritableAttr, TargetSpecificAttr { def AsmLabel : InheritableAttr { let Spellings = [Keyword<"asm">, Keyword<"__asm__">]; - let Args = [StringArgument<"Label">]; + let Args = [ + // Label specifies the mangled name for the decl. + StringArgument<"Label">, + + // IsLiteralLabel specifies whether the label is literal (i.e. suppresses + // the global C symbol prefix) or not. If not, the mangle-suppression prefix + // ('\01') is omitted from the decl name at the LLVM IR level. + // + // Non-literal labels are used by some external AST sources like LLDB. + BoolArgument<"IsLiteralLabel", /*optional=*/0, /*fake=*/1> + ]; let SemaHandler = 0; - let Documentation = [Undocumented]; + let Documentation = [AsmLabelDocs]; + let AdditionalMembers = +[{ +bool isEquivalent(AsmLabelAttr *Other) const { + return getLabel() == Other->getLabel() && getIsLiteralLabel() == Other->getIsLiteralLabel(); +} +}]; } def Availability : InheritableAttr { @@ -911,6 +927,17 @@ def Const : InheritableAttr { let Documentation = [Undocumented]; } +def ConstInit : InheritableAttr { + // This attribute does not have a C [[]] spelling because it requires the + // CPlusPlus language option. + let Spellings = [Keyword<"constinit">, + Clang<"require_constant_initialization", 0>]; + let Subjects = SubjectList<[GlobalVar], ErrorDiag>; + let Accessors = [Accessor<"isConstinit", [Keyword<"constinit">]>]; + let Documentation = [ConstInitDocs]; + let LangOpts = [CPlusPlus]; +} + def Constructor : InheritableAttr { let Spellings = [GCC<"constructor">]; let Args = [DefaultIntArgument<"Priority", 65535>]; @@ -1170,7 +1197,7 @@ def ExtVectorType : Attr { def FallThrough : StmtAttr { let Spellings = [CXX11<"", "fallthrough", 201603>, C2x<"", "fallthrough">, - CXX11<"clang", "fallthrough">]; + CXX11<"clang", "fallthrough">, GCC<"fallthrough">]; // let Subjects = [NullStmt]; let Documentation = [FallthroughDocs]; } @@ -1935,15 +1962,6 @@ def ReqdWorkGroupSize : InheritableAttr { let Documentation = [Undocumented]; } -def RequireConstantInit : InheritableAttr { - // This attribute does not have a C [[]] spelling because it requires the - // CPlusPlus language option. - let Spellings = [Clang<"require_constant_initialization", 0>]; - let Subjects = SubjectList<[GlobalVar], ErrorDiag>; - let Documentation = [RequireConstantInitDocs]; - let LangOpts = [CPlusPlus]; -} - def WorkGroupSizeHint : InheritableAttr { // Does not have a [[]] spelling because it is an OpenCL-related attribute. let Spellings = [GNU<"work_group_size_hint">]; @@ -2002,6 +2020,14 @@ def PragmaClangRodataSection : InheritableAttr { let Documentation = [Undocumented]; } +def PragmaClangRelroSection : InheritableAttr { + // This attribute has no spellings as it is only ever created implicitly. + let Spellings = []; + let Args = [StringArgument<"Name">]; + let Subjects = SubjectList<[GlobalVar], ErrorDiag>; + let Documentation = [Undocumented]; +} + def PragmaClangTextSection : InheritableAttr { // This attribute has no spellings as it is only ever created implicitly. let Spellings = []; @@ -2335,11 +2361,19 @@ def WarnUnused : InheritableAttr { } def WarnUnusedResult : InheritableAttr { - let Spellings = [CXX11<"", "nodiscard", 201603>, C2x<"", "nodiscard">, + let Spellings = [CXX11<"", "nodiscard", 201907>, C2x<"", "nodiscard">, CXX11<"clang", "warn_unused_result">, GCC<"warn_unused_result">]; let Subjects = SubjectList<[ObjCMethod, Enum, Record, FunctionLike]>; + let Args = [StringArgument<"Message", 1>]; let Documentation = [WarnUnusedResultsDocs]; + let AdditionalMembers = [{ + // Check whether this the C++11 nodiscard version, even in non C++11 + // spellings. + bool IsCXX11NoDiscard() const { + return this->getSemanticSpelling() == CXX11_nodiscard; + } + }]; } def Weak : InheritableAttr { @@ -2428,6 +2462,12 @@ def NoSanitizeSpecific : InheritableAttr { let ASTNode = 0; } +def CFICanonicalJumpTable : InheritableAttr { + let Spellings = [Clang<"cfi_canonical_jump_table">]; + let Subjects = SubjectList<[Function], ErrorDiag>; + let Documentation = [CFICanonicalJumpTableDocs]; +} + // C/C++ Thread safety attributes (e.g. for deadlock, data race checking) // Not all of these attributes will be given a [[]] spelling. The attributes // which require access to function parameter names cannot use the [[]] spelling @@ -2788,6 +2828,20 @@ def TypeTagForDatatype : InheritableAttr { let Documentation = [TypeTagForDatatypeDocs]; } +def Owner : InheritableAttr { + let Spellings = [CXX11<"gsl", "Owner">]; + let Subjects = SubjectList<[Struct]>; + let Args = [TypeArgument<"DerefType", /*opt=*/1>]; + let Documentation = [LifetimeOwnerDocs]; +} + +def Pointer : InheritableAttr { + let Spellings = [CXX11<"gsl", "Pointer">]; + let Subjects = SubjectList<[Struct]>; + let Args = [TypeArgument<"DerefType", /*opt=*/1>]; + let Documentation = [LifetimePointerDocs]; +} + // Microsoft-related attributes def MSNoVTable : InheritableAttr, TargetSpecificAttr { @@ -2981,10 +3035,12 @@ def LoopHint : Attr { let Args = [EnumArgument<"Option", "OptionType", ["vectorize", "vectorize_width", "interleave", "interleave_count", "unroll", "unroll_count", "unroll_and_jam", "unroll_and_jam_count", - "pipeline", "pipeline_initiation_interval", "distribute"], + "pipeline", "pipeline_initiation_interval", "distribute", + "vectorize_predicate"], ["Vectorize", "VectorizeWidth", "Interleave", "InterleaveCount", "Unroll", "UnrollCount", "UnrollAndJam", "UnrollAndJamCount", - "PipelineDisabled", "PipelineInitiationInterval", "Distribute"]>, + "PipelineDisabled", "PipelineInitiationInterval", "Distribute", + "VectorizePredicate"]>, EnumArgument<"State", "LoopHintState", ["enable", "disable", "numeric", "assume_safety", "full"], ["Enable", "Disable", "Numeric", "AssumeSafety", "Full"]>, @@ -3004,12 +3060,13 @@ def LoopHint : Attr { case PipelineDisabled: return "pipeline"; case PipelineInitiationInterval: return "pipeline_initiation_interval"; case Distribute: return "distribute"; + case VectorizePredicate: return "vectorize_predicate"; } llvm_unreachable("Unhandled LoopHint option."); } void printPrettyPragma(raw_ostream &OS, const PrintingPolicy &Policy) const { - unsigned SpellingIndex = getSpellingListIndex(); + unsigned SpellingIndex = getAttributeSpellingListIndex(); // For "#pragma unroll" and "#pragma nounroll" the string "unroll" or // "nounroll" is already emitted as the pragma name. if (SpellingIndex == Pragma_nounroll || SpellingIndex == Pragma_nounroll_and_jam) @@ -3045,7 +3102,7 @@ def LoopHint : Attr { // Return a string suitable for identifying this attribute in diagnostics. std::string getDiagnosticName(const PrintingPolicy &Policy) const { - unsigned SpellingIndex = getSpellingListIndex(); + unsigned SpellingIndex = getAttributeSpellingListIndex(); if (SpellingIndex == Pragma_nounroll) return "#pragma nounroll"; else if (SpellingIndex == Pragma_unroll) @@ -3176,11 +3233,16 @@ def OMPDeclareTargetDecl : InheritableAttr { let Args = [ EnumArgument<"MapType", "MapTypeTy", [ "to", "link" ], - [ "MT_To", "MT_Link" ]> + [ "MT_To", "MT_Link" ]>, + EnumArgument<"DevType", "DevTypeTy", + [ "host", "nohost", "any" ], + [ "DT_Host", "DT_NoHost", "DT_Any" ]> ]; let AdditionalMembers = [{ void printPrettyPragma(raw_ostream &OS, const PrintingPolicy &Policy) const { // Use fake syntax because it is for testing and debugging purpose only. + if (getDevType() != DT_Any) + OS << " device_type(" << ConvertDevTypeTyToStr(getDevType()) << ")"; if (getMapType() != MT_To) OS << ' ' << ConvertMapTypeTyToStr(getMapType()); } @@ -3193,6 +3255,14 @@ def OMPDeclareTargetDecl : InheritableAttr { return llvm::None; } + static llvm::Optional getDeviceType(const ValueDecl *VD) { + if (!VD->hasAttrs()) + return llvm::None; + if (const auto *Attr = VD->getAttr()) + return Attr->getDevType(); + + return llvm::None; + } }]; } @@ -3219,6 +3289,82 @@ def OMPAllocateDecl : InheritableAttr { let Documentation = [Undocumented]; } +def OMPDeclareVariant : InheritableAttr { + let Spellings = [Pragma<"omp", "declare variant">]; + let Subjects = SubjectList<[Function]>; + let SemaHandler = 0; + let HasCustomParsing = 1; + let InheritEvenIfAlreadyPresent = 1; + let Documentation = [OMPDeclareVariantDocs]; + let Args = [ + ExprArgument<"VariantFuncRef">, + ExprArgument<"Score">, + EnumArgument<"CtxSelectorSet", "CtxSelectorSetType", + [ "", "implementation" + ], + [ + "CtxSetUnknown", "CtxSetImplementation" + ]>, + EnumArgument<"CtxScore", "ScoreType", + [ "", "score" + ], + [ + "ScoreUnknown", "ScoreSpecified" + ]>, + EnumArgument<"CtxSelector", "CtxSelectorType", + [ "", "vendor" + ], + [ + "CtxUnknown", "CtxVendor" + ]>, + VariadicStringArgument<"ImplVendors"> + ]; + let AdditionalMembers = [{ + void printScore(raw_ostream & OS, const PrintingPolicy &Policy) const { + if (const Expr *E = getScore()) { + OS << "score("; + E->printPretty(OS, nullptr, Policy); + OS << "):"; + } + } + void printPrettyPragma(raw_ostream & OS, const PrintingPolicy &Policy) + const { + assert(getCtxSelectorSet() != CtxSetUnknown && + getCtxSelector() != CtxUnknown && "Unknown context selector."); + if (const Expr *E = getVariantFuncRef()) { + OS << "("; + E->printPretty(OS, nullptr, Policy); + OS << ")"; + } + // TODO: add printing of real context selectors. + OS << " match("; + switch (getCtxSelectorSet()) { + case CtxSetImplementation: + OS << "implementation={"; + switch (getCtxSelector()) { + case CtxVendor: + OS << "vendor("; + printScore(OS, Policy); + if (implVendors_size() > 0) { + OS << *implVendors(). begin(); + for (StringRef VendorName : llvm::drop_begin(implVendors(), 1)) + OS << ", " << VendorName; + } + OS << ")"; + break; + case CtxUnknown: + llvm_unreachable("Unknown context selector."); + } + OS << "}"; + break; + case CtxSetUnknown: + llvm_unreachable("Unknown context selector set."); + } + OS << ")"; + } + }]; +} + def InternalLinkage : InheritableAttr { let Spellings = [Clang<"internal_linkage">]; let Subjects = SubjectList<[Var, Function, CXXRecord]>; diff --git a/include/clang/Basic/AttrDocs.td b/include/clang/Basic/AttrDocs.td index fac6116057dc2..114a9856c5fb4 100644 --- a/include/clang/Basic/AttrDocs.td +++ b/include/clang/Basic/AttrDocs.td @@ -1408,15 +1408,17 @@ all optional, but the attribute has to have at least one clause. }]; } -def RequireConstantInitDocs : Documentation { +def ConstInitDocs : Documentation { let Category = DocCatVariable; + let Heading = "require_constant_initialization, constinit (C++20)"; let Content = [{ This attribute specifies that the variable to which it is attached is intended to have a `constant initializer `_ according to the rules of [basic.start.static]. The variable is required to have static or thread storage duration. If the initialization of the variable is not a constant initializer an error will be produced. This attribute may -only be used in C++. +only be used in C++; the ``constinit`` spelling is only accepted in C++20 +onwards. Note that in C++03 strict constant expression checking is not done. Instead the attribute reports if Clang can emit the variable as a constant, even if it's @@ -1431,6 +1433,12 @@ for constant initialization have been met. Since these requirements change between dialects and have subtle pitfalls it's important to fail fast instead of silently falling back on dynamic initialization. +The first use of the attribute on a variable must be part of, or precede, the +initializing declaration of the variable. C++20 requires the ``constinit`` +spelling of the attribute to be present on the initializing declaration if it +is used anywhere. The other spellings can be specified on a forward declaration +and omitted on a later initializing declaration. + .. code-block:: c++ // -std=c++14 @@ -1482,6 +1490,13 @@ generated when a function or its return type is marked with ``[[nodiscard]]`` potentially-evaluated discarded-value expression that is not explicitly cast to `void`. +A string literal may optionally be provided to the attribute, which will be +reproduced in any resulting diagnostics. Redeclarations using different forms +of the attribute (with or without the string literal or with different string +literal contents) are allowed. If there are redeclarations of the entity with +differing string literals, it is unspecified which one will be used by Clang +in any resulting diagnostics. + .. code-block: c++ struct [[nodiscard]] error_info { /*...*/ }; error_info enable_missile_safety_mode(); @@ -1493,6 +1508,33 @@ potentially-evaluated discarded-value expression that is not explicitly cast to } error_info &foo(); void f() { foo(); } // Does not diagnose, error_info is a reference. + +Additionally, discarded temporaries resulting from a call to a constructor +marked with ``[[nodiscard]]`` or a constructor of a type marked +``[[nodiscard]]`` will also diagnose. This also applies to type conversions that +use the annotated ``[[nodiscard]]`` constructor or result in an annotated type. + +.. code-block: c++ + struct [[nodiscard]] marked_type {/*..*/ }; + struct marked_ctor { + [[nodiscard]] marked_ctor(); + marked_ctor(int); + }; + + struct S { + operator marked_type() const; + [[nodiscard]] operator int() const; + }; + + void usages() { + marked_type(); // diagnoses. + marked_ctor(); // diagnoses. + marked_ctor(3); // Does not diagnose, int constructor isn't marked nodiscard. + + S s; + static_cast(s); // diagnoses + (int)s; // diagnoses + } }]; } @@ -2187,6 +2229,18 @@ to avoid false positives in other places. }]; } +def CFICanonicalJumpTableDocs : Documentation { + let Category = DocCatFunction; + let Heading = "cfi_canonical_jump_table"; + let Content = [{ +.. _langext-cfi_canonical_jump_table: + +Use ``__attribute__((cfi_canonical_jump_table))`` on a function declaration to +make the function's CFI jump table canonical. See :ref:`the CFI documentation +` for more details. + }]; +} + def DocCatTypeSafety : DocumentationCategory<"Type Safety Checking"> { let Content = [{ Clang supports additional attributes to enable checking type safety properties @@ -2504,6 +2558,30 @@ manipulating bits of the enumerator when issuing warnings. }]; } +def AsmLabelDocs : Documentation { + let Category = DocCatDecl; + let Content = [{ +This attribute can be used on a function or variable to specify its symbol name. + +On some targets, all C symbols are prefixed by default with a single character, typically ``_``. This was done historically to distinguish them from symbols used by other languages. (This prefix is also added to the standard Itanium C++ ABI prefix on "mangled" symbol names, so that e.g. on such targets the true symbol name for a C++ variable declared as ``int cppvar;`` would be ``__Z6cppvar``; note the two underscores.) This prefix is *not* added to the symbol names specified by the ``asm`` attribute; programmers wishing to match a C symbol name must compensate for this. + +For example, consider the following C code: + +.. code-block:: c + + int var1 asm("altvar") = 1; // "altvar" in symbol table. + int var2 = 1; // "_var2" in symbol table. + + void func1(void) asm("altfunc"); + void func1(void) {} // "altfunc" in symbol table. + void func2(void) {} // "_func2" in symbol table. + +Clang's implementation of this attribute is compatible with GCC's, `documented here `_. + +While it is possible to use this attribute to name a special symbol used internally by the compiler, such as an LLVM intrinsic, this is neither recommended nor supported and may cause the compiler to crash or miscompile. Users who wish to gain access to intrinsic behavior are strongly encouraged to request new builtin functions. + }]; +} + def EnumExtensibilityDocs : Documentation { let Category = DocCatDecl; let Content = [{ @@ -2583,7 +2661,7 @@ is retained by the return value of the annotated function It is only supported in C++. This attribute provides an experimental implementation of the facility -described in the C++ committee paper [http://wg21.link/p0936r0](P0936R0), +described in the C++ committee paper `P0936R0 `_, and is subject to change as the design of the corresponding functionality changes. }]; @@ -2709,9 +2787,10 @@ def LoopHintDocs : Documentation { let Content = [{ The ``#pragma clang loop`` directive allows loop optimization hints to be specified for the subsequent loop. The directive allows pipelining to be -disabled, or vectorization, interleaving, and unrolling to be enabled or disabled. -Vector width, interleave count, unrolling count, and the initiation interval -for pipelining can be explicitly specified. See `language extensions +disabled, or vectorization, vector predication, interleaving, and unrolling to +be enabled or disabled. Vector width, vector predication, interleave count, +unrolling count, and the initiation interval for pipelining can be explicitly +specified. See `language extensions `_ for details. }]; @@ -3129,6 +3208,55 @@ The syntax of the declare target directive is as follows: #pragma omp declare target new-line declarations-definition-seq #pragma omp end declare target new-line + +or + + .. code-block:: c + + #pragma omp declare target (extended-list) new-line + +or + + .. code-block:: c + + #pragma omp declare target clause[ [,] clause ... ] new-line + +where clause is one of the following: + + + .. code-block:: c + + to(extended-list) + link(list) + device_type(host | nohost | any) + }]; +} + +def OMPDeclareVariantDocs : Documentation { + let Category = DocCatFunction; + let Heading = "#pragma omp declare variant"; + let Content = [{ +The `declare variant` directive declares a specialized variant of a base + function and specifies the context in which that specialized variant is used. + The declare variant directive is a declarative directive. +The syntax of the `declare variant` construct is as follows: + + .. code-block:: none + + #pragma omp declare variant(variant-func-id) clause new-line + [#pragma omp declare variant(variant-func-id) clause new-line] + [...] + function definition or declaration + +where clause is one of the following: + + .. code-block:: none + + match(context-selector-specification) + +and where `variant-func-id` is the name of a function variant that is either a + base language identifier or, for C++, a template-id. + }]; } @@ -4194,4 +4322,72 @@ be accessed on both device side and host side. It has external linkage and is not initialized on device side. It has internal linkage and is initialized by the initializer on host side. }]; -} \ No newline at end of file +} + +def LifetimeOwnerDocs : Documentation { + let Category = DocCatDecl; + let Content = [{ +.. Note:: This attribute is experimental and its effect on analysis is subject to change in + a future version of clang. + +The attribute ``[[gsl::Owner(T)]]`` applies to structs and classes that own an +object of type ``T``: + +.. code-block:: c++ + + class [[gsl::Owner(int)]] IntOwner { + private: + int value; + public: + int *getInt() { return &value; } + }; + +The argument ``T`` is optional and is ignored. +This attribute may be used by analysis tools and has no effect on code +generation. + +See Pointer_ for an example. +}]; +} + +def LifetimePointerDocs : Documentation { + let Category = DocCatDecl; + let Content = [{ +.. Note:: This attribute is experimental and its effect on analysis is subject to change in + a future version of clang. + +The attribute ``[[gsl::Pointer(T)]]`` applies to structs and classes that behave +like pointers to an object of type ``T``: + +.. code-block:: c++ + + class [[gsl::Pointer(int)]] IntPointer { + private: + int *valuePointer; + public: + int *getInt() { return &valuePointer; } + }; + +The argument ``T`` is optional and is ignored. +This attribute may be used by analysis tools and has no effect on code +generation. + +Example: +When constructing an instance of a class annotated like this (a Pointer) from +an instance of a class annotated with ``[[gsl::Owner]]`` (an Owner), +then the analysis will consider the Pointer to point inside the Owner. +When the Owner's lifetime ends, it will consider the Pointer to be dangling. + +.. code-block:: c++ + + int f() { + IntPointer P; + if (true) { + IntOwner O(7); + P = IntPointer(O); // P "points into" O + } // P is dangling + return P.get(); // error: Using a dangling Pointer. + } + +}]; +} diff --git a/include/clang/Basic/AttributeCommonInfo.h b/include/clang/Basic/AttributeCommonInfo.h new file mode 100644 index 0000000000000..545e7e9a2b47e --- /dev/null +++ b/include/clang/Basic/AttributeCommonInfo.h @@ -0,0 +1,190 @@ +//======- AttributeCommonInfo.h - Base info about Attributes-----*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines the AttributeCommonInfo type, which is the base for a +// ParsedAttr and is used by Attr as a way to share info between the two. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_BASIC_ATTRIBUTECOMMONINFO_H +#define LLVM_CLANG_BASIC_ATTRIBUTECOMMONINFO_H +#include "clang/Basic/SourceLocation.h" + +namespace clang { +class IdentifierInfo; +class ASTRecordWriter; + +class AttributeCommonInfo { +public: + /// The style used to specify an attribute. + enum Syntax { + /// __attribute__((...)) + AS_GNU, + + /// [[...]] + AS_CXX11, + + /// [[...]] + AS_C2x, + + /// __declspec(...) + AS_Declspec, + + /// [uuid("...")] class Foo + AS_Microsoft, + + /// __ptr16, alignas(...), etc. + AS_Keyword, + + /// #pragma ... + AS_Pragma, + + // Note TableGen depends on the order above. Do not add or change the order + // without adding related code to TableGen/ClangAttrEmitter.cpp. + /// Context-sensitive version of a keyword attribute. + AS_ContextSensitiveKeyword, + }; + enum Kind { +#define PARSED_ATTR(NAME) AT_##NAME, +#include "clang/Sema/AttrParsedAttrList.inc" +#undef PARSED_ATTR + NoSemaHandlerAttribute, + IgnoredAttribute, + UnknownAttribute, + }; + +private: + const IdentifierInfo *AttrName = nullptr; + const IdentifierInfo *ScopeName = nullptr; + SourceRange AttrRange; + const SourceLocation ScopeLoc; + // Corresponds to the Kind enum. + unsigned AttrKind : 16; + /// Corresponds to the Syntax enum. + unsigned SyntaxUsed : 3; + unsigned SpellingIndex : 4; + +protected: + static constexpr unsigned SpellingNotCalculated = 0xf; + +public: + AttributeCommonInfo(SourceRange AttrRange) + : AttrRange(AttrRange), ScopeLoc(), AttrKind(0), SyntaxUsed(0), + SpellingIndex(SpellingNotCalculated) {} + + AttributeCommonInfo(SourceLocation AttrLoc) + : AttrRange(AttrLoc), ScopeLoc(), AttrKind(0), SyntaxUsed(0), + SpellingIndex(SpellingNotCalculated) {} + + AttributeCommonInfo(const IdentifierInfo *AttrName, + const IdentifierInfo *ScopeName, SourceRange AttrRange, + SourceLocation ScopeLoc, Syntax SyntaxUsed) + : AttrName(AttrName), ScopeName(ScopeName), AttrRange(AttrRange), + ScopeLoc(ScopeLoc), + AttrKind(getParsedKind(AttrName, ScopeName, SyntaxUsed)), + SyntaxUsed(SyntaxUsed), SpellingIndex(SpellingNotCalculated) {} + + AttributeCommonInfo(const IdentifierInfo *AttrName, + const IdentifierInfo *ScopeName, SourceRange AttrRange, + SourceLocation ScopeLoc, Kind AttrKind, Syntax SyntaxUsed) + : AttrName(AttrName), ScopeName(ScopeName), AttrRange(AttrRange), + ScopeLoc(ScopeLoc), AttrKind(AttrKind), SyntaxUsed(SyntaxUsed), + SpellingIndex(SpellingNotCalculated) {} + + AttributeCommonInfo(const IdentifierInfo *AttrName, + const IdentifierInfo *ScopeName, SourceRange AttrRange, + SourceLocation ScopeLoc, Kind AttrKind, Syntax SyntaxUsed, + unsigned Spelling) + : AttrName(AttrName), ScopeName(ScopeName), AttrRange(AttrRange), + ScopeLoc(ScopeLoc), AttrKind(AttrKind), SyntaxUsed(SyntaxUsed), + SpellingIndex(Spelling) {} + + AttributeCommonInfo(const IdentifierInfo *AttrName, SourceRange AttrRange, + Syntax SyntaxUsed) + : AttrName(AttrName), ScopeName(nullptr), AttrRange(AttrRange), + ScopeLoc(), AttrKind(getParsedKind(AttrName, ScopeName, SyntaxUsed)), + SyntaxUsed(SyntaxUsed), SpellingIndex(SpellingNotCalculated) {} + + AttributeCommonInfo(SourceRange AttrRange, Kind K, Syntax SyntaxUsed) + : AttrName(nullptr), ScopeName(nullptr), AttrRange(AttrRange), ScopeLoc(), + AttrKind(K), SyntaxUsed(SyntaxUsed), + SpellingIndex(SpellingNotCalculated) {} + + AttributeCommonInfo(SourceRange AttrRange, Kind K, Syntax SyntaxUsed, + unsigned Spelling) + : AttrName(nullptr), ScopeName(nullptr), AttrRange(AttrRange), ScopeLoc(), + AttrKind(K), SyntaxUsed(SyntaxUsed), SpellingIndex(Spelling) {} + + AttributeCommonInfo(AttributeCommonInfo &&) = default; + AttributeCommonInfo(const AttributeCommonInfo &) = default; + + Kind getParsedKind() const { return Kind(AttrKind); } + Syntax getSyntax() const { return Syntax(SyntaxUsed); } + const IdentifierInfo *getAttrName() const { return AttrName; } + SourceLocation getLoc() const { return AttrRange.getBegin(); } + SourceRange getRange() const { return AttrRange; } + void setRange(SourceRange R) { AttrRange = R; } + + bool hasScope() const { return ScopeName; } + const IdentifierInfo *getScopeName() const { return ScopeName; } + SourceLocation getScopeLoc() const { return ScopeLoc; } + + bool isDeclspecAttribute() const { return SyntaxUsed == AS_Declspec; } + bool isMicrosoftAttribute() const { return SyntaxUsed == AS_Microsoft; } + + bool isGNUScope() const; + + bool isAlignasAttribute() const { + // FIXME: Use a better mechanism to determine this. + return getParsedKind() == AT_Aligned && isKeywordAttribute(); + } + + bool isCXX11Attribute() const { + return SyntaxUsed == AS_CXX11 || isAlignasAttribute(); + } + + bool isC2xAttribute() const { return SyntaxUsed == AS_C2x; } + + bool isKeywordAttribute() const { + return SyntaxUsed == AS_Keyword || SyntaxUsed == AS_ContextSensitiveKeyword; + } + + bool isContextSensitiveKeywordAttribute() const { + return SyntaxUsed == AS_ContextSensitiveKeyword; + } + + unsigned getAttributeSpellingListIndex() const { + assert((isAttributeSpellingListCalculated() || AttrName) && + "Spelling cannot be found"); + return isAttributeSpellingListCalculated() + ? SpellingIndex + : calculateAttributeSpellingListIndex(); + } + void setAttributeSpellingListIndex(unsigned V) { SpellingIndex = V; } + + static Kind getParsedKind(const IdentifierInfo *Name, + const IdentifierInfo *Scope, Syntax SyntaxUsed); + +private: + /// Get an index into the attribute spelling list + /// defined in Attr.td. This index is used by an attribute + /// to pretty print itself. + unsigned calculateAttributeSpellingListIndex() const; + + friend class clang::ASTRecordWriter; + // Used exclusively by ASTDeclWriter to get the raw spelling list state. + unsigned getAttributeSpellingListIndexRaw() const { return SpellingIndex; } + +protected: + bool isAttributeSpellingListCalculated() const { + return SpellingIndex != SpellingNotCalculated; + } +}; +} // namespace clang + +#endif // LLVM_CLANG_BASIC_ATTRIBUTECOMMONINFO_H diff --git a/include/clang/Basic/Builtins.def b/include/clang/Basic/Builtins.def index 984e607a2fc45..76e3b03de833b 100644 --- a/include/clang/Basic/Builtins.def +++ b/include/clang/Basic/Builtins.def @@ -113,14 +113,17 @@ BUILTIN(__builtin_atan2l, "LdLdLd", "Fne") BUILTIN(__builtin_abs , "ii" , "ncF") BUILTIN(__builtin_copysign, "ddd", "ncF") BUILTIN(__builtin_copysignf, "fff", "ncF") +BUILTIN(__builtin_copysignf16, "hhh", "ncF") BUILTIN(__builtin_copysignl, "LdLdLd", "ncF") BUILTIN(__builtin_copysignf128, "LLdLLdLLd", "ncF") BUILTIN(__builtin_fabs , "dd" , "ncF") BUILTIN(__builtin_fabsf, "ff" , "ncF") BUILTIN(__builtin_fabsl, "LdLd", "ncF") +BUILTIN(__builtin_fabsf16, "hh" , "ncF") BUILTIN(__builtin_fabsf128, "LLdLLd", "ncF") BUILTIN(__builtin_fmod , "ddd" , "Fne") BUILTIN(__builtin_fmodf, "fff" , "Fne") +BUILTIN(__builtin_fmodf16, "hhh" , "Fne") BUILTIN(__builtin_fmodl, "LdLdLd", "Fne") BUILTIN(__builtin_frexp , "ddi*" , "Fn") BUILTIN(__builtin_frexpf, "ffi*" , "Fn") @@ -154,6 +157,7 @@ BUILTIN(__builtin_powif, "ffi" , "Fnc") BUILTIN(__builtin_powil, "LdLdi", "Fnc") BUILTIN(__builtin_pow , "ddd" , "Fne") BUILTIN(__builtin_powf, "fff" , "Fne") +BUILTIN(__builtin_powf16, "hhh" , "Fne") BUILTIN(__builtin_powl, "LdLdLd", "Fne") // Standard unary libc/libm functions with double/float/long double variants: @@ -180,9 +184,11 @@ BUILTIN(__builtin_cbrtf, "ff", "Fnc") BUILTIN(__builtin_cbrtl, "LdLd", "Fnc") BUILTIN(__builtin_ceil , "dd" , "Fnc") BUILTIN(__builtin_ceilf, "ff" , "Fnc") +BUILTIN(__builtin_ceilf16, "hh" , "Fnc") BUILTIN(__builtin_ceill, "LdLd", "Fnc") BUILTIN(__builtin_cos , "dd" , "Fne") BUILTIN(__builtin_cosf, "ff" , "Fne") +BUILTIN(__builtin_cosf16, "hh" , "Fne") BUILTIN(__builtin_cosh , "dd" , "Fne") BUILTIN(__builtin_coshf, "ff" , "Fne") BUILTIN(__builtin_coshl, "LdLd", "Fne") @@ -195,9 +201,11 @@ BUILTIN(__builtin_erfcf, "ff", "Fne") BUILTIN(__builtin_erfcl, "LdLd", "Fne") BUILTIN(__builtin_exp , "dd" , "Fne") BUILTIN(__builtin_expf, "ff" , "Fne") +BUILTIN(__builtin_expf16, "hh" , "Fne") BUILTIN(__builtin_expl, "LdLd", "Fne") BUILTIN(__builtin_exp2 , "dd" , "Fne") BUILTIN(__builtin_exp2f, "ff" , "Fne") +BUILTIN(__builtin_exp2f16, "hh" , "Fne") BUILTIN(__builtin_exp2l, "LdLd", "Fne") BUILTIN(__builtin_expm1 , "dd", "Fne") BUILTIN(__builtin_expm1f, "ff", "Fne") @@ -207,15 +215,19 @@ BUILTIN(__builtin_fdimf, "fff", "Fne") BUILTIN(__builtin_fdiml, "LdLdLd", "Fne") BUILTIN(__builtin_floor , "dd" , "Fnc") BUILTIN(__builtin_floorf, "ff" , "Fnc") +BUILTIN(__builtin_floorf16, "hh" , "Fnc") BUILTIN(__builtin_floorl, "LdLd", "Fnc") BUILTIN(__builtin_fma, "dddd", "Fne") BUILTIN(__builtin_fmaf, "ffff", "Fne") +BUILTIN(__builtin_fmaf16, "hhhh", "Fne") BUILTIN(__builtin_fmal, "LdLdLdLd", "Fne") BUILTIN(__builtin_fmax, "ddd", "Fnc") BUILTIN(__builtin_fmaxf, "fff", "Fnc") +BUILTIN(__builtin_fmaxf16, "hhh", "Fnc") BUILTIN(__builtin_fmaxl, "LdLdLd", "Fnc") BUILTIN(__builtin_fmin, "ddd", "Fnc") BUILTIN(__builtin_fminf, "fff", "Fnc") +BUILTIN(__builtin_fminf16, "hhh", "Fnc") BUILTIN(__builtin_fminl, "LdLdLd", "Fnc") BUILTIN(__builtin_hypot , "ddd" , "Fne") BUILTIN(__builtin_hypotf, "fff" , "Fne") @@ -235,17 +247,20 @@ BUILTIN(__builtin_llroundl, "LLiLd", "Fne") BUILTIN(__builtin_log , "dd" , "Fne") BUILTIN(__builtin_log10 , "dd" , "Fne") BUILTIN(__builtin_log10f, "ff" , "Fne") +BUILTIN(__builtin_log10f16, "hh" , "Fne") BUILTIN(__builtin_log10l, "LdLd", "Fne") BUILTIN(__builtin_log1p , "dd" , "Fne") BUILTIN(__builtin_log1pf, "ff" , "Fne") BUILTIN(__builtin_log1pl, "LdLd", "Fne") BUILTIN(__builtin_log2, "dd" , "Fne") BUILTIN(__builtin_log2f, "ff" , "Fne") +BUILTIN(__builtin_log2f16, "hh" , "Fne") BUILTIN(__builtin_log2l, "LdLd" , "Fne") BUILTIN(__builtin_logb , "dd", "Fne") BUILTIN(__builtin_logbf, "ff", "Fne") BUILTIN(__builtin_logbl, "LdLd", "Fne") BUILTIN(__builtin_logf, "ff" , "Fne") +BUILTIN(__builtin_logf16, "hh" , "Fne") BUILTIN(__builtin_logl, "LdLd", "Fne") BUILTIN(__builtin_lrint , "Lid", "Fne") BUILTIN(__builtin_lrintf, "Lif", "Fne") @@ -270,9 +285,11 @@ BUILTIN(__builtin_remquof, "fffi*", "Fn") BUILTIN(__builtin_remquol, "LdLdLdi*", "Fn") BUILTIN(__builtin_rint , "dd", "Fnc") BUILTIN(__builtin_rintf, "ff", "Fnc") +BUILTIN(__builtin_rintf16, "hh", "Fnc") BUILTIN(__builtin_rintl, "LdLd", "Fnc") BUILTIN(__builtin_round, "dd" , "Fnc") BUILTIN(__builtin_roundf, "ff" , "Fnc") +BUILTIN(__builtin_roundf16, "hh" , "Fnc") BUILTIN(__builtin_roundl, "LdLd" , "Fnc") BUILTIN(__builtin_scalbln , "ddLi", "Fne") BUILTIN(__builtin_scalblnf, "ffLi", "Fne") @@ -282,12 +299,14 @@ BUILTIN(__builtin_scalbnf, "ffi", "Fne") BUILTIN(__builtin_scalbnl, "LdLdi", "Fne") BUILTIN(__builtin_sin , "dd" , "Fne") BUILTIN(__builtin_sinf, "ff" , "Fne") +BUILTIN(__builtin_sinf16, "hh" , "Fne") BUILTIN(__builtin_sinh , "dd" , "Fne") BUILTIN(__builtin_sinhf, "ff" , "Fne") BUILTIN(__builtin_sinhl, "LdLd", "Fne") BUILTIN(__builtin_sinl, "LdLd", "Fne") BUILTIN(__builtin_sqrt , "dd" , "Fne") BUILTIN(__builtin_sqrtf, "ff" , "Fne") +BUILTIN(__builtin_sqrtf16, "hh" , "Fne") BUILTIN(__builtin_sqrtl, "LdLd", "Fne") BUILTIN(__builtin_tan , "dd" , "Fne") BUILTIN(__builtin_tanf, "ff" , "Fne") @@ -301,6 +320,7 @@ BUILTIN(__builtin_tgammal, "LdLd", "Fne") BUILTIN(__builtin_trunc , "dd", "Fnc") BUILTIN(__builtin_truncf, "ff", "Fnc") BUILTIN(__builtin_truncl, "LdLd", "Fnc") +BUILTIN(__builtin_truncf16, "hh", "Fnc") // C99 complex builtins BUILTIN(__builtin_cabs, "dXd", "Fne") @@ -394,6 +414,7 @@ BUILTIN(__builtin_signbitl, "iLd", "Fnc") // Special FP builtins. BUILTIN(__builtin_canonicalize, "dd", "nc") BUILTIN(__builtin_canonicalizef, "ff", "nc") +BUILTIN(__builtin_canonicalizef16, "hh", "nc") BUILTIN(__builtin_canonicalizel, "LdLd", "nc") // Builtins for arithmetic. @@ -440,7 +461,7 @@ BUILTIN(__builtin_rotateleft64, "UWiUWiUWi", "nc") BUILTIN(__builtin_rotateright8, "UcUcUc", "nc") BUILTIN(__builtin_rotateright16, "UsUsUs", "nc") BUILTIN(__builtin_rotateright32, "UZiUZiUZi", "nc") -BUILTIN(__builtin_rotateright64, "UWiUWiWi", "nc") +BUILTIN(__builtin_rotateright64, "UWiUWiUWi", "nc") // Random GCC builtins BUILTIN(__builtin_constant_p, "i.", "nctu") @@ -984,9 +1005,7 @@ LIBBUILTIN(pthread_create, "", "fC<2,3>", "pthread.h", ALL_GNU_LANGUAGES) LIBBUILTIN(_setjmp, "iJ", "fj", "setjmp.h", ALL_LANGUAGES) LIBBUILTIN(__sigsetjmp, "iSJi", "fj", "setjmp.h", ALL_LANGUAGES) LIBBUILTIN(sigsetjmp, "iSJi", "fj", "setjmp.h", ALL_LANGUAGES) -LIBBUILTIN(setjmp_syscall, "iJ", "fj", "setjmp.h", ALL_LANGUAGES) LIBBUILTIN(savectx, "iJ", "fj", "setjmp.h", ALL_LANGUAGES) -LIBBUILTIN(qsetjmp, "iJ", "fj", "setjmp.h", ALL_LANGUAGES) LIBBUILTIN(getcontext, "iK*", "fj", "setjmp.h", ALL_LANGUAGES) LIBBUILTIN(_longjmp, "vJi", "fr", "setjmp.h", ALL_GNU_LANGUAGES) @@ -1449,7 +1468,7 @@ BUILTIN(__builtin_operator_new, "v*z", "tc") BUILTIN(__builtin_operator_delete, "vv*", "tn") BUILTIN(__builtin_char_memchr, "c*cC*iz", "n") BUILTIN(__builtin_dump_struct, "ivC*v*", "tn") -BUILTIN(__builtin_preserve_access_index, "vC*vC*", "nU") +BUILTIN(__builtin_preserve_access_index, "v.", "t") // Safestack builtins BUILTIN(__builtin___get_unsafe_stack_start, "v*", "Fn") diff --git a/include/clang/Basic/BuiltinsAArch64.def b/include/clang/Basic/BuiltinsAArch64.def index 7701ad98f4832..4df8d5a167621 100644 --- a/include/clang/Basic/BuiltinsAArch64.def +++ b/include/clang/Basic/BuiltinsAArch64.def @@ -91,12 +91,18 @@ LANGBUILTIN(__sevl, "v", "", ALL_MS_LANGUAGES) // Misc BUILTIN(__builtin_sponentry, "v*", "c") +// Transactional Memory Extension +BUILTIN(__builtin_arm_tstart, "WUi", "nj") +BUILTIN(__builtin_arm_tcommit, "v", "n") +BUILTIN(__builtin_arm_tcancel, "vWUIi", "n") +BUILTIN(__builtin_arm_ttest, "WUi", "nc") + TARGET_HEADER_BUILTIN(_BitScanForward, "UcUNi*UNi", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_BitScanReverse, "UcUNi*UNi", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_BitScanForward64, "UcUNi*ULLi", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_BitScanReverse64, "UcUNi*ULLi", "nh", "intrin.h", ALL_MS_LANGUAGES, "") -TARGET_HEADER_BUILTIN(_InterlockedAdd, "LiLiD*Li", "nh", "intrin.h", ALL_MS_LANGUAGES, "") +TARGET_HEADER_BUILTIN(_InterlockedAdd, "NiNiD*Ni", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedAnd64, "LLiLLiD*LLi", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedDecrement64, "LLiLLiD*", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedExchange64, "LLiLLiD*LLi", "nh", "intrin.h", ALL_MS_LANGUAGES, "") @@ -106,9 +112,9 @@ TARGET_HEADER_BUILTIN(_InterlockedIncrement64, "LLiLLiD*", "nh", "intrin.h" TARGET_HEADER_BUILTIN(_InterlockedOr64, "LLiLLiD*LLi", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedXor64, "LLiLLiD*LLi", "nh", "intrin.h", ALL_MS_LANGUAGES, "") -TARGET_HEADER_BUILTIN(_InterlockedExchangeAdd_acq, "LiLiD*Li", "nh", "intrin.h", ALL_MS_LANGUAGES, "") -TARGET_HEADER_BUILTIN(_InterlockedExchangeAdd_rel, "LiLiD*Li", "nh", "intrin.h", ALL_MS_LANGUAGES, "") -TARGET_HEADER_BUILTIN(_InterlockedExchangeAdd_nf, "LiLiD*Li", "nh", "intrin.h", ALL_MS_LANGUAGES, "") +TARGET_HEADER_BUILTIN(_InterlockedExchangeAdd_acq, "NiNiD*Ni", "nh", "intrin.h", ALL_MS_LANGUAGES, "") +TARGET_HEADER_BUILTIN(_InterlockedExchangeAdd_rel, "NiNiD*Ni", "nh", "intrin.h", ALL_MS_LANGUAGES, "") +TARGET_HEADER_BUILTIN(_InterlockedExchangeAdd_nf, "NiNiD*Ni", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedExchangeAdd8_acq, "ccD*c", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedExchangeAdd8_rel, "ccD*c", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedExchangeAdd8_nf, "ccD*c", "nh", "intrin.h", ALL_MS_LANGUAGES, "") @@ -125,9 +131,9 @@ TARGET_HEADER_BUILTIN(_InterlockedExchange8_rel, "ccD*c", "nh", "intrin.h TARGET_HEADER_BUILTIN(_InterlockedExchange16_acq, "ssD*s", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedExchange16_nf, "ssD*s", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedExchange16_rel, "ssD*s", "nh", "intrin.h", ALL_MS_LANGUAGES, "") -TARGET_HEADER_BUILTIN(_InterlockedExchange_acq, "LiLiD*Li", "nh", "intrin.h", ALL_MS_LANGUAGES, "") -TARGET_HEADER_BUILTIN(_InterlockedExchange_nf, "LiLiD*Li", "nh", "intrin.h", ALL_MS_LANGUAGES, "") -TARGET_HEADER_BUILTIN(_InterlockedExchange_rel, "LiLiD*Li", "nh", "intrin.h", ALL_MS_LANGUAGES, "") +TARGET_HEADER_BUILTIN(_InterlockedExchange_acq, "NiNiD*Ni", "nh", "intrin.h", ALL_MS_LANGUAGES, "") +TARGET_HEADER_BUILTIN(_InterlockedExchange_nf, "NiNiD*Ni", "nh", "intrin.h", ALL_MS_LANGUAGES, "") +TARGET_HEADER_BUILTIN(_InterlockedExchange_rel, "NiNiD*Ni", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedExchange64_acq, "LLiLLiD*LLi", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedExchange64_nf, "LLiLLiD*LLi", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedExchange64_rel, "LLiLLiD*LLi", "nh", "intrin.h", ALL_MS_LANGUAGES, "") @@ -138,9 +144,9 @@ TARGET_HEADER_BUILTIN(_InterlockedCompareExchange8_rel, "ccD*cc", "nh", TARGET_HEADER_BUILTIN(_InterlockedCompareExchange16_acq, "ssD*ss", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedCompareExchange16_nf, "ssD*ss", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedCompareExchange16_rel, "ssD*ss", "nh", "intrin.h", ALL_MS_LANGUAGES, "") -TARGET_HEADER_BUILTIN(_InterlockedCompareExchange_acq, "LiLiD*LiLi", "nh", "intrin.h", ALL_MS_LANGUAGES, "") -TARGET_HEADER_BUILTIN(_InterlockedCompareExchange_nf, "LiLiD*LiLi", "nh", "intrin.h", ALL_MS_LANGUAGES, "") -TARGET_HEADER_BUILTIN(_InterlockedCompareExchange_rel, "LiLiD*LiLi", "nh", "intrin.h", ALL_MS_LANGUAGES, "") +TARGET_HEADER_BUILTIN(_InterlockedCompareExchange_acq, "NiNiD*NiNi", "nh", "intrin.h", ALL_MS_LANGUAGES, "") +TARGET_HEADER_BUILTIN(_InterlockedCompareExchange_nf, "NiNiD*NiNi", "nh", "intrin.h", ALL_MS_LANGUAGES, "") +TARGET_HEADER_BUILTIN(_InterlockedCompareExchange_rel, "NiNiD*NiNi", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedCompareExchange64_acq, "LLiLLiD*LLiLLi", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedCompareExchange64_nf, "LLiLLiD*LLiLLi", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedCompareExchange64_rel, "LLiLLiD*LLiLLi", "nh", "intrin.h", ALL_MS_LANGUAGES, "") @@ -151,9 +157,9 @@ TARGET_HEADER_BUILTIN(_InterlockedOr8_rel, "ccD*c", "nh", "intrin.h", ALL TARGET_HEADER_BUILTIN(_InterlockedOr16_acq, "ssD*s", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedOr16_nf, "ssD*s", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedOr16_rel, "ssD*s", "nh", "intrin.h", ALL_MS_LANGUAGES, "") -TARGET_HEADER_BUILTIN(_InterlockedOr_acq, "LiLiD*Li", "nh", "intrin.h", ALL_MS_LANGUAGES, "") -TARGET_HEADER_BUILTIN(_InterlockedOr_nf, "LiLiD*Li", "nh", "intrin.h", ALL_MS_LANGUAGES, "") -TARGET_HEADER_BUILTIN(_InterlockedOr_rel, "LiLiD*Li", "nh", "intrin.h", ALL_MS_LANGUAGES, "") +TARGET_HEADER_BUILTIN(_InterlockedOr_acq, "NiNiD*Ni", "nh", "intrin.h", ALL_MS_LANGUAGES, "") +TARGET_HEADER_BUILTIN(_InterlockedOr_nf, "NiNiD*Ni", "nh", "intrin.h", ALL_MS_LANGUAGES, "") +TARGET_HEADER_BUILTIN(_InterlockedOr_rel, "NiNiD*Ni", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedOr64_acq, "LLiLLiD*LLi", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedOr64_nf, "LLiLLiD*LLi", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedOr64_rel, "LLiLLiD*LLi", "nh", "intrin.h", ALL_MS_LANGUAGES, "") @@ -164,9 +170,9 @@ TARGET_HEADER_BUILTIN(_InterlockedXor8_rel, "ccD*c", "nh", "intrin.h", AL TARGET_HEADER_BUILTIN(_InterlockedXor16_acq, "ssD*s", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedXor16_nf, "ssD*s", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedXor16_rel, "ssD*s", "nh", "intrin.h", ALL_MS_LANGUAGES, "") -TARGET_HEADER_BUILTIN(_InterlockedXor_acq, "LiLiD*Li", "nh", "intrin.h", ALL_MS_LANGUAGES, "") -TARGET_HEADER_BUILTIN(_InterlockedXor_nf, "LiLiD*Li", "nh", "intrin.h", ALL_MS_LANGUAGES, "") -TARGET_HEADER_BUILTIN(_InterlockedXor_rel, "LiLiD*Li", "nh", "intrin.h", ALL_MS_LANGUAGES, "") +TARGET_HEADER_BUILTIN(_InterlockedXor_acq, "NiNiD*Ni", "nh", "intrin.h", ALL_MS_LANGUAGES, "") +TARGET_HEADER_BUILTIN(_InterlockedXor_nf, "NiNiD*Ni", "nh", "intrin.h", ALL_MS_LANGUAGES, "") +TARGET_HEADER_BUILTIN(_InterlockedXor_rel, "NiNiD*Ni", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedXor64_acq, "LLiLLiD*LLi", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedXor64_nf, "LLiLLiD*LLi", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedXor64_rel, "LLiLLiD*LLi", "nh", "intrin.h", ALL_MS_LANGUAGES, "") @@ -177,9 +183,9 @@ TARGET_HEADER_BUILTIN(_InterlockedAnd8_rel, "ccD*c", "nh", "intrin.h", AL TARGET_HEADER_BUILTIN(_InterlockedAnd16_acq, "ssD*s", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedAnd16_nf, "ssD*s", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedAnd16_rel, "ssD*s", "nh", "intrin.h", ALL_MS_LANGUAGES, "") -TARGET_HEADER_BUILTIN(_InterlockedAnd_acq, "LiLiD*Li", "nh", "intrin.h", ALL_MS_LANGUAGES, "") -TARGET_HEADER_BUILTIN(_InterlockedAnd_nf, "LiLiD*Li", "nh", "intrin.h", ALL_MS_LANGUAGES, "") -TARGET_HEADER_BUILTIN(_InterlockedAnd_rel, "LiLiD*Li", "nh", "intrin.h", ALL_MS_LANGUAGES, "") +TARGET_HEADER_BUILTIN(_InterlockedAnd_acq, "NiNiD*Ni", "nh", "intrin.h", ALL_MS_LANGUAGES, "") +TARGET_HEADER_BUILTIN(_InterlockedAnd_nf, "NiNiD*Ni", "nh", "intrin.h", ALL_MS_LANGUAGES, "") +TARGET_HEADER_BUILTIN(_InterlockedAnd_rel, "NiNiD*Ni", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedAnd64_acq, "LLiLLiD*LLi", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedAnd64_nf, "LLiLLiD*LLi", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedAnd64_rel, "LLiLLiD*LLi", "nh", "intrin.h", ALL_MS_LANGUAGES, "") @@ -187,9 +193,9 @@ TARGET_HEADER_BUILTIN(_InterlockedAnd64_rel, "LLiLLiD*LLi", "nh", "intrin.h", AL TARGET_HEADER_BUILTIN(_InterlockedIncrement16_acq, "ssD*", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedIncrement16_nf, "ssD*", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedIncrement16_rel, "ssD*", "nh", "intrin.h", ALL_MS_LANGUAGES, "") -TARGET_HEADER_BUILTIN(_InterlockedIncrement_acq, "LiLiD*", "nh", "intrin.h", ALL_MS_LANGUAGES, "") -TARGET_HEADER_BUILTIN(_InterlockedIncrement_nf, "LiLiD*", "nh", "intrin.h", ALL_MS_LANGUAGES, "") -TARGET_HEADER_BUILTIN(_InterlockedIncrement_rel, "LiLiD*", "nh", "intrin.h", ALL_MS_LANGUAGES, "") +TARGET_HEADER_BUILTIN(_InterlockedIncrement_acq, "NiNiD*", "nh", "intrin.h", ALL_MS_LANGUAGES, "") +TARGET_HEADER_BUILTIN(_InterlockedIncrement_nf, "NiNiD*", "nh", "intrin.h", ALL_MS_LANGUAGES, "") +TARGET_HEADER_BUILTIN(_InterlockedIncrement_rel, "NiNiD*", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedIncrement64_acq, "LLiLLiD*", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedIncrement64_nf, "LLiLLiD*", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedIncrement64_rel, "LLiLLiD*", "nh", "intrin.h", ALL_MS_LANGUAGES, "") @@ -197,9 +203,9 @@ TARGET_HEADER_BUILTIN(_InterlockedIncrement64_rel, "LLiLLiD*", "nh", "intrin.h", TARGET_HEADER_BUILTIN(_InterlockedDecrement16_acq, "ssD*", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedDecrement16_nf, "ssD*", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedDecrement16_rel, "ssD*", "nh", "intrin.h", ALL_MS_LANGUAGES, "") -TARGET_HEADER_BUILTIN(_InterlockedDecrement_acq, "LiLiD*", "nh", "intrin.h", ALL_MS_LANGUAGES, "") -TARGET_HEADER_BUILTIN(_InterlockedDecrement_nf, "LiLiD*", "nh", "intrin.h", ALL_MS_LANGUAGES, "") -TARGET_HEADER_BUILTIN(_InterlockedDecrement_rel, "LiLiD*", "nh", "intrin.h", ALL_MS_LANGUAGES, "") +TARGET_HEADER_BUILTIN(_InterlockedDecrement_acq, "NiNiD*", "nh", "intrin.h", ALL_MS_LANGUAGES, "") +TARGET_HEADER_BUILTIN(_InterlockedDecrement_nf, "NiNiD*", "nh", "intrin.h", ALL_MS_LANGUAGES, "") +TARGET_HEADER_BUILTIN(_InterlockedDecrement_rel, "NiNiD*", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedDecrement64_acq, "LLiLLiD*", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedDecrement64_nf, "LLiLLiD*", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedDecrement64_rel, "LLiLLiD*", "nh", "intrin.h", ALL_MS_LANGUAGES, "") diff --git a/include/clang/Basic/BuiltinsAMDGPU.def b/include/clang/Basic/BuiltinsAMDGPU.def index 2f8fb9000a76a..9b3a0f96798f9 100644 --- a/include/clang/Basic/BuiltinsAMDGPU.def +++ b/include/clang/Basic/BuiltinsAMDGPU.def @@ -118,6 +118,13 @@ BUILTIN(__builtin_amdgcn_cvt_pknorm_u16, "E2Usff", "nc") BUILTIN(__builtin_amdgcn_cvt_pk_i16, "E2sii", "nc") BUILTIN(__builtin_amdgcn_cvt_pk_u16, "E2UsUiUi", "nc") BUILTIN(__builtin_amdgcn_cvt_pk_u8_f32, "UifUiUi", "nc") +BUILTIN(__builtin_amdgcn_sad_u8, "UiUiUiUi", "nc") +BUILTIN(__builtin_amdgcn_msad_u8, "UiUiUiUi", "nc") +BUILTIN(__builtin_amdgcn_sad_hi_u8, "UiUiUiUi", "nc") +BUILTIN(__builtin_amdgcn_sad_u16, "UiUiUiUi", "nc") +BUILTIN(__builtin_amdgcn_qsad_pk_u16_u8, "LUiLUiUiLUi", "nc") +BUILTIN(__builtin_amdgcn_mqsad_pk_u16_u8, "LUiLUiUiLUi", "nc") +BUILTIN(__builtin_amdgcn_mqsad_u32_u8, "V4UiLUiUiV4Ui", "nc") //===----------------------------------------------------------------------===// // CI+ only builtins. @@ -125,6 +132,8 @@ BUILTIN(__builtin_amdgcn_cvt_pk_u8_f32, "UifUiUi", "nc") TARGET_BUILTIN(__builtin_amdgcn_s_dcache_inv_vol, "v", "n", "ci-insts") TARGET_BUILTIN(__builtin_amdgcn_buffer_wbinvl1_vol, "v", "n", "ci-insts") TARGET_BUILTIN(__builtin_amdgcn_ds_gws_sema_release_all, "vUi", "n", "ci-insts") +TARGET_BUILTIN(__builtin_amdgcn_is_shared, "bvC*0", "nc", "flat-address-space") +TARGET_BUILTIN(__builtin_amdgcn_is_private, "bvC*0", "nc", "flat-address-space") //===----------------------------------------------------------------------===// // Interpolation builtins. diff --git a/include/clang/Basic/BuiltinsARM.def b/include/clang/Basic/BuiltinsARM.def index 3f0765115b1cc..19b51694aa22d 100644 --- a/include/clang/Basic/BuiltinsARM.def +++ b/include/clang/Basic/BuiltinsARM.def @@ -221,9 +221,9 @@ TARGET_HEADER_BUILTIN(_InterlockedIncrement64, "LLiLLiD*", "nh", "intrin.h" TARGET_HEADER_BUILTIN(_InterlockedOr64, "LLiLLiD*LLi", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedXor64, "LLiLLiD*LLi", "nh", "intrin.h", ALL_MS_LANGUAGES, "") -TARGET_HEADER_BUILTIN(_InterlockedExchangeAdd_acq, "LiLiD*Li", "nh", "intrin.h", ALL_MS_LANGUAGES, "") -TARGET_HEADER_BUILTIN(_InterlockedExchangeAdd_rel, "LiLiD*Li", "nh", "intrin.h", ALL_MS_LANGUAGES, "") -TARGET_HEADER_BUILTIN(_InterlockedExchangeAdd_nf, "LiLiD*Li", "nh", "intrin.h", ALL_MS_LANGUAGES, "") +TARGET_HEADER_BUILTIN(_InterlockedExchangeAdd_acq, "NiNiD*Ni", "nh", "intrin.h", ALL_MS_LANGUAGES, "") +TARGET_HEADER_BUILTIN(_InterlockedExchangeAdd_rel, "NiNiD*Ni", "nh", "intrin.h", ALL_MS_LANGUAGES, "") +TARGET_HEADER_BUILTIN(_InterlockedExchangeAdd_nf, "NiNiD*Ni", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedExchangeAdd8_acq, "ccD*c", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedExchangeAdd8_rel, "ccD*c", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedExchangeAdd8_nf, "ccD*c", "nh", "intrin.h", ALL_MS_LANGUAGES, "") @@ -240,9 +240,9 @@ TARGET_HEADER_BUILTIN(_InterlockedExchange8_rel, "ccD*c", "nh", "intrin.h TARGET_HEADER_BUILTIN(_InterlockedExchange16_acq, "ssD*s", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedExchange16_nf, "ssD*s", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedExchange16_rel, "ssD*s", "nh", "intrin.h", ALL_MS_LANGUAGES, "") -TARGET_HEADER_BUILTIN(_InterlockedExchange_acq, "LiLiD*Li", "nh", "intrin.h", ALL_MS_LANGUAGES, "") -TARGET_HEADER_BUILTIN(_InterlockedExchange_nf, "LiLiD*Li", "nh", "intrin.h", ALL_MS_LANGUAGES, "") -TARGET_HEADER_BUILTIN(_InterlockedExchange_rel, "LiLiD*Li", "nh", "intrin.h", ALL_MS_LANGUAGES, "") +TARGET_HEADER_BUILTIN(_InterlockedExchange_acq, "NiNiD*Ni", "nh", "intrin.h", ALL_MS_LANGUAGES, "") +TARGET_HEADER_BUILTIN(_InterlockedExchange_nf, "NiNiD*Ni", "nh", "intrin.h", ALL_MS_LANGUAGES, "") +TARGET_HEADER_BUILTIN(_InterlockedExchange_rel, "NiNiD*Ni", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedExchange64_acq, "LLiLLiD*LLi", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedExchange64_nf, "LLiLLiD*LLi", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedExchange64_rel, "LLiLLiD*LLi", "nh", "intrin.h", ALL_MS_LANGUAGES, "") @@ -253,9 +253,9 @@ TARGET_HEADER_BUILTIN(_InterlockedCompareExchange8_rel, "ccD*cc", "nh", TARGET_HEADER_BUILTIN(_InterlockedCompareExchange16_acq, "ssD*ss", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedCompareExchange16_nf, "ssD*ss", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedCompareExchange16_rel, "ssD*ss", "nh", "intrin.h", ALL_MS_LANGUAGES, "") -TARGET_HEADER_BUILTIN(_InterlockedCompareExchange_acq, "LiLiD*LiLi", "nh", "intrin.h", ALL_MS_LANGUAGES, "") -TARGET_HEADER_BUILTIN(_InterlockedCompareExchange_nf, "LiLiD*LiLi", "nh", "intrin.h", ALL_MS_LANGUAGES, "") -TARGET_HEADER_BUILTIN(_InterlockedCompareExchange_rel, "LiLiD*LiLi", "nh", "intrin.h", ALL_MS_LANGUAGES, "") +TARGET_HEADER_BUILTIN(_InterlockedCompareExchange_acq, "NiNiD*NiNi", "nh", "intrin.h", ALL_MS_LANGUAGES, "") +TARGET_HEADER_BUILTIN(_InterlockedCompareExchange_nf, "NiNiD*NiNi", "nh", "intrin.h", ALL_MS_LANGUAGES, "") +TARGET_HEADER_BUILTIN(_InterlockedCompareExchange_rel, "NiNiD*NiNi", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedCompareExchange64_acq, "LLiLLiD*LLiLLi", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedCompareExchange64_nf, "LLiLLiD*LLiLLi", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedCompareExchange64_rel, "LLiLLiD*LLiLLi", "nh", "intrin.h", ALL_MS_LANGUAGES, "") @@ -266,9 +266,9 @@ TARGET_HEADER_BUILTIN(_InterlockedOr8_rel, "ccD*c", "nh", "intrin.h", ALL TARGET_HEADER_BUILTIN(_InterlockedOr16_acq, "ssD*s", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedOr16_nf, "ssD*s", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedOr16_rel, "ssD*s", "nh", "intrin.h", ALL_MS_LANGUAGES, "") -TARGET_HEADER_BUILTIN(_InterlockedOr_acq, "LiLiD*Li", "nh", "intrin.h", ALL_MS_LANGUAGES, "") -TARGET_HEADER_BUILTIN(_InterlockedOr_nf, "LiLiD*Li", "nh", "intrin.h", ALL_MS_LANGUAGES, "") -TARGET_HEADER_BUILTIN(_InterlockedOr_rel, "LiLiD*Li", "nh", "intrin.h", ALL_MS_LANGUAGES, "") +TARGET_HEADER_BUILTIN(_InterlockedOr_acq, "NiNiD*Ni", "nh", "intrin.h", ALL_MS_LANGUAGES, "") +TARGET_HEADER_BUILTIN(_InterlockedOr_nf, "NiNiD*Ni", "nh", "intrin.h", ALL_MS_LANGUAGES, "") +TARGET_HEADER_BUILTIN(_InterlockedOr_rel, "NiNiD*Ni", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedOr64_acq, "LLiLLiD*LLi", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedOr64_nf, "LLiLLiD*LLi", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedOr64_rel, "LLiLLiD*LLi", "nh", "intrin.h", ALL_MS_LANGUAGES, "") @@ -279,9 +279,9 @@ TARGET_HEADER_BUILTIN(_InterlockedXor8_rel, "ccD*c", "nh", "intrin.h", AL TARGET_HEADER_BUILTIN(_InterlockedXor16_acq, "ssD*s", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedXor16_nf, "ssD*s", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedXor16_rel, "ssD*s", "nh", "intrin.h", ALL_MS_LANGUAGES, "") -TARGET_HEADER_BUILTIN(_InterlockedXor_acq, "LiLiD*Li", "nh", "intrin.h", ALL_MS_LANGUAGES, "") -TARGET_HEADER_BUILTIN(_InterlockedXor_nf, "LiLiD*Li", "nh", "intrin.h", ALL_MS_LANGUAGES, "") -TARGET_HEADER_BUILTIN(_InterlockedXor_rel, "LiLiD*Li", "nh", "intrin.h", ALL_MS_LANGUAGES, "") +TARGET_HEADER_BUILTIN(_InterlockedXor_acq, "NiNiD*Ni", "nh", "intrin.h", ALL_MS_LANGUAGES, "") +TARGET_HEADER_BUILTIN(_InterlockedXor_nf, "NiNiD*Ni", "nh", "intrin.h", ALL_MS_LANGUAGES, "") +TARGET_HEADER_BUILTIN(_InterlockedXor_rel, "NiNiD*Ni", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedXor64_acq, "LLiLLiD*LLi", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedXor64_nf, "LLiLLiD*LLi", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedXor64_rel, "LLiLLiD*LLi", "nh", "intrin.h", ALL_MS_LANGUAGES, "") @@ -292,9 +292,9 @@ TARGET_HEADER_BUILTIN(_InterlockedAnd8_rel, "ccD*c", "nh", "intrin.h", AL TARGET_HEADER_BUILTIN(_InterlockedAnd16_acq, "ssD*s", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedAnd16_nf, "ssD*s", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedAnd16_rel, "ssD*s", "nh", "intrin.h", ALL_MS_LANGUAGES, "") -TARGET_HEADER_BUILTIN(_InterlockedAnd_acq, "LiLiD*Li", "nh", "intrin.h", ALL_MS_LANGUAGES, "") -TARGET_HEADER_BUILTIN(_InterlockedAnd_nf, "LiLiD*Li", "nh", "intrin.h", ALL_MS_LANGUAGES, "") -TARGET_HEADER_BUILTIN(_InterlockedAnd_rel, "LiLiD*Li", "nh", "intrin.h", ALL_MS_LANGUAGES, "") +TARGET_HEADER_BUILTIN(_InterlockedAnd_acq, "NiNiD*Ni", "nh", "intrin.h", ALL_MS_LANGUAGES, "") +TARGET_HEADER_BUILTIN(_InterlockedAnd_nf, "NiNiD*Ni", "nh", "intrin.h", ALL_MS_LANGUAGES, "") +TARGET_HEADER_BUILTIN(_InterlockedAnd_rel, "NiNiD*Ni", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedAnd64_acq, "LLiLLiD*LLi", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedAnd64_nf, "LLiLLiD*LLi", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedAnd64_rel, "LLiLLiD*LLi", "nh", "intrin.h", ALL_MS_LANGUAGES, "") @@ -302,9 +302,9 @@ TARGET_HEADER_BUILTIN(_InterlockedAnd64_rel, "LLiLLiD*LLi", "nh", "intrin.h", AL TARGET_HEADER_BUILTIN(_InterlockedIncrement16_acq, "ssD*", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedIncrement16_nf, "ssD*", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedIncrement16_rel, "ssD*", "nh", "intrin.h", ALL_MS_LANGUAGES, "") -TARGET_HEADER_BUILTIN(_InterlockedIncrement_acq, "LiLiD*", "nh", "intrin.h", ALL_MS_LANGUAGES, "") -TARGET_HEADER_BUILTIN(_InterlockedIncrement_nf, "LiLiD*", "nh", "intrin.h", ALL_MS_LANGUAGES, "") -TARGET_HEADER_BUILTIN(_InterlockedIncrement_rel, "LiLiD*", "nh", "intrin.h", ALL_MS_LANGUAGES, "") +TARGET_HEADER_BUILTIN(_InterlockedIncrement_acq, "NiNiD*", "nh", "intrin.h", ALL_MS_LANGUAGES, "") +TARGET_HEADER_BUILTIN(_InterlockedIncrement_nf, "NiNiD*", "nh", "intrin.h", ALL_MS_LANGUAGES, "") +TARGET_HEADER_BUILTIN(_InterlockedIncrement_rel, "NiNiD*", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedIncrement64_acq, "LLiLLiD*", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedIncrement64_nf, "LLiLLiD*", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedIncrement64_rel, "LLiLLiD*", "nh", "intrin.h", ALL_MS_LANGUAGES, "") @@ -312,9 +312,9 @@ TARGET_HEADER_BUILTIN(_InterlockedIncrement64_rel, "LLiLLiD*", "nh", "intrin.h", TARGET_HEADER_BUILTIN(_InterlockedDecrement16_acq, "ssD*", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedDecrement16_nf, "ssD*", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedDecrement16_rel, "ssD*", "nh", "intrin.h", ALL_MS_LANGUAGES, "") -TARGET_HEADER_BUILTIN(_InterlockedDecrement_acq, "LiLiD*", "nh", "intrin.h", ALL_MS_LANGUAGES, "") -TARGET_HEADER_BUILTIN(_InterlockedDecrement_nf, "LiLiD*", "nh", "intrin.h", ALL_MS_LANGUAGES, "") -TARGET_HEADER_BUILTIN(_InterlockedDecrement_rel, "LiLiD*", "nh", "intrin.h", ALL_MS_LANGUAGES, "") +TARGET_HEADER_BUILTIN(_InterlockedDecrement_acq, "NiNiD*", "nh", "intrin.h", ALL_MS_LANGUAGES, "") +TARGET_HEADER_BUILTIN(_InterlockedDecrement_nf, "NiNiD*", "nh", "intrin.h", ALL_MS_LANGUAGES, "") +TARGET_HEADER_BUILTIN(_InterlockedDecrement_rel, "NiNiD*", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedDecrement64_acq, "LLiLLiD*", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedDecrement64_nf, "LLiLLiD*", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_InterlockedDecrement64_rel, "LLiLLiD*", "nh", "intrin.h", ALL_MS_LANGUAGES, "") diff --git a/include/clang/Basic/BuiltinsBPF.def b/include/clang/Basic/BuiltinsBPF.def new file mode 100644 index 0000000000000..bd96b9ef531ba --- /dev/null +++ b/include/clang/Basic/BuiltinsBPF.def @@ -0,0 +1,24 @@ +//===--- BuiltinsBPF.def - BPF Builtin function database --------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines the BPF-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. + +#if defined(BUILTIN) && !defined(TARGET_BUILTIN) +# define TARGET_BUILTIN(ID, TYPE, ATTRS, FEATURE) BUILTIN(ID, TYPE, ATTRS) +#endif + +// Get record field information. +TARGET_BUILTIN(__builtin_preserve_field_info, "Ui.", "t", "") + +#undef BUILTIN +#undef TARGET_BUILTIN diff --git a/include/clang/Basic/BuiltinsPPC.def b/include/clang/Basic/BuiltinsPPC.def index 3b6348ad7d70e..314e1cc059070 100644 --- a/include/clang/Basic/BuiltinsPPC.def +++ b/include/clang/Basic/BuiltinsPPC.def @@ -50,17 +50,17 @@ BUILTIN(__builtin_altivec_vavguw, "V4UiV4UiV4Ui", "") BUILTIN(__builtin_altivec_vrfip, "V4fV4f", "") -BUILTIN(__builtin_altivec_vcfsx, "V4fV4iIi", "") -BUILTIN(__builtin_altivec_vcfux, "V4fV4iIi", "") +BUILTIN(__builtin_altivec_vcfsx, "V4fV4SiIi", "") +BUILTIN(__builtin_altivec_vcfux, "V4fV4UiIi", "") BUILTIN(__builtin_altivec_vctsxs, "V4SiV4fIi", "") BUILTIN(__builtin_altivec_vctuxs, "V4UiV4fIi", "") -BUILTIN(__builtin_altivec_dss, "vUi", "") +BUILTIN(__builtin_altivec_dss, "vUIi", "") BUILTIN(__builtin_altivec_dssall, "v", "") -BUILTIN(__builtin_altivec_dst, "vvC*iUi", "") -BUILTIN(__builtin_altivec_dstt, "vvC*iUi", "") -BUILTIN(__builtin_altivec_dstst, "vvC*iUi", "") -BUILTIN(__builtin_altivec_dststt, "vvC*iUi", "") +BUILTIN(__builtin_altivec_dst, "vvC*iUIi", "") +BUILTIN(__builtin_altivec_dstt, "vvC*iUIi", "") +BUILTIN(__builtin_altivec_dstst, "vvC*iUIi", "") +BUILTIN(__builtin_altivec_dststt, "vvC*iUIi", "") BUILTIN(__builtin_altivec_vexptefp, "V4fV4f", "") diff --git a/include/clang/Basic/BuiltinsWebAssembly.def b/include/clang/Basic/BuiltinsWebAssembly.def index 63177f016ac77..7fed9d2e43e74 100644 --- a/include/clang/Basic/BuiltinsWebAssembly.def +++ b/include/clang/Basic/BuiltinsWebAssembly.def @@ -31,6 +31,8 @@ TARGET_BUILTIN(__builtin_wasm_data_drop, "vIUi", "", "bulk-memory") // Thread-local storage TARGET_BUILTIN(__builtin_wasm_tls_size, "z", "nc", "bulk-memory") +TARGET_BUILTIN(__builtin_wasm_tls_align, "z", "nc", "bulk-memory") +TARGET_BUILTIN(__builtin_wasm_tls_base, "v*", "nU", "bulk-memory") // Floating point min/max BUILTIN(__builtin_wasm_min_f32, "fff", "nc") @@ -47,6 +49,16 @@ BUILTIN(__builtin_wasm_atomic_wait_i32, "ii*iLLi", "n") BUILTIN(__builtin_wasm_atomic_wait_i64, "iLLi*LLiLLi", "n") BUILTIN(__builtin_wasm_atomic_notify, "Uii*Ui", "n") +// Trapping fp-to-int conversions +BUILTIN(__builtin_wasm_trunc_s_i32_f32, "if", "nc") +BUILTIN(__builtin_wasm_trunc_u_i32_f32, "if", "nc") +BUILTIN(__builtin_wasm_trunc_s_i32_f64, "id", "nc") +BUILTIN(__builtin_wasm_trunc_u_i32_f64, "id", "nc") +BUILTIN(__builtin_wasm_trunc_s_i64_f32, "LLif", "nc") +BUILTIN(__builtin_wasm_trunc_u_i64_f32, "LLif", "nc") +BUILTIN(__builtin_wasm_trunc_s_i64_f64, "LLid", "nc") +BUILTIN(__builtin_wasm_trunc_u_i64_f64, "LLid", "nc") + // Saturating fp-to-int conversions TARGET_BUILTIN(__builtin_wasm_trunc_saturate_s_i32_f32, "if", "nc", "nontrapping-fptoint") TARGET_BUILTIN(__builtin_wasm_trunc_saturate_u_i32_f32, "if", "nc", "nontrapping-fptoint") @@ -58,6 +70,8 @@ TARGET_BUILTIN(__builtin_wasm_trunc_saturate_s_i64_f64, "LLid", "nc", "nontrappi TARGET_BUILTIN(__builtin_wasm_trunc_saturate_u_i64_f64, "LLid", "nc", "nontrapping-fptoint") // SIMD builtins +TARGET_BUILTIN(__builtin_wasm_swizzle_v8x16, "V16cV16cV16c", "nc", "unimplemented-simd128") + TARGET_BUILTIN(__builtin_wasm_extract_lane_s_i8x16, "iV16cIi", "nc", "simd128") TARGET_BUILTIN(__builtin_wasm_extract_lane_u_i8x16, "iV16cIi", "nc", "unimplemented-simd128") TARGET_BUILTIN(__builtin_wasm_extract_lane_s_i16x8, "iV8sIi", "nc", "simd128") @@ -106,10 +120,29 @@ TARGET_BUILTIN(__builtin_wasm_max_f64x2, "V2dV2dV2d", "nc", "unimplemented-simd1 TARGET_BUILTIN(__builtin_wasm_sqrt_f32x4, "V4fV4f", "nc", "unimplemented-simd128") TARGET_BUILTIN(__builtin_wasm_sqrt_f64x2, "V2dV2d", "nc", "unimplemented-simd128") +TARGET_BUILTIN(__builtin_wasm_qfma_f32x4, "V4fV4fV4fV4f", "nc", "simd128") +TARGET_BUILTIN(__builtin_wasm_qfms_f32x4, "V4fV4fV4fV4f", "nc", "simd128") +TARGET_BUILTIN(__builtin_wasm_qfma_f64x2, "V2dV2dV2dV2d", "nc", "unimplemented-simd128") +TARGET_BUILTIN(__builtin_wasm_qfms_f64x2, "V2dV2dV2dV2d", "nc", "unimplemented-simd128") + TARGET_BUILTIN(__builtin_wasm_trunc_saturate_s_i32x4_f32x4, "V4iV4f", "nc", "simd128") TARGET_BUILTIN(__builtin_wasm_trunc_saturate_u_i32x4_f32x4, "V4iV4f", "nc", "simd128") TARGET_BUILTIN(__builtin_wasm_trunc_saturate_s_i64x2_f64x2, "V2LLiV2d", "nc", "unimplemented-simd128") TARGET_BUILTIN(__builtin_wasm_trunc_saturate_u_i64x2_f64x2, "V2LLiV2d", "nc", "unimplemented-simd128") +TARGET_BUILTIN(__builtin_wasm_narrow_s_i8x16_i16x8, "V16cV8sV8s", "nc", "simd128") +TARGET_BUILTIN(__builtin_wasm_narrow_u_i8x16_i16x8, "V16cV8sV8s", "nc", "simd128") +TARGET_BUILTIN(__builtin_wasm_narrow_s_i16x8_i32x4, "V8sV4iV4i", "nc", "simd128") +TARGET_BUILTIN(__builtin_wasm_narrow_u_i16x8_i32x4, "V8sV4iV4i", "nc", "simd128") + +TARGET_BUILTIN(__builtin_wasm_widen_low_s_i16x8_i8x16, "V8sV16c", "nc", "simd128") +TARGET_BUILTIN(__builtin_wasm_widen_high_s_i16x8_i8x16, "V8sV16c", "nc", "simd128") +TARGET_BUILTIN(__builtin_wasm_widen_low_u_i16x8_i8x16, "V8sV16c", "nc", "simd128") +TARGET_BUILTIN(__builtin_wasm_widen_high_u_i16x8_i8x16, "V8sV16c", "nc", "simd128") +TARGET_BUILTIN(__builtin_wasm_widen_low_s_i32x4_i16x8, "V4iV8s", "nc", "simd128") +TARGET_BUILTIN(__builtin_wasm_widen_high_s_i32x4_i16x8, "V4iV8s", "nc", "simd128") +TARGET_BUILTIN(__builtin_wasm_widen_low_u_i32x4_i16x8, "V4iV8s", "nc", "simd128") +TARGET_BUILTIN(__builtin_wasm_widen_high_u_i32x4_i16x8, "V4iV8s", "nc", "simd128") + #undef BUILTIN #undef TARGET_BUILTIN diff --git a/include/clang/Basic/BuiltinsX86.def b/include/clang/Basic/BuiltinsX86.def index a0ba0ecf36bb8..5ab9dc1c3ac3a 100644 --- a/include/clang/Basic/BuiltinsX86.def +++ b/include/clang/Basic/BuiltinsX86.def @@ -751,8 +751,8 @@ TARGET_BUILTIN(__builtin_ia32_bextri_u32, "UiUiIUi", "nc", "tbm") // LWP TARGET_BUILTIN(__builtin_ia32_llwpcb, "vv*", "n", "lwp") TARGET_BUILTIN(__builtin_ia32_slwpcb, "v*", "n", "lwp") -TARGET_BUILTIN(__builtin_ia32_lwpins32, "UcUiUiUi", "n", "lwp") -TARGET_BUILTIN(__builtin_ia32_lwpval32, "vUiUiUi", "n", "lwp") +TARGET_BUILTIN(__builtin_ia32_lwpins32, "UcUiUiIUi", "n", "lwp") +TARGET_BUILTIN(__builtin_ia32_lwpval32, "vUiUiIUi", "n", "lwp") // SHA TARGET_BUILTIN(__builtin_ia32_sha1rnds4, "V4iV4iV4iIc", "ncV:128:", "sha") diff --git a/include/clang/Basic/BuiltinsX86_64.def b/include/clang/Basic/BuiltinsX86_64.def index 56051af55e7d5..c535f43203e56 100644 --- a/include/clang/Basic/BuiltinsX86_64.def +++ b/include/clang/Basic/BuiltinsX86_64.def @@ -86,8 +86,8 @@ TARGET_BUILTIN(__builtin_ia32_bzhi_di, "UOiUOiUOi", "nc", "bmi2") TARGET_BUILTIN(__builtin_ia32_pdep_di, "UOiUOiUOi", "nc", "bmi2") TARGET_BUILTIN(__builtin_ia32_pext_di, "UOiUOiUOi", "nc", "bmi2") TARGET_BUILTIN(__builtin_ia32_bextri_u64, "UOiUOiIUOi", "nc", "tbm") -TARGET_BUILTIN(__builtin_ia32_lwpins64, "UcUOiUiUi", "n", "lwp") -TARGET_BUILTIN(__builtin_ia32_lwpval64, "vUOiUiUi", "n", "lwp") +TARGET_BUILTIN(__builtin_ia32_lwpins64, "UcUOiUiIUi", "n", "lwp") +TARGET_BUILTIN(__builtin_ia32_lwpval64, "vUOiUiIUi", "n", "lwp") TARGET_BUILTIN(__builtin_ia32_vcvtsd2si64, "OiV2dIi", "ncV:128:", "avx512f") TARGET_BUILTIN(__builtin_ia32_vcvtsd2usi64, "UOiV2dIi", "ncV:128:", "avx512f") TARGET_BUILTIN(__builtin_ia32_vcvtss2si64, "OiV4fIi", "ncV:128:", "avx512f") diff --git a/include/clang/Basic/CodeGenOptions.def b/include/clang/Basic/CodeGenOptions.def index cd7a84548765c..d2266cc2d613c 100644 --- a/include/clang/Basic/CodeGenOptions.def +++ b/include/clang/Basic/CodeGenOptions.def @@ -47,7 +47,8 @@ CODEGENOPT(CXXCtorDtorAliases, 1, 0) ///< Emit complete ctors/dtors as linker ///< aliases to base ctors when possible. CODEGENOPT(DataSections , 1, 0) ///< Set when -fdata-sections is enabled. CODEGENOPT(UniqueSectionNames, 1, 1) ///< Set for -funique-section-names. -CODEGENOPT(DisableFPElim , 1, 0) ///< Set when -fomit-frame-pointer is enabled. +ENUM_CODEGENOPT(FramePointer, FramePointerKind, 2, FramePointerKind::None) /// frame-pointer: all,non-leaf,none + CODEGENOPT(DisableFree , 1, 0) ///< Don't free memory. CODEGENOPT(DiscardValueNames , 1, 0) ///< Discard Value Names from the IR (LLVMContext flag) CODEGENOPT(DisableGCov , 1, 0) ///< Don't run the GCov pass, for testing. @@ -132,6 +133,7 @@ CODEGENOPT(NoDwarfDirectoryAsm , 1, 0) ///< Set when -fno-dwarf-directory-asm is CODEGENOPT(NoExecStack , 1, 0) ///< Set when -Wa,--noexecstack is enabled. CODEGENOPT(FatalWarnings , 1, 0) ///< Set when -Wa,--fatal-warnings is ///< enabled. +CODEGENOPT(NoWarn , 1, 0) ///< Set when -Wa,--no-warn is enabled. CODEGENOPT(EnableSegmentedStacks , 1, 0) ///< Set when -fsplit-stack is enabled. CODEGENOPT(NoImplicitFloat , 1, 0) ///< Set when -mno-implicit-float is enabled. CODEGENOPT(NoInfsFPMath , 1, 0) ///< Assume FP arguments, results not +-Inf. @@ -155,8 +157,6 @@ CODEGENOPT(NoZeroInitializedInBSS , 1, 0) ///< -fno-zero-initialized-in-bss. ENUM_CODEGENOPT(ObjCDispatchMethod, ObjCDispatchMethodKind, 2, Legacy) /// Replace certain message sends with calls to ObjC runtime entrypoints CODEGENOPT(ObjCConvertMessagesToRuntimeCalls , 1, 1) -CODEGENOPT(OmitLeafFramePointer , 1, 0) ///< Set when -momit-leaf-frame-pointer is - ///< enabled. VALUE_CODEGENOPT(OptimizationLevel, 2, 0) ///< The -O[0-3] option specified. VALUE_CODEGENOPT(OptimizeSize, 2, 0) ///< If -Os (==1) or -Oz (==2) is specified. @@ -195,6 +195,8 @@ CODEGENOPT(SanitizeMinimalRuntime, 1, 0) ///< Use "_minimal" sanitizer runtime f ///< diagnostics. CODEGENOPT(SanitizeCfiICallGeneralizePointers, 1, 0) ///< Generalize pointer types in ///< CFI icall function signatures +CODEGENOPT(SanitizeCfiCanonicalJumpTables, 1, 0) ///< Make jump table symbols canonical + ///< instead of creating a local jump table. CODEGENOPT(SanitizeCoverageType, 2, 0) ///< Type of sanitizer coverage ///< instrumentation. CODEGENOPT(SanitizeCoverageIndirectCalls, 1, 0) ///< Enable sanitizer coverage @@ -226,6 +228,8 @@ CODEGENOPT(StrictEnums , 1, 0) ///< Optimize based on strict enum definiti CODEGENOPT(StrictVTablePointers, 1, 0) ///< Optimize based on the strict vtable pointers CODEGENOPT(TimePasses , 1, 0) ///< Set when -ftime-report is enabled. CODEGENOPT(TimeTrace , 1, 0) ///< Set when -ftime-trace is enabled. +VALUE_CODEGENOPT(TimeTraceGranularity, 32, 500) ///< Minimum time granularity (in microseconds), + ///< traced by time profiler CODEGENOPT(UnrollLoops , 1, 0) ///< Control whether loops are unrolled. CODEGENOPT(RerollLoops , 1, 0) ///< Control whether loops are rerolled. CODEGENOPT(NoUseJumpTables , 1, 0) ///< Set when -fno-jump-tables is enabled. @@ -274,6 +278,10 @@ CODEGENOPT(EmitLLVMUseLists, 1, 0) ///< Control whether to serialize use-lists. CODEGENOPT(WholeProgramVTables, 1, 0) ///< Whether to apply whole-program /// vtable optimization. +CODEGENOPT(VirtualFunctionElimination, 1, 0) ///< Whether to apply the dead + /// virtual function elimination + /// optimization. + /// Whether to use public LTO visibility for entities in std and stdext /// namespaces. This is enabled by clang-cl's /MT and /MTd flags. CODEGENOPT(LTOVisibilityPublicStd, 1, 0) diff --git a/include/clang/Basic/CodeGenOptions.h b/include/clang/Basic/CodeGenOptions.h index 4e9025d2fea9d..8881a316d1fb8 100644 --- a/include/clang/Basic/CodeGenOptions.h +++ b/include/clang/Basic/CodeGenOptions.h @@ -117,6 +117,12 @@ public: enum SignReturnAddressKeyValue { AKey, BKey }; + enum class FramePointerKind { + None, // Omit all frame pointers. + NonLeaf, // Keep non-leaf frame pointers. + All, // Keep all frame pointers. + }; + /// The code model to use (-mcmodel). std::string CodeModel; diff --git a/include/clang/Basic/Diagnostic.h b/include/clang/Basic/Diagnostic.h index 5a707007e463f..9e494aa371cda 100644 --- a/include/clang/Basic/Diagnostic.h +++ b/include/clang/Basic/Diagnostic.h @@ -632,24 +632,22 @@ public: /// Suppress all diagnostics, to silence the front end when we /// know that we don't want any more diagnostics to be passed along to the /// client - void setSuppressAllDiagnostics(bool Val = true) { - SuppressAllDiagnostics = Val; - } + void setSuppressAllDiagnostics(bool Val) { SuppressAllDiagnostics = Val; } bool getSuppressAllDiagnostics() const { return SuppressAllDiagnostics; } /// Set type eliding, to skip outputting same types occurring in /// template types. - void setElideType(bool Val = true) { ElideType = Val; } + void setElideType(bool Val) { ElideType = Val; } bool getElideType() { return ElideType; } /// Set tree printing, to outputting the template difference in a /// tree format. - void setPrintTemplateTree(bool Val = false) { PrintTemplateTree = Val; } + void setPrintTemplateTree(bool Val) { PrintTemplateTree = Val; } bool getPrintTemplateTree() { return PrintTemplateTree; } /// Set color printing, so the type diffing will inject color markers /// into the output. - void setShowColors(bool Val = false) { ShowColors = Val; } + void setShowColors(bool Val) { ShowColors = Val; } bool getShowColors() { return ShowColors; } /// Specify which overload candidates to show when overload resolution @@ -667,7 +665,7 @@ public: /// the middle of another diagnostic. /// /// This can be used by clients who suppress diagnostics themselves. - void setLastDiagnosticIgnored(bool Ignored = true) { + void setLastDiagnosticIgnored(bool Ignored) { if (LastDiagLevel == DiagnosticIDs::Fatal) FatalErrorOccurred = true; LastDiagLevel = Ignored ? DiagnosticIDs::Ignored : DiagnosticIDs::Warning; @@ -1127,11 +1125,6 @@ public: Emit(); } - /// Retrieve an empty diagnostic builder. - static DiagnosticBuilder getEmpty() { - return {}; - } - /// Forces the diagnostic to be emitted. const DiagnosticBuilder &setForceEmit() const { IsForceEmit = true; diff --git a/include/clang/Basic/DiagnosticASTKinds.td b/include/clang/Basic/DiagnosticASTKinds.td index 23502152b4ac0..04d767445a8f4 100644 --- a/include/clang/Basic/DiagnosticASTKinds.td +++ b/include/clang/Basic/DiagnosticASTKinds.td @@ -37,8 +37,9 @@ def note_constexpr_virtual_call : Note< def note_constexpr_pure_virtual_call : Note< "pure virtual function %q0 called">; def note_constexpr_polymorphic_unknown_dynamic_type : Note< - "%select{||||virtual function called on|dynamic_cast applied to|" - "typeid applied to}0 object '%1' whose dynamic type is not constant">; + "%select{|||||virtual function called on|dynamic_cast applied to|" + "typeid applied to|construction of|destruction of}0 object '%1' " + "whose dynamic type is not constant">; def note_constexpr_dynamic_cast_to_reference_failed : Note< "reference dynamic_cast failed: %select{" "static type %1 of operand is a non-public base class of dynamic type %2|" @@ -53,6 +54,9 @@ def note_constexpr_nonliteral : Note< def note_constexpr_non_global : Note< "%select{pointer|reference}0 to %select{|subobject of }1" "%select{temporary|%3}2 is not a constant expression">; +def note_constexpr_dynamic_alloc : Note< + "%select{pointer|reference}0 to %select{|subobject of }1" + "heap-allocated object is not a constant expression">; def note_constexpr_uninitialized : Note< "%select{|sub}0object of type %1 is not initialized">; def note_constexpr_subobject_declared_here : Note< @@ -100,6 +104,7 @@ def note_constexpr_typeid_polymorphic : Note< def note_constexpr_void_comparison : Note< "comparison between unequal pointers to void has unspecified result">; def note_constexpr_temporary_here : Note<"temporary created here">; +def note_constexpr_dynamic_alloc_here : Note<"heap allocation performed here">; def note_constexpr_conditional_never_const : Note< "both arms of conditional operator are unable to produce a " "constant expression">; @@ -109,16 +114,20 @@ def note_constexpr_call_limit_exceeded : Note< "constexpr evaluation hit maximum call limit">; def note_constexpr_step_limit_exceeded : Note< "constexpr evaluation hit maximum step limit; possible infinite loop?">; +def note_constexpr_heap_alloc_limit_exceeded : Note< + "constexpr evaluation hit maximum heap allocation limit">; def note_constexpr_this : Note< "%select{|implicit }0use of 'this' pointer is only allowed within the " "evaluation of a call to a 'constexpr' member function">; def note_constexpr_lifetime_ended : Note< - "%select{read of|assignment to|increment of|decrement of|member call on|" - "dynamic_cast of|typeid applied to}0 " - "%select{temporary|variable}1 whose lifetime has ended">; + "%select{read of|read of|assignment to|increment of|decrement of|" + "member call on|dynamic_cast of|typeid applied to|construction of|" + "destruction of}0 %select{temporary|variable}1 whose " + "%plural{8:storage duration|:lifetime}0 has ended">; def note_constexpr_access_uninit : Note< - "%select{read of|assignment to|increment of|decrement of|member call on|" - "dynamic_cast of|typeid applied to}0 " + "%select{read of|read of|assignment to|increment of|decrement of|" + "member call on|dynamic_cast of|typeid applied to|" + "construction of subobject of|destruction of}0 " "%select{object outside its lifetime|uninitialized object}1 " "is not allowed in a constant expression">; def note_constexpr_use_uninit_reference : Note< @@ -128,16 +137,21 @@ 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 " + "%select{read of|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 " + "%select{read of|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_volatile_here : Note< "volatile %select{temporary created|object declared|member declared}0 here">; -def note_constexpr_ltor_mutable : Note< - "read of mutable member %0 is not allowed in a constant expression">; +def note_constexpr_access_mutable : Note< + "%select{read of|read of|assignment to|increment of|decrement of|" + "member call on|dynamic_cast of|typeid applied to|construction of|" + "destruction of}0 " + "mutable member %1 is not allowed in a constant expression">; 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< @@ -145,31 +159,44 @@ def note_constexpr_ltor_non_constexpr : Note< def note_constexpr_ltor_incomplete_type : Note< "read of incomplete type %0 is not allowed in a constant expression">; def note_constexpr_access_null : Note< - "%select{read of|assignment to|increment of|decrement of|member call on|" - "dynamic_cast of|typeid applied to}0 " + "%select{read of|read of|assignment to|increment of|decrement of|" + "member call on|dynamic_cast of|typeid applied to|construction of|" + "destruction 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|member call on|" - "dynamic_cast of|typeid applied to}0 " - "dereferenced one-past-the-end pointer is not allowed in a constant expression">; + "%select{read of|read of|assignment to|increment of|decrement of|" + "member call on|dynamic_cast of|typeid applied to|construction of|" + "destruction of}0 " + "dereferenced one-past-the-end pointer is not allowed " + "in a constant expression">; def note_constexpr_access_unsized_array : Note< - "%select{read of|assignment to|increment of|decrement of|member call on|" - "dynamic_cast of|typeid applied to}0 " + "%select{read of|read of|assignment to|increment of|decrement of|" + "member call on|dynamic_cast of|typeid applied to|construction of|" + "destruction of}0 " "element of array without known bound " "is not allowed in a constant expression">; def note_constexpr_access_inactive_union_member : Note< - "%select{read of|assignment to|increment of|decrement of|member call on|" - "dynamic_cast of|typeid applied to}0 " + "%select{read of|read of|assignment to|increment of|decrement of|" + "member call on|dynamic_cast of|typeid applied to|" + "construction of subobject of|destruction 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_access_static_temporary : Note< - "%select{read of|assignment to|increment of|decrement of|member call on|" - "dynamic_cast of|typeid applied to}0 temporary " + "%select{read of|read of|assignment to|increment of|decrement of|" + "member call on|dynamic_cast of|typeid applied to|reconstruction of|" + "destruction of}0 temporary " "is not allowed in a constant expression outside the expression that " "created the temporary">; def note_constexpr_access_unreadable_object : Note< - "%select{read of|assignment to|increment of|decrement of|member call on|" - "dynamic_cast of|typeid applied to}0 object '%1' whose value is not known">; + "%select{read of|read of|assignment to|increment of|decrement of|" + "member call on|dynamic_cast of|typeid applied to|construction of|" + "destruction of}0 " + "object '%1' whose value is not known">; +def note_constexpr_access_deleted_object : Note< + "%select{read of|read of|assignment to|increment of|decrement of|" + "member call on|dynamic_cast of|typeid applied to|construction of|" + "destruction of}0 " + "heap allocated object that has been deleted">; def note_constexpr_modify_global : Note< "a constant expression cannot modify an object that is visible outside " "that expression">; @@ -189,6 +216,13 @@ def note_constexpr_baa_insufficient_alignment : Note< def note_constexpr_baa_value_insufficient_alignment : Note< "value of the aligned pointer (%0) is not a multiple of the asserted %1 " "%plural{1:byte|:bytes}1">; +def note_constexpr_destroy_out_of_lifetime : Note< + "destroying object '%0' whose lifetime has already ended">; +def note_constexpr_unsupported_destruction : Note< + "non-trivial destruction of type %0 in a constant expression is not supported">; +def note_constexpr_unsupported_tempoarary_nontrivial_dtor : Note< + "non-trivial destruction of lifetime-extended temporary with type %0 " + "used in the result of a constant expression is not yet supported">; def note_constexpr_unsupported_unsized_array : Note< "array-to-pointer decay of array member without known bound is not supported">; def note_constexpr_unsized_array_indexed : Note< @@ -228,6 +262,62 @@ def note_constexpr_bit_cast_invalid_subtype : Note< def note_constexpr_bit_cast_indet_dest : Note< "indeterminate value can only initialize an object of type 'unsigned char'" "%select{, 'char',|}1 or 'std::byte'; %0 is invalid">; +def note_constexpr_pseudo_destructor : Note< + "pseudo-destructor call is not permitted in constant expressions " + "until C++20">; +def note_constexpr_construct_complex_elem : Note< + "construction of individual component of complex number is not yet supported " + "in constant expressions">; +def note_constexpr_destroy_complex_elem : Note< + "destruction of individual component of complex number is not yet supported " + "in constant expressions">; +def note_constexpr_new : Note< + "dynamic memory allocation is not permitted in constant expressions " + "until C++20">; +def note_constexpr_new_non_replaceable : Note< + "call to %select{placement|class-specific}0 %1">; +def note_constexpr_new_placement : Note< + "this placement new expression is not yet supported in constant expressions">; +def note_constexpr_placement_new_wrong_type : Note< + "placement new would change type of storage from %0 to %1">; +def note_constexpr_new_negative : Note< + "cannot allocate array; evaluated array bound %0 is negative">; +def note_constexpr_new_too_large : Note< + "cannot allocate array; evaluated array bound %0 is too large">; +def note_constexpr_new_too_small : Note< + "cannot allocate array; evaluated array bound %0 is too small to hold " + "%1 explicitly initialized elements">; +def note_constexpr_new_untyped : Note< + "cannot allocate untyped memory in a constant expression; " + "use 'std::allocator::allocate' to allocate memory of type 'T'">; +def note_constexpr_new_not_complete_object_type : Note< + "cannot allocate memory of %select{incomplete|function}0 type %1">; +def note_constexpr_operator_new_bad_size : Note< + "allocated size %0 is not a multiple of size %1 of element type %2">; +def note_constexpr_delete_not_heap_alloc : Note< + "delete of pointer '%0' that does not point to a heap-allocated object">; +def note_constexpr_double_delete : Note< + "delete of pointer that has already been deleted">; +def note_constexpr_double_destroy : Note< + "destruction of object that is already being destroyed">; +def note_constexpr_new_delete_mismatch : Note< + "%plural{2:'delete' used to delete pointer to object " + "allocated with 'std::allocator<...>::allocate'|" + ":%select{non-array delete|array delete|'std::allocator<...>::deallocate'}0 " + "used to delete pointer to " + "%select{array object of type %2|non-array object of type %2|" + "object allocated with 'new'}0}1">; +def note_constexpr_delete_subobject : Note< + "delete of pointer%select{ to subobject|}1 '%0' " + "%select{|that does not point to complete object}1">; +def note_constexpr_delete_base_nonvirt_dtor : Note< + "delete of object with dynamic type %1 through pointer to " + "base class type %0 with non-virtual destructor">; +def note_constexpr_memory_leak : Note< + "allocation performed here was not deallocated" + "%plural{0:|: (along with %0 other memory leak%s0)}0">; +def err_experimental_clang_interp_failed : Error< + "the experimental clang interpreter failed to evaluate an expression">; def warn_integer_constant_overflow : Warning< "overflow in expression; result is %0 with type %1">, @@ -277,7 +367,6 @@ def warn_odr_variable_multiple_def : Warning< "external variable %0 defined in multiple translation units">, InGroup; def note_odr_value_here : Note<"declared here with type %0">; -def note_odr_defined_here : Note<"also defined here">; def err_odr_function_type_inconsistent : Error< "external function %0 declared with incompatible types in different " "translation units (%1 vs. %2)">; diff --git a/include/clang/Basic/DiagnosticCommentKinds.td b/include/clang/Basic/DiagnosticCommentKinds.td index fcda3f3a21130..c577ac408539d 100644 --- a/include/clang/Basic/DiagnosticCommentKinds.td +++ b/include/clang/Basic/DiagnosticCommentKinds.td @@ -153,6 +153,12 @@ def warn_doc_deprecated_not_sync : Warning< def note_add_deprecation_attr : Note< "add a deprecation attribute to the declaration to silence this warning">; +// inline contents commands + +def warn_doc_inline_contents_no_argument : Warning< + "'%select{\\|@}0%1' command does not have a valid word argument">, + InGroup, DefaultIgnore; + // verbatim block commands def warn_verbatim_block_end_without_start : Warning< diff --git a/include/clang/Basic/DiagnosticCommonKinds.td b/include/clang/Basic/DiagnosticCommonKinds.td index ca2faf59d70f9..484cc317f9658 100644 --- a/include/clang/Basic/DiagnosticCommonKinds.td +++ b/include/clang/Basic/DiagnosticCommonKinds.td @@ -12,11 +12,21 @@ let Component = "Common" in { +// Substitutions. + +def select_constexpr_spec_kind : TextSubstitution< + "%select{|constexpr|consteval|constinit}0">; + // Basic. def fatal_too_many_errors : Error<"too many errors emitted, stopping now">, DefaultFatal; +def warn_stack_exhausted : Warning< + "stack nearly exhausted; compilation time may suffer, and " + "crashes due to stack overflow are likely">, + InGroup>, NoSFINAE; + def note_declared_at : Note<"declared here">; def note_previous_definition : Note<"previous definition is here">; def note_previous_declaration : Note<"previous declaration is here">; @@ -107,6 +117,10 @@ def err_attribute_not_type_attr : Error< "%0 attribute cannot be applied to types">; def err_enum_template : Error<"enumeration cannot be a template">; +def warn_cxx20_compat_consteval : Warning< + "'consteval' specifier is incompatible with C++ standards before C++20">, + InGroup, DefaultIgnore; + } let CategoryName = "Nullability Issue" in { @@ -172,9 +186,6 @@ def ext_cxx11_longlong : Extension< def warn_cxx98_compat_longlong : Warning< "'long long' is incompatible with C++98">, InGroup, DefaultIgnore; -def warn_cxx20_compat_consteval : Warning< - "consteval is incompatible with C++ standards before C++20">, - InGroup, DefaultIgnore; def err_integer_literal_too_large : Error< "integer literal is too large to be represented in any %select{signed |}0" "integer type">; @@ -259,8 +270,6 @@ def err_target_unsupported_mcmse : Error< "-mcmse is not supported for %0">; def err_opt_not_valid_with_opt : Error< "option '%0' cannot be specified with '%1'">; -def err_opt_not_valid_without_opt : Error< - "option '%0' cannot be specified without '%1'">; def err_opt_not_valid_on_target : Error< "option '%0' cannot be specified on this target">; @@ -300,8 +309,13 @@ def err_omp_more_one_clause : Error< "directive '#pragma omp %0' cannot contain more than one '%1' clause%select{| with '%3' name modifier| with 'source' dependence}2">; // Static Analyzer Core -def err_unknown_analyzer_checker : Error< +def err_unknown_analyzer_checker_or_package : Error< "no analyzer checkers or packages are associated with '%0'">; def note_suggest_disabling_all_checkers : Note< "use -analyzer-disable-all-checks to disable all static analyzer checkers">; + +// Poison system directories. +def warn_poison_system_directories : Warning < + "include location '%0' is unsafe for cross-compilation">, + InGroup>, DefaultIgnore; } diff --git a/include/clang/Basic/DiagnosticDriverKinds.td b/include/clang/Basic/DiagnosticDriverKinds.td index eab453ee20ec9..5ff03e1335636 100644 --- a/include/clang/Basic/DiagnosticDriverKinds.td +++ b/include/clang/Basic/DiagnosticDriverKinds.td @@ -184,9 +184,6 @@ def warn_drv_unknown_argument_clang_cl_with_suggestion : Warning< def warn_drv_ycyu_different_arg_clang_cl : Warning< "support for '/Yc' and '/Yu' with different filenames not implemented yet; flags ignored">, InGroup; -def warn_drv_ycyu_no_fi_arg_clang_cl : Warning< - "support for '%0' without a corresponding /FI flag not implemented yet; flag ignored">, - InGroup; def warn_drv_yc_multiple_inputs_clang_cl : Warning< "support for '/Yc' with more than one source file not implemented yet; flag ignored">, InGroup; @@ -368,6 +365,9 @@ def err_drv_ropi_rwpi_incompatible_with_pic : Error< def err_drv_ropi_incompatible_with_cxx : Error< "ROPI is not compatible with c++">; +def err_stack_tagging_requires_hardware_feature : Error< + "'-fsanitize=memtag' requires hardware support (+memtag)">; + def warn_target_unsupported_nan2008 : Warning< "ignoring '-mnan=2008' option because the '%0' architecture does not support it">, InGroup; @@ -383,6 +383,9 @@ def warn_target_unsupported_abs2008 : Warning< def warn_target_unsupported_compact_branches : Warning< "ignoring '-mcompact-branches=' option because the '%0' architecture does not" " support it">, InGroup; +def warn_target_unsupported_extension : Warning< + "ignoring extension '%0' because the '%1' architecture does not support it">, + InGroup; def warn_drv_unsupported_gpopt : Warning< "ignoring '-mgpopt' option as it cannot be used with %select{|the implicit" " usage of }0-mabicalls">, diff --git a/include/clang/Basic/DiagnosticFrontendKinds.td b/include/clang/Basic/DiagnosticFrontendKinds.td index 7a990164b0d9f..a798b498d4e9a 100644 --- a/include/clang/Basic/DiagnosticFrontendKinds.td +++ b/include/clang/Basic/DiagnosticFrontendKinds.td @@ -64,8 +64,6 @@ def err_fe_backend_unsupported : Error<"%0">, BackendInfo; def err_fe_invalid_code_complete_file : Error< "cannot locate code-completion file %0">, DefaultFatal; -def err_fe_stdout_binary : Error<"unable to change standard output to binary">, - DefaultFatal; def err_fe_dependency_file_requires_MT : Error< "-dependency-file requires at least one -MT or -MQ option">; def err_fe_invalid_plugin_name : Error< @@ -217,10 +215,6 @@ def err_modules_embed_file_not_found : DefaultFatal; def err_module_header_file_not_found : Error<"module header file '%0' not found">, DefaultFatal; -def err_module_header_file_invalid : - Error<"unexpected module header file input '%0'">, DefaultFatal; - -def err_interface_stubs : Error<"clang-ifs (-emit-iterface-stubs): %0">; def err_test_module_file_extension_version : Error< "test module file extension '%0' has different version (%1.%2) than expected " @@ -275,7 +269,12 @@ def warn_profile_data_missing : Warning< def warn_profile_data_unprofiled : Warning< "no profile data available for file \"%0\"">, InGroup; - +def warn_profile_data_misexpect : Warning< + "Potential performance regression from use of __builtin_expect(): " + "Annotation was correct on %0 of profiled executions.">, + BackendInfo, + InGroup, + DefaultIgnore; } // end of instrumentation issue category } diff --git a/include/clang/Basic/DiagnosticGroups.td b/include/clang/Basic/DiagnosticGroups.td index 56f2ecfe8e4a6..928059539558a 100644 --- a/include/clang/Basic/DiagnosticGroups.td +++ b/include/clang/Basic/DiagnosticGroups.td @@ -61,8 +61,16 @@ def BoolConversion : DiagGroup<"bool-conversion", [PointerBoolConversion, UndefinedBoolConversion]>; def IntConversion : DiagGroup<"int-conversion">; def EnumConversion : DiagGroup<"enum-conversion">; -def ImplicitIntConversion : DiagGroup<"implicit-int-conversion">; -def ImplicitFloatConversion : DiagGroup<"implicit-float-conversion">; +def ObjCSignedCharBoolImplicitIntConversion : + DiagGroup<"objc-signed-char-bool-implicit-int-conversion">; +def ImplicitIntConversion : DiagGroup<"implicit-int-conversion", + [ObjCSignedCharBoolImplicitIntConversion]>; +def ImplicitIntFloatConversion : DiagGroup<"implicit-int-float-conversion">; +def ObjCSignedCharBoolImplicitFloatConversion : + DiagGroup<"objc-signed-char-bool-implicit-float-conversion">; +def ImplicitFloatConversion : DiagGroup<"implicit-float-conversion", + [ImplicitIntFloatConversion, + ObjCSignedCharBoolImplicitFloatConversion]>; def ImplicitFixedPointConversion : DiagGroup<"implicit-fixed-point-conversion">; def FloatOverflowConversion : DiagGroup<"float-overflow-conversion">; @@ -99,7 +107,6 @@ def GNUComplexInteger : DiagGroup<"gnu-complex-integer">; def GNUConditionalOmittedOperand : DiagGroup<"gnu-conditional-omitted-operand">; def ConfigMacros : DiagGroup<"config-macros">; def : DiagGroup<"ctor-dtor-privacy">; -def GNUDesignator : DiagGroup<"gnu-designator">; def GNUStringLiteralOperatorTemplate : DiagGroup<"gnu-string-literal-operator-template">; def UndefinedVarTemplate : DiagGroup<"undefined-var-template">; @@ -113,11 +120,13 @@ def DeleteNonVirtualDtor : DiagGroup<"delete-non-virtual-dtor", [DeleteNonAbstractNonVirtualDtor, DeleteAbstractNonVirtualDtor]>; def AbstractFinalClass : DiagGroup<"abstract-final-class">; +def FinalDtorNonFinalClass : DiagGroup<"final-dtor-non-final-class">; def CXX11CompatDeprecatedWritableStr : DiagGroup<"c++11-compat-deprecated-writable-strings">; def DeprecatedAttributes : DiagGroup<"deprecated-attributes">; +def DeprecatedCommaSubscript : DiagGroup<"deprecated-comma-subscript">; def DeprecatedDeclarations : DiagGroup<"deprecated-declarations">; def UnavailableDeclarations : DiagGroup<"unavailable-declarations">; def UnguardedAvailabilityNew : DiagGroup<"unguarded-availability-new">; @@ -131,18 +140,27 @@ def DeprecatedImplementations :DiagGroup<"deprecated-implementations">; def DeprecatedIncrementBool : DiagGroup<"deprecated-increment-bool">; def DeprecatedRegister : DiagGroup<"deprecated-register">; def DeprecatedThisCapture : DiagGroup<"deprecated-this-capture">; +def DeprecatedVolatile : DiagGroup<"deprecated-volatile">; def DeprecatedWritableStr : DiagGroup<"deprecated-writable-strings", [CXX11CompatDeprecatedWritableStr]>; // FIXME: Why is DeprecatedImplementations not in this group? def Deprecated : DiagGroup<"deprecated", [DeprecatedAttributes, + DeprecatedCommaSubscript, DeprecatedDeclarations, DeprecatedDynamicExceptionSpec, DeprecatedIncrementBool, DeprecatedRegister, DeprecatedThisCapture, + DeprecatedVolatile, DeprecatedWritableStr]>, DiagCategory<"Deprecations">; +def CXX2aDesignator : DiagGroup<"c++2a-designator">; +// Allow -Wno-c99-designator to be used to turn off all warnings on valid C99 +// designators (including the warning controlled by -Wc++2a-designator). +def C99Designator : DiagGroup<"c99-designator", [CXX2aDesignator]>; +def GNUDesignator : DiagGroup<"gnu-designator">; + def DynamicExceptionSpec : DiagGroup<"dynamic-exception-spec", [DeprecatedDynamicExceptionSpec]>; @@ -278,6 +296,7 @@ def ExitTimeDestructors : DiagGroup<"exit-time-destructors">; def FlexibleArrayExtensions : DiagGroup<"flexible-array-extensions">; def FourByteMultiChar : DiagGroup<"four-char-constants">; def GlobalConstructors : DiagGroup<"global-constructors">; +def BitwiseConditionalParentheses: DiagGroup<"bitwise-conditional-parentheses">; def BitwiseOpParentheses: DiagGroup<"bitwise-op-parentheses">; def LogicalOpParentheses: DiagGroup<"logical-op-parentheses">; def LogicalNotParentheses: DiagGroup<"logical-not-parentheses">; @@ -286,9 +305,11 @@ def OverloadedShiftOpParentheses: DiagGroup<"overloaded-shift-op-parentheses">; def DanglingElse: DiagGroup<"dangling-else">; def DanglingField : DiagGroup<"dangling-field">; def DanglingInitializerList : DiagGroup<"dangling-initializer-list">; +def DanglingGsl : DiagGroup<"dangling-gsl">; def ReturnStackAddress : DiagGroup<"return-stack-address">; def Dangling : DiagGroup<"dangling", [DanglingField, DanglingInitializerList, + DanglingGsl, ReturnStackAddress]>; def DistributedObjectModifiers : DiagGroup<"distributed-object-modifiers">; def ExpansionToDefined : DiagGroup<"expansion-to-defined">; @@ -481,6 +502,7 @@ def StringCompare : DiagGroup<"string-compare">; def StringPlusInt : DiagGroup<"string-plus-int">; def StringPlusChar : DiagGroup<"string-plus-char">; def StrncatSize : DiagGroup<"strncat-size">; +def IntInBoolContext : DiagGroup<"int-in-bool-context">; def TautologicalTypeLimitCompare : DiagGroup<"tautological-type-limit-compare">; def TautologicalUnsignedZeroCompare : DiagGroup<"tautological-unsigned-zero-compare">; def TautologicalUnsignedEnumZeroCompare : DiagGroup<"tautological-unsigned-enum-zero-compare">; @@ -495,12 +517,14 @@ def TautologicalConstantCompare : DiagGroup<"tautological-constant-compare", [TautologicalOutOfRangeCompare]>; def TautologicalPointerCompare : DiagGroup<"tautological-pointer-compare">; def TautologicalOverlapCompare : DiagGroup<"tautological-overlap-compare">; +def TautologicalBitwiseCompare : DiagGroup<"tautological-bitwise-compare">; def TautologicalUndefinedCompare : DiagGroup<"tautological-undefined-compare">; def TautologicalObjCBoolCompare : DiagGroup<"tautological-objc-bool-compare">; def TautologicalCompare : DiagGroup<"tautological-compare", [TautologicalConstantCompare, TautologicalPointerCompare, TautologicalOverlapCompare, + TautologicalBitwiseCompare, TautologicalUndefinedCompare, TautologicalObjCBoolCompare]>; def HeaderHygiene : DiagGroup<"header-hygiene">; @@ -509,6 +533,7 @@ def CompareDistinctPointerType : DiagGroup<"compare-distinct-pointer-types">; def GNUUnionCast : DiagGroup<"gnu-union-cast">; def GNUVariableSizedTypeNotAtEnd : DiagGroup<"gnu-variable-sized-type-not-at-end">; def Varargs : DiagGroup<"varargs">; +def XorUsedAsPow : DiagGroup<"xor-used-as-pow">; def Unsequenced : DiagGroup<"unsequenced">; // GCC name for -Wunsequenced @@ -543,6 +568,7 @@ def CoveredSwitchDefault : DiagGroup<"covered-switch-default">; def SwitchBool : DiagGroup<"switch-bool">; def SwitchEnum : DiagGroup<"switch-enum">; def Switch : DiagGroup<"switch">; +def EnumCompareConditional : DiagGroup<"enum-compare-conditional">; def EnumCompareSwitch : DiagGroup<"enum-compare-switch">; def EnumCompare : DiagGroup<"enum-compare", [EnumCompareSwitch]>; def ImplicitFallthroughPerFunction : @@ -620,7 +646,9 @@ def UnusedGetterReturnValue : DiagGroup<"unused-getter-return-value">; def UsedButMarkedUnused : DiagGroup<"used-but-marked-unused">; def UserDefinedLiterals : DiagGroup<"user-defined-literals">; def UserDefinedWarnings : DiagGroup<"user-defined-warnings">; -def Reorder : DiagGroup<"reorder">; +def ReorderCtor : DiagGroup<"reorder-ctor">; +def ReorderInitList : DiagGroup<"reorder-init-list">; +def Reorder : DiagGroup<"reorder", [ReorderCtor, ReorderInitList]>; def UndeclaredSelector : DiagGroup<"undeclared-selector">; def ImplicitAtomic : DiagGroup<"implicit-atomic-properties">; def CustomAtomic : DiagGroup<"custom-atomic-properties">; @@ -710,6 +738,7 @@ def ParenthesesOnEquality : DiagGroup<"parentheses-equality">; def Parentheses : DiagGroup<"parentheses", [LogicalOpParentheses, LogicalNotParentheses, + BitwiseConditionalParentheses, BitwiseOpParentheses, ShiftOpParentheses, OverloadedShiftOpParentheses, @@ -757,6 +786,7 @@ def FormatSecurity : DiagGroup<"format-security">; def FormatNonStandard : DiagGroup<"format-non-iso">; def FormatY2K : DiagGroup<"format-y2k">; def FormatPedantic : DiagGroup<"format-pedantic">; +def FormatTypeConfusion : DiagGroup<"format-type-confusion">; def Format : DiagGroup<"format", [FormatExtraArgs, FormatZeroLength, NonNull, FormatSecurity, FormatY2K, FormatInvalidSpecifier]>, @@ -800,6 +830,7 @@ def Most : DiagGroup<"most", [ Format, Implicit, InfiniteRecursion, + IntInBoolContext, MismatchedTags, MissingBraces, Move, @@ -890,7 +921,7 @@ def CXX17 : DiagGroup<"c++17-extensions">; // A warning group for warnings about using C++2a features as extensions in // earlier C++ versions. -def CXX2a : DiagGroup<"c++2a-extensions">; +def CXX2a : DiagGroup<"c++2a-extensions", [CXX2aDesignator]>; def : DiagGroup<"c++0x-extensions", [CXX11]>; def : DiagGroup<"c++1y-extensions", [CXX14]>; @@ -903,7 +934,7 @@ def DelegatingCtorCycles : def C11 : DiagGroup<"c11-extensions">; // A warning group for warnings about using C99 features as extensions. -def C99 : DiagGroup<"c99-extensions">; +def C99 : DiagGroup<"c99-extensions", [C99Designator]>; // A warning group for warnings about GCC extensions. def GNU : DiagGroup<"gnu", [GNUAlignofExpression, GNUAnonymousStruct, @@ -1001,6 +1032,12 @@ def ObjCLiteralComparison : DiagGroup<"objc-literal-compare", [ ObjCStringComparison ]>; +def ObjCSignedCharBool : DiagGroup<"objc-signed-char-bool", + [ObjCSignedCharBoolImplicitIntConversion, + ObjCSignedCharBoolImplicitFloatConversion, + ObjCBoolConstantConversion, + TautologicalObjCBoolCompare]>; + // Inline ASM warnings. def ASMOperandWidths : DiagGroup<"asm-operand-widths">; def ASMIgnoredQualifier : DiagGroup<"asm-ignored-qualifier">; @@ -1028,6 +1065,7 @@ def BackendOptimizationFailure : DiagGroup<"pass-failed">; def ProfileInstrMissing : DiagGroup<"profile-instr-missing">; def ProfileInstrOutOfDate : DiagGroup<"profile-instr-out-of-date">; def ProfileInstrUnprofiled : DiagGroup<"profile-instr-unprofiled">; +def MisExpect : DiagGroup<"misexpect">; // AddressSanitizer frontend instrumentation remarks. def SanitizeAddressRemarks : DiagGroup<"sanitize-address">; diff --git a/include/clang/Basic/DiagnosticOptions.def b/include/clang/Basic/DiagnosticOptions.def index baafd7ac723f6..6d1a1af92821b 100644 --- a/include/clang/Basic/DiagnosticOptions.def +++ b/include/clang/Basic/DiagnosticOptions.def @@ -49,6 +49,7 @@ DIAGOPT(Pedantic, 1, 0) /// -pedantic DIAGOPT(PedanticErrors, 1, 0) /// -pedantic-errors DIAGOPT(ShowColumn, 1, 1) /// Show column number on diagnostics. DIAGOPT(ShowLocation, 1, 1) /// Show source location information. +DIAGOPT(ShowLevel, 1, 1) /// Show diagnostic level. DIAGOPT(AbsolutePath, 1, 0) /// Use absolute paths. DIAGOPT(ShowCarets, 1, 1) /// Show carets in diagnostics. DIAGOPT(ShowFixits, 1, 1) /// Show fixit information. diff --git a/include/clang/Basic/DiagnosticParseKinds.td b/include/clang/Basic/DiagnosticParseKinds.td index 8e6ced0dea54f..7c9f4da778a60 100644 --- a/include/clang/Basic/DiagnosticParseKinds.td +++ b/include/clang/Basic/DiagnosticParseKinds.td @@ -119,18 +119,16 @@ def warn_microsoft_qualifiers_ignored : Warning< "qualifiers after comma in declarator list are ignored">, InGroup; -def ext_c11_generic_selection : Extension< - "generic selections are a C11-specific feature">, InGroup; def err_duplicate_default_assoc : Error< "duplicate default generic association">; def note_previous_default_assoc : Note< "previous default generic association is here">; -def ext_c11_alignment : Extension< - "%0 is a C11-specific feature">, InGroup; +def ext_c99_feature : Extension< + "'%0' is a C99 extension">, InGroup; +def ext_c11_feature : Extension< + "'%0' is a C11 extension">, InGroup; -def ext_c11_noreturn : Extension< - "_Noreturn functions are a C11-specific feature">, InGroup; def err_c11_noreturn_misplaced : Error< "'_Noreturn' keyword must precede function declarator">; @@ -203,6 +201,7 @@ def err_invalid_token_after_declarator_suggest_equal : Error< "invalid %0 at end of declaration; did you mean '='?">; def err_expected_statement : Error<"expected statement">; def err_expected_lparen_after : Error<"expected '(' after '%0'">; +def err_expected_lbrace_after : Error<"expected '{' after '%0'">; def err_expected_rparen_after : Error<"expected ')' after '%0'">; def err_expected_punc : Error<"expected ')' or ',' after '%0'">; def err_expected_less_after : Error<"expected '<' after '%0'">; @@ -360,7 +359,8 @@ def err_typename_invalid_storageclass : Error< def err_typename_invalid_functionspec : Error< "type name does not allow function specifier to be specified">; def err_typename_invalid_constexpr : Error< - "type name does not allow %select{constexpr|consteval}0 specifier to be specified">; + "type name does not allow %sub{select_constexpr_spec_kind}0 specifier " + "to be specified">; def err_typename_identifiers_only : Error< "typename is allowed for identifiers only">; @@ -374,8 +374,6 @@ def err_unexpected_token_in_nested_name_spec : Error< "'%0' cannot be a part of nested name specifier; did you mean ':'?">; def err_bool_redeclaration : Error< "redeclaration of C++ built-in type 'bool'">; -def ext_c11_static_assert : Extension< - "_Static_assert is a C11-specific feature">, InGroup; def warn_cxx98_compat_static_assert : Warning< "static_assert declarations are incompatible with C++98">, InGroup, DefaultIgnore; @@ -436,8 +434,6 @@ def err_objc_property_requires_field_name : Error< "property requires fields to be named">; def err_objc_property_bitfield : Error<"property name cannot be a bit-field">; def err_objc_expected_property_attr : Error<"unknown property attribute %0">; -def err_objc_properties_require_objc2 : Error< - "properties are an Objective-C 2 feature">; def err_objc_unexpected_attr : Error< "prefix attribute must be followed by an interface, protocol, or implementation">; def err_objc_postfix_attribute : Error < @@ -976,11 +972,13 @@ def warn_pragma_missing_argument : Warning< def warn_pragma_invalid_argument : Warning< "unexpected argument '%0' to '#pragma %1'%select{|; expected %3}2">, InGroup; +def err_pragma_misplaced_in_decl : Error<"this pragma cannot appear in %0 declaration">; + // '#pragma clang section' related errors def err_pragma_expected_clang_section_name : Error< - "expected one of [bss|data|rodata|text] section kind in '#pragma %0'">; + "expected one of [bss|data|rodata|text|relro] section kind in '#pragma %0'">; def err_pragma_clang_section_expected_equal : Error< - "expected '=' following '#pragma clang section %select{invalid|bss|data|rodata|text}0'">; + "expected '=' following '#pragma clang section %select{invalid|bss|data|rodata|text|relro}0'">; def warn_pragma_expected_section_name : Warning< "expected a string literal for the section name in '#pragma %0' - ignored">, InGroup; @@ -1180,8 +1178,8 @@ def err_omp_expected_identifier_for_critical : Error< "expected identifier specifying the name of the 'omp critical' directive">; def err_omp_expected_reduction_identifier : Error< "expected identifier or one of the following operators: '+', '-', '*', '&', '|', '^', '&&', or '||'">; -def err_omp_decl_in_declare_simd : Error< - "function declaration is expected after 'declare simd' directive">; +def err_omp_decl_in_declare_simd_variant : Error< + "function declaration is expected after 'declare %select{simd|variant}0' directive">; def err_omp_unknown_map_type : Error< "incorrect map type, expected one of 'to', 'from', 'tofrom', 'alloc', 'release', or 'delete'">; def err_omp_unknown_map_type_modifier : Error< @@ -1195,13 +1193,36 @@ def err_omp_declare_simd_inbranch_notinbranch : Error< def err_expected_end_declare_target : Error< "expected '#pragma omp end declare target'">; def err_omp_declare_target_unexpected_clause: Error< - "unexpected '%0' clause, only 'to' or 'link' clauses expected">; + "unexpected '%0' clause, only %select{'to' or 'link'|'to', 'link' or 'device_type'}1 clauses expected">; def err_omp_expected_clause: Error< "expected at least one clause on '#pragma omp %0' directive">; def err_omp_mapper_illegal_identifier : Error< "illegal OpenMP user-defined mapper identifier">; def err_omp_mapper_expected_declarator : Error< "expected declarator on 'omp declare mapper' directive">; +def err_omp_declare_variant_wrong_clause : Error< + "expected '%0' clause on 'omp declare variant' directive">; +def err_omp_declare_variant_no_ctx_selector : Error< + "expected context selector in '%0' clause on 'omp declare variant' directive">; +def err_omp_declare_variant_equal_expected : Error< + "expected '=' after '%0' context selector set name on 'omp declare variant' directive">; +def warn_omp_declare_variant_cs_name_expected : Warning< + "unknown context selector in '%0' context selector set of 'omp declare variant' directive, ignored">, + InGroup; +def err_omp_declare_variant_item_expected : Error< + "expected %0 in '%1' context selector of '%2' selector set of 'omp declare variant' directive">; +def err_omp_declare_variant_ctx_set_mutiple_use : Error< + "context selector set '%0' is used already in the same 'omp declare variant' directive">; +def note_omp_declare_variant_ctx_set_used_here : Note< + "previously context selector set '%0' used here">; +def err_omp_expected_comma_brace : Error<"expected '}' or ',' after '%0'">; +def err_omp_declare_variant_ctx_mutiple_use : Error< + "context trait selector '%0' is used already in the same '%1' context selector set of 'omp declare variant' directive">; +def note_omp_declare_variant_ctx_used_here : Note< + "previously context trait selector '%0' used here">; +def warn_omp_more_one_device_type_clause : Warning< + "more than one 'device_type' clause is specified">, + InGroup; // Pragma loop support. def err_pragma_loop_missing_argument : Error< @@ -1210,7 +1231,7 @@ def err_pragma_loop_missing_argument : Error< def err_pragma_loop_invalid_option : Error< "%select{invalid|missing}0 option%select{ %1|}0; expected vectorize, " "vectorize_width, interleave, interleave_count, unroll, unroll_count, " - "pipeline, pipeline_initiation_interval, or distribute">; + "pipeline, pipeline_initiation_interval, vectorize_predicate, or distribute">; def err_pragma_fp_invalid_option : Error< "%select{invalid|missing}0 option%select{ %1|}0; expected contract">; diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index effcbad78b231..d802a92c42c04 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -12,7 +12,6 @@ let Component = "Sema" in { let CategoryName = "Semantic Issue" in { - def note_previous_decl : Note<"%0 declared here">; def note_entity_declared_at : Note<"%0 declared here">; def note_callee_decl : Note<"%0 declared here">; @@ -155,7 +154,7 @@ def err_variably_modified_new_type : Error< // C99 Designated Initializers def ext_designated_init : Extension< - "designated initializers are a C99 feature">, InGroup; + "designated initializers are a C99 feature">, InGroup; def err_array_designator_negative : Error< "array designator value '%0' is negative">; def err_array_designator_empty_range : Error< @@ -174,15 +173,17 @@ def err_field_designator_nonfield : Error< def note_field_designator_found : Note<"field designator refers here">; def err_designator_for_scalar_init : Error< "designator in initializer for scalar type %0">; -def warn_subobject_initializer_overrides : Warning< - "subobject initialization overrides initialization of other fields " - "within its enclosing subobject">, InGroup; def warn_initializer_overrides : Warning< - "initializer overrides prior initialization of this subobject">, - InGroup; + "initializer %select{partially |}0overrides prior initialization of " + "this subobject">, InGroup; +def ext_initializer_overrides : ExtWarn, + InGroup, SFINAEFailure; +def err_initializer_overrides_destructed : Error< + "initializer would partially override prior initialization of object of " + "type %1 with non-trivial destruction">; def note_previous_initializer : Note< "previous initialization %select{|with side effects }0is here" - "%select{| (side effects may not occur at run time)}0">; + "%select{| (side effects will not occur at run time)}0">; def err_designator_into_flexible_array_member : Error< "designator into flexible array member subobject">; def note_flexible_array_member : Note< @@ -190,6 +191,28 @@ def note_flexible_array_member : Note< def ext_flexible_array_init : Extension< "flexible array initialization is a GNU extension">, InGroup; +// C++20 designated initializers +def ext_cxx_designated_init : Extension< + "designated initializers are a C++20 extension">, InGroup; +def warn_cxx17_compat_designated_init : Warning< + "designated initializers are incompatible with C++ standards before C++20">, + InGroup, DefaultIgnore; +def ext_designated_init_mixed : ExtWarn< + "mixture of designated and non-designated initializers in the same " + "initializer list is a C99 extension">, InGroup; +def note_designated_init_mixed : Note< + "first non-designated initializer is here">; +def ext_designated_init_array : ExtWarn< + "array designators are a C99 extension">, InGroup; +def ext_designated_init_nested : ExtWarn< + "nested designators are a C99 extension">, InGroup; +def ext_designated_init_reordered : ExtWarn< + "ISO C++ requires field designators to be specified in declaration order; " + "field %1 will be initialized after field %0">, InGroup, + SFINAEFailure; +def note_previous_field_init : Note< + "previous initialization for field %0 is here">; + // Declarations. def ext_plain_complex : ExtWarn< "plain '_Complex' requires a type specifier; assuming '_Complex double'">; @@ -598,6 +621,10 @@ def ext_implicit_lib_function_decl : ExtWarn< def note_include_header_or_declare : Note< "include the header <%0> or explicitly provide a declaration for '%1'">; def note_previous_builtin_declaration : Note<"%0 is a builtin with type %1">; +def warn_implicit_decl_no_jmp_buf + : Warning<"declaration of built-in function '%0' requires the declaration" + " of the 'jmp_buf' type, commonly provided in the header .">, + InGroup>; def warn_implicit_decl_requires_sysheader : Warning< "declaration of built-in function '%1' requires inclusion of the header <%0>">, InGroup; @@ -2212,6 +2239,11 @@ def err_class_marked_final_used_as_base : Error< "base %0 is marked '%select{final|sealed}1'">; def warn_abstract_final_class : Warning< "abstract class is marked '%select{final|sealed}0'">, InGroup; +def warn_final_dtor_non_final_class : Warning< + "class with destructor marked '%select{final|sealed}0' cannot be inherited from">, + InGroup; +def note_final_dtor_non_final_class_silence : Note< + "mark %0 as '%select{final|sealed}1' to silence this warning">; // C++11 attributes def err_repeat_attribute : Error<"%0 attribute cannot be repeated">; @@ -2326,18 +2358,24 @@ def warn_cxx14_compat_constexpr_not_const : Warning< "in C++14; 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 %select{constexpr|consteval}1">; + "%select{function parameter|typedef}0 " + "cannot be %sub{select_constexpr_spec_kind}1">; def err_invalid_constexpr_member : Error<"non-static data member cannot be " "constexpr%select{; did you intend to make it %select{const|static}0?|}1">; def err_constexpr_tag : Error< "%select{class|struct|interface|union|enum}0 " - "cannot be marked %select{constexpr|consteval}1">; + "cannot be marked %sub{select_constexpr_spec_kind}1">; def err_constexpr_dtor : Error< - "destructor cannot be marked %select{constexpr|consteval}0">; + "destructor cannot be declared %sub{select_constexpr_spec_kind}0">; +def err_constexpr_dtor_subobject : Error< + "destructor cannot be declared %sub{select_constexpr_spec_kind}0 because " + "%select{data member %2|base class %3}1 does not have a " + "constexpr destructor">; +def note_constexpr_dtor_subobject : Note< + "%select{data member %1|base class %2}0 declared here">; def err_constexpr_wrong_decl_kind : Error< - "%select{constexpr|consteval}0 can only be used " - "in %select{variable and |}0function declarations">; + "%sub{select_constexpr_spec_kind}0 can only be used " + "in %select{|variable and function|function|variable}0 declarations">; def err_invalid_constexpr_var_decl : Error< "constexpr variable declaration must be a definition">; def err_constexpr_static_mem_var_requires_init : Error< @@ -2346,6 +2384,8 @@ def err_constexpr_var_non_literal : Error< "constexpr variable cannot have non-literal type %0">; def err_constexpr_var_requires_const_init : Error< "constexpr variable %0 must be initialized by a constant expression">; +def err_constexpr_var_requires_const_destruction : Error< + "constexpr variable %0 must have constant destruction">; def err_constexpr_redecl_mismatch : Error< "%select{non-constexpr|constexpr|consteval}1 declaration of %0" " follows %select{non-constexpr|constexpr|consteval}2 declaration">; @@ -2406,9 +2446,13 @@ def err_constexpr_local_var_static : Error< 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_local_var_no_init : ExtWarn< + "uninitialized variable in a constexpr %select{function|constructor}0 " + "is a C++20 extension">, InGroup; +def warn_cxx17_compat_constexpr_local_var_no_init : Warning< + "uninitialized variable in a constexpr %select{function|constructor}0 " + "is incompatible with C++ standards before C++20">, + InGroup, DefaultIgnore; def ext_constexpr_function_never_constant_expr : ExtWarn< "constexpr %select{function|constructor}0 never produces a " "constant expression">, InGroup>, DefaultError; @@ -2433,10 +2477,8 @@ def warn_cxx11_compat_constexpr_body_multiple_return : Warning< InGroup, DefaultIgnore; def note_constexpr_body_previous_return : Note< "previous return statement is here">; -def err_constexpr_function_try_block : Error< - "function try block not allowed in constexpr %select{function|constructor}0">; -// c++2a function try blocks in constexpr +// C++2a function try blocks in constexpr def ext_constexpr_function_try_block_cxx2a : ExtWarn< "function try block in constexpr %select{function|constructor}0 is " "a C++2a extension">, InGroup; @@ -2445,10 +2487,20 @@ def warn_cxx17_compat_constexpr_function_try_block : Warning< "incompatible with C++ standards before C++2a">, InGroup, DefaultIgnore; -def err_constexpr_union_ctor_no_init : Error< - "constexpr union constructor does not initialize any member">; -def err_constexpr_ctor_missing_init : Error< - "constexpr constructor must initialize all members">; +def ext_constexpr_union_ctor_no_init : ExtWarn< + "constexpr union constructor that does not initialize any member " + "is a C++20 extension">, InGroup; +def warn_cxx17_compat_constexpr_union_ctor_no_init : Warning< + "constexpr union constructor that does not initialize any member " + "is incompatible with C++ standards before C++20">, + InGroup, DefaultIgnore; +def ext_constexpr_ctor_missing_init : ExtWarn< + "constexpr constructor that does not initialize all members " + "is a C++20 extension">, InGroup; +def warn_cxx17_compat_constexpr_ctor_missing_init : Warning< + "constexpr constructor that does not initialize all members " + "is incompatible with C++ standards before C++20">, + InGroup, DefaultIgnore; def note_constexpr_ctor_missing_init : Note< "member not initialized by constructor">; def note_non_literal_no_constexpr_ctors : Note< @@ -2463,6 +2515,8 @@ def note_non_literal_user_provided_dtor : Note< "%0 is not literal because it has a user-provided destructor">; def note_non_literal_nontrivial_dtor : Note< "%0 is not literal because it has a non-trivial destructor">; +def note_non_literal_non_constexpr_dtor : Note< + "%0 is not literal because its destructor is not constexpr">; def note_non_literal_lambda : Note< "lambda closure types are non-literal types before C++17">; def warn_private_extern : Warning< @@ -2472,8 +2526,6 @@ def note_private_extern : Note< "use __attribute__((visibility(\"hidden\"))) attribute instead">; // C++ Concepts -def err_concept_initialized_with_non_bool_type : Error< - "constraint expression must be of type 'bool' but is of type %0">; def err_concept_decls_may_only_appear_in_global_namespace_scope : Error< "concept declarations may only appear in global or namespace scope">; def err_concept_no_parameters : Error< @@ -2485,9 +2537,14 @@ def err_concept_no_associated_constraints : Error< "concept cannot have associated constraints">; def err_concept_not_implemented : Error< "sorry, unimplemented concepts feature %0 used">; +def err_non_constant_constraint_expression : Error< + "substitution into constraint expression resulted in a non-constant " + "expression">; +def err_non_bool_atomic_constraint : Error< + "atomic constraint must be of type 'bool' (found %0)">; -def err_template_different_associated_constraints : Error< - "associated constraints differ in template redeclaration">; +def err_template_different_requires_clause : Error< + "requires clause differs in template redeclaration">; // C++11 char16_t/char32_t def warn_cxx98_compat_unicode_type : Warning< @@ -2519,6 +2576,9 @@ def err_nsobject_attribute : Error< "'NSObject' attribute is for pointer types only">; def err_attributes_are_not_compatible : Error< "%0 and %1 attributes are not compatible">; +def err_attribute_invalid_argument : Error< + "%select{'void'|a reference type|an array type|a non-vector or " + "non-vectorizable scalar type}0 is an invalid argument to attribute %1">; def err_attribute_wrong_number_arguments : Error< "%0 attribute %plural{0:takes no arguments|1:takes one argument|" ":requires exactly %1 arguments}1">; @@ -2567,8 +2627,6 @@ def err_attribute_argument_out_of_range : Error< def err_init_priority_object_attr : Error< "can only use 'init_priority' attribute on file-scope definitions " "of objects of class type">; -def err_attribute_argument_vec_type_hint : Error< - "invalid attribute argument %0 - expecting a vector or vectorizable scalar type">; def err_attribute_argument_out_of_bounds : Error< "%0 attribute parameter %1 is out of bounds">; def err_attribute_only_once_per_parameter : Error< @@ -2778,6 +2836,11 @@ def err_no_accessor_for_property : Error< def err_cannot_find_suitable_accessor : Error< "cannot find suitable %select{getter|setter}0 for property %1">; +def warn_alloca : Warning< + "use of function %0 is discouraged; there is no way to check for failure but " + "failure may still occur, resulting in a possibly exploitable security vulnerability">, + InGroup>, DefaultIgnore; + def warn_alloca_align_alignof : Warning< "second argument to __builtin_alloca_with_align is supposed to be in bits">, InGroup>; @@ -2793,6 +2856,10 @@ def err_alignment_dependent_typedef_name : Error< def err_attribute_aligned_too_great : Error< "requested alignment must be %0 bytes or smaller">; +def warn_assume_aligned_too_great + : Warning<"requested alignment must be %0 bytes or smaller; maximum " + "alignment assumed">, + InGroup>; def warn_redeclaration_without_attribute_prev_attribute_ignored : Warning< "%q0 redeclared without %1 attribute: previous %1 ignored">, InGroup; @@ -2950,6 +3017,10 @@ def warn_gnu_inline_attribute_requires_inline : Warning< "'gnu_inline' attribute requires function to be marked 'inline'," " attribute ignored">, InGroup; +def warn_gnu_inline_cplusplus_without_extern : Warning< + "'gnu_inline' attribute without 'extern' in C++ treated as externally" + " available, this changed in Clang 10">, + InGroup>; def err_attribute_vecreturn_only_vector_member : Error< "the vecreturn attribute can only be used on a class or structure with one member, which must be a vector">; def err_attribute_vecreturn_only_pod_record : Error< @@ -2966,6 +3037,7 @@ def warn_cconv_unsupported : Warning< "|on builtin function" "}1">, InGroup; +def error_cconv_unsupported : Error; def err_cconv_knr : Error< "function with no prototype cannot use the %0 calling convention">; def warn_cconv_knr : Warning< @@ -3251,9 +3323,9 @@ def warn_impcast_integer_precision_constant : Warning< def warn_impcast_bitfield_precision_constant : Warning< "implicit truncation from %2 to bit-field changes value from %0 to %1">, InGroup; -def warn_impcast_constant_int_to_objc_bool : Warning< - "implicit conversion from constant value %0 to BOOL; " - "the only well defined values for BOOL are YES and NO">, +def warn_impcast_constant_value_to_objc_bool : Warning< + "implicit conversion from constant value %0 to 'BOOL'; " + "the only well defined values for 'BOOL' are YES and NO">, InGroup; def warn_impcast_fixed_point_range : Warning< @@ -3269,6 +3341,20 @@ def warn_impcast_literal_float_to_integer_out_of_range : Warning< def warn_impcast_float_integer : Warning< "implicit conversion turns floating-point number into integer: %0 to %1">, InGroup, DefaultIgnore; +def warn_impcast_float_to_objc_signed_char_bool : Warning< + "implicit conversion from floating-point type %0 to 'BOOL'">, + InGroup; +def warn_impcast_int_to_objc_signed_char_bool : Warning< + "implicit conversion from integral type %0 to 'BOOL'">, + InGroup, DefaultIgnore; + +// Implicit int -> float conversion precision loss warnings. +def warn_impcast_integer_float_precision : Warning< + "implicit conversion from %0 to %1 may lose precision">, + InGroup, DefaultIgnore; +def warn_impcast_integer_float_precision_constant : Warning< + "implicit conversion from %2 to %3 changes value from %0 to %1">, + InGroup; def warn_impcast_float_to_integer : Warning< "implicit conversion from %0 to %1 changes value from %2 to %3">, @@ -3292,6 +3378,10 @@ def warn_impcast_bool_to_null_pointer : Warning< def warn_non_literal_null_pointer : Warning< "expression which evaluates to zero treated as a null pointer constant of " "type %0">, InGroup; +def warn_pointer_compare : Warning< + "comparing a pointer to a null character constant; did you mean " + "to compare to %select{NULL|(void *)0}0?">, + InGroup>; def warn_impcast_null_pointer_to_integer : Warning< "implicit conversion of %select{NULL|nullptr}0 constant to %1">, InGroup; @@ -3318,6 +3408,18 @@ def warn_address_of_reference_bool_conversion : Warning< "code; pointer may be assumed to always convert to true">, InGroup; +def warn_xor_used_as_pow : Warning< + "result of '%0' is %1; did you mean exponentiation?">, + InGroup; +def warn_xor_used_as_pow_base_extra : Warning< + "result of '%0' is %1; did you mean '%2' (%3)?">, + InGroup; +def warn_xor_used_as_pow_base : Warning< + "result of '%0' is %1; did you mean '%2'?">, + InGroup; +def note_xor_used_as_pow_silence : Note< + "replace expression with '%0' %select{|or use 'xor' instead of '^' }1to silence this warning">; + def warn_null_pointer_compare : Warning< "comparison of %select{address of|function|array}0 '%1' %select{not |}2" "equal to a null pointer is always %select{true|false}2">, @@ -3338,9 +3440,15 @@ def warn_address_of_reference_null_compare : Warning< InGroup; def note_reference_is_return_value : Note<"%0 returns a reference">; +def note_pointer_declared_here : Note< + "pointer %0 declared here">; def warn_division_sizeof_ptr : Warning< "'%0' will return the size of the pointer, not the array itself">, InGroup>; +def warn_division_sizeof_array : Warning< + "expression does not compute the number of elements in this array; element " + "type is %0, not %1">, + InGroup>; def note_function_warning_silence : Note< "prefix with the address-of operator to silence this warning">; @@ -3661,7 +3769,8 @@ def note_ovl_too_many_candidates : Note< "pass -fshow-overloads=all to show them">; def select_ovl_candidate_kind : TextSubstitution< - "%select{function|function|constructor|" + "%select{function|function|function (with reversed parameter order)|" + "constructor|" "constructor (the implicit default constructor)|" "constructor (the implicit copy constructor)|" "constructor (the implicit move constructor)|" @@ -3855,10 +3964,7 @@ def note_implicit_member_target_infer_collision : Note< def note_ambiguous_type_conversion: Note< "because of ambiguity in conversion %diff{of $ to $|between types}0,1">; -def note_ovl_builtin_binary_candidate : Note< - "built-in candidate %0">; -def note_ovl_builtin_unary_candidate : Note< - "built-in candidate %0">; +def note_ovl_builtin_candidate : Note<"built-in candidate %0">; def err_ovl_no_viable_function_in_init : Error< "no matching constructor for initialization of %0">; def err_ovl_no_conversion_in_cast : Error< @@ -3885,6 +3991,13 @@ def err_ovl_ambiguous_oper_unary : Error< "use of overloaded operator '%0' is ambiguous (operand type %1)">; def err_ovl_ambiguous_oper_binary : Error< "use of overloaded operator '%0' is ambiguous (with operand types %1 and %2)">; +def ext_ovl_ambiguous_oper_binary_reversed : ExtWarn< + "ISO C++20 considers use of overloaded operator '%0' (with operand types %1 " + "and %2) to be ambiguous despite there being a unique best viable function">, + InGroup>, SFINAEFailure; +def note_ovl_ambiguous_oper_binary_reversed_candidate : Note< + "ambiguity is between a regular call to this operator and a call with the " + "argument order reversed">; def err_ovl_no_viable_oper : Error<"no viable overloaded '%0'">; def note_assign_lhs_incomplete : Note<"type %0 is incomplete">; def err_ovl_deleted_oper : Error< @@ -3892,6 +4005,9 @@ def err_ovl_deleted_oper : Error< def err_ovl_deleted_special_oper : Error< "object of type %0 cannot be %select{constructed|copied|moved|assigned|" "assigned|destroyed}1 because its %sub{select_special_member_kind}1 is implicitly deleted">; +def err_ovl_rewrite_equalequal_not_bool : Error< + "return type %0 of selected 'operator==' function for rewritten " + "'%1' comparison is not 'bool'">; def err_ovl_no_viable_subscript : Error<"no viable overloaded operator[] for type %0">; def err_ovl_no_oper : @@ -3934,6 +4050,8 @@ def err_ovl_no_viable_literal_operator : Error< // C++ Template Declarations def err_template_param_shadow : Error< "declaration of %0 shadows template parameter">; +def ext_template_param_shadow : ExtWarn< + err_template_param_shadow.Text>, InGroup; def note_template_param_here : Note<"template parameter is declared here">; def warn_template_export_unsupported : Warning< "exported templates are unsupported">; @@ -4392,6 +4510,10 @@ def note_prior_template_arg_substitution : Note< " template parameter%1 %2">; def note_template_default_arg_checking : Note< "while checking a default template argument used here">; +def note_concept_specialization_here : Note< + "while checking the satisfaction of concept '%0' requested here">; +def note_constraint_substitution_here : Note< + "while substituting template arguments into constraint expression here">; def note_instantiation_contexts_suppressed : Note< "(skipping %0 context%s0 in backtrace; use -ftemplate-backtrace-limit=0 to " "see all)">; @@ -5334,9 +5456,6 @@ def err_arc_mismatched_cast : Error< " to %3 is disallowed with ARC">; def err_arc_nolifetime_behavior : Error< "explicit ownership qualifier on cast result has no effect">; -def err_arc_objc_object_in_tag : Error< - "ARC forbids %select{Objective-C objects|blocks}0 in " - "%select{struct|interface|union|<>|enum}1">; def err_arc_objc_property_default_assign_on_object : Error< "ARC forbids synthesizing a property of an Objective-C object " "with unspecified ownership or storage attribute">; @@ -5626,9 +5745,18 @@ def note_precedence_silence : Note< def warn_precedence_conditional : Warning< "operator '?:' has lower precedence than '%0'; '%0' will be evaluated first">, InGroup; +def warn_precedence_bitwise_conditional : Warning< + "operator '?:' has lower precedence than '%0'; '%0' will be evaluated first">, + InGroup; def note_precedence_conditional_first : Note< "place parentheses around the '?:' expression to evaluate it first">; +def warn_enum_constant_in_bool_context : Warning< + "converting the enum constant to a boolean">, + InGroup, DefaultIgnore; +def warn_left_shift_in_bool_context : Warning< + "converting the result of '<<' to a boolean; did you mean '(%0) != 0'?">, + InGroup, DefaultIgnore; def warn_logical_instead_of_bitwise : Warning< "use of logical '%0' with constant operand">, InGroup>; @@ -5638,10 +5766,10 @@ def note_logical_instead_of_bitwise_remove_constant : Note< "remove constant to silence this warning">; def warn_bitwise_op_in_bitwise_op : Warning< - "'%0' within '%1'">, InGroup; + "'%0' within '%1'">, InGroup, DefaultIgnore; def warn_logical_and_in_logical_or : Warning< - "'&&' within '||'">, InGroup; + "'&&' within '||'">, InGroup, DefaultIgnore; def warn_overloaded_shift_in_comparison :Warning< "overloaded operator %select{>>|<<}0 has higher precedence than " @@ -5718,6 +5846,9 @@ def err_arithmetic_nonfragile_interface : Error< "arithmetic on pointer to interface %0, which is not a constant size for " "this architecture and platform">; +def warn_deprecated_comma_subscript : Warning< + "top-level comma expression in array subscript is deprecated">, + InGroup; def ext_subscript_non_lvalue : Extension< "ISO C90 does not allow subscripting non-lvalue array">; @@ -6038,8 +6169,8 @@ def warn_tautological_constant_compare : Warning< "%select{%1|%3}0 is always %4">, InGroup, DefaultIgnore; def warn_tautological_compare_objc_bool : Warning< - "result of comparison of constant %0 with expression of type BOOL" - " is always %1, as the only well defined values for BOOL are YES and NO">, + "result of comparison of constant %0 with expression of type 'BOOL'" + " is always %1, as the only well defined values for 'BOOL' are YES and NO">, InGroup; def warn_mixed_sign_comparison : Warning< @@ -6051,10 +6182,22 @@ def warn_out_of_range_compare : Warning< InGroup; def warn_tautological_bool_compare : Warning, InGroup; +def warn_integer_constants_in_conditional_always_true : Warning< + "converting the result of '?:' with integer constants to a boolean always " + "evaluates to 'true'">, + InGroup; +def warn_left_shift_always : Warning< + "converting the result of '<<' to a boolean always evaluates " + "to %select{false|true}0">, + InGroup; def warn_comparison_of_mixed_enum_types : Warning< "comparison of two values with different enumeration types" "%diff{ ($ and $)|}0,1">, InGroup; +def warn_conditional_mixed_enum_types : Warning< + "enumeration type mismatch in conditional expression" + "%diff{ ($ and $)|}0,1">, + InGroup, DefaultIgnore; def warn_comparison_of_mixed_enum_types_switch : Warning< "comparison of two values with different enumeration types in switch statement" "%diff{ ($ and $)|}0,1">, @@ -6093,6 +6236,8 @@ def err_invalid_qualified_function_type : Error< def err_compound_qualified_function_type : Error< "%select{block pointer|pointer|reference}0 to function type %select{%2 |}1" "cannot have '%3' qualifier">; +def err_qualified_function_typeid : Error< + "type operand %0 of 'typeid' cannot have '%1' qualifier">; def err_ref_qualifier_overload : Error< "cannot overload a member function %select{without a ref-qualifier|with " @@ -6517,6 +6662,10 @@ def note_member_declared_here : Note< "member %0 declared here">; def note_member_first_declared_here : Note< "member %0 first declared here">; +def warn_bitwise_negation_bool : Warning< + "bitwise negation of a boolean expression%select{;| always evaluates to 'true';}0 " + "did you mean logical negation?">, + InGroup>; def err_decrement_bool : Error<"cannot decrement expression of type bool">; def warn_increment_bool : Warning< "incrementing expression of type bool is deprecated and " @@ -6526,6 +6675,26 @@ def ext_increment_bool : ExtWarn< DefaultError, InGroup; def err_increment_decrement_enum : Error< "cannot %select{decrement|increment}0 expression of enum type %1">; + +def warn_deprecated_increment_decrement_volatile : Warning< + "%select{decrement|increment}0 of object of volatile-qualified type %1 " + "is deprecated">, InGroup; +def warn_deprecated_simple_assign_volatile : Warning< + "use of result of assignment to object of volatile-qualified type %0 " + "is deprecated">, InGroup; +def warn_deprecated_compound_assign_volatile : Warning< + "compound assignment to object of volatile-qualified type %0 is deprecated">, + InGroup; +def warn_deprecated_volatile_return : Warning< + "volatile-qualified return type %0 is deprecated">, + InGroup; +def warn_deprecated_volatile_param : Warning< + "volatile-qualified parameter type %0 is deprecated">, + InGroup; +def warn_deprecated_volatile_structured_binding : Warning< + "volatile qualifier in structured binding declaration is deprecated">, + InGroup; + def err_catch_incomplete_ptr : Error< "cannot catch pointer to incomplete type %0">; def err_catch_incomplete_ref : Error< @@ -7426,6 +7595,12 @@ def warn_unused_container_subscript_expr : Warning< def warn_unused_call : Warning< "ignoring return value of function declared with %0 attribute">, InGroup; +def warn_unused_constructor : Warning< + "ignoring temporary created by a constructor declared with %0 attribute">, + InGroup; +def warn_unused_constructor_msg : Warning< + "ignoring temporary created by a constructor declared with %0 attribute: %1">, + InGroup; def warn_side_effects_unevaluated_context : Warning< "expression with side effects has no effect in an unevaluated context">, InGroup; @@ -7435,6 +7610,9 @@ def warn_side_effects_typeid : Warning< def warn_unused_result : Warning< "ignoring return value of function declared with %0 attribute">, InGroup; +def warn_unused_result_msg : Warning< + "ignoring return value of function declared with %0 attribute: %1">, + InGroup; def warn_unused_volatile : Warning< "expression result unused; assign into a variable to force a volatile load">, InGroup>; @@ -7443,6 +7621,8 @@ def ext_cxx14_attr : Extension< "use of the %0 attribute is a C++14 extension">, InGroup; def ext_cxx17_attr : Extension< "use of the %0 attribute is a C++17 extension">, InGroup; +def ext_cxx2a_attr : Extension< + "use of the %0 attribute is a C++2a extension">, InGroup; def warn_unused_comparison : Warning< "%select{equality|inequality|relational|three-way}0 comparison result unused">, @@ -7453,10 +7633,30 @@ def note_inequality_comparison_to_or_assign : Note< def err_incomplete_type_used_in_type_trait_expr : Error< "incomplete type %0 used in type trait expression">; +// C++20 constinit and require_constant_initialization attribute +def warn_cxx20_compat_constinit : Warning< + "'constinit' specifier is incompatible with C++ standards before C++20">, + InGroup, DefaultIgnore; +def err_constinit_local_variable : Error< + "local variable cannot be declared 'constinit'">; def err_require_constant_init_failed : Error< "variable does not have a constant initializer">; def note_declared_required_constant_init_here : Note< - "required by 'require_constant_initialization' attribute here">; + "required by %select{'require_constant_initialization' attribute|" + "'constinit' specifier}0 here">; +def ext_constinit_missing : ExtWarn< + "'constinit' specifier missing on initializing declaration of %0">, + InGroup>; +def note_constinit_specified_here : Note<"variable declared constinit here">; +def err_constinit_added_too_late : Error< + "'constinit' specifier added after initialization of variable">; +def warn_require_const_init_added_too_late : Warning< + "'require_constant_initialization' attribute added after initialization " + "of variable">, InGroup; +def note_constinit_missing_here : Note< + "add the " + "%select{'require_constant_initialization' attribute|'constinit' specifier}0 " + "to the initializing declaration here">; def err_dimension_expr_not_constant_integer : Error< "dimension expression does not evaluate to a constant unsigned int">; @@ -7493,8 +7693,6 @@ let CategoryName = "Inline Assembly Issue" in { "invalid lvalue in asm input for constraint '%0'">; def err_asm_invalid_input_constraint : Error< "invalid input constraint '%0' in asm">; - def err_asm_immediate_expected : Error<"constraint '%0' expects " - "an integer constant expression">; def err_asm_tying_incompatible_types : Error< "unsupported inline asm: input with type " "%diff{$ matching output with type $|}0,1">; @@ -7583,7 +7781,7 @@ def err_mem_init_not_member_or_class : Error< def warn_initializer_out_of_order : Warning< "%select{field|base class}0 %1 will be initialized after " "%select{field|base}2 %3">, - InGroup, DefaultIgnore; + InGroup, DefaultIgnore; def warn_abstract_vbase_init_ignored : Warning< "initializer for virtual base class %0 of abstract class %1 " "will never be used">, @@ -7919,7 +8117,7 @@ def warn_array_index_precedes_bounds : Warning< def warn_array_index_exceeds_bounds : Warning< "array index %0 is past the end of the array (which contains %1 " "element%s2)">, InGroup; -def note_array_index_out_of_bounds : Note< +def note_array_declared_here : Note< "array %0 declared here">; def warn_printf_insufficient_data_args : Warning< @@ -7940,16 +8138,17 @@ def warn_format_conversion_argument_type_mismatch : Warning< "%select{type|underlying type}2 %1">, InGroup; def warn_format_conversion_argument_type_mismatch_pedantic : Extension< - "format specifies type %0 but the argument has " - "%select{type|underlying type}2 %1">, + warn_format_conversion_argument_type_mismatch.Text>, InGroup; +def warn_format_conversion_argument_type_mismatch_confusion : Warning< + warn_format_conversion_argument_type_mismatch.Text>, + InGroup, DefaultIgnore; def warn_format_argument_needs_cast : Warning< "%select{values of type|enum values with underlying type}2 '%0' should not " "be used as format arguments; add an explicit cast to %1 instead">, InGroup; def warn_format_argument_needs_cast_pedantic : Warning< - "%select{values of type|enum values with underlying type}2 '%0' should not " - "be used as format arguments; add an explicit cast to %1 instead">, + warn_format_argument_needs_cast.Text>, InGroup, DefaultIgnore; def warn_printf_positional_arg_exceeds_data_args : Warning < "data argument position '%0' exceeds the number of data arguments (%1)">, @@ -8023,6 +8222,9 @@ def warn_printf_invalid_objc_flag: Warning< def warn_scanf_scanlist_incomplete : Warning< "no closing ']' for '%%[' in scanf format string">, InGroup; +def warn_format_bool_as_character : Warning< + "using '%0' format specifier, but argument has boolean value">, + InGroup; def note_format_string_defined : Note<"format string is defined here">; def note_format_fix_specifier : Note<"did you mean to use '%0'?">; def note_printf_c_str: Note<"did you mean to call the %0 method?">; @@ -8085,6 +8287,10 @@ def warn_dangling_member : Warning< "%select{binds to|is}2 a temporary object " "whose lifetime is shorter than the lifetime of the constructed object">, InGroup; +def warn_dangling_lifetime_pointer_member : Warning< + "initializing pointer member %0 to point to a temporary object " + "whose lifetime is shorter than the lifetime of the constructed object">, + InGroup; def note_lifetime_extending_member_declared_here : Note< "%select{%select{reference|'std::initializer_list'}0 member|" "member with %select{reference|'std::initializer_list'}0 subobject}1 " @@ -8103,6 +8309,10 @@ def warn_new_dangling_reference : Warning< "temporary bound to reference member of allocated object " "will be destroyed at the end of the full-expression">, InGroup; +def warn_dangling_lifetime_pointer : Warning< + "object backing the pointer " + "will be destroyed at the end of the full-expression">, + InGroup; def warn_new_dangling_initializer_list : Warning< "array backing " "%select{initializer list subobject of the allocated object|" @@ -8120,11 +8330,15 @@ def warn_unsupported_lifetime_extension : Warning< // should result in a warning, since these always evaluate to a constant. // Array comparisons have similar warnings def warn_comparison_always : Warning< - "%select{self-|array }0comparison always evaluates to %select{a constant|%2}1">, + "%select{self-|array }0comparison always evaluates to " + "%select{a constant|true|false|'std::strong_ordering::equal'}1">, InGroup; def warn_comparison_bitwise_always : Warning< "bitwise comparison always evaluates to %select{false|true}0">, - InGroup; + InGroup, DefaultIgnore; +def warn_comparison_bitwise_or : Warning< + "bitwise or with non-zero value always evaluates to true">, + InGroup, DefaultIgnore; def warn_tautological_overlap_comparison : Warning< "overlapping comparisons always evaluate to %select{false|true}0">, InGroup, DefaultIgnore; @@ -8503,10 +8717,11 @@ def warn_sync_fetch_and_nand_semantics_change : Warning< InGroup>; // Type -def ext_invalid_sign_spec : Extension<"'%0' cannot be signed or unsigned">; +def ext_wchar_t_sign_spec : ExtWarn<"'%0' cannot be signed or unsigned">, + InGroup>, DefaultError; def warn_receiver_forward_class : Warning< - "receiver %0 is a forward class and corresponding @interface may not exist">, - InGroup; + "receiver %0 is a forward class and corresponding @interface may not exist">, + InGroup; def note_method_sent_forward_class : Note<"method %0 is used for the forward class">; def ext_missing_declspec : ExtWarn< "declaration specifier missing, defaulting to 'int'">; @@ -8984,7 +9199,7 @@ def ext_omp_loop_not_canonical_init : ExtWarn< "('var = init' or 'T var = init')">, InGroup; def err_omp_loop_not_canonical_cond : Error< "condition of OpenMP for loop must be a relational comparison " - "('<', '<=', '>', or '>=') of loop variable %0">; + "('<', '<=', '>', %select{or '>='|'>=', or '!='}0) of loop variable %1">; def err_omp_loop_not_canonical_incr : Error< "increment clause of OpenMP for loop must perform simple addition " "or subtraction on loop variable %0">; @@ -9101,10 +9316,10 @@ def err_omp_single_copyprivate_with_nowait : Error< "the 'copyprivate' clause must not be used with the 'nowait' clause">; def note_omp_nowait_clause_here : Note< "'nowait' clause is here">; -def err_omp_single_decl_in_declare_simd : Error< - "single declaration is expected after 'declare simd' directive">; +def err_omp_single_decl_in_declare_simd_variant : Error< + "single declaration is expected after 'declare %select{simd|variant}0' directive">; def err_omp_function_expected : Error< - "'#pragma omp declare simd' can only be applied to functions">; + "'#pragma omp declare %select{simd|variant}0' can only be applied to functions">; def err_omp_wrong_cancel_region : Error< "one of 'for', 'parallel', 'sections' or 'taskgroup' is expected">; def err_omp_parent_cancel_region_nowait : Error< @@ -9286,6 +9501,44 @@ def err_omp_wrong_dependency_iterator_type : Error< "expected an integer or a pointer type of the outer loop counter '%0' for non-rectangular nests">; def err_omp_unsupported_type : Error < "host requires %0 bit size %1 type support, but device '%2' does not support it">; +def err_omp_lambda_capture_in_declare_target_not_to : Error< + "variable captured in declare target region must appear in a to clause">; +def err_omp_device_type_mismatch : Error< + "'device_type(%0)' does not match previously specified 'device_type(%1)' for the same declaration">; +def err_omp_wrong_device_function_call : Error< + "function with 'device_type(%0)' is not available on %select{device|host}1">; +def note_omp_marked_device_type_here : Note<"marked as 'device_type(%0)' here">; +def warn_omp_declare_target_after_first_use : Warning< + "declaration marked as declare target after first use, it may lead to incorrect results">, + InGroup; +def err_omp_declare_variant_incompat_attributes : Error< + "'#pragma omp declare variant' is not compatible with any target-specific attributes">; +def warn_omp_declare_variant_after_used : Warning< + "'#pragma omp declare variant' cannot be applied for function after first " + "usage; the original function might be used">, InGroup; +def warn_omp_declare_variant_after_emitted : Warning< + "'#pragma omp declare variant' cannot be applied to the function that was defined already;" + " the original function might be used">, InGroup; +def err_omp_declare_variant_noproto : Error< + "function with '#pragma omp declare variant' must have a prototype">; +def note_omp_declare_variant_specified_here : Note< + "'#pragma omp declare variant' for function specified here">; +def err_omp_declare_variant_doesnt_support : Error< + "'#pragma omp declare variant' does not " + "support %select{function templates|virtual functions|" + "deduced return types|constructors|destructors|deleted functions|" + "defaulted functions|constexpr functions|consteval function}0">; +def err_omp_declare_variant_diff : Error< + "function with '#pragma omp declare variant' has a different %select{calling convention" + "|return type|constexpr specification|inline specification|storage class|" + "linkage}0">; +def err_omp_declare_variant_incompat_types : Error< + "variant in '#pragma omp declare variant' with type %0 is incompatible with type %1" + >; +def warn_omp_declare_variant_marked_as_declare_variant : Warning< + "variant function in '#pragma omp declare variant' is itself marked as '#pragma omp declare variant'" + >, InGroup; +def note_omp_marked_declare_variant_here : Note<"marked as 'declare variant' here">; } // end of OpenMP category let CategoryName = "Related Result Type Issue" in { @@ -9725,6 +9978,8 @@ def err_std_compare_type_not_supported : Error< "member '%2' is missing|" "the type is not trivially copyable|" "the type does not have the expected form}1">; +def note_rewriting_operator_as_spaceship : Note< + "while rewriting comparison as call to 'operator<=>' declared here">; // Memory Tagging Extensions (MTE) diagnostics def err_memtag_arg_null_or_pointer : Error< @@ -9735,8 +9990,6 @@ def err_memtag_arg_must_be_pointer : Error< "%0 argument of MTE builtin function must be a pointer (%1 invalid)">; def err_memtag_arg_must_be_integer : Error< "%0 argument of MTE builtin function must be an integer type (%1 invalid)">; -def err_memtag_arg_must_be_unsigned : Error< - "%0 argument of MTE builtin function must be an unsigned integer type (%1 invalid)">; def warn_dereference_of_noderef_type : Warning< "dereferencing %0; was declared with a 'noderef' type">, InGroup; @@ -9751,6 +10004,11 @@ def err_builtin_launder_invalid_arg : Error< "%select{non-pointer|function pointer|void pointer}0 argument to " "'__builtin_launder' is not allowed">; +def err_preserve_field_info_not_field : Error< + "__builtin_preserve_field_info argument %0 not a field access">; +def err_preserve_field_info_not_const: Error< + "__builtin_preserve_field_info argument %0 not a constant">; + def err_bit_cast_non_trivially_copyable : Error< "__builtin_bit_cast %select{source|destination}0 type must be trivially copyable">; def err_bit_cast_type_size_mismatch : Error< diff --git a/include/clang/Basic/DiagnosticSerializationKinds.td b/include/clang/Basic/DiagnosticSerializationKinds.td index 43ba19b5853e2..757dbbeee3cc0 100644 --- a/include/clang/Basic/DiagnosticSerializationKinds.td +++ b/include/clang/Basic/DiagnosticSerializationKinds.td @@ -18,13 +18,16 @@ def err_fe_pch_malformed : Error< def err_fe_pch_malformed_block : Error< "malformed block record in PCH file: '%0'">, DefaultFatal; def err_fe_pch_file_modified : Error< - "file '%0' has been modified since the precompiled header '%1' was built">, + "file '%0' has been modified since the precompiled header '%1' was built" + ": %select{size|mtime|content}2 changed">, DefaultFatal; def err_fe_module_file_modified : Error< - "file '%0' has been modified since the module file '%1' was built">, + "file '%0' has been modified since the module file '%1' was built" + ": %select{size|mtime|content}2 changed">, DefaultFatal; def err_fe_ast_file_modified : Error< - "file '%0' has been modified since the AST file '%1' was built">, + "file '%0' has been modified since the AST file '%1' was built" + ": %select{size|mtime|content}2 changed">, DefaultFatal; def err_fe_pch_file_overridden : Error< "file '%0' from the precompiled header has been overridden">; @@ -77,13 +80,13 @@ def remark_module_import : Remark< InGroup; def err_imported_module_not_found : Error< - "module '%0' in AST file '%1' (imported by AST file '%2') " + "module '%0' in AST file '%1' %select{(imported by AST file '%2') |}4" "is not defined in any loaded module map file; " "maybe you need to load '%3'?">, DefaultFatal; def note_imported_by_pch_module_not_found : Note< "consider adding '%0' to the header search path">; def err_imported_module_modmap_changed : Error< - "module '%0' imported by AST file '%1' found in a different module map file" + "module '%0' %select{in|imported by}4 AST file '%1' found in a different module map file" " (%2) than when the importing AST file was built (%3)">, DefaultFatal; def err_imported_module_relocated : Error< "module '%0' was built in directory '%1' but now resides in " @@ -399,6 +402,8 @@ def warn_module_uses_date_time : Warning< def err_module_no_size_mtime_for_header : Error< "cannot emit module %0: %select{size|mtime}1 must be explicitly specified " "for missing header file \"%2\"">; +def err_module_unable_to_hash_content : Error< + "failed to hash content for '%0' because memory buffer cannot be retrieved">; } // let CategoryName } // let Component diff --git a/include/clang/Basic/Features.def b/include/clang/Basic/Features.def index 7081c02e83eac..28eb694ba9a89 100644 --- a/include/clang/Basic/Features.def +++ b/include/clang/Basic/Features.def @@ -39,6 +39,8 @@ FEATURE(address_sanitizer, LangOpts.Sanitize.hasOneOf(SanitizerKind::Address | SanitizerKind::KernelAddress)) +FEATURE(leak_sanitizer, + LangOpts.Sanitize.has(SanitizerKind::Leak)) FEATURE(hwaddress_sanitizer, LangOpts.Sanitize.hasOneOf(SanitizerKind::HWAddress | SanitizerKind::KernelHWAddress)) @@ -187,7 +189,7 @@ FEATURE(cxx_variable_templates, LangOpts.CPlusPlus14) // FEATURE(raw_invocation_type, LangOpts.CPlusPlus) // Type traits // N.B. Additional type traits should not be added to the following list. -// Instead, they should be detected by has_extension. +// Instead, they should be detected by has_builtin. FEATURE(has_nothrow_assign, LangOpts.CPlusPlus) FEATURE(has_nothrow_copy, LangOpts.CPlusPlus) FEATURE(has_nothrow_constructor, LangOpts.CPlusPlus) diff --git a/include/clang/Basic/FileManager.h b/include/clang/Basic/FileManager.h index 96983475f454c..28cd058180871 100644 --- a/include/clang/Basic/FileManager.h +++ b/include/clang/Basic/FileManager.h @@ -45,12 +45,31 @@ class FileSystemStatCache; class DirectoryEntry { friend class FileManager; + // FIXME: We should not be storing a directory entry name here. StringRef Name; // Name of the directory. public: StringRef getName() const { return Name; } }; +/// A reference to a \c DirectoryEntry that includes the name of the directory +/// as it was accessed by the FileManager's client. +class DirectoryEntryRef { +public: + const DirectoryEntry &getDirEntry() const { return *Entry->getValue(); } + + StringRef getName() const { return Entry->getKey(); } + +private: + friend class FileManager; + + DirectoryEntryRef( + llvm::StringMapEntry> *Entry) + : Entry(Entry) {} + + const llvm::StringMapEntry> *Entry; +}; + /// Cached information about one file (either on disk /// or in the virtual file system). /// @@ -64,8 +83,8 @@ class FileEntry { off_t Size; // File size in bytes. time_t ModTime; // Modification time of file. const DirectoryEntry *Dir; // Directory file lives in. - unsigned UID; // A unique (small) ID for the file. llvm::sys::fs::UniqueID UniqueID; + unsigned UID; // A unique (small) ID for the file. bool IsNamedPipe; bool IsValid; // Is this \c FileEntry initialized and valid? @@ -106,6 +125,42 @@ public: bool isOpenForTests() const { return File != nullptr; } }; +/// A reference to a \c FileEntry that includes the name of the file as it was +/// accessed by the FileManager's client. +class FileEntryRef { +public: + FileEntryRef() = delete; + FileEntryRef(StringRef Name, const FileEntry &Entry) + : Name(Name), Entry(&Entry) {} + + const StringRef getName() const { return Name; } + + bool isValid() const { return Entry->isValid(); } + + const FileEntry &getFileEntry() const { return *Entry; } + + off_t getSize() const { return Entry->getSize(); } + + unsigned getUID() const { return Entry->getUID(); } + + const llvm::sys::fs::UniqueID &getUniqueID() const { + return Entry->getUniqueID(); + } + + time_t getModificationTime() const { return Entry->getModificationTime(); } + + friend bool operator==(const FileEntryRef &LHS, const FileEntryRef &RHS) { + return LHS.Entry == RHS.Entry && LHS.Name == RHS.Name; + } + friend bool operator!=(const FileEntryRef &LHS, const FileEntryRef &RHS) { + return !(LHS == RHS); + } + +private: + StringRef Name; + const FileEntry *Entry; +}; + /// Implements support for file system lookup, file system caching, /// and directory search management. /// @@ -131,21 +186,41 @@ class FileManager : public RefCountedBase { /// The virtual files that we have allocated. SmallVector, 4> VirtualFileEntries; + /// A set of files that bypass the maps and uniquing. They can have + /// conflicting filenames. + SmallVector, 0> BypassFileEntries; + /// A cache that maps paths to directory entries (either real or - /// virtual) we have looked up + /// virtual) we have looked up, or an error that occurred when we looked up + /// the directory. /// /// The actual Entries for real directories/files are /// owned by UniqueRealDirs/UniqueRealFiles above, while the Entries /// for virtual directories/files are owned by /// VirtualDirectoryEntries/VirtualFileEntries above. /// - llvm::StringMap SeenDirEntries; + llvm::StringMap, llvm::BumpPtrAllocator> + SeenDirEntries; + + /// A reference to the file entry that is associated with a particular + /// filename, or a reference to another filename that should be looked up + /// instead of the accessed filename. + /// + /// The reference to another filename is specifically useful for Redirecting + /// VFSs that use external names. In that case, the \c FileEntryRef returned + /// by the \c FileManager will have the external name, and not the name that + /// was used to lookup the file. + using SeenFileEntryOrRedirect = + llvm::PointerUnion; /// A cache that maps paths to file entries (either real or - /// virtual) we have looked up. + /// virtual) we have looked up, or an error that occurred when we looked up + /// the file. /// /// \see SeenDirEntries - llvm::StringMap SeenFileEntries; + llvm::StringMap, + llvm::BumpPtrAllocator> + SeenFileEntries; /// The canonical names of directories. llvm::DenseMap CanonicalDirNames; @@ -157,15 +232,12 @@ class FileManager : public RefCountedBase { /// unsigned NextFileUID; - // Statistics. - unsigned NumDirLookups, NumFileLookups; - unsigned NumDirCacheMisses, NumFileCacheMisses; - // Caching. std::unique_ptr StatCache; - bool getStatValue(StringRef Path, llvm::vfs::Status &Status, bool isFile, - std::unique_ptr *F); + std::error_code getStatValue(StringRef Path, llvm::vfs::Status &Status, + bool isFile, + std::unique_ptr *F); /// Add all ancestors of the given path (pointing to either a file /// or a directory) as virtual directories. @@ -195,27 +267,86 @@ public: /// Removes the FileSystemStatCache object from the manager. void clearStatCache(); + /// Returns the number of unique real file entries cached by the file manager. + size_t getNumUniqueRealFiles() const { return UniqueRealFiles.size(); } + /// Lookup, cache, and verify the specified directory (real or /// virtual). /// - /// This returns NULL if the directory doesn't exist. + /// This returns a \c std::error_code if there was an error reading the + /// directory. On success, returns the reference to the directory entry + /// together with the exact path that was used to access a file by a + /// particular call to getDirectoryRef. /// /// \param CacheFailure If true and the file does not exist, we'll cache /// the failure to find this file. - const DirectoryEntry *getDirectory(StringRef DirName, - bool CacheFailure = true); + llvm::Expected getDirectoryRef(StringRef DirName, + bool CacheFailure = true); + + /// Get a \c DirectoryEntryRef if it exists, without doing anything on error. + llvm::Optional + getOptionalDirectoryRef(StringRef DirName, bool CacheFailure = true) { + return llvm::expectedToOptional(getDirectoryRef(DirName, CacheFailure)); + } + + /// Lookup, cache, and verify the specified directory (real or + /// virtual). + /// + /// This function is deprecated and will be removed at some point in the + /// future, new clients should use + /// \c getDirectoryRef. + /// + /// This returns a \c std::error_code if there was an error reading the + /// directory. If there is no error, the DirectoryEntry is guaranteed to be + /// non-NULL. + /// + /// \param CacheFailure If true and the file does not exist, we'll cache + /// the failure to find this file. + llvm::ErrorOr + getDirectory(StringRef DirName, bool CacheFailure = true); /// Lookup, cache, and verify the specified file (real or /// virtual). /// - /// This returns NULL if the file doesn't exist. + /// This function is deprecated and will be removed at some point in the + /// future, new clients should use + /// \c getFileRef. + /// + /// This returns a \c std::error_code if there was an error loading the file. + /// If there is no error, the FileEntry is guaranteed to be non-NULL. /// /// \param OpenFile if true and the file exists, it will be opened. /// /// \param CacheFailure If true and the file does not exist, we'll cache /// the failure to find this file. - const FileEntry *getFile(StringRef Filename, bool OpenFile = false, - bool CacheFailure = true); + llvm::ErrorOr + getFile(StringRef Filename, bool OpenFile = false, bool CacheFailure = true); + + /// Lookup, cache, and verify the specified file (real or virtual). Return the + /// reference to the file entry together with the exact path that was used to + /// access a file by a particular call to getFileRef. If the underlying VFS is + /// a redirecting VFS that uses external file names, the returned FileEntryRef + /// will use the external name instead of the filename that was passed to this + /// method. + /// + /// This returns a \c std::error_code if there was an error loading the file, + /// or a \c FileEntryRef otherwise. + /// + /// \param OpenFile if true and the file exists, it will be opened. + /// + /// \param CacheFailure If true and the file does not exist, we'll cache + /// the failure to find this file. + llvm::Expected getFileRef(StringRef Filename, + bool OpenFile = false, + bool CacheFailure = true); + + /// Get a FileEntryRef if it exists, without doing anything on error. + llvm::Optional getOptionalFileRef(StringRef Filename, + bool OpenFile = false, + bool CacheFailure = true) { + return llvm::expectedToOptional( + getFileRef(Filename, OpenFile, CacheFailure)); + } /// Returns the current file system options FileSystemOptions &getFileSystemOpts() { return FileSystemOpts; } @@ -223,6 +354,10 @@ public: llvm::vfs::FileSystem &getVirtualFileSystem() const { return *FS; } + void setVirtualFileSystem(IntrusiveRefCntPtr FS) { + this->FS = std::move(FS); + } + /// Retrieve a file entry for a "virtual" file that acts as /// if there were a file with the given name on disk. /// @@ -230,24 +365,38 @@ public: const FileEntry *getVirtualFile(StringRef Filename, off_t Size, time_t ModificationTime); + /// Retrieve a FileEntry that bypasses VFE, which is expected to be a virtual + /// file entry, to access the real file. The returned FileEntry will have + /// the same filename as FE but a different identity and its own stat. + /// + /// This should be used only for rare error recovery paths because it + /// bypasses all mapping and uniquing, blindly creating a new FileEntry. + /// There is no attempt to deduplicate these; if you bypass the same file + /// twice, you get two new file entries. + llvm::Optional getBypassFile(FileEntryRef VFE); + /// Open the specified file as a MemoryBuffer, returning a new /// MemoryBuffer if successful, otherwise returning null. llvm::ErrorOr> - getBufferForFile(const FileEntry *Entry, bool isVolatile = false, - bool ShouldCloseOpenFile = true); + getBufferForFile(const FileEntry *Entry, bool isVolatile = false); + llvm::ErrorOr> + getBufferForFile(StringRef Filename, bool isVolatile = false) { + return getBufferForFileImpl(Filename, /*FileSize=*/-1, isVolatile); + } + +private: llvm::ErrorOr> - getBufferForFile(StringRef Filename, bool isVolatile = false); + getBufferForFileImpl(StringRef Filename, int64_t FileSize, bool isVolatile); +public: /// Get the 'stat' information for the given \p Path. /// /// If the path is relative, it will be resolved against the WorkingDir of the /// FileManager's FileSystemOptions. /// - /// \returns false on success, true on error. - bool getNoncachedStatValue(StringRef Path, llvm::vfs::Status &Result); - - /// Remove the real file \p Entry from the cache. - void invalidateCache(const FileEntry *Entry); + /// \returns a \c std::error_code describing an error, if there was one + std::error_code getNoncachedStatValue(StringRef Path, + llvm::vfs::Status &Result); /// If path is not absolute and FileSystemOptions set the working /// directory, the path is modified to be relative to the given @@ -265,11 +414,6 @@ public: void GetUniqueIDMapping( SmallVectorImpl &UIDToFiles) const; - /// Modifies the size and modification time of a previously created - /// FileEntry. Use with caution. - static void modifyFileEntry(FileEntry *File, off_t Size, - time_t ModificationTime); - /// Retrieve the canonical name for a given directory. /// /// This is a very expensive operation, despite its results being cached, diff --git a/include/clang/Basic/IdentifierTable.h b/include/clang/Basic/IdentifierTable.h index 465486ede7151..37d7198c64017 100644 --- a/include/clang/Basic/IdentifierTable.h +++ b/include/clang/Basic/IdentifierTable.h @@ -750,6 +750,12 @@ public: return getIdentifierInfoFlag() == ZeroArg; } + /// If this selector is the specific keyword selector described by Names. + bool isKeywordSelector(ArrayRef Names) const; + + /// If this selector is the specific unary selector described by Name. + bool isUnarySelector(StringRef Name) const; + unsigned getNumArgs() const; /// Retrieve the identifier at a given position in the selector. diff --git a/include/clang/Basic/LangOptions.def b/include/clang/Basic/LangOptions.def index 31aca2b0d6950..a423654d5e038 100644 --- a/include/clang/Basic/LangOptions.def +++ b/include/clang/Basic/LangOptions.def @@ -111,6 +111,7 @@ BENIGN_LANGOPT(DollarIdents , 1, 1, "'$' in identifiers") BENIGN_LANGOPT(AsmPreprocessor, 1, 0, "preprocessor in asm mode") LANGOPT(GNUMode , 1, 1, "GNU extensions") LANGOPT(GNUKeywords , 1, 1, "GNU keywords") +VALUE_LANGOPT(GNUCVersion , 32, 0, "GNU C compatibility version") BENIGN_LANGOPT(ImplicitInt, 1, !C99 && !CPlusPlus, "C89 implicit 'int'") LANGOPT(Digraphs , 1, 0, "digraphs") BENIGN_LANGOPT(HexFloats , 1, C99, "C99 hexadecimal float constants") @@ -119,7 +120,8 @@ LANGOPT(AppleKext , 1, 0, "Apple kext support") BENIGN_LANGOPT(PascalStrings, 1, 0, "Pascal string support") LANGOPT(WritableStrings , 1, 0, "writable string support") LANGOPT(ConstStrings , 1, 0, "const-qualified string support") -LANGOPT(LaxVectorConversions , 1, 1, "lax vector conversions") +ENUM_LANGOPT(LaxVectorConversions, LaxVectorConversionKind, 2, + LaxVectorConversionKind::All, "lax vector conversions") LANGOPT(AltiVec , 1, 0, "AltiVec-style vector initializers") LANGOPT(ZVector , 1, 0, "System z vector extensions") LANGOPT(Exceptions , 1, 0, "exception handling") @@ -128,6 +130,7 @@ LANGOPT(CXXExceptions , 1, 0, "C++ exceptions") LANGOPT(DWARFExceptions , 1, 0, "dwarf exception handling") LANGOPT(SjLjExceptions , 1, 0, "setjmp-longjump exception handling") LANGOPT(SEHExceptions , 1, 0, "SEH .xdata exception handling") +LANGOPT(WasmExceptions , 1, 0, "WebAssembly exception handling") LANGOPT(ExternCNoUnwind , 1, 0, "Assume extern C functions don't unwind") LANGOPT(TraditionalCPP , 1, 0, "traditional CPP emulation") LANGOPT(RTTI , 1, 1, "run-time type information") @@ -224,6 +227,8 @@ LANGOPT(GPURelocatableDeviceCode, 1, 0, "generate relocatable device code") LANGOPT(SYCLIsDevice , 1, 0, "Generate code for SYCL device") +LANGOPT(HIPUseNewLaunchAPI, 1, 0, "Use new kernel launching API for HIP") + LANGOPT(SizedDeallocation , 1, 0, "sized deallocation") LANGOPT(AlignedAllocation , 1, 0, "aligned allocation") LANGOPT(AlignedAllocationUnavailable, 1, 0, "aligned allocation functions are unavailable") @@ -288,6 +293,10 @@ BENIGN_LANGOPT(ConstexprCallDepth, 32, 512, "maximum constexpr call depth") BENIGN_LANGOPT(ConstexprStepLimit, 32, 1048576, "maximum constexpr evaluation steps") +BENIGN_LANGOPT(EnableNewConstInterp, 1, 0, + "enable the experimental new constant interpreter") +BENIGN_LANGOPT(ForceNewConstInterp, 1, 0, + "force the use of the experimental new constant interpreter") BENIGN_LANGOPT(BracketDepth, 32, 256, "maximum bracket nesting depth") BENIGN_LANGOPT(NumLargeByValueCopy, 32, 0, diff --git a/include/clang/Basic/LangOptions.h b/include/clang/Basic/LangOptions.h index 8099eed28c5e0..5f808f04e9ae7 100644 --- a/include/clang/Basic/LangOptions.h +++ b/include/clang/Basic/LangOptions.h @@ -138,6 +138,12 @@ public: /// rather than returning the required alignment. Ver7, + /// Attempt to be ABI-compatible with code generated by Clang 9.0.x + /// (SVN r351319). This causes vectors of __int128 to be passed in memory + /// instead of passing in multiple scalar registers on x86_64 on Linux and + /// NetBSD. + Ver9, + /// Conform to the underlying platform's C and C++ ABIs as closely /// as we can. Latest @@ -178,6 +184,16 @@ public: FEA_On }; + enum class LaxVectorConversionKind { + /// Permit no implicit vector bitcasts. + None, + /// Permit vector bitcasts between integer vectors with different numbers + /// of elements but the same total bit-width. + Integer, + /// Permit vector bitcasts between all vectors with the same total + /// bit-width. + All, + }; public: /// Set of enabled sanitizers. diff --git a/include/clang/Basic/LangStandard.h b/include/clang/Basic/LangStandard.h new file mode 100644 index 0000000000000..e7deb7d64638c --- /dev/null +++ b/include/clang/Basic/LangStandard.h @@ -0,0 +1,136 @@ +//===--- LangStandard.h -----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_BASIC_LANGSTANDARD_H +#define LLVM_CLANG_BASIC_LANGSTANDARD_H + +#include "clang/Basic/LLVM.h" +#include "llvm/ADT/StringRef.h" + +namespace clang { + +/// The language for the input, used to select and validate the language +/// standard and possible actions. +enum class Language : uint8_t { + Unknown, + + /// Assembly: we accept this only so that we can preprocess it. + Asm, + + /// LLVM IR: we accept this so that we can run the optimizer on it, + /// and compile it to assembly or object code. + LLVM_IR, + + ///@{ Languages that the frontend can parse and compile. + C, + CXX, + ObjC, + ObjCXX, + OpenCL, + CUDA, + RenderScript, + HIP, + ///@} +}; + +enum LangFeatures { + LineComment = (1 << 0), + C99 = (1 << 1), + C11 = (1 << 2), + C17 = (1 << 3), + C2x = (1 << 4), + CPlusPlus = (1 << 5), + CPlusPlus11 = (1 << 6), + CPlusPlus14 = (1 << 7), + CPlusPlus17 = (1 << 8), + CPlusPlus2a = (1 << 9), + Digraphs = (1 << 10), + GNUMode = (1 << 11), + HexFloat = (1 << 12), + ImplicitInt = (1 << 13), + OpenCL = (1 << 14) +}; + +/// LangStandard - Information about the properties of a particular language +/// standard. +struct LangStandard { + enum Kind { +#define LANGSTANDARD(id, name, lang, desc, features) \ + lang_##id, +#include "clang/Basic/LangStandards.def" + lang_unspecified + }; + + const char *ShortName; + const char *Description; + unsigned Flags; + clang::Language Language; + +public: + /// getName - Get the name of this standard. + const char *getName() const { return ShortName; } + + /// getDescription - Get the description of this standard. + const char *getDescription() const { return Description; } + + /// Get the language that this standard describes. + clang::Language getLanguage() const { return Language; } + + /// Language supports '//' comments. + bool hasLineComments() const { return Flags & LineComment; } + + /// isC99 - Language is a superset of C99. + bool isC99() const { return Flags & C99; } + + /// isC11 - Language is a superset of C11. + bool isC11() const { return Flags & C11; } + + /// isC17 - Language is a superset of C17. + bool isC17() const { return Flags & C17; } + + /// isC2x - Language is a superset of C2x. + bool isC2x() const { return Flags & C2x; } + + /// isCPlusPlus - Language is a C++ variant. + bool isCPlusPlus() const { return Flags & CPlusPlus; } + + /// isCPlusPlus11 - Language is a C++11 variant (or later). + bool isCPlusPlus11() const { return Flags & CPlusPlus11; } + + /// isCPlusPlus14 - Language is a C++14 variant (or later). + bool isCPlusPlus14() const { return Flags & CPlusPlus14; } + + /// isCPlusPlus17 - Language is a C++17 variant (or later). + bool isCPlusPlus17() const { return Flags & CPlusPlus17; } + + /// isCPlusPlus2a - Language is a post-C++17 variant (or later). + bool isCPlusPlus2a() const { return Flags & CPlusPlus2a; } + + /// hasDigraphs - Language supports digraphs. + bool hasDigraphs() const { return Flags & Digraphs; } + + /// isGNUMode - Language includes GNU extensions. + bool isGNUMode() const { return Flags & GNUMode; } + + /// hasHexFloats - Language supports hexadecimal float constants. + bool hasHexFloats() const { return Flags & HexFloat; } + + /// hasImplicitInt - Language allows variables to be typed as int implicitly. + bool hasImplicitInt() const { return Flags & ImplicitInt; } + + /// isOpenCL - Language is a OpenCL variant. + bool isOpenCL() const { return Flags & OpenCL; } + + static Kind getLangKind(StringRef Name); + static const LangStandard &getLangStandardForKind(Kind K); + static const LangStandard *getLangStandardForName(StringRef Name); +}; + +} // end namespace clang + +#endif diff --git a/include/clang/Basic/LangStandards.def b/include/clang/Basic/LangStandards.def new file mode 100644 index 0000000000000..427691fb71e94 --- /dev/null +++ b/include/clang/Basic/LangStandards.def @@ -0,0 +1,189 @@ +//===-- LangStandards.def - Language Standard Data --------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LANGSTANDARD +#error "LANGSTANDARD must be defined before including this file" +#endif + +/// LANGSTANDARD(IDENT, NAME, LANG, DESC, FEATURES) +/// +/// \param IDENT - The name of the standard as a C++ identifier. +/// \param NAME - The name of the standard. +/// \param LANG - The Language for which this is a standard. +/// \param DESC - A short description of the standard. +/// \param FEATURES - The standard features as flags, these are enums from the +/// clang::frontend namespace, which is assumed to be be available. + +/// LANGSTANDARD_ALIAS(IDENT, ALIAS) +/// \param IDENT - The name of the standard as a C++ identifier. +/// \param ALIAS - The alias of the standard. + +/// LANGSTANDARD_ALIAS_DEPR(IDENT, ALIAS) +/// Same as LANGSTANDARD_ALIAS, but for a deprecated alias. + +#ifndef LANGSTANDARD_ALIAS +#define LANGSTANDARD_ALIAS(IDENT, ALIAS) +#endif + +#ifndef LANGSTANDARD_ALIAS_DEPR +#define LANGSTANDARD_ALIAS_DEPR(IDENT, ALIAS) LANGSTANDARD_ALIAS(IDENT, ALIAS) +#endif + +// C89-ish modes. +LANGSTANDARD(c89, "c89", + C, "ISO C 1990", + ImplicitInt) +LANGSTANDARD_ALIAS(c89, "c90") +LANGSTANDARD_ALIAS(c89, "iso9899:1990") + +LANGSTANDARD(c94, "iso9899:199409", + C, "ISO C 1990 with amendment 1", + Digraphs | ImplicitInt) + +LANGSTANDARD(gnu89, "gnu89", + C, "ISO C 1990 with GNU extensions", + LineComment | Digraphs | GNUMode | ImplicitInt) +LANGSTANDARD_ALIAS(gnu89, "gnu90") + +// C99-ish modes +LANGSTANDARD(c99, "c99", + C, "ISO C 1999", + LineComment | C99 | Digraphs | HexFloat) +LANGSTANDARD_ALIAS(c99, "iso9899:1999") +LANGSTANDARD_ALIAS_DEPR(c99, "c9x") +LANGSTANDARD_ALIAS_DEPR(c99, "iso9899:199x") + +LANGSTANDARD(gnu99, "gnu99", + C, "ISO C 1999 with GNU extensions", + LineComment | C99 | Digraphs | GNUMode | HexFloat) +LANGSTANDARD_ALIAS_DEPR(gnu99, "gnu9x") + +// C11 modes +LANGSTANDARD(c11, "c11", + C, "ISO C 2011", + LineComment | C99 | C11 | Digraphs | HexFloat) +LANGSTANDARD_ALIAS(c11, "iso9899:2011") +LANGSTANDARD_ALIAS_DEPR(c11, "c1x") +LANGSTANDARD_ALIAS_DEPR(c11, "iso9899:201x") + +LANGSTANDARD(gnu11, "gnu11", + C, "ISO C 2011 with GNU extensions", + LineComment | C99 | C11 | Digraphs | GNUMode | HexFloat) +LANGSTANDARD_ALIAS_DEPR(gnu11, "gnu1x") + +// C17 modes +LANGSTANDARD(c17, "c17", + C, "ISO C 2017", + LineComment | C99 | C11 | C17 | Digraphs | HexFloat) +LANGSTANDARD_ALIAS(c17, "iso9899:2017") +LANGSTANDARD_ALIAS(c17, "c18") +LANGSTANDARD_ALIAS(c17, "iso9899:2018") +LANGSTANDARD(gnu17, "gnu17", + C, "ISO C 2017 with GNU extensions", + LineComment | C99 | C11 | C17 | Digraphs | GNUMode | HexFloat) +LANGSTANDARD_ALIAS(gnu17, "gnu18") + +// C2x modes +LANGSTANDARD(c2x, "c2x", + C, "Working Draft for ISO C2x", + LineComment | C99 | C11 | C17 | C2x | Digraphs | HexFloat) +LANGSTANDARD(gnu2x, "gnu2x", + C, "Working Draft for ISO C2x with GNU extensions", + LineComment | C99 | C11 | C17 | C2x | Digraphs | GNUMode | HexFloat) + +// C++ modes +LANGSTANDARD(cxx98, "c++98", + CXX, "ISO C++ 1998 with amendments", + LineComment | CPlusPlus | Digraphs) +LANGSTANDARD_ALIAS(cxx98, "c++03") + +LANGSTANDARD(gnucxx98, "gnu++98", + CXX, "ISO C++ 1998 with amendments and GNU extensions", + LineComment | CPlusPlus | Digraphs | GNUMode) +LANGSTANDARD_ALIAS(gnucxx98, "gnu++03") + +LANGSTANDARD(cxx11, "c++11", + CXX, "ISO C++ 2011 with amendments", + LineComment | CPlusPlus | CPlusPlus11 | Digraphs) +LANGSTANDARD_ALIAS_DEPR(cxx11, "c++0x") + +LANGSTANDARD(gnucxx11, "gnu++11", CXX, + "ISO C++ 2011 with amendments and GNU extensions", + LineComment | CPlusPlus | CPlusPlus11 | Digraphs | GNUMode) +LANGSTANDARD_ALIAS_DEPR(gnucxx11, "gnu++0x") + +LANGSTANDARD(cxx14, "c++14", + CXX, "ISO C++ 2014 with amendments", + LineComment | CPlusPlus | CPlusPlus11 | CPlusPlus14 | Digraphs) +LANGSTANDARD_ALIAS_DEPR(cxx14, "c++1y") + +LANGSTANDARD(gnucxx14, "gnu++14", + CXX, "ISO C++ 2014 with amendments and GNU extensions", + LineComment | CPlusPlus | CPlusPlus11 | CPlusPlus14 | Digraphs | + GNUMode) +LANGSTANDARD_ALIAS_DEPR(gnucxx14, "gnu++1y") + +LANGSTANDARD(cxx17, "c++17", + CXX, "ISO C++ 2017 with amendments", + LineComment | CPlusPlus | CPlusPlus11 | CPlusPlus14 | CPlusPlus17 | + Digraphs | HexFloat) +LANGSTANDARD_ALIAS_DEPR(cxx17, "c++1z") + +LANGSTANDARD(gnucxx17, "gnu++17", + CXX, "ISO C++ 2017 with amendments and GNU extensions", + LineComment | CPlusPlus | CPlusPlus11 | CPlusPlus14 | CPlusPlus17 | + Digraphs | HexFloat | GNUMode) +LANGSTANDARD_ALIAS_DEPR(gnucxx17, "gnu++1z") + +LANGSTANDARD(cxx2a, "c++2a", + CXX, "Working draft for ISO C++ 2020", + LineComment | CPlusPlus | CPlusPlus11 | CPlusPlus14 | CPlusPlus17 | + CPlusPlus2a | Digraphs | HexFloat) + +LANGSTANDARD(gnucxx2a, "gnu++2a", + CXX, "Working draft for ISO C++ 2020 with GNU extensions", + LineComment | CPlusPlus | CPlusPlus11 | CPlusPlus14 | CPlusPlus17 | + CPlusPlus2a | Digraphs | HexFloat | GNUMode) + +// OpenCL +LANGSTANDARD(opencl10, "cl1.0", + OpenCL, "OpenCL 1.0", + LineComment | C99 | Digraphs | HexFloat | OpenCL) +LANGSTANDARD_ALIAS_DEPR(opencl10, "cl") + +LANGSTANDARD(opencl11, "cl1.1", + OpenCL, "OpenCL 1.1", + LineComment | C99 | Digraphs | HexFloat | OpenCL) +LANGSTANDARD(opencl12, "cl1.2", + OpenCL, "OpenCL 1.2", + LineComment | C99 | Digraphs | HexFloat | OpenCL) +LANGSTANDARD(opencl20, "cl2.0", + OpenCL, "OpenCL 2.0", + LineComment | C99 | Digraphs | HexFloat | OpenCL) +LANGSTANDARD(openclcpp, "clc++", + OpenCL, "C++ for OpenCL", + LineComment | CPlusPlus | CPlusPlus11 | CPlusPlus14 | CPlusPlus17 | + Digraphs | HexFloat | OpenCL) + +LANGSTANDARD_ALIAS_DEPR(opencl10, "CL") +LANGSTANDARD_ALIAS_DEPR(opencl11, "CL1.1") +LANGSTANDARD_ALIAS_DEPR(opencl12, "CL1.2") +LANGSTANDARD_ALIAS_DEPR(opencl20, "CL2.0") +LANGSTANDARD_ALIAS_DEPR(openclcpp, "CLC++") + +// CUDA +LANGSTANDARD(cuda, "cuda", CUDA, "NVIDIA CUDA(tm)", + LineComment | CPlusPlus | Digraphs) + +// HIP +LANGSTANDARD(hip, "hip", HIP, "HIP", + LineComment | CPlusPlus | Digraphs) + +#undef LANGSTANDARD +#undef LANGSTANDARD_ALIAS +#undef LANGSTANDARD_ALIAS_DEPR diff --git a/include/clang/Basic/Linkage.h b/include/clang/Basic/Linkage.h index 696f85b185356..f4d442c084cfe 100644 --- a/include/clang/Basic/Linkage.h +++ b/include/clang/Basic/Linkage.h @@ -82,6 +82,12 @@ inline bool isDiscardableGVALinkage(GVALinkage L) { return L <= GVA_DiscardableODR; } +/// Do we know that this will be the only definition of this symbol (excluding +/// inlining-only definitions)? +inline bool isUniqueGVALinkage(GVALinkage L) { + return L == GVA_Internal || L == GVA_StrongExternal; +} + inline bool isExternallyVisible(Linkage L) { return L >= VisibleNoLinkage; } diff --git a/include/clang/Basic/OpenCLOptions.h b/include/clang/Basic/OpenCLOptions.h index 47310da1d6d95..15661154eab54 100644 --- a/include/clang/Basic/OpenCLOptions.h +++ b/include/clang/Basic/OpenCLOptions.h @@ -42,7 +42,7 @@ public: // Is supported as either an extension or an (optional) core feature for // OpenCL version \p CLVer. - bool isSupported(llvm::StringRef Ext, LangOptions LO) const { + bool isSupported(llvm::StringRef Ext, const LangOptions &LO) const { // In C++ mode all extensions should work at least as in v2.0. auto CLVer = LO.OpenCLCPlusPlus ? 200 : LO.OpenCLVersion; auto I = OptMap.find(Ext)->getValue(); @@ -51,7 +51,7 @@ public: // Is supported (optional) OpenCL core features for OpenCL version \p CLVer. // For supported extension, return false. - bool isSupportedCore(llvm::StringRef Ext, LangOptions LO) const { + bool isSupportedCore(llvm::StringRef Ext, const LangOptions &LO) const { // In C++ mode all extensions should work at least as in v2.0. auto CLVer = LO.OpenCLCPlusPlus ? 200 : LO.OpenCLVersion; auto I = OptMap.find(Ext)->getValue(); @@ -60,7 +60,7 @@ public: // Is supported OpenCL extension for OpenCL version \p CLVer. // For supported (optional) core feature, return false. - bool isSupportedExtension(llvm::StringRef Ext, LangOptions LO) const { + bool isSupportedExtension(llvm::StringRef Ext, const LangOptions &LO) const { // In C++ mode all extensions should work at least as in v2.0. auto CLVer = LO.OpenCLCPlusPlus ? 200 : LO.OpenCLVersion; auto I = OptMap.find(Ext)->getValue(); diff --git a/include/clang/Basic/OpenMPKinds.def b/include/clang/Basic/OpenMPKinds.def index 9685af4cade8b..ff8f07aa5def4 100644 --- a/include/clang/Basic/OpenMPKinds.def +++ b/include/clang/Basic/OpenMPKinds.def @@ -92,6 +92,15 @@ #ifndef OPENMP_TASKLOOP_SIMD_CLAUSE # define OPENMP_TASKLOOP_SIMD_CLAUSE(Name) #endif +#ifndef OPENMP_MASTER_TASKLOOP_CLAUSE +# define OPENMP_MASTER_TASKLOOP_CLAUSE(Name) +#endif +#ifndef OPENMP_MASTER_TASKLOOP_SIMD_CLAUSE +# define OPENMP_MASTER_TASKLOOP_SIMD_CLAUSE(Name) +#endif +#ifndef OPENMP_PARALLEL_MASTER_TASKLOOP_CLAUSE +# define OPENMP_PARALLEL_MASTER_TASKLOOP_CLAUSE(Name) +#endif #ifndef OPENMP_CRITICAL_CLAUSE # define OPENMP_CRITICAL_CLAUSE(Name) #endif @@ -191,6 +200,15 @@ #ifndef OPENMP_ALLOCATE_CLAUSE # define OPENMP_ALLOCATE_CLAUSE(Name) #endif +#ifndef OPENMP_DEVICE_TYPE_KIND +#define OPENMP_DEVICE_TYPE_KIND(Name) +#endif +#ifndef OPENMP_DECLARE_VARIANT_CLAUSE +#define OPENMP_DECLARE_VARIANT_CLAUSE(Name) +#endif +#ifndef OPENMP_MATCH_KIND +#define OPENMP_MATCH_KIND(Name) +#endif // OpenMP directives. OPENMP_DIRECTIVE(threadprivate) @@ -248,6 +266,10 @@ OPENMP_DIRECTIVE_EXT(target_teams_distribute_parallel_for, "target teams distrib OPENMP_DIRECTIVE_EXT(target_teams_distribute_parallel_for_simd, "target teams distribute parallel for simd") OPENMP_DIRECTIVE_EXT(target_teams_distribute_simd, "target teams distribute simd") OPENMP_DIRECTIVE(allocate) +OPENMP_DIRECTIVE_EXT(declare_variant, "declare variant") +OPENMP_DIRECTIVE_EXT(master_taskloop, "master taskloop") +OPENMP_DIRECTIVE_EXT(parallel_master_taskloop, "parallel master taskloop") +OPENMP_DIRECTIVE_EXT(master_taskloop_simd, "master taskloop simd") // OpenMP clauses. OPENMP_CLAUSE(allocator, OMPAllocatorClause) @@ -656,6 +678,69 @@ OPENMP_TASKLOOP_SIMD_CLAUSE(reduction) OPENMP_TASKLOOP_SIMD_CLAUSE(in_reduction) OPENMP_TASKLOOP_SIMD_CLAUSE(allocate) +// Clauses allowed for OpenMP directive 'master taskloop'. +OPENMP_MASTER_TASKLOOP_CLAUSE(if) +OPENMP_MASTER_TASKLOOP_CLAUSE(shared) +OPENMP_MASTER_TASKLOOP_CLAUSE(private) +OPENMP_MASTER_TASKLOOP_CLAUSE(firstprivate) +OPENMP_MASTER_TASKLOOP_CLAUSE(lastprivate) +OPENMP_MASTER_TASKLOOP_CLAUSE(default) +OPENMP_MASTER_TASKLOOP_CLAUSE(collapse) +OPENMP_MASTER_TASKLOOP_CLAUSE(final) +OPENMP_MASTER_TASKLOOP_CLAUSE(untied) +OPENMP_MASTER_TASKLOOP_CLAUSE(mergeable) +OPENMP_MASTER_TASKLOOP_CLAUSE(priority) +OPENMP_MASTER_TASKLOOP_CLAUSE(grainsize) +OPENMP_MASTER_TASKLOOP_CLAUSE(nogroup) +OPENMP_MASTER_TASKLOOP_CLAUSE(num_tasks) +OPENMP_MASTER_TASKLOOP_CLAUSE(reduction) +OPENMP_MASTER_TASKLOOP_CLAUSE(in_reduction) +OPENMP_MASTER_TASKLOOP_CLAUSE(allocate) + +// Clauses allowed for OpenMP directive 'master taskloop simd'. +OPENMP_MASTER_TASKLOOP_SIMD_CLAUSE(if) +OPENMP_MASTER_TASKLOOP_SIMD_CLAUSE(shared) +OPENMP_MASTER_TASKLOOP_SIMD_CLAUSE(private) +OPENMP_MASTER_TASKLOOP_SIMD_CLAUSE(firstprivate) +OPENMP_MASTER_TASKLOOP_SIMD_CLAUSE(lastprivate) +OPENMP_MASTER_TASKLOOP_SIMD_CLAUSE(default) +OPENMP_MASTER_TASKLOOP_SIMD_CLAUSE(collapse) +OPENMP_MASTER_TASKLOOP_SIMD_CLAUSE(final) +OPENMP_MASTER_TASKLOOP_SIMD_CLAUSE(untied) +OPENMP_MASTER_TASKLOOP_SIMD_CLAUSE(mergeable) +OPENMP_MASTER_TASKLOOP_SIMD_CLAUSE(priority) +OPENMP_MASTER_TASKLOOP_SIMD_CLAUSE(linear) +OPENMP_MASTER_TASKLOOP_SIMD_CLAUSE(aligned) +OPENMP_MASTER_TASKLOOP_SIMD_CLAUSE(safelen) +OPENMP_MASTER_TASKLOOP_SIMD_CLAUSE(simdlen) +OPENMP_MASTER_TASKLOOP_SIMD_CLAUSE(grainsize) +OPENMP_MASTER_TASKLOOP_SIMD_CLAUSE(nogroup) +OPENMP_MASTER_TASKLOOP_SIMD_CLAUSE(num_tasks) +OPENMP_MASTER_TASKLOOP_SIMD_CLAUSE(reduction) +OPENMP_MASTER_TASKLOOP_SIMD_CLAUSE(in_reduction) +OPENMP_MASTER_TASKLOOP_SIMD_CLAUSE(allocate) + +// Clauses allowed for OpenMP directive 'parallel master taskloop'. +OPENMP_PARALLEL_MASTER_TASKLOOP_CLAUSE(if) +OPENMP_PARALLEL_MASTER_TASKLOOP_CLAUSE(shared) +OPENMP_PARALLEL_MASTER_TASKLOOP_CLAUSE(private) +OPENMP_PARALLEL_MASTER_TASKLOOP_CLAUSE(firstprivate) +OPENMP_PARALLEL_MASTER_TASKLOOP_CLAUSE(lastprivate) +OPENMP_PARALLEL_MASTER_TASKLOOP_CLAUSE(default) +OPENMP_PARALLEL_MASTER_TASKLOOP_CLAUSE(collapse) +OPENMP_PARALLEL_MASTER_TASKLOOP_CLAUSE(final) +OPENMP_PARALLEL_MASTER_TASKLOOP_CLAUSE(untied) +OPENMP_PARALLEL_MASTER_TASKLOOP_CLAUSE(mergeable) +OPENMP_PARALLEL_MASTER_TASKLOOP_CLAUSE(priority) +OPENMP_PARALLEL_MASTER_TASKLOOP_CLAUSE(grainsize) +OPENMP_PARALLEL_MASTER_TASKLOOP_CLAUSE(nogroup) +OPENMP_PARALLEL_MASTER_TASKLOOP_CLAUSE(num_tasks) +OPENMP_PARALLEL_MASTER_TASKLOOP_CLAUSE(reduction) +OPENMP_PARALLEL_MASTER_TASKLOOP_CLAUSE(allocate) +OPENMP_PARALLEL_MASTER_TASKLOOP_CLAUSE(num_threads) +OPENMP_PARALLEL_MASTER_TASKLOOP_CLAUSE(proc_bind) +OPENMP_PARALLEL_MASTER_TASKLOOP_CLAUSE(copyin) + // Clauses allowed for OpenMP directive 'critical'. OPENMP_CRITICAL_CLAUSE(hint) @@ -950,9 +1035,27 @@ OPENMP_TASKGROUP_CLAUSE(allocate) // Clauses allowed for OpenMP directive 'declare mapper'. OPENMP_DECLARE_MAPPER_CLAUSE(map) +// Device types for 'device_type' clause. +OPENMP_DEVICE_TYPE_KIND(host) +OPENMP_DEVICE_TYPE_KIND(nohost) +OPENMP_DEVICE_TYPE_KIND(any) + +// Clauses allowed for OpenMP directive 'declare variant'. +OPENMP_DECLARE_VARIANT_CLAUSE(match) + +// Context selectors for 'match' clause. +// TODO: add other context selectors. +OPENMP_MATCH_KIND(implementation) + +#undef OPENMP_MATCH_KIND +#undef OPENMP_DECLARE_VARIANT_CLAUSE +#undef OPENMP_DEVICE_TYPE_KIND #undef OPENMP_ALLOCATE_CLAUSE #undef OPENMP_DECLARE_MAPPER_CLAUSE #undef OPENMP_TASKGROUP_CLAUSE +#undef OPENMP_PARALLEL_MASTER_TASKLOOP_CLAUSE +#undef OPENMP_MASTER_TASKLOOP_SIMD_CLAUSE +#undef OPENMP_MASTER_TASKLOOP_CLAUSE #undef OPENMP_TASKLOOP_SIMD_CLAUSE #undef OPENMP_TASKLOOP_CLAUSE #undef OPENMP_LINEAR_KIND diff --git a/include/clang/Basic/OpenMPKinds.h b/include/clang/Basic/OpenMPKinds.h index d8dee2310ec21..4129cca0fe682 100644 --- a/include/clang/Basic/OpenMPKinds.h +++ b/include/clang/Basic/OpenMPKinds.h @@ -35,6 +35,8 @@ enum OpenMPClauseKind { #include "clang/Basic/OpenMPKinds.def" OMPC_threadprivate, OMPC_uniform, + OMPC_device_type, + OMPC_match, OMPC_unknown }; @@ -152,6 +154,14 @@ enum OpenMPAtomicDefaultMemOrderClauseKind { OMPC_ATOMIC_DEFAULT_MEM_ORDER_unknown }; +/// OpenMP device type for 'device_type' clause. +enum OpenMPDeviceType { +#define OPENMP_DEVICE_TYPE_KIND(Name) \ + OMPC_DEVICE_TYPE_##Name, +#include "clang/Basic/OpenMPKinds.def" + OMPC_DEVICE_TYPE_unknown +}; + /// Scheduling data for loop-based OpenMP directives. struct OpenMPScheduleTy final { OpenMPScheduleClauseKind Schedule = OMPC_SCHEDULE_unknown; @@ -259,7 +269,8 @@ bool isOpenMPPrivate(OpenMPClauseKind Kind); bool isOpenMPThreadPrivate(OpenMPClauseKind Kind); /// Checks if the specified directive kind is one of tasking directives - task, -/// taskloop or taksloop simd. +/// taskloop, taksloop simd, master taskloop, parallel master taskloop or master +/// taskloop simd. bool isOpenMPTaskingDirective(OpenMPDirectiveKind Kind); /// Checks if the specified directive kind is one of the composite or combined diff --git a/include/clang/Basic/OperatorKinds.h b/include/clang/Basic/OperatorKinds.h index 9757acaa53009..d661891445115 100644 --- a/include/clang/Basic/OperatorKinds.h +++ b/include/clang/Basic/OperatorKinds.h @@ -30,6 +30,25 @@ enum OverloadedOperatorKind : int { /// the preceding "operator" keyword. const char *getOperatorSpelling(OverloadedOperatorKind Operator); +/// Get the other overloaded operator that the given operator can be rewritten +/// into, if any such operator exists. +inline OverloadedOperatorKind +getRewrittenOverloadedOperator(OverloadedOperatorKind Kind) { + switch (Kind) { + case OO_Less: + case OO_LessEqual: + case OO_Greater: + case OO_GreaterEqual: + return OO_Spaceship; + + case OO_ExclaimEqual: + return OO_EqualEqual; + + default: + return OO_None; + } +} + } // end namespace clang #endif diff --git a/include/clang/Basic/SourceManager.h b/include/clang/Basic/SourceManager.h index e32f749ae6abd..3185ca0f4a25c 100644 --- a/include/clang/Basic/SourceManager.h +++ b/include/clang/Basic/SourceManager.h @@ -140,9 +140,9 @@ namespace SrcMgr { /// exist. unsigned BufferOverridden : 1; - /// True if this content cache was initially created for a source - /// file considered as a system one. - unsigned IsSystemFile : 1; + /// True if this content cache was initially created for a source file + /// considered to be volatile (likely to change between stat and open). + unsigned IsFileVolatile : 1; /// True if this file may be transient, that is, if it might not /// exist at some later point in time when this content entry is used, @@ -152,15 +152,15 @@ namespace SrcMgr { ContentCache(const FileEntry *Ent = nullptr) : ContentCache(Ent, Ent) {} ContentCache(const FileEntry *Ent, const FileEntry *contentEnt) - : Buffer(nullptr, false), OrigEntry(Ent), ContentsEntry(contentEnt), - BufferOverridden(false), IsSystemFile(false), IsTransient(false) {} + : Buffer(nullptr, false), OrigEntry(Ent), ContentsEntry(contentEnt), + BufferOverridden(false), IsFileVolatile(false), IsTransient(false) {} /// The copy ctor does not allow copies where source object has either /// a non-NULL Buffer or SourceLineCache. Ownership of allocated memory /// is not transferred, so this is a logical error. ContentCache(const ContentCache &RHS) - : Buffer(nullptr, false), BufferOverridden(false), IsSystemFile(false), - IsTransient(false) { + : Buffer(nullptr, false), BufferOverridden(false), + IsFileVolatile(false), IsTransient(false) { OrigEntry = RHS.OrigEntry; ContentsEntry = RHS.ContentsEntry; @@ -185,7 +185,7 @@ namespace SrcMgr { /// /// \param Invalid If non-NULL, will be set \c true if an error occurred. const llvm::MemoryBuffer *getBuffer(DiagnosticsEngine &Diag, - const SourceManager &SM, + FileManager &FM, SourceLocation Loc = SourceLocation(), bool *Invalid = nullptr) const; @@ -265,16 +265,21 @@ namespace SrcMgr { llvm::PointerIntPair ContentAndKind; + /// The filename that is used to access the file entry represented by the + /// content cache. + StringRef Filename; + public: /// Return a FileInfo object. static FileInfo get(SourceLocation IL, const ContentCache *Con, - CharacteristicKind FileCharacter) { + CharacteristicKind FileCharacter, StringRef Filename) { FileInfo X; X.IncludeLoc = IL.getRawEncoding(); X.NumCreatedFIDs = 0; X.HasLineDirectives = false; X.ContentAndKind.setPointer(Con); X.ContentAndKind.setInt(FileCharacter); + X.Filename = Filename; return X; } @@ -299,6 +304,10 @@ namespace SrcMgr { void setHasLineDirectives() { HasLineDirectives = true; } + + /// Returns the name of the file that was used when the file was loaded from + /// the underlying file system. + StringRef getName() const { return Filename; } }; /// Each ExpansionInfo encodes the expansion location - where @@ -821,7 +830,18 @@ public: const SrcMgr::ContentCache *IR = getOrCreateContentCache(SourceFile, isSystem(FileCharacter)); assert(IR && "getOrCreateContentCache() cannot return NULL"); - return createFileID(IR, IncludePos, FileCharacter, LoadedID, LoadedOffset); + return createFileID(IR, SourceFile->getName(), IncludePos, FileCharacter, + LoadedID, LoadedOffset); + } + + FileID createFileID(FileEntryRef SourceFile, SourceLocation IncludePos, + SrcMgr::CharacteristicKind FileCharacter, + int LoadedID = 0, unsigned LoadedOffset = 0) { + const SrcMgr::ContentCache *IR = getOrCreateContentCache( + &SourceFile.getFileEntry(), isSystem(FileCharacter)); + assert(IR && "getOrCreateContentCache() cannot return NULL"); + return createFileID(IR, SourceFile.getName(), IncludePos, FileCharacter, + LoadedID, LoadedOffset); } /// Create a new FileID that represents the specified memory buffer. @@ -832,9 +852,10 @@ public: SrcMgr::CharacteristicKind FileCharacter = SrcMgr::C_User, int LoadedID = 0, unsigned LoadedOffset = 0, SourceLocation IncludeLoc = SourceLocation()) { + StringRef Name = Buffer->getBufferIdentifier(); return createFileID( createMemBufferContentCache(Buffer.release(), /*DoNotFree*/ false), - IncludeLoc, FileCharacter, LoadedID, LoadedOffset); + Name, IncludeLoc, FileCharacter, LoadedID, LoadedOffset); } enum UnownedTag { Unowned }; @@ -847,8 +868,9 @@ public: SrcMgr::CharacteristicKind FileCharacter = SrcMgr::C_User, int LoadedID = 0, unsigned LoadedOffset = 0, SourceLocation IncludeLoc = SourceLocation()) { - return createFileID(createMemBufferContentCache(Buffer, /*DoNotFree*/true), - IncludeLoc, FileCharacter, LoadedID, LoadedOffset); + return createFileID(createMemBufferContentCache(Buffer, /*DoNotFree*/ true), + Buffer->getBufferIdentifier(), IncludeLoc, + FileCharacter, LoadedID, LoadedOffset); } /// Get the FileID for \p SourceFile if it exists. Otherwise, create a @@ -930,11 +952,12 @@ public: return false; } - /// Disable overridding the contents of a file, previously enabled - /// with #overrideFileContents. + /// Bypass the overridden contents of a file. This creates a new FileEntry + /// and initializes the content cache for it. Returns nullptr if there is no + /// such file in the filesystem. /// /// This should be called before parsing has begun. - void disableFileContentsOverride(const FileEntry *File); + const FileEntry *bypassFileContentsOverride(const FileEntry &File); /// Specify that a file is transient. void setFileIsTransient(const FileEntry *SourceFile); @@ -964,8 +987,8 @@ public: return getFakeBufferForRecovery(); } - return Entry.getFile().getContentCache()->getBuffer(Diag, *this, Loc, - Invalid); + return Entry.getFile().getContentCache()->getBuffer(Diag, getFileManager(), + Loc, Invalid); } const llvm::MemoryBuffer *getBuffer(FileID FID, @@ -979,9 +1002,8 @@ public: return getFakeBufferForRecovery(); } - return Entry.getFile().getContentCache()->getBuffer(Diag, *this, - SourceLocation(), - Invalid); + return Entry.getFile().getContentCache()->getBuffer( + Diag, getFileManager(), SourceLocation(), Invalid); } /// Returns the FileEntry record for the provided FileID. @@ -997,6 +1019,19 @@ public: return Content->OrigEntry; } + /// Returns the FileEntryRef for the provided FileID. + Optional getFileEntryRefForID(FileID FID) const { + bool Invalid = false; + const SrcMgr::SLocEntry &Entry = getSLocEntry(FID, &Invalid); + if (Invalid || !Entry.isFile()) + return None; + + const SrcMgr::ContentCache *Content = Entry.getFile().getContentCache(); + if (!Content || !Content->OrigEntry) + return None; + return FileEntryRef(Entry.getFile().getName(), *Content->OrigEntry); + } + /// Returns the FileEntry record for the provided SLocEntry. const FileEntry *getFileEntryForSLocEntry(const SrcMgr::SLocEntry &sloc) const { @@ -1785,10 +1820,10 @@ private: /// /// This works regardless of whether the ContentCache corresponds to a /// file or some other input source. - FileID createFileID(const SrcMgr::ContentCache* File, + FileID createFileID(const SrcMgr::ContentCache *File, StringRef Filename, SourceLocation IncludePos, - SrcMgr::CharacteristicKind DirCharacter, - int LoadedID, unsigned LoadedOffset); + SrcMgr::CharacteristicKind DirCharacter, int LoadedID, + unsigned LoadedOffset); const SrcMgr::ContentCache * getOrCreateContentCache(const FileEntry *SourceFile, diff --git a/include/clang/Basic/Specifiers.h b/include/clang/Basic/Specifiers.h index d1236e798e5d2..fad97a26d9572 100644 --- a/include/clang/Basic/Specifiers.h +++ b/include/clang/Basic/Specifiers.h @@ -32,7 +32,8 @@ namespace clang { enum ConstexprSpecKind { CSK_unspecified, CSK_constexpr, - CSK_consteval + CSK_consteval, + CSK_constinit }; /// Specifies the width of a type, e.g., short, long, or long long. diff --git a/include/clang/Basic/Stack.h b/include/clang/Basic/Stack.h index e0b04099de58d..3418c3bad11bd 100644 --- a/include/clang/Basic/Stack.h +++ b/include/clang/Basic/Stack.h @@ -16,11 +16,40 @@ #include +#include "llvm/ADT/STLExtras.h" +#include "llvm/Support/Compiler.h" + namespace clang { /// The amount of stack space that Clang would like to be provided with. /// If less than this much is available, we may be unable to reach our /// template instantiation depth limit and other similar limits. constexpr size_t DesiredStackSize = 8 << 20; + + /// Call this once on each thread, as soon after starting the thread as + /// feasible, to note the approximate address of the bottom of the stack. + void noteBottomOfStack(); + + /// Determine whether the stack is nearly exhausted. + bool isStackNearlyExhausted(); + + void runWithSufficientStackSpaceSlow(llvm::function_ref Diag, + llvm::function_ref Fn); + + /// Run a given function on a stack with "sufficient" space. If stack space + /// is insufficient, calls Diag to emit a diagnostic before calling Fn. + inline void runWithSufficientStackSpace(llvm::function_ref Diag, + llvm::function_ref Fn) { +#ifdef LLVM_ENABLE_THREADS + if (LLVM_UNLIKELY(isStackNearlyExhausted())) + runWithSufficientStackSpaceSlow(Diag, Fn); + else + Fn(); +#else + if (LLVM_UNLIKELY(isStackNearlyExhausted())) + Diag(); + Fn(); +#endif + } } // end namespace clang #endif // LLVM_CLANG_BASIC_STACK_H diff --git a/include/clang/Basic/StmtNodes.td b/include/clang/Basic/StmtNodes.td index be364de1a76cf..59444b2919a95 100644 --- a/include/clang/Basic/StmtNodes.td +++ b/include/clang/Basic/StmtNodes.td @@ -114,6 +114,7 @@ def GNUNullExpr : DStmt; // C++ Expressions. def CXXOperatorCallExpr : DStmt; def CXXMemberCallExpr : DStmt; +def CXXRewrittenBinaryOperator : DStmt; def CXXNamedCastExpr : DStmt; def CXXStaticCastExpr : DStmt; def CXXDynamicCastExpr : DStmt; @@ -163,6 +164,9 @@ def CoawaitExpr : DStmt; def DependentCoawaitExpr : DStmt; def CoyieldExpr : DStmt; +// C++2a Concepts expressions +def ConceptSpecializationExpr : DStmt; + // Obj-C Expressions. def ObjCStringLiteral : DStmt; def ObjCBoxedExpr : DStmt; @@ -242,6 +246,9 @@ def OMPCancellationPointDirective : DStmt; def OMPCancelDirective : DStmt; def OMPTaskLoopDirective : DStmt; def OMPTaskLoopSimdDirective : DStmt; +def OMPMasterTaskLoopDirective : DStmt; +def OMPMasterTaskLoopSimdDirective : DStmt; +def OMPParallelMasterTaskLoopDirective : DStmt; def OMPDistributeDirective : DStmt; def OMPDistributeParallelForDirective : DStmt; def OMPDistributeParallelForSimdDirective : DStmt; diff --git a/include/clang/Basic/SyncScope.h b/include/clang/Basic/SyncScope.h index 15af02d83cde1..ce8fb9cbed131 100644 --- a/include/clang/Basic/SyncScope.h +++ b/include/clang/Basic/SyncScope.h @@ -144,7 +144,7 @@ AtomicScopeModel::create(AtomicScopeModelKind K) { case AtomicScopeModelKind::None: return std::unique_ptr{}; case AtomicScopeModelKind::OpenCL: - return llvm::make_unique(); + return std::make_unique(); } llvm_unreachable("Invalid atomic scope model kind"); } diff --git a/include/clang/Basic/TargetBuiltins.h b/include/clang/Basic/TargetBuiltins.h index 50262fa310ce7..0e2f0753b0c59 100644 --- a/include/clang/Basic/TargetBuiltins.h +++ b/include/clang/Basic/TargetBuiltins.h @@ -52,6 +52,16 @@ namespace clang { }; } + /// BPF builtins + namespace BPF { + enum { + LastTIBuiltin = clang::Builtin::FirstTSBuiltin - 1, + #define BUILTIN(ID, TYPE, ATTRS) BI##ID, + #include "clang/Basic/BuiltinsBPF.def" + LastTSBuiltin + }; + } + /// PPC builtins namespace PPC { enum { diff --git a/include/clang/Basic/TargetInfo.h b/include/clang/Basic/TargetInfo.h index 7a8384f5fbc01..9a3bb986930ef 100644 --- a/include/clang/Basic/TargetInfo.h +++ b/include/clang/Basic/TargetInfo.h @@ -19,14 +19,15 @@ #include "clang/Basic/Specifiers.h" #include "clang/Basic/TargetCXXABI.h" #include "clang/Basic/TargetOptions.h" +#include "llvm/ADT/APFloat.h" #include "llvm/ADT/APInt.h" +#include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/IntrusiveRefCntPtr.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallSet.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Triple.h" -#include "llvm/IR/DataLayout.h" #include "llvm/Support/DataTypes.h" #include "llvm/Support/VersionTuple.h" #include @@ -35,6 +36,7 @@ namespace llvm { struct fltSemantics; +class DataLayout; } namespace clang { @@ -193,12 +195,12 @@ protected: unsigned IsRenderScriptTarget : 1; + unsigned HasAArch64SVETypes : 1; + // TargetInfo Constructor. Default initializes all fields. TargetInfo(const llvm::Triple &T); - void resetDataLayout(StringRef DL) { - DataLayout.reset(new llvm::DataLayout(DL)); - } + void resetDataLayout(StringRef DL); public: /// Construct a target for the given options. @@ -789,6 +791,10 @@ public: /// Returns true for RenderScript. bool isRenderScriptTarget() const { return IsRenderScriptTarget; } + /// Returns whether or not the AArch64 SVE built-in types are + /// available on this target. + bool hasAArch64SVETypes() const { return HasAArch64SVETypes; } + /// Returns whether the passed in string is a valid clobber in an /// inline asm statement. /// @@ -1249,15 +1255,9 @@ public: bool isBigEndian() const { return BigEndian; } bool isLittleEndian() const { return !BigEndian; } - enum CallingConvMethodType { - CCMT_Unknown, - CCMT_Member, - CCMT_NonMember - }; - /// Gets the default calling convention for the given target and /// declaration context. - virtual CallingConv getDefaultCallingConv(CallingConvMethodType MT) const { + virtual CallingConv getDefaultCallingConv() const { // Not all targets will specify an explicit calling convention that we can // express. This will always do the right thing, even though it's not // an explicit calling convention. @@ -1268,6 +1268,7 @@ public: CCCR_OK, CCCR_Warning, CCCR_Ignore, + CCCR_Error, }; /// Determines whether a given calling convention is valid for the diff --git a/include/clang/Basic/TokenKinds.def b/include/clang/Basic/TokenKinds.def index 55e94d387c9d4..94fe1ba63a9f6 100644 --- a/include/clang/Basic/TokenKinds.def +++ b/include/clang/Basic/TokenKinds.def @@ -68,6 +68,9 @@ #ifndef ANNOTATION #define ANNOTATION(X) TOK(annot_ ## X) #endif +#ifndef PRAGMA_ANNOTATION +#define PRAGMA_ANNOTATION(X) ANNOTATION(X) +#endif //===----------------------------------------------------------------------===// // Preprocessor keywords. @@ -386,6 +389,7 @@ MODULES_KEYWORD(import) // C++20 keywords. CXX2A_KEYWORD(char8_t , CHAR8SUPPORT) CXX2A_KEYWORD(consteval , 0) +CXX2A_KEYWORD(constinit , 0) // C11 Extension KEYWORD(_Float16 , KEYALL) @@ -446,16 +450,18 @@ TYPE_TRAIT_N(__is_nothrow_constructible, IsNothrowConstructible, KEYCXX) // MSVC14.0 / VS2015 Type Traits TYPE_TRAIT_2(__is_assignable, IsAssignable, KEYCXX) +// MSVC Type Traits of unknown vintage +TYPE_TRAIT_1(__has_nothrow_move_assign, HasNothrowMoveAssign, KEYCXX) +TYPE_TRAIT_1(__has_trivial_move_assign, HasTrivialMoveAssign, KEYCXX) +TYPE_TRAIT_1(__has_trivial_move_constructor, HasTrivialMoveConstructor, KEYCXX) + // GNU and MS Type Traits TYPE_TRAIT_1(__has_nothrow_assign, HasNothrowAssign, KEYCXX) -TYPE_TRAIT_1(__has_nothrow_move_assign, HasNothrowMoveAssign, KEYCXX) TYPE_TRAIT_1(__has_nothrow_copy, HasNothrowCopy, KEYCXX) TYPE_TRAIT_1(__has_nothrow_constructor, HasNothrowConstructor, KEYCXX) TYPE_TRAIT_1(__has_trivial_assign, HasTrivialAssign, KEYCXX) -TYPE_TRAIT_1(__has_trivial_move_assign, HasTrivialMoveAssign, KEYCXX) TYPE_TRAIT_1(__has_trivial_copy, HasTrivialCopy, KEYCXX) TYPE_TRAIT_1(__has_trivial_constructor, HasTrivialDefaultConstructor, KEYCXX) -TYPE_TRAIT_1(__has_trivial_move_constructor, HasTrivialMoveConstructor, KEYCXX) TYPE_TRAIT_1(__has_trivial_destructor, HasTrivialDestructor, KEYCXX) TYPE_TRAIT_1(__has_virtual_destructor, HasVirtualDestructor, KEYCXX) TYPE_TRAIT_1(__is_abstract, IsAbstract, KEYCXX) @@ -472,17 +478,18 @@ TYPE_TRAIT_1(__is_literal, IsLiteral, KEYCXX) ALIAS("__is_literal_type", __is_literal, KEYCXX) TYPE_TRAIT_1(__is_pod, IsPOD, KEYCXX) TYPE_TRAIT_1(__is_polymorphic, IsPolymorphic, KEYCXX) +TYPE_TRAIT_1(__is_standard_layout, IsStandardLayout, KEYCXX) TYPE_TRAIT_1(__is_trivial, IsTrivial, KEYCXX) +TYPE_TRAIT_2(__is_trivially_assignable, IsTriviallyAssignable, KEYCXX) +TYPE_TRAIT_N(__is_trivially_constructible, IsTriviallyConstructible, KEYCXX) +TYPE_TRAIT_1(__is_trivially_copyable, IsTriviallyCopyable, KEYCXX) TYPE_TRAIT_1(__is_union, IsUnion, KEYCXX) TYPE_TRAIT_1(__has_unique_object_representations, HasUniqueObjectRepresentations, KEYCXX) +KEYWORD(__underlying_type , KEYCXX) // Clang-only C++ Type Traits -TYPE_TRAIT_N(__is_trivially_constructible, IsTriviallyConstructible, KEYCXX) -TYPE_TRAIT_1(__is_trivially_copyable, IsTriviallyCopyable, KEYCXX) -TYPE_TRAIT_2(__is_trivially_assignable, IsTriviallyAssignable, KEYCXX) TYPE_TRAIT_2(__reference_binds_to_temporary, ReferenceBindsToTemporary, KEYCXX) -KEYWORD(__underlying_type , KEYCXX) // Embarcadero Expression Traits KEYWORD(__is_lvalue_expr , KEYCXX) @@ -509,7 +516,6 @@ TYPE_TRAIT_1(__is_member_function_pointer, IsMemberFunctionPointer, KEYCXX) TYPE_TRAIT_1(__is_member_pointer, IsMemberPointer, KEYCXX) TYPE_TRAIT_1(__is_const, IsConst, KEYCXX) TYPE_TRAIT_1(__is_volatile, IsVolatile, KEYCXX) -TYPE_TRAIT_1(__is_standard_layout, IsStandardLayout, KEYCXX) TYPE_TRAIT_1(__is_signed, IsSigned, KEYCXX) TYPE_TRAIT_1(__is_unsigned, IsUnsigned, KEYCXX) @@ -722,6 +728,11 @@ ANNOTATION(typename) // annotation for a C typedef name, a C++ (possibly ANNOTATION(template_id) // annotation for a C++ template-id that names a // function template specialization (not a type), // e.g., "std::swap" +ANNOTATION(non_type) // annotation for a single non-type declaration +ANNOTATION(non_type_undeclared) // annotation for an undeclared identifier that + // was assumed to be an ADL-only function name +ANNOTATION(non_type_dependent) // annotation for an assumed non-type member of + // a dependent base class ANNOTATION(primary_expr) // annotation for a primary expression ANNOTATION(decltype) // annotation for a decltype expression, // e.g., "decltype(foo.bar())" @@ -729,103 +740,103 @@ ANNOTATION(decltype) // annotation for a decltype expression, // Annotation for #pragma unused(...) // For each argument inside the parentheses the pragma handler will produce // one 'pragma_unused' annotation token followed by the argument token. -ANNOTATION(pragma_unused) +PRAGMA_ANNOTATION(pragma_unused) // Annotation for #pragma GCC visibility... // The lexer produces these so that they only take effect when the parser // handles them. -ANNOTATION(pragma_vis) +PRAGMA_ANNOTATION(pragma_vis) // Annotation for #pragma pack... // The lexer produces these so that they only take effect when the parser // handles them. -ANNOTATION(pragma_pack) +PRAGMA_ANNOTATION(pragma_pack) // Annotation for #pragma clang __debug parser_crash... // The lexer produces these so that they only take effect when the parser // handles them. -ANNOTATION(pragma_parser_crash) +PRAGMA_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) +PRAGMA_ANNOTATION(pragma_captured) // Annotation for #pragma clang __debug dump... // The lexer produces these so that the parser and semantic analysis can // look up and dump the operand. -ANNOTATION(pragma_dump) +PRAGMA_ANNOTATION(pragma_dump) // Annotation for #pragma ms_struct... // The lexer produces these so that they only take effect when the parser // handles them. -ANNOTATION(pragma_msstruct) +PRAGMA_ANNOTATION(pragma_msstruct) // Annotation for #pragma align... // The lexer produces these so that they only take effect when the parser // handles them. -ANNOTATION(pragma_align) +PRAGMA_ANNOTATION(pragma_align) // Annotation for #pragma weak id // The lexer produces these so that they only take effect when the parser // handles them. -ANNOTATION(pragma_weak) +PRAGMA_ANNOTATION(pragma_weak) // Annotation for #pragma weak id = id // The lexer produces these so that they only take effect when the parser // handles them. -ANNOTATION(pragma_weakalias) +PRAGMA_ANNOTATION(pragma_weakalias) // Annotation for #pragma redefine_extname... // The lexer produces these so that they only take effect when the parser // handles them. -ANNOTATION(pragma_redefine_extname) +PRAGMA_ANNOTATION(pragma_redefine_extname) // Annotation for #pragma STDC FP_CONTRACT... // The lexer produces these so that they only take effect when the parser // handles them. -ANNOTATION(pragma_fp_contract) +PRAGMA_ANNOTATION(pragma_fp_contract) // Annotation for #pragma STDC FENV_ACCESS // The lexer produces these so that they only take effect when the parser // handles them. -ANNOTATION(pragma_fenv_access) +PRAGMA_ANNOTATION(pragma_fenv_access) // Annotation for #pragma pointers_to_members... // The lexer produces these so that they only take effect when the parser // handles them. -ANNOTATION(pragma_ms_pointers_to_members) +PRAGMA_ANNOTATION(pragma_ms_pointers_to_members) // Annotation for #pragma vtordisp... // The lexer produces these so that they only take effect when the parser // handles them. -ANNOTATION(pragma_ms_vtordisp) +PRAGMA_ANNOTATION(pragma_ms_vtordisp) // Annotation for all microsoft #pragmas... // The lexer produces these so that they only take effect when the parser // handles them. -ANNOTATION(pragma_ms_pragma) +PRAGMA_ANNOTATION(pragma_ms_pragma) // Annotation for #pragma OPENCL EXTENSION... // The lexer produces these so that they only take effect when the parser // handles them. -ANNOTATION(pragma_opencl_extension) +PRAGMA_ANNOTATION(pragma_opencl_extension) // Annotations for OpenMP pragma directives - #pragma omp ... // The lexer produces these so that they only take effect when the parser // handles #pragma omp ... directives. -ANNOTATION(pragma_openmp) -ANNOTATION(pragma_openmp_end) +PRAGMA_ANNOTATION(pragma_openmp) +PRAGMA_ANNOTATION(pragma_openmp_end) // Annotations for loop pragma directives #pragma clang loop ... // The lexer produces these so that they only take effect when the parser // handles #pragma loop ... directives. -ANNOTATION(pragma_loop_hint) +PRAGMA_ANNOTATION(pragma_loop_hint) -ANNOTATION(pragma_fp) +PRAGMA_ANNOTATION(pragma_fp) // Annotation for the attribute pragma directives - #pragma clang attribute ... -ANNOTATION(pragma_attribute) +PRAGMA_ANNOTATION(pragma_attribute) // Annotations for module import translated from #include etc. ANNOTATION(module_include) @@ -836,6 +847,7 @@ ANNOTATION(module_end) // into the name of a header unit. ANNOTATION(header_unit) +#undef PRAGMA_ANNOTATION #undef ANNOTATION #undef TESTING_KEYWORD #undef OBJC_AT_KEYWORD diff --git a/include/clang/Basic/TokenKinds.h b/include/clang/Basic/TokenKinds.h index 1d5be5f9152eb..c25181e6827c7 100644 --- a/include/clang/Basic/TokenKinds.h +++ b/include/clang/Basic/TokenKinds.h @@ -90,13 +90,10 @@ inline bool isLiteral(TokenKind K) { } /// Return true if this is any of tok::annot_* kinds. -inline bool isAnnotation(TokenKind K) { -#define ANNOTATION(NAME) \ - if (K == tok::annot_##NAME) \ - return true; -#include "clang/Basic/TokenKinds.def" - return false; -} +bool isAnnotation(TokenKind K); + +/// Return true if this is an annotation token representing a pragma. +bool isPragmaAnnotation(TokenKind K); } // end namespace tok } // end namespace clang diff --git a/include/clang/Basic/TypeNodes.td b/include/clang/Basic/TypeNodes.td new file mode 100644 index 0000000000000..b2554de24aaf4 --- /dev/null +++ b/include/clang/Basic/TypeNodes.td @@ -0,0 +1,106 @@ +class Type { + bit Abstract = abstract; +} + +class DerivedType : Type { + Type Base = base; +} + +/// A type node that is only used to represent dependent types in C++. For +/// example, DependentTemplateSpecializationType is used to represent types +/// where the base template-id is dependent (such as `T::foo`). Code +/// that only works with non-dependent types can ignore these type nodes. +class AlwaysDependent {} + +/// A type node that is never used to represent a canonical type, which is to +/// say that it always represents some sort of type "sugar" which can +/// (supposedly) be erased without affecting the formal behavior of the +/// language. For example, in standard C/C++, typedefs do not introduce new +/// types and do not affect the semantics of the program. Code that only +/// works with canonical types can ignore these type nodes. +/// +/// Note that this simple story about non-canonical types is not the whole +/// truth. Languages and extensions often have formation rules which differ +/// based on how a type is spelled and which therefore are not consistent +/// with immediately stipping away type sugar. More critically, attributes on +/// typedefs can have semantic impacts in ways that are only reflected in our +/// AST by preserving the typedef sugar; for example, we do not otherwise +/// represent the alignment attribute on typedefs, and so it is necessary to +/// preserve typedef structure into most parts of IR generation. +class NeverCanonical {} + +/// A type node that only represents a canonical type in some dependent cases. +/// For example, `std::vector` (a TemplateSpecializationType) is +/// considered to be a non-canonical representation for the RecordType +/// referencing the concrete ClassTemplateSpecializationDecl; but +/// `std::vector` cannot be resolved to a concrete specialization +/// and so remains canonical. Code which only works with non-dependent +/// canonical types can ignore these nodes. +class NeverCanonicalUnlessDependent {} + +/// A type node which never has component type structure. Some code may be +/// able to operate on leaf types faster than they can on non-leaf types. +/// +/// For example, the function type `void (int)` is not a leaf type because it +/// is structurally composed of component types (`void` and `int`). +/// +/// A struct type is a leaf type because its field types are not part of its +/// type-expression. +/// +/// Nodes like `TypedefType` which are syntactically leaves but can desugar +/// to types that may not be leaves should not declare this. +class LeafType {} + +def BuiltinType : Type, LeafType; +def ComplexType : Type; +def PointerType : Type; +def BlockPointerType : Type; +def ReferenceType : Type<1>; +def LValueReferenceType : DerivedType; +def RValueReferenceType : DerivedType; +def MemberPointerType : Type; +def ArrayType : Type<1>; +def ConstantArrayType : DerivedType; +def IncompleteArrayType : DerivedType; +def VariableArrayType : DerivedType; +def DependentSizedArrayType : DerivedType, AlwaysDependent; +def DependentSizedExtVectorType : Type, AlwaysDependent; +def DependentAddressSpaceType : Type, AlwaysDependent; +def VectorType : Type; +def DependentVectorType : Type, AlwaysDependent; +def ExtVectorType : DerivedType; +def FunctionType : Type<1>; +def FunctionProtoType : DerivedType; +def FunctionNoProtoType : DerivedType; +def UnresolvedUsingType : Type, AlwaysDependent; +def ParenType : Type, NeverCanonical; +def TypedefType : Type, NeverCanonical; +def MacroQualifiedType : Type, NeverCanonical; +def AdjustedType : Type, NeverCanonical; +def DecayedType : DerivedType, NeverCanonical; +def TypeOfExprType : Type, NeverCanonicalUnlessDependent; +def TypeOfType : Type, NeverCanonicalUnlessDependent; +def DecltypeType : Type, NeverCanonicalUnlessDependent; +def UnaryTransformType : Type, NeverCanonicalUnlessDependent; +def TagType : Type<1>; +def RecordType : DerivedType, LeafType; +def EnumType : DerivedType, LeafType; +def ElaboratedType : Type, NeverCanonical; +def AttributedType : Type, NeverCanonical; +def TemplateTypeParmType : Type, AlwaysDependent, LeafType; +def SubstTemplateTypeParmType : Type, NeverCanonical; +def SubstTemplateTypeParmPackType : Type, AlwaysDependent; +def TemplateSpecializationType : Type, NeverCanonicalUnlessDependent; +def DeducedType : Type<1>; +def AutoType : DerivedType; +def DeducedTemplateSpecializationType : DerivedType; +def InjectedClassNameType : Type, AlwaysDependent, LeafType; +def DependentNameType : Type, AlwaysDependent; +def DependentTemplateSpecializationType : Type, AlwaysDependent; +def PackExpansionType : Type, NeverCanonicalUnlessDependent; +def ObjCTypeParamType : Type, NeverCanonical; +def ObjCObjectType : Type; +def ObjCInterfaceType : DerivedType, LeafType; +def ObjCObjectPointerType : Type; +def PipeType : Type; +def AtomicType : Type; diff --git a/include/clang/Basic/X86Target.def b/include/clang/Basic/X86Target.def index 94ccb9fd8b2f5..ba4e5981e7dcc 100644 --- a/include/clang/Basic/X86Target.def +++ b/include/clang/Basic/X86Target.def @@ -173,6 +173,10 @@ PROC(IcelakeClient, "icelake-client", PROC_64_BIT) /// Icelake server microarchitecture based processors. PROC(IcelakeServer, "icelake-server", PROC_64_BIT) +/// \name Tigerlake +/// Tigerlake microarchitecture based processors. +PROC(Tigerlake, "tigerlake", PROC_64_BIT) + /// \name Knights Landing /// Knights Landing processor. PROC_WITH_FEAT(KNL, "knl", PROC_64_BIT, FEATURE_AVX512F) @@ -297,6 +301,7 @@ FEATURE(FEATURE_VPCLMULQDQ) FEATURE(FEATURE_AVX512VNNI) FEATURE(FEATURE_AVX512BITALG) FEATURE(FEATURE_AVX512BF16) +FEATURE(FEATURE_AVX512VP2INTERSECT) // FIXME: When commented out features are supported in LLVM, enable them here. diff --git a/include/clang/Basic/arm_neon.td b/include/clang/Basic/arm_neon.td index 428c22d1a0111..a52ed496580d6 100644 --- a/include/clang/Basic/arm_neon.td +++ b/include/clang/Basic/arm_neon.td @@ -1651,10 +1651,10 @@ let ArchGuard = "defined(__ARM_FEATURE_DOTPROD) && defined(__aarch64__)" in { // v8.2-A FP16 fused multiply-add long instructions. let ArchGuard = "defined(__ARM_FEATURE_FP16FML) && defined(__aarch64__)" in { - def VFMLAL_LOW : SInst<"vfmlal_low", "ffHH", "hQh">; - def VFMLSL_LOW : SInst<"vfmlsl_low", "ffHH", "hQh">; - def VFMLAL_HIGH : SInst<"vfmlal_high", "ffHH", "hQh">; - def VFMLSL_HIGH : SInst<"vfmlsl_high", "ffHH", "hQh">; + def VFMLAL_LOW : SInst<"vfmlal_low", "nndd", "hQh">; + def VFMLSL_LOW : SInst<"vfmlsl_low", "nndd", "hQh">; + def VFMLAL_HIGH : SInst<"vfmlal_high", "nndd", "hQh">; + def VFMLSL_HIGH : SInst<"vfmlsl_high", "nndd", "hQh">; def VFMLAL_LANE_LOW : SOpInst<"vfmlal_lane_low", "ffH0i", "hQh", OP_FMLAL_LN>; def VFMLSL_LANE_LOW : SOpInst<"vfmlsl_lane_low", "ffH0i", "hQh", OP_FMLSL_LN>; diff --git a/include/clang/CodeGen/CGFunctionInfo.h b/include/clang/CodeGen/CGFunctionInfo.h index 1f81072e23d00..5069d9af42a3c 100644 --- a/include/clang/CodeGen/CGFunctionInfo.h +++ b/include/clang/CodeGen/CGFunctionInfo.h @@ -109,14 +109,12 @@ private: UnpaddedCoerceAndExpandType = T; } - ABIArgInfo(Kind K) - : TheKind(K), PaddingInReg(false), InReg(false) { - } - public: - ABIArgInfo() + ABIArgInfo(Kind K = Direct) : TypeData(nullptr), PaddingType(nullptr), DirectOffset(0), - TheKind(Direct), PaddingInReg(false), InReg(false) {} + TheKind(K), PaddingInReg(false), InAllocaSRet(false), + IndirectByVal(false), IndirectRealign(false), SRetAfterThis(false), + InReg(false), CanBeFlattened(false), SignExt(false) {} static ABIArgInfo getDirect(llvm::Type *T = nullptr, unsigned Offset = 0, llvm::Type *Padding = nullptr, diff --git a/include/clang/CrossTU/CrossTranslationUnit.h b/include/clang/CrossTU/CrossTranslationUnit.h index d64329cdff3e6..4d2b7109c62a6 100644 --- a/include/clang/CrossTU/CrossTranslationUnit.h +++ b/include/clang/CrossTU/CrossTranslationUnit.h @@ -17,6 +17,7 @@ #include "clang/AST/ASTImporterSharedState.h" #include "clang/Basic/LLVM.h" #include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/StringMap.h" #include "llvm/Support/Error.h" @@ -153,18 +154,34 @@ public: /// was passed to the constructor. /// /// \return Returns the resulting definition or an error. - llvm::Expected importDefinition(const FunctionDecl *FD); - llvm::Expected importDefinition(const VarDecl *VD); + llvm::Expected importDefinition(const FunctionDecl *FD, + ASTUnit *Unit); + llvm::Expected importDefinition(const VarDecl *VD, + ASTUnit *Unit); /// Get a name to identify a named decl. - static std::string getLookupName(const NamedDecl *ND); + static llvm::Optional getLookupName(const NamedDecl *ND); /// Emit diagnostics for the user for potential configuration errors. void emitCrossTUDiagnostics(const IndexError &IE); + /// Determine the original source location in the original TU for an + /// imported source location. + /// \p ToLoc Source location in the imported-to AST. + /// \return Source location in the imported-from AST and the corresponding + /// ASTUnit object (the AST was loaded from a file using an internal ASTUnit + /// object that is returned here). + /// If any error happens (ToLoc is a non-imported source location) empty is + /// returned. + llvm::Optional> + getImportedFromSourceLocation(const clang::SourceLocation &ToLoc) const; + private: + using ImportedFileIDMap = + llvm::DenseMap>; + void lazyInitImporterSharedSt(TranslationUnitDecl *ToTU); - ASTImporter &getOrCreateASTImporter(ASTContext &From); + ASTImporter &getOrCreateASTImporter(ASTUnit *Unit); template llvm::Expected getCrossTUDefinitionImpl(const T *D, StringRef CrossTUDir, @@ -174,20 +191,114 @@ private: const T *findDefInDeclContext(const DeclContext *DC, StringRef LookupName); template - llvm::Expected importDefinitionImpl(const T *D); - - llvm::StringMap> FileASTUnitMap; - llvm::StringMap NameASTUnitMap; - llvm::StringMap NameFileMap; - llvm::DenseMap> - ASTUnitImporterMap; - CompilerInstance &CI; + llvm::Expected importDefinitionImpl(const T *D, ASTUnit *Unit); + + using ImporterMapTy = + llvm::DenseMap>; + + ImporterMapTy ASTUnitImporterMap; + ASTContext &Context; std::shared_ptr ImporterSharedSt; - /// \p CTULoadTreshold should serve as an upper limit to the number of TUs - /// imported in order to reduce the memory footprint of CTU analysis. - const unsigned CTULoadThreshold; - unsigned NumASTLoaded{0u}; + /// Map of imported FileID's (in "To" context) to FileID in "From" context + /// and the ASTUnit for the From context. + /// This map is used by getImportedFromSourceLocation to lookup a FileID and + /// its Preprocessor when knowing only the FileID in the 'To' context. The + /// FileID could be imported by any of multiple 'From' ASTImporter objects. + /// we do not want to loop over all ASTImporter's to find the one that + /// imported the FileID. + ImportedFileIDMap ImportedFileIDs; + + /// Functor for loading ASTUnits from AST-dump files. + class ASTFileLoader { + public: + ASTFileLoader(const CompilerInstance &CI); + std::unique_ptr operator()(StringRef ASTFilePath); + + private: + const CompilerInstance &CI; + }; + + /// Maintain number of AST loads and check for reaching the load limit. + class ASTLoadGuard { + public: + ASTLoadGuard(unsigned Limit) : Limit(Limit) {} + + /// Indicates, whether a new load operation is permitted, it is within the + /// threshold. + operator bool() const { return Count < Limit; } + + /// Tell that a new AST was loaded successfully. + void indicateLoadSuccess() { ++Count; } + + private: + /// The number of ASTs actually imported. + unsigned Count{0u}; + /// The limit (threshold) value for number of loaded ASTs. + const unsigned Limit; + }; + + /// Storage and load of ASTUnits, cached access, and providing searchability + /// are the concerns of ASTUnitStorage class. + class ASTUnitStorage { + public: + ASTUnitStorage(const CompilerInstance &CI); + /// Loads an ASTUnit for a function. + /// + /// \param FunctionName USR name of the function. + /// \param CrossTUDir Path to the directory used to store CTU related files. + /// \param IndexName Name of the file inside \p CrossTUDir which maps + /// function USR names to file paths. These files contain the corresponding + /// AST-dumps. + /// \param DisplayCTUProgress Display a message about loading new ASTs. + /// + /// \return An Expected instance which contains the ASTUnit pointer or the + /// error occured during the load. + llvm::Expected getASTUnitForFunction(StringRef FunctionName, + StringRef CrossTUDir, + StringRef IndexName, + bool DisplayCTUProgress); + /// Identifies the path of the file which can be used to load the ASTUnit + /// for a given function. + /// + /// \param FunctionName USR name of the function. + /// \param CrossTUDir Path to the directory used to store CTU related files. + /// \param IndexName Name of the file inside \p CrossTUDir which maps + /// function USR names to file paths. These files contain the corresponding + /// AST-dumps. + /// + /// \return An Expected instance containing the filepath. + llvm::Expected getFileForFunction(StringRef FunctionName, + StringRef CrossTUDir, + StringRef IndexName); + + private: + llvm::Error ensureCTUIndexLoaded(StringRef CrossTUDir, StringRef IndexName); + llvm::Expected getASTUnitForFile(StringRef FileName, + bool DisplayCTUProgress); + + template using BaseMapTy = llvm::StringMap; + using OwningMapTy = BaseMapTy>; + using NonOwningMapTy = BaseMapTy; + + OwningMapTy FileASTUnitMap; + NonOwningMapTy NameASTUnitMap; + + using IndexMapTy = BaseMapTy; + IndexMapTy NameFileMap; + + ASTFileLoader FileAccessor; + + /// Limit the number of loaded ASTs. Used to limit the memory usage of the + /// CrossTranslationUnitContext. + /// The ASTUnitStorage has the knowledge about if the AST to load is + /// actually loaded or returned from cache. This information is needed to + /// maintain the counter. + ASTLoadGuard LoadGuard; + }; + + ASTUnitStorage ASTStorage; + }; } // namespace cross_tu diff --git a/include/clang/DirectoryWatcher/DirectoryWatcher.h b/include/clang/DirectoryWatcher/DirectoryWatcher.h index e74443e0bc81c..4475807dfce9a 100644 --- a/include/clang/DirectoryWatcher/DirectoryWatcher.h +++ b/include/clang/DirectoryWatcher/DirectoryWatcher.h @@ -11,6 +11,7 @@ #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/StringRef.h" +#include "llvm/Support/Error.h" #include #include #include @@ -98,10 +99,11 @@ public: : Kind(Kind), Filename(Filename) {} }; - /// Returns nullptr if \param Path doesn't exist or isn't a directory. - /// Returns nullptr if OS kernel API told us we can't start watching. In such - /// case it's unclear whether just retrying has any chance to succeeed. - static std::unique_ptr + /// llvm fatal_error if \param Path doesn't exist or isn't a directory. + /// Returns llvm::Expected Error if OS kernel API told us we can't start + /// watching. In such case it's unclear whether just retrying has any chance + /// to succeed. + static llvm::Expected> create(llvm::StringRef Path, std::function Events, bool IsInitial)> diff --git a/include/clang/Driver/Action.h b/include/clang/Driver/Action.h index c1ff0b1a60230..8ccbb6c2bbfa2 100644 --- a/include/clang/Driver/Action.h +++ b/include/clang/Driver/Action.h @@ -65,15 +65,17 @@ public: BackendJobClass, AssembleJobClass, LinkJobClass, + IfsMergeJobClass, LipoJobClass, DsymutilJobClass, VerifyDebugInfoJobClass, VerifyPCHJobClass, OffloadBundlingJobClass, OffloadUnbundlingJobClass, + OffloadWrapperJobClass, JobClassFirst = PreprocessJobClass, - JobClassLast = OffloadUnbundlingJobClass + JobClassLast = OffloadWrapperJobClass }; // The offloading kind determines if this action is binded to a particular @@ -485,6 +487,17 @@ public: } }; +class IfsMergeJobAction : public JobAction { + void anchor() override; + +public: + IfsMergeJobAction(ActionList &Inputs, types::ID Type); + + static bool classof(const Action *A) { + return A->getKind() == IfsMergeJobClass; + } +}; + class LinkJobAction : public JobAction { void anchor() override; @@ -613,6 +626,17 @@ public: } }; +class OffloadWrapperJobAction : public JobAction { + void anchor() override; + +public: + OffloadWrapperJobAction(ActionList &Inputs, types::ID Type); + + static bool classof(const Action *A) { + return A->getKind() == OffloadWrapperJobClass; + } +}; + } // namespace driver } // namespace clang diff --git a/include/clang/Driver/CC1Options.td b/include/clang/Driver/CC1Options.td index 1f6c000ecf6a1..4518aca82ef6b 100644 --- a/include/clang/Driver/CC1Options.td +++ b/include/clang/Driver/CC1Options.td @@ -140,7 +140,8 @@ def analyzer_checker_help_developer : Flag<["-"], "analyzer-checker-help-develop "and debug checkers">; def analyzer_config_help : Flag<["-"], "analyzer-config-help">, - HelpText<"Display the list of -analyzer-config options">; + HelpText<"Display the list of -analyzer-config options. These are meant for " + "development purposes only!">; def analyzer_list_enabled_checkers : Flag<["-"], "analyzer-list-enabled-checkers">, HelpText<"Display the list of enabled analyzer checkers">; @@ -200,6 +201,8 @@ def compress_debug_sections_EQ : Joined<["-", "--"], "compress-debug-sections="> HelpText<"DWARF debug sections compression type">; def mno_exec_stack : Flag<["-"], "mnoexecstack">, HelpText<"Mark the file as not needing an executable stack">; +def massembler_no_warn : Flag<["-"], "massembler-no-warn">, + HelpText<"Make assembler not emit warnings">; def massembler_fatal_warnings : Flag<["-"], "massembler-fatal-warnings">, HelpText<"Make assembler warnings fatal">; def mrelax_relocations : Flag<["--"], "mrelax-relocations">, @@ -285,8 +288,8 @@ def mcode_model : Separate<["-"], "mcode-model">, HelpText<"The code model to use">, Values<"tiny,small,kernel,medium,large">; def mdebug_pass : Separate<["-"], "mdebug-pass">, HelpText<"Enable additional debug output">; -def mdisable_fp_elim : Flag<["-"], "mdisable-fp-elim">, - HelpText<"Disable frame pointer elimination optimization">; +def mframe_pointer_EQ : Joined<["-"], "mframe-pointer=">, + HelpText<"Specify which frame pointers to retain (all, non-leaf, none).">, Values<"all,non-leaf,none">; def mdisable_tail_calls : Flag<["-"], "mdisable-tail-calls">, HelpText<"Disable tail call optimization, keeping the call stack accurate">; def menable_no_infinities : Flag<["-"], "menable-no-infs">, @@ -684,7 +687,7 @@ let Flags = [CC1Option, CC1AsOption, NoDriverOption] in { def version : Flag<["-"], "version">, HelpText<"Print the compiler version">; def main_file_name : Separate<["-"], "main-file-name">, - HelpText<"Main file name to use for debug info">; + HelpText<"Main file name to use for debug info and source if missing">; def split_dwarf_output : Separate<["-"], "split-dwarf-output">, HelpText<"File name to use for split dwarf debug info output">; @@ -812,6 +815,9 @@ def fdisable_module_hash : Flag<["-"], "fdisable-module-hash">, HelpText<"Disable the module hash">; def fmodules_hash_content : Flag<["-"], "fmodules-hash-content">, HelpText<"Enable hashing the content of a module file">; +def fmodules_strict_context_hash : Flag<["-"], "fmodules-strict-context-hash">, + HelpText<"Enable hashing of all compiler options that could impact the " + "semantics of a module in an implicit build">; def c_isystem : JoinedOrSeparate<["-"], "c-isystem">, MetaVarName<"">, HelpText<"Add directory to the C SYSTEM include search path">; def objc_isystem : JoinedOrSeparate<["-"], "objc-isystem">, @@ -843,6 +849,8 @@ def preamble_bytes_EQ : Joined<["-"], "preamble-bytes=">, "covering the first N bytes of the main file">; def detailed_preprocessing_record : Flag<["-"], "detailed-preprocessing-record">, HelpText<"include a detailed record of preprocessing actions">; +def setup_static_analyzer : Flag<["-"], "setup-static-analyzer">, + HelpText<"Set up preprocessor for static analyzer (done automatically when static analyzer is run).">; //===----------------------------------------------------------------------===// // OpenCL Options diff --git a/include/clang/Driver/CLCompatOptions.td b/include/clang/Driver/CLCompatOptions.td index a0af3035ea46b..50d4622009c95 100644 --- a/include/clang/Driver/CLCompatOptions.td +++ b/include/clang/Driver/CLCompatOptions.td @@ -254,7 +254,13 @@ def _SLASH_Zp_flag : CLFlag<"Zp">, Alias, AliasArgs<["1"]>; def _SLASH_Zs : CLFlag<"Zs">, HelpText<"Syntax-check only">, Alias; - +def _SLASH_openmp_ : CLFlag<"openmp-">, + HelpText<"Disable OpenMP support">, Alias; +def _SLASH_openmp : CLFlag<"openmp">, HelpText<"Enable OpenMP support">, + Alias; +def _SLASH_openmp_experimental : CLFlag<"openmp:experimental">, + HelpText<"Enable OpenMP support with experimental SIMD support">, + Alias; // Non-aliases: @@ -381,7 +387,6 @@ def _SLASH_FS : CLIgnoredFlag<"FS">; def _SLASH_JMC : CLIgnoredFlag<"JMC">; def _SLASH_kernel_ : CLIgnoredFlag<"kernel-">; def _SLASH_nologo : CLIgnoredFlag<"nologo">; -def _SLASH_openmp_ : CLIgnoredFlag<"openmp-">; def _SLASH_permissive_ : CLIgnoredFlag<"permissive-">; def _SLASH_RTC : CLIgnoredJoined<"RTC">; def _SLASH_sdl : CLIgnoredFlag<"sdl">; @@ -396,6 +401,9 @@ def _SLASH_Zc_inline : CLIgnoredFlag<"Zc:inline">; def _SLASH_Zc_rvalueCast : CLIgnoredFlag<"Zc:rvalueCast">; def _SLASH_Zc_ternary : CLIgnoredFlag<"Zc:ternary">; def _SLASH_Zc_wchar_t : CLIgnoredFlag<"Zc:wchar_t">; +def _SLASH_ZH_MD5 : CLIgnoredFlag<"ZH:MD5">; +def _SLASH_ZH_SHA1 : CLIgnoredFlag<"ZH:SHA1">; +def _SLASH_ZH_SHA_256 : CLIgnoredFlag<"ZH:SHA_256">; def _SLASH_Zm : CLIgnoredJoined<"Zm">; def _SLASH_Zo : CLIgnoredFlag<"Zo">; def _SLASH_Zo_ : CLIgnoredFlag<"Zo-">; @@ -436,8 +444,6 @@ def _SLASH_hotpatch : CLFlag<"hotpatch">; def _SLASH_kernel : CLFlag<"kernel">; def _SLASH_LN : CLFlag<"LN">; def _SLASH_MP : CLJoined<"MP">; -def _SLASH_openmp : CLFlag<"openmp">; -def _SLASH_openmp_experimental : CLFlag<"openmp:experimental">; def _SLASH_Qfast_transcendentals : CLFlag<"Qfast_transcendentals">; def _SLASH_QIfist : CLFlag<"QIfist">; def _SLASH_Qimprecise_fwaits : CLFlag<"Qimprecise_fwaits">; diff --git a/include/clang/Driver/Driver.h b/include/clang/Driver/Driver.h index f9528641073a4..5e7283e31ee06 100644 --- a/include/clang/Driver/Driver.h +++ b/include/clang/Driver/Driver.h @@ -12,12 +12,14 @@ #include "clang/Basic/Diagnostic.h" #include "clang/Basic/LLVM.h" #include "clang/Driver/Action.h" +#include "clang/Driver/Options.h" #include "clang/Driver/Phases.h" #include "clang/Driver/ToolChain.h" #include "clang/Driver/Types.h" #include "clang/Driver/Util.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" +#include "llvm/Option/Arg.h" #include "llvm/Option/ArgList.h" #include "llvm/Support/StringSaver.h" @@ -55,8 +57,6 @@ enum LTOKind { /// Driver - Encapsulate logic for constructing compilation processes /// from a set of gcc-driver-like command line arguments. class Driver { - std::unique_ptr Opts; - DiagnosticsEngine &Diags; IntrusiveRefCntPtr VFS; @@ -250,9 +250,17 @@ private: // getFinalPhase - Determine which compilation mode we are in and record // which option we used to determine the final phase. + // TODO: Much of what getFinalPhase returns are not actually true compiler + // modes. Fold this functionality into Types::getCompilationPhases and + // handleArguments. phases::ID getFinalPhase(const llvm::opt::DerivedArgList &DAL, llvm::opt::Arg **FinalPhaseArg = nullptr) const; + // handleArguments - All code related to claiming and printing diagnostics + // related to arguments to the driver are done here. + void handleArguments(Compilation &C, llvm::opt::DerivedArgList &Args, + const InputList &Inputs, ActionList &Actions) const; + // Before executing jobs, sets up response files for commands that need them. void setUpResponseFiles(Compilation &C, Command &Cmd); @@ -292,7 +300,7 @@ public: const std::string &getConfigFile() const { return ConfigFile; } - const llvm::opt::OptTable &getOpts() const { return *Opts; } + const llvm::opt::OptTable &getOpts() const { return getDriverOptTable(); } const DiagnosticsEngine &getDiags() const { return Diags; } diff --git a/include/clang/Driver/Options.h b/include/clang/Driver/Options.h index f8963d48112a0..7c5cddd9e8960 100644 --- a/include/clang/Driver/Options.h +++ b/include/clang/Driver/Options.h @@ -47,7 +47,7 @@ enum ID { }; } -std::unique_ptr createDriverOptTable(); +const llvm::opt::OptTable &getDriverOptTable(); } } diff --git a/include/clang/Driver/Options.td b/include/clang/Driver/Options.td index dfd27fab796e3..3ce6fcf29f94f 100644 --- a/include/clang/Driver/Options.td +++ b/include/clang/Driver/Options.td @@ -211,6 +211,10 @@ def clang_ignored_legacy_options_Group : OptionGroup<"">, def : Flag<["-"], "fslp-vectorize-aggressive">, Group; def : Flag<["-"], "fno-slp-vectorize-aggressive">, Group; +// Retired with clang-10.0. Previously controlled X86 MPX ISA. +def mmpx : Flag<["-"], "mmpx">, Group; +def mno_mpx : Flag<["-"], "mno-mpx">, Group; + // Group that ignores all gcc optimizations that won't be implemented def clang_ignored_gcc_optimization_f_Group : OptionGroup< "">, Group, Flags<[Ignored]>; @@ -280,6 +284,8 @@ def arcmt_migrate_emit_arc_errors : Flag<["-"], "arcmt-migrate-emit-errors">, Flags<[CC1Option]>; def gen_reproducer: Flag<["-"], "gen-reproducer">, InternalDebugOpt, HelpText<"Auto-generates preprocessed source files and a reproduction script">; +def gen_cdb_fragment_path: Separate<["-"], "gen-cdb-fragment-path">, InternalDebugOpt, + HelpText<"Emit a compilation database fragment to the specified directory">; def _migrate : Flag<["--"], "migrate">, Flags<[DriverOption]>, HelpText<"Run the migrator">; @@ -518,7 +524,7 @@ def cl_mad_enable : Flag<["-"], "cl-mad-enable">, Group, Flags<[CC def cl_no_signed_zeros : Flag<["-"], "cl-no-signed-zeros">, Group, Flags<[CC1Option]>, HelpText<"OpenCL only. Allow use of less precise no signed zeros computations in the generated binary.">; def cl_std_EQ : Joined<["-"], "cl-std=">, Group, Flags<[CC1Option]>, - HelpText<"OpenCL language standard to compile for.">, Values<"cl,CL,cl1.1,CL1.1,cl1.2,CL1.2,cl2.0,CL2.0,c++">; + HelpText<"OpenCL language standard to compile for.">, Values<"cl,CL,cl1.1,CL1.1,cl1.2,CL1.2,cl2.0,CL2.0,clc++,CLC++">; def cl_denorms_are_zero : Flag<["-"], "cl-denorms-are-zero">, Group, Flags<[CC1Option]>, HelpText<"OpenCL only. Allow denormals to be flushed to zero.">; def cl_fp32_correctly_rounded_divide_sqrt : Flag<["-"], "cl-fp32-correctly-rounded-divide-sqrt">, Group, Flags<[CC1Option]>, @@ -593,9 +599,11 @@ def hip_device_lib_EQ : Joined<["--"], "hip-device-lib=">, Group, HelpText<"HIP device library">; def fhip_dump_offload_linker_script : Flag<["-"], "fhip-dump-offload-linker-script">, Group, Flags<[NoArgumentUnused, HelpHidden]>; +def fhip_new_launch_api : Flag<["-"], "fhip-new-launch-api">, + Flags<[CC1Option]>, HelpText<"Use new kernel launching API for HIP.">; +def fno_hip_new_launch_api : Flag<["-"], "fno-hip-new-launch-api">; def libomptarget_nvptx_path_EQ : Joined<["--"], "libomptarget-nvptx-path=">, Group, HelpText<"Path to libomptarget-nvptx libraries">; -def dA : Flag<["-"], "dA">, Group; def dD : Flag<["-"], "dD">, Group, Flags<[CC1Option]>, HelpText<"Print macro definitions in -E mode in addition to normal output">; def dI : Flag<["-"], "dI">, Group, Flags<[CC1Option]>, @@ -623,9 +631,12 @@ def emit_ast : Flag<["-"], "emit-ast">, HelpText<"Emit Clang AST files for source inputs">; def emit_llvm : Flag<["-"], "emit-llvm">, Flags<[CC1Option]>, Group, HelpText<"Use the LLVM representation for assembler and object files">; -def emit_iterface_stubs : Flag<["-"], "emit-interface-stubs">, Flags<[CC1Option]>, Group, +def emit_interface_stubs : Flag<["-"], "emit-interface-stubs">, Flags<[CC1Option]>, Group, HelpText<"Generate Inteface Stub Files.">; -def iterface_stub_version_EQ : JoinedOrSeparate<["-"], "interface-stub-version=">, Flags<[CC1Option]>; +def emit_merged_ifs : Flag<["-"], "emit-merged-ifs">, + Flags<[CC1Option]>, Group, + HelpText<"Generate Interface Stub Files, emit merged text not binary.">; +def interface_stub_version_EQ : JoinedOrSeparate<["-"], "interface-stub-version=">, Flags<[CC1Option]>; def exported__symbols__list : Separate<["-"], "exported_symbols_list">; def e : JoinedOrSeparate<["-"], "e">, Group; def fPIC : Flag<["-"], "fPIC">, Group; @@ -833,6 +844,10 @@ def fconstant_cfstrings : Flag<["-"], "fconstant-cfstrings">, Group; def fconstant_string_class_EQ : Joined<["-"], "fconstant-string-class=">, Group; def fconstexpr_depth_EQ : Joined<["-"], "fconstexpr-depth=">, Group; def fconstexpr_steps_EQ : Joined<["-"], "fconstexpr-steps=">, Group; +def fexperimental_new_constant_interpreter : Flag<["-"], "fexperimental-new-constant-interpreter">, Group, + HelpText<"Enable the experimental new constant interpreter">, Flags<[CC1Option]>; +def fforce_experimental_new_constant_interpreter : Flag<["-"], "fforce-experimental-new-constant-interpreter">, Group, + HelpText<"Force the use of the experimental new constant interpreter, failing on missing features">, Flags<[CC1Option]>; def fconstexpr_backtrace_limit_EQ : Joined<["-"], "fconstexpr-backtrace-limit=">, Group; def fno_crash_diagnostics : Flag<["-"], "fno-crash-diagnostics">, Group, Flags<[NoArgumentUnused, CoreOption]>, @@ -899,6 +914,8 @@ def fsjlj_exceptions : Flag<["-"], "fsjlj-exceptions">, Group, Flags<[CC1Option]>, HelpText<"Use SjLj style exceptions">; def fseh_exceptions : Flag<["-"], "fseh-exceptions">, Group, Flags<[CC1Option]>, HelpText<"Use SEH style exceptions">; +def fwasm_exceptions : Flag<["-"], "fwasm-exceptions">, Group, + Flags<[CC1Option]>, HelpText<"Use WebAssembly style exceptions">; def fexcess_precision_EQ : Joined<["-"], "fexcess-precision=">, Group; def : Flag<["-"], "fexpensive-optimizations">, Group; @@ -1043,8 +1060,14 @@ def fsanitize_minimal_runtime : Flag<["-"], "fsanitize-minimal-runtime">, Group; def fno_sanitize_minimal_runtime : Flag<["-"], "fno-sanitize-minimal-runtime">, Group; +def fsanitize_link_runtime : Flag<["-"], "fsanitize-link-runtime">, + Group; +def fno_sanitize_link_runtime : Flag<["-"], "fno-sanitize-link-runtime">, + Group; def fsanitize_link_cxx_runtime : Flag<["-"], "fsanitize-link-c++-runtime">, Group; +def fno_sanitize_link_cxx_runtime : Flag<["-"], "fno-sanitize-link-c++-runtime">, + Group; def fsanitize_cfi_cross_dso : Flag<["-"], "fsanitize-cfi-cross-dso">, Group, HelpText<"Enable control flow integrity (CFI) checks for cross-DSO calls.">; @@ -1055,6 +1078,13 @@ def fno_sanitize_cfi_cross_dso : Flag<["-"], "fno-sanitize-cfi-cross-dso">, def fsanitize_cfi_icall_generalize_pointers : Flag<["-"], "fsanitize-cfi-icall-generalize-pointers">, Group, HelpText<"Generalize pointers in CFI indirect call type signature checks">; +def fsanitize_cfi_canonical_jump_tables : Flag<["-"], "fsanitize-cfi-canonical-jump-tables">, + Group, + HelpText<"Make the jump table addresses canonical in the symbol table">; +def fno_sanitize_cfi_canonical_jump_tables : Flag<["-"], "fno-sanitize-cfi-canonical-jump-tables">, + Group, + Flags<[CoreOption, DriverOption]>, + HelpText<"Do not make the jump table addresses canonical in the symbol table">; def fsanitize_stats : Flag<["-"], "fsanitize-stats">, Group, HelpText<"Enable sanitizer statistics gathering.">; @@ -1117,7 +1147,8 @@ def ftrapping_math : Flag<["-"], "ftrapping-math">, Group, Flags<[CC1Op def fno_trapping_math : Flag<["-"], "fno-trapping-math">, Group, Flags<[CC1Option]>; def ffp_contract : Joined<["-"], "ffp-contract=">, Group, Flags<[CC1Option]>, HelpText<"Form fused FP ops (e.g. FMAs): fast (everywhere)" - " | on (according to FP_CONTRACT pragma, default) | off (never fuse)">, Values<"fast,on,off">; + " | on (according to FP_CONTRACT pragma) | off (never fuse). Default" + " is 'fast' for CUDA/HIP and 'on' otherwise.">, Values<"fast,on,off">; def fstrict_float_cast_overflow : Flag<["-"], "fstrict-float-cast-overflow">, Group, Flags<[CC1Option]>, @@ -1157,6 +1188,9 @@ def fno_use_line_directives : Flag<["-"], "fno-use-line-directives">, Group, Group, Flags<[CC1Option]>, HelpText<"Assert that the compilation takes place in a freestanding environment">; +def fgnuc_version_EQ : Joined<["-"], "fgnuc-version=">, Group, + HelpText<"Sets various macros to claim compatibility with the given GCC version (default is 4.2.1)">, + Flags<[CC1Option, CoreOption]>; def fgnu_keywords : Flag<["-"], "fgnu-keywords">, Group, Flags<[CC1Option]>, HelpText<"Allow GNU-extension keywords regardless of language standard">; def fgnu89_inline : Flag<["-"], "fgnu89-inline">, Group, Flags<[CC1Option]>, @@ -1254,7 +1288,10 @@ def fno_fine_grained_bitfield_accesses : Flag<["-"], HelpText<"Use large-integer access for consecutive bitfield runs.">; def flat__namespace : Flag<["-"], "flat_namespace">; -def flax_vector_conversions : Flag<["-"], "flax-vector-conversions">, Group; +def flax_vector_conversions_EQ : Joined<["-"], "flax-vector-conversions=">, Group, + HelpText<"Enable implicit vector bit-casts">, Values<"none,integer,all">, Flags<[CC1Option]>; +def flax_vector_conversions : Flag<["-"], "flax-vector-conversions">, Group, + Alias, AliasArgs<["integer"]>; def flimited_precision_EQ : Joined<["-"], "flimited-precision=">, Group; def fapple_link_rtlib : Flag<["-"], "fapple-link-rtlib">, Group, HelpText<"Force linking the clang builtins runtime library">; @@ -1331,6 +1368,28 @@ def fmodules_validate_system_headers : Flag<["-"], "fmodules-validate-system-hea HelpText<"Validate the system headers that a module depends on when loading the module">; def fno_modules_validate_system_headers : Flag<["-"], "fno-modules-validate-system-headers">, Group, Flags<[DriverOption]>; + +def fvalidate_ast_input_files_content: + Flag <["-"], "fvalidate-ast-input-files-content">, + Group, Flags<[CC1Option]>, + HelpText<"Compute and store the hash of input files used to build an AST." + " Files with mismatching mtime's are considered valid" + " if both contents is identical">; +def fmodules_validate_input_files_content: + Flag <["-"], "fmodules-validate-input-files-content">, + Group, Flags<[DriverOption]>, + HelpText<"Validate PCM input files based on content if mtime differs">; +def fno_modules_validate_input_files_content: + Flag <["-"], "fno_modules-validate-input-files-content">, + Group, Flags<[DriverOption]>; +def fpch_validate_input_files_content: + Flag <["-"], "fpch-validate-input-files-content">, + Group, Flags<[DriverOption]>, + HelpText<"Validate PCH input files based on content if mtime differs">; +def fno_pch_validate_input_files_content: + Flag <["-"], "fno_pch-validate-input-files-content">, + Group, Flags<[DriverOption]>; + def fmodules : Flag <["-"], "fmodules">, Group, Flags<[DriverOption, CC1Option]>, HelpText<"Enable the 'modules' language feature">; @@ -1428,7 +1487,7 @@ def fno_experimental_new_pass_manager : Flag<["-"], "fno-experimental-new-pass-m def fveclib : Joined<["-"], "fveclib=">, Group, Flags<[CC1Option]>, HelpText<"Use the given vector functions library">, Values<"Accelerate,MASSV,SVML,none">; def fno_lax_vector_conversions : Flag<["-"], "fno-lax-vector-conversions">, Group, - HelpText<"Disallow implicit conversions between vectors with a different number of elements or different element types">, Flags<[CC1Option]>; + Alias, AliasArgs<["none"]>; def fno_merge_all_constants : Flag<["-"], "fno-merge-all-constants">, Group, HelpText<"Disallow merging of constants">; def fno_modules : Flag <["-"], "fno-modules">, Group, @@ -1571,8 +1630,6 @@ def fnoopenmp_use_tls : Flag<["-"], "fnoopenmp-use-tls">, Group, Flags<[CC1Option, NoArgumentUnused, HelpHidden]>; def fopenmp_targets_EQ : CommaJoined<["-"], "fopenmp-targets=">, Flags<[DriverOption, CC1Option]>, HelpText<"Specify comma-separated list of triples OpenMP offloading targets to be supported">; -def fopenmp_dump_offload_linker_script : Flag<["-"], "fopenmp-dump-offload-linker-script">, - Group, Flags<[NoArgumentUnused, HelpHidden]>; def fopenmp_relocatable_target : Flag<["-"], "fopenmp-relocatable-target">, Group, Flags<[CC1Option, NoArgumentUnused, HelpHidden]>; def fnoopenmp_relocatable_target : Flag<["-"], "fnoopenmp-relocatable-target">, @@ -1598,6 +1655,8 @@ def fopenmp_optimistic_collapse : Flag<["-"], "fopenmp-optimistic-collapse">, Gr Flags<[CC1Option, NoArgumentUnused, HelpHidden]>; def fno_openmp_optimistic_collapse : Flag<["-"], "fno-openmp-optimistic-collapse">, Group, Flags<[NoArgumentUnused, HelpHidden]>; +def static_openmp: Flag<["-"], "static-openmp">, + HelpText<"Use the static host OpenMP runtime while linking.">; def fno_optimize_sibling_calls : Flag<["-"], "fno-optimize-sibling-calls">, Group; def foptimize_sibling_calls : Flag<["-"], "foptimize-sibling-calls">, Group; def fno_escaping_block_tail_calls : Flag<["-"], "fno-escaping-block-tail-calls">, Group, Flags<[CC1Option]>; @@ -1683,10 +1742,10 @@ def fstack_protector : Flag<["-"], "fstack-protector">, Group, "alloca, which are of greater size than ssp-buffer-size (default: 8 bytes). " "All variable sized calls to alloca are considered vulnerable">; def ftrivial_auto_var_init : Joined<["-"], "ftrivial-auto-var-init=">, Group, - Flags<[CC1Option]>, HelpText<"Initialize trivial automatic stack variables: uninitialized (default)" + Flags<[CC1Option, CoreOption]>, HelpText<"Initialize trivial automatic stack variables: uninitialized (default)" " | pattern">, Values<"uninitialized,pattern">; -def enable_trivial_var_init_zero : Joined<["-"], "enable-trivial-auto-var-init-zero-knowing-it-will-be-removed-from-clang">, - Flags<[CC1Option]>, +def enable_trivial_var_init_zero : Flag<["-"], "enable-trivial-auto-var-init-zero-knowing-it-will-be-removed-from-clang">, + Flags<[CC1Option, CoreOption]>, HelpText<"Trivial automatic variable initialization to zero is only here for benchmarks, it'll eventually be removed, and I'm OK with that because I'm only using it to benchmark">; def fstandalone_debug : Flag<["-"], "fstandalone-debug">, Group, Flags<[CoreOption]>, HelpText<"Emit full debug info for all types used by the program">; @@ -1757,7 +1816,16 @@ def Wframe_larger_than_EQ : Joined<["-"], "Wframe-larger-than=">, Group def : Flag<["-"], "fterminated-vtables">, Alias; def fthreadsafe_statics : Flag<["-"], "fthreadsafe-statics">, Group; def ftime_report : Flag<["-"], "ftime-report">, Group, Flags<[CC1Option]>; -def ftime_trace : Flag<["-"], "ftime-trace">, Group, Flags<[CC1Option, CoreOption]>; +def ftime_trace : Flag<["-"], "ftime-trace">, Group, + HelpText<"Turn on time profiler. Generates JSON file based on output filename.">, + DocBrief<[{ +Turn on time profiler. Generates JSON file based on output filename. Results +can be analyzed with chrome://tracing or `Speedscope App +`_ for flamegraph visualization.}]>, + Flags<[CC1Option, CoreOption]>; +def ftime_trace_granularity_EQ : Joined<["-"], "ftime-trace-granularity=">, Group, + HelpText<"Minimum time granularity (in microseconds) traced by time profiler">, + Flags<[CC1Option, CoreOption]>; def ftlsmodel_EQ : Joined<["-"], "ftls-model=">, Group, Flags<[CC1Option]>; def ftrapv : Flag<["-"], "ftrapv">, Group, Flags<[CC1Option]>, HelpText<"Trap on integer overflow">; @@ -1791,6 +1859,7 @@ def fuse_init_array : Flag<["-"], "fuse-init-array">, Group, Flags<[CC1 HelpText<"Use .init_array instead of .ctors">; def fno_var_tracking : Flag<["-"], "fno-var-tracking">, Group; def fverbose_asm : Flag<["-"], "fverbose-asm">, Group; +def dA : Flag<["-"], "dA">, Alias; def fvisibility_EQ : Joined<["-"], "fvisibility=">, Group, HelpText<"Set the default symbol visibility for all global declarations">, Values<"hidden,default">; def fvisibility_inlines_hidden : Flag<["-"], "fvisibility-inlines-hidden">, Group, @@ -1816,6 +1885,13 @@ def fforce_emit_vtables : Flag<["-"], "fforce-emit-vtables">, Group, HelpText<"Emits more virtual tables to improve devirtualization">; def fno_force_emit_vtables : Flag<["-"], "fno-force-emit-vtables">, Group, Flags<[CoreOption]>; + +def fvirtual_function_elimination : Flag<["-"], "fvirtual-function-elimination">, Group, + Flags<[CoreOption, CC1Option]>, + HelpText<"Enables dead virtual function elimination optimization. Requires -flto=full">; +def fno_virtual_function_elimination : Flag<["-"], "fno-virtual-function_elimination">, Group, + Flags<[CoreOption]>; + def fwrapv : Flag<["-"], "fwrapv">, Group, Flags<[CC1Option]>, HelpText<"Treat signed integer overflow as two's complement">; def fwritable_strings : Flag<["-"], "fwritable-strings">, Group, Flags<[CC1Option]>, @@ -2023,9 +2099,14 @@ def malign_jumps_EQ : Joined<["-"], "malign-jumps=">, Group, Group; def mlong_calls : Flag<["-"], "mlong-calls">, Group, HelpText<"Generate branches with extended addressability, usually via indirect jumps.">; -def mlong_double_64 : Flag<["-"], "mlong-double-64">, Group, Flags<[CC1Option]>, +def LongDouble_Group : OptionGroup<"">, Group, + DocName<"Long double flags">, + DocBrief<[{Selects the long double implementation}]>; +def mlong_double_64 : Flag<["-"], "mlong-double-64">, Group, Flags<[CC1Option]>, HelpText<"Force long double to be 64 bits">; -def mlong_double_128 : Flag<["-"], "mlong-double-128">, Group, Flags<[CC1Option]>, +def mlong_double_80 : Flag<["-"], "mlong-double-80">, Group, Flags<[CC1Option]>, + HelpText<"Force long double to be 80 bits, padded to 128 bits for storage">; +def mlong_double_128 : Flag<["-"], "mlong-double-128">, Group, Flags<[CC1Option]>, HelpText<"Force long double to be 128 bits">; def mno_long_calls : Flag<["-"], "mno-long-calls">, Group, HelpText<"Restore the default behaviour of not generating long calls">; @@ -2138,6 +2219,12 @@ def msave_restore : Flag<["-"], "msave-restore">, Group, HelpText<"Enable using library calls for save and restore">; def mno_save_restore : Flag<["-"], "mno-save-restore">, Group, HelpText<"Disable using library calls for save and restore">; +def mcmodel_EQ_medlow : Flag<["-"], "mcmodel=medlow">, Group, + Flags<[CC1Option]>, Alias, AliasArgs<["small"]>, + HelpText<"Equivalent to -mcmodel=small, compatible with RISC-V gcc.">; +def mcmodel_EQ_medany : Flag<["-"], "mcmodel=medany">, Group, + Flags<[CC1Option]>, Alias, AliasArgs<["medium"]>, + HelpText<"Equivalent to -mcmodel=medium, compatible with RISC-V gcc.">; def munaligned_access : Flag<["-"], "munaligned-access">, Group, HelpText<"Allow memory accesses to be unaligned (AArch32/AArch64 only)">; @@ -2241,6 +2328,8 @@ def faltivec : Flag<["-"], "faltivec">, Group, Flags<[DriverOption]>; def fno_altivec : Flag<["-"], "fno-altivec">, Group, Flags<[DriverOption]>; def maltivec : Flag<["-"], "maltivec">, Group; def mno_altivec : Flag<["-"], "mno-altivec">, Group; +def mspe : Flag<["-"], "mspe">, Group; +def mno_spe : Flag<["-"], "mno-spe">, Group; def mvsx : Flag<["-"], "mvsx">, Group; def mno_vsx : Flag<["-"], "mno-vsx">, Group; def msecure_plt : Flag<["-"], "msecure-plt">, Group; @@ -2309,7 +2398,7 @@ def mno_backchain : Flag<["-"], "mno-backchain">, Group, Flags<[DriverO def mno_warn_nonportable_cfstrings : Flag<["-"], "mno-warn-nonportable-cfstrings">, Group; def mno_omit_leaf_frame_pointer : Flag<["-"], "mno-omit-leaf-frame-pointer">, Group; def momit_leaf_frame_pointer : Flag<["-"], "momit-leaf-frame-pointer">, Group, - HelpText<"Omit frame pointer setup for leaf functions">, Flags<[CC1Option]>; + HelpText<"Omit frame pointer setup for leaf functions">; def moslib_EQ : Joined<["-"], "moslib=">, Group; def mpascal_strings : Flag<["-"], "mpascal-strings">, Alias; def mred_zone : Flag<["-"], "mred-zone">, Group; @@ -2340,7 +2429,7 @@ def mpie_copy_relocations : Flag<["-"], "mpie-copy-relocations">, Group Flags<[CC1Option]>, HelpText<"Use copy relocations support for PIE builds">; def mno_pie_copy_relocations : Flag<["-"], "mno-pie-copy-relocations">, Group; -def mfentry : Flag<["-"], "mfentry">, HelpText<"Insert calls to fentry at function entry (x86 only)">, +def mfentry : Flag<["-"], "mfentry">, HelpText<"Insert calls to fentry at function entry (x86/SystemZ only)">, Flags<[CC1Option]>, Group; def mips16 : Flag<["-"], "mips16">, Group; def mno_mips16 : Flag<["-"], "mno-mips16">, Group; @@ -2506,7 +2595,9 @@ def no__dead__strip__inits__and__terms : Flag<["-"], "no_dead_strip_inits_and_te def nobuiltininc : Flag<["-"], "nobuiltininc">, Flags<[CC1Option, CoreOption]>, HelpText<"Disable builtin #include directories">; def nocudainc : Flag<["-"], "nocudainc">; -def nocudalib : Flag<["-"], "nocudalib">; +def nogpulib : Flag<["-"], "nogpulib">, + HelpText<"Do not link device library for CUDA/HIP device compilation">; +def : Flag<["-"], "nocudalib">, Alias; def nodefaultlibs : Flag<["-"], "nodefaultlibs">; def nofixprebinding : Flag<["-"], "nofixprebinding">; def nolibc : Flag<["-"], "nolibc">; @@ -2627,11 +2718,15 @@ def std_EQ : Joined<["-", "--"], "std=">, Flags<[CC1Option]>, const char *Values = #define LANGSTANDARD(id, name, lang, desc, features) name "," #define LANGSTANDARD_ALIAS(id, alias) alias "," - #include "clang/Frontend/LangStandards.def" + #include "clang/Basic/LangStandards.def" ; }]>; def stdlib_EQ : Joined<["-", "--"], "stdlib=">, Flags<[CC1Option]>, HelpText<"C++ standard library to use">, Values<"libc++,libstdc++,platform">; +def stdlibxx_isystem : JoinedOrSeparate<["-"], "stdlib++-isystem">, + Group, + HelpText<"Use directory as the C++ standard library include path">, + Flags<[DriverOption]>, MetaVarName<"">; def unwindlib_EQ : Joined<["-", "--"], "unwindlib=">, Flags<[CC1Option]>, HelpText<"Unwind library to use">, Values<"libgcc,unwindlib,platform">; def sub__library : JoinedOrSeparate<["-"], "sub_library">; @@ -2711,7 +2806,6 @@ def _mhwdiv : Separate<["--"], "mhwdiv">, Alias; def _CLASSPATH_EQ : Joined<["--"], "CLASSPATH=">, Alias; def _CLASSPATH : Separate<["--"], "CLASSPATH">, Alias; def _all_warnings : Flag<["--"], "all-warnings">, Alias; -def _analyze_auto : Flag<["--"], "analyze-auto">, Flags<[DriverOption]>; def _analyzer_no_default_checks : Flag<["--"], "analyzer-no-default-checks">, Flags<[DriverOption]>; def _analyzer_output : JoinedOrSeparate<["--"], "analyzer-output">, Flags<[DriverOption]>, HelpText<"Static analyzer report output format (html|plist|plist-multi-file|plist-html|text).">; @@ -2972,8 +3066,6 @@ def mmovdiri : Flag<["-"], "mmovdiri">, Group; def mno_movdiri : Flag<["-"], "mno-movdiri">, Group; def mmovdir64b : Flag<["-"], "mmovdir64b">, Group; def mno_movdir64b : Flag<["-"], "mno-movdir64b">, Group; -def mmpx : Flag<["-"], "mmpx">, Group; -def mno_mpx : Flag<["-"], "mno-mpx">, Group; def mmwaitx : Flag<["-"], "mmwaitx">, Group; def mno_mwaitx : Flag<["-"], "mno-mwaitx">, Group; def mpku : Flag<["-"], "mpku">, Group; diff --git a/include/clang/Driver/Phases.h b/include/clang/Driver/Phases.h index 7199c657848ca..63931c00c8901 100644 --- a/include/clang/Driver/Phases.h +++ b/include/clang/Driver/Phases.h @@ -20,7 +20,8 @@ namespace phases { Compile, Backend, Assemble, - Link + Link, + IfsMerge, }; enum { diff --git a/include/clang/Driver/SanitizerArgs.h b/include/clang/Driver/SanitizerArgs.h index 957e752b68775..c37499e0f201f 100644 --- a/include/clang/Driver/SanitizerArgs.h +++ b/include/clang/Driver/SanitizerArgs.h @@ -32,6 +32,7 @@ class SanitizerArgs { bool MsanUseAfterDtor = true; bool CfiCrossDso = false; bool CfiICallGeneralizePointers = false; + bool CfiCanonicalJumpTables = false; int AsanFieldPadding = 0; bool SharedRuntime = false; bool AsanUseAfterScope = true; @@ -41,6 +42,7 @@ class SanitizerArgs { bool AsanInvalidPointerCmp = false; bool AsanInvalidPointerSub = false; std::string HwasanAbi; + bool LinkRuntimes = true; bool LinkCXXRuntimes = false; bool NeedPIE = false; bool SafeStackRuntime = false; @@ -59,7 +61,9 @@ class SanitizerArgs { bool needsSharedRt() const { return SharedRuntime; } bool needsAsanRt() const { return Sanitizers.has(SanitizerKind::Address); } - bool needsHwasanRt() const { return Sanitizers.has(SanitizerKind::HWAddress); } + bool needsHwasanRt() const { + return Sanitizers.has(SanitizerKind::HWAddress); + } bool needsTsanRt() const { return Sanitizers.has(SanitizerKind::Thread); } bool needsMsanRt() const { return Sanitizers.has(SanitizerKind::Memory); } bool needsFuzzer() const { return Sanitizers.has(SanitizerKind::Fuzzer); } @@ -80,6 +84,7 @@ class SanitizerArgs { bool requiresPIE() const; bool needsUnwindTables() const; bool needsLTO() const; + bool linkRuntimes() const { return LinkRuntimes; } bool linkCXXRuntimes() const { return LinkCXXRuntimes; } bool hasCrossDsoCfi() const { return CfiCrossDso; } bool hasAnySanitizer() const { return !Sanitizers.empty(); } diff --git a/include/clang/Driver/ToolChain.h b/include/clang/Driver/ToolChain.h index 7dd3db376c8c9..f0676eee2d6ce 100644 --- a/include/clang/Driver/ToolChain.h +++ b/include/clang/Driver/ToolChain.h @@ -136,13 +136,17 @@ private: mutable std::unique_ptr Clang; mutable std::unique_ptr Assemble; mutable std::unique_ptr Link; + mutable std::unique_ptr IfsMerge; mutable std::unique_ptr OffloadBundler; + mutable std::unique_ptr OffloadWrapper; Tool *getClang() const; Tool *getAssemble() const; Tool *getLink() const; + Tool *getIfsMerge() const; Tool *getClangAs() const; Tool *getOffloadBundler() const; + Tool *getOffloadWrapper() const; mutable std::unique_ptr SanitizerArguments; mutable std::unique_ptr XRayArguments; @@ -542,6 +546,11 @@ public: AddClangCXXStdlibIncludeArgs(const llvm::opt::ArgList &DriverArgs, llvm::opt::ArgStringList &CC1Args) const; + /// AddClangCXXStdlibIsystemArgs - Add the clang -cc1 level arguments to set + /// the specified include paths for the C++ standard library. + void AddClangCXXStdlibIsystemArgs(const llvm::opt::ArgList &DriverArgs, + llvm::opt::ArgStringList &CC1Args) const; + /// Returns if the C++ standard library should be linked in. /// Note that e.g. -lm should still be linked even if this returns false. bool ShouldLinkCXXStdlib(const llvm::opt::ArgList &Args) const; diff --git a/include/clang/Driver/Types.def b/include/clang/Driver/Types.def index b45789d4b3144..79e8d109cd97e 100644 --- a/include/clang/Driver/Types.def +++ b/include/clang/Driver/Types.def @@ -29,77 +29,74 @@ // The fourth value is the suffix to use when creating temporary files // of this type, or null if unspecified. -// The fifth value is a string containing option flags. Valid values: -// a - The type should only be assembled. -// p - The type should only be precompiled. -// u - The type can be user specified (with -x). -// m - Precompiling this type produces a module file. -// A - The type's temporary suffix should be appended when generating -// outputs of this type. - +// The final value is a variadic list of phases for each type. Eventually the +// options flag string will be replaced with this variadic list. +// Most of the options in Flags have been removed in favor of subsuming their +// meaning from the phases list. // C family source language (with and without preprocessing). -TYPE("cpp-output", PP_C, INVALID, "i", "u") -TYPE("c", C, PP_C, "c", "u") -TYPE("cl", CL, PP_C, "cl", "u") -TYPE("cuda-cpp-output", PP_CUDA, INVALID, "cui", "u") -TYPE("cuda", CUDA, PP_CUDA, "cu", "u") -TYPE("cuda", CUDA_DEVICE, PP_CUDA, "cu", "") -TYPE("hip-cpp-output", PP_HIP, INVALID, "cui", "u") -TYPE("hip", HIP, PP_HIP, "cu", "u") -TYPE("hip", HIP_DEVICE, PP_HIP, "cu", "") -TYPE("objective-c-cpp-output", PP_ObjC, INVALID, "mi", "u") -TYPE("objc-cpp-output", PP_ObjC_Alias, INVALID, "mi", "u") -TYPE("objective-c", ObjC, PP_ObjC, "m", "u") -TYPE("c++-cpp-output", PP_CXX, INVALID, "ii", "u") -TYPE("c++", CXX, PP_CXX, "cpp", "u") -TYPE("objective-c++-cpp-output", PP_ObjCXX, INVALID, "mii", "u") -TYPE("objc++-cpp-output", PP_ObjCXX_Alias, INVALID, "mii", "u") -TYPE("objective-c++", ObjCXX, PP_ObjCXX, "mm", "u") -TYPE("renderscript", RenderScript, PP_C, "rs", "u") +TYPE("cpp-output", PP_C, INVALID, "i", phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("c", C, PP_C, "c", phases::Preprocess, phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("cl", CL, PP_C, "cl", phases::Preprocess, phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("cuda-cpp-output", PP_CUDA, INVALID, "cui", phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("cuda", CUDA, PP_CUDA, "cu", phases::Preprocess, phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("cuda", CUDA_DEVICE, PP_CUDA, "cu", phases::Preprocess, phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("hip-cpp-output", PP_HIP, INVALID, "cui", phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("hip", HIP, PP_HIP, "cu", phases::Preprocess, phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("hip", HIP_DEVICE, PP_HIP, "cu", phases::Preprocess, phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("objective-c-cpp-output", PP_ObjC, INVALID, "mi", phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("objc-cpp-output", PP_ObjC_Alias, INVALID, "mi", phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("objective-c", ObjC, PP_ObjC, "m", phases::Preprocess, phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("c++-cpp-output", PP_CXX, INVALID, "ii", phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("c++", CXX, PP_CXX, "cpp", phases::Preprocess, phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("objective-c++-cpp-output", PP_ObjCXX, INVALID, "mii", phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("objc++-cpp-output", PP_ObjCXX_Alias, INVALID, "mii", phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("objective-c++", ObjCXX, PP_ObjCXX, "mm", phases::Preprocess, phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("renderscript", RenderScript, PP_C, "rs", phases::Preprocess, phases::Compile, phases::Backend, phases::Assemble, phases::Link) // C family input files to precompile. -TYPE("c-header-cpp-output", PP_CHeader, INVALID, "i", "p") -TYPE("c-header", CHeader, PP_CHeader, "h", "pu") -TYPE("cl-header", CLHeader, PP_CHeader, "h", "pu") -TYPE("objective-c-header-cpp-output", PP_ObjCHeader, INVALID, "mi", "p") -TYPE("objective-c-header", ObjCHeader, PP_ObjCHeader, "h", "pu") -TYPE("c++-header-cpp-output", PP_CXXHeader, INVALID, "ii", "p") -TYPE("c++-header", CXXHeader, PP_CXXHeader, "hh", "pu") -TYPE("objective-c++-header-cpp-output", PP_ObjCXXHeader, INVALID, "mii", "p") -TYPE("objective-c++-header", ObjCXXHeader, PP_ObjCXXHeader, "h", "pu") -TYPE("c++-module", CXXModule, PP_CXXModule, "cppm", "mu") -TYPE("c++-module-cpp-output", PP_CXXModule, INVALID, "iim", "m") +TYPE("c-header-cpp-output", PP_CHeader, INVALID, "i", phases::Precompile) +TYPE("c-header", CHeader, PP_CHeader, "h", phases::Preprocess, phases::Precompile) +TYPE("cl-header", CLHeader, PP_CHeader, "h", phases::Preprocess, phases::Precompile) +TYPE("objective-c-header-cpp-output", PP_ObjCHeader, INVALID, "mi", phases::Precompile) +TYPE("objective-c-header", ObjCHeader, PP_ObjCHeader, "h", phases::Preprocess, phases::Precompile) +TYPE("c++-header-cpp-output", PP_CXXHeader, INVALID, "ii", phases::Precompile) +TYPE("c++-header", CXXHeader, PP_CXXHeader, "hh", phases::Preprocess, phases::Precompile) +TYPE("objective-c++-header-cpp-output", PP_ObjCXXHeader, INVALID, "mii", phases::Precompile) +TYPE("objective-c++-header", ObjCXXHeader, PP_ObjCXXHeader, "h", phases::Preprocess, phases::Precompile) +TYPE("c++-module", CXXModule, PP_CXXModule, "cppm", phases::Preprocess, phases::Precompile, phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("c++-module-cpp-output", PP_CXXModule, INVALID, "iim", phases::Precompile, phases::Compile, phases::Backend, phases::Assemble, phases::Link) // Other languages. -TYPE("ada", Ada, INVALID, nullptr, "u") -TYPE("assembler", PP_Asm, INVALID, "s", "au") -TYPE("assembler-with-cpp", Asm, PP_Asm, "S", "au") -TYPE("f95", PP_Fortran, INVALID, nullptr, "u") -TYPE("f95-cpp-input", Fortran, PP_Fortran, nullptr, "u") -TYPE("java", Java, INVALID, nullptr, "u") +TYPE("ada", Ada, INVALID, nullptr, phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("assembler", PP_Asm, INVALID, "s", phases::Assemble, phases::Link) +TYPE("assembler-with-cpp", Asm, PP_Asm, "S", phases::Preprocess, phases::Assemble, phases::Link) +TYPE("f95", PP_Fortran, INVALID, nullptr, phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("f95-cpp-input", Fortran, PP_Fortran, nullptr, phases::Preprocess, phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("java", Java, INVALID, nullptr, phases::Compile, phases::Backend, phases::Assemble, phases::Link) // LLVM IR/LTO types. We define separate types for IR and LTO because LTO // outputs should use the standard suffixes. -TYPE("ir", LLVM_IR, INVALID, "ll", "u") -TYPE("ir", LLVM_BC, INVALID, "bc", "u") -TYPE("lto-ir", LTO_IR, INVALID, "s", "") -TYPE("lto-bc", LTO_BC, INVALID, "o", "") +TYPE("ir", LLVM_IR, INVALID, "ll", phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("ir", LLVM_BC, INVALID, "bc", phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("lto-ir", LTO_IR, INVALID, "s", phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("lto-bc", LTO_BC, INVALID, "o", phases::Compile, phases::Backend, phases::Assemble, phases::Link) // Misc. -TYPE("ast", AST, INVALID, "ast", "u") -TYPE("ifs", IFS, INVALID, "ifs", "u") -TYPE("pcm", ModuleFile, INVALID, "pcm", "u") -TYPE("plist", Plist, INVALID, "plist", "") -TYPE("rewritten-objc", RewrittenObjC,INVALID, "cpp", "") -TYPE("rewritten-legacy-objc", RewrittenLegacyObjC,INVALID, "cpp", "") -TYPE("remap", Remap, INVALID, "remap", "") -TYPE("precompiled-header", PCH, INVALID, "gch", "A") -TYPE("object", Object, INVALID, "o", "") -TYPE("treelang", Treelang, INVALID, nullptr, "u") -TYPE("image", Image, INVALID, "out", "") -TYPE("dSYM", dSYM, INVALID, "dSYM", "A") -TYPE("dependencies", Dependencies, INVALID, "d", "") -TYPE("cuda-fatbin", CUDA_FATBIN, INVALID, "fatbin","A") -TYPE("hip-fatbin", HIP_FATBIN, INVALID, "hipfb", "A") -TYPE("none", Nothing, INVALID, nullptr, "u") +TYPE("ast", AST, INVALID, "ast", phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("ifs", IFS, INVALID, "ifs", phases::IfsMerge) +TYPE("ifs-cpp", IFS_CPP, INVALID, "ifs", phases::Compile, phases::IfsMerge) +TYPE("pcm", ModuleFile, INVALID, "pcm", phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("plist", Plist, INVALID, "plist", phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("rewritten-objc", RewrittenObjC,INVALID, "cpp", phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("rewritten-legacy-objc", RewrittenLegacyObjC,INVALID, "cpp", phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("remap", Remap, INVALID, "remap", phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("precompiled-header", PCH, INVALID, "gch", phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("object", Object, INVALID, "o", phases::Link) +TYPE("treelang", Treelang, INVALID, nullptr, phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("image", Image, INVALID, "out", phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("dSYM", dSYM, INVALID, "dSYM", phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("dependencies", Dependencies, INVALID, "d", phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("cuda-fatbin", CUDA_FATBIN, INVALID, "fatbin", phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("hip-fatbin", HIP_FATBIN, INVALID, "hipfb", phases::Compile, phases::Backend, phases::Assemble, phases::Link) +TYPE("none", Nothing, INVALID, nullptr, phases::Compile, phases::Backend, phases::Assemble, phases::Link) diff --git a/include/clang/Driver/Types.h b/include/clang/Driver/Types.h index 53afada7abca4..a605450e6e3dd 100644 --- a/include/clang/Driver/Types.h +++ b/include/clang/Driver/Types.h @@ -11,16 +11,18 @@ #include "clang/Driver/Phases.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/Option/ArgList.h" namespace llvm { class StringRef; } namespace clang { namespace driver { +class Driver; namespace types { enum ID { TY_INVALID, -#define TYPE(NAME, ID, PP_TYPE, TEMP_SUFFIX, FLAGS) TY_##ID, +#define TYPE(NAME, ID, PP_TYPE, TEMP_SUFFIX, ...) TY_##ID, #include "clang/Driver/Types.def" #undef TYPE TY_LAST @@ -100,6 +102,9 @@ namespace types { void getCompilationPhases( ID Id, llvm::SmallVectorImpl &Phases); + void getCompilationPhases(const clang::driver::Driver &Driver, + llvm::opt::DerivedArgList &DAL, ID Id, + llvm::SmallVectorImpl &Phases); /// lookupCXXTypeForCType - Lookup CXX input type that corresponds to given /// C type (used for clang++ emulation of g++ behaviour) diff --git a/include/clang/Format/Format.h b/include/clang/Format/Format.h index 6388e4fc17272..7e71b7e8b167b 100644 --- a/include/clang/Format/Format.h +++ b/include/clang/Format/Format.h @@ -216,10 +216,37 @@ struct FormatStyle { /// \endcode bool AllowAllParametersOfDeclarationOnNextLine; - /// Allows contracting simple braced statements to a single line. - /// - /// E.g., this allows ``if (a) { return; }`` to be put on a single line. - bool AllowShortBlocksOnASingleLine; + /// Different styles for merging short blocks containing at most one + /// statement. + enum ShortBlockStyle { + /// Never merge blocks into a single line. + /// \code + /// while (true) { + /// } + /// while (true) { + /// continue; + /// } + /// \endcode + SBS_Never, + /// Only merge empty blocks. + /// \code + /// while (true) {} + /// while (true) { + /// continue; + /// } + /// \endcode + SBS_Empty, + /// Always merge short blocks into a single line. + /// \code + /// while (true) {} + /// while (true) { continue; } + /// \endcode + SBS_Always, + }; + + /// Dependent on the value, ``while (true) { continue; }`` can be put on a + /// single line. + ShortBlockStyle AllowShortBlocksOnASingleLine; /// If ``true``, short case labels will be contracted to a single line. /// \code @@ -462,38 +489,38 @@ struct FormatStyle { /// Different ways to break after the template declaration. enum BreakTemplateDeclarationsStyle { - /// Do not force break before declaration. - /// ``PenaltyBreakTemplateDeclaration`` is taken into account. - /// \code - /// template T foo() { - /// } - /// template T foo(int aaaaaaaaaaaaaaaaaaaaa, - /// int bbbbbbbbbbbbbbbbbbbbb) { - /// } - /// \endcode - BTDS_No, - /// Force break after template declaration only when the following - /// declaration spans multiple lines. - /// \code - /// template T foo() { - /// } - /// template - /// T foo(int aaaaaaaaaaaaaaaaaaaaa, - /// int bbbbbbbbbbbbbbbbbbbbb) { - /// } - /// \endcode - BTDS_MultiLine, - /// Always break after template declaration. - /// \code - /// template - /// T foo() { - /// } - /// template - /// T foo(int aaaaaaaaaaaaaaaaaaaaa, - /// int bbbbbbbbbbbbbbbbbbbbb) { - /// } - /// \endcode - BTDS_Yes + /// Do not force break before declaration. + /// ``PenaltyBreakTemplateDeclaration`` is taken into account. + /// \code + /// template T foo() { + /// } + /// template T foo(int aaaaaaaaaaaaaaaaaaaaa, + /// int bbbbbbbbbbbbbbbbbbbbb) { + /// } + /// \endcode + BTDS_No, + /// Force break after template declaration only when the following + /// declaration spans multiple lines. + /// \code + /// template T foo() { + /// } + /// template + /// T foo(int aaaaaaaaaaaaaaaaaaaaa, + /// int bbbbbbbbbbbbbbbbbbbbb) { + /// } + /// \endcode + BTDS_MultiLine, + /// Always break after template declaration. + /// \code + /// template + /// T foo() { + /// } + /// template + /// T foo(int aaaaaaaaaaaaaaaaaaaaa, + /// int bbbbbbbbbbbbbbbbbbbbb) { + /// } + /// \endcode + BTDS_Yes }; /// The template declaration breaking style to use. @@ -706,6 +733,32 @@ struct FormatStyle { /// B /// }; /// \endcode + BS_Whitesmiths, + /// Like ``Allman`` but always indent braces and line up code with braces. + /// \code + /// try + /// { + /// foo(); + /// } + /// catch () + /// { + /// } + /// void foo() { bar(); } + /// class foo + /// { + /// }; + /// if (foo()) + /// { + /// } + /// else + /// { + /// } + /// enum X : int + /// { + /// A, + /// B + /// }; + /// \endcode BS_GNU, /// Like ``Attach``, but break before functions. /// \code @@ -729,6 +782,40 @@ struct FormatStyle { /// The brace breaking style to use. BraceBreakingStyle BreakBeforeBraces; + // Different ways to wrap braces after control statements. + enum BraceWrappingAfterControlStatementStyle { + /// Never wrap braces after a control statement. + /// \code + /// if (foo()) { + /// } else { + /// } + /// for (int i = 0; i < 10; ++i) { + /// } + /// \endcode + BWACS_Never, + /// Only wrap braces after a multi-line control statement. + /// \code + /// if (foo && bar && + /// baz) + /// { + /// quux(); + /// } + /// while (foo || bar) { + /// } + /// \endcode + BWACS_MultiLine, + /// Always wrap braces after a control statement. + /// \code + /// if (foo()) + /// { + /// } else + /// {} + /// for (int i = 0; i < 10; ++i) + /// {} + /// \endcode + BWACS_Always + }; + /// Precise control over the wrapping of braces. /// \code /// # Should be declared this way: @@ -764,23 +851,7 @@ struct FormatStyle { /// \endcode bool AfterClass; /// Wrap control statements (``if``/``for``/``while``/``switch``/..). - /// \code - /// true: - /// if (foo()) - /// { - /// } else - /// {} - /// for (int i = 0; i < 10; ++i) - /// {} - /// - /// false: - /// if (foo()) { - /// } else { - /// } - /// for (int i = 0; i < 10; ++i) { - /// } - /// \endcode - bool AfterControlStatement; + BraceWrappingAfterControlStatementStyle AfterControlStatement; /// Wrap enum definitions. /// \code /// true: @@ -1238,6 +1309,22 @@ struct FormatStyle { /// \endcode bool IndentCaseLabels; + /// Indent goto labels. + /// + /// When ``false``, goto labels are flushed left. + /// \code + /// true: false: + /// int f() { vs. int f() { + /// if (foo()) { if (foo()) { + /// label1: label1: + /// bar(); bar(); + /// } } + /// label2: label2: + /// return 1; return 1; + /// } } + /// \endcode + bool IndentGotoLabels; + /// Options for indenting preprocessor directives. enum PPDirectiveIndentStyle { /// Does not indent any directives. @@ -1711,8 +1798,8 @@ struct FormatStyle { /// If ``false``, spaces will be removed before assignment operators. /// \code /// true: false: - /// int a = 5; vs. int a=5; - /// a += 42 a+=42; + /// int a = 5; vs. int a= 5; + /// a += 42; a+= 42; /// \endcode bool SpaceBeforeAssignmentOperators; @@ -1799,6 +1886,14 @@ struct FormatStyle { /// \endcode bool SpaceBeforeRangeBasedForLoopColon; + /// If ``true``, spaces will be inserted into ``{}``. + /// \code + /// true: false: + /// void f() { } vs. void f() {} + /// while (true) { } while (true) {} + /// \endcode + bool SpaceInEmptyBlock; + /// If ``true``, spaces may be inserted into ``()``. /// \code /// true: false: @@ -1868,15 +1963,32 @@ struct FormatStyle { /// \endcode bool SpacesInSquareBrackets; - /// Supported language standards. + /// Supported language standards for parsing and formatting C++ constructs. + /// \code + /// Latest: vector> + /// c++03 vs. vector > + /// \endcode + /// + /// The correct way to spell a specific language version is e.g. ``c++11``. + /// The historical aliases ``Cpp03`` and ``Cpp11`` are deprecated. enum LanguageStandard { - /// Use C++03-compatible syntax. + /// c++03: Parse and format as C++03. LS_Cpp03, - /// Use features of C++11, C++14 and C++1z (e.g. ``A>`` instead of - /// ``A >``). + /// c++11: Parse and format as C++11. LS_Cpp11, - /// Automatic detection based on the input. - LS_Auto + /// c++14: Parse and format as C++14. + LS_Cpp14, + /// c++17: Parse and format as C++17. + LS_Cpp17, + /// c++20: Parse and format as C++20. + LS_Cpp20, + /// Latest: Parse and format using the latest supported language version. + /// 'Cpp11' is an alias for LS_Latest for historical reasons. + LS_Latest, + + /// Auto: Automatic detection based on the input. + /// Parse using the latest language version. Format based on detected input. + LS_Auto, }; /// Format compatible with this standard, e.g. use ``A >`` @@ -1955,6 +2067,7 @@ struct FormatStyle { IncludeStyle.IncludeBlocks == R.IncludeStyle.IncludeBlocks && IncludeStyle.IncludeCategories == R.IncludeStyle.IncludeCategories && IndentCaseLabels == R.IndentCaseLabels && + IndentGotoLabels == R.IndentGotoLabels && IndentPPDirectives == R.IndentPPDirectives && IndentWidth == R.IndentWidth && Language == R.Language && IndentWrappedFunctionNames == R.IndentWrappedFunctionNames && @@ -1995,6 +2108,7 @@ struct FormatStyle { SpaceBeforeParens == R.SpaceBeforeParens && SpaceBeforeRangeBasedForLoopColon == R.SpaceBeforeRangeBasedForLoopColon && + SpaceInEmptyBlock == R.SpaceInEmptyBlock && SpaceInEmptyParentheses == R.SpaceInEmptyParentheses && SpacesBeforeTrailingComments == R.SpacesBeforeTrailingComments && SpacesInAngles == R.SpacesInAngles && @@ -2072,6 +2186,10 @@ FormatStyle getWebKitStyle(); /// http://www.gnu.org/prep/standards/standards.html FormatStyle getGNUStyle(); +/// Returns a format style complying with Microsoft style guide: +/// https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-code-style-settings-reference?view=vs-2017 +FormatStyle getMicrosoftStyle(FormatStyle::LanguageKind Language); + /// Returns style indicating formatting should be not applied at all. FormatStyle getNoStyle(); diff --git a/include/clang/Frontend/ASTUnit.h b/include/clang/Frontend/ASTUnit.h index 7fb1d2d93380c..a36655150d4ec 100644 --- a/include/clang/Frontend/ASTUnit.h +++ b/include/clang/Frontend/ASTUnit.h @@ -315,7 +315,7 @@ public: CodeCompletionTUInfo &getCodeCompletionTUInfo() { if (!CCTUInfo) - CCTUInfo = llvm::make_unique( + CCTUInfo = std::make_unique( std::make_shared()); return *CCTUInfo; } @@ -390,7 +390,7 @@ private: /// just about any usage. /// Becomes a noop in release mode; only useful for debug mode checking. class ConcurrencyState { - void *Mutex; // a llvm::sys::MutexImpl in debug; + void *Mutex; // a std::recursive_mutex in debug; public: ConcurrencyState(); @@ -832,6 +832,7 @@ public: SkipFunctionBodiesScope::None, bool SingleFileParse = false, bool UserFilesAreVolatile = false, bool ForSerialization = false, + bool RetainExcludedConditionalBlocks = false, llvm::Optional ModuleFormat = llvm::None, std::unique_ptr *ErrAST = nullptr, IntrusiveRefCntPtr VFS = nullptr); diff --git a/include/clang/Frontend/CompilerInstance.h b/include/clang/Frontend/CompilerInstance.h index eb49c53ff40b7..d15bdc4665a31 100644 --- a/include/clang/Frontend/CompilerInstance.h +++ b/include/clang/Frontend/CompilerInstance.h @@ -155,6 +155,12 @@ class CompilerInstance : public ModuleLoader { /// One or more modules failed to build. bool ModuleBuildFailed = false; + /// The stream for verbose output if owned, otherwise nullptr. + std::unique_ptr OwnedVerboseOutputStream; + + /// The stream for verbose output. + raw_ostream *VerboseOutputStream = &llvm::errs(); + /// Holds information about the output file. /// /// If TempFilename is not empty we must rename it to Filename at the end. @@ -217,9 +223,6 @@ public: /// \param Act - The action to execute. /// \return - True on success. // - // FIXME: This function should take the stream to write any debugging / - // verbose output to as an argument. - // // FIXME: Eliminate the llvm_shutdown requirement, that should either be part // of the context or else not CompilerInstance specific. bool ExecuteAction(FrontendAction &Act); @@ -349,6 +352,21 @@ public: return *Diagnostics->getClient(); } + /// } + /// @name VerboseOutputStream + /// } + + /// Replace the current stream for verbose output. + void setVerboseOutputStream(raw_ostream &Value); + + /// Replace the current stream for verbose output. + void setVerboseOutputStream(std::unique_ptr Value); + + /// Get the current stream for verbose output. + raw_ostream &getVerboseOutputStream() { + return *VerboseOutputStream; + } + /// } /// @name Target Info /// { diff --git a/include/clang/Frontend/CompilerInvocation.h b/include/clang/Frontend/CompilerInvocation.h index 413134be4cef0..f3253d5b40e3c 100644 --- a/include/clang/Frontend/CompilerInvocation.h +++ b/include/clang/Frontend/CompilerInvocation.h @@ -14,13 +14,14 @@ #include "clang/Basic/FileSystemOptions.h" #include "clang/Basic/LLVM.h" #include "clang/Basic/LangOptions.h" +#include "clang/Basic/LangStandard.h" #include "clang/Frontend/DependencyOutputOptions.h" #include "clang/Frontend/FrontendOptions.h" -#include "clang/Frontend/LangStandard.h" #include "clang/Frontend/MigratorOptions.h" #include "clang/Frontend/PreprocessorOutputOptions.h" #include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" #include "llvm/ADT/IntrusiveRefCntPtr.h" +#include "llvm/ADT/ArrayRef.h" #include #include @@ -147,13 +148,14 @@ public: /// Create a compiler invocation from a list of input options. /// \returns true on success. /// + /// \returns false if an error was encountered while parsing the arguments + /// and attempts to recover and continue parsing the rest of the arguments. + /// The recovery is best-effort and only guarantees that \p Res will end up in + /// one of the vaild-to-access (albeit arbitrary) states. + /// /// \param [out] Res - The resulting invocation. - /// \param ArgBegin - The first element in the argument vector. - /// \param ArgEnd - The last element in the argument vector. - /// \param Diags - The diagnostic engine to use for errors. static bool CreateFromArgs(CompilerInvocation &Res, - const char* const *ArgBegin, - const char* const *ArgEnd, + ArrayRef CommandLineArgs, DiagnosticsEngine &Diags); /// Get the directory where the compiler headers diff --git a/include/clang/Frontend/FrontendActions.h b/include/clang/Frontend/FrontendActions.h index 6c7bc6046f334..89ac20075fa45 100644 --- a/include/clang/Frontend/FrontendActions.h +++ b/include/clang/Frontend/FrontendActions.h @@ -126,14 +126,7 @@ protected: bool hasASTFileSupport() const override { return false; } }; -// Support different interface stub formats this way: -class GenerateInterfaceYAMLExpV1Action : public GenerateInterfaceStubAction { -protected: - std::unique_ptr CreateASTConsumer(CompilerInstance &CI, - StringRef InFile) override; -}; - -class GenerateInterfaceTBEExpV1Action : public GenerateInterfaceStubAction { +class GenerateInterfaceIfsExpV1Action : public GenerateInterfaceStubAction { protected: std::unique_ptr CreateASTConsumer(CompilerInstance &CI, StringRef InFile) override; diff --git a/include/clang/Frontend/FrontendOptions.h b/include/clang/Frontend/FrontendOptions.h index a0acb1f066f2c..09d83adf579b8 100644 --- a/include/clang/Frontend/FrontendOptions.h +++ b/include/clang/Frontend/FrontendOptions.h @@ -10,15 +10,16 @@ #define LLVM_CLANG_FRONTEND_FRONTENDOPTIONS_H #include "clang/AST/ASTDumperUtils.h" +#include "clang/Basic/LangStandard.h" #include "clang/Frontend/CommandLineSourceLoc.h" -#include "clang/Serialization/ModuleFileExtension.h" #include "clang/Sema/CodeCompleteOptions.h" +#include "clang/Serialization/ModuleFileExtension.h" #include "llvm/ADT/StringRef.h" #include #include #include -#include #include +#include namespace llvm { @@ -89,8 +90,7 @@ enum ActionKind { GeneratePCH, /// Generate Interface Stub Files. - GenerateInterfaceYAMLExpV1, - GenerateInterfaceTBEExpV1, + GenerateInterfaceIfsExpV1, /// Only execute frontend initialization. InitOnly, @@ -143,35 +143,11 @@ enum ActionKind { /// The kind of a file that we've been handed as an input. class InputKind { private: - unsigned Lang : 4; + Language Lang; unsigned Fmt : 3; unsigned Preprocessed : 1; public: - /// The language for the input, used to select and validate the language - /// standard and possible actions. - enum Language { - Unknown, - - /// Assembly: we accept this only so that we can preprocess it. - Asm, - - /// LLVM IR: we accept this so that we can run the optimizer on it, - /// and compile it to assembly or object code. - LLVM_IR, - - ///@{ Languages that the frontend can parse and compile. - C, - CXX, - ObjC, - ObjCXX, - OpenCL, - CUDA, - RenderScript, - HIP, - ///@} - }; - /// The input file format. enum Format { Source, @@ -179,7 +155,7 @@ public: Precompiled }; - constexpr InputKind(Language L = Unknown, Format F = Source, + constexpr InputKind(Language L = Language::Unknown, Format F = Source, bool PP = false) : Lang(L), Fmt(F), Preprocessed(PP) {} @@ -188,10 +164,12 @@ public: bool isPreprocessed() const { return Preprocessed; } /// Is the input kind fully-unknown? - bool isUnknown() const { return Lang == Unknown && Fmt == Source; } + bool isUnknown() const { return Lang == Language::Unknown && Fmt == Source; } /// Is the language of the input some dialect of Objective-C? - bool isObjectiveC() const { return Lang == ObjC || Lang == ObjCXX; } + bool isObjectiveC() const { + return Lang == Language::ObjC || Lang == Language::ObjCXX; + } InputKind getPreprocessed() const { return InputKind(getLanguage(), getFormat(), true); @@ -451,6 +429,9 @@ public: /// Filename to write statistics to. std::string StatsFile; + /// Minimum time granularity (in microseconds) traced by time profiler. + unsigned TimeTraceGranularity; + public: FrontendOptions() : DisableFree(false), RelocatablePCH(false), ShowHelp(false), @@ -461,12 +442,12 @@ public: UseGlobalModuleIndex(true), GenerateGlobalModuleIndex(true), ASTDumpDecls(false), ASTDumpLookups(false), BuildingImplicitModule(false), ModulesEmbedAllFiles(false), - IncludeTimestamps(true) {} + IncludeTimestamps(true), TimeTraceGranularity(500) {} /// getInputKindForExtension - Return the appropriate input kind for a file - /// extension. For example, "c" would return InputKind::C. + /// extension. For example, "c" would return Language::C. /// - /// \return The input kind for the extension, or InputKind::Unknown if the + /// \return The input kind for the extension, or Language::Unknown if the /// extension is not recognized. static InputKind getInputKindForExtension(StringRef Extension); }; diff --git a/include/clang/Frontend/LangStandard.h b/include/clang/Frontend/LangStandard.h deleted file mode 100644 index 244f14c793de0..0000000000000 --- a/include/clang/Frontend/LangStandard.h +++ /dev/null @@ -1,117 +0,0 @@ -//===--- LangStandard.h -----------------------------------------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_CLANG_FRONTEND_LANGSTANDARD_H -#define LLVM_CLANG_FRONTEND_LANGSTANDARD_H - -#include "clang/Basic/LLVM.h" -#include "clang/Frontend/FrontendOptions.h" -#include "llvm/ADT/StringRef.h" - -namespace clang { - -namespace frontend { - -enum LangFeatures { - LineComment = (1 << 0), - C99 = (1 << 1), - C11 = (1 << 2), - C17 = (1 << 3), - C2x = (1 << 4), - CPlusPlus = (1 << 5), - CPlusPlus11 = (1 << 6), - CPlusPlus14 = (1 << 7), - CPlusPlus17 = (1 << 8), - CPlusPlus2a = (1 << 9), - Digraphs = (1 << 10), - GNUMode = (1 << 11), - HexFloat = (1 << 12), - ImplicitInt = (1 << 13), - OpenCL = (1 << 14) -}; - -} - -/// LangStandard - Information about the properties of a particular language -/// standard. -struct LangStandard { - enum Kind { -#define LANGSTANDARD(id, name, lang, desc, features) \ - lang_##id, -#include "clang/Frontend/LangStandards.def" - lang_unspecified - }; - - const char *ShortName; - const char *Description; - unsigned Flags; - InputKind::Language Language; - -public: - /// getName - Get the name of this standard. - const char *getName() const { return ShortName; } - - /// getDescription - Get the description of this standard. - const char *getDescription() const { return Description; } - - /// Get the language that this standard describes. - InputKind::Language getLanguage() const { return Language; } - - /// Language supports '//' comments. - bool hasLineComments() const { return Flags & frontend::LineComment; } - - /// isC99 - Language is a superset of C99. - bool isC99() const { return Flags & frontend::C99; } - - /// isC11 - Language is a superset of C11. - bool isC11() const { return Flags & frontend::C11; } - - /// isC17 - Language is a superset of C17. - bool isC17() const { return Flags & frontend::C17; } - - /// isC2x - Language is a superset of C2x. - bool isC2x() const { return Flags & frontend::C2x; } - - /// isCPlusPlus - Language is a C++ variant. - bool isCPlusPlus() const { return Flags & frontend::CPlusPlus; } - - /// isCPlusPlus11 - Language is a C++11 variant (or later). - bool isCPlusPlus11() const { return Flags & frontend::CPlusPlus11; } - - /// isCPlusPlus14 - Language is a C++14 variant (or later). - bool isCPlusPlus14() const { return Flags & frontend::CPlusPlus14; } - - /// isCPlusPlus17 - Language is a C++17 variant (or later). - bool isCPlusPlus17() const { return Flags & frontend::CPlusPlus17; } - - /// isCPlusPlus2a - Language is a post-C++17 variant (or later). - bool isCPlusPlus2a() const { return Flags & frontend::CPlusPlus2a; } - - - /// hasDigraphs - Language supports digraphs. - bool hasDigraphs() const { return Flags & frontend::Digraphs; } - - /// isGNUMode - Language includes GNU extensions. - bool isGNUMode() const { return Flags & frontend::GNUMode; } - - /// hasHexFloats - Language supports hexadecimal float constants. - bool hasHexFloats() const { return Flags & frontend::HexFloat; } - - /// hasImplicitInt - Language allows variables to be typed as int implicitly. - bool hasImplicitInt() const { return Flags & frontend::ImplicitInt; } - - /// isOpenCL - Language is a OpenCL variant. - bool isOpenCL() const { return Flags & frontend::OpenCL; } - - static const LangStandard &getLangStandardForKind(Kind K); - static const LangStandard *getLangStandardForName(StringRef Name); -}; - -} // end namespace clang - -#endif diff --git a/include/clang/Frontend/LangStandards.def b/include/clang/Frontend/LangStandards.def deleted file mode 100644 index 0964e9b90a038..0000000000000 --- a/include/clang/Frontend/LangStandards.def +++ /dev/null @@ -1,188 +0,0 @@ -//===-- LangStandards.def - Language Standard Data --------------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#ifndef LANGSTANDARD -#error "LANGSTANDARD must be defined before including this file" -#endif - -/// LANGSTANDARD(IDENT, NAME, LANG, DESC, FEATURES) -/// -/// \param IDENT - The name of the standard as a C++ identifier. -/// \param NAME - The name of the standard. -/// \param LANG - The InputKind::Language for which this is a standard. -/// \param DESC - A short description of the standard. -/// \param FEATURES - The standard features as flags, these are enums from the -/// clang::frontend namespace, which is assumed to be be available. - -/// LANGSTANDARD_ALIAS(IDENT, ALIAS) -/// \param IDENT - The name of the standard as a C++ identifier. -/// \param ALIAS - The alias of the standard. - -/// LANGSTANDARD_ALIAS_DEPR(IDENT, ALIAS) -/// Same as LANGSTANDARD_ALIAS, but for a deprecated alias. - -#ifndef LANGSTANDARD_ALIAS -#define LANGSTANDARD_ALIAS(IDENT, ALIAS) -#endif - -#ifndef LANGSTANDARD_ALIAS_DEPR -#define LANGSTANDARD_ALIAS_DEPR(IDENT, ALIAS) LANGSTANDARD_ALIAS(IDENT, ALIAS) -#endif - -// C89-ish modes. -LANGSTANDARD(c89, "c89", - C, "ISO C 1990", - ImplicitInt) -LANGSTANDARD_ALIAS(c89, "c90") -LANGSTANDARD_ALIAS(c89, "iso9899:1990") - -LANGSTANDARD(c94, "iso9899:199409", - C, "ISO C 1990 with amendment 1", - Digraphs | ImplicitInt) - -LANGSTANDARD(gnu89, "gnu89", - C, "ISO C 1990 with GNU extensions", - LineComment | Digraphs | GNUMode | ImplicitInt) -LANGSTANDARD_ALIAS(gnu89, "gnu90") - -// C99-ish modes -LANGSTANDARD(c99, "c99", - C, "ISO C 1999", - LineComment | C99 | Digraphs | HexFloat) -LANGSTANDARD_ALIAS(c99, "iso9899:1999") -LANGSTANDARD_ALIAS_DEPR(c99, "c9x") -LANGSTANDARD_ALIAS_DEPR(c99, "iso9899:199x") - -LANGSTANDARD(gnu99, "gnu99", - C, "ISO C 1999 with GNU extensions", - LineComment | C99 | Digraphs | GNUMode | HexFloat) -LANGSTANDARD_ALIAS_DEPR(gnu99, "gnu9x") - -// C11 modes -LANGSTANDARD(c11, "c11", - C, "ISO C 2011", - LineComment | C99 | C11 | Digraphs | HexFloat) -LANGSTANDARD_ALIAS(c11, "iso9899:2011") -LANGSTANDARD_ALIAS_DEPR(c11, "c1x") -LANGSTANDARD_ALIAS_DEPR(c11, "iso9899:201x") - -LANGSTANDARD(gnu11, "gnu11", - C, "ISO C 2011 with GNU extensions", - LineComment | C99 | C11 | Digraphs | GNUMode | HexFloat) -LANGSTANDARD_ALIAS_DEPR(gnu11, "gnu1x") - -// C17 modes -LANGSTANDARD(c17, "c17", - C, "ISO C 2017", - LineComment | C99 | C11 | C17 | Digraphs | HexFloat) -LANGSTANDARD_ALIAS(c17, "iso9899:2017") -LANGSTANDARD_ALIAS(c17, "c18") -LANGSTANDARD_ALIAS(c17, "iso9899:2018") -LANGSTANDARD(gnu17, "gnu17", - C, "ISO C 2017 with GNU extensions", - LineComment | C99 | C11 | C17 | Digraphs | GNUMode | HexFloat) -LANGSTANDARD_ALIAS(gnu17, "gnu18") - -// C2x modes -LANGSTANDARD(c2x, "c2x", - C, "Working Draft for ISO C2x", - LineComment | C99 | C11 | C17 | C2x | Digraphs | HexFloat) -LANGSTANDARD(gnu2x, "gnu2x", - C, "Working Draft for ISO C2x with GNU extensions", - LineComment | C99 | C11 | C17 | C2x | Digraphs | GNUMode | HexFloat) - -// C++ modes -LANGSTANDARD(cxx98, "c++98", - CXX, "ISO C++ 1998 with amendments", - LineComment | CPlusPlus | Digraphs) -LANGSTANDARD_ALIAS(cxx98, "c++03") - -LANGSTANDARD(gnucxx98, "gnu++98", - CXX, "ISO C++ 1998 with amendments and GNU extensions", - LineComment | CPlusPlus | Digraphs | GNUMode) -LANGSTANDARD_ALIAS(gnucxx98, "gnu++03") - -LANGSTANDARD(cxx11, "c++11", - CXX, "ISO C++ 2011 with amendments", - LineComment | CPlusPlus | CPlusPlus11 | Digraphs) -LANGSTANDARD_ALIAS_DEPR(cxx11, "c++0x") - -LANGSTANDARD(gnucxx11, "gnu++11", CXX, - "ISO C++ 2011 with amendments and GNU extensions", - LineComment | CPlusPlus | CPlusPlus11 | Digraphs | GNUMode) -LANGSTANDARD_ALIAS_DEPR(gnucxx11, "gnu++0x") - -LANGSTANDARD(cxx14, "c++14", - CXX, "ISO C++ 2014 with amendments", - LineComment | CPlusPlus | CPlusPlus11 | CPlusPlus14 | Digraphs) -LANGSTANDARD_ALIAS_DEPR(cxx14, "c++1y") - -LANGSTANDARD(gnucxx14, "gnu++14", - CXX, "ISO C++ 2014 with amendments and GNU extensions", - LineComment | CPlusPlus | CPlusPlus11 | CPlusPlus14 | Digraphs | - GNUMode) -LANGSTANDARD_ALIAS_DEPR(gnucxx14, "gnu++1y") - -LANGSTANDARD(cxx17, "c++17", - CXX, "ISO C++ 2017 with amendments", - LineComment | CPlusPlus | CPlusPlus11 | CPlusPlus14 | CPlusPlus17 | - Digraphs | HexFloat) -LANGSTANDARD_ALIAS_DEPR(cxx17, "c++1z") - -LANGSTANDARD(gnucxx17, "gnu++17", - CXX, "ISO C++ 2017 with amendments and GNU extensions", - LineComment | CPlusPlus | CPlusPlus11 | CPlusPlus14 | CPlusPlus17 | - Digraphs | HexFloat | GNUMode) -LANGSTANDARD_ALIAS_DEPR(gnucxx17, "gnu++1z") - -LANGSTANDARD(cxx2a, "c++2a", - CXX, "Working draft for ISO C++ 2020", - LineComment | CPlusPlus | CPlusPlus11 | CPlusPlus14 | CPlusPlus17 | - CPlusPlus2a | Digraphs | HexFloat) - -LANGSTANDARD(gnucxx2a, "gnu++2a", - CXX, "Working draft for ISO C++ 2020 with GNU extensions", - LineComment | CPlusPlus | CPlusPlus11 | CPlusPlus14 | CPlusPlus17 | - CPlusPlus2a | Digraphs | HexFloat | GNUMode) - -// OpenCL -LANGSTANDARD(opencl10, "cl1.0", - OpenCL, "OpenCL 1.0", - LineComment | C99 | Digraphs | HexFloat | OpenCL) -LANGSTANDARD_ALIAS_DEPR(opencl10, "cl") - -LANGSTANDARD(opencl11, "cl1.1", - OpenCL, "OpenCL 1.1", - LineComment | C99 | Digraphs | HexFloat | OpenCL) -LANGSTANDARD(opencl12, "cl1.2", - OpenCL, "OpenCL 1.2", - LineComment | C99 | Digraphs | HexFloat | OpenCL) -LANGSTANDARD(opencl20, "cl2.0", - OpenCL, "OpenCL 2.0", - LineComment | C99 | Digraphs | HexFloat | OpenCL) -LANGSTANDARD(openclcpp, "c++", - OpenCL, "C++ for OpenCL", - LineComment | CPlusPlus | CPlusPlus11 | CPlusPlus14 | CPlusPlus17 | - Digraphs | HexFloat | OpenCL) - -LANGSTANDARD_ALIAS_DEPR(opencl10, "CL") -LANGSTANDARD_ALIAS_DEPR(opencl11, "CL1.1") -LANGSTANDARD_ALIAS_DEPR(opencl12, "CL1.2") -LANGSTANDARD_ALIAS_DEPR(opencl20, "CL2.0") - -// CUDA -LANGSTANDARD(cuda, "cuda", CUDA, "NVIDIA CUDA(tm)", - LineComment | CPlusPlus | Digraphs) - -// HIP -LANGSTANDARD(hip, "hip", HIP, "HIP", - LineComment | CPlusPlus | Digraphs) - -#undef LANGSTANDARD -#undef LANGSTANDARD_ALIAS -#undef LANGSTANDARD_ALIAS_DEPR diff --git a/include/clang/Frontend/Utils.h b/include/clang/Frontend/Utils.h index 74e563218c31f..0f9b17ee50893 100644 --- a/include/clang/Frontend/Utils.h +++ b/include/clang/Frontend/Utils.h @@ -99,11 +99,11 @@ public: /// Return true if system files should be passed to sawDependency(). virtual bool needSystemDependencies() { return false; } - // implementation detail /// Add a dependency \p Filename if it has not been seen before and /// sawDependency() returns true. - void maybeAddDependency(StringRef Filename, bool FromModule, bool IsSystem, - bool IsModuleFile, bool IsMissing); + virtual void maybeAddDependency(StringRef Filename, bool FromModule, + bool IsSystem, bool IsModuleFile, + bool IsMissing); protected: /// Return true if the filename was added to the list of dependencies, false @@ -213,13 +213,18 @@ createChainedIncludesSource(CompilerInstance &CI, /// createInvocationFromCommandLine - Construct a compiler invocation object for /// a command line argument vector. /// +/// \param ShouldRecoverOnErrors - whether we should attempt to return a +/// non-null (and possibly incorrect) CompilerInvocation if any errors were +/// encountered. When this flag is false, always return null on errors. +/// /// \return A CompilerInvocation, or 0 if none was built for the given /// argument vector. std::unique_ptr createInvocationFromCommandLine( ArrayRef Args, IntrusiveRefCntPtr Diags = IntrusiveRefCntPtr(), - IntrusiveRefCntPtr VFS = nullptr); + IntrusiveRefCntPtr VFS = nullptr, + bool ShouldRecoverOnErrors = false); /// Return the value of the last argument as an integer, or a default. If Diags /// is non-null, emits an error if the argument is given, but non-integral. diff --git a/include/clang/Index/CodegenNameGenerator.h b/include/clang/Index/CodegenNameGenerator.h deleted file mode 100644 index 98b3a5de817af..0000000000000 --- a/include/clang/Index/CodegenNameGenerator.h +++ /dev/null @@ -1,52 +0,0 @@ -//===- CodegenNameGenerator.h - Codegen name generation -------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// Determines the name that the symbol will get for code generation. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_CLANG_INDEX_CODEGENNAMEGENERATOR_H -#define LLVM_CLANG_INDEX_CODEGENNAMEGENERATOR_H - -#include "clang/AST/Mangle.h" -#include "clang/Basic/LLVM.h" -#include -#include -#include - -namespace clang { - class ASTContext; - class Decl; - -namespace index { - -class CodegenNameGenerator { -public: - explicit CodegenNameGenerator(ASTContext &Ctx); - ~CodegenNameGenerator(); - - /// \returns true on failure to produce a name for the given decl, false on - /// success. - bool writeName(const Decl *D, raw_ostream &OS); - - /// Version of \c writeName function that returns a string. - std::string getName(const Decl *D); - - /// This can return multiple mangled names when applicable, e.g. for C++ - /// constructors/destructors. - std::vector getAllManglings(const Decl *D); - -private: - struct Implementation; - std::unique_ptr Impl; -}; - -} // namespace index -} // namespace clang - -#endif // LLVM_CLANG_INDEX_CODEGENNAMEGENERATOR_H diff --git a/include/clang/Index/IndexDataConsumer.h b/include/clang/Index/IndexDataConsumer.h index bc1d86696df91..72747821bf545 100644 --- a/include/clang/Index/IndexDataConsumer.h +++ b/include/clang/Index/IndexDataConsumer.h @@ -32,7 +32,7 @@ public: const DeclContext *ContainerDC; }; - virtual ~IndexDataConsumer() {} + virtual ~IndexDataConsumer() = default; virtual void initialize(ASTContext &Ctx) {} @@ -41,12 +41,16 @@ public: /// \returns true to continue indexing, or false to abort. virtual bool handleDeclOccurence(const Decl *D, SymbolRoleSet Roles, ArrayRef Relations, - SourceLocation Loc, ASTNodeInfo ASTNode); + SourceLocation Loc, ASTNodeInfo ASTNode) { + return true; + } /// \returns true to continue indexing, or false to abort. virtual bool handleMacroOccurence(const IdentifierInfo *Name, const MacroInfo *MI, SymbolRoleSet Roles, - SourceLocation Loc); + SourceLocation Loc) { + return true; + } /// \returns true to continue indexing, or false to abort. /// @@ -54,8 +58,10 @@ public: /// For "@import MyMod.SubMod", there will be a call for 'MyMod' with the /// 'reference' role, and a call for 'SubMod' with the 'declaration' role. virtual bool handleModuleOccurence(const ImportDecl *ImportD, - const Module *Mod, - SymbolRoleSet Roles, SourceLocation Loc); + const Module *Mod, SymbolRoleSet Roles, + SourceLocation Loc) { + return true; + } virtual void finish() {} }; diff --git a/include/clang/Index/IndexingAction.h b/include/clang/Index/IndexingAction.h index 9756f3c539e65..9ed2a018f1617 100644 --- a/include/clang/Index/IndexingAction.h +++ b/include/clang/Index/IndexingAction.h @@ -9,7 +9,9 @@ #ifndef LLVM_CLANG_INDEX_INDEXINGACTION_H #define LLVM_CLANG_INDEX_INDEXINGACTION_H +#include "clang/AST/ASTConsumer.h" #include "clang/Basic/LLVM.h" +#include "clang/Index/IndexingOptions.h" #include "clang/Lex/PPCallbacks.h" #include "clang/Lex/Preprocessor.h" #include "llvm/ADT/ArrayRef.h" @@ -17,6 +19,7 @@ namespace clang { class ASTContext; + class ASTConsumer; class ASTReader; class ASTUnit; class Decl; @@ -29,32 +32,24 @@ namespace serialization { namespace index { class IndexDataConsumer; -struct IndexingOptions { - enum class SystemSymbolFilterKind { - None, - DeclarationsOnly, - All, - }; +/// Creates an ASTConsumer that indexes all symbols (macros and AST decls). +std::unique_ptr createIndexingASTConsumer( + std::shared_ptr DataConsumer, + const IndexingOptions &Opts, std::shared_ptr PP, + std::function ShouldSkipFunctionBody); - SystemSymbolFilterKind SystemSymbolFilter - = SystemSymbolFilterKind::DeclarationsOnly; - bool IndexFunctionLocals = false; - bool IndexImplicitInstantiation = false; - // Whether to index macro definitions in the Preprocesor when preprocessor - // callback is not available (e.g. after parsing has finished). Note that - // macro references are not available in Proprocessor. - bool IndexMacrosInPreprocessor = false; - // Has no effect if IndexFunctionLocals are false. - bool IndexParametersInDeclarations = false; - bool IndexTemplateParameters = false; -}; +inline std::unique_ptr createIndexingASTConsumer( + std::shared_ptr DataConsumer, + const IndexingOptions &Opts, std::shared_ptr PP) { + return createIndexingASTConsumer( + std::move(DataConsumer), Opts, std::move(PP), + /*ShouldSkipFunctionBody=*/[](const Decl *) { return false; }); +} /// Creates a frontend action that indexes all symbols (macros and AST decls). -/// \param WrappedAction another frontend action to wrap over or null. std::unique_ptr createIndexingAction(std::shared_ptr DataConsumer, - IndexingOptions Opts, - std::unique_ptr WrappedAction); + const IndexingOptions &Opts); /// Recursively indexes all decls in the AST. void indexASTUnit(ASTUnit &Unit, IndexDataConsumer &DataConsumer, diff --git a/include/clang/Index/IndexingOptions.h b/include/clang/Index/IndexingOptions.h new file mode 100644 index 0000000000000..bbfd6e4a72c62 --- /dev/null +++ b/include/clang/Index/IndexingOptions.h @@ -0,0 +1,42 @@ +//===--- IndexingOptions.h - Options for indexing ---------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_INDEX_INDEXINGOPTIONS_H +#define LLVM_CLANG_INDEX_INDEXINGOPTIONS_H + +#include "clang/Frontend/FrontendOptions.h" +#include +#include + +namespace clang { +namespace index { + +struct IndexingOptions { + enum class SystemSymbolFilterKind { + None, + DeclarationsOnly, + All, + }; + + SystemSymbolFilterKind SystemSymbolFilter = + SystemSymbolFilterKind::DeclarationsOnly; + bool IndexFunctionLocals = false; + bool IndexImplicitInstantiation = false; + // Whether to index macro definitions in the Preprocesor when preprocessor + // callback is not available (e.g. after parsing has finished). Note that + // macro references are not available in Proprocessor. + bool IndexMacrosInPreprocessor = false; + // Has no effect if IndexFunctionLocals are false. + bool IndexParametersInDeclarations = false; + bool IndexTemplateParameters = false; +}; + +} // namespace index +} // namespace clang + +#endif // LLVM_CLANG_INDEX_INDEXINGOPTIONS_H diff --git a/include/clang/Lex/DependencyDirectivesSourceMinimizer.h b/include/clang/Lex/DependencyDirectivesSourceMinimizer.h index 41641078afe43..d832df6b6146b 100644 --- a/include/clang/Lex/DependencyDirectivesSourceMinimizer.h +++ b/include/clang/Lex/DependencyDirectivesSourceMinimizer.h @@ -38,6 +38,7 @@ enum TokenKind { pp_undef, pp_import, pp_pragma_import, + pp_pragma_once, pp_include_next, pp_if, pp_ifdef, @@ -46,6 +47,9 @@ enum TokenKind { pp_else, pp_endif, decl_at_import, + cxx_export_decl, + cxx_module_decl, + cxx_import_decl, pp_eof, }; @@ -62,6 +66,24 @@ struct Token { Token(TokenKind K, int Offset) : K(K), Offset(Offset) {} }; +/// Simplified token range to track the range of a potentially skippable PP +/// directive. +struct SkippedRange { + /// Offset into the output byte stream of where the skipped directive begins. + int Offset; + + /// The number of bytes that can be skipped before the preprocessing must + /// resume. + int Length; +}; + +/// Computes the potential source ranges that can be skipped by the preprocessor +/// when skipping a directive like #if, #ifdef or #elsif. +/// +/// \returns false on success, true on error. +bool computeSkippedRanges(ArrayRef Input, + llvm::SmallVectorImpl &Range); + } // end namespace minimize_source_to_dependency_directives /// Minimize the input down to the preprocessor directives that might have diff --git a/include/clang/Lex/DirectoryLookup.h b/include/clang/Lex/DirectoryLookup.h index 7c556ac351758..d526319a68c60 100644 --- a/include/clang/Lex/DirectoryLookup.h +++ b/include/clang/Lex/DirectoryLookup.h @@ -36,14 +36,17 @@ public: LT_HeaderMap }; private: - union { // This union is discriminated by isHeaderMap. + union DLU { // This union is discriminated by isHeaderMap. /// Dir - This is the actual directory that we're referring to for a normal /// directory or a framework. - const DirectoryEntry *Dir; + DirectoryEntryRef Dir; /// Map - This is the HeaderMap if this is a headermap lookup. /// const HeaderMap *Map; + + DLU(DirectoryEntryRef Dir) : Dir(Dir) {} + DLU(const HeaderMap *Map) : Map(Map) {} } u; /// DirCharacteristic - The type of directory this is: this is an instance of @@ -62,24 +65,18 @@ private: unsigned SearchedAllModuleMaps : 1; public: - /// DirectoryLookup ctor - Note that this ctor *does not take ownership* of - /// 'dir'. - DirectoryLookup(const DirectoryEntry *dir, SrcMgr::CharacteristicKind DT, + /// This ctor *does not take ownership* of 'Dir'. + DirectoryLookup(DirectoryEntryRef Dir, SrcMgr::CharacteristicKind DT, bool isFramework) - : DirCharacteristic(DT), - LookupType(isFramework ? LT_Framework : LT_NormalDir), - IsIndexHeaderMap(false), SearchedAllModuleMaps(false) { - u.Dir = dir; - } + : u(Dir), DirCharacteristic(DT), + LookupType(isFramework ? LT_Framework : LT_NormalDir), + IsIndexHeaderMap(false), SearchedAllModuleMaps(false) {} - /// DirectoryLookup ctor - Note that this ctor *does not take ownership* of - /// 'map'. - DirectoryLookup(const HeaderMap *map, SrcMgr::CharacteristicKind DT, + /// This ctor *does not take ownership* of 'Map'. + DirectoryLookup(const HeaderMap *Map, SrcMgr::CharacteristicKind DT, bool isIndexHeaderMap) - : DirCharacteristic(DT), LookupType(LT_HeaderMap), - IsIndexHeaderMap(isIndexHeaderMap), SearchedAllModuleMaps(false) { - u.Map = map; - } + : u(Map), DirCharacteristic(DT), LookupType(LT_HeaderMap), + IsIndexHeaderMap(isIndexHeaderMap), SearchedAllModuleMaps(false) {} /// getLookupType - Return the kind of directory lookup that this is: either a /// normal directory, a framework path, or a HeaderMap. @@ -92,13 +89,17 @@ public: /// getDir - Return the directory that this entry refers to. /// const DirectoryEntry *getDir() const { - return isNormalDir() ? u.Dir : nullptr; + return isNormalDir() ? &u.Dir.getDirEntry() : nullptr; } /// getFrameworkDir - Return the directory that this framework refers to. /// const DirectoryEntry *getFrameworkDir() const { - return isFramework() ? u.Dir : nullptr; + return isFramework() ? &u.Dir.getDirEntry() : nullptr; + } + + Optional getFrameworkDirRef() const { + return isFramework() ? Optional(u.Dir) : None; } /// getHeaderMap - Return the directory that this entry refers to. @@ -176,27 +177,20 @@ public: /// \param [out] MappedName if this is a headermap which maps the filename to /// a framework include ("Foo.h" -> "Foo/Foo.h"), set the new name to this /// vector and point Filename to it. - const FileEntry *LookupFile(StringRef &Filename, HeaderSearch &HS, - SourceLocation IncludeLoc, - SmallVectorImpl *SearchPath, - SmallVectorImpl *RelativePath, - Module *RequestingModule, - ModuleMap::KnownHeader *SuggestedModule, - bool &InUserSpecifiedSystemFramework, - bool &IsFrameworkFound, - bool &HasBeenMapped, - SmallVectorImpl &MappedName) const; + Optional + LookupFile(StringRef &Filename, HeaderSearch &HS, SourceLocation IncludeLoc, + SmallVectorImpl *SearchPath, + SmallVectorImpl *RelativePath, Module *RequestingModule, + ModuleMap::KnownHeader *SuggestedModule, + bool &InUserSpecifiedSystemFramework, bool &IsFrameworkFound, + bool &IsInHeaderMap, SmallVectorImpl &MappedName) const; private: - const FileEntry *DoFrameworkLookup( - StringRef Filename, HeaderSearch &HS, - SmallVectorImpl *SearchPath, - SmallVectorImpl *RelativePath, - Module *RequestingModule, + Optional DoFrameworkLookup( + StringRef Filename, HeaderSearch &HS, SmallVectorImpl *SearchPath, + SmallVectorImpl *RelativePath, Module *RequestingModule, ModuleMap::KnownHeader *SuggestedModule, - bool &InUserSpecifiedSystemFramework, - bool &IsFrameworkFound) const; - + bool &InUserSpecifiedSystemFramework, bool &IsFrameworkFound) const; }; } // end namespace clang diff --git a/include/clang/Lex/HeaderMap.h b/include/clang/Lex/HeaderMap.h index eca8755d45254..accb061e51ba3 100644 --- a/include/clang/Lex/HeaderMap.h +++ b/include/clang/Lex/HeaderMap.h @@ -13,6 +13,7 @@ #ifndef LLVM_CLANG_LEX_HEADERMAP_H #define LLVM_CLANG_LEX_HEADERMAP_H +#include "clang/Basic/FileManager.h" #include "clang/Basic/LLVM.h" #include "llvm/ADT/Optional.h" #include "llvm/Support/Compiler.h" @@ -21,8 +22,6 @@ namespace clang { -class FileEntry; -class FileManager; struct HMapBucket; struct HMapHeader; @@ -78,7 +77,7 @@ public: /// NULL and the file is found, RawPath will be set to the raw path at which /// the file was found in the file system. For example, for a search path /// ".." and a filename "../file.h" this would be "../../file.h". - const FileEntry *LookupFile(StringRef Filename, FileManager &FM) const; + Optional LookupFile(StringRef Filename, FileManager &FM) const; using HeaderMapImpl::lookupFilename; using HeaderMapImpl::getFileName; diff --git a/include/clang/Lex/HeaderSearch.h b/include/clang/Lex/HeaderSearch.h index c5e66242444a3..0d20dafe2cb11 100644 --- a/include/clang/Lex/HeaderSearch.h +++ b/include/clang/Lex/HeaderSearch.h @@ -250,12 +250,6 @@ class HeaderSearch { /// Entity used to look up stored header file information. ExternalHeaderFileInfoSource *ExternalSource = nullptr; - // Various statistics we track for performance analysis. - unsigned NumIncluded = 0; - unsigned NumMultiIncludeFileOptzn = 0; - unsigned NumFrameworkLookups = 0; - unsigned NumSubFrameworkLookups = 0; - public: HeaderSearch(std::shared_ptr HSOpts, SourceManager &SourceMgr, DiagnosticsEngine &Diags, @@ -395,7 +389,7 @@ public: /// found in any of searched SearchDirs. Will be set to false if a framework /// is found only through header maps. Doesn't guarantee the requested file is /// found. - const FileEntry *LookupFile( + Optional LookupFile( StringRef Filename, SourceLocation IncludeLoc, bool isAngled, const DirectoryLookup *FromDir, const DirectoryLookup *&CurDir, ArrayRef> Includers, @@ -410,7 +404,7 @@ public: /// within ".../Carbon.framework/Headers/Carbon.h", check to see if /// HIToolbox is a subframework within Carbon.framework. If so, return /// the FileEntry for the designated file, otherwise return null. - const FileEntry *LookupSubframeworkHeader( + Optional LookupSubframeworkHeader( StringRef Filename, const FileEntry *ContextFileEnt, SmallVectorImpl *SearchPath, SmallVectorImpl *RelativePath, Module *RequestingModule, ModuleMap::KnownHeader *SuggestedModule); @@ -544,8 +538,6 @@ public: const FileEntry *lookupModuleMapFile(const DirectoryEntry *Dir, bool IsFramework); - void IncrementFrameworkLookupCount() { ++NumFrameworkLookups; } - /// Determine whether there is a module map that may map the header /// with the given file name to a (sub)module. /// Always returns false if modules are disabled. @@ -649,7 +641,7 @@ private: /// Look up the file with the specified name and determine its owning /// module. - const FileEntry * + Optional getFileAndSuggestModule(StringRef FileName, SourceLocation IncludeLoc, const DirectoryEntry *Dir, bool IsSystemHeaderDir, Module *RequestingModule, diff --git a/include/clang/Lex/HeaderSearchOptions.h b/include/clang/Lex/HeaderSearchOptions.h index ed128bce485fa..5c19a41986b5f 100644 --- a/include/clang/Lex/HeaderSearchOptions.h +++ b/include/clang/Lex/HeaderSearchOptions.h @@ -11,6 +11,7 @@ #include "clang/Basic/LLVM.h" #include "llvm/ADT/CachedHashString.h" +#include "llvm/ADT/Hashing.h" #include "llvm/ADT/SetVector.h" #include "llvm/ADT/StringRef.h" #include @@ -195,6 +196,10 @@ public: /// Whether to validate system input files when a module is loaded. unsigned ModulesValidateSystemHeaders : 1; + // Whether the content of input files should be hashed and used to + // validate consistency. + unsigned ValidateASTInputFilesContent : 1; + /// Whether the module includes debug information (-gmodules). unsigned UseDebugInfo : 1; @@ -202,14 +207,23 @@ public: unsigned ModulesHashContent : 1; + /// Whether we should include all things that could impact the module in the + /// hash. + /// + /// This includes things like the full header search path, and enabled + /// diagnostics. + unsigned ModulesStrictContextHash : 1; + HeaderSearchOptions(StringRef _Sysroot = "/") : Sysroot(_Sysroot), ModuleFormat("raw"), DisableModuleHash(false), ImplicitModuleMaps(false), ModuleMapFileHomeIsCwd(false), UseBuiltinIncludes(true), UseStandardSystemIncludes(true), UseStandardCXXIncludes(true), UseLibcxx(false), Verbose(false), ModulesValidateOncePerBuildSession(false), - ModulesValidateSystemHeaders(false), UseDebugInfo(false), - ModulesValidateDiagnosticOptions(true), ModulesHashContent(false) {} + ModulesValidateSystemHeaders(false), + ValidateASTInputFilesContent(false), UseDebugInfo(false), + ModulesValidateDiagnosticOptions(true), ModulesHashContent(false), + ModulesStrictContextHash(false) {} /// AddPath - Add the \p Path path to the specified \p Group list. void AddPath(StringRef Path, frontend::IncludeDirGroup Group, @@ -233,6 +247,15 @@ public: } }; +inline llvm::hash_code hash_value(const HeaderSearchOptions::Entry &E) { + return llvm::hash_combine(E.Path, E.Group, E.IsFramework, E.IgnoreSysRoot); +} + +inline llvm::hash_code +hash_value(const HeaderSearchOptions::SystemHeaderPrefix &SHP) { + return llvm::hash_combine(SHP.Prefix, SHP.IsSystemHeader); +} + } // namespace clang #endif // LLVM_CLANG_LEX_HEADERSEARCHOPTIONS_H diff --git a/include/clang/Lex/Lexer.h b/include/clang/Lex/Lexer.h index 69cfe62e4bdbc..97a222f4a703f 100644 --- a/include/clang/Lex/Lexer.h +++ b/include/clang/Lex/Lexer.h @@ -265,6 +265,21 @@ public: /// Return the current location in the buffer. const char *getBufferLocation() const { return BufferPtr; } + /// Returns the current lexing offset. + unsigned getCurrentBufferOffset() { + assert(BufferPtr >= BufferStart && "Invalid buffer state"); + return BufferPtr - BufferStart; + } + + /// Skip over \p NumBytes bytes. + /// + /// If the skip is successful, the next token will be lexed from the new + /// offset. The lexer also assumes that we skipped to the start of the line. + /// + /// \returns true if the skip failed (new offset would have been past the + /// end of the buffer), false otherwise. + bool skipOver(unsigned NumBytes); + /// Stringify - Convert the specified string into a C string by i) escaping /// '\\' and " characters and ii) replacing newline character(s) with "\\n". /// If Charify is true, this escapes the ' character instead of ". diff --git a/include/clang/Lex/MacroArgs.h b/include/clang/Lex/MacroArgs.h index 8806f2d8c6567..59676c30e0a59 100644 --- a/include/clang/Lex/MacroArgs.h +++ b/include/clang/Lex/MacroArgs.h @@ -48,10 +48,6 @@ class MacroArgs final /// stream. std::vector > PreExpArgTokens; - /// StringifiedArgs - This contains arguments in 'stringified' form. If the - /// stringified form of an argument has not yet been computed, this is empty. - std::vector StringifiedArgs; - /// ArgCache - This is a linked list of MacroArgs objects that the /// Preprocessor owns which we use to avoid thrashing malloc/free. MacroArgs *ArgCache; @@ -94,12 +90,6 @@ public: const std::vector & getPreExpArgument(unsigned Arg, Preprocessor &PP); - /// getStringifiedArgument - Compute, cache, and return the specified argument - /// that has been 'stringified' as required by the # operator. - const Token &getStringifiedArgument(unsigned ArgNo, Preprocessor &PP, - SourceLocation ExpansionLocStart, - SourceLocation ExpansionLocEnd); - /// getNumMacroArguments - Return the number of arguments the invoked macro /// expects. unsigned getNumMacroArguments() const { return NumMacroArgs; } diff --git a/include/clang/Lex/PPCallbacks.h b/include/clang/Lex/PPCallbacks.h index f3f3796b1a309..1edcb567de660 100644 --- a/include/clang/Lex/PPCallbacks.h +++ b/include/clang/Lex/PPCallbacks.h @@ -57,10 +57,9 @@ public: /// \param FilenameTok The file name token in \#include "FileName" directive /// or macro expanded file name token from \#include MACRO(PARAMS) directive. /// Note that FilenameTok contains corresponding quotes/angles symbols. - virtual void FileSkipped(const FileEntry &SkippedFile, + virtual void FileSkipped(const FileEntryRef &SkippedFile, const Token &FilenameTok, - SrcMgr::CharacteristicKind FileType) { - } + SrcMgr::CharacteristicKind FileType) {} /// Callback invoked whenever an inclusion directive results in a /// file-not-found error. @@ -308,7 +307,7 @@ public: /// Hook called when a '__has_include' or '__has_include_next' directive is /// read. virtual void HasInclude(SourceLocation Loc, StringRef FileName, bool IsAngled, - const FileEntry *File, + Optional File, SrcMgr::CharacteristicKind FileType) {} /// Hook called when a source range is skipped. @@ -390,8 +389,7 @@ public: Second->FileChanged(Loc, Reason, FileType, PrevFID); } - void FileSkipped(const FileEntry &SkippedFile, - const Token &FilenameTok, + void FileSkipped(const FileEntryRef &SkippedFile, const Token &FilenameTok, SrcMgr::CharacteristicKind FileType) override { First->FileSkipped(SkippedFile, FilenameTok, FileType); Second->FileSkipped(SkippedFile, FilenameTok, FileType); @@ -491,7 +489,7 @@ public: } void HasInclude(SourceLocation Loc, StringRef FileName, bool IsAngled, - const FileEntry *File, + Optional File, SrcMgr::CharacteristicKind FileType) override { First->HasInclude(Loc, FileName, IsAngled, File, FileType); Second->HasInclude(Loc, FileName, IsAngled, File, FileType); diff --git a/include/clang/Lex/Preprocessor.h b/include/clang/Lex/Preprocessor.h index f65b0cda462f4..1bdd2be04c0ef 100644 --- a/include/clang/Lex/Preprocessor.h +++ b/include/clang/Lex/Preprocessor.h @@ -28,6 +28,7 @@ #include "clang/Lex/ModuleLoader.h" #include "clang/Lex/ModuleMap.h" #include "clang/Lex/PPCallbacks.h" +#include "clang/Lex/PreprocessorExcludedConditionalDirectiveSkipMapping.h" #include "clang/Lex/Token.h" #include "clang/Lex/TokenLexer.h" #include "llvm/ADT/ArrayRef.h" @@ -370,9 +371,9 @@ class Preprocessor { /// it expects a '.' or ';'. bool ModuleImportExpectsIdentifier = false; - /// The source location of the currently-active + /// The identifier and source location of the currently-active /// \#pragma clang arc_cf_code_audited begin. - SourceLocation PragmaARCCFCodeAuditedLoc; + std::pair PragmaARCCFCodeAuditedInfo; /// The source location of the currently-active /// \#pragma clang assume_nonnull begin. @@ -994,7 +995,7 @@ public: PPCallbacks *getPPCallbacks() const { return Callbacks.get(); } void addPPCallbacks(std::unique_ptr C) { if (Callbacks) - C = llvm::make_unique(std::move(C), + C = std::make_unique(std::move(C), std::move(Callbacks)); Callbacks = std::move(C); } @@ -1471,7 +1472,7 @@ public: if (LexLevel) { // It's not correct in general to enter caching lex mode while in the // middle of a nested lexing action. - auto TokCopy = llvm::make_unique(1); + auto TokCopy = std::make_unique(1); TokCopy[0] = Tok; EnterTokenStream(std::move(TokCopy), 1, true, IsReinject); } else { @@ -1601,14 +1602,16 @@ public: /// arc_cf_code_audited begin. /// /// Returns an invalid location if there is no such pragma active. - SourceLocation getPragmaARCCFCodeAuditedLoc() const { - return PragmaARCCFCodeAuditedLoc; + std::pair + getPragmaARCCFCodeAuditedInfo() const { + return PragmaARCCFCodeAuditedInfo; } /// Set the location of the currently-active \#pragma clang /// arc_cf_code_audited begin. An invalid location ends the pragma. - void setPragmaARCCFCodeAuditedLoc(SourceLocation Loc) { - PragmaARCCFCodeAuditedLoc = Loc; + void setPragmaARCCFCodeAuditedInfo(IdentifierInfo *Ident, + SourceLocation Loc) { + PragmaARCCFCodeAuditedInfo = {Ident, Loc}; } /// The location of the currently-active \#pragma clang @@ -1949,17 +1952,15 @@ public: /// Given a "foo" or \ reference, look up the indicated file. /// - /// Returns null on failure. \p isAngled indicates whether the file + /// Returns None on failure. \p isAngled indicates whether the file /// reference is for system \#include's or not (i.e. using <> instead of ""). - const FileEntry *LookupFile(SourceLocation FilenameLoc, StringRef Filename, - bool isAngled, const DirectoryLookup *FromDir, - const FileEntry *FromFile, - const DirectoryLookup *&CurDir, - SmallVectorImpl *SearchPath, - SmallVectorImpl *RelativePath, - ModuleMap::KnownHeader *SuggestedModule, - bool *IsMapped, bool *IsFrameworkFound, - bool SkipCache = false); + Optional + LookupFile(SourceLocation FilenameLoc, StringRef Filename, bool isAngled, + const DirectoryLookup *FromDir, const FileEntry *FromFile, + const DirectoryLookup *&CurDir, SmallVectorImpl *SearchPath, + SmallVectorImpl *RelativePath, + ModuleMap::KnownHeader *SuggestedModule, bool *IsMapped, + bool *IsFrameworkFound, bool SkipCache = false); /// Get the DirectoryLookup structure used to find the current /// FileEntry, if CurLexer is non-null and if applicable. @@ -2202,6 +2203,15 @@ private: } }; + Optional LookupHeaderIncludeOrImport( + const DirectoryLookup *&CurDir, StringRef Filename, + SourceLocation FilenameLoc, CharSourceRange FilenameRange, + const Token &FilenameTok, bool &IsFrameworkFound, bool IsImportDecl, + bool &IsMapped, const DirectoryLookup *LookupFrom, + const FileEntry *LookupFromFile, StringRef LookupFilename, + SmallVectorImpl &RelativePath, SmallVectorImpl &SearchPath, + ModuleMap::KnownHeader &SuggestedModule, bool isAngled); + // File inclusion. void HandleIncludeDirective(SourceLocation HashLoc, Token &Tok, const DirectoryLookup *LookupFrom = nullptr, @@ -2313,6 +2323,15 @@ public: /// A macro is used, update information about macros that need unused /// warnings. void markMacroAsUsed(MacroInfo *MI); + +private: + Optional + getSkippedRangeForExcludedConditionalBlock(SourceLocation HashLoc); + + /// Contains the currently active skipped range mappings for skipping excluded + /// conditional directives. + ExcludedPreprocessorDirectiveSkipMapping + *ExcludedConditionalDirectiveSkipMappings; }; /// Abstract base class that describes a handler that will receive diff --git a/include/clang/Lex/PreprocessorExcludedConditionalDirectiveSkipMapping.h b/include/clang/Lex/PreprocessorExcludedConditionalDirectiveSkipMapping.h new file mode 100644 index 0000000000000..893b7ba7a9f5a --- /dev/null +++ b/include/clang/Lex/PreprocessorExcludedConditionalDirectiveSkipMapping.h @@ -0,0 +1,31 @@ +//===- PreprocessorExcludedConditionalDirectiveSkipMapping.h - --*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LEX_PREPROCESSOR_EXCLUDED_COND_DIRECTIVE_SKIP_MAPPING_H +#define LLVM_CLANG_LEX_PREPROCESSOR_EXCLUDED_COND_DIRECTIVE_SKIP_MAPPING_H + +#include "clang/Basic/LLVM.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/Support/MemoryBuffer.h" + +namespace clang { + +/// A mapping from an offset into a buffer to the number of bytes that can be +/// skipped by the preprocessor when skipping over excluded conditional +/// directive ranges. +using PreprocessorSkippedRangeMapping = llvm::DenseMap; + +/// The datastructure that holds the mapping between the active memory buffers +/// and the individual skip mappings. +using ExcludedPreprocessorDirectiveSkipMapping = + llvm::DenseMap; + +} // end namespace clang + +#endif // LLVM_CLANG_LEX_PREPROCESSOR_EXCLUDED_COND_DIRECTIVE_SKIP_MAPPING_H diff --git a/include/clang/Lex/PreprocessorOptions.h b/include/clang/Lex/PreprocessorOptions.h index 1480548c7fbe4..344afa8941723 100644 --- a/include/clang/Lex/PreprocessorOptions.h +++ b/include/clang/Lex/PreprocessorOptions.h @@ -10,6 +10,7 @@ #define LLVM_CLANG_LEX_PREPROCESSOROPTIONS_H_ #include "clang/Basic/LLVM.h" +#include "clang/Lex/PreprocessorExcludedConditionalDirectiveSkipMapping.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSet.h" #include @@ -142,6 +143,9 @@ public: /// compiler invocation and its buffers will be reused. bool RetainRemappedFileBuffers = false; + /// When enabled, excluded conditional blocks retain in the main file. + bool RetainExcludedConditionalBlocks = false; + /// The Objective-C++ ARC standard library that we should support, /// by providing appropriate definitions to retrofit the standard library /// with support for lifetime-qualified pointers. @@ -169,6 +173,17 @@ public: /// build it again. std::shared_ptr FailedModules; + /// Contains the currently active skipped range mappings for skipping excluded + /// conditional directives. + /// + /// The pointer is passed to the Preprocessor when it's constructed. The + /// pointer is unowned, the client is responsible for its lifetime. + ExcludedPreprocessorDirectiveSkipMapping + *ExcludedConditionalDirectiveSkipMappings = nullptr; + + /// Set up preprocessor for RunAnalysis action. + bool SetUpStaticAnalyzer = false; + public: PreprocessorOptions() : PrecompiledPreambleBytes(0, false) {} @@ -201,6 +216,7 @@ public: RetainRemappedFileBuffers = true; PrecompiledPreambleBytes.first = 0; PrecompiledPreambleBytes.second = false; + RetainExcludedConditionalBlocks = false; } }; diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index 7c67c35f615ab..52d159062cd7f 100644 --- a/include/clang/Parse/Parser.h +++ b/include/clang/Parse/Parser.h @@ -766,6 +766,22 @@ private: Tok.setAnnotationValue(T.getAsOpaquePtr()); } + static NamedDecl *getNonTypeAnnotation(const Token &Tok) { + return static_cast(Tok.getAnnotationValue()); + } + + static void setNonTypeAnnotation(Token &Tok, NamedDecl *ND) { + Tok.setAnnotationValue(ND); + } + + static IdentifierInfo *getIdentifierAnnotation(const Token &Tok) { + return static_cast(Tok.getAnnotationValue()); + } + + static void setIdentifierAnnotation(Token &Tok, IdentifierInfo *ND) { + Tok.setAnnotationValue(ND); + } + /// Read an already-translated primary expression out of an annotation /// token. static ExprResult getExprAnnotation(const Token &Tok) { @@ -799,8 +815,7 @@ private: /// Annotation was successful. ANK_Success }; - AnnotatedNameKind TryAnnotateName(bool IsAddressOfOperand, - CorrectionCandidateCallback *CCC = nullptr); + AnnotatedNameKind TryAnnotateName(CorrectionCandidateCallback *CCC = nullptr); /// Push a tok::annot_cxxscope token onto the token stream. void AnnotateScopeToken(CXXScopeSpec &SS, bool IsNewAnnotation); @@ -972,7 +987,7 @@ private: }; /// Consume any extra semi-colons until the end of the line. - void ConsumeExtraSemi(ExtraSemiKind Kind, unsigned TST = TST_unspecified); + void ConsumeExtraSemi(ExtraSemiKind Kind, DeclSpec::TST T = TST_unspecified); /// Return false if the next token is an identifier. An 'expected identifier' /// error is emitted otherwise. @@ -2107,12 +2122,13 @@ private: DeclGroupPtrTy ParseDeclaration(DeclaratorContext Context, SourceLocation &DeclEnd, - ParsedAttributesWithRange &attrs); - DeclGroupPtrTy ParseSimpleDeclaration(DeclaratorContext Context, - SourceLocation &DeclEnd, - ParsedAttributesWithRange &attrs, - bool RequireSemi, - ForRangeInit *FRI = nullptr); + ParsedAttributesWithRange &attrs, + SourceLocation *DeclSpecStart = nullptr); + DeclGroupPtrTy + ParseSimpleDeclaration(DeclaratorContext Context, SourceLocation &DeclEnd, + ParsedAttributesWithRange &attrs, bool RequireSemi, + ForRangeInit *FRI = nullptr, + SourceLocation *DeclSpecStart = nullptr); bool MightBeDeclarator(DeclaratorContext Context); DeclGroupPtrTy ParseDeclGroup(ParsingDeclSpec &DS, DeclaratorContext Context, SourceLocation *DeclEnd = nullptr, @@ -2160,7 +2176,7 @@ private: const ParsedTemplateInfo &TemplateInfo, AccessSpecifier AS, DeclSpecContext DSC); void ParseEnumBody(SourceLocation StartLoc, Decl *TagDecl); - void ParseStructUnionBody(SourceLocation StartLoc, unsigned TagType, + void ParseStructUnionBody(SourceLocation StartLoc, DeclSpec::TST TagType, Decl *TagDecl); void ParseStructDeclaration( @@ -2833,6 +2849,17 @@ private: DeclGroupPtrTy ParseOMPDeclareSimdClauses(DeclGroupPtrTy Ptr, CachedTokens &Toks, SourceLocation Loc); + /// Parses OpenMP context selectors and calls \p Callback for each + /// successfully parsed context selector. + bool parseOpenMPContextSelectors( + SourceLocation Loc, + llvm::function_ref< + void(SourceRange, const Sema::OpenMPDeclareVariantCtsSelectorData &)> + Callback); + + /// Parse clauses for '#pragma omp declare variant'. + void ParseOMPDeclareVariantClauses(DeclGroupPtrTy Ptr, CachedTokens &Toks, + SourceLocation Loc); /// Parse clauses for '#pragma omp declare target'. DeclGroupPtrTy ParseOMPDeclareTargetClauses(); /// Parse '#pragma omp end declare target'. @@ -2926,7 +2953,8 @@ public: /// Parses simple expression in parens for single-expression clauses of OpenMP /// constructs. /// \param RLoc Returned location of right paren. - ExprResult ParseOpenMPParensExpr(StringRef ClauseName, SourceLocation &RLoc); + ExprResult ParseOpenMPParensExpr(StringRef ClauseName, SourceLocation &RLoc, + bool IsAddressOfOperand = false); /// Data used for parsing list of variables in OpenMP clauses. struct OpenMPVarListDataTy { diff --git a/include/clang/Rewrite/Core/Rewriter.h b/include/clang/Rewrite/Core/Rewriter.h index 84c5ac3d72ee4..c89015e405582 100644 --- a/include/clang/Rewrite/Core/Rewriter.h +++ b/include/clang/Rewrite/Core/Rewriter.h @@ -46,6 +46,17 @@ public: /// If true and removing some text leaves a blank line /// also remove the empty line (false by default). + /// + /// FIXME: This sometimes corrupts the file's rewrite buffer due to + /// incorrect indexing in the implementation (see the FIXME in + /// clang::RewriteBuffer::RemoveText). Moreover, it's inefficient because + /// it must scan the buffer from the beginning to find the start of the + /// line. When feasible, it's better for the caller to check for a blank + /// line and then, if found, expand the removal range to include it. + /// Checking for a blank line is easy if, for example, the caller can + /// guarantee this is the first edit of a line. In that case, it can just + /// scan before and after the removal range until the next newline or + /// begin/end of the input. bool RemoveLineIfEmpty = false; RewriteOptions() {} diff --git a/include/clang/Sema/Overload.h b/include/clang/Sema/Overload.h index 96aadeac2ba38..a97a7181f7d5f 100644 --- a/include/clang/Sema/Overload.h +++ b/include/clang/Sema/Overload.h @@ -71,6 +71,30 @@ class Sema; OCD_ViableCandidates }; + /// The parameter ordering that will be used for the candidate. This is + /// used to represent C++20 binary operator rewrites that reverse the order + /// of the arguments. If the parameter ordering is Reversed, the Args list is + /// reversed (but obviously the ParamDecls for the function are not). + /// + /// After forming an OverloadCandidate with reversed parameters, the list + /// of conversions will (as always) be indexed by argument, so will be + /// in reverse parameter order. + enum class OverloadCandidateParamOrder : char { Normal, Reversed }; + + /// The kinds of rewrite we perform on overload candidates. Note that the + /// values here are chosen to serve as both bitflags and as a rank (lower + /// values are preferred by overload resolution). + enum OverloadCandidateRewriteKind : unsigned { + /// Candidate is not a rewritten candidate. + CRK_None = 0x0, + + /// Candidate is a rewritten candidate with a different operator name. + CRK_DifferentOperator = 0x1, + + /// Candidate is a rewritten candidate with a reversed order of parameters. + CRK_Reversed = 0x2, + }; + /// ImplicitConversionKind - The kind of implicit conversion used to /// convert an argument to a parameter's type. The enumerator values /// match with the table titled 'Conversions' in [over.ics.scs] and are listed @@ -757,7 +781,8 @@ class Sema; CXXConversionDecl *Surrogate; /// The conversion sequences used to convert the function arguments - /// to the function parameters. + /// to the function parameters. Note that these are indexed by argument, + /// so may not match the parameter order of Function. ConversionSequenceList Conversions; /// The FixIt hints which can be used to fix the Bad candidate. @@ -783,6 +808,9 @@ class Sema; /// True if the candidate was found using ADL. CallExpr::ADLCallKind IsADLCandidate : 1; + /// Whether this is a rewritten candidate, and if so, of what kind? + OverloadCandidateRewriteKind RewriteKind : 2; + /// FailureKind - The reason why this candidate is not viable. /// Actually an OverloadFailureKind. unsigned char FailureKind; @@ -826,10 +854,10 @@ class Sema; unsigned getNumParams() const { if (IsSurrogate) { - auto STy = Surrogate->getConversionType(); + QualType STy = Surrogate->getConversionType(); while (STy->isPointerType() || STy->isReferenceType()) STy = STy->getPointeeType(); - return STy->getAs()->getNumParams(); + return STy->castAs()->getNumParams(); } if (Function) return Function->getNumParams(); @@ -838,7 +866,8 @@ class Sema; private: friend class OverloadCandidateSet; - OverloadCandidate() : IsADLCandidate(CallExpr::NotADL) {} + OverloadCandidate() + : IsADLCandidate(CallExpr::NotADL), RewriteKind(CRK_None) {} }; /// OverloadCandidateSet - A set of overload candidates, used in C++ @@ -867,9 +896,54 @@ class Sema; CSK_InitByConstructor, }; + /// Information about operator rewrites to consider when adding operator + /// functions to a candidate set. + struct OperatorRewriteInfo { + OperatorRewriteInfo() + : OriginalOperator(OO_None), AllowRewrittenCandidates(false) {} + OperatorRewriteInfo(OverloadedOperatorKind Op, bool AllowRewritten) + : OriginalOperator(Op), AllowRewrittenCandidates(AllowRewritten) {} + + /// The original operator as written in the source. + OverloadedOperatorKind OriginalOperator; + /// Whether we should include rewritten candidates in the overload set. + bool AllowRewrittenCandidates; + + /// Would use of this function result in a rewrite using a different + /// operator? + bool isRewrittenOperator(const FunctionDecl *FD) { + return OriginalOperator && + FD->getDeclName().getCXXOverloadedOperator() != OriginalOperator; + } + + bool isAcceptableCandidate(const FunctionDecl *FD) { + return AllowRewrittenCandidates || !isRewrittenOperator(FD); + } + + /// Determine the kind of rewrite that should be performed for this + /// candidate. + OverloadCandidateRewriteKind + getRewriteKind(const FunctionDecl *FD, OverloadCandidateParamOrder PO) { + OverloadCandidateRewriteKind CRK = CRK_None; + if (isRewrittenOperator(FD)) + CRK = OverloadCandidateRewriteKind(CRK | CRK_DifferentOperator); + if (PO == OverloadCandidateParamOrder::Reversed) + CRK = OverloadCandidateRewriteKind(CRK | CRK_Reversed); + return CRK; + } + + /// Determine whether we should consider looking for and adding reversed + /// candidates for operator Op. + bool shouldAddReversed(OverloadedOperatorKind Op); + + /// Determine whether we should add a rewritten candidate for \p FD with + /// reversed parameter order. + bool shouldAddReversed(ASTContext &Ctx, const FunctionDecl *FD); + }; + private: SmallVector Candidates; - llvm::SmallPtrSet Functions; + llvm::SmallPtrSet Functions; // Allocator for ConversionSequenceLists. We store the first few of these // inline to avoid allocation for small sets. @@ -877,11 +951,12 @@ class Sema; SourceLocation Loc; CandidateSetKind Kind; + OperatorRewriteInfo RewriteInfo; constexpr static unsigned NumInlineBytes = 24 * sizeof(ImplicitConversionSequence); unsigned NumInlineBytesUsed = 0; - llvm::AlignedCharArray InlineSpace; + alignas(void *) char InlineSpace[NumInlineBytes]; // Address space of the object being constructed. LangAS DestAS = LangAS::Default; @@ -904,7 +979,7 @@ class Sema; unsigned NBytes = sizeof(T) * N; if (NBytes > NumInlineBytes - NumInlineBytesUsed) return SlabAllocator.Allocate(N); - char *FreeSpaceStart = InlineSpace.buffer + NumInlineBytesUsed; + char *FreeSpaceStart = InlineSpace + NumInlineBytesUsed; assert(uintptr_t(FreeSpaceStart) % alignof(void *) == 0 && "Misaligned storage!"); @@ -915,19 +990,24 @@ class Sema; void destroyCandidates(); public: - OverloadCandidateSet(SourceLocation Loc, CandidateSetKind CSK) - : Loc(Loc), Kind(CSK) {} + OverloadCandidateSet(SourceLocation Loc, CandidateSetKind CSK, + OperatorRewriteInfo RewriteInfo = {}) + : Loc(Loc), Kind(CSK), RewriteInfo(RewriteInfo) {} OverloadCandidateSet(const OverloadCandidateSet &) = delete; OverloadCandidateSet &operator=(const OverloadCandidateSet &) = delete; ~OverloadCandidateSet() { destroyCandidates(); } SourceLocation getLocation() const { return Loc; } CandidateSetKind getKind() const { return Kind; } + OperatorRewriteInfo getRewriteInfo() const { return RewriteInfo; } /// Determine when this overload candidate will be new to the /// overload set. - bool isNewCandidate(Decl *F) { - return Functions.insert(F->getCanonicalDecl()).second; + bool isNewCandidate(Decl *F, OverloadCandidateParamOrder PO = + OverloadCandidateParamOrder::Normal) { + uintptr_t Key = reinterpret_cast(F->getCanonicalDecl()); + Key |= static_cast(PO); + return Functions.insert(Key).second; } /// Clear out all of the candidates. diff --git a/include/clang/Sema/ParsedAttr.h b/include/clang/Sema/ParsedAttr.h index d87d5da04accf..d9d8585970d99 100644 --- a/include/clang/Sema/ParsedAttr.h +++ b/include/clang/Sema/ParsedAttr.h @@ -15,6 +15,7 @@ #define LLVM_CLANG_SEMA_ATTRIBUTELIST_H #include "clang/Basic/AttrSubjectMatchRules.h" +#include "clang/Basic/AttributeCommonInfo.h" #include "clang/Basic/Diagnostic.h" #include "clang/Basic/SourceLocation.h" #include "clang/Basic/TargetInfo.h" @@ -114,7 +115,8 @@ using ArgsVector = llvm::SmallVector; /// 4: __attribute__(( aligned(16) )). ParmName is unused, Args/Num used. /// class ParsedAttr final - : private llvm::TrailingObjects< + : public AttributeCommonInfo, + private llvm::TrailingObjects< ParsedAttr, ArgsUnion, detail::AvailabilityData, detail::TypeTagForDatatypeData, ParsedType, detail::PropertyData> { friend TrailingObjects; @@ -134,54 +136,15 @@ class ParsedAttr final return IsProperty; } -public: - /// The style used to specify an attribute. - enum Syntax { - /// __attribute__((...)) - AS_GNU, - - /// [[...]] - AS_CXX11, - - /// [[...]] - AS_C2x, - - /// __declspec(...) - AS_Declspec, - - /// [uuid("...")] class Foo - AS_Microsoft, - - /// __ptr16, alignas(...), etc. - AS_Keyword, - - /// #pragma ... - AS_Pragma, - - // Note TableGen depends on the order above. Do not add or change the order - // without adding related code to TableGen/ClangAttrEmitter.cpp. - /// Context-sensitive version of a keyword attribute. - AS_ContextSensitiveKeyword, - }; - private: - IdentifierInfo *AttrName; - IdentifierInfo *ScopeName; IdentifierInfo *MacroII = nullptr; SourceLocation MacroExpansionLoc; - SourceRange AttrRange; - SourceLocation ScopeLoc; SourceLocation EllipsisLoc; - unsigned AttrKind : 16; - /// The number of expression arguments this attribute has. /// The expressions themselves are stored after the object. unsigned NumArgs : 16; - /// Corresponds to the Syntax enum. - unsigned SyntaxUsed : 3; - /// True if already diagnosed as invalid. mutable unsigned Invalid : 1; @@ -239,14 +202,14 @@ private: IdentifierInfo *scopeName, SourceLocation scopeLoc, ArgsUnion *args, unsigned numArgs, Syntax syntaxUsed, SourceLocation ellipsisLoc) - : AttrName(attrName), ScopeName(scopeName), AttrRange(attrRange), - ScopeLoc(scopeLoc), EllipsisLoc(ellipsisLoc), NumArgs(numArgs), - SyntaxUsed(syntaxUsed), Invalid(false), UsedAsTypeAttr(false), - IsAvailability(false), IsTypeTagForDatatype(false), IsProperty(false), - HasParsedType(false), HasProcessingCache(false), - IsPragmaClangAttribute(false) { - if (numArgs) memcpy(getArgsBuffer(), args, numArgs * sizeof(ArgsUnion)); - AttrKind = getKind(getName(), getScopeName(), syntaxUsed); + : AttributeCommonInfo(attrName, scopeName, attrRange, scopeLoc, + syntaxUsed), + EllipsisLoc(ellipsisLoc), NumArgs(numArgs), Invalid(false), + UsedAsTypeAttr(false), IsAvailability(false), + IsTypeTagForDatatype(false), IsProperty(false), HasParsedType(false), + HasProcessingCache(false), IsPragmaClangAttribute(false) { + if (numArgs) + memcpy(getArgsBuffer(), args, numArgs * sizeof(ArgsUnion)); } /// Constructor for availability attributes. @@ -257,9 +220,9 @@ private: const AvailabilityChange &obsoleted, SourceLocation unavailable, const Expr *messageExpr, Syntax syntaxUsed, SourceLocation strict, const Expr *replacementExpr) - : AttrName(attrName), ScopeName(scopeName), AttrRange(attrRange), - ScopeLoc(scopeLoc), NumArgs(1), SyntaxUsed(syntaxUsed), Invalid(false), - UsedAsTypeAttr(false), IsAvailability(true), + : AttributeCommonInfo(attrName, scopeName, attrRange, scopeLoc, + syntaxUsed), + NumArgs(1), Invalid(false), UsedAsTypeAttr(false), IsAvailability(true), IsTypeTagForDatatype(false), IsProperty(false), HasParsedType(false), HasProcessingCache(false), IsPragmaClangAttribute(false), UnavailableLoc(unavailable), MessageExpr(messageExpr) { @@ -267,7 +230,6 @@ private: memcpy(getArgsBuffer(), &PVal, sizeof(ArgsUnion)); new (getAvailabilityData()) detail::AvailabilityData( introduced, deprecated, obsoleted, strict, replacementExpr); - AttrKind = getKind(getName(), getScopeName(), syntaxUsed); } /// Constructor for objc_bridge_related attributes. @@ -275,16 +237,16 @@ private: IdentifierInfo *scopeName, SourceLocation scopeLoc, IdentifierLoc *Parm1, IdentifierLoc *Parm2, IdentifierLoc *Parm3, Syntax syntaxUsed) - : AttrName(attrName), ScopeName(scopeName), AttrRange(attrRange), - ScopeLoc(scopeLoc), NumArgs(3), SyntaxUsed(syntaxUsed), Invalid(false), - UsedAsTypeAttr(false), IsAvailability(false), - IsTypeTagForDatatype(false), IsProperty(false), HasParsedType(false), - HasProcessingCache(false), IsPragmaClangAttribute(false) { + : AttributeCommonInfo(attrName, scopeName, attrRange, scopeLoc, + syntaxUsed), + NumArgs(3), Invalid(false), UsedAsTypeAttr(false), + IsAvailability(false), IsTypeTagForDatatype(false), IsProperty(false), + HasParsedType(false), HasProcessingCache(false), + IsPragmaClangAttribute(false) { ArgsUnion *Args = getArgsBuffer(); Args[0] = Parm1; Args[1] = Parm2; Args[2] = Parm3; - AttrKind = getKind(getName(), getScopeName(), syntaxUsed); } /// Constructor for type_tag_for_datatype attribute. @@ -292,31 +254,31 @@ private: IdentifierInfo *scopeName, SourceLocation scopeLoc, IdentifierLoc *ArgKind, ParsedType matchingCType, bool layoutCompatible, bool mustBeNull, Syntax syntaxUsed) - : AttrName(attrName), ScopeName(scopeName), AttrRange(attrRange), - ScopeLoc(scopeLoc), NumArgs(1), SyntaxUsed(syntaxUsed), Invalid(false), - UsedAsTypeAttr(false), IsAvailability(false), - IsTypeTagForDatatype(true), IsProperty(false), HasParsedType(false), - HasProcessingCache(false), IsPragmaClangAttribute(false) { + : AttributeCommonInfo(attrName, scopeName, attrRange, scopeLoc, + syntaxUsed), + NumArgs(1), Invalid(false), UsedAsTypeAttr(false), + IsAvailability(false), IsTypeTagForDatatype(true), IsProperty(false), + HasParsedType(false), HasProcessingCache(false), + IsPragmaClangAttribute(false) { ArgsUnion PVal(ArgKind); memcpy(getArgsBuffer(), &PVal, sizeof(ArgsUnion)); detail::TypeTagForDatatypeData &ExtraData = getTypeTagForDatatypeDataSlot(); new (&ExtraData.MatchingCType) ParsedType(matchingCType); ExtraData.LayoutCompatible = layoutCompatible; ExtraData.MustBeNull = mustBeNull; - AttrKind = getKind(getName(), getScopeName(), syntaxUsed); } /// Constructor for attributes with a single type argument. ParsedAttr(IdentifierInfo *attrName, SourceRange attrRange, IdentifierInfo *scopeName, SourceLocation scopeLoc, ParsedType typeArg, Syntax syntaxUsed) - : AttrName(attrName), ScopeName(scopeName), AttrRange(attrRange), - ScopeLoc(scopeLoc), NumArgs(0), SyntaxUsed(syntaxUsed), Invalid(false), - UsedAsTypeAttr(false), IsAvailability(false), - IsTypeTagForDatatype(false), IsProperty(false), HasParsedType(true), - HasProcessingCache(false), IsPragmaClangAttribute(false) { + : AttributeCommonInfo(attrName, scopeName, attrRange, scopeLoc, + syntaxUsed), + NumArgs(0), Invalid(false), UsedAsTypeAttr(false), + IsAvailability(false), IsTypeTagForDatatype(false), IsProperty(false), + HasParsedType(true), HasProcessingCache(false), + IsPragmaClangAttribute(false) { new (&getTypeBuffer()) ParsedType(typeArg); - AttrKind = getKind(getName(), getScopeName(), syntaxUsed); } /// Constructor for microsoft __declspec(property) attribute. @@ -324,13 +286,13 @@ private: IdentifierInfo *scopeName, SourceLocation scopeLoc, IdentifierInfo *getterId, IdentifierInfo *setterId, Syntax syntaxUsed) - : AttrName(attrName), ScopeName(scopeName), AttrRange(attrRange), - ScopeLoc(scopeLoc), NumArgs(0), SyntaxUsed(syntaxUsed), Invalid(false), - UsedAsTypeAttr(false), IsAvailability(false), - IsTypeTagForDatatype(false), IsProperty(true), HasParsedType(false), - HasProcessingCache(false), IsPragmaClangAttribute(false) { + : AttributeCommonInfo(attrName, scopeName, attrRange, scopeLoc, + syntaxUsed), + NumArgs(0), Invalid(false), UsedAsTypeAttr(false), + IsAvailability(false), IsTypeTagForDatatype(false), IsProperty(true), + HasParsedType(false), HasProcessingCache(false), + IsPragmaClangAttribute(false) { new (&getPropertyDataBuffer()) detail::PropertyData(getterId, setterId); - AttrKind = getKind(getName(), getScopeName(), syntaxUsed); } /// Type tag information is stored immediately following the arguments, if @@ -372,27 +334,6 @@ public: void operator delete(void *) = delete; - enum Kind { - #define PARSED_ATTR(NAME) AT_##NAME, - #include "clang/Sema/AttrParsedAttrList.inc" - #undef PARSED_ATTR - IgnoredAttribute, - UnknownAttribute - }; - - IdentifierInfo *getName() const { return AttrName; } - SourceLocation getLoc() const { return AttrRange.getBegin(); } - SourceRange getRange() const { return AttrRange; } - - bool hasScope() const { return ScopeName; } - IdentifierInfo *getScopeName() const { return ScopeName; } - SourceLocation getScopeLoc() const { return ScopeLoc; } - - bool isGNUScope() const { - return ScopeName && - (ScopeName->isStr("gnu") || ScopeName->isStr("__gnu__")); - } - bool hasParsedType() const { return HasParsedType; } /// Is this the Microsoft __declspec(property) attribute? @@ -400,30 +341,6 @@ public: return IsProperty; } - bool isAlignasAttribute() const { - // FIXME: Use a better mechanism to determine this. - return getKind() == AT_Aligned && isKeywordAttribute(); - } - - bool isDeclspecAttribute() const { return SyntaxUsed == AS_Declspec; } - bool isMicrosoftAttribute() const { return SyntaxUsed == AS_Microsoft; } - - bool isCXX11Attribute() const { - return SyntaxUsed == AS_CXX11 || isAlignasAttribute(); - } - - bool isC2xAttribute() const { - return SyntaxUsed == AS_C2x; - } - - bool isKeywordAttribute() const { - return SyntaxUsed == AS_Keyword || SyntaxUsed == AS_ContextSensitiveKeyword; - } - - bool isContextSensitiveKeywordAttribute() const { - return SyntaxUsed == AS_ContextSensitiveKeyword; - } - bool isInvalid() const { return Invalid; } void setInvalid(bool b = true) const { Invalid = b; } @@ -450,10 +367,6 @@ public: bool isPackExpansion() const { return EllipsisLoc.isValid(); } SourceLocation getEllipsisLoc() const { return EllipsisLoc; } - Kind getKind() const { return Kind(AttrKind); } - static Kind getKind(const IdentifierInfo *Name, const IdentifierInfo *Scope, - Syntax SyntaxUsed); - /// getNumArgs - Return the number of actual arguments to this attribute. unsigned getNumArgs() const { return NumArgs; } @@ -480,54 +393,61 @@ public: } const AvailabilityChange &getAvailabilityIntroduced() const { - assert(getKind() == AT_Availability && "Not an availability attribute"); + assert(getParsedKind() == AT_Availability && + "Not an availability attribute"); return getAvailabilityData()->Changes[detail::IntroducedSlot]; } const AvailabilityChange &getAvailabilityDeprecated() const { - assert(getKind() == AT_Availability && "Not an availability attribute"); + assert(getParsedKind() == AT_Availability && + "Not an availability attribute"); return getAvailabilityData()->Changes[detail::DeprecatedSlot]; } const AvailabilityChange &getAvailabilityObsoleted() const { - assert(getKind() == AT_Availability && "Not an availability attribute"); + assert(getParsedKind() == AT_Availability && + "Not an availability attribute"); return getAvailabilityData()->Changes[detail::ObsoletedSlot]; } SourceLocation getStrictLoc() const { - assert(getKind() == AT_Availability && "Not an availability attribute"); + assert(getParsedKind() == AT_Availability && + "Not an availability attribute"); return getAvailabilityData()->StrictLoc; } SourceLocation getUnavailableLoc() const { - assert(getKind() == AT_Availability && "Not an availability attribute"); + assert(getParsedKind() == AT_Availability && + "Not an availability attribute"); return UnavailableLoc; } const Expr * getMessageExpr() const { - assert(getKind() == AT_Availability && "Not an availability attribute"); + assert(getParsedKind() == AT_Availability && + "Not an availability attribute"); return MessageExpr; } const Expr *getReplacementExpr() const { - assert(getKind() == AT_Availability && "Not an availability attribute"); + assert(getParsedKind() == AT_Availability && + "Not an availability attribute"); return getAvailabilityData()->Replacement; } const ParsedType &getMatchingCType() const { - assert(getKind() == AT_TypeTagForDatatype && + assert(getParsedKind() == AT_TypeTagForDatatype && "Not a type_tag_for_datatype attribute"); return getTypeTagForDatatypeDataSlot().MatchingCType; } bool getLayoutCompatible() const { - assert(getKind() == AT_TypeTagForDatatype && + assert(getParsedKind() == AT_TypeTagForDatatype && "Not a type_tag_for_datatype attribute"); return getTypeTagForDatatypeDataSlot().LayoutCompatible; } bool getMustBeNull() const { - assert(getKind() == AT_TypeTagForDatatype && + assert(getParsedKind() == AT_TypeTagForDatatype && "Not a type_tag_for_datatype attribute"); return getTypeTagForDatatypeDataSlot().MustBeNull; } @@ -570,11 +490,6 @@ public: return MacroExpansionLoc; } - /// Get an index into the attribute spelling list - /// defined in Attr.td. This index is used by an attribute - /// to pretty print itself. - unsigned getAttributeSpellingListIndex() const; - bool isTargetSpecificAttr() const; bool isTypeAttr() const; bool isStmtAttr() const; @@ -603,7 +518,7 @@ public: /// If this is an OpenCL addr space attribute returns its representation /// in LangAS, otherwise returns default addr space. LangAS asOpenCLLangAS() const { - switch (getKind()) { + switch (getParsedKind()) { case ParsedAttr::AT_OpenCLConstantAddressSpace: return LangAS::opencl_constant; case ParsedAttr::AT_OpenCLGlobalAddressSpace: @@ -618,6 +533,8 @@ public: return LangAS::Default; } } + + AttributeCommonInfo::Kind getKind() const { return getParsedKind(); } }; class AttributePool; @@ -889,8 +806,9 @@ public: } bool hasAttribute(ParsedAttr::Kind K) const { - return llvm::any_of( - AttrList, [K](const ParsedAttr *AL) { return AL->getKind() == K; }); + return llvm::any_of(AttrList, [K](const ParsedAttr *AL) { + return AL->getParsedKind() == K; + }); } private: @@ -1038,28 +956,28 @@ enum AttributeDeclKind { inline const DiagnosticBuilder &operator<<(const DiagnosticBuilder &DB, const ParsedAttr &At) { - DB.AddTaggedVal(reinterpret_cast(At.getName()), + DB.AddTaggedVal(reinterpret_cast(At.getAttrName()), DiagnosticsEngine::ak_identifierinfo); return DB; } inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD, const ParsedAttr &At) { - PD.AddTaggedVal(reinterpret_cast(At.getName()), + PD.AddTaggedVal(reinterpret_cast(At.getAttrName()), DiagnosticsEngine::ak_identifierinfo); return PD; } inline const DiagnosticBuilder &operator<<(const DiagnosticBuilder &DB, const ParsedAttr *At) { - DB.AddTaggedVal(reinterpret_cast(At->getName()), + DB.AddTaggedVal(reinterpret_cast(At->getAttrName()), DiagnosticsEngine::ak_identifierinfo); return DB; } inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD, const ParsedAttr *At) { - PD.AddTaggedVal(reinterpret_cast(At->getName()), + PD.AddTaggedVal(reinterpret_cast(At->getAttrName()), DiagnosticsEngine::ak_identifierinfo); return PD; } diff --git a/include/clang/Sema/ScopeInfo.h b/include/clang/Sema/ScopeInfo.h index ea2595113d589..4f7534f9ef1af 100644 --- a/include/clang/Sema/ScopeInfo.h +++ b/include/clang/Sema/ScopeInfo.h @@ -756,13 +756,16 @@ public: unsigned short CapRegionKind; unsigned short OpenMPLevel; + unsigned short OpenMPCaptureLevel; CapturedRegionScopeInfo(DiagnosticsEngine &Diag, Scope *S, CapturedDecl *CD, RecordDecl *RD, ImplicitParamDecl *Context, - CapturedRegionKind K, unsigned OpenMPLevel) + CapturedRegionKind K, unsigned OpenMPLevel, + unsigned OpenMPCaptureLevel) : CapturingScopeInfo(Diag, ImpCap_CapturedRegion), TheCapturedDecl(CD), TheRecordDecl(RD), TheScope(S), - ContextParam(Context), CapRegionKind(K), OpenMPLevel(OpenMPLevel) { + ContextParam(Context), CapRegionKind(K), OpenMPLevel(OpenMPLevel), + OpenMPCaptureLevel(OpenMPCaptureLevel) { Kind = SK_CapturedRegion; } @@ -817,6 +820,9 @@ public: /// Whether the lambda contains an unexpanded parameter pack. bool ContainsUnexpandedParameterPack = false; + /// Packs introduced by this lambda, if any. + SmallVector LocalPacks; + /// If this is a generic lambda, use this as the depth of /// each 'auto' parameter, during initial AST construction. unsigned AutoTemplateParameterDepth = 0; diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index af762f74d745c..a911c61a07f89 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -57,6 +57,7 @@ #include #include #include +#include #include namespace llvm { @@ -158,6 +159,8 @@ namespace clang { class OMPClause; struct OMPVarListLocTy; struct OverloadCandidate; + enum class OverloadCandidateParamOrder : char; + enum OverloadCandidateRewriteKind : unsigned; class OverloadCandidateSet; class OverloadExpr; class ParenListExpr; @@ -405,13 +408,20 @@ public: /// Source location for newly created implicit MSInheritanceAttrs SourceLocation ImplicitMSInheritanceAttrLoc; + /// Holds TypoExprs that are created from `createDelayedTypo`. This is used by + /// `TransformTypos` in order to keep track of any TypoExprs that are created + /// recursively during typo correction and wipe them away if the correction + /// fails. + llvm::SmallVector TypoExprs; + /// pragma clang section kind enum PragmaClangSectionKind { PCSK_Invalid = 0, PCSK_BSS = 1, PCSK_Data = 2, PCSK_Rodata = 3, - PCSK_Text = 4 + PCSK_Text = 4, + PCSK_Relro = 5 }; enum PragmaClangSectionAction { @@ -432,6 +442,7 @@ public: PragmaClangSection PragmaClangBSSSection; PragmaClangSection PragmaClangDataSection; PragmaClangSection PragmaClangRodataSection; + PragmaClangSection PragmaClangRelroSection; PragmaClangSection PragmaClangTextSection; enum PragmaMsStackAction { @@ -1039,13 +1050,6 @@ public: /// suffice, e.g., in a default function argument. Decl *ManglingContextDecl; - /// The context information used to mangle lambda expressions - /// and block literals within this context. - /// - /// This mangling information is allocated lazily, since most contexts - /// do not have lambda expressions or block literals. - std::unique_ptr MangleNumbering; - /// If we are processing a decltype type, a set of call expressions /// for which we have deferred checking the completeness of the return type. SmallVector DelayedDecltypeCalls; @@ -1056,6 +1060,11 @@ public: llvm::SmallPtrSet PossibleDerefs; + /// Expressions appearing as the LHS of a volatile assignment in this + /// context. We produce a warning for these when popping the context if + /// they are not discarded-value expressions nor unevaluated operands. + SmallVector VolatileAssignmentLHSs; + /// \brief Describes whether we are in an expression constext which we have /// to handle differently. enum ExpressionKind { @@ -1069,12 +1078,7 @@ public: ExpressionKind ExprContext) : Context(Context), ParentCleanup(ParentCleanup), NumCleanupObjects(NumCleanupObjects), NumTypos(0), - ManglingContextDecl(ManglingContextDecl), MangleNumbering(), - ExprContext(ExprContext) {} - - /// Retrieve the mangling numbering context, used to consistently - /// number constructs like lambdas for mangling. - MangleNumberingContext &getMangleNumberingContext(ASTContext &Ctx); + ManglingContextDecl(ManglingContextDecl), ExprContext(ExprContext) {} bool isUnevaluated() const { return Context == ExpressionEvaluationContext::Unevaluated || @@ -1093,15 +1097,12 @@ public: void WarnOnPendingNoDerefs(ExpressionEvaluationContextRecord &Rec); /// Compute the mangling number context for a lambda expression or - /// block literal. + /// block literal. Also return the extra mangling decl if any. /// /// \param DC - The DeclContext containing the lambda expression or /// block literal. - /// \param[out] ManglingContextDecl - Returns the ManglingContextDecl - /// associated with the context, if relevant. - MangleNumberingContext *getCurrentMangleNumberContext( - const DeclContext *DC, - Decl *&ManglingContextDecl); + std::tuple + getCurrentMangleNumberContext(const DeclContext *DC); /// SpecialMemberOverloadResult - The overloading result for a special member @@ -1272,6 +1273,8 @@ public: void addImplicitTypedef(StringRef Name, QualType T); + bool WarnedStackExhausted = false; + public: Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer, TranslationUnitKind TUKind = TU_Complete, @@ -1303,6 +1306,16 @@ public: void PrintStats() const; + /// Warn that the stack is nearly exhausted. + void warnStackExhausted(SourceLocation Loc); + + /// Run some code with "sufficient" stack space. (Currently, at least 256K is + /// guaranteed). Produces a warning if we're low on stack space and allocates + /// more in that case. Use this in code that may recurse deeply (for example, + /// in template instantiation) to avoid stack overflow. + void runWithSufficientStackSpace(SourceLocation Loc, + llvm::function_ref Fn); + /// Helper class that creates diagnostics with optional /// template instantiation stacks. /// @@ -1415,8 +1428,8 @@ public: void RecordParsingTemplateParameterDepth(unsigned Depth); void PushCapturedRegionScope(Scope *RegionScope, CapturedDecl *CD, - RecordDecl *RD, - CapturedRegionKind K); + RecordDecl *RD, CapturedRegionKind K, + unsigned OpenMPCaptureLevel = 0); /// Custom deleter to allow FunctionScopeInfos to be kept alive for a short /// time after they've been popped. @@ -1456,6 +1469,11 @@ public: /// Retrieve the current block, if any. sema::BlockScopeInfo *getCurBlock(); + /// Get the innermost lambda enclosing the current location, if any. This + /// looks through intervening non-lambda scopes such as local functions and + /// blocks. + sema::LambdaScopeInfo *getEnclosingLambda() const; + /// Retrieve the current lambda scope info, if any. /// \param IgnoreNonLambdaCapturingScope true if should find the top-most /// lambda scope info ignoring all inner capturing scopes that are not @@ -1499,6 +1517,8 @@ public: QualType BuildAddressSpaceAttr(QualType &T, Expr *AddrSpace, SourceLocation AttrLoc); + bool CheckQualifiedFunctionForTypeId(QualType T, SourceLocation Loc); + bool CheckFunctionReturnType(QualType T, SourceLocation Loc); /// Build a function type. @@ -1621,7 +1641,7 @@ public: template void emit(const SemaDiagnosticBuilder &DB, - llvm::index_sequence) const { + std::index_sequence) const { // Apply all tuple elements to the builder in order. bool Dummy[] = {false, (DB << getPrintable(std::get(Args)))...}; (void)Dummy; @@ -1635,7 +1655,7 @@ public: void diagnose(Sema &S, SourceLocation Loc, QualType T) override { const SemaDiagnosticBuilder &DB = S.Diag(Loc, DiagID); - emit(DB, llvm::index_sequence_for()); + emit(DB, std::index_sequence_for()); DB << T; } }; @@ -1839,29 +1859,52 @@ public: /// Describes the result of the name lookup and resolution performed /// by \c ClassifyName(). enum NameClassificationKind { + /// This name is not a type or template in this context, but might be + /// something else. NC_Unknown, + /// Classification failed; an error has been produced. NC_Error, + /// The name has been typo-corrected to a keyword. NC_Keyword, + /// The name was classified as a type. NC_Type, - NC_Expression, - NC_NestedNameSpecifier, + /// The name was classified as a specific non-type, non-template + /// declaration. ActOnNameClassifiedAsNonType should be called to + /// convert the declaration to an expression. + NC_NonType, + /// The name was classified as an ADL-only function name. + /// ActOnNameClassifiedAsUndeclaredNonType should be called to convert the + /// result to an expression. + NC_UndeclaredNonType, + /// The name denotes a member of a dependent type that could not be + /// resolved. ActOnNameClassifiedAsDependentNonType should be called to + /// convert the result to an expression. + NC_DependentNonType, + /// The name was classified as a non-type, and an expression representing + /// that name has been formed. + NC_ContextIndependentExpr, + /// The name was classified as a template whose specializations are types. NC_TypeTemplate, + /// The name was classified as a variable template name. NC_VarTemplate, + /// The name was classified as a function template name. NC_FunctionTemplate, + /// The name was classified as an ADL-only function template name. NC_UndeclaredTemplate, }; class NameClassification { NameClassificationKind Kind; - ExprResult Expr; - TemplateName Template; - ParsedType Type; + union { + ExprResult Expr; + NamedDecl *NonTypeDecl; + TemplateName Template; + ParsedType Type; + }; explicit NameClassification(NameClassificationKind Kind) : Kind(Kind) {} public: - NameClassification(ExprResult Expr) : Kind(NC_Expression), Expr(Expr) {} - NameClassification(ParsedType Type) : Kind(NC_Type), Type(Type) {} NameClassification(const IdentifierInfo *Keyword) : Kind(NC_Keyword) {} @@ -1874,8 +1917,24 @@ public: return NameClassification(NC_Unknown); } - static NameClassification NestedNameSpecifier() { - return NameClassification(NC_NestedNameSpecifier); + static NameClassification ContextIndependentExpr(ExprResult E) { + NameClassification Result(NC_ContextIndependentExpr); + Result.Expr = E; + return Result; + } + + static NameClassification NonType(NamedDecl *D) { + NameClassification Result(NC_NonType); + Result.NonTypeDecl = D; + return Result; + } + + static NameClassification UndeclaredNonType() { + return NameClassification(NC_UndeclaredNonType); + } + + static NameClassification DependentNonType() { + return NameClassification(NC_DependentNonType); } static NameClassification TypeTemplate(TemplateName Name) { @@ -1904,14 +1963,19 @@ public: NameClassificationKind getKind() const { return Kind; } + ExprResult getExpression() const { + assert(Kind == NC_ContextIndependentExpr); + return Expr; + } + ParsedType getType() const { assert(Kind == NC_Type); return Type; } - ExprResult getExpression() const { - assert(Kind == NC_Expression); - return Expr; + NamedDecl *getNonTypeDecl() const { + assert(Kind == NC_NonType); + return NonTypeDecl; } TemplateName getTemplateName() const { @@ -1955,17 +2019,29 @@ public: /// \param NextToken The token following the identifier. Used to help /// disambiguate the name. /// - /// \param IsAddressOfOperand True if this name is the operand of a unary - /// address of ('&') expression, assuming it is classified as an - /// expression. - /// /// \param CCC The correction callback, if typo correction is desired. NameClassification ClassifyName(Scope *S, CXXScopeSpec &SS, IdentifierInfo *&Name, SourceLocation NameLoc, const Token &NextToken, - bool IsAddressOfOperand, CorrectionCandidateCallback *CCC = nullptr); + /// Act on the result of classifying a name as an undeclared (ADL-only) + /// non-type declaration. + ExprResult ActOnNameClassifiedAsUndeclaredNonType(IdentifierInfo *Name, + SourceLocation NameLoc); + /// Act on the result of classifying a name as an undeclared member of a + /// dependent base class. + ExprResult ActOnNameClassifiedAsDependentNonType(const CXXScopeSpec &SS, + IdentifierInfo *Name, + SourceLocation NameLoc, + bool IsAddressOfOperand); + /// Act on the result of classifying a name as a specific non-type + /// declaration. + ExprResult ActOnNameClassifiedAsNonType(Scope *S, const CXXScopeSpec &SS, + NamedDecl *Found, + SourceLocation NameLoc, + const Token &NextToken); + /// Describes the detailed kind of a template name. Used in diagnostics. enum class TemplateNameKindForDiagnostics { ClassTemplate, @@ -2076,8 +2152,16 @@ public: bool &AddToScope); bool AddOverriddenMethods(CXXRecordDecl *DC, CXXMethodDecl *MD); - bool CheckConstexprFunctionDecl(const FunctionDecl *FD); - bool CheckConstexprFunctionBody(const FunctionDecl *FD, Stmt *Body); + enum class CheckConstexprKind { + /// Diagnose issues that are non-constant or that are extensions. + Diagnose, + /// Identify whether this function satisfies the formal rules for constexpr + /// functions in the current lanugage mode (with no extensions). + CheckValid + }; + + bool CheckConstexprFunctionDefinition(const FunctionDecl *FD, + CheckConstexprKind Kind); void DiagnoseHiddenVirtualMethods(CXXMethodDecl *MD); void FindHiddenVirtualMethods(CXXMethodDecl *MD, @@ -2634,48 +2718,44 @@ public: }; /// Attribute merging methods. Return true if a new attribute was added. - AvailabilityAttr *mergeAvailabilityAttr( - NamedDecl *D, SourceRange Range, IdentifierInfo *Platform, bool Implicit, - VersionTuple Introduced, VersionTuple Deprecated, VersionTuple Obsoleted, - bool IsUnavailable, StringRef Message, bool IsStrict, - StringRef Replacement, AvailabilityMergeKind AMK, int Priority, - unsigned AttrSpellingListIndex); - TypeVisibilityAttr *mergeTypeVisibilityAttr(Decl *D, SourceRange Range, - TypeVisibilityAttr::VisibilityType Vis, - unsigned AttrSpellingListIndex); - VisibilityAttr *mergeVisibilityAttr(Decl *D, SourceRange Range, - VisibilityAttr::VisibilityType Vis, - unsigned AttrSpellingListIndex); - UuidAttr *mergeUuidAttr(Decl *D, SourceRange Range, - unsigned AttrSpellingListIndex, StringRef Uuid); - DLLImportAttr *mergeDLLImportAttr(Decl *D, SourceRange Range, - unsigned AttrSpellingListIndex); - DLLExportAttr *mergeDLLExportAttr(Decl *D, SourceRange Range, - unsigned AttrSpellingListIndex); + AvailabilityAttr * + mergeAvailabilityAttr(NamedDecl *D, const AttributeCommonInfo &CI, + IdentifierInfo *Platform, bool Implicit, + VersionTuple Introduced, VersionTuple Deprecated, + VersionTuple Obsoleted, bool IsUnavailable, + StringRef Message, bool IsStrict, StringRef Replacement, + AvailabilityMergeKind AMK, int Priority); + TypeVisibilityAttr * + mergeTypeVisibilityAttr(Decl *D, const AttributeCommonInfo &CI, + TypeVisibilityAttr::VisibilityType Vis); + VisibilityAttr *mergeVisibilityAttr(Decl *D, const AttributeCommonInfo &CI, + VisibilityAttr::VisibilityType Vis); + UuidAttr *mergeUuidAttr(Decl *D, const AttributeCommonInfo &CI, + StringRef Uuid); + DLLImportAttr *mergeDLLImportAttr(Decl *D, const AttributeCommonInfo &CI); + DLLExportAttr *mergeDLLExportAttr(Decl *D, const AttributeCommonInfo &CI); MSInheritanceAttr * - mergeMSInheritanceAttr(Decl *D, SourceRange Range, bool BestCase, - unsigned AttrSpellingListIndex, + mergeMSInheritanceAttr(Decl *D, const AttributeCommonInfo &CI, bool BestCase, MSInheritanceAttr::Spelling SemanticSpelling); - FormatAttr *mergeFormatAttr(Decl *D, SourceRange Range, + FormatAttr *mergeFormatAttr(Decl *D, const AttributeCommonInfo &CI, IdentifierInfo *Format, int FormatIdx, - int FirstArg, unsigned AttrSpellingListIndex); - SectionAttr *mergeSectionAttr(Decl *D, SourceRange Range, StringRef Name, - unsigned AttrSpellingListIndex); - CodeSegAttr *mergeCodeSegAttr(Decl *D, SourceRange Range, StringRef Name, - unsigned AttrSpellingListIndex); - AlwaysInlineAttr *mergeAlwaysInlineAttr(Decl *D, SourceRange Range, - IdentifierInfo *Ident, - unsigned AttrSpellingListIndex); - MinSizeAttr *mergeMinSizeAttr(Decl *D, SourceRange Range, - unsigned AttrSpellingListIndex); + int FirstArg); + SectionAttr *mergeSectionAttr(Decl *D, const AttributeCommonInfo &CI, + StringRef Name); + CodeSegAttr *mergeCodeSegAttr(Decl *D, const AttributeCommonInfo &CI, + StringRef Name); + AlwaysInlineAttr *mergeAlwaysInlineAttr(Decl *D, + const AttributeCommonInfo &CI, + const IdentifierInfo *Ident); + MinSizeAttr *mergeMinSizeAttr(Decl *D, const AttributeCommonInfo &CI); NoSpeculativeLoadHardeningAttr * mergeNoSpeculativeLoadHardeningAttr(Decl *D, const NoSpeculativeLoadHardeningAttr &AL); SpeculativeLoadHardeningAttr * mergeSpeculativeLoadHardeningAttr(Decl *D, const SpeculativeLoadHardeningAttr &AL); - OptimizeNoneAttr *mergeOptimizeNoneAttr(Decl *D, SourceRange Range, - unsigned AttrSpellingListIndex); + OptimizeNoneAttr *mergeOptimizeNoneAttr(Decl *D, + const AttributeCommonInfo &CI); InternalLinkageAttr *mergeInternalLinkageAttr(Decl *D, const ParsedAttr &AL); InternalLinkageAttr *mergeInternalLinkageAttr(Decl *D, const InternalLinkageAttr &AL); @@ -2786,6 +2866,9 @@ public: Expr *Value, bool AllowNRVO = true); + bool CanPerformAggregateInitializationForOverloadResolution( + const InitializedEntity &Entity, InitListExpr *From); + bool CanPerformCopyInitialization(const InitializedEntity &Entity, ExprResult Init); ExprResult PerformCopyInitialization(const InitializedEntity &Entity, @@ -2938,7 +3021,8 @@ public: bool AllowExplicit = true, bool AllowExplicitConversion = false, ADLCallKind IsADLCandidate = ADLCallKind::NotADL, - ConversionSequenceList EarlyConversions = None); + ConversionSequenceList EarlyConversions = None, + OverloadCandidateParamOrder PO = {}); void AddFunctionCandidates(const UnresolvedSetImpl &Functions, ArrayRef Args, OverloadCandidateSet &CandidateSet, @@ -2951,7 +3035,8 @@ public: Expr::Classification ObjectClassification, ArrayRef Args, OverloadCandidateSet& CandidateSet, - bool SuppressUserConversion = false); + bool SuppressUserConversion = false, + OverloadCandidateParamOrder PO = {}); void AddMethodCandidate(CXXMethodDecl *Method, DeclAccessPair FoundDecl, CXXRecordDecl *ActingContext, QualType ObjectType, @@ -2960,7 +3045,8 @@ public: OverloadCandidateSet& CandidateSet, bool SuppressUserConversions = false, bool PartialOverloading = false, - ConversionSequenceList EarlyConversions = None); + ConversionSequenceList EarlyConversions = None, + OverloadCandidateParamOrder PO = {}); void AddMethodTemplateCandidate(FunctionTemplateDecl *MethodTmpl, DeclAccessPair FoundDecl, CXXRecordDecl *ActingContext, @@ -2970,23 +3056,22 @@ public: ArrayRef Args, OverloadCandidateSet& CandidateSet, bool SuppressUserConversions = false, - bool PartialOverloading = false); + bool PartialOverloading = false, + OverloadCandidateParamOrder PO = {}); void AddTemplateOverloadCandidate( FunctionTemplateDecl *FunctionTemplate, DeclAccessPair FoundDecl, TemplateArgumentListInfo *ExplicitTemplateArgs, ArrayRef Args, OverloadCandidateSet &CandidateSet, bool SuppressUserConversions = false, bool PartialOverloading = false, bool AllowExplicit = true, - ADLCallKind IsADLCandidate = ADLCallKind::NotADL); - bool CheckNonDependentConversions(FunctionTemplateDecl *FunctionTemplate, - ArrayRef ParamTypes, - ArrayRef Args, - OverloadCandidateSet &CandidateSet, - ConversionSequenceList &Conversions, - bool SuppressUserConversions, - CXXRecordDecl *ActingContext = nullptr, - QualType ObjectType = QualType(), - Expr::Classification - ObjectClassification = {}); + ADLCallKind IsADLCandidate = ADLCallKind::NotADL, + OverloadCandidateParamOrder PO = {}); + bool CheckNonDependentConversions( + FunctionTemplateDecl *FunctionTemplate, ArrayRef ParamTypes, + ArrayRef Args, OverloadCandidateSet &CandidateSet, + ConversionSequenceList &Conversions, bool SuppressUserConversions, + CXXRecordDecl *ActingContext = nullptr, QualType ObjectType = QualType(), + Expr::Classification ObjectClassification = {}, + OverloadCandidateParamOrder PO = {}); void AddConversionCandidate( CXXConversionDecl *Conversion, DeclAccessPair FoundDecl, CXXRecordDecl *ActingContext, Expr *From, QualType ToType, @@ -3003,10 +3088,14 @@ public: const FunctionProtoType *Proto, Expr *Object, ArrayRef Args, OverloadCandidateSet& CandidateSet); + void AddNonMemberOperatorCandidates( + const UnresolvedSetImpl &Functions, ArrayRef Args, + OverloadCandidateSet &CandidateSet, + TemplateArgumentListInfo *ExplicitTemplateArgs = nullptr); void AddMemberOperatorCandidates(OverloadedOperatorKind Op, SourceLocation OpLoc, ArrayRef Args, - OverloadCandidateSet& CandidateSet, - SourceRange OpRange = SourceRange()); + OverloadCandidateSet &CandidateSet, + OverloadCandidateParamOrder PO = {}); void AddBuiltinCandidate(QualType *ParamTys, ArrayRef Args, OverloadCandidateSet& CandidateSet, bool IsAssignmentOperator = false, @@ -3022,9 +3111,10 @@ public: bool PartialOverloading = false); // Emit as a 'note' the specific overload candidate - void NoteOverloadCandidate(NamedDecl *Found, FunctionDecl *Fn, - QualType DestType = QualType(), - bool TakingAddress = false); + void NoteOverloadCandidate( + NamedDecl *Found, FunctionDecl *Fn, + OverloadCandidateRewriteKind RewriteKind = OverloadCandidateRewriteKind(), + QualType DestType = QualType(), bool TakingAddress = false); // Emit as a series of 'note's all template and non-templates identified by // the expression Expr @@ -3156,7 +3246,8 @@ public: BinaryOperatorKind Opc, const UnresolvedSetImpl &Fns, Expr *LHS, Expr *RHS, - bool RequiresADL = true); + bool RequiresADL = true, + bool AllowRewrittenCandidates = true); ExprResult CreateOverloadedArraySubscriptExpr(SourceLocation LLoc, SourceLocation RLoc, @@ -3384,6 +3475,7 @@ public: LookupNameKind NameKind, RedeclarationKind Redecl = NotForRedeclaration); + bool LookupBuiltin(LookupResult &R); bool LookupName(LookupResult &R, Scope *S, bool AllowBuiltinCreation = false); bool LookupQualifiedName(LookupResult &R, DeclContext *LookupCtx, @@ -3426,6 +3518,19 @@ public: bool DiagnoseMissing); bool isKnownName(StringRef name); + /// Status of the function emission on the CUDA/HIP/OpenMP host/device attrs. + enum class FunctionEmissionStatus { + Emitted, + CUDADiscarded, // Discarded due to CUDA/HIP hostness + OMPDiscarded, // Discarded due to OpenMP hostness + TemplateDiscarded, // Discarded due to uninstantiated templates + Unknown, + }; + FunctionEmissionStatus getEmissionStatus(FunctionDecl *Decl); + + // Whether the callee should be ignored in CUDA/HIP/OpenMP host/device check. + bool shouldIgnoreInHostDeviceCheck(FunctionDecl *Callee); + void ArgumentDependentLookup(DeclarationName Name, SourceLocation Loc, ArrayRef Args, ADLResult &Functions); @@ -4007,7 +4112,8 @@ public: typedef std::pair CapturedParamNameType; void ActOnCapturedRegionStart(SourceLocation Loc, Scope *CurScope, CapturedRegionKind Kind, - ArrayRef Params); + ArrayRef Params, + unsigned OpenMPCaptureLevel = 0); StmtResult ActOnCapturedRegionEnd(Stmt *S); void ActOnCapturedRegionError(); RecordDecl *CreateCapturedStmtRecordDecl(CapturedDecl *&CD, @@ -4215,6 +4321,9 @@ public: ExprResult TransformToPotentiallyEvaluated(Expr *E); ExprResult HandleExprEvaluationContextForTypeof(Expr *E); + ExprResult CheckUnevaluatedOperand(Expr *E); + void CheckUnusedVolatileAssignment(Expr *E); + ExprResult ActOnConstantExpression(ExprResult Res); // Functions for marking a declaration referenced. These functions also @@ -4349,6 +4458,10 @@ public: TemplateArgumentListInfo *ExplicitTemplateArgs = nullptr, ArrayRef Args = None, TypoExpr **Out = nullptr); + DeclResult LookupIvarInObjCMethod(LookupResult &Lookup, Scope *S, + IdentifierInfo *II); + ExprResult BuildIvarRefExpr(Scope *S, SourceLocation Loc, ObjCIvarDecl *IV); + ExprResult LookupInObjCMethod(LookupResult &LookUp, Scope *S, IdentifierInfo *II, bool AllowBuiltinCreation=false); @@ -4606,6 +4719,12 @@ public: MultiExprArg ArgExprs, SourceLocation RParenLoc, Expr *ExecConfig = nullptr, bool IsExecConfig = false); + enum class AtomicArgumentOrder { API, AST }; + ExprResult + BuildAtomicExpr(SourceRange CallRange, SourceRange ExprRange, + SourceLocation RParenLoc, MultiExprArg Args, + AtomicExpr::AtomicOp Op, + AtomicArgumentOrder ArgOrder = AtomicArgumentOrder::API); ExprResult BuildResolvedCallExpr(Expr *Fn, NamedDecl *NDecl, SourceLocation LParenLoc, ArrayRef Arg, SourceLocation RParenLoc, @@ -4646,8 +4765,12 @@ public: MultiExprArg InitArgList, SourceLocation RBraceLoc); + ExprResult BuildInitList(SourceLocation LBraceLoc, + MultiExprArg InitArgList, + SourceLocation RBraceLoc); + ExprResult ActOnDesignatedInitializer(Designation &Desig, - SourceLocation Loc, + SourceLocation EqualOrColonLoc, bool GNUSyntax, ExprResult Init); @@ -5803,12 +5926,17 @@ public: LambdaCaptureDefault CaptureDefault); /// Start the definition of a lambda expression. - CXXMethodDecl * - startLambdaDefinition(CXXRecordDecl *Class, SourceRange IntroducerRange, - TypeSourceInfo *MethodType, SourceLocation EndLoc, - ArrayRef Params, - ConstexprSpecKind ConstexprKind, - Optional> Mangling = None); + CXXMethodDecl *startLambdaDefinition(CXXRecordDecl *Class, + SourceRange IntroducerRange, + TypeSourceInfo *MethodType, + SourceLocation EndLoc, + ArrayRef Params, + ConstexprSpecKind ConstexprKind); + + /// Number lambda for linkage purposes if necessary. + void handleLambdaNumbering( + CXXRecordDecl *Class, CXXMethodDecl *Method, + Optional> Mangling = None); /// Endow the lambda scope info with the relevant properties. void buildLambdaScope(sema::LambdaScopeInfo *LSI, @@ -5936,6 +6064,21 @@ public: CXXConversionDecl *Conv, Expr *Src); + /// Check whether the given expression is a valid constraint expression. + /// A diagnostic is emitted if it is not, and false is returned. + bool CheckConstraintExpression(Expr *CE); + + bool CalculateConstraintSatisfaction(ConceptDecl *NamedConcept, + MultiLevelTemplateArgumentList &MLTAL, + Expr *ConstraintExpr, + bool &IsSatisfied); + + /// Check that the associated constraints of a template declaration match the + /// associated constraints of an older declaration of which it is a + /// redeclaration. + bool CheckRedeclarationConstraintMatch(TemplateParameterList *Old, + TemplateParameterList *New); + // ParseObjCStringLiteral - Parse Objective-C string literals. ExprResult ParseObjCStringLiteral(SourceLocation *AtLocs, ArrayRef Strings); @@ -6150,6 +6293,17 @@ public: ClassTemplateSpecializationDecl *BaseTemplateSpec, SourceLocation BaseLoc); + /// Add gsl::Pointer attribute to std::container::iterator + /// \param ND The declaration that introduces the name + /// std::container::iterator. \param UnderlyingRecord The record named by ND. + void inferGslPointerAttribute(NamedDecl *ND, CXXRecordDecl *UnderlyingRecord); + + /// Add [[gsl::Owner]] and [[gsl::Pointer]] attributes for std:: types. + void inferGslOwnerPointerAttribute(CXXRecordDecl *Record); + + /// Add [[gsl::Pointer]] attributes for std:: types. + void inferGslPointerAttribute(TypedefNameDecl *TD); + void CheckCompletedCXXClass(CXXRecordDecl *Record); /// Check that the C++ class annoated with "trivial_abi" satisfies all the @@ -6596,9 +6750,9 @@ public: ExprResult CheckConceptTemplateId(const CXXScopeSpec &SS, - const DeclarationNameInfo &NameInfo, - ConceptDecl *Template, - SourceLocation TemplateLoc, + SourceLocation TemplateKWLoc, + SourceLocation ConceptNameLoc, NamedDecl *FoundDecl, + ConceptDecl *NamedConcept, const TemplateArgumentListInfo *TemplateArgs); void diagnoseMissingTemplateArguments(TemplateName Name, SourceLocation Loc); @@ -7517,6 +7671,18 @@ public: /// member). DefiningSynthesizedFunction, + // We are checking the constraints associated with a constrained entity or + // the constraint expression of a concept. This includes the checks that + // atomic constraints have the type 'bool' and that they can be constant + // evaluated. + ConstraintsCheck, + + // We are substituting template arguments into a constraint expression. + ConstraintSubstitution, + + /// We are rewriting a comparison operator in terms of an operator<=>. + RewritingOperatorAsSpaceship, + /// Added for Template instantiation observation. /// Memoization means we are _not_ instantiating a template because /// it is already instantiated (but we entered a context where we @@ -7777,6 +7943,23 @@ public: ArrayRef TemplateArgs, SourceRange InstantiationRange); + struct ConstraintsCheck {}; + /// \brief Note that we are checking the constraints associated with some + /// constrained entity (a concept declaration or a template with associated + /// constraints). + InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation, + ConstraintsCheck, TemplateDecl *Template, + ArrayRef TemplateArgs, + SourceRange InstantiationRange); + + struct ConstraintSubstitution {}; + /// \brief Note that we are checking a constraint expression associated + /// with a template declaration or as part of the satisfaction check of a + /// concept. + InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation, + ConstraintSubstitution, TemplateDecl *Template, + sema::TemplateDeductionInfo &DeductionInfo, + SourceRange InstantiationRange); /// Note that we have finished instantiating this template. void Clear(); @@ -8225,6 +8408,11 @@ public: LocalInstantiationScope *StartingScope, bool InstantiatingVarTemplate = false, VarTemplateSpecializationDecl *PrevVTSD = nullptr); + + VarDecl *getVarTemplateSpecialization( + VarTemplateDecl *VarTempl, const TemplateArgumentListInfo *TemplateArgs, + const DeclarationNameInfo &MemberNameInfo, SourceLocation TemplateKWLoc); + void InstantiateVariableInitializer( VarDecl *Var, VarDecl *OldVar, const MultiLevelTemplateArgumentList &TemplateArgs); @@ -8844,51 +9032,50 @@ public: void AddOptnoneAttributeIfNoConflicts(FunctionDecl *FD, SourceLocation Loc); /// AddAlignedAttr - Adds an aligned attribute to a particular declaration. - void AddAlignedAttr(SourceRange AttrRange, Decl *D, Expr *E, - unsigned SpellingListIndex, bool IsPackExpansion); - void AddAlignedAttr(SourceRange AttrRange, Decl *D, TypeSourceInfo *T, - unsigned SpellingListIndex, bool IsPackExpansion); + void AddAlignedAttr(Decl *D, const AttributeCommonInfo &CI, Expr *E, + bool IsPackExpansion); + void AddAlignedAttr(Decl *D, const AttributeCommonInfo &CI, TypeSourceInfo *T, + bool IsPackExpansion); /// AddAssumeAlignedAttr - Adds an assume_aligned attribute to a particular /// declaration. - void AddAssumeAlignedAttr(SourceRange AttrRange, Decl *D, Expr *E, Expr *OE, - unsigned SpellingListIndex); + void AddAssumeAlignedAttr(Decl *D, const AttributeCommonInfo &CI, Expr *E, + Expr *OE); /// AddAllocAlignAttr - Adds an alloc_align attribute to a particular /// declaration. - void AddAllocAlignAttr(SourceRange AttrRange, Decl *D, Expr *ParamExpr, - unsigned SpellingListIndex); + void AddAllocAlignAttr(Decl *D, const AttributeCommonInfo &CI, + Expr *ParamExpr); /// AddAlignValueAttr - Adds an align_value attribute to a particular /// declaration. - void AddAlignValueAttr(SourceRange AttrRange, Decl *D, Expr *E, - unsigned SpellingListIndex); + void AddAlignValueAttr(Decl *D, const AttributeCommonInfo &CI, Expr *E); /// AddLaunchBoundsAttr - Adds a launch_bounds attribute to a particular /// declaration. - void AddLaunchBoundsAttr(SourceRange AttrRange, Decl *D, Expr *MaxThreads, - Expr *MinBlocks, unsigned SpellingListIndex); + void AddLaunchBoundsAttr(Decl *D, const AttributeCommonInfo &CI, + Expr *MaxThreads, Expr *MinBlocks); /// AddModeAttr - Adds a mode attribute to a particular declaration. - void AddModeAttr(SourceRange AttrRange, Decl *D, IdentifierInfo *Name, - unsigned SpellingListIndex, bool InInstantiation = false); + void AddModeAttr(Decl *D, const AttributeCommonInfo &CI, IdentifierInfo *Name, + bool InInstantiation = false); - void AddParameterABIAttr(SourceRange AttrRange, Decl *D, - ParameterABI ABI, unsigned SpellingListIndex); + void AddParameterABIAttr(Decl *D, const AttributeCommonInfo &CI, + ParameterABI ABI); enum class RetainOwnershipKind {NS, CF, OS}; - void AddXConsumedAttr(Decl *D, SourceRange SR, unsigned SpellingIndex, + void AddXConsumedAttr(Decl *D, const AttributeCommonInfo &CI, RetainOwnershipKind K, bool IsTemplateInstantiation); /// addAMDGPUFlatWorkGroupSizeAttr - Adds an amdgpu_flat_work_group_size /// attribute to a particular declaration. - void addAMDGPUFlatWorkGroupSizeAttr(SourceRange AttrRange, Decl *D, Expr *Min, - Expr *Max, unsigned SpellingListIndex); + void addAMDGPUFlatWorkGroupSizeAttr(Decl *D, const AttributeCommonInfo &CI, + Expr *Min, Expr *Max); /// addAMDGPUWavePersEUAttr - Adds an amdgpu_waves_per_eu attribute to a /// particular declaration. - void addAMDGPUWavesPerEUAttr(SourceRange AttrRange, Decl *D, Expr *Min, - Expr *Max, unsigned SpellingListIndex); + void addAMDGPUWavesPerEUAttr(Decl *D, const AttributeCommonInfo &CI, + Expr *Min, Expr *Max); bool checkNSReturnsRetainedReturnType(SourceLocation loc, QualType type); @@ -9002,6 +9189,10 @@ private: void adjustOpenMPTargetScopeIndex(unsigned &FunctionScopesIndex, unsigned Level) const; + /// Returns the number of scopes associated with the construct on the given + /// OpenMP level. + int getNumberOfConstructScopes(unsigned Level) const; + /// Push new OpenMP function region for non-capturing function. void pushOpenMPFunctionRegion(); @@ -9009,12 +9200,21 @@ private: void popOpenMPFunctionRegion(const sema::FunctionScopeInfo *OldFSI); /// Check whether we're allowed to call Callee from the current function. - void checkOpenMPDeviceFunction(SourceLocation Loc, FunctionDecl *Callee); + void checkOpenMPDeviceFunction(SourceLocation Loc, FunctionDecl *Callee, + bool CheckForDelayedContext = true); + + /// Check whether we're allowed to call Callee from the current function. + void checkOpenMPHostFunction(SourceLocation Loc, FunctionDecl *Callee, + bool CheckCaller = true); /// Check if the expression is allowed to be used in expressions for the /// OpenMP devices. void checkOpenMPDeviceExpr(const Expr *E); + /// Finishes analysis of the deferred functions calls that may be declared as + /// host/nohost during device/host compilation. + void finalizeOpenMPDelayedAnalysis(); + /// Checks if a type or a declaration is disabled due to the owning extension /// being disabled, and emits diagnostic messages if it is disabled. /// \param D type or declaration to be checked. @@ -9030,7 +9230,39 @@ private: MapT &Map, unsigned Selector = 0, SourceRange SrcRange = SourceRange()); + /// Marks all the functions that might be required for the currently active + /// OpenMP context. + void markOpenMPDeclareVariantFuncsReferenced(SourceLocation Loc, + FunctionDecl *Func, + bool MightBeOdrUse); + public: + /// Struct to store the context selectors info for declare variant directive. + struct OpenMPDeclareVariantCtsSelectorData { + OMPDeclareVariantAttr::CtxSelectorSetType CtxSet = + OMPDeclareVariantAttr::CtxSetUnknown; + OMPDeclareVariantAttr::CtxSelectorType Ctx = + OMPDeclareVariantAttr::CtxUnknown; + MutableArrayRef ImplVendors; + ExprResult CtxScore; + explicit OpenMPDeclareVariantCtsSelectorData() = default; + explicit OpenMPDeclareVariantCtsSelectorData( + OMPDeclareVariantAttr::CtxSelectorSetType CtxSet, + OMPDeclareVariantAttr::CtxSelectorType Ctx, + MutableArrayRef ImplVendors, ExprResult CtxScore) + : CtxSet(CtxSet), Ctx(Ctx), ImplVendors(ImplVendors), + CtxScore(CtxScore) {} + }; + + /// Checks if the variant/multiversion functions are compatible. + bool areMultiversionVariantFunctionsCompatible( + const FunctionDecl *OldFD, const FunctionDecl *NewFD, + const PartialDiagnostic &NoProtoDiagID, + const PartialDiagnosticAt &NoteCausedDiagIDAt, + const PartialDiagnosticAt &NoSupportDiagIDAt, + const PartialDiagnosticAt &DiffDiagIDAt, bool TemplatesSupported, + bool ConstexprSupported, bool CLinkageMayDiffer); + /// Function tries to capture lambda's captured variables in the OpenMP region /// before the original lambda is captured. void tryCaptureOpenMPLambdas(ValueDecl *V); @@ -9039,7 +9271,9 @@ public: /// reference. /// \param Level Relative level of nested OpenMP construct for that the check /// is performed. - bool isOpenMPCapturedByRef(const ValueDecl *D, unsigned Level) const; + /// \param OpenMPCaptureLevel Capture level within an OpenMP construct. + bool isOpenMPCapturedByRef(const ValueDecl *D, unsigned Level, + unsigned OpenMPCaptureLevel) const; /// Check if the specified variable is used in one of the private /// clauses (private, firstprivate, lastprivate, reduction etc.) in OpenMP @@ -9053,6 +9287,10 @@ public: /// construct. void startOpenMPLoop(); + /// If the current region is a range loop-based region, mark the start of the + /// loop construct. + void startOpenMPCXXRangeFor(); + /// Check if the specified variable is used in 'private' clause. /// \param Level Relative level of nested OpenMP construct for that the check /// is performed. @@ -9159,11 +9397,16 @@ public: bool ActOnStartOpenMPDeclareTargetDirective(SourceLocation Loc); /// Called at the end of target region i.e. '#pragme omp end declare target'. void ActOnFinishOpenMPDeclareTargetDirective(); + /// Searches for the provided declaration name for OpenMP declare target + /// directive. + NamedDecl * + lookupOpenMPDeclareTargetName(Scope *CurScope, CXXScopeSpec &ScopeSpec, + const DeclarationNameInfo &Id, + NamedDeclSetType &SameDirectiveDecls); /// Called on correct id-expression from the '#pragma omp declare target'. - void ActOnOpenMPDeclareTargetName(Scope *CurScope, CXXScopeSpec &ScopeSpec, - const DeclarationNameInfo &Id, + void ActOnOpenMPDeclareTargetName(NamedDecl *ND, SourceLocation Loc, OMPDeclareTargetDeclAttr::MapTypeTy MT, - NamedDeclSetType &SameDirectiveDecls); + OMPDeclareTargetDeclAttr::DevTypeTy DT); /// Check declaration inside target region. void checkDeclIsAllowedInOpenMPTarget(Expr *E, Decl *D, @@ -9348,6 +9591,21 @@ public: StmtResult ActOnOpenMPTaskLoopSimdDirective( ArrayRef Clauses, Stmt *AStmt, SourceLocation StartLoc, SourceLocation EndLoc, VarsWithInheritedDSAType &VarsWithImplicitDSA); + /// Called on well-formed '\#pragma omp master taskloop' after parsing of the + /// associated statement. + StmtResult ActOnOpenMPMasterTaskLoopDirective( + ArrayRef Clauses, Stmt *AStmt, SourceLocation StartLoc, + SourceLocation EndLoc, VarsWithInheritedDSAType &VarsWithImplicitDSA); + /// Called on well-formed '\#pragma omp master taskloop simd' after parsing of + /// the associated statement. + StmtResult ActOnOpenMPMasterTaskLoopSimdDirective( + ArrayRef Clauses, Stmt *AStmt, SourceLocation StartLoc, + SourceLocation EndLoc, VarsWithInheritedDSAType &VarsWithImplicitDSA); + /// Called on well-formed '\#pragma omp parallel master taskloop' after + /// parsing of the associated statement. + StmtResult ActOnOpenMPParallelMasterTaskLoopDirective( + ArrayRef Clauses, Stmt *AStmt, SourceLocation StartLoc, + SourceLocation EndLoc, VarsWithInheritedDSAType &VarsWithImplicitDSA); /// Called on well-formed '\#pragma omp distribute' after parsing /// of the associated statement. StmtResult @@ -9448,6 +9706,29 @@ public: ArrayRef Alignments, ArrayRef Linears, ArrayRef LinModifiers, ArrayRef Steps, SourceRange SR); + /// Checks '\#pragma omp declare variant' variant function and original + /// functions after parsing of the associated method/function. + /// \param DG Function declaration to which declare variant directive is + /// applied to. + /// \param VariantRef Expression that references the variant function, which + /// must be used instead of the original one, specified in \p DG. + /// \returns None, if the function/variant function are not compatible with + /// the pragma, pair of original function/variant ref expression otherwise. + Optional> checkOpenMPDeclareVariantFunction( + DeclGroupPtrTy DG, Expr *VariantRef, SourceRange SR); + + /// Called on well-formed '\#pragma omp declare variant' after parsing of + /// the associated method/function. + /// \param FD Function declaration to which declare variant directive is + /// applied to. + /// \param VariantRef Expression that references the variant function, which + /// must be used instead of the original one, specified in \p DG. + /// \param Data Set of context-specific data for the specified context + /// selector. + void ActOnOpenMPDeclareVariantDirective( + FunctionDecl *FD, Expr *VariantRef, SourceRange SR, + const Sema::OpenMPDeclareVariantCtsSelectorData &Data); + OMPClause *ActOnOpenMPSingleExprClause(OpenMPClauseKind Kind, Expr *Expr, SourceLocation StartLoc, @@ -10051,6 +10332,7 @@ public: QualType CheckShiftOperands( // C99 6.5.7 ExprResult &LHS, ExprResult &RHS, SourceLocation Loc, BinaryOperatorKind Opc, bool IsCompAssign = false); + void CheckPtrComparisonWithNullChar(ExprResult &E, ExprResult &NullE); QualType CheckCompareOperands( // C99 6.5.8/9 ExprResult &LHS, ExprResult &RHS, SourceLocation Loc, BinaryOperatorKind Opc); @@ -10137,11 +10419,11 @@ public: Ref_Compatible }; - ReferenceCompareResult CompareReferenceRelationship(SourceLocation Loc, - QualType T1, QualType T2, - bool &DerivedToBase, - bool &ObjCConversion, - bool &ObjCLifetimeConversion); + ReferenceCompareResult + CompareReferenceRelationship(SourceLocation Loc, QualType T1, QualType T2, + bool &DerivedToBase, bool &ObjCConversion, + bool &ObjCLifetimeConversion, + bool &FunctionConversion); ExprResult checkUnknownAnyCast(SourceRange TypeRange, QualType CastType, Expr *CastExpr, CastKind &CastKind, @@ -10546,6 +10828,21 @@ public: /// // Otherwise, continue parsing as normal. DeviceDiagBuilder diagIfOpenMPDeviceCode(SourceLocation Loc, unsigned DiagID); + /// Creates a DeviceDiagBuilder that emits the diagnostic if the current + /// context is "used as host code". + /// + /// - If CurContext is a `declare target` function or it is known that the + /// function is emitted for the host, emits the diagnostics immediately. + /// - If CurContext is a non-host function, just ignore it. + /// + /// Example usage: + /// + /// // Variable-length arrays are not allowed in NVPTX device code. + /// if (diagIfOpenMPHostode(Loc, diag::err_vla_unsupported)) + /// return ExprError(); + /// // Otherwise, continue parsing as normal. + DeviceDiagBuilder diagIfOpenMPHostCode(SourceLocation Loc, unsigned DiagID); + DeviceDiagBuilder targetDiag(SourceLocation Loc, unsigned DiagID); enum CUDAFunctionTarget { @@ -10907,6 +11204,7 @@ private: bool CheckARMBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall); bool CheckAArch64BuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall); + bool CheckBPFBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall); bool CheckHexagonBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall); bool CheckHexagonBuiltinCpu(unsigned BuiltinID, CallExpr *TheCall); bool CheckHexagonBuiltinArgument(unsigned BuiltinID, CallExpr *TheCall); @@ -11165,6 +11463,7 @@ public: // Emitting members of dllexported classes is delayed until the class // (including field initializers) is fully parsed. SmallVector DelayedDllExportClasses; + SmallVector DelayedDllExportMemberFunctions; private: class SavePendingParsedClassStateRAII { diff --git a/include/clang/Sema/SemaInternal.h b/include/clang/Sema/SemaInternal.h index dfb34daa14d30..cdaf7b70a92f0 100644 --- a/include/clang/Sema/SemaInternal.h +++ b/include/clang/Sema/SemaInternal.h @@ -97,7 +97,7 @@ public: bool EnteringContext) : Typo(TypoName.getName().getAsIdentifierInfo()), CurrentTCIndex(0), SavedTCIndex(0), SemaRef(SemaRef), S(S), - SS(SS ? llvm::make_unique(*SS) : nullptr), + SS(SS ? std::make_unique(*SS) : nullptr), CorrectionValidator(std::move(CCC)), MemberContext(MemberContext), Result(SemaRef, TypoName, LookupKind), Namespaces(SemaRef.Context, SemaRef.CurContext, SS), diff --git a/include/clang/Sema/TypoCorrection.h b/include/clang/Sema/TypoCorrection.h index b49a96c0b93fe..e0f8d152dbe55 100644 --- a/include/clang/Sema/TypoCorrection.h +++ b/include/clang/Sema/TypoCorrection.h @@ -356,7 +356,7 @@ public: : CorrectionCandidateCallback(Typo, TypoNNS) {} std::unique_ptr clone() override { - return llvm::make_unique(*this); + return std::make_unique(*this); } }; @@ -369,7 +369,7 @@ public: return candidate.getCorrectionDeclAs(); } std::unique_ptr clone() override { - return llvm::make_unique(*this); + return std::make_unique(*this); } }; @@ -384,7 +384,7 @@ public: bool ValidateCandidate(const TypoCorrection &candidate) override; std::unique_ptr clone() override { - return llvm::make_unique(*this); + return std::make_unique(*this); } private: @@ -409,7 +409,7 @@ public: return false; } std::unique_ptr clone() override { - return llvm::make_unique(*this); + return std::make_unique(*this); } }; diff --git a/include/clang/Serialization/ASTBitCodes.h b/include/clang/Serialization/ASTBitCodes.h index 0e1b9e0af9e6d..f310572754790 100644 --- a/include/clang/Serialization/ASTBitCodes.h +++ b/include/clang/Serialization/ASTBitCodes.h @@ -41,7 +41,7 @@ namespace serialization { /// Version 4 of AST files also requires that the version control branch and /// revision match exactly, since there is no backward compatibility of /// AST files at this time. - const unsigned VERSION_MAJOR = 7; + const unsigned VERSION_MAJOR = 8; /// AST file minor version number supported by this version of /// Clang. @@ -382,7 +382,10 @@ namespace serialization { /// inside the control block. enum InputFileRecordTypes { /// An input file. - INPUT_FILE = 1 + INPUT_FILE = 1, + + /// The input file content hash + INPUT_FILE_HASH }; /// Record types that occur within the AST block itself. @@ -1018,6 +1021,9 @@ namespace serialization { #define EXT_OPAQUE_TYPE(ExtType, Id, Ext) \ PREDEF_TYPE_##Id##_ID, #include "clang/Basic/OpenCLExtensionTypes.def" + // \brief SVE types with auto numeration +#define SVE_TYPE(Name, Id, SingletonId) PREDEF_TYPE_##Id##_ID, +#include "clang/Basic/AArch64SVEACLETypes.def" }; /// The number of predefined type IDs that are reserved for @@ -1839,6 +1845,9 @@ namespace serialization { /// A CXXMemberCallExpr record. EXPR_CXX_MEMBER_CALL, + /// A CXXRewrittenBinaryOperator record. + EXPR_CXX_REWRITTEN_BINARY_OPERATOR, + /// A CXXConstructExpr record. EXPR_CXX_CONSTRUCT, @@ -1909,6 +1918,7 @@ namespace serialization { EXPR_FUNCTION_PARM_PACK, // FunctionParmPackExpr EXPR_MATERIALIZE_TEMPORARY, // MaterializeTemporaryExpr EXPR_CXX_FOLD, // CXXFoldExpr + EXPR_CONCEPT_SPECIALIZATION,// ConceptSpecializationExpr // CUDA EXPR_CUDA_KERNEL_CALL, // CUDAKernelCallExpr @@ -1958,6 +1968,9 @@ namespace serialization { STMT_OMP_CANCEL_DIRECTIVE, STMT_OMP_TASKLOOP_DIRECTIVE, STMT_OMP_TASKLOOP_SIMD_DIRECTIVE, + STMT_OMP_MASTER_TASKLOOP_DIRECTIVE, + STMT_OMP_MASTER_TASKLOOP_SIMD_DIRECTIVE, + STMT_OMP_PARALLEL_MASTER_TASKLOOP_DIRECTIVE, STMT_OMP_DISTRIBUTE_DIRECTIVE, STMT_OMP_TARGET_UPDATE_DIRECTIVE, STMT_OMP_DISTRIBUTE_PARALLEL_FOR_DIRECTIVE, diff --git a/include/clang/Serialization/ASTReader.h b/include/clang/Serialization/ASTReader.h index 37bea48d88465..7495c2b17aa27 100644 --- a/include/clang/Serialization/ASTReader.h +++ b/include/clang/Serialization/ASTReader.h @@ -930,6 +930,9 @@ private: /// Whether validate system input files. bool ValidateSystemInputs; + /// Whether validate headers and module maps using hash based on contents. + bool ValidateASTInputFilesContent; + /// Whether we are allowed to use the global module index. bool UseGlobalIndex; @@ -1203,6 +1206,7 @@ private: struct InputFileInfo { std::string Filename; + uint64_t ContentHash; off_t StoredSize; time_t StoredTime; bool Overridden; @@ -1437,6 +1441,8 @@ private: void Error(StringRef Msg) const; void Error(unsigned DiagID, StringRef Arg1 = StringRef(), StringRef Arg2 = StringRef()) const; + void Error(unsigned DiagID, StringRef Arg1, StringRef Arg2, + unsigned Select) const; void Error(llvm::Error &&Err) const; public: @@ -1485,7 +1491,9 @@ public: StringRef isysroot = "", bool DisableValidation = false, bool AllowASTWithCompilerErrors = false, bool AllowConfigurationMismatch = false, - bool ValidateSystemInputs = false, bool UseGlobalIndex = true, + bool ValidateSystemInputs = false, + bool ValidateASTInputFilesContent = false, + bool UseGlobalIndex = true, std::unique_ptr ReadTimer = {}); ASTReader(const ASTReader &) = delete; ASTReader &operator=(const ASTReader &) = delete; @@ -1578,7 +1586,7 @@ public: /// Takes ownership of \p L. void addListener(std::unique_ptr L) { if (Listener) - L = llvm::make_unique(std::move(L), + L = std::make_unique(std::move(L), std::move(Listener)); Listener = std::move(L); } @@ -1594,7 +1602,7 @@ public: auto Old = Reader.takeListener(); if (Old) { Chained = true; - L = llvm::make_unique(std::move(L), + L = std::make_unique(std::move(L), std::move(Old)); } Reader.setListener(std::move(L)); diff --git a/include/clang/StaticAnalyzer/Checkers/Checkers.td b/include/clang/StaticAnalyzer/Checkers/Checkers.td index 2b29efba66a41..4d52655045b3b 100644 --- a/include/clang/StaticAnalyzer/Checkers/Checkers.td +++ b/include/clang/StaticAnalyzer/Checkers/Checkers.td @@ -504,6 +504,15 @@ def MoveChecker: Checker<"Move">, ]>, Documentation; +def VirtualCallModeling : Checker<"VirtualCallModeling">, + HelpText<"Auxiliary modeling for the virtual method call checkers">, + Documentation, + Hidden; + +def PureVirtualCallChecker : Checker<"PureVirtualCall">, + HelpText<"Check pure virtual function calls during construction/destruction">, + Dependencies<[VirtualCallModeling]>, + Documentation; } // end: "cplusplus" let ParentPackage = CplusplusOptIn in { @@ -552,14 +561,22 @@ def UninitializedObjectChecker: Checker<"UninitializedObject">, Documentation; def VirtualCallChecker : Checker<"VirtualCall">, - HelpText<"Check virtual function calls during construction or destruction">, + HelpText<"Check virtual function calls during construction/destruction">, CheckerOptions<[ + CmdLineOption, CmdLineOption + InAlpha> ]>, + Dependencies<[VirtualCallModeling]>, Documentation; } // end: "optin.cplusplus" @@ -636,6 +653,19 @@ let ParentPackage = DeadCode in { def DeadStoresChecker : Checker<"DeadStores">, HelpText<"Check for values stored to variables that are never read " "afterwards">, + CheckerOptions<[ + CmdLineOption, + CmdLineOption + ]>, Documentation; } // end DeadCode @@ -799,6 +829,13 @@ let ParentPackage = Taint in { def GenericTaintChecker : Checker<"TaintPropagation">, HelpText<"Generate taint information used by other checkers">, + CheckerOptions<[ + CmdLineOption, + ]>, Documentation; } // end "alpha.security.taint" diff --git a/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def b/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def index 70bd476b6c438..d853fb74f9c77 100644 --- a/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def +++ b/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def @@ -294,12 +294,20 @@ ANALYZER_OPTION(bool, DisplayCTUProgress, "display-ctu-progress", ANALYZER_OPTION(bool, ShouldTrackConditions, "track-conditions", "Whether to track conditions that are a control dependency of " "an already tracked variable.", - false) + true) ANALYZER_OPTION(bool, ShouldTrackConditionsDebug, "track-conditions-debug", "Whether to place an event at each tracked condition.", false) +ANALYZER_OPTION(bool, ShouldEmitFixItHintsAsRemarks, "fixits-as-remarks", + "Emit fix-it hints as remarks for testing purposes", + false) + +//===----------------------------------------------------------------------===// +// Unsigned analyzer options. +//===----------------------------------------------------------------------===// + ANALYZER_OPTION(unsigned, CTUImportThreshold, "ctu-import-threshold", "The maximal amount of translation units that is considered " "for import when inlining functions during CTU analysis. " @@ -308,10 +316,6 @@ ANALYZER_OPTION(unsigned, CTUImportThreshold, "ctu-import-threshold", "various translation units.", 100u) -//===----------------------------------------------------------------------===// -// Unsinged analyzer options. -//===----------------------------------------------------------------------===// - ANALYZER_OPTION( unsigned, AlwaysInlineSize, "ipa-always-inline-size", "The size of the functions (in basic blocks), which should be considered " @@ -380,12 +384,6 @@ ANALYZER_OPTION( "Value: \"constructors\", \"destructors\", \"methods\".", "destructors") -ANALYZER_OPTION_DEPENDS_ON_USER_MODE( - StringRef, IPAMode, "ipa", - "Controls the mode of inter-procedural analysis. Value: \"none\", " - "\"basic-inlining\", \"inlining\", \"dynamic\", \"dynamic-bifurcate\".", - /* SHALLOW_VAL */ "inlining", /* DEEP_VAL */ "dynamic-bifurcate") - ANALYZER_OPTION( StringRef, ExplorationStrategy, "exploration_strategy", "Value: \"dfs\", \"bfs\", \"unexplored_first\", " @@ -393,5 +391,17 @@ ANALYZER_OPTION( "\"bfs_block_dfs_contents\".", "unexplored_first_queue") +ANALYZER_OPTION( + StringRef, RawSilencedCheckersAndPackages, "silence-checkers", + "A semicolon separated list of checker and package names to silence. " + "Silenced checkers will not emit reports, but the modeling remain enabled.", + "") + +ANALYZER_OPTION_DEPENDS_ON_USER_MODE( + StringRef, IPAMode, "ipa", + "Controls the mode of inter-procedural analysis. Value: \"none\", " + "\"basic-inlining\", \"inlining\", \"dynamic\", \"dynamic-bifurcate\".", + /* SHALLOW_VAL */ "inlining", /* DEEP_VAL */ "dynamic-bifurcate") + #undef ANALYZER_OPTION_DEPENDS_ON_USER_MODE #undef ANALYZER_OPTION diff --git a/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h b/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h index 9630a229bd3bf..ce16095e10c0b 100644 --- a/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h +++ b/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h @@ -163,9 +163,16 @@ class AnalyzerOptions : public RefCountedBase { public: using ConfigTable = llvm::StringMap; + /// Retrieves the list of checkers generated from Checkers.td. This doesn't + /// contain statically linked but non-generated checkers and plugin checkers! static std::vector getRegisteredCheckers(bool IncludeExperimental = false); + /// Retrieves the list of packages generated from Checkers.td. This doesn't + /// contain statically linked but non-generated packages and plugin packages! + static std::vector + getRegisteredPackages(bool IncludeExperimental = false); + /// Convenience function for printing options or checkers and their /// description in a formatted manner. If \p MinLineWidth is set to 0, no line /// breaks are introduced for the description. @@ -188,9 +195,11 @@ public: std::pair EntryDescPair, size_t EntryWidth, size_t InitialPad, size_t MinLineWidth = 0); + /// Pairs of checker/package name and enable/disable. + std::vector> CheckersAndPackages; - /// Pair of checker name and enable/disable. - std::vector> CheckersControlList; + /// Vector of checker/package names which will not emit warnings. + std::vector SilencedCheckersAndPackages; /// A key-value table of use-specified configuration values. // TODO: This shouldn't be public. @@ -212,12 +221,12 @@ public: /// The maximum number of times the analyzer visits a block. unsigned maxBlockVisitOnPath; - /// Disable all analyzer checks. + /// Disable all analyzer checkers. /// - /// This flag allows one to disable analyzer checks on the code processed by + /// This flag allows one to disable analyzer checkers on the code processed by /// the given analysis consumer. Note, the code will get parsed and the /// command-line options will get checked. - unsigned DisableAllChecks : 1; + unsigned DisableAllCheckers : 1; unsigned ShowCheckerHelp : 1; unsigned ShowCheckerHelpAlpha : 1; @@ -269,13 +278,13 @@ public: // Create an array of all -analyzer-config command line options. Sort it in // the constructor. - std::vector AnalyzerConfigCmdFlags = { + std::vector AnalyzerConfigCmdFlags = { #define ANALYZER_OPTION_DEPENDS_ON_USER_MODE(TYPE, NAME, CMDFLAG, DESC, \ SHALLOW_VAL, DEEP_VAL) \ ANALYZER_OPTION(TYPE, NAME, CMDFLAG, DESC, SHALLOW_VAL) #define ANALYZER_OPTION(TYPE, NAME, CMDFLAG, DESC, DEFAULT_VAL) \ - CMDFLAG, + llvm::StringLiteral(CMDFLAG), #include "clang/StaticAnalyzer/Core/AnalyzerOptions.def" #undef ANALYZER_OPTION @@ -292,7 +301,7 @@ public: } AnalyzerOptions() - : DisableAllChecks(false), ShowCheckerHelp(false), + : DisableAllCheckers(false), ShowCheckerHelp(false), ShowCheckerHelpAlpha(false), ShowCheckerHelpDeveloper(false), ShowCheckerOptionList(false), ShowCheckerOptionAlphaList(false), ShowCheckerOptionDeveloperList(false), ShowEnabledCheckerList(false), @@ -310,7 +319,7 @@ public: /// If an option value is not provided, returns the given \p DefaultVal. /// @param [in] CheckerName The *full name* of the checker. One may retrieve /// this from the checker object's field \c Name, or through \c - /// CheckerManager::getCurrentCheckName within the checker's registry + /// CheckerManager::getCurrentCheckerName within the checker's registry /// function. /// Checker options are retrieved in the following format: /// `-analyzer-config CheckerName:OptionName=Value. @@ -330,7 +339,7 @@ public: /// If an option value is not provided, returns the given \p DefaultVal. /// @param [in] CheckerName The *full name* of the checker. One may retrieve /// this from the checker object's field \c Name, or through \c - /// CheckerManager::getCurrentCheckName within the checker's registry + /// CheckerManager::getCurrentCheckerName within the checker's registry /// function. /// Checker options are retrieved in the following format: /// `-analyzer-config CheckerName:OptionName=Value. @@ -350,7 +359,7 @@ public: /// If an option value is not provided, returns the given \p DefaultVal. /// @param [in] CheckerName The *full name* of the checker. One may retrieve /// this from the checker object's field \c Name, or through \c - /// CheckerManager::getCurrentCheckName within the checker's registry + /// CheckerManager::getCurrentCheckerName within the checker's registry /// function. /// Checker options are retrieved in the following format: /// `-analyzer-config CheckerName:OptionName=Value. @@ -404,6 +413,43 @@ inline UserModeKind AnalyzerOptions::getUserMode() const { return K.getValue(); } +inline std::vector +AnalyzerOptions::getRegisteredCheckers(bool IncludeExperimental) { + static constexpr llvm::StringLiteral StaticAnalyzerCheckerNames[] = { +#define GET_CHECKERS +#define CHECKER(FULLNAME, CLASS, HELPTEXT, DOC_URI, IS_HIDDEN) \ + llvm::StringLiteral(FULLNAME), +#include "clang/StaticAnalyzer/Checkers/Checkers.inc" +#undef CHECKER +#undef GET_CHECKERS + }; + std::vector Checkers; + for (StringRef CheckerName : StaticAnalyzerCheckerNames) { + if (!CheckerName.startswith("debug.") && + (IncludeExperimental || !CheckerName.startswith("alpha."))) + Checkers.push_back(CheckerName); + } + return Checkers; +} + +inline std::vector +AnalyzerOptions::getRegisteredPackages(bool IncludeExperimental) { + static constexpr llvm::StringLiteral StaticAnalyzerPackageNames[] = { +#define GET_PACKAGES +#define PACKAGE(FULLNAME) llvm::StringLiteral(FULLNAME), +#include "clang/StaticAnalyzer/Checkers/Checkers.inc" +#undef PACKAGE +#undef GET_PACKAGES + }; + std::vector Packages; + for (StringRef PackageName : StaticAnalyzerPackageNames) { + if (PackageName != "debug" && + (IncludeExperimental || PackageName != "alpha")) + Packages.push_back(PackageName); + } + return Packages; +} + } // namespace clang #endif // LLVM_CLANG_STATICANALYZER_CORE_ANALYZEROPTIONS_H diff --git a/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h b/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h index d30ad19b20f85..e94b544172a04 100644 --- a/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h +++ b/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h @@ -14,10 +14,10 @@ #ifndef LLVM_CLANG_STATICANALYZER_CORE_BUGREPORTER_BUGREPORTER_H #define LLVM_CLANG_STATICANALYZER_CORE_BUGREPORTER_BUGREPORTER_H +#include "clang/Analysis/PathDiagnostic.h" #include "clang/Basic/LLVM.h" #include "clang/Basic/SourceLocation.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h" -#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" @@ -70,59 +70,253 @@ class SValBuilder; using DiagnosticForConsumerMapTy = llvm::DenseMap>; -/// This class provides an interface through which checkers can create -/// individual bug reports. -class BugReport : public llvm::ilist_node { +/// Interface for classes constructing Stack hints. +/// +/// If a PathDiagnosticEvent occurs in a different frame than the final +/// diagnostic the hints can be used to summarize the effect of the call. +class StackHintGenerator { public: - class NodeResolver { - virtual void anchor(); + virtual ~StackHintGenerator() = 0; - public: - virtual ~NodeResolver() = default; + /// Construct the Diagnostic message for the given ExplodedNode. + virtual std::string getMessage(const ExplodedNode *N) = 0; +}; - virtual const ExplodedNode* - getOriginalNode(const ExplodedNode *N) = 0; - }; +/// Constructs a Stack hint for the given symbol. +/// +/// The class knows how to construct the stack hint message based on +/// traversing the CallExpr associated with the call and checking if the given +/// symbol is returned or is one of the arguments. +/// The hint can be customized by redefining 'getMessageForX()' methods. +class StackHintGeneratorForSymbol : public StackHintGenerator { +private: + SymbolRef Sym; + std::string Msg; - using ranges_iterator = const SourceRange *; - using VisitorList = SmallVector, 8>; - using visitor_iterator = VisitorList::iterator; - using ExtraTextList = SmallVector; - using NoteList = SmallVector, 4>; +public: + StackHintGeneratorForSymbol(SymbolRef S, StringRef M) : Sym(S), Msg(M) {} + ~StackHintGeneratorForSymbol() override = default; + + /// Search the call expression for the symbol Sym and dispatch the + /// 'getMessageForX()' methods to construct a specific message. + std::string getMessage(const ExplodedNode *N) override; + + /// Produces the message of the following form: + /// 'Msg via Nth parameter' + virtual std::string getMessageForArg(const Expr *ArgE, unsigned ArgIndex); + + virtual std::string getMessageForReturn(const CallExpr *CallExpr) { + return Msg; + } + + virtual std::string getMessageForSymbolNotFound() { + return Msg; + } +}; + +/// This class provides an interface through which checkers can create +/// individual bug reports. +class BugReport { +public: + enum class Kind { Basic, PathSensitive }; protected: friend class BugReportEquivClass; friend class BugReporter; + Kind K; const BugType& BT; - const Decl *DeclWithIssue = nullptr; std::string ShortDescription; std::string Description; + + SmallVector Ranges; + SmallVector, 4> Notes; + SmallVector Fixits; + + BugReport(Kind kind, const BugType &bt, StringRef desc) + : K(kind), BT(bt), Description(desc) {} + + BugReport(Kind K, const BugType &BT, StringRef ShortDescription, + StringRef Description) + : K(K), BT(BT), ShortDescription(ShortDescription), + Description(Description) {} + +public: + virtual ~BugReport() = default; + + Kind getKind() const { return K; } + + const BugType& getBugType() const { return BT; } + + /// A verbose warning message that is appropriate for displaying next to + /// the source code that introduces the problem. The description should be + /// at least a full sentence starting with a capital letter. The period at + /// the end of the warning is traditionally omitted. If the description + /// consists of multiple sentences, periods between the sentences are + /// encouraged, but the period at the end of the description is still omitted. + StringRef getDescription() const { return Description; } + + /// A short general warning message that is appropriate for displaying in + /// the list of all reported bugs. It should describe what kind of bug is found + /// but does not need to try to go into details of that specific bug. + /// Grammatical conventions of getDescription() apply here as well. + StringRef getShortDescription(bool UseFallback = true) const { + if (ShortDescription.empty() && UseFallback) + return Description; + return ShortDescription; + } + + /// The primary location of the bug report that points at the undesirable + /// behavior in the code. UIs should attach the warning description to this + /// location. The warning description should describe the bad behavior + /// at this location. + virtual PathDiagnosticLocation getLocation() const = 0; + + /// The smallest declaration that contains the bug location. + /// This is purely cosmetic; the declaration can be displayed to the user + /// but it does not affect whether the report is emitted. + virtual const Decl *getDeclWithIssue() const = 0; + + /// Get the location on which the report should be uniqued. Two warnings are + /// considered to be equivalent whenever they have the same bug types, + /// descriptions, and uniqueing locations. Out of a class of equivalent + /// warnings only one gets displayed to the user. For most warnings the + /// uniqueing location coincides with their location, but sometimes + /// it makes sense to use different locations. For example, a leak + /// checker can place the warning at the location where the last reference + /// to the leaking resource is dropped but at the same time unique the warning + /// by where that resource is acquired (allocated). + virtual PathDiagnosticLocation getUniqueingLocation() const = 0; + + /// Get the declaration that corresponds to (usually contains) the uniqueing + /// location. This is not actively used for uniqueing, i.e. otherwise + /// identical reports that have different uniqueing decls will be considered + /// equivalent. + virtual const Decl *getUniqueingDecl() const = 0; + + /// Add new item to the list of additional notes that need to be attached to + /// this report. If the report is path-sensitive, these notes will not be + /// displayed as part of the execution path explanation, but will be displayed + /// separately. Use bug visitors if you need to add an extra path note. + void addNote(StringRef Msg, const PathDiagnosticLocation &Pos, + ArrayRef Ranges = {}) { + auto P = std::make_shared(Pos, Msg); + + for (const auto &R : Ranges) + P->addRange(R); + + Notes.push_back(std::move(P)); + } + + ArrayRef> getNotes() { + return Notes; + } + + /// Add a range to a bug report. + /// + /// Ranges are used to highlight regions of interest in the source code. + /// They should be at the same source code line as the BugReport location. + /// By default, the source range of the statement corresponding to the error + /// node will be used; add a single invalid range to specify absence of + /// ranges. + void addRange(SourceRange R) { + assert((R.isValid() || Ranges.empty()) && "Invalid range can only be used " + "to specify that the report does not have a range."); + Ranges.push_back(R); + } + + /// Get the SourceRanges associated with the report. + virtual ArrayRef getRanges() const { + return Ranges; + } + + /// Add a fix-it hint to the bug report. + /// + /// Fix-it hints are the suggested edits to the code that would resolve + /// the problem explained by the bug report. Fix-it hints should be + /// as conservative as possible because it is not uncommon for the user + /// to blindly apply all fixits to their project. Note that it is very hard + /// to produce a good fix-it hint for most path-sensitive warnings. + void addFixItHint(const FixItHint &F) { + Fixits.push_back(F); + } + + llvm::ArrayRef getFixits() const { return Fixits; } + + /// Reports are uniqued to ensure that we do not emit multiple diagnostics + /// for each bug. + virtual void Profile(llvm::FoldingSetNodeID& hash) const = 0; +}; + +class BasicBugReport : public BugReport { PathDiagnosticLocation Location; - PathDiagnosticLocation UniqueingLocation; - const Decl *UniqueingDecl; + const Decl *DeclWithIssue = nullptr; + +public: + BasicBugReport(const BugType &bt, StringRef desc, PathDiagnosticLocation l) + : BugReport(Kind::Basic, bt, desc), Location(l) {} + + static bool classof(const BugReport *R) { + return R->getKind() == Kind::Basic; + } + + PathDiagnosticLocation getLocation() const override { + assert(Location.isValid()); + return Location; + } + + const Decl *getDeclWithIssue() const override { + return DeclWithIssue; + } + + PathDiagnosticLocation getUniqueingLocation() const override { + return getLocation(); + } + + const Decl *getUniqueingDecl() const override { + return getDeclWithIssue(); + } + + /// Specifically set the Decl where an issue occurred. This isn't necessary + /// for BugReports that cover a path as it will be automatically inferred. + void setDeclWithIssue(const Decl *declWithIssue) { + DeclWithIssue = declWithIssue; + } + void Profile(llvm::FoldingSetNodeID& hash) const override; +}; + +class PathSensitiveBugReport : public BugReport { +public: + using VisitorList = SmallVector, 8>; + using visitor_iterator = VisitorList::iterator; + using visitor_range = llvm::iterator_range; + +protected: + /// The ExplodedGraph node against which the report was thrown. It corresponds + /// to the end of the execution path that demonstrates the bug. const ExplodedNode *ErrorNode = nullptr; - SmallVector Ranges; - ExtraTextList ExtraText; - NoteList Notes; - using Symbols = llvm::DenseSet; - using Regions = llvm::DenseSet; + /// The range that corresponds to ErrorNode's program point. It is usually + /// highlighted in the report. + const SourceRange ErrorNodeRange; + + /// Profile to identify equivalent bug reports for error report coalescing. /// A (stack of) a set of symbols that are registered with this /// report as being "interesting", and thus used to help decide which /// diagnostics to include when constructing the final path diagnostic. /// The stack is largely used by BugReporter when generating PathDiagnostics /// for multiple PathDiagnosticConsumers. - SmallVector interestingSymbols; + llvm::DenseMap InterestingSymbols; /// A (stack of) set of regions that are registered with this report as being /// "interesting", and thus used to help decide which diagnostics /// to include when constructing the final path diagnostic. /// The stack is largely used by BugReporter when generating PathDiagnostics /// for multiple PathDiagnosticConsumers. - SmallVector interestingRegions; + llvm::DenseMap + InterestingRegions; /// A set of location contexts that correspoind to call sites which should be /// considered "interesting". @@ -156,66 +350,58 @@ protected: /// Conditions we're already tracking. llvm::SmallSet TrackedConditions; -private: - // Used internally by BugReporter. - Symbols &getInterestingSymbols(); - Regions &getInterestingRegions(); - - void lazyInitializeInterestingSets(); - void pushInterestingSymbolsAndRegions(); - void popInterestingSymbolsAndRegions(); - -public: - BugReport(const BugType& bt, StringRef desc, const ExplodedNode *errornode) - : BT(bt), Description(desc), ErrorNode(errornode) {} + /// Reports with different uniqueing locations are considered to be different + /// for the purposes of deduplication. + PathDiagnosticLocation UniqueingLocation; + const Decl *UniqueingDecl; - BugReport(const BugType& bt, StringRef shortDesc, StringRef desc, - const ExplodedNode *errornode) - : BT(bt), ShortDescription(shortDesc), Description(desc), - ErrorNode(errornode) {} + const Stmt *getStmt() const; - BugReport(const BugType &bt, StringRef desc, PathDiagnosticLocation l) - : BT(bt), Description(desc), Location(l) {} + /// If an event occurs in a different frame than the final diagnostic, + /// supply a message that will be used to construct an extra hint on the + /// returns from all the calls on the stack from this event to the final + /// diagnostic. + // FIXME: Allow shared_ptr keys in DenseMap? + std::map> + StackHints; - /// Create a BugReport with a custom uniqueing location. +public: + PathSensitiveBugReport(const BugType &bt, StringRef desc, + const ExplodedNode *errorNode) + : BugReport(Kind::PathSensitive, bt, desc), ErrorNode(errorNode), + ErrorNodeRange(getStmt() ? getStmt()->getSourceRange() + : SourceRange()) {} + + PathSensitiveBugReport(const BugType &bt, StringRef shortDesc, StringRef desc, + const ExplodedNode *errorNode) + : BugReport(Kind::PathSensitive, bt, shortDesc, desc), + ErrorNode(errorNode), + ErrorNodeRange(getStmt() ? getStmt()->getSourceRange() + : SourceRange()) {} + + /// Create a PathSensitiveBugReport with a custom uniqueing location. /// /// The reports that have the same report location, description, bug type, and /// ranges are uniqued - only one of the equivalent reports will be presented /// to the user. This method allows to rest the location which should be used /// for uniquing reports. For example, memory leaks checker, could set this to /// the allocation site, rather then the location where the bug is reported. - BugReport(BugType& bt, StringRef desc, const ExplodedNode *errornode, - PathDiagnosticLocation LocationToUnique, const Decl *DeclToUnique) - : BT(bt), Description(desc), UniqueingLocation(LocationToUnique), - UniqueingDecl(DeclToUnique), ErrorNode(errornode) {} - - virtual ~BugReport(); - - const BugType& getBugType() const { return BT; } - //BugType& getBugType() { return BT; } + PathSensitiveBugReport(BugType &bt, StringRef desc, + const ExplodedNode *errorNode, + PathDiagnosticLocation LocationToUnique, + const Decl *DeclToUnique) + : BugReport(Kind::PathSensitive, bt, desc), ErrorNode(errorNode), + ErrorNodeRange(getStmt() ? getStmt()->getSourceRange() : SourceRange()), + UniqueingLocation(LocationToUnique), UniqueingDecl(DeclToUnique) { + assert(errorNode); + } - /// True when the report has an execution path associated with it. - /// - /// A report is said to be path-sensitive if it was thrown against a - /// particular exploded node in the path-sensitive analysis graph. - /// Path-sensitive reports have their intermediate path diagnostics - /// auto-generated, perhaps with the help of checker-defined visitors, - /// and may contain extra notes. - /// Path-insensitive reports consist only of a single warning message - /// in a specific location, and perhaps extra notes. - /// Path-sensitive checkers are allowed to throw path-insensitive reports. - bool isPathSensitive() const { return ErrorNode != nullptr; } + static bool classof(const BugReport *R) { + return R->getKind() == Kind::PathSensitive; + } const ExplodedNode *getErrorNode() const { return ErrorNode; } - StringRef getDescription() const { return Description; } - - StringRef getShortDescription(bool UseFallback = true) const { - if (ShortDescription.empty() && UseFallback) - return Description; - return ShortDescription; - } - /// Indicates whether or not any path pruning should take place /// when generating a PathDiagnostic from this BugReport. bool shouldPrunePath() const { return !DoNotPrunePath; } @@ -223,15 +409,54 @@ public: /// Disable all path pruning when generating a PathDiagnostic. void disablePathPruning() { DoNotPrunePath = true; } - void markInteresting(SymbolRef sym); - void markInteresting(const MemRegion *R); - void markInteresting(SVal V); + /// Get the location on which the report should be uniqued. + PathDiagnosticLocation getUniqueingLocation() const override { + return UniqueingLocation; + } + + /// Get the declaration containing the uniqueing location. + const Decl *getUniqueingDecl() const override { + return UniqueingDecl; + } + + const Decl *getDeclWithIssue() const override; + + ArrayRef getRanges() const override; + + PathDiagnosticLocation getLocation() const override; + + /// Marks a symbol as interesting. Different kinds of interestingness will + /// be processed differently by visitors (e.g. if the tracking kind is + /// condition, will append "will be used as a condition" to the message). + void markInteresting(SymbolRef sym, bugreporter::TrackingKind TKind = + bugreporter::TrackingKind::Thorough); + + /// Marks a region as interesting. Different kinds of interestingness will + /// be processed differently by visitors (e.g. if the tracking kind is + /// condition, will append "will be used as a condition" to the message). + void markInteresting( + const MemRegion *R, + bugreporter::TrackingKind TKind = bugreporter::TrackingKind::Thorough); + + /// Marks a symbolic value as interesting. Different kinds of interestingness + /// will be processed differently by visitors (e.g. if the tracking kind is + /// condition, will append "will be used as a condition" to the message). + void markInteresting(SVal V, bugreporter::TrackingKind TKind = + bugreporter::TrackingKind::Thorough); void markInteresting(const LocationContext *LC); - bool isInteresting(SymbolRef sym); - bool isInteresting(const MemRegion *R); - bool isInteresting(SVal V); - bool isInteresting(const LocationContext *LC); + bool isInteresting(SymbolRef sym) const; + bool isInteresting(const MemRegion *R) const; + bool isInteresting(SVal V) const; + bool isInteresting(const LocationContext *LC) const; + + Optional + getInterestingnessKind(SymbolRef sym) const; + + Optional + getInterestingnessKind(const MemRegion *R) const; + + Optional getInterestingnessKind(SVal V) const; /// Returns whether or not this report should be considered valid. /// @@ -254,87 +479,10 @@ public: Invalidations.insert(std::make_pair(Tag, Data)); } - /// Return the canonical declaration, be it a method or class, where - /// this issue semantically occurred. - const Decl *getDeclWithIssue() const; - - /// Specifically set the Decl where an issue occurred. This isn't necessary - /// for BugReports that cover a path as it will be automatically inferred. - void setDeclWithIssue(const Decl *declWithIssue) { - DeclWithIssue = declWithIssue; - } - - /// Add new item to the list of additional notes that need to be attached to - /// this path-insensitive report. If you want to add extra notes to a - /// path-sensitive report, you need to use a BugReporterVisitor because it - /// allows you to specify where exactly in the auto-generated path diagnostic - /// the extra note should appear. - void addNote(StringRef Msg, const PathDiagnosticLocation &Pos, - ArrayRef Ranges) { - auto P = std::make_shared(Pos, Msg); - - for (const auto &R : Ranges) - P->addRange(R); - - Notes.push_back(std::move(P)); - } - - // FIXME: Instead of making an override, we could have default-initialized - // Ranges with {}, however it crashes the MSVC 2013 compiler. - void addNote(StringRef Msg, const PathDiagnosticLocation &Pos) { - std::vector Ranges; - addNote(Msg, Pos, Ranges); - } - - virtual const NoteList &getNotes() { - return Notes; - } - - /// This allows for addition of meta data to the diagnostic. - /// - /// Currently, only the HTMLDiagnosticClient knows how to display it. - void addExtraText(StringRef S) { - ExtraText.push_back(S); - } - - virtual const ExtraTextList &getExtraText() { - return ExtraText; - } - - /// Return the "definitive" location of the reported bug. - /// - /// While a bug can span an entire path, usually there is a specific - /// location that can be used to identify where the key issue occurred. - /// This location is used by clients rendering diagnostics. - virtual PathDiagnosticLocation getLocation(const SourceManager &SM) const; - - /// Get the location on which the report should be uniqued. - PathDiagnosticLocation getUniqueingLocation() const { - return UniqueingLocation; - } - - /// Get the declaration containing the uniqueing location. - const Decl *getUniqueingDecl() const { - return UniqueingDecl; - } - - const Stmt *getStmt() const; - - /// Add a range to a bug report. - /// - /// Ranges are used to highlight regions of interest in the source code. - /// They should be at the same source code line as the BugReport location. - /// By default, the source range of the statement corresponding to the error - /// node will be used; add a single invalid range to specify absence of - /// ranges. - void addRange(SourceRange R) { - assert((R.isValid() || Ranges.empty()) && "Invalid range can only be used " - "to specify that the report does not have a range."); - Ranges.push_back(R); - } - - /// Get the SourceRanges associated with the report. - virtual llvm::iterator_range getRanges(); + /// Profile to identify equivalent bug reports for error report coalescing. + /// Reports are uniqued to ensure that we do not emit multiple diagnostics + /// for each bug. + void Profile(llvm::FoldingSetNodeID &hash) const override; /// Add custom or predefined bug report visitors to this report. /// @@ -351,6 +499,7 @@ public: /// Iterators through the custom diagnostic visitors. visitor_iterator visitor_begin() { return Callbacks.begin(); } visitor_iterator visitor_end() { return Callbacks.end(); } + visitor_range visitors() { return {visitor_begin(), visitor_end()}; } /// Notes that the condition of the CFGBlock associated with \p Cond is /// being tracked. @@ -359,10 +508,25 @@ public: return TrackedConditions.insert(Cond).second; } - /// Profile to identify equivalent bug reports for error report coalescing. - /// Reports are uniqued to ensure that we do not emit multiple diagnostics - /// for each bug. - virtual void Profile(llvm::FoldingSetNodeID& hash) const; + void addCallStackHint(PathDiagnosticPieceRef Piece, + std::unique_ptr StackHint) { + StackHints[Piece] = std::move(StackHint); + } + + bool hasCallStackHint(PathDiagnosticPieceRef Piece) const { + return StackHints.count(Piece) > 0; + } + + /// Produce the hint for the given node. The node contains + /// information about the call for which the diagnostic can be generated. + std::string + getCallStackMessage(PathDiagnosticPieceRef Piece, + const ExplodedNode *N) const { + auto I = StackHints.find(Piece); + if (I != StackHints.end()) + return I->second->getMessage(N); + return ""; + } }; //===----------------------------------------------------------------------===// @@ -373,29 +537,21 @@ class BugReportEquivClass : public llvm::FoldingSetNode { friend class BugReporter; /// List of *owned* BugReport objects. - llvm::ilist Reports; + llvm::SmallVector, 4> Reports; - void AddReport(std::unique_ptr R) { - Reports.push_back(R.release()); + void AddReport(std::unique_ptr &&R) { + Reports.push_back(std::move(R)); } public: BugReportEquivClass(std::unique_ptr R) { AddReport(std::move(R)); } - ~BugReportEquivClass(); + + ArrayRef> getReports() const { return Reports; } void Profile(llvm::FoldingSetNodeID& ID) const { assert(!Reports.empty()); - Reports.front().Profile(ID); + Reports.front()->Profile(ID); } - - using iterator = llvm::ilist::iterator; - using const_iterator = llvm::ilist::const_iterator; - - iterator begin() { return Reports.begin(); } - iterator end() { return Reports.end(); } - - const_iterator begin() const { return Reports.begin(); } - const_iterator end() const { return Reports.end(); } }; //===----------------------------------------------------------------------===// @@ -404,9 +560,8 @@ public: class BugReporterData { public: - virtual ~BugReporterData(); + virtual ~BugReporterData() = default; - virtual DiagnosticsEngine& getDiagnostic() = 0; virtual ArrayRef getPathDiagnosticConsumers() = 0; virtual ASTContext &getASTContext() = 0; virtual SourceManager &getSourceManager() = 0; @@ -419,60 +574,29 @@ public: /// /// The base class is used for generating path-insensitive class BugReporter { -public: - enum Kind { BaseBRKind, GRBugReporterKind }; - private: - using BugTypesTy = llvm::ImmutableSet; - - BugTypesTy::Factory F; - BugTypesTy BugTypes; - - const Kind kind; BugReporterData& D; /// Generate and flush the diagnostics for the given bug report. void FlushReport(BugReportEquivClass& EQ); - /// Generate the diagnostics for the given bug report. - std::unique_ptr - generateDiagnosticForConsumerMap(BugReport *exampleReport, - ArrayRef consumers, - ArrayRef bugReports); - /// The set of bug reports tracked by the BugReporter. llvm::FoldingSet EQClasses; /// A vector of BugReports for tracking the allocated pointers and cleanup. std::vector EQClassesVector; -protected: - BugReporter(BugReporterData& d, Kind k) - : BugTypes(F.getEmptySet()), kind(k), D(d) {} - public: - BugReporter(BugReporterData& d) - : BugTypes(F.getEmptySet()), kind(BaseBRKind), D(d) {} + BugReporter(BugReporterData &d) : D(d) {} virtual ~BugReporter(); /// Generate and flush diagnostics for all bug reports. void FlushReports(); - Kind getKind() const { return kind; } - - DiagnosticsEngine& getDiagnostic() { - return D.getDiagnostic(); - } - ArrayRef getPathDiagnosticConsumers() { return D.getPathDiagnosticConsumers(); } - /// Iterator over the set of BugTypes tracked by the BugReporter. - using iterator = BugTypesTy::iterator; - iterator begin() { return BugTypes.begin(); } - iterator end() { return BugTypes.end(); } - /// Iterator over the set of BugReports tracked by the BugReporter. using EQClasses_iterator = llvm::FoldingSet::iterator; EQClasses_iterator EQClasses_begin() { return EQClasses.begin(); } @@ -480,126 +604,116 @@ public: ASTContext &getContext() { return D.getASTContext(); } - SourceManager &getSourceManager() { return D.getSourceManager(); } - - AnalyzerOptions &getAnalyzerOptions() { return D.getAnalyzerOptions(); } - - virtual std::unique_ptr - generatePathDiagnostics(ArrayRef consumers, - ArrayRef &bugReports) { - return {}; - } + const SourceManager &getSourceManager() { return D.getSourceManager(); } - void Register(const BugType *BT); + const AnalyzerOptions &getAnalyzerOptions() { return D.getAnalyzerOptions(); } /// Add the given report to the set of reports tracked by BugReporter. /// /// The reports are usually generated by the checkers. Further, they are /// folded based on the profile value, which is done to coalesce similar /// reports. - void emitReport(std::unique_ptr R); + virtual void emitReport(std::unique_ptr R); void EmitBasicReport(const Decl *DeclWithIssue, const CheckerBase *Checker, StringRef BugName, StringRef BugCategory, StringRef BugStr, PathDiagnosticLocation Loc, - ArrayRef Ranges = None); + ArrayRef Ranges = None, + ArrayRef Fixits = None); - void EmitBasicReport(const Decl *DeclWithIssue, CheckName CheckName, + void EmitBasicReport(const Decl *DeclWithIssue, CheckerNameRef CheckerName, StringRef BugName, StringRef BugCategory, StringRef BugStr, PathDiagnosticLocation Loc, - ArrayRef Ranges = None); + ArrayRef Ranges = None, + ArrayRef Fixits = None); private: llvm::StringMap StrBugTypes; /// Returns a BugType that is associated with the given name and /// category. - BugType *getBugTypeForName(CheckName CheckName, StringRef name, + BugType *getBugTypeForName(CheckerNameRef CheckerName, StringRef name, StringRef category); + + virtual BugReport * + findReportInEquivalenceClass(BugReportEquivClass &eqClass, + SmallVectorImpl &bugReports) { + return eqClass.getReports()[0].get(); + } + +protected: + /// Generate the diagnostics for the given bug report. + virtual std::unique_ptr + generateDiagnosticForConsumerMap(BugReport *exampleReport, + ArrayRef consumers, + ArrayRef bugReports); }; /// GRBugReporter is used for generating path-sensitive reports. -class GRBugReporter : public BugReporter { +class PathSensitiveBugReporter final : public BugReporter { ExprEngine& Eng; -public: - GRBugReporter(BugReporterData& d, ExprEngine& eng) - : BugReporter(d, GRBugReporterKind), Eng(eng) {} + BugReport *findReportInEquivalenceClass( + BugReportEquivClass &eqClass, + SmallVectorImpl &bugReports) override; - ~GRBugReporter() override; + /// Generate the diagnostics for the given bug report. + std::unique_ptr + generateDiagnosticForConsumerMap(BugReport *exampleReport, + ArrayRef consumers, + ArrayRef bugReports) override; +public: + PathSensitiveBugReporter(BugReporterData& d, ExprEngine& eng) + : BugReporter(d), Eng(eng) {} /// getGraph - Get the exploded graph created by the analysis engine /// for the analyzed method or function. - ExplodedGraph &getGraph(); + const ExplodedGraph &getGraph() const; /// getStateManager - Return the state manager used by the analysis /// engine. - ProgramStateManager &getStateManager(); + ProgramStateManager &getStateManager() const; /// \p bugReports A set of bug reports within a *single* equivalence class /// /// \return A mapping from consumers to the corresponding diagnostics. /// Iterates through the bug reports within a single equivalence class, /// stops at a first non-invalidated report. - std::unique_ptr - generatePathDiagnostics(ArrayRef consumers, - ArrayRef &bugReports) override; + std::unique_ptr generatePathDiagnostics( + ArrayRef consumers, + ArrayRef &bugReports); - /// classof - Used by isa<>, cast<>, and dyn_cast<>. - static bool classof(const BugReporter* R) { - return R->getKind() == GRBugReporterKind; - } + void emitReport(std::unique_ptr R) override; }; -class NodeMapClosure : public BugReport::NodeResolver { - InterExplodedGraphMap &M; - -public: - NodeMapClosure(InterExplodedGraphMap &m) : M(m) {} - - const ExplodedNode *getOriginalNode(const ExplodedNode *N) override { - return M.lookup(N); - } -}; - class BugReporterContext { - GRBugReporter &BR; - NodeMapClosure NMC; + PathSensitiveBugReporter &BR; virtual void anchor(); public: - BugReporterContext(GRBugReporter &br, InterExplodedGraphMap &Backmap) - : BR(br), NMC(Backmap) {} + BugReporterContext(PathSensitiveBugReporter &br) : BR(br) {} virtual ~BugReporterContext() = default; - GRBugReporter& getBugReporter() { return BR; } + PathSensitiveBugReporter& getBugReporter() { return BR; } - ExplodedGraph &getGraph() { return BR.getGraph(); } - - ProgramStateManager& getStateManager() { + ProgramStateManager& getStateManager() const { return BR.getStateManager(); } - SValBuilder &getSValBuilder() { - return getStateManager().getSValBuilder(); - } - - ASTContext &getASTContext() { + ASTContext &getASTContext() const { return BR.getContext(); } - SourceManager& getSourceManager() { + const SourceManager& getSourceManager() const { return BR.getSourceManager(); } - AnalyzerOptions &getAnalyzerOptions() { + const AnalyzerOptions &getAnalyzerOptions() const { return BR.getAnalyzerOptions(); } - - NodeMapClosure& getNodeResolver() { return NMC; } }; @@ -648,7 +762,7 @@ public: public: const NoteTag *makeNoteTag(Callback &&Cb, bool IsPrunable = false) { - // We cannot use make_unique because we cannot access the private + // We cannot use std::make_unique because we cannot access the private // constructor from inside it. std::unique_ptr T(new NoteTag(std::move(Cb), IsPrunable)); Tags.push_back(std::move(T)); diff --git a/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h b/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h index ef5d327d39da5..de0ee5de81b53 100644 --- a/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h +++ b/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h @@ -33,11 +33,12 @@ class Stmt; namespace ento { -class BugReport; +class PathSensitiveBugReport; class BugReporterContext; class ExplodedNode; class MemRegion; class PathDiagnosticPiece; +using PathDiagnosticPieceRef = std::shared_ptr; /// BugReporterVisitors are used to add custom diagnostics along a path. class BugReporterVisitor : public llvm::FoldingSetNode { @@ -57,32 +58,68 @@ public: /// /// The last parameter can be used to register a new visitor with the given /// BugReport while processing a node. - virtual std::shared_ptr - VisitNode(const ExplodedNode *Succ, - BugReporterContext &BRC, BugReport &BR) = 0; + virtual PathDiagnosticPieceRef VisitNode(const ExplodedNode *Succ, + BugReporterContext &BRC, + PathSensitiveBugReport &BR) = 0; /// Last function called on the visitor, no further calls to VisitNode /// would follow. virtual void finalizeVisitor(BugReporterContext &BRC, const ExplodedNode *EndPathNode, - BugReport &BR); + PathSensitiveBugReport &BR); /// Provide custom definition for the final diagnostic piece on the /// path - the piece, which is displayed before the path is expanded. /// /// NOTE that this function can be implemented on at most one used visitor, /// and otherwise it crahes at runtime. - virtual std::shared_ptr - getEndPath(BugReporterContext &BRC, const ExplodedNode *N, BugReport &BR); + virtual PathDiagnosticPieceRef getEndPath(BugReporterContext &BRC, + const ExplodedNode *N, + PathSensitiveBugReport &BR); virtual void Profile(llvm::FoldingSetNodeID &ID) const = 0; /// Generates the default final diagnostic piece. - static std::shared_ptr - getDefaultEndPath(BugReporterContext &BRC, const ExplodedNode *N, - BugReport &BR); + static PathDiagnosticPieceRef + getDefaultEndPath(const BugReporterContext &BRC, const ExplodedNode *N, + const PathSensitiveBugReport &BR); }; +namespace bugreporter { + +/// Specifies the type of tracking for an expression. +enum class TrackingKind { + /// Default tracking kind -- specifies that as much information should be + /// gathered about the tracked expression value as possible. + Thorough, + /// Specifies that a more moderate tracking should be used for the expression + /// value. This will essentially make sure that functions relevant to the it + /// aren't pruned, but otherwise relies on the user reading the code or + /// following the arrows. + Condition +}; + +/// Attempts to add visitors to track expression value back to its point of +/// origin. +/// +/// \param N A node "downstream" from the evaluation of the statement. +/// \param E The expression value which we are tracking +/// \param R The bug report to which visitors should be attached. +/// \param EnableNullFPSuppression Whether we should employ false positive +/// suppression (inlined defensive checks, returned null). +/// +/// \return Whether or not the function was able to add visitors for this +/// statement. Note that returning \c true does not actually imply +/// that any visitors were added. +bool trackExpressionValue(const ExplodedNode *N, const Expr *E, + PathSensitiveBugReport &R, + TrackingKind TKind = TrackingKind::Thorough, + bool EnableNullFPSuppression = true); + +const Expr *getDerefExpr(const Stmt *S); + +} // namespace bugreporter + /// Finds last store into the given region, /// which is different from a given symbolic value. class FindLastStoreBRVisitor final : public BugReporterVisitor { @@ -94,21 +131,34 @@ class FindLastStoreBRVisitor final : public BugReporterVisitor { /// bug, we are going to employ false positive suppression. bool EnableNullFPSuppression; -public: - /// Creates a visitor for every VarDecl inside a Stmt and registers it with - /// the BugReport. - static void registerStatementVarDecls(BugReport &BR, const Stmt *S, - bool EnableNullFPSuppression); + using TrackingKind = bugreporter::TrackingKind; + TrackingKind TKind; + const StackFrameContext *OriginSFC; +public: + /// \param V We're searching for the store where \c R received this value. + /// \param R The region we're tracking. + /// \param TKind May limit the amount of notes added to the bug report. + /// \param OriginSFC Only adds notes when the last store happened in a + /// different stackframe to this one. Disregarded if the tracking kind + /// is thorough. + /// This is useful, because for non-tracked regions, notes about + /// changes to its value in a nested stackframe could be pruned, and + /// this visitor can prevent that without polluting the bugpath too + /// much. FindLastStoreBRVisitor(KnownSVal V, const MemRegion *R, - bool InEnableNullFPSuppression) - : R(R), V(V), EnableNullFPSuppression(InEnableNullFPSuppression) {} + bool InEnableNullFPSuppression, TrackingKind TKind, + const StackFrameContext *OriginSFC = nullptr) + : R(R), V(V), EnableNullFPSuppression(InEnableNullFPSuppression), + TKind(TKind), OriginSFC(OriginSFC) { + assert(R); + } void Profile(llvm::FoldingSetNodeID &ID) const override; - std::shared_ptr VisitNode(const ExplodedNode *N, - BugReporterContext &BRC, - BugReport &BR) override; + PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + PathSensitiveBugReport &BR) override; }; class TrackConstraintBRVisitor final : public BugReporterVisitor { @@ -132,9 +182,9 @@ public: /// to make all PathDiagnosticPieces created by this visitor. static const char *getTag(); - std::shared_ptr VisitNode(const ExplodedNode *N, - BugReporterContext &BRC, - BugReport &BR) override; + PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + PathSensitiveBugReport &BR) override; private: /// Checks if the constraint is valid in the current state. @@ -150,9 +200,9 @@ public: ID.AddPointer(&x); } - std::shared_ptr VisitNode(const ExplodedNode *N, - BugReporterContext &BRC, - BugReport &BR) override; + PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + PathSensitiveBugReport &BR) override; /// If the statement is a message send expression with nil receiver, returns /// the receiver expression. Returns NULL otherwise. @@ -162,8 +212,10 @@ public: /// Visitor that tries to report interesting diagnostics from conditions. class ConditionBRVisitor final : public BugReporterVisitor { // FIXME: constexpr initialization isn't supported by MSVC2013. - static const char *const GenericTrueMessage; - static const char *const GenericFalseMessage; + constexpr static llvm::StringLiteral GenericTrueMessage = + "Assuming the condition is true"; + constexpr static llvm::StringLiteral GenericFalseMessage = + "Assuming the condition is false"; public: void Profile(llvm::FoldingSetNodeID &ID) const override { @@ -175,41 +227,44 @@ public: /// to make all PathDiagnosticPieces created by this visitor. static const char *getTag(); - std::shared_ptr VisitNode(const ExplodedNode *N, - BugReporterContext &BRC, - BugReport &BR) override; + PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + PathSensitiveBugReport &BR) override; - std::shared_ptr VisitNodeImpl(const ExplodedNode *N, - BugReporterContext &BRC, - BugReport &BR); + PathDiagnosticPieceRef VisitNodeImpl(const ExplodedNode *N, + BugReporterContext &BRC, + PathSensitiveBugReport &BR); - std::shared_ptr + PathDiagnosticPieceRef VisitTerminator(const Stmt *Term, const ExplodedNode *N, - const CFGBlock *srcBlk, const CFGBlock *dstBlk, BugReport &R, - BugReporterContext &BRC); + const CFGBlock *SrcBlk, const CFGBlock *DstBlk, + PathSensitiveBugReport &R, BugReporterContext &BRC); - std::shared_ptr - VisitTrueTest(const Expr *Cond, BugReporterContext &BRC, BugReport &R, - const ExplodedNode *N, bool TookTrue); + PathDiagnosticPieceRef VisitTrueTest(const Expr *Cond, + BugReporterContext &BRC, + PathSensitiveBugReport &R, + const ExplodedNode *N, bool TookTrue); - std::shared_ptr - VisitTrueTest(const Expr *Cond, const DeclRefExpr *DR, - BugReporterContext &BRC, BugReport &R, const ExplodedNode *N, - bool TookTrue, bool IsAssuming); + PathDiagnosticPieceRef VisitTrueTest(const Expr *Cond, const DeclRefExpr *DR, + BugReporterContext &BRC, + PathSensitiveBugReport &R, + const ExplodedNode *N, bool TookTrue, + bool IsAssuming); - std::shared_ptr + PathDiagnosticPieceRef VisitTrueTest(const Expr *Cond, const BinaryOperator *BExpr, - BugReporterContext &BRC, BugReport &R, const ExplodedNode *N, - bool TookTrue, bool IsAssuming); + BugReporterContext &BRC, PathSensitiveBugReport &R, + const ExplodedNode *N, bool TookTrue, bool IsAssuming); - std::shared_ptr - VisitTrueTest(const Expr *Cond, const MemberExpr *ME, BugReporterContext &BRC, - BugReport &R, const ExplodedNode *N, bool TookTrue, - bool IsAssuming); + PathDiagnosticPieceRef VisitTrueTest(const Expr *Cond, const MemberExpr *ME, + BugReporterContext &BRC, + PathSensitiveBugReport &R, + const ExplodedNode *N, bool TookTrue, + bool IsAssuming); - std::shared_ptr + PathDiagnosticPieceRef VisitConditionVariable(StringRef LhsString, const Expr *CondVarExpr, - BugReporterContext &BRC, BugReport &R, + BugReporterContext &BRC, PathSensitiveBugReport &R, const ExplodedNode *N, bool TookTrue); /// Tries to print the value of the given expression. @@ -228,7 +283,7 @@ public: const Expr *ParentEx, raw_ostream &Out, BugReporterContext &BRC, - BugReport &R, + PathSensitiveBugReport &R, const ExplodedNode *N, Optional &prunable, bool IsSameFieldName); @@ -251,14 +306,13 @@ public: ID.AddPointer(getTag()); } - std::shared_ptr VisitNode(const ExplodedNode *, - BugReporterContext &, - BugReport &) override { + PathDiagnosticPieceRef VisitNode(const ExplodedNode *, BugReporterContext &, + PathSensitiveBugReport &) override { return nullptr; } void finalizeVisitor(BugReporterContext &BRC, const ExplodedNode *N, - BugReport &BR) override; + PathSensitiveBugReport &BR) override; }; /// When a region containing undefined value or '0' value is passed @@ -279,9 +333,9 @@ public: ID.AddPointer(R); } - std::shared_ptr VisitNode(const ExplodedNode *N, - BugReporterContext &BRC, - BugReport &BR) override; + PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + PathSensitiveBugReport &BR) override; }; class SuppressInlineDefensiveChecksVisitor final : public BugReporterVisitor { @@ -308,9 +362,9 @@ public: /// to make all PathDiagnosticPieces created by this visitor. static const char *getTag(); - std::shared_ptr VisitNode(const ExplodedNode *Succ, - BugReporterContext &BRC, - BugReport &BR) override; + PathDiagnosticPieceRef VisitNode(const ExplodedNode *Succ, + BugReporterContext &BRC, + PathSensitiveBugReport &BR) override; }; /// The bug visitor will walk all the nodes in a path and collect all the @@ -326,12 +380,12 @@ public: void Profile(llvm::FoldingSetNodeID &ID) const override; - std::shared_ptr VisitNode(const ExplodedNode *N, - BugReporterContext &BRC, - BugReport &BR) override; + PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + PathSensitiveBugReport &BR) override; void finalizeVisitor(BugReporterContext &BRC, const ExplodedNode *EndPathNode, - BugReport &BR) override; + PathSensitiveBugReport &BR) override; }; @@ -340,32 +394,11 @@ class TagVisitor : public BugReporterVisitor { public: void Profile(llvm::FoldingSetNodeID &ID) const override; - std::shared_ptr VisitNode(const ExplodedNode *N, - BugReporterContext &BRC, - BugReport &R) override; + PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + PathSensitiveBugReport &R) override; }; -namespace bugreporter { - -/// Attempts to add visitors to track expression value back to its point of -/// origin. -/// -/// \param N A node "downstream" from the evaluation of the statement. -/// \param E The expression value which we are tracking -/// \param R The bug report to which visitors should be attached. -/// \param EnableNullFPSuppression Whether we should employ false positive -/// suppression (inlined defensive checks, returned null). -/// -/// \return Whether or not the function was able to add visitors for this -/// statement. Note that returning \c true does not actually imply -/// that any visitors were added. -bool trackExpressionValue(const ExplodedNode *N, const Expr *E, BugReport &R, - bool EnableNullFPSuppression = true); - -const Expr *getDerefExpr(const Stmt *S); - -} // namespace bugreporter - } // namespace ento } // namespace clang diff --git a/include/clang/StaticAnalyzer/Core/BugReporter/BugType.h b/include/clang/StaticAnalyzer/Core/BugReporter/BugType.h index 324b5312e790d..237053df7e442 100644 --- a/include/clang/StaticAnalyzer/Core/BugReporter/BugType.h +++ b/include/clang/StaticAnalyzer/Core/BugReporter/BugType.h @@ -28,8 +28,8 @@ class ExprEngine; class BugType { private: - const CheckName Check; - const std::string Name; + const CheckerNameRef CheckerName; + const std::string Description; const std::string Category; const CheckerBase *Checker; bool SuppressOnSink; @@ -37,28 +37,27 @@ private: virtual void anchor(); public: - BugType(CheckName Check, StringRef Name, StringRef Cat, - bool SuppressOnSink=false) - : Check(Check), Name(Name), Category(Cat), Checker(nullptr), - SuppressOnSink(SuppressOnSink) {} + BugType(CheckerNameRef CheckerName, StringRef Name, StringRef Cat, + bool SuppressOnSink = false) + : CheckerName(CheckerName), Description(Name), Category(Cat), + Checker(nullptr), SuppressOnSink(SuppressOnSink) {} BugType(const CheckerBase *Checker, StringRef Name, StringRef Cat, - bool SuppressOnSink=false) - : Check(Checker->getCheckName()), Name(Name), Category(Cat), - Checker(Checker), SuppressOnSink(SuppressOnSink) {} + bool SuppressOnSink = false) + : CheckerName(Checker->getCheckerName()), Description(Name), + Category(Cat), Checker(Checker), SuppressOnSink(SuppressOnSink) {} virtual ~BugType() = default; - StringRef getName() const { return Name; } + StringRef getDescription() const { return Description; } StringRef getCategory() const { return Category; } - StringRef getCheckName() const { - // FIXME: This is a workaround to ensure that the correct check name is used - // The check names are set after the constructors are run. + StringRef getCheckerName() const { + // FIXME: This is a workaround to ensure that the correct checerk name is + // used. The checker names are set after the constructors are run. // In case the BugType object is initialized in the checker's ctor - // the Check field will be empty. To circumvent this problem we use + // the CheckerName field will be empty. To circumvent this problem we use // CheckerBase whenever it is possible. - StringRef CheckName = - Checker ? Checker->getCheckName().getName() : Check.getName(); - assert(!CheckName.empty() && "Check name is not set properly."); - return CheckName; + StringRef Ret = Checker ? Checker->getCheckerName() : CheckerName; + assert(!Ret.empty() && "Checker name is not set properly."); + return Ret; } /// isSuppressOnSink - Returns true if bug reports associated with this bug @@ -71,8 +70,9 @@ class BuiltinBug : public BugType { const std::string desc; void anchor() override; public: - BuiltinBug(class CheckName check, const char *name, const char *description) - : BugType(check, name, categories::LogicError), desc(description) {} + BuiltinBug(class CheckerNameRef checker, const char *name, + const char *description) + : BugType(checker, name, categories::LogicError), desc(description) {} BuiltinBug(const CheckerBase *checker, const char *name, const char *description) diff --git a/include/clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h b/include/clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h index 85526eb49f0cb..22c1a7dd98ccb 100644 --- a/include/clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h +++ b/include/clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h @@ -18,6 +18,7 @@ namespace clang { extern const char * const MemoryRefCount; extern const char * const MemoryError; extern const char * const UnixAPI; + extern const char * const CXXObjectLifecycle; } } } diff --git a/include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h b/include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h deleted file mode 100644 index 5230742a4aa43..0000000000000 --- a/include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h +++ /dev/null @@ -1,923 +0,0 @@ -//===- PathDiagnostic.h - Path-Specific Diagnostic Handling -----*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// This file defines the PathDiagnostic-related interfaces. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_CLANG_STATICANALYZER_CORE_BUGREPORTER_PATHDIAGNOSTIC_H -#define LLVM_CLANG_STATICANALYZER_CORE_BUGREPORTER_PATHDIAGNOSTIC_H - -#include "clang/AST/Stmt.h" -#include "clang/Analysis/AnalysisDeclContext.h" -#include "clang/Basic/LLVM.h" -#include "clang/Basic/SourceLocation.h" -#include "llvm/ADT/ArrayRef.h" -#include "llvm/ADT/FoldingSet.h" -#include "llvm/ADT/Optional.h" -#include "llvm/ADT/PointerUnion.h" -#include "llvm/ADT/SmallVector.h" -#include "llvm/ADT/StringRef.h" -#include "llvm/Support/Allocator.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace clang { - -class AnalysisDeclContext; -class BinaryOperator; -class CallEnter; -class CallExitEnd; -class CallExpr; -class ConditionalOperator; -class Decl; -class Expr; -class LocationContext; -class MemberExpr; -class ProgramPoint; -class SourceManager; - -namespace ento { - -class ExplodedNode; -class SymExpr; - -using SymbolRef = const SymExpr *; - -//===----------------------------------------------------------------------===// -// High-level interface for handlers of path-sensitive diagnostics. -//===----------------------------------------------------------------------===// - -class PathDiagnostic; - -class PathDiagnosticConsumer { -public: - class PDFileEntry : public llvm::FoldingSetNode { - public: - PDFileEntry(llvm::FoldingSetNodeID &NodeID) : NodeID(NodeID) {} - - using ConsumerFiles = std::vector>; - - /// A vector of pairs. - ConsumerFiles files; - - /// A precomputed hash tag used for uniquing PDFileEntry objects. - const llvm::FoldingSetNodeID NodeID; - - /// Used for profiling in the FoldingSet. - void Profile(llvm::FoldingSetNodeID &ID) { ID = NodeID; } - }; - - class FilesMade { - llvm::BumpPtrAllocator Alloc; - llvm::FoldingSet Set; - - public: - ~FilesMade(); - - bool empty() const { return Set.empty(); } - - void addDiagnostic(const PathDiagnostic &PD, - StringRef ConsumerName, - StringRef fileName); - - PDFileEntry::ConsumerFiles *getFiles(const PathDiagnostic &PD); - }; - -private: - virtual void anchor(); - -public: - PathDiagnosticConsumer() = default; - virtual ~PathDiagnosticConsumer(); - - void FlushDiagnostics(FilesMade *FilesMade); - - virtual void FlushDiagnosticsImpl(std::vector &Diags, - FilesMade *filesMade) = 0; - - virtual StringRef getName() const = 0; - - void HandlePathDiagnostic(std::unique_ptr D); - - enum PathGenerationScheme { - /// Only runs visitors, no output generated. - None, - - /// Used for HTML, SARIF, and text output. - Minimal, - - /// Used for plist output, used for "arrows" generation. - Extensive, - }; - - virtual PathGenerationScheme getGenerationScheme() const { return Minimal; } - virtual bool supportsLogicalOpControlFlow() const { return false; } - - /// Return true if the PathDiagnosticConsumer supports individual - /// PathDiagnostics that span multiple files. - virtual bool supportsCrossFileDiagnostics() const { return false; } - -protected: - bool flushed = false; - llvm::FoldingSet Diags; -}; - -//===----------------------------------------------------------------------===// -// Path-sensitive diagnostics. -//===----------------------------------------------------------------------===// - -class PathDiagnosticRange : public SourceRange { -public: - bool isPoint = false; - - PathDiagnosticRange(SourceRange R, bool isP = false) - : SourceRange(R), isPoint(isP) {} - PathDiagnosticRange() = default; -}; - -using LocationOrAnalysisDeclContext = - llvm::PointerUnion; - -class PathDiagnosticLocation { -private: - enum Kind { RangeK, SingleLocK, StmtK, DeclK } K = SingleLocK; - - const Stmt *S = nullptr; - const Decl *D = nullptr; - const SourceManager *SM = nullptr; - FullSourceLoc Loc; - PathDiagnosticRange Range; - - PathDiagnosticLocation(SourceLocation L, const SourceManager &sm, Kind kind) - : K(kind), SM(&sm), Loc(genLocation(L)), Range(genRange()) {} - - FullSourceLoc genLocation( - SourceLocation L = SourceLocation(), - LocationOrAnalysisDeclContext LAC = (AnalysisDeclContext *)nullptr) const; - - PathDiagnosticRange genRange( - LocationOrAnalysisDeclContext LAC = (AnalysisDeclContext *)nullptr) const; - -public: - /// Create an invalid location. - PathDiagnosticLocation() = default; - - /// Create a location corresponding to the given statement. - PathDiagnosticLocation(const Stmt *s, const SourceManager &sm, - LocationOrAnalysisDeclContext lac) - : K(s->getBeginLoc().isValid() ? StmtK : SingleLocK), - S(K == StmtK ? s : nullptr), SM(&sm), - Loc(genLocation(SourceLocation(), lac)), Range(genRange(lac)) { - assert(K == SingleLocK || S); - assert(K == SingleLocK || Loc.isValid()); - assert(K == SingleLocK || Range.isValid()); - } - - /// Create a location corresponding to the given declaration. - PathDiagnosticLocation(const Decl *d, const SourceManager &sm) - : K(DeclK), D(d), SM(&sm), Loc(genLocation()), Range(genRange()) { - assert(D); - assert(Loc.isValid()); - assert(Range.isValid()); - } - - /// Create a location at an explicit offset in the source. - /// - /// This should only be used if there are no more appropriate constructors. - PathDiagnosticLocation(SourceLocation loc, const SourceManager &sm) - : SM(&sm), Loc(loc, sm), Range(genRange()) { - assert(Loc.isValid()); - assert(Range.isValid()); - } - - /// Create a location corresponding to the given declaration. - static PathDiagnosticLocation create(const Decl *D, - const SourceManager &SM) { - return PathDiagnosticLocation(D, SM); - } - - /// Create a location for the beginning of the declaration. - static PathDiagnosticLocation createBegin(const Decl *D, - const SourceManager &SM); - - /// Create a location for the beginning of the declaration. - /// The third argument is ignored, useful for generic treatment - /// of statements and declarations. - static PathDiagnosticLocation - createBegin(const Decl *D, const SourceManager &SM, - const LocationOrAnalysisDeclContext LAC) { - return createBegin(D, SM); - } - - /// Create a location for the beginning of the statement. - static PathDiagnosticLocation createBegin(const Stmt *S, - const SourceManager &SM, - const LocationOrAnalysisDeclContext LAC); - - /// Create a location for the end of the statement. - /// - /// If the statement is a CompoundStatement, the location will point to the - /// closing brace instead of following it. - static PathDiagnosticLocation createEnd(const Stmt *S, - const SourceManager &SM, - const LocationOrAnalysisDeclContext LAC); - - /// Create the location for the operator of the binary expression. - /// Assumes the statement has a valid location. - static PathDiagnosticLocation createOperatorLoc(const BinaryOperator *BO, - const SourceManager &SM); - static PathDiagnosticLocation createConditionalColonLoc( - const ConditionalOperator *CO, - const SourceManager &SM); - - /// For member expressions, return the location of the '.' or '->'. - /// Assumes the statement has a valid location. - static PathDiagnosticLocation createMemberLoc(const MemberExpr *ME, - const SourceManager &SM); - - /// Create a location for the beginning of the compound statement. - /// Assumes the statement has a valid location. - static PathDiagnosticLocation createBeginBrace(const CompoundStmt *CS, - const SourceManager &SM); - - /// Create a location for the end of the compound statement. - /// Assumes the statement has a valid location. - static PathDiagnosticLocation createEndBrace(const CompoundStmt *CS, - const SourceManager &SM); - - /// Create a location for the beginning of the enclosing declaration body. - /// Defaults to the beginning of the first statement in the declaration body. - static PathDiagnosticLocation createDeclBegin(const LocationContext *LC, - const SourceManager &SM); - - /// Constructs a location for the end of the enclosing declaration body. - /// Defaults to the end of brace. - static PathDiagnosticLocation createDeclEnd(const LocationContext *LC, - const SourceManager &SM); - - /// Create a location corresponding to the given valid ExplodedNode. - static PathDiagnosticLocation create(const ProgramPoint &P, - const SourceManager &SMng); - - /// Create a location corresponding to the next valid ExplodedNode as end - /// of path location. - static PathDiagnosticLocation createEndOfPath(const ExplodedNode* N, - const SourceManager &SM); - - /// Convert the given location into a single kind location. - static PathDiagnosticLocation createSingleLocation( - const PathDiagnosticLocation &PDL); - - bool operator==(const PathDiagnosticLocation &X) const { - return K == X.K && Loc == X.Loc && Range == X.Range; - } - - bool operator!=(const PathDiagnosticLocation &X) const { - return !(*this == X); - } - - bool isValid() const { - return SM != nullptr; - } - - FullSourceLoc asLocation() const { - return Loc; - } - - PathDiagnosticRange asRange() const { - return Range; - } - - const Stmt *asStmt() const { assert(isValid()); return S; } - const Stmt *getStmtOrNull() const { - if (!isValid()) - return nullptr; - return asStmt(); - } - - const Decl *asDecl() const { assert(isValid()); return D; } - - bool hasRange() const { return K == StmtK || K == RangeK || K == DeclK; } - - bool hasValidLocation() const { return asLocation().isValid(); } - - void invalidate() { - *this = PathDiagnosticLocation(); - } - - void flatten(); - - const SourceManager& getManager() const { assert(isValid()); return *SM; } - - void Profile(llvm::FoldingSetNodeID &ID) const; - - void dump() const; - - /// Given an exploded node, retrieve the statement that should be used - /// for the diagnostic location. - static const Stmt *getStmt(const ExplodedNode *N); - - /// Retrieve the statement corresponding to the successor node. - static const Stmt *getNextStmt(const ExplodedNode *N); -}; - -class PathDiagnosticLocationPair { -private: - PathDiagnosticLocation Start, End; - -public: - PathDiagnosticLocationPair(const PathDiagnosticLocation &start, - const PathDiagnosticLocation &end) - : Start(start), End(end) {} - - 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(); - } - - void Profile(llvm::FoldingSetNodeID &ID) const { - Start.Profile(ID); - End.Profile(ID); - } -}; - -//===----------------------------------------------------------------------===// -// Path "pieces" for path-sensitive diagnostics. -//===----------------------------------------------------------------------===// - -class PathDiagnosticPiece: public llvm::FoldingSetNode { -public: - enum Kind { ControlFlow, Event, Macro, Call, Note, PopUp }; - enum DisplayHint { Above, Below }; - -private: - const std::string str; - const Kind kind; - const DisplayHint Hint; - - /// In the containing bug report, this piece is the last piece from - /// the main source file. - bool LastInMainSourceFile = false; - - /// A constant string that can be used to tag the PathDiagnosticPiece, - /// typically with the identification of the creator. The actual pointer - /// value is meant to be an identifier; the string itself is useful for - /// debugging. - StringRef Tag; - - std::vector ranges; - -protected: - PathDiagnosticPiece(StringRef s, Kind k, DisplayHint hint = Below); - PathDiagnosticPiece(Kind k, DisplayHint hint = Below); - -public: - PathDiagnosticPiece() = delete; - PathDiagnosticPiece(const PathDiagnosticPiece &) = delete; - PathDiagnosticPiece &operator=(const PathDiagnosticPiece &) = delete; - virtual ~PathDiagnosticPiece(); - - StringRef getString() const { return str; } - - /// Tag this PathDiagnosticPiece with the given C-string. - void setTag(const char *tag) { Tag = tag; } - - /// Return the opaque tag (if any) on the PathDiagnosticPiece. - const void *getTag() const { return Tag.data(); } - - /// Return the string representation of the tag. This is useful - /// for debugging. - StringRef getTagStr() const { return Tag; } - - /// getDisplayHint - Return a hint indicating where the diagnostic should - /// be displayed by the PathDiagnosticConsumer. - DisplayHint getDisplayHint() const { return Hint; } - - virtual PathDiagnosticLocation getLocation() const = 0; - virtual void flattenLocations() = 0; - - Kind getKind() const { return kind; } - - void addRange(SourceRange R) { - if (!R.isValid()) - return; - ranges.push_back(R); - } - - void addRange(SourceLocation B, SourceLocation E) { - if (!B.isValid() || !E.isValid()) - return; - ranges.push_back(SourceRange(B,E)); - } - - /// Return the SourceRanges associated with this PathDiagnosticPiece. - ArrayRef getRanges() const { return ranges; } - - virtual void Profile(llvm::FoldingSetNodeID &ID) const; - - void setAsLastInMainSourceFile() { - LastInMainSourceFile = true; - } - - bool isLastInMainSourceFile() const { - return LastInMainSourceFile; - } - - virtual void dump() const = 0; -}; - -class PathPieces : public std::list> { - void flattenTo(PathPieces &Primary, PathPieces &Current, - bool ShouldFlattenMacros) const; - -public: - PathPieces flatten(bool ShouldFlattenMacros) const { - PathPieces Result; - flattenTo(Result, Result, ShouldFlattenMacros); - return Result; - } - - void dump() const; -}; - -class PathDiagnosticSpotPiece : public PathDiagnosticPiece { -private: - PathDiagnosticLocation Pos; - -public: - PathDiagnosticSpotPiece(const PathDiagnosticLocation &pos, - StringRef s, - PathDiagnosticPiece::Kind k, - bool addPosRange = true) - : PathDiagnosticPiece(s, k), Pos(pos) { - assert(Pos.isValid() && Pos.hasValidLocation() && - "PathDiagnosticSpotPiece's must have a valid location."); - if (addPosRange && Pos.hasRange()) addRange(Pos.asRange()); - } - - PathDiagnosticLocation getLocation() const override { return Pos; } - void flattenLocations() override { Pos.flatten(); } - - void Profile(llvm::FoldingSetNodeID &ID) const override; - - static bool classof(const PathDiagnosticPiece *P) { - return P->getKind() == Event || P->getKind() == Macro || - P->getKind() == Note || P->getKind() == PopUp; - } -}; - -/// Interface for classes constructing Stack hints. -/// -/// If a PathDiagnosticEvent occurs in a different frame than the final -/// diagnostic the hints can be used to summarize the effect of the call. -class StackHintGenerator { -public: - virtual ~StackHintGenerator() = 0; - - /// Construct the Diagnostic message for the given ExplodedNode. - virtual std::string getMessage(const ExplodedNode *N) = 0; -}; - -/// Constructs a Stack hint for the given symbol. -/// -/// The class knows how to construct the stack hint message based on -/// traversing the CallExpr associated with the call and checking if the given -/// symbol is returned or is one of the arguments. -/// The hint can be customized by redefining 'getMessageForX()' methods. -class StackHintGeneratorForSymbol : public StackHintGenerator { -private: - SymbolRef Sym; - std::string Msg; - -public: - StackHintGeneratorForSymbol(SymbolRef S, StringRef M) : Sym(S), Msg(M) {} - ~StackHintGeneratorForSymbol() override = default; - - /// Search the call expression for the symbol Sym and dispatch the - /// 'getMessageForX()' methods to construct a specific message. - std::string getMessage(const ExplodedNode *N) override; - - /// Produces the message of the following form: - /// 'Msg via Nth parameter' - virtual std::string getMessageForArg(const Expr *ArgE, unsigned ArgIndex); - - virtual std::string getMessageForReturn(const CallExpr *CallExpr) { - return Msg; - } - - virtual std::string getMessageForSymbolNotFound() { - return Msg; - } -}; - -class PathDiagnosticEventPiece : public PathDiagnosticSpotPiece { - Optional IsPrunable; - - /// If the event occurs in a different frame than the final diagnostic, - /// supply a message that will be used to construct an extra hint on the - /// returns from all the calls on the stack from this event to the final - /// diagnostic. - std::unique_ptr CallStackHint; - -public: - PathDiagnosticEventPiece(const PathDiagnosticLocation &pos, - StringRef s, bool addPosRange = true, - StackHintGenerator *stackHint = nullptr) - : PathDiagnosticSpotPiece(pos, s, Event, addPosRange), - CallStackHint(stackHint) {} - ~PathDiagnosticEventPiece() override; - - /// Mark the diagnostic piece as being potentially prunable. This - /// flag may have been previously set, at which point it will not - /// be reset unless one specifies to do so. - void setPrunable(bool isPrunable, bool override = false) { - if (IsPrunable.hasValue() && !override) - return; - IsPrunable = isPrunable; - } - - /// Return true if the diagnostic piece is prunable. - bool isPrunable() const { - return IsPrunable.hasValue() ? IsPrunable.getValue() : false; - } - - bool hasCallStackHint() { return (bool)CallStackHint; } - - /// Produce the hint for the given node. The node contains - /// information about the call for which the diagnostic can be generated. - std::string getCallStackMessage(const ExplodedNode *N) { - if (CallStackHint) - return CallStackHint->getMessage(N); - return {}; - } - - void dump() const override; - - static bool classof(const PathDiagnosticPiece *P) { - return P->getKind() == Event; - } -}; - -class PathDiagnosticCallPiece : public PathDiagnosticPiece { - const Decl *Caller; - const Decl *Callee = nullptr; - - // Flag signifying that this diagnostic has only call enter and no matching - // call exit. - bool NoExit; - - // Flag signifying that the callee function is an Objective-C autosynthesized - // property getter or setter. - bool IsCalleeAnAutosynthesizedPropertyAccessor = false; - - // The custom string, which should appear after the call Return Diagnostic. - // TODO: Should we allow multiple diagnostics? - std::string CallStackMessage; - - PathDiagnosticCallPiece(const Decl *callerD, - const PathDiagnosticLocation &callReturnPos) - : PathDiagnosticPiece(Call), Caller(callerD), NoExit(false), - callReturn(callReturnPos) {} - PathDiagnosticCallPiece(PathPieces &oldPath, const Decl *caller) - : PathDiagnosticPiece(Call), Caller(caller), NoExit(true), - path(oldPath) {} - -public: - PathDiagnosticLocation callEnter; - PathDiagnosticLocation callEnterWithin; - PathDiagnosticLocation callReturn; - PathPieces path; - - ~PathDiagnosticCallPiece() override; - - const Decl *getCaller() const { return Caller; } - - const Decl *getCallee() const { return Callee; } - void setCallee(const CallEnter &CE, const SourceManager &SM); - - bool hasCallStackMessage() { return !CallStackMessage.empty(); } - void setCallStackMessage(StringRef st) { CallStackMessage = st; } - - PathDiagnosticLocation getLocation() const override { return callEnter; } - - std::shared_ptr getCallEnterEvent() const; - std::shared_ptr - getCallEnterWithinCallerEvent() const; - std::shared_ptr getCallExitEvent() const; - - void flattenLocations() override { - callEnter.flatten(); - callReturn.flatten(); - for (const auto &I : path) - I->flattenLocations(); - } - - static std::shared_ptr - construct(const CallExitEnd &CE, - const SourceManager &SM); - - static PathDiagnosticCallPiece *construct(PathPieces &pieces, - const Decl *caller); - - void dump() const override; - - void Profile(llvm::FoldingSetNodeID &ID) const override; - - static bool classof(const PathDiagnosticPiece *P) { - return P->getKind() == Call; - } -}; - -class PathDiagnosticControlFlowPiece : public PathDiagnosticPiece { - std::vector LPairs; - -public: - PathDiagnosticControlFlowPiece(const PathDiagnosticLocation &startPos, - const PathDiagnosticLocation &endPos, - StringRef s) - : PathDiagnosticPiece(s, ControlFlow) { - LPairs.push_back(PathDiagnosticLocationPair(startPos, endPos)); - } - - PathDiagnosticControlFlowPiece(const PathDiagnosticLocation &startPos, - const PathDiagnosticLocation &endPos) - : PathDiagnosticPiece(ControlFlow) { - LPairs.push_back(PathDiagnosticLocationPair(startPos, endPos)); - } - - ~PathDiagnosticControlFlowPiece() override; - - PathDiagnosticLocation getStartLocation() const { - assert(!LPairs.empty() && - "PathDiagnosticControlFlowPiece needs at least one location."); - return LPairs[0].getStart(); - } - - PathDiagnosticLocation getEndLocation() const { - assert(!LPairs.empty() && - "PathDiagnosticControlFlowPiece needs at least one location."); - 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); } - - PathDiagnosticLocation getLocation() const override { - return getStartLocation(); - } - - using iterator = std::vector::iterator; - - iterator begin() { return LPairs.begin(); } - iterator end() { return LPairs.end(); } - - void flattenLocations() override { - for (auto &I : *this) - I.flatten(); - } - - using const_iterator = - std::vector::const_iterator; - - const_iterator begin() const { return LPairs.begin(); } - const_iterator end() const { return LPairs.end(); } - - static bool classof(const PathDiagnosticPiece *P) { - return P->getKind() == ControlFlow; - } - - void dump() const override; - - void Profile(llvm::FoldingSetNodeID &ID) const override; -}; - -class PathDiagnosticMacroPiece : public PathDiagnosticSpotPiece { -public: - PathDiagnosticMacroPiece(const PathDiagnosticLocation &pos) - : PathDiagnosticSpotPiece(pos, "", Macro) {} - ~PathDiagnosticMacroPiece() override; - - PathPieces subPieces; - - bool containsEvent() const; - - void flattenLocations() override { - PathDiagnosticSpotPiece::flattenLocations(); - for (const auto &I : subPieces) - I->flattenLocations(); - } - - static bool classof(const PathDiagnosticPiece *P) { - return P->getKind() == Macro; - } - - void dump() const override; - - void Profile(llvm::FoldingSetNodeID &ID) const override; -}; - -class PathDiagnosticNotePiece: public PathDiagnosticSpotPiece { -public: - PathDiagnosticNotePiece(const PathDiagnosticLocation &Pos, StringRef S, - bool AddPosRange = true) - : PathDiagnosticSpotPiece(Pos, S, Note, AddPosRange) {} - ~PathDiagnosticNotePiece() override; - - static bool classof(const PathDiagnosticPiece *P) { - return P->getKind() == Note; - } - - void dump() const override; - - void Profile(llvm::FoldingSetNodeID &ID) const override; -}; - -class PathDiagnosticPopUpPiece: public PathDiagnosticSpotPiece { -public: - PathDiagnosticPopUpPiece(const PathDiagnosticLocation &Pos, StringRef S, - bool AddPosRange = true) - : PathDiagnosticSpotPiece(Pos, S, PopUp, AddPosRange) {} - ~PathDiagnosticPopUpPiece() override; - - static bool classof(const PathDiagnosticPiece *P) { - return P->getKind() == PopUp; - } - - void dump() const override; - - void Profile(llvm::FoldingSetNodeID &ID) const override; -}; - -/// File IDs mapped to sets of line numbers. -using FilesToLineNumsMap = std::map>; - -/// PathDiagnostic - PathDiagnostic objects represent a single path-sensitive -/// diagnostic. It represents an ordered-collection of PathDiagnosticPieces, -/// each which represent the pieces of the path. -class PathDiagnostic : public llvm::FoldingSetNode { - std::string CheckName; - const Decl *DeclWithIssue; - std::string BugType; - std::string VerboseDesc; - std::string ShortDesc; - std::string Category; - std::deque OtherDesc; - - /// Loc The location of the path diagnostic report. - PathDiagnosticLocation Loc; - - PathPieces pathImpl; - SmallVector pathStack; - - /// Important bug uniqueing location. - /// The location info is useful to differentiate between bugs. - PathDiagnosticLocation UniqueingLoc; - const Decl *UniqueingDecl; - - /// Lines executed in the path. - std::unique_ptr ExecutedLines; - -public: - PathDiagnostic() = delete; - PathDiagnostic(StringRef CheckName, const Decl *DeclWithIssue, - StringRef bugtype, StringRef verboseDesc, StringRef shortDesc, - StringRef category, PathDiagnosticLocation LocationToUnique, - const Decl *DeclToUnique, - std::unique_ptr ExecutedLines); - ~PathDiagnostic(); - - const PathPieces &path; - - /// Return the path currently used by builders for constructing the - /// PathDiagnostic. - PathPieces &getActivePath() { - if (pathStack.empty()) - return pathImpl; - return *pathStack.back(); - } - - /// Return a mutable version of 'path'. - PathPieces &getMutablePieces() { - return pathImpl; - } - - /// Return the unrolled size of the path. - unsigned full_size(); - - void pushActivePath(PathPieces *p) { pathStack.push_back(p); } - void popActivePath() { if (!pathStack.empty()) pathStack.pop_back(); } - - bool isWithinCall() const { return !pathStack.empty(); } - - void setEndOfPath(std::shared_ptr EndPiece) { - assert(!Loc.isValid() && "End location already set!"); - Loc = EndPiece->getLocation(); - assert(Loc.isValid() && "Invalid location for end-of-path piece"); - getActivePath().push_back(std::move(EndPiece)); - } - - void appendToDesc(StringRef S) { - if (!ShortDesc.empty()) - ShortDesc += S; - VerboseDesc += S; - } - - /// If the last piece of the report point to the header file, resets - /// the location of the report to be the last location in the main source - /// file. - void resetDiagnosticLocationToMainFile(); - - StringRef getVerboseDescription() const { return VerboseDesc; } - - StringRef getShortDescription() const { - return ShortDesc.empty() ? VerboseDesc : ShortDesc; - } - - StringRef getCheckName() const { return CheckName; } - StringRef getBugType() const { return BugType; } - StringRef getCategory() const { return Category; } - - /// Return the semantic context where an issue occurred. If the - /// issue occurs along a path, this represents the "central" area - /// where the bug manifests. - const Decl *getDeclWithIssue() const { return DeclWithIssue; } - - using meta_iterator = std::deque::const_iterator; - - meta_iterator meta_begin() const { return OtherDesc.begin(); } - meta_iterator meta_end() const { return OtherDesc.end(); } - void addMeta(StringRef s) { OtherDesc.push_back(s); } - - const FilesToLineNumsMap &getExecutedLines() const { - return *ExecutedLines; - } - - FilesToLineNumsMap &getExecutedLines() { - return *ExecutedLines; - } - - PathDiagnosticLocation getLocation() const { - return Loc; - } - - /// Get the location on which the report should be uniqued. - PathDiagnosticLocation getUniqueingLoc() const { - return UniqueingLoc; - } - - /// Get the declaration containing the uniqueing location. - const Decl *getUniqueingDecl() const { - return UniqueingDecl; - } - - void flattenLocations() { - Loc.flatten(); - for (const auto &I : pathImpl) - I->flattenLocations(); - } - - /// Profiles the diagnostic, independent of the path it references. - /// - /// This can be used to merge diagnostics that refer to the same issue - /// along different paths. - void Profile(llvm::FoldingSetNodeID &ID) const; - - /// Profiles the diagnostic, including its path. - /// - /// Two diagnostics with the same issue along different paths will generate - /// different profiles. - void FullProfile(llvm::FoldingSetNodeID &ID) const; -}; - -} // namespace ento - -} // namespace clang - -#endif // LLVM_CLANG_STATICANALYZER_CORE_BUGREPORTER_PATHDIAGNOSTIC_H diff --git a/include/clang/StaticAnalyzer/Core/Checker.h b/include/clang/StaticAnalyzer/Core/Checker.h index d0fe15f8b8963..0c7acdbc3a97e 100644 --- a/include/clang/StaticAnalyzer/Core/Checker.h +++ b/include/clang/StaticAnalyzer/Core/Checker.h @@ -490,12 +490,12 @@ public: } // end eval namespace class CheckerBase : public ProgramPointTag { - CheckName Name; + CheckerNameRef Name; friend class ::clang::ento::CheckerManager; public: StringRef getTagDescription() const override; - CheckName getCheckName() const; + CheckerNameRef getCheckerName() const; /// See CheckerManager::runCheckersForPrintState. virtual void printState(raw_ostream &Out, ProgramStateRef State, diff --git a/include/clang/StaticAnalyzer/Core/CheckerManager.h b/include/clang/StaticAnalyzer/Core/CheckerManager.h index 6cc4baa1687fd..38a9aaf72c27a 100644 --- a/include/clang/StaticAnalyzer/Core/CheckerManager.h +++ b/include/clang/StaticAnalyzer/Core/CheckerManager.h @@ -90,21 +90,23 @@ enum PointerEscapeKind { PSK_EscapeOther }; -// This wrapper is used to ensure that only StringRefs originating from the -// CheckerRegistry are used as check names. We want to make sure all check -// name strings have a lifetime that keeps them alive at least until the path -// diagnostics have been processed. -class CheckName { +/// This wrapper is used to ensure that only StringRefs originating from the +/// CheckerRegistry are used as check names. We want to make sure all checker +/// name strings have a lifetime that keeps them alive at least until the path +/// diagnostics have been processed, since they are expected to be constexpr +/// string literals (most likely generated by TblGen). +class CheckerNameRef { friend class ::clang::ento::CheckerRegistry; StringRef Name; - explicit CheckName(StringRef Name) : Name(Name) {} + explicit CheckerNameRef(StringRef Name) : Name(Name) {} public: - CheckName() = default; + CheckerNameRef() = default; StringRef getName() const { return Name; } + operator StringRef() const { return Name; } }; enum class ObjCMessageVisitKind { @@ -117,7 +119,7 @@ class CheckerManager { ASTContext &Context; const LangOptions LangOpts; AnalyzerOptions &AOptions; - CheckName CurrentCheckName; + CheckerNameRef CurrentCheckerName; public: CheckerManager(ASTContext &Context, AnalyzerOptions &AOptions) @@ -125,8 +127,8 @@ public: ~CheckerManager(); - void setCurrentCheckName(CheckName name) { CurrentCheckName = name; } - CheckName getCurrentCheckName() const { return CurrentCheckName; } + void setCurrentCheckerName(CheckerNameRef name) { CurrentCheckerName = name; } + CheckerNameRef getCurrentCheckerName() const { return CurrentCheckerName; } bool hasPathSensitiveCheckers() const; @@ -162,7 +164,7 @@ public: assert(!ref && "Checker already registered, use getChecker!"); CHECKER *checker = new CHECKER(std::forward(Args)...); - checker->Name = CurrentCheckName; + checker->Name = CurrentCheckerName; CheckerDtors.push_back(CheckerDtor(checker, destruct)); CHECKER::_register(checker, *this); ref = checker; diff --git a/include/clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h b/include/clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h index ef6e7e0f45d57..8601966c91e58 100644 --- a/include/clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h +++ b/include/clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h @@ -20,17 +20,19 @@ namespace clang { class AnalyzerOptions; class Preprocessor; +namespace cross_tu { +class CrossTranslationUnitContext; +} namespace ento { class PathDiagnosticConsumer; typedef std::vector PathDiagnosticConsumers; -#define ANALYSIS_DIAGNOSTICS(NAME, CMDFLAG, DESC, CREATEFN)\ -void CREATEFN(AnalyzerOptions &AnalyzerOpts,\ - PathDiagnosticConsumers &C,\ - const std::string &Prefix,\ - const Preprocessor &PP); +#define ANALYSIS_DIAGNOSTICS(NAME, CMDFLAG, DESC, CREATEFN) \ + void CREATEFN(AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C, \ + const std::string &Prefix, const Preprocessor &PP, \ + const cross_tu::CrossTranslationUnitContext &CTU); #include "clang/StaticAnalyzer/Core/Analyses.def" } // end 'ento' namespace diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h b/include/clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h index b0dda78a00a98..d605a6a667f6b 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h @@ -15,9 +15,9 @@ #define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_ANALYSISMANAGER_H #include "clang/Analysis/AnalysisDeclContext.h" +#include "clang/Analysis/PathDiagnostic.h" #include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" -#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" #include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h" namespace clang { @@ -32,7 +32,6 @@ class AnalysisManager : public BugReporterData { AnalysisDeclContextManager AnaCtxMgr; ASTContext &Ctx; - DiagnosticsEngine &Diags; const LangOptions &LangOpts; PathDiagnosticConsumers PathConsumers; @@ -45,7 +44,7 @@ class AnalysisManager : public BugReporterData { public: AnalyzerOptions &options; - AnalysisManager(ASTContext &ctx, DiagnosticsEngine &diags, + AnalysisManager(ASTContext &ctx, const PathDiagnosticConsumers &Consumers, StoreManagerCreator storemgr, ConstraintManagerCreator constraintmgr, @@ -84,10 +83,6 @@ public: return getASTContext().getSourceManager(); } - DiagnosticsEngine &getDiagnostic() override { - return Diags; - } - const LangOptions &getLangOpts() const { return LangOpts; } diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h b/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h index db84102983af1..fc1cc91388266 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h @@ -347,7 +347,7 @@ public: ProgramStateRef invalidateRegions(unsigned BlockCount, ProgramStateRef Orig = nullptr) const; - using FrameBindingTy = std::pair; + using FrameBindingTy = std::pair; using BindingsTy = SmallVectorImpl; /// Populates the given SmallVector with the bindings in the callee's stack @@ -386,11 +386,12 @@ public: /// during analysis if the call is inlined, but it may still be useful /// in intermediate calculations even if the call isn't inlined. /// May fail; returns null on failure. - const StackFrameContext *getCalleeStackFrame() const; + const StackFrameContext *getCalleeStackFrame(unsigned BlockCount) const; /// Returns memory location for a parameter variable within the callee stack /// frame. May fail; returns null on failure. - const VarRegion *getParameterLocation(unsigned Index) const; + const VarRegion *getParameterLocation(unsigned Index, + unsigned BlockCount) const; /// Returns true if on the current path, the argument was constructed by /// calling a C++ constructor over it. This is an internal detail of the @@ -1063,8 +1064,19 @@ class CallDescription { // e.g. "{a, b}" represent the qualified names, like "a::b". std::vector QualifiedName; Optional RequiredArgs; + Optional RequiredParams; int Flags; + // A constructor helper. + static Optional readRequiredParams(Optional RequiredArgs, + Optional RequiredParams) { + if (RequiredParams) + return RequiredParams; + if (RequiredArgs) + return static_cast(*RequiredArgs); + return None; + } + public: /// Constructs a CallDescription object. /// @@ -1077,14 +1089,17 @@ public: /// call. Omit this parameter to match every occurrence of call with a given /// name regardless the number of arguments. CallDescription(int Flags, ArrayRef QualifiedName, - Optional RequiredArgs = None) + Optional RequiredArgs = None, + Optional RequiredParams = None) : QualifiedName(QualifiedName), RequiredArgs(RequiredArgs), + RequiredParams(readRequiredParams(RequiredArgs, RequiredParams)), Flags(Flags) {} /// Construct a CallDescription with default flags. CallDescription(ArrayRef QualifiedName, - Optional RequiredArgs = None) - : CallDescription(0, QualifiedName, RequiredArgs) {} + Optional RequiredArgs = None, + Optional RequiredParams = None) + : CallDescription(0, QualifiedName, RequiredArgs, RequiredParams) {} /// Get the name of the function that this object matches. StringRef getFunctionName() const { return QualifiedName.back(); } diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h b/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h index 981133e669775..7f4df0d88def6 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h @@ -103,7 +103,7 @@ public: return Eng.getBugReporter(); } - SourceManager &getSourceManager() { + const SourceManager &getSourceManager() { return getBugReporter().getSourceManager(); } @@ -234,7 +234,7 @@ public: } /// A shorthand version of getNoteTag that doesn't require you to accept - /// the BugReporterContext arguments when you don't need it. + /// the 'BugReporterContext' argument when you don't need it. /// /// @param Cb Callback only with 'BugReport &' parameter. /// @param IsPrunable Whether the note is prunable. It allows BugReporter @@ -247,6 +247,19 @@ public: IsPrunable); } + /// A shorthand version of getNoteTag that doesn't require you to accept + /// the arguments when you don't need it. + /// + /// @param Cb Callback without parameters. + /// @param IsPrunable Whether the note is prunable. It allows BugReporter + /// to omit the note from the report if it would make the displayed + /// bug path significantly shorter. + const NoteTag *getNoteTag(std::function &&Cb, + bool IsPrunable = false) { + return getNoteTag([Cb](BugReporterContext &, BugReport &) { return Cb(); }, + IsPrunable); + } + /// A shorthand version of getNoteTag that accepts a plain note. /// /// @param Note The note. diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicCastInfo.h b/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicCastInfo.h new file mode 100644 index 0000000000000..f5a710c77a6ad --- /dev/null +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicCastInfo.h @@ -0,0 +1,55 @@ +//===- DynamicCastInfo.h - Runtime cast information -------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_DYNAMICCASTINFO_H +#define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_DYNAMICCASTINFO_H + +#include "clang/AST/Type.h" + +namespace clang { +namespace ento { + +class DynamicCastInfo { +public: + enum CastResult { Success, Failure }; + + DynamicCastInfo(QualType from, QualType to, CastResult resultKind) + : From(from), To(to), ResultKind(resultKind) {} + + QualType from() const { return From; } + QualType to() const { return To; } + + bool equals(QualType from, QualType to) const { + return From == from && To == to; + } + + bool succeeds() const { return ResultKind == CastResult::Success; } + bool fails() const { return ResultKind == CastResult::Failure; } + + bool operator==(const DynamicCastInfo &RHS) const { + return From == RHS.From && To == RHS.To; + } + bool operator<(const DynamicCastInfo &RHS) const { + return From < RHS.From && To < RHS.To; + } + + void Profile(llvm::FoldingSetNodeID &ID) const { + ID.Add(From); + ID.Add(To); + ID.AddInteger(ResultKind); + } + +private: + QualType From, To; + CastResult ResultKind; +}; + +} // namespace ento +} // namespace clang + +#endif // LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_DYNAMICCASTINFO_H diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h b/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h new file mode 100644 index 0000000000000..356401d77561c --- /dev/null +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h @@ -0,0 +1,73 @@ +//===- DynamicType.h - Dynamic type related APIs ----------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines APIs that track and query dynamic type information. This +// information can be used to devirtualize calls during the symbolic execution +// or do type checking. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_DYNAMICTYPE_H +#define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_DYNAMICTYPE_H + +#include "clang/AST/Type.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicCastInfo.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeInfo.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" +#include "llvm/ADT/ImmutableMap.h" +#include "llvm/ADT/Optional.h" +#include + +namespace clang { +namespace ento { + +/// Get dynamic type information for the region \p MR. +DynamicTypeInfo getDynamicTypeInfo(ProgramStateRef State, const MemRegion *MR); + +/// Get raw dynamic type information for the region \p MR. +const DynamicTypeInfo *getRawDynamicTypeInfo(ProgramStateRef State, + const MemRegion *MR); + +/// Get dynamic cast information from \p CastFromTy to \p CastToTy of \p MR. +const DynamicCastInfo *getDynamicCastInfo(ProgramStateRef State, + const MemRegion *MR, + QualType CastFromTy, + QualType CastToTy); + +/// Set dynamic type information of the region; return the new state. +ProgramStateRef setDynamicTypeInfo(ProgramStateRef State, const MemRegion *MR, + DynamicTypeInfo NewTy); + +/// Set dynamic type information of the region; return the new state. +ProgramStateRef setDynamicTypeInfo(ProgramStateRef State, const MemRegion *MR, + QualType NewTy, bool CanBeSubClassed = true); + +/// Set dynamic type and cast information of the region; return the new state. +ProgramStateRef setDynamicTypeAndCastInfo(ProgramStateRef State, + const MemRegion *MR, + QualType CastFromTy, + QualType CastToTy, + bool IsCastSucceeds); + +/// Removes the dead type informations from \p State. +ProgramStateRef removeDeadTypes(ProgramStateRef State, SymbolReaper &SR); + +/// Removes the dead cast informations from \p State. +ProgramStateRef removeDeadCasts(ProgramStateRef State, SymbolReaper &SR); + +void printDynamicTypeInfoJson(raw_ostream &Out, ProgramStateRef State, + const char *NL = "\n", unsigned int Space = 0, + bool IsDot = false); + +} // namespace ento +} // namespace clang + +#endif // LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_DYNAMICTYPE_H diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeInfo.h b/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeInfo.h index 9bb1e21375666..6262c4a1ce378 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeInfo.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeInfo.h @@ -1,10 +1,11 @@ -//== DynamicTypeInfo.h - Runtime type information ----------------*- C++ -*--=// +//===- DynamicTypeInfo.h - Runtime type information -------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// + #ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_DYNAMICTYPEINFO_H #define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_DYNAMICTYPEINFO_H @@ -16,36 +17,37 @@ namespace ento { /// Stores the currently inferred strictest bound on the runtime type /// of a region in a given state along the analysis path. class DynamicTypeInfo { -private: - QualType T; - bool CanBeASubClass; - public: + DynamicTypeInfo() : DynTy(QualType()) {} - DynamicTypeInfo() : T(QualType()) {} - DynamicTypeInfo(QualType WithType, bool CanBeSub = true) - : T(WithType), CanBeASubClass(CanBeSub) {} + DynamicTypeInfo(QualType Ty, bool CanBeSub = true) + : DynTy(Ty), CanBeASubClass(CanBeSub) {} + + /// Returns false if the type information is precise (the type 'DynTy' is + /// the only type in the lattice), true otherwise. + bool canBeASubClass() const { return CanBeASubClass; } - /// Return false if no dynamic type info is available. - bool isValid() const { return !T.isNull(); } + /// Returns true if the dynamic type info is available. + bool isValid() const { return !DynTy.isNull(); } /// Returns the currently inferred upper bound on the runtime type. - QualType getType() const { return T; } + QualType getType() const { return DynTy; } - /// Returns false if the type information is precise (the type T is - /// the only type in the lattice), true otherwise. - bool canBeASubClass() const { return CanBeASubClass; } + bool operator==(const DynamicTypeInfo &RHS) const { + return DynTy == RHS.DynTy && CanBeASubClass == RHS.CanBeASubClass; + } void Profile(llvm::FoldingSetNodeID &ID) const { - ID.Add(T); - ID.AddInteger((unsigned)CanBeASubClass); - } - bool operator==(const DynamicTypeInfo &X) const { - return T == X.T && CanBeASubClass == X.CanBeASubClass; + ID.Add(DynTy); + ID.AddBoolean(CanBeASubClass); } + +private: + QualType DynTy; + bool CanBeASubClass; }; -} // end ento -} // end clang +} // namespace ento +} // namespace clang -#endif +#endif // LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_DYNAMICTYPEINFO_H diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h b/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h deleted file mode 100644 index a84b248720618..0000000000000 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h +++ /dev/null @@ -1,63 +0,0 @@ -//===- DynamicTypeMap.h - Dynamic type map ----------------------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// This file provides APIs for tracking dynamic type information. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_DYNAMICTYPEMAP_H -#define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_DYNAMICTYPEMAP_H - -#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeInfo.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" -#include "llvm/ADT/ImmutableMap.h" -#include "clang/AST/Type.h" - -namespace clang { -namespace ento { - -class MemRegion; - -/// The GDM component containing the dynamic type info. This is a map from a -/// symbol to its most likely type. -struct DynamicTypeMap {}; - -using DynamicTypeMapTy = llvm::ImmutableMap; - -template <> -struct ProgramStateTrait - : public ProgramStatePartialTrait { - static void *GDMIndex(); -}; - -/// Get dynamic type information for a region. -DynamicTypeInfo getDynamicTypeInfo(ProgramStateRef State, - const MemRegion *Reg); - -/// Set dynamic type information of the region; return the new state. -ProgramStateRef setDynamicTypeInfo(ProgramStateRef State, const MemRegion *Reg, - DynamicTypeInfo NewTy); - -/// Set dynamic type information of the region; return the new state. -inline ProgramStateRef setDynamicTypeInfo(ProgramStateRef State, - const MemRegion *Reg, QualType NewTy, - bool CanBeSubClassed = true) { - return setDynamicTypeInfo(State, Reg, - DynamicTypeInfo(NewTy, CanBeSubClassed)); -} - -void printDynamicTypeInfoJson(raw_ostream &Out, ProgramStateRef State, - const char *NL = "\n", unsigned int Space = 0, - bool IsDot = false); - -} // namespace ento -} // namespace clang - -#endif // LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_DYNAMICTYPEMAP_H diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h b/include/clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h index 727d04cba2784..e87772c04b9be 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h @@ -131,10 +131,12 @@ class ExplodedNode : public llvm::FoldingSetNode { /// Succs - The successors of this node. NodeGroup Succs; + int64_t Id; + public: explicit ExplodedNode(const ProgramPoint &loc, ProgramStateRef state, - bool IsSink) - : Location(loc), State(std::move(state)), Succs(IsSink) { + int64_t Id, bool IsSink) + : Location(loc), State(std::move(state)), Succs(IsSink), Id(Id) { assert(isSink() == IsSink); } @@ -153,7 +155,11 @@ public: CFG &getCFG() const { return *getLocationContext()->getCFG(); } - ParentMap &getParentMap() const {return getLocationContext()->getParentMap();} + const CFGBlock *getCFGBlock() const; + + const ParentMap &getParentMap() const { + return getLocationContext()->getParentMap(); + } template T &getAnalysis() const { @@ -219,12 +225,20 @@ public: // Iterators over successor and predecessor vertices. using succ_iterator = ExplodedNode * const *; + using succ_range = llvm::iterator_range; + using const_succ_iterator = const ExplodedNode * const *; + using const_succ_range = llvm::iterator_range; + using pred_iterator = ExplodedNode * const *; + using pred_range = llvm::iterator_range; + using const_pred_iterator = const ExplodedNode * const *; + using const_pred_range = llvm::iterator_range; pred_iterator pred_begin() { return Preds.begin(); } pred_iterator pred_end() { return Preds.end(); } + pred_range preds() { return {Preds.begin(), Preds.end()}; } const_pred_iterator pred_begin() const { return const_cast(this)->pred_begin(); @@ -232,9 +246,11 @@ public: const_pred_iterator pred_end() const { return const_cast(this)->pred_end(); } + const_pred_range preds() const { return {Preds.begin(), Preds.end()}; } succ_iterator succ_begin() { return Succs.begin(); } succ_iterator succ_end() { return Succs.end(); } + succ_range succs() { return {Succs.begin(), Succs.end()}; } const_succ_iterator succ_begin() const { return const_cast(this)->succ_begin(); @@ -242,8 +258,9 @@ public: const_succ_iterator succ_end() const { return const_cast(this)->succ_end(); } + const_succ_range succs() const { return {Succs.begin(), Succs.end()}; } - int64_t getID(ExplodedGraph *G) const; + int64_t getID() const { return Id; } /// The node is trivial if it has only one successor, only one predecessor, /// it's predecessor has only one successor, @@ -252,6 +269,30 @@ public: /// Trivial nodes may be skipped while printing exploded graph. bool isTrivial() const; + /// If the node's program point corresponds to a statement, retrieve that + /// statement. Useful for figuring out where to put a warning or a note. + /// If the statement belongs to a body-farmed definition, + /// retrieve the call site for that definition. + const Stmt *getStmtForDiagnostics() const; + + /// Find the next statement that was executed on this node's execution path. + /// Useful for explaining control flow that follows the current node. + /// If the statement belongs to a body-farmed definition, retrieve the + /// call site for that definition. + const Stmt *getNextStmtForDiagnostics() const; + + /// Find the statement that was executed immediately before this node. + /// Useful when the node corresponds to a CFG block entrance. + /// If the statement belongs to a body-farmed definition, retrieve the + /// call site for that definition. + const Stmt *getPreviousStmtForDiagnostics() const; + + /// Find the statement that was executed at or immediately before this node. + /// Useful when any nearby statement will do. + /// If the statement belongs to a body-farmed definition, retrieve the + /// call site for that definition. + const Stmt *getCurrentOrPreviousStmtForDiagnostics() const; + private: void replaceSuccessor(ExplodedNode *node) { Succs.replaceNode(node); } void replacePredecessor(ExplodedNode *node) { Preds.replaceNode(node); } @@ -285,7 +326,7 @@ protected: BumpVectorContext BVC; /// NumNodes - The number of nodes in the graph. - unsigned NumNodes = 0; + int64_t NumNodes = 0; /// A list of recently allocated nodes that can potentially be recycled. NodeVector ChangedNodes; @@ -319,10 +360,11 @@ public: /// ExplodedGraph for further processing. ExplodedNode *createUncachedNode(const ProgramPoint &L, ProgramStateRef State, + int64_t Id, bool IsSink = false); std::unique_ptr MakeEmptyGraph() const { - return llvm::make_unique(); + return std::make_unique(); } /// addRoot - Add an untyped node to the set of roots. diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h index 2629d7121de4c..2d0967616ff20 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h @@ -145,9 +145,9 @@ private: ObjCNoReturn ObjCNoRet; /// The BugReporter associated with this engine. It is important that - /// this object be placed at the very end of member variables so that its - /// destructor is called before the rest of the ExprEngine is destroyed. - GRBugReporter BR; + /// this object be placed at the very end of member variables so that its + /// destructor is called before the rest of the ExprEngine is destroyed. + PathSensitiveBugReporter BR; /// The functions which have been analyzed through inlining. This is owned by /// AnalysisConsumer. It can be null. @@ -530,7 +530,7 @@ public: void VisitCXXDestructor(QualType ObjectType, const MemRegion *Dest, const Stmt *S, bool IsBaseDtor, ExplodedNode *Pred, ExplodedNodeSet &Dst, - const EvalCallOptions &Options); + EvalCallOptions &Options); void VisitCXXNewAllocatorCall(const CXXNewExpr *CNE, ExplodedNode *Pred, @@ -666,7 +666,7 @@ public: const LocationContext *LCtx, ProgramStateRef State); - /// Evaluate a call, running pre- and post-call checks and allowing checkers + /// Evaluate a call, running pre- and post-call checkers and allowing checkers /// to be responsible for handling the evaluation of the call itself. void evalCall(ExplodedNodeSet &Dst, ExplodedNode *Pred, const CallEvent &Call); diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h b/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h index 071e35085a5f9..71cbbe28fc258 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h @@ -169,6 +169,7 @@ public: Kind getKind() const { return kind; } template const RegionTy* getAs() const; + template const RegionTy* castAs() const; virtual bool isBoundable() const { return false; } @@ -1231,6 +1232,11 @@ const RegionTy* MemRegion::getAs() const { return nullptr; } +template +const RegionTy* MemRegion::castAs() const { + return cast(this); +} + //===----------------------------------------------------------------------===// // MemRegionManager - Factory object for creating regions. //===----------------------------------------------------------------------===// diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h b/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h index d38058f9af56d..07920790c80aa 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h @@ -507,6 +507,10 @@ public: return *svalBuilder; } + const SValBuilder &getSValBuilder() const { + return *svalBuilder; + } + SymbolManager &getSymbolManager() { return svalBuilder->getSymbolManager(); } diff --git a/include/clang/Tooling/ASTDiff/ASTDiff.h b/include/clang/Tooling/ASTDiff/ASTDiff.h index d6cbc09dcede0..c1cc124e1e9f0 100644 --- a/include/clang/Tooling/ASTDiff/ASTDiff.h +++ b/include/clang/Tooling/ASTDiff/ASTDiff.h @@ -71,7 +71,7 @@ public: /// Constructs a tree from any AST node. template SyntaxTree(T *Node, ASTContext &AST) - : TreeImpl(llvm::make_unique(this, Node, AST)) {} + : TreeImpl(std::make_unique(this, Node, AST)) {} SyntaxTree(SyntaxTree &&Other) = default; ~SyntaxTree(); diff --git a/include/clang/Tooling/AllTUsExecution.h b/include/clang/Tooling/AllTUsExecution.h index e670f54234a67..1e618b5ba2f0f 100644 --- a/include/clang/Tooling/AllTUsExecution.h +++ b/include/clang/Tooling/AllTUsExecution.h @@ -44,8 +44,6 @@ public: StringRef getExecutorName() const override { return ExecutorName; } - bool isSingleProcess() const override { return true; } - using ToolExecutor::execute; llvm::Error @@ -71,6 +69,7 @@ private: unsigned ThreadCount; }; +extern llvm::cl::opt ExecutorConcurrency; extern llvm::cl::opt Filter; } // end namespace tooling diff --git a/include/clang/Tooling/ArgumentsAdjusters.h b/include/clang/Tooling/ArgumentsAdjusters.h index bf0886034324a..c48a8725aae90 100644 --- a/include/clang/Tooling/ArgumentsAdjusters.h +++ b/include/clang/Tooling/ArgumentsAdjusters.h @@ -43,6 +43,10 @@ ArgumentsAdjuster getClangSyntaxOnlyAdjuster(); /// arguments. ArgumentsAdjuster getClangStripOutputAdjuster(); +/// Gets an argument adjuster which removes command line arguments related to +/// diagnostic serialization. +ArgumentsAdjuster getClangStripSerializeDiagnosticAdjuster(); + /// Gets an argument adjuster which removes dependency-file /// related command line arguments. ArgumentsAdjuster getClangStripDependencyFileAdjuster(); diff --git a/include/clang/Tooling/DependencyScanning/DependencyScanningFilesystem.h b/include/clang/Tooling/DependencyScanning/DependencyScanningFilesystem.h new file mode 100644 index 0000000000000..7d0881343478a --- /dev/null +++ b/include/clang/Tooling/DependencyScanning/DependencyScanningFilesystem.h @@ -0,0 +1,188 @@ +//===- DependencyScanningFilesystem.h - clang-scan-deps fs ===---*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLING_DEPENDENCY_SCANNING_FILESYSTEM_H +#define LLVM_CLANG_TOOLING_DEPENDENCY_SCANNING_FILESYSTEM_H + +#include "clang/Basic/LLVM.h" +#include "clang/Lex/PreprocessorExcludedConditionalDirectiveSkipMapping.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringSet.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/ErrorOr.h" +#include "llvm/Support/VirtualFileSystem.h" +#include + +namespace clang { +namespace tooling { +namespace dependencies { + +/// An in-memory representation of a file system entity that is of interest to +/// the dependency scanning filesystem. +/// +/// It represents one of the following: +/// - an opened source file with minimized contents and a stat value. +/// - an opened source file with original contents and a stat value. +/// - a directory entry with its stat value. +/// - an error value to represent a file system error. +/// - a placeholder with an invalid stat indicating a not yet initialized entry. +class CachedFileSystemEntry { +public: + /// Default constructor creates an entry with an invalid stat. + CachedFileSystemEntry() : MaybeStat(llvm::vfs::Status()) {} + + CachedFileSystemEntry(std::error_code Error) : MaybeStat(std::move(Error)) {} + + /// Create an entry that represents an opened source file with minimized or + /// original contents. + /// + /// The filesystem opens the file even for `stat` calls open to avoid the + /// issues with stat + open of minimized files that might lead to a + /// mismatching size of the file. If file is not minimized, the full file is + /// read and copied into memory to ensure that it's not memory mapped to avoid + /// running out of file descriptors. + static CachedFileSystemEntry createFileEntry(StringRef Filename, + llvm::vfs::FileSystem &FS, + bool Minimize = true); + + /// Create an entry that represents a directory on the filesystem. + static CachedFileSystemEntry createDirectoryEntry(llvm::vfs::Status &&Stat); + + /// \returns True if the entry is valid. + bool isValid() const { return !MaybeStat || MaybeStat->isStatusKnown(); } + + /// \returns True if the current entry points to a directory. + bool isDirectory() const { return MaybeStat && MaybeStat->isDirectory(); } + + /// \returns The error or the file's contents. + llvm::ErrorOr getContents() const { + if (!MaybeStat) + return MaybeStat.getError(); + assert(!MaybeStat->isDirectory() && "not a file"); + assert(isValid() && "not initialized"); + return StringRef(Contents); + } + + /// \returns The error or the status of the entry. + llvm::ErrorOr getStatus() const { + assert(isValid() && "not initialized"); + return MaybeStat; + } + + /// \returns the name of the file. + StringRef getName() const { + assert(isValid() && "not initialized"); + return MaybeStat->getName(); + } + + /// Return the mapping between location -> distance that is used to speed up + /// the block skipping in the preprocessor. + const PreprocessorSkippedRangeMapping &getPPSkippedRangeMapping() const { + return PPSkippedRangeMapping; + } + + CachedFileSystemEntry(CachedFileSystemEntry &&) = default; + CachedFileSystemEntry &operator=(CachedFileSystemEntry &&) = default; + + CachedFileSystemEntry(const CachedFileSystemEntry &) = delete; + CachedFileSystemEntry &operator=(const CachedFileSystemEntry &) = delete; + +private: + llvm::ErrorOr MaybeStat; + // Store the contents in a small string to allow a + // move from the small string for the minimized contents. + // Note: small size of 1 allows us to store an empty string with an implicit + // null terminator without any allocations. + llvm::SmallString<1> Contents; + PreprocessorSkippedRangeMapping PPSkippedRangeMapping; +}; + +/// This class is a shared cache, that caches the 'stat' and 'open' calls to the +/// underlying real file system. +/// +/// It is sharded based on the hash of the key to reduce the lock contention for +/// the worker threads. +class DependencyScanningFilesystemSharedCache { +public: + struct SharedFileSystemEntry { + std::mutex ValueLock; + CachedFileSystemEntry Value; + }; + + DependencyScanningFilesystemSharedCache(); + + /// Returns a cache entry for the corresponding key. + /// + /// A new cache entry is created if the key is not in the cache. This is a + /// thread safe call. + SharedFileSystemEntry &get(StringRef Key); + +private: + struct CacheShard { + std::mutex CacheLock; + llvm::StringMap Cache; + }; + std::unique_ptr CacheShards; + unsigned NumShards; +}; + +/// A virtual file system optimized for the dependency discovery. +/// +/// It is primarily designed to work with source files whose contents was was +/// preprocessed to remove any tokens that are unlikely to affect the dependency +/// computation. +/// +/// This is not a thread safe VFS. A single instance is meant to be used only in +/// one thread. Multiple instances are allowed to service multiple threads +/// running in parallel. +class DependencyScanningWorkerFilesystem : public llvm::vfs::ProxyFileSystem { +public: + DependencyScanningWorkerFilesystem( + DependencyScanningFilesystemSharedCache &SharedCache, + IntrusiveRefCntPtr FS, + ExcludedPreprocessorDirectiveSkipMapping *PPSkipMappings) + : ProxyFileSystem(std::move(FS)), SharedCache(SharedCache), + PPSkipMappings(PPSkipMappings) {} + + llvm::ErrorOr status(const Twine &Path) override; + llvm::ErrorOr> + openFileForRead(const Twine &Path) override; + + /// The set of files that should not be minimized. + llvm::StringSet<> IgnoredFiles; + +private: + void setCachedEntry(StringRef Filename, const CachedFileSystemEntry *Entry) { + bool IsInserted = Cache.try_emplace(Filename, Entry).second; + (void)IsInserted; + assert(IsInserted && "local cache is updated more than once"); + } + + const CachedFileSystemEntry *getCachedEntry(StringRef Filename) { + auto It = Cache.find(Filename); + return It == Cache.end() ? nullptr : It->getValue(); + } + + llvm::ErrorOr + getOrCreateFileSystemEntry(const StringRef Filename); + + DependencyScanningFilesystemSharedCache &SharedCache; + /// The local cache is used by the worker thread to cache file system queries + /// locally instead of querying the global cache every time. + llvm::StringMap Cache; + /// The optional mapping structure which records information about the + /// excluded conditional directive skip mappings that are used by the + /// currently active preprocessor. + ExcludedPreprocessorDirectiveSkipMapping *PPSkipMappings; +}; + +} // end namespace dependencies +} // end namespace tooling +} // end namespace clang + +#endif // LLVM_CLANG_TOOLING_DEPENDENCY_SCANNING_FILESYSTEM_H diff --git a/include/clang/Tooling/DependencyScanning/DependencyScanningService.h b/include/clang/Tooling/DependencyScanning/DependencyScanningService.h new file mode 100644 index 0000000000000..fd8ed80b143ca --- /dev/null +++ b/include/clang/Tooling/DependencyScanning/DependencyScanningService.h @@ -0,0 +1,65 @@ +//===- DependencyScanningService.h - clang-scan-deps service ===-*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLING_DEPENDENCY_SCANNING_SERVICE_H +#define LLVM_CLANG_TOOLING_DEPENDENCY_SCANNING_SERVICE_H + +#include "clang/Tooling/DependencyScanning/DependencyScanningFilesystem.h" + +namespace clang { +namespace tooling { +namespace dependencies { + +/// The mode in which the dependency scanner will operate to find the +/// dependencies. +enum class ScanningMode { + /// This mode is used to compute the dependencies by running the preprocessor + /// over + /// the unmodified source files. + CanonicalPreprocessing, + + /// This mode is used to compute the dependencies by running the preprocessor + /// over + /// the source files that have been minimized to contents that might affect + /// the dependencies. + MinimizedSourcePreprocessing +}; + +/// The dependency scanning service contains the shared state that is used by +/// the invidual dependency scanning workers. +class DependencyScanningService { +public: + DependencyScanningService(ScanningMode Mode, bool ReuseFileManager = true, + bool SkipExcludedPPRanges = true); + + ScanningMode getMode() const { return Mode; } + + bool canReuseFileManager() const { return ReuseFileManager; } + + bool canSkipExcludedPPRanges() const { return SkipExcludedPPRanges; } + + DependencyScanningFilesystemSharedCache &getSharedCache() { + return SharedCache; + } + +private: + const ScanningMode Mode; + const bool ReuseFileManager; + /// Set to true to use the preprocessor optimization that skips excluded PP + /// ranges by bumping the buffer pointer in the lexer instead of lexing the + /// tokens in the range until reaching the corresponding directive. + const bool SkipExcludedPPRanges; + /// The global file system cache. + DependencyScanningFilesystemSharedCache SharedCache; +}; + +} // end namespace dependencies +} // end namespace tooling +} // end namespace clang + +#endif // LLVM_CLANG_TOOLING_DEPENDENCY_SCANNING_SERVICE_H diff --git a/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h b/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h new file mode 100644 index 0000000000000..0c9efccb1d8bd --- /dev/null +++ b/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h @@ -0,0 +1,48 @@ +//===- DependencyScanningTool.h - clang-scan-deps service ------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLING_DEPENDENCY_SCANNING_TOOL_H +#define LLVM_CLANG_TOOLING_DEPENDENCY_SCANNING_TOOL_H + +#include "clang/Tooling/DependencyScanning/DependencyScanningService.h" +#include "clang/Tooling/DependencyScanning/DependencyScanningWorker.h" +#include "clang/Tooling/JSONCompilationDatabase.h" +#include + +namespace clang{ +namespace tooling{ +namespace dependencies{ + +/// The high-level implementation of the dependency discovery tool that runs on +/// an individual worker thread. +class DependencyScanningTool { +public: + /// Construct a dependency scanning tool. + /// + /// \param Compilations The reference to the compilation database that's + /// used by the clang tool. + DependencyScanningTool(DependencyScanningService &Service, const clang::tooling::CompilationDatabase &Compilations); + + /// Print out the dependency information into a string using the dependency + /// file format that is specified in the options (-MD is the default) and + /// return it. + /// + /// \returns A \c StringError with the diagnostic output if clang errors + /// occurred, dependency file contents otherwise. + llvm::Expected getDependencyFile(const std::string &Input, StringRef CWD); + +private: + DependencyScanningWorker Worker; + const tooling::CompilationDatabase &Compilations; +}; + +} // end namespace dependencies +} // end namespace tooling +} // end namespace clang + +#endif // LLVM_CLANG_TOOLING_DEPENDENCY_SCANNING_TOOL_H diff --git a/include/clang/Tooling/DependencyScanning/DependencyScanningWorker.h b/include/clang/Tooling/DependencyScanning/DependencyScanningWorker.h index 3ea261a30d0f5..45c9fb4f029d2 100644 --- a/include/clang/Tooling/DependencyScanning/DependencyScanningWorker.h +++ b/include/clang/Tooling/DependencyScanning/DependencyScanningWorker.h @@ -10,17 +10,35 @@ #define LLVM_CLANG_TOOLING_DEPENDENCY_SCANNING_WORKER_H #include "clang/Basic/DiagnosticOptions.h" +#include "clang/Basic/FileManager.h" #include "clang/Basic/LLVM.h" #include "clang/Frontend/PCHContainerOperations.h" +#include "clang/Lex/PreprocessorExcludedConditionalDirectiveSkipMapping.h" #include "clang/Tooling/CompilationDatabase.h" #include "llvm/Support/Error.h" #include "llvm/Support/FileSystem.h" #include namespace clang { + +class DependencyOutputOptions; + namespace tooling { namespace dependencies { +class DependencyScanningService; +class DependencyScanningWorkerFilesystem; + +class DependencyConsumer { +public: + virtual ~DependencyConsumer() {} + + virtual void handleFileDependency(const DependencyOutputOptions &Opts, + StringRef Filename) = 0; + + // FIXME: Add support for reporting modular dependencies. +}; + /// An individual dependency scanning worker that is able to run on its own /// thread. /// @@ -29,26 +47,32 @@ namespace dependencies { /// using the regular processing run. class DependencyScanningWorker { public: - DependencyScanningWorker(); + DependencyScanningWorker(DependencyScanningService &Service); - /// Print out the dependency information into a string using the dependency - /// file format that is specified in the options (-MD is the default) and - /// return it. + /// Run the dependency scanning tool for a given clang driver invocation (as + /// specified for the given Input in the CDB), and report the discovered + /// dependencies to the provided consumer. /// /// \returns A \c StringError with the diagnostic output if clang errors - /// occurred, dependency file contents otherwise. - llvm::Expected getDependencyFile(const std::string &Input, - StringRef WorkingDirectory, - const CompilationDatabase &CDB); + /// occurred, success otherwise. + llvm::Error computeDependencies(const std::string &Input, + StringRef WorkingDirectory, + const CompilationDatabase &CDB, + DependencyConsumer &Consumer); private: IntrusiveRefCntPtr DiagOpts; std::shared_ptr PCHContainerOps; + std::unique_ptr PPSkipMappings; + llvm::IntrusiveRefCntPtr RealFS; /// The file system that is used by each worker when scanning for /// dependencies. This filesystem persists accross multiple compiler /// invocations. - llvm::IntrusiveRefCntPtr WorkerFS; + llvm::IntrusiveRefCntPtr DepFS; + /// The file manager that is reused accross multiple invocations by this + /// worker. If null, the file manager will not be reused. + llvm::IntrusiveRefCntPtr Files; }; } // end namespace dependencies diff --git a/include/clang/Tooling/Execution.h b/include/clang/Tooling/Execution.h index 74f0df5a5b912..ca6f22c5da3bf 100644 --- a/include/clang/Tooling/Execution.h +++ b/include/clang/Tooling/Execution.h @@ -115,13 +115,6 @@ public: /// Returns the name of a specific executor. virtual StringRef getExecutorName() const = 0; - /// Should return true iff executor runs all actions in a single process. - /// Clients can use this signal to find out if they can collect results - /// in-memory (e.g. to avoid serialization costs of using ToolResults). - /// The single-process executors can still run multiple threads, but all - /// executions are guaranteed to share the same memory. - virtual bool isSingleProcess() const = 0; - /// Executes each action with a corresponding arguments adjuster. virtual llvm::Error execute(llvm::ArrayRef< diff --git a/include/clang/Tooling/Inclusions/HeaderIncludes.h b/include/clang/Tooling/Inclusions/HeaderIncludes.h index ec6f0ea45ffed..6e6d6d8fb024b 100644 --- a/include/clang/Tooling/Inclusions/HeaderIncludes.h +++ b/include/clang/Tooling/Inclusions/HeaderIncludes.h @@ -32,6 +32,7 @@ public: /// 0. Otherwise, returns the priority of the matching category or INT_MAX. /// NOTE: this API is not thread-safe! int getIncludePriority(StringRef IncludeName, bool CheckMainHeader) const; + int getSortIncludePriority(StringRef IncludeName, bool CheckMainHeader) const; private: bool isMainHeader(StringRef IncludeName) const; diff --git a/include/clang/Tooling/Inclusions/IncludeStyle.h b/include/clang/Tooling/Inclusions/IncludeStyle.h index a0f236e6fc461..266763a5b1bd0 100644 --- a/include/clang/Tooling/Inclusions/IncludeStyle.h +++ b/include/clang/Tooling/Inclusions/IncludeStyle.h @@ -58,6 +58,8 @@ struct IncludeStyle { std::string Regex; /// The priority to assign to this category. int Priority; + /// The custom priority to sort before grouping. + int SortPriority; bool operator==(const IncludeCategory &Other) const { return Regex == Other.Regex && Priority == Other.Priority; } diff --git a/include/clang/Tooling/Refactoring/Extract/SourceExtraction.h b/include/clang/Tooling/Refactoring/Extract/SourceExtraction.h new file mode 100755 index 0000000000000..034a0aaaf6db7 --- /dev/null +++ b/include/clang/Tooling/Refactoring/Extract/SourceExtraction.h @@ -0,0 +1,51 @@ +//===--- SourceExtraction.cpp - Clang refactoring library -----------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLING_REFACTORING_EXTRACT_SOURCE_EXTRACTION_H +#define LLVM_CLANG_TOOLING_REFACTORING_EXTRACT_SOURCE_EXTRACTION_H + +#include "clang/Basic/LLVM.h" + +namespace clang { + +class LangOptions; +class SourceManager; +class SourceRange; +class Stmt; + +namespace tooling { + +/// Determines which semicolons should be inserted during extraction. +class ExtractionSemicolonPolicy { +public: + bool isNeededInExtractedFunction() const { + return IsNeededInExtractedFunction; + } + + bool isNeededInOriginalFunction() const { return IsNeededInOriginalFunction; } + + /// Returns the semicolon insertion policy that is needed for extraction of + /// the given statement from the given source range. + static ExtractionSemicolonPolicy compute(const Stmt *S, + SourceRange &ExtractedRange, + const SourceManager &SM, + const LangOptions &LangOpts); + +private: + ExtractionSemicolonPolicy(bool IsNeededInExtractedFunction, + bool IsNeededInOriginalFunction) + : IsNeededInExtractedFunction(IsNeededInExtractedFunction), + IsNeededInOriginalFunction(IsNeededInOriginalFunction) {} + bool IsNeededInExtractedFunction; + bool IsNeededInOriginalFunction; +}; + +} // end namespace tooling +} // end namespace clang + +#endif //LLVM_CLANG_TOOLING_REFACTORING_EXTRACT_SOURCE_EXTRACTION_H diff --git a/include/clang/Tooling/Refactoring/RangeSelector.h b/include/clang/Tooling/Refactoring/RangeSelector.h deleted file mode 100644 index b117e4d82ad46..0000000000000 --- a/include/clang/Tooling/Refactoring/RangeSelector.h +++ /dev/null @@ -1,89 +0,0 @@ -//===--- RangeSelector.h - Source-selection library ---------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// Defines a combinator library supporting the definition of _selectors_, -/// which select source ranges based on (bound) AST nodes. -/// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_CLANG_TOOLING_REFACTOR_RANGE_SELECTOR_H_ -#define LLVM_CLANG_TOOLING_REFACTOR_RANGE_SELECTOR_H_ - -#include "clang/ASTMatchers/ASTMatchFinder.h" -#include "clang/Basic/SourceLocation.h" -#include "llvm/Support/Error.h" -#include -#include - -namespace clang { -namespace tooling { -using RangeSelector = std::function( - const ast_matchers::MatchFinder::MatchResult &)>; - -inline RangeSelector charRange(CharSourceRange R) { - return [R](const ast_matchers::MatchFinder::MatchResult &) - -> Expected { return R; }; -} - -/// Selects from the start of \p Begin and to the end of \p End. -RangeSelector range(RangeSelector Begin, RangeSelector End); - -/// Convenience version of \c range where end-points are bound nodes. -RangeSelector range(std::string BeginID, std::string EndID); - -/// Selects the (empty) range [B,B) when \p Selector selects the range [B,E). -RangeSelector before(RangeSelector Selector); - -/// Selects the the point immediately following \p Selector. That is, the -/// (empty) range [E,E), when \p Selector selects either -/// * the CharRange [B,E) or -/// * the TokenRange [B,E'] where the token at E' spans the range [E,E'). -RangeSelector after(RangeSelector Selector); - -/// Selects a node, including trailing semicolon (for non-expression -/// statements). \p ID is the node's binding in the match result. -RangeSelector node(std::string ID); - -/// Selects a node, including trailing semicolon (always). Useful for selecting -/// expression statements. \p ID is the node's binding in the match result. -RangeSelector statement(std::string ID); - -/// Given a \c MemberExpr, selects the member token. \p ID is the node's -/// binding in the match result. -RangeSelector member(std::string ID); - -/// Given a node with a "name", (like \c NamedDecl, \c DeclRefExpr or \c -/// CxxCtorInitializer) selects the name's token. Only selects the final -/// identifier of a qualified name, but not any qualifiers or template -/// arguments. For example, for `::foo::bar::baz` and `::foo::bar::baz`, -/// it selects only `baz`. -/// -/// \param ID is the node's binding in the match result. -RangeSelector name(std::string ID); - -// Given a \c CallExpr (bound to \p ID), selects the arguments' source text (all -// source between the call's parentheses). -RangeSelector callArgs(std::string ID); - -// Given a \c CompoundStmt (bound to \p ID), selects the source of the -// statements (all source between the braces). -RangeSelector statements(std::string ID); - -// Given a \c InitListExpr (bound to \p ID), selects the range of the elements -// (all source between the braces). -RangeSelector initListElements(std::string ID); - -/// Selects the range from which `S` was expanded (possibly along with other -/// source), if `S` is an expansion, and `S` itself, otherwise. Corresponds to -/// `SourceManager::getExpansionRange`. -RangeSelector expansion(RangeSelector S); -} // namespace tooling -} // namespace clang - -#endif // LLVM_CLANG_TOOLING_REFACTOR_RANGE_SELECTOR_H_ diff --git a/include/clang/Tooling/Refactoring/RecursiveSymbolVisitor.h b/include/clang/Tooling/Refactoring/RecursiveSymbolVisitor.h index 41a448f035a40..c0f995d85c14c 100644 --- a/include/clang/Tooling/Refactoring/RecursiveSymbolVisitor.h +++ b/include/clang/Tooling/Refactoring/RecursiveSymbolVisitor.h @@ -98,7 +98,17 @@ public: TypeBeginLoc, TypeEndLoc)) return false; } - return visit(Loc.getType()->getAsCXXRecordDecl(), TypeBeginLoc, TypeEndLoc); + if (const Type *TP = Loc.getTypePtr()) { + if (TP->getTypeClass() == clang::Type::Record) + return visit(TP->getAsCXXRecordDecl(), TypeBeginLoc, TypeEndLoc); + } + return true; + } + + bool VisitTypedefTypeLoc(TypedefTypeLoc TL) { + const SourceLocation TypeEndLoc = + Lexer::getLocForEndOfToken(TL.getBeginLoc(), 0, SM, LangOpts); + return visit(TL.getTypedefNameDecl(), TL.getBeginLoc(), TypeEndLoc); } bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc NNS) { @@ -122,8 +132,7 @@ private: ND, SourceRange(BeginLoc, EndLoc)); } bool visit(const NamedDecl *ND, SourceLocation Loc) { - return visit(ND, Loc, - Loc.getLocWithOffset(ND->getNameAsString().length() - 1)); + return visit(ND, Loc, Lexer::getLocForEndOfToken(Loc, 0, SM, LangOpts)); } }; diff --git a/include/clang/Tooling/Refactoring/RefactoringActionRulesInternal.h b/include/clang/Tooling/Refactoring/RefactoringActionRulesInternal.h index cc6ae83202f15..fb373fcf5029a 100644 --- a/include/clang/Tooling/Refactoring/RefactoringActionRulesInternal.h +++ b/include/clang/Tooling/Refactoring/RefactoringActionRulesInternal.h @@ -47,7 +47,7 @@ template void invokeRuleAfterValidatingRequirements( RefactoringResultConsumer &Consumer, RefactoringRuleContext &Context, const std::tuple &Requirements, - llvm::index_sequence) { + std::index_sequence) { // Check if the requirements we're interested in can be evaluated. auto Values = std::make_tuple(std::get(Requirements).evaluate(Context)...); @@ -87,7 +87,7 @@ template void visitRefactoringOptions( RefactoringOptionVisitor &Visitor, const std::tuple &Requirements, - llvm::index_sequence) { + std::index_sequence) { visitRefactoringOptionsImpl(Visitor, std::get(Requirements)...); } @@ -131,7 +131,7 @@ createRefactoringActionRule(const RequirementTypes &... Requirements) { RefactoringRuleContext &Context) override { internal::invokeRuleAfterValidatingRequirements( Consumer, Context, Requirements, - llvm::index_sequence_for()); + std::index_sequence_for()); } bool hasSelectionRequirement() override { @@ -142,13 +142,13 @@ createRefactoringActionRule(const RequirementTypes &... Requirements) { void visitRefactoringOptions(RefactoringOptionVisitor &Visitor) override { internal::visitRefactoringOptions( Visitor, Requirements, - llvm::index_sequence_for()); + std::index_sequence_for()); } private: std::tuple Requirements; }; - return llvm::make_unique(std::make_tuple(Requirements...)); + return std::make_unique(std::make_tuple(Requirements...)); } } // end namespace tooling diff --git a/include/clang/Tooling/Refactoring/SourceCode.h b/include/clang/Tooling/Refactoring/SourceCode.h deleted file mode 100644 index 498dbea96c701..0000000000000 --- a/include/clang/Tooling/Refactoring/SourceCode.h +++ /dev/null @@ -1,77 +0,0 @@ -//===--- SourceCode.h - Source code manipulation routines -------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// This file provides functions that simplify extraction of source code. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_CLANG_TOOLING_REFACTOR_SOURCE_CODE_H -#define LLVM_CLANG_TOOLING_REFACTOR_SOURCE_CODE_H - -#include "clang/AST/ASTContext.h" -#include "clang/Basic/SourceLocation.h" -#include "clang/Basic/TokenKinds.h" - -namespace clang { -namespace tooling { - -/// Extends \p Range to include the token \p Next, if it immediately follows the -/// end of the range. Otherwise, returns \p Range unchanged. -CharSourceRange maybeExtendRange(CharSourceRange Range, tok::TokenKind Next, - ASTContext &Context); - -/// Returns the source range spanning the node, extended to include \p Next, if -/// it immediately follows \p Node. Otherwise, returns the normal range of \p -/// Node. See comments on `getExtendedText()` for examples. -template -CharSourceRange getExtendedRange(const T &Node, tok::TokenKind Next, - ASTContext &Context) { - return maybeExtendRange(CharSourceRange::getTokenRange(Node.getSourceRange()), - Next, Context); -} - -/// Returns the source-code text in the specified range. -StringRef getText(CharSourceRange Range, const ASTContext &Context); - -/// Returns the source-code text corresponding to \p Node. -template -StringRef getText(const T &Node, const ASTContext &Context) { - return getText(CharSourceRange::getTokenRange(Node.getSourceRange()), - Context); -} - -/// Returns the source text of the node, extended to include \p Next, if it -/// immediately follows the node. Otherwise, returns the text of just \p Node. -/// -/// For example, given statements S1 and S2 below: -/// \code -/// { -/// // S1: -/// if (!x) return foo(); -/// // S2: -/// if (!x) { return 3; } -/// } -/// \endcode -/// then -/// \code -/// getText(S1, Context) = "if (!x) return foo()" -/// getExtendedText(S1, tok::TokenKind::semi, Context) -/// = "if (!x) return foo();" -/// getExtendedText(*S1.getThen(), tok::TokenKind::semi, Context) -/// = "return foo();" -/// getExtendedText(*S2.getThen(), tok::TokenKind::semi, Context) -/// = getText(S2, Context) = "{ return 3; }" -/// \endcode -template -StringRef getExtendedText(const T &Node, tok::TokenKind Next, - ASTContext &Context) { - return getText(getExtendedRange(Node, Next, Context), Context); -} -} // namespace tooling -} // namespace clang -#endif // LLVM_CLANG_TOOLING_REFACTOR_SOURCE_CODE_H diff --git a/include/clang/Tooling/Refactoring/Stencil.h b/include/clang/Tooling/Refactoring/Stencil.h deleted file mode 100644 index e57a576e55755..0000000000000 --- a/include/clang/Tooling/Refactoring/Stencil.h +++ /dev/null @@ -1,173 +0,0 @@ -//===--- Stencil.h - Stencil class ------------------------------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// /file -/// This file defines the *Stencil* abstraction: a code-generating object, -/// parameterized by named references to (bound) AST nodes. Given a match -/// result, a stencil can be evaluated to a string of source code. -/// -/// A stencil is similar in spirit to a format string: it is composed of a -/// series of raw text strings, references to nodes (the parameters) and helper -/// code-generation operations. -/// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_CLANG_TOOLING_REFACTOR_STENCIL_H_ -#define LLVM_CLANG_TOOLING_REFACTOR_STENCIL_H_ - -#include "clang/AST/ASTContext.h" -#include "clang/AST/ASTTypeTraits.h" -#include "clang/ASTMatchers/ASTMatchFinder.h" -#include "clang/Tooling/Refactoring/RangeSelector.h" -#include "llvm/ADT/StringRef.h" -#include "llvm/Support/Error.h" -#include -#include - -namespace clang { -namespace tooling { - -/// A stencil is represented as a sequence of "parts" that can each individually -/// generate a code string based on a match result. The different kinds of -/// parts include (raw) text, references to bound nodes and assorted operations -/// on bound nodes. -/// -/// Users can create custom Stencil operations by implementing this interface. -class StencilPartInterface { -public: - virtual ~StencilPartInterface() = default; - - /// Evaluates this part to a string and appends it to \c Result. \c Result is - /// undefined in the case of an error. - virtual llvm::Error eval(const ast_matchers::MatchFinder::MatchResult &Match, - std::string *Result) const = 0; - - virtual bool isEqual(const StencilPartInterface &other) const = 0; - - const void *typeId() const { return TypeId; } - -protected: - StencilPartInterface(const void *DerivedId) : TypeId(DerivedId) {} - - // Since this is an abstract class, copying/assigning only make sense for - // derived classes implementing `clone()`. - StencilPartInterface(const StencilPartInterface &) = default; - StencilPartInterface &operator=(const StencilPartInterface &) = default; - - /// Unique identifier of the concrete type of this instance. Supports safe - /// downcasting. - const void *TypeId; -}; - -/// A copyable facade for a std::unique_ptr. Copies result -/// in a copy of the underlying pointee object. -class StencilPart { -public: - explicit StencilPart(std::shared_ptr Impl) - : Impl(std::move(Impl)) {} - - /// See `StencilPartInterface::eval()`. - llvm::Error eval(const ast_matchers::MatchFinder::MatchResult &Match, - std::string *Result) const { - return Impl->eval(Match, Result); - } - - bool operator==(const StencilPart &Other) const { - if (Impl == Other.Impl) - return true; - if (Impl == nullptr || Other.Impl == nullptr) - return false; - return Impl->isEqual(*Other.Impl); - } - -private: - std::shared_ptr Impl; -}; - -/// A sequence of code fragments, references to parameters and code-generation -/// operations that together can be evaluated to (a fragment of) source code, -/// given a match result. -class Stencil { -public: - Stencil() = default; - - /// Composes a stencil from a series of parts. - template static Stencil cat(Ts &&... Parts) { - Stencil S; - S.Parts = {wrap(std::forward(Parts))...}; - return S; - } - - /// Appends data from a \p OtherStencil to this stencil. - void append(Stencil OtherStencil); - - // Evaluates the stencil given a match result. Requires that the nodes in the - // result includes any ids referenced in the stencil. References to missing - // nodes will result in an invalid_argument error. - llvm::Expected - eval(const ast_matchers::MatchFinder::MatchResult &Match) const; - - // Allow Stencils to operate as std::function, for compatibility with - // Transformer's TextGenerator. - llvm::Expected - operator()(const ast_matchers::MatchFinder::MatchResult &Result) const { - return eval(Result); - } - -private: - friend bool operator==(const Stencil &A, const Stencil &B); - static StencilPart wrap(llvm::StringRef Text); - static StencilPart wrap(RangeSelector Selector); - static StencilPart wrap(StencilPart Part) { return Part; } - - std::vector Parts; -}; - -inline bool operator==(const Stencil &A, const Stencil &B) { - return A.Parts == B.Parts; -} - -inline bool operator!=(const Stencil &A, const Stencil &B) { return !(A == B); } - -// Functions for conveniently building stencils. -namespace stencil { -/// Convenience wrapper for Stencil::cat that can be imported with a using decl. -template Stencil cat(Ts &&... Parts) { - return Stencil::cat(std::forward(Parts)...); -} - -/// \returns exactly the text provided. -StencilPart text(llvm::StringRef Text); - -/// \returns the source corresponding to the selected range. -StencilPart selection(RangeSelector Selector); - -/// \returns the source corresponding to the identified node. -/// FIXME: Deprecated. Write `selection(node(Id))` instead. -inline StencilPart node(llvm::StringRef Id) { - return selection(tooling::node(Id)); -} - -/// Variant of \c node() that identifies the node as a statement, for purposes -/// of deciding whether to include any trailing semicolon. Only relevant for -/// Expr nodes, which, by default, are *not* considered as statements. -/// \returns the source corresponding to the identified node, considered as a -/// statement. -/// FIXME: Deprecated. Write `selection(statement(Id))` instead. -inline StencilPart sNode(llvm::StringRef Id) { - return selection(tooling::statement(Id)); -} - -/// For debug use only; semantics are not guaranteed. -/// -/// \returns the string resulting from calling the node's print() method. -StencilPart dPrint(llvm::StringRef Id); -} // namespace stencil -} // namespace tooling -} // namespace clang -#endif // LLVM_CLANG_TOOLING_REFACTOR_STENCIL_H_ diff --git a/include/clang/Tooling/Refactoring/Transformer.h b/include/clang/Tooling/Refactoring/Transformer.h deleted file mode 100644 index 6d9c5a37cc18f..0000000000000 --- a/include/clang/Tooling/Refactoring/Transformer.h +++ /dev/null @@ -1,308 +0,0 @@ -//===--- Transformer.h - Clang source-rewriting library ---------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// Defines a library supporting the concise specification of clang-based -/// source-to-source transformations. -/// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_CLANG_TOOLING_REFACTOR_TRANSFORMER_H_ -#define LLVM_CLANG_TOOLING_REFACTOR_TRANSFORMER_H_ - -#include "clang/ASTMatchers/ASTMatchFinder.h" -#include "clang/ASTMatchers/ASTMatchers.h" -#include "clang/ASTMatchers/ASTMatchersInternal.h" -#include "clang/Tooling/Refactoring/AtomicChange.h" -#include "clang/Tooling/Refactoring/RangeSelector.h" -#include "llvm/ADT/STLExtras.h" -#include "llvm/ADT/SmallVector.h" -#include "llvm/Support/Error.h" -#include -#include -#include -#include -#include - -namespace clang { -namespace tooling { - -// Note that \p TextGenerator is allowed to fail, e.g. when trying to access a -// matched node that was not bound. Allowing this to fail simplifies error -// handling for interactive tools like clang-query. -using TextGenerator = std::function( - const ast_matchers::MatchFinder::MatchResult &)>; - -/// Wraps a string as a TextGenerator. -inline TextGenerator text(std::string M) { - return [M](const ast_matchers::MatchFinder::MatchResult &) - -> Expected { return M; }; -} - -// Description of a source-code edit, expressed in terms of an AST node. -// Includes: an ID for the (bound) node, a selector for source related to the -// node, a replacement and, optionally, an explanation for the edit. -// -// * Target: the source code impacted by the rule. This identifies an AST node, -// or part thereof (\c Part), whose source range indicates the extent of the -// replacement applied by the replacement term. By default, the extent is the -// node matched by the pattern term (\c NodePart::Node). Target's are typed -// (\c Kind), which guides the determination of the node extent. -// -// * Replacement: a function that produces a replacement string for the target, -// based on the match result. -// -// * Note: (optional) a note specifically for this edit, potentially referencing -// elements of the match. This will be displayed to the user, where possible; -// for example, in clang-tidy diagnostics. Use of notes should be rare -- -// explanations of the entire rewrite should be set in the rule -// (`RewriteRule::Explanation`) instead. Notes serve the rare cases wherein -// edit-specific diagnostics are required. -// -// `ASTEdit` should be built using the `change` convenience functions. For -// example, -// \code -// change(name(fun), text("Frodo")) -// \endcode -// Or, if we use Stencil for the TextGenerator: -// \code -// using stencil::cat; -// change(statement(thenNode), cat("{", thenNode, "}")) -// change(callArgs(call), cat(x, ",", y)) -// \endcode -// Or, if you are changing the node corresponding to the rule's matcher, you can -// use the single-argument override of \c change: -// \code -// change(cat("different_expr")) -// \endcode -struct ASTEdit { - RangeSelector TargetRange; - TextGenerator Replacement; - TextGenerator Note; -}; - -/// Format of the path in an include directive -- angle brackets or quotes. -enum class IncludeFormat { - Quoted, - Angled, -}; - -/// Description of a source-code transformation. -// -// A *rewrite rule* describes a transformation of source code. A simple rule -// contains each of the following components: -// -// * Matcher: the pattern term, expressed as clang matchers (with Transformer -// extensions). -// -// * Edits: a set of Edits to the source code, described with ASTEdits. -// -// * Explanation: explanation of the rewrite. This will be displayed to the -// user, where possible; for example, in clang-tidy diagnostics. -// -// However, rules can also consist of (sub)rules, where the first that matches -// is applied and the rest are ignored. So, the above components are gathered -// as a `Case` and a rule is a list of cases. -// -// Rule cases have an additional, implicit, component: the parameters. These are -// portions of the pattern which are left unspecified, yet bound in the pattern -// so that we can reference them in the edits. -// -// The \c Transformer class can be used to apply the rewrite rule and obtain the -// corresponding replacements. -struct RewriteRule { - struct Case { - ast_matchers::internal::DynTypedMatcher Matcher; - SmallVector Edits; - TextGenerator Explanation; - // Include paths to add to the file affected by this case. These are - // bundled with the `Case`, rather than the `RewriteRule`, because each case - // might have different associated changes to the includes. - std::vector> AddedIncludes; - }; - // We expect RewriteRules will most commonly include only one case. - SmallVector Cases; - - // ID used as the default target of each match. The node described by the - // matcher is should always be bound to this id. - static constexpr llvm::StringLiteral RootID = "___root___"; -}; - -/// Convenience function for constructing a simple \c RewriteRule. -RewriteRule makeRule(ast_matchers::internal::DynTypedMatcher M, - SmallVector Edits, - TextGenerator Explanation = nullptr); - -/// Convenience overload of \c makeRule for common case of only one edit. -inline RewriteRule makeRule(ast_matchers::internal::DynTypedMatcher M, - ASTEdit Edit, - TextGenerator Explanation = nullptr) { - SmallVector Edits; - Edits.emplace_back(std::move(Edit)); - return makeRule(std::move(M), std::move(Edits), std::move(Explanation)); -} - -/// For every case in Rule, adds an include directive for the given header. The -/// common use is assumed to be a rule with only one case. For example, to -/// replace a function call and add headers corresponding to the new code, one -/// could write: -/// \code -/// auto R = makeRule(callExpr(callee(functionDecl(hasName("foo")))), -/// change(text("bar()"))); -/// AddInclude(R, "path/to/bar_header.h"); -/// AddInclude(R, "vector", IncludeFormat::Angled); -/// \endcode -void addInclude(RewriteRule &Rule, llvm::StringRef Header, - IncludeFormat Format = IncludeFormat::Quoted); - -/// Applies the first rule whose pattern matches; other rules are ignored. -/// -/// N.B. All of the rules must use the same kind of matcher (that is, share a -/// base class in the AST hierarchy). However, this constraint is caused by an -/// implementation detail and should be lifted in the future. -// -// `applyFirst` is like an `anyOf` matcher with an edit action attached to each -// of its cases. Anywhere you'd use `anyOf(m1.bind("id1"), m2.bind("id2"))` and -// then dispatch on those ids in your code for control flow, `applyFirst` lifts -// that behavior to the rule level. So, you can write `applyFirst({makeRule(m1, -// action1), makeRule(m2, action2), ...});` -// -// For example, consider a type `T` with a deterministic serialization function, -// `serialize()`. For performance reasons, we would like to make it -// non-deterministic. Therefore, we want to drop the expectation that -// `a.serialize() = b.serialize() iff a = b` (although we'll maintain -// `deserialize(a.serialize()) = a`). -// -// We have three cases to consider (for some equality function, `eq`): -// ``` -// eq(a.serialize(), b.serialize()) --> eq(a,b) -// eq(a, b.serialize()) --> eq(deserialize(a), b) -// eq(a.serialize(), b) --> eq(a, deserialize(b)) -// ``` -// -// `applyFirst` allows us to specify each independently: -// ``` -// auto eq_fun = functionDecl(...); -// auto method_call = cxxMemberCallExpr(...); -// -// auto two_calls = callExpr(callee(eq_fun), hasArgument(0, method_call), -// hasArgument(1, method_call)); -// auto left_call = -// callExpr(callee(eq_fun), callExpr(hasArgument(0, method_call))); -// auto right_call = -// callExpr(callee(eq_fun), callExpr(hasArgument(1, method_call))); -// -// RewriteRule R = applyFirst({makeRule(two_calls, two_calls_action), -// makeRule(left_call, left_call_action), -// makeRule(right_call, right_call_action)}); -// ``` -RewriteRule applyFirst(ArrayRef Rules); - -/// Replaces a portion of the source text with \p Replacement. -ASTEdit change(RangeSelector Target, TextGenerator Replacement); - -/// Replaces the entirety of a RewriteRule's match with \p Replacement. For -/// example, to replace a function call, one could write: -/// \code -/// makeRule(callExpr(callee(functionDecl(hasName("foo")))), -/// change(text("bar()"))) -/// \endcode -inline ASTEdit change(TextGenerator Replacement) { - return change(node(RewriteRule::RootID), std::move(Replacement)); -} - -/// Inserts \p Replacement before \p S, leaving the source selected by \S -/// unchanged. -inline ASTEdit insertBefore(RangeSelector S, TextGenerator Replacement) { - return change(before(std::move(S)), std::move(Replacement)); -} - -/// Inserts \p Replacement after \p S, leaving the source selected by \S -/// unchanged. -inline ASTEdit insertAfter(RangeSelector S, TextGenerator Replacement) { - return change(after(std::move(S)), std::move(Replacement)); -} - -/// Removes the source selected by \p S. -inline ASTEdit remove(RangeSelector S) { - return change(std::move(S), text("")); -} - -/// The following three functions are a low-level part of the RewriteRule -/// API. We expose them for use in implementing the fixtures that interpret -/// RewriteRule, like Transformer and TransfomerTidy, or for more advanced -/// users. -// -// FIXME: These functions are really public, if advanced, elements of the -// RewriteRule API. Recast them as such. Or, just declare these functions -// public and well-supported and move them out of `detail`. -namespace detail { -/// Builds a single matcher for the rule, covering all of the rule's cases. -ast_matchers::internal::DynTypedMatcher buildMatcher(const RewriteRule &Rule); - -/// Returns the \c Case of \c Rule that was selected in the match result. -/// Assumes a matcher built with \c buildMatcher. -const RewriteRule::Case & -findSelectedCase(const ast_matchers::MatchFinder::MatchResult &Result, - const RewriteRule &Rule); - -/// A source "transformation," represented by a character range in the source to -/// be replaced and a corresponding replacement string. -struct Transformation { - CharSourceRange Range; - std::string Replacement; -}; - -/// Attempts to translate `Edits`, which are in terms of AST nodes bound in the -/// match `Result`, into Transformations, which are in terms of the source code -/// text. -/// -/// Returns an empty vector if any of the edits apply to portions of the source -/// that are ineligible for rewriting (certain interactions with macros, for -/// example). Fails if any invariants are violated relating to bound nodes in -/// the match. However, it does not fail in the case of conflicting edits -- -/// conflict handling is left to clients. We recommend use of the \c -/// AtomicChange or \c Replacements classes for assistance in detecting such -/// conflicts. -Expected> -translateEdits(const ast_matchers::MatchFinder::MatchResult &Result, - llvm::ArrayRef Edits); -} // namespace detail - -/// Handles the matcher and callback registration for a single rewrite rule, as -/// defined by the arguments of the constructor. -class Transformer : public ast_matchers::MatchFinder::MatchCallback { -public: - using ChangeConsumer = - std::function Change)>; - - /// \param Consumer Receives each rewrite or error. Will not necessarily be - /// called for each match; for example, if the rewrite is not applicable - /// because of macros, but doesn't fail. Note that clients are responsible - /// for handling the case that independent \c AtomicChanges conflict with each - /// other. - Transformer(RewriteRule Rule, ChangeConsumer Consumer) - : Rule(std::move(Rule)), Consumer(std::move(Consumer)) {} - - /// N.B. Passes `this` pointer to `MatchFinder`. So, this object should not - /// be moved after this call. - void registerMatchers(ast_matchers::MatchFinder *MatchFinder); - - /// Not called directly by users -- called by the framework, via base class - /// pointer. - void run(const ast_matchers::MatchFinder::MatchResult &Result) override; - -private: - RewriteRule Rule; - /// Receives each successful rewrites as an \c AtomicChange. - ChangeConsumer Consumer; -}; -} // namespace tooling -} // namespace clang - -#endif // LLVM_CLANG_TOOLING_REFACTOR_TRANSFORMER_H_ diff --git a/include/clang/Tooling/StandaloneExecution.h b/include/clang/Tooling/StandaloneExecution.h index 5fbc1e479c59b..8db6229acf7f9 100644 --- a/include/clang/Tooling/StandaloneExecution.h +++ b/include/clang/Tooling/StandaloneExecution.h @@ -52,8 +52,6 @@ public: StringRef getExecutorName() const override { return ExecutorName; } - bool isSingleProcess() const override { return true; } - using ToolExecutor::execute; llvm::Error diff --git a/include/clang/Tooling/Syntax/Tokens.h b/include/clang/Tooling/Syntax/Tokens.h index 4640ccb2d30aa..301432d3888b3 100644 --- a/include/clang/Tooling/Syntax/Tokens.h +++ b/include/clang/Tooling/Syntax/Tokens.h @@ -236,6 +236,16 @@ public: /// #pragma, etc. llvm::ArrayRef spelledTokens(FileID FID) const; + /// Get all tokens that expand a macro in \p FID. For the following input + /// #define FOO B + /// #define FOO2(X) int X + /// FOO2(XY) + /// int B; + /// FOO; + /// macroExpansions() returns {"FOO2", "FOO"} (from line 3 and 5 + /// respecitvely). + std::vector macroExpansions(FileID FID) const; + const SourceManager &sourceManager() const { return *SourceMgr; } std::string dumpForTests() const; diff --git a/include/clang/Tooling/Tooling.h b/include/clang/Tooling/Tooling.h index 83fe43ac59e02..19421f0a39f30 100644 --- a/include/clang/Tooling/Tooling.h +++ b/include/clang/Tooling/Tooling.h @@ -99,9 +99,7 @@ public: DiagnosticConsumer *DiagConsumer) override; /// Returns a new clang::FrontendAction. - /// - /// The caller takes ownership of the returned action. - virtual FrontendAction *create() = 0; + virtual std::unique_ptr create() = 0; }; /// Returns a new FrontendActionFactory for a given type. @@ -156,7 +154,7 @@ inline std::unique_ptr newFrontendActionFactory( /// clang modules. /// /// \return - True if 'ToolAction' was successfully executed. -bool runToolOnCode(FrontendAction *ToolAction, const Twine &Code, +bool runToolOnCode(std::unique_ptr ToolAction, const Twine &Code, const Twine &FileName = "input.cc", std::shared_ptr PCHContainerOps = std::make_shared()); @@ -179,7 +177,7 @@ using FileContentMappings = std::vector>; /// /// \return - True if 'ToolAction' was successfully executed. bool runToolOnCodeWithArgs( - FrontendAction *ToolAction, const Twine &Code, + std::unique_ptr ToolAction, const Twine &Code, const std::vector &Args, const Twine &FileName = "input.cc", const Twine &ToolName = "clang-tool", std::shared_ptr PCHContainerOps = @@ -188,7 +186,7 @@ bool runToolOnCodeWithArgs( // Similar to the overload except this takes a VFS. bool runToolOnCodeWithArgs( - FrontendAction *ToolAction, const Twine &Code, + std::unique_ptr ToolAction, const Twine &Code, llvm::IntrusiveRefCntPtr VFS, const std::vector &Args, const Twine &FileName = "input.cc", const Twine &ToolName = "clang-tool", @@ -237,13 +235,13 @@ public: /// uses its binary name (CommandLine[0]) to locate its builtin headers. /// Callers have to ensure that they are installed in a compatible location /// (see clang driver implementation) or mapped in via mapVirtualFile. - /// \param FAction The action to be executed. Class takes ownership. + /// \param FAction The action to be executed. /// \param Files The FileManager used for the execution. Class does not take /// ownership. /// \param PCHContainerOps The PCHContainerOperations for loading and creating /// clang modules. - ToolInvocation(std::vector CommandLine, FrontendAction *FAction, - FileManager *Files, + ToolInvocation(std::vector CommandLine, + std::unique_ptr FAction, FileManager *Files, std::shared_ptr PCHContainerOps = std::make_shared()); @@ -314,12 +312,15 @@ public: /// clang modules. /// \param BaseFS VFS used for all underlying file accesses when running the /// tool. + /// \param Files The file manager to use for underlying file operations when + /// running the tool. ClangTool(const CompilationDatabase &Compilations, ArrayRef SourcePaths, std::shared_ptr PCHContainerOps = std::make_shared(), IntrusiveRefCntPtr BaseFS = - llvm::vfs::getRealFileSystem()); + llvm::vfs::getRealFileSystem(), + IntrusiveRefCntPtr Files = nullptr); ~ClangTool(); @@ -397,7 +398,9 @@ template std::unique_ptr newFrontendActionFactory() { class SimpleFrontendActionFactory : public FrontendActionFactory { public: - FrontendAction *create() override { return new T; } + std::unique_ptr create() override { + return std::make_unique(); + } }; return std::unique_ptr( @@ -413,8 +416,9 @@ inline std::unique_ptr newFrontendActionFactory( SourceFileCallbacks *Callbacks) : ConsumerFactory(ConsumerFactory), Callbacks(Callbacks) {} - FrontendAction *create() override { - return new ConsumerFactoryAdaptor(ConsumerFactory, Callbacks); + std::unique_ptr create() override { + return std::make_unique(ConsumerFactory, + Callbacks); } private: diff --git a/include/clang/Tooling/Transformer/MatchConsumer.h b/include/clang/Tooling/Transformer/MatchConsumer.h new file mode 100644 index 0000000000000..0a1dbe13ea1e4 --- /dev/null +++ b/include/clang/Tooling/Transformer/MatchConsumer.h @@ -0,0 +1,62 @@ +//===--- MatchConsumer.h - MatchConsumer abstraction ------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file This file defines the *MatchConsumer* abstraction: a computation over +/// match results, specifically the `ast_matchers::MatchFinder::MatchResult` +/// class. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLING_TRANSFORMER_MATCH_CONSUMER_H_ +#define LLVM_CLANG_TOOLING_TRANSFORMER_MATCH_CONSUMER_H_ + +#include "clang/AST/ASTTypeTraits.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/Error.h" + +namespace clang { +namespace transformer { +/// A failable computation over nodes bound by AST matchers. +/// +/// The computation should report any errors though its return value (rather +/// than terminating the program) to enable usage in interactive scenarios like +/// clang-query. +/// +/// This is a central abstraction of the Transformer framework. +template +using MatchConsumer = + std::function(const ast_matchers::MatchFinder::MatchResult &)>; + +/// Creates an error that signals that a `MatchConsumer` expected a certain node +/// to be bound by AST matchers, but it was not actually bound. +inline llvm::Error notBoundError(llvm::StringRef Id) { + return llvm::make_error(llvm::errc::invalid_argument, + "Id not bound: " + Id); +} + +/// Chooses between the two consumers, based on whether \p ID is bound in the +/// match. +template +MatchConsumer ifBound(std::string ID, MatchConsumer TrueC, + MatchConsumer FalseC) { + return [=](const ast_matchers::MatchFinder::MatchResult &Result) { + auto &Map = Result.Nodes.getMap(); + return (Map.find(ID) != Map.end() ? TrueC : FalseC)(Result); + }; +} +} // namespace transformer + +namespace tooling { +// DEPRECATED: Temporary alias supporting client migration to the `transformer` +// namespace. +using transformer::ifBound; +} // namespace tooling +} // namespace clang +#endif // LLVM_CLANG_TOOLING_TRANSFORMER_MATCH_CONSUMER_H_ diff --git a/include/clang/Tooling/Transformer/RangeSelector.h b/include/clang/Tooling/Transformer/RangeSelector.h new file mode 100644 index 0000000000000..9f556d206321c --- /dev/null +++ b/include/clang/Tooling/Transformer/RangeSelector.h @@ -0,0 +1,113 @@ +//===--- RangeSelector.h - Source-selection library ---------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Defines a combinator library supporting the definition of _selectors_, +/// which select source ranges based on (bound) AST nodes. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLING_REFACTOR_RANGE_SELECTOR_H_ +#define LLVM_CLANG_TOOLING_REFACTOR_RANGE_SELECTOR_H_ + +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Tooling/Transformer/MatchConsumer.h" +#include "llvm/Support/Error.h" +#include +#include + +namespace clang { +namespace transformer { +using RangeSelector = MatchConsumer; + +inline RangeSelector charRange(CharSourceRange R) { + return [R](const ast_matchers::MatchFinder::MatchResult &) + -> Expected { return R; }; +} + +/// Selects from the start of \p Begin and to the end of \p End. +RangeSelector range(RangeSelector Begin, RangeSelector End); + +/// Convenience version of \c range where end-points are bound nodes. +RangeSelector range(std::string BeginID, std::string EndID); + +/// Selects the (empty) range [B,B) when \p Selector selects the range [B,E). +RangeSelector before(RangeSelector Selector); + +/// Selects the the point immediately following \p Selector. That is, the +/// (empty) range [E,E), when \p Selector selects either +/// * the CharRange [B,E) or +/// * the TokenRange [B,E'] where the token at E' spans the range [E,E'). +RangeSelector after(RangeSelector Selector); + +/// Selects a node, including trailing semicolon (for non-expression +/// statements). \p ID is the node's binding in the match result. +RangeSelector node(std::string ID); + +/// Selects a node, including trailing semicolon (always). Useful for selecting +/// expression statements. \p ID is the node's binding in the match result. +RangeSelector statement(std::string ID); + +/// Given a \c MemberExpr, selects the member token. \p ID is the node's +/// binding in the match result. +RangeSelector member(std::string ID); + +/// Given a node with a "name", (like \c NamedDecl, \c DeclRefExpr or \c +/// CxxCtorInitializer) selects the name's token. Only selects the final +/// identifier of a qualified name, but not any qualifiers or template +/// arguments. For example, for `::foo::bar::baz` and `::foo::bar::baz`, +/// it selects only `baz`. +/// +/// \param ID is the node's binding in the match result. +RangeSelector name(std::string ID); + +// Given a \c CallExpr (bound to \p ID), selects the arguments' source text (all +// source between the call's parentheses). +RangeSelector callArgs(std::string ID); + +// Given a \c CompoundStmt (bound to \p ID), selects the source of the +// statements (all source between the braces). +RangeSelector statements(std::string ID); + +// Given a \c InitListExpr (bound to \p ID), selects the range of the elements +// (all source between the braces). +RangeSelector initListElements(std::string ID); + +/// Given an \IfStmt (bound to \p ID), selects the range of the else branch, +/// starting from the \c else keyword. +RangeSelector elseBranch(std::string ID); + +/// Selects the range from which `S` was expanded (possibly along with other +/// source), if `S` is an expansion, and `S` itself, otherwise. Corresponds to +/// `SourceManager::getExpansionRange`. +RangeSelector expansion(RangeSelector S); +} // namespace transformer + +namespace tooling { +// DEPRECATED: These are temporary aliases supporting client migration to the +// `transformer` namespace. +using RangeSelector = transformer::RangeSelector; + +using transformer::after; +using transformer::before; +using transformer::callArgs; +using transformer::charRange; +using transformer::elseBranch; +using transformer::expansion; +using transformer::initListElements; +using transformer::member; +using transformer::name; +using transformer::node; +using transformer::range; +using transformer::statement; +using transformer::statements; +} // namespace tooling +} // namespace clang + +#endif // LLVM_CLANG_TOOLING_REFACTOR_RANGE_SELECTOR_H_ diff --git a/include/clang/Tooling/Transformer/RewriteRule.h b/include/clang/Tooling/Transformer/RewriteRule.h new file mode 100644 index 0000000000000..6e99151c1c7f5 --- /dev/null +++ b/include/clang/Tooling/Transformer/RewriteRule.h @@ -0,0 +1,309 @@ +//===--- RewriteRule.h - RewriteRule class ----------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Defines the RewriteRule class and related functions for creating, +/// modifying and interpreting RewriteRules. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLING_TRANSFORMER_REWRITE_RULE_H_ +#define LLVM_CLANG_TOOLING_TRANSFORMER_REWRITE_RULE_H_ + +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/ASTMatchers/ASTMatchers.h" +#include "clang/ASTMatchers/ASTMatchersInternal.h" +#include "clang/Tooling/Refactoring/AtomicChange.h" +#include "clang/Tooling/Transformer/MatchConsumer.h" +#include "clang/Tooling/Transformer/RangeSelector.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/Support/Error.h" +#include +#include +#include + +namespace clang { +namespace transformer { +using TextGenerator = MatchConsumer; + +// Description of a source-code edit, expressed in terms of an AST node. +// Includes: an ID for the (bound) node, a selector for source related to the +// node, a replacement and, optionally, an explanation for the edit. +// +// * Target: the source code impacted by the rule. This identifies an AST node, +// or part thereof (\c Part), whose source range indicates the extent of the +// replacement applied by the replacement term. By default, the extent is the +// node matched by the pattern term (\c NodePart::Node). Target's are typed +// (\c Kind), which guides the determination of the node extent. +// +// * Replacement: a function that produces a replacement string for the target, +// based on the match result. +// +// * Note: (optional) a note specifically for this edit, potentially referencing +// elements of the match. This will be displayed to the user, where possible; +// for example, in clang-tidy diagnostics. Use of notes should be rare -- +// explanations of the entire rewrite should be set in the rule +// (`RewriteRule::Explanation`) instead. Notes serve the rare cases wherein +// edit-specific diagnostics are required. +// +// `ASTEdit` should be built using the `change` convenience functions. For +// example, +// \code +// change(name(fun), text("Frodo")) +// \endcode +// Or, if we use Stencil for the TextGenerator: +// \code +// using stencil::cat; +// change(statement(thenNode), cat("{", thenNode, "}")) +// change(callArgs(call), cat(x, ",", y)) +// \endcode +// Or, if you are changing the node corresponding to the rule's matcher, you can +// use the single-argument override of \c change: +// \code +// change(cat("different_expr")) +// \endcode +struct ASTEdit { + RangeSelector TargetRange; + TextGenerator Replacement; + TextGenerator Note; +}; + +/// Format of the path in an include directive -- angle brackets or quotes. +enum class IncludeFormat { + Quoted, + Angled, +}; + +/// Description of a source-code transformation. +// +// A *rewrite rule* describes a transformation of source code. A simple rule +// contains each of the following components: +// +// * Matcher: the pattern term, expressed as clang matchers (with Transformer +// extensions). +// +// * Edits: a set of Edits to the source code, described with ASTEdits. +// +// * Explanation: explanation of the rewrite. This will be displayed to the +// user, where possible; for example, in clang-tidy diagnostics. +// +// However, rules can also consist of (sub)rules, where the first that matches +// is applied and the rest are ignored. So, the above components are gathered +// as a `Case` and a rule is a list of cases. +// +// Rule cases have an additional, implicit, component: the parameters. These are +// portions of the pattern which are left unspecified, yet bound in the pattern +// so that we can reference them in the edits. +// +// The \c Transformer class can be used to apply the rewrite rule and obtain the +// corresponding replacements. +struct RewriteRule { + struct Case { + ast_matchers::internal::DynTypedMatcher Matcher; + SmallVector Edits; + TextGenerator Explanation; + // Include paths to add to the file affected by this case. These are + // bundled with the `Case`, rather than the `RewriteRule`, because each case + // might have different associated changes to the includes. + std::vector> AddedIncludes; + }; + // We expect RewriteRules will most commonly include only one case. + SmallVector Cases; + + // ID used as the default target of each match. The node described by the + // matcher is should always be bound to this id. + static constexpr llvm::StringLiteral RootID = "___root___"; +}; + +/// Convenience function for constructing a simple \c RewriteRule. +RewriteRule makeRule(ast_matchers::internal::DynTypedMatcher M, + SmallVector Edits, + TextGenerator Explanation = nullptr); + +/// Convenience overload of \c makeRule for common case of only one edit. +inline RewriteRule makeRule(ast_matchers::internal::DynTypedMatcher M, + ASTEdit Edit, + TextGenerator Explanation = nullptr) { + SmallVector Edits; + Edits.emplace_back(std::move(Edit)); + return makeRule(std::move(M), std::move(Edits), std::move(Explanation)); +} + +/// For every case in Rule, adds an include directive for the given header. The +/// common use is assumed to be a rule with only one case. For example, to +/// replace a function call and add headers corresponding to the new code, one +/// could write: +/// \code +/// auto R = makeRule(callExpr(callee(functionDecl(hasName("foo")))), +/// change(text("bar()"))); +/// AddInclude(R, "path/to/bar_header.h"); +/// AddInclude(R, "vector", IncludeFormat::Angled); +/// \endcode +void addInclude(RewriteRule &Rule, llvm::StringRef Header, + IncludeFormat Format = IncludeFormat::Quoted); + +/// Applies the first rule whose pattern matches; other rules are ignored. If +/// the matchers are independent then order doesn't matter. In that case, +/// `applyFirst` is simply joining the set of rules into one. +// +// `applyFirst` is like an `anyOf` matcher with an edit action attached to each +// of its cases. Anywhere you'd use `anyOf(m1.bind("id1"), m2.bind("id2"))` and +// then dispatch on those ids in your code for control flow, `applyFirst` lifts +// that behavior to the rule level. So, you can write `applyFirst({makeRule(m1, +// action1), makeRule(m2, action2), ...});` +// +// For example, consider a type `T` with a deterministic serialization function, +// `serialize()`. For performance reasons, we would like to make it +// non-deterministic. Therefore, we want to drop the expectation that +// `a.serialize() = b.serialize() iff a = b` (although we'll maintain +// `deserialize(a.serialize()) = a`). +// +// We have three cases to consider (for some equality function, `eq`): +// ``` +// eq(a.serialize(), b.serialize()) --> eq(a,b) +// eq(a, b.serialize()) --> eq(deserialize(a), b) +// eq(a.serialize(), b) --> eq(a, deserialize(b)) +// ``` +// +// `applyFirst` allows us to specify each independently: +// ``` +// auto eq_fun = functionDecl(...); +// auto method_call = cxxMemberCallExpr(...); +// +// auto two_calls = callExpr(callee(eq_fun), hasArgument(0, method_call), +// hasArgument(1, method_call)); +// auto left_call = +// callExpr(callee(eq_fun), callExpr(hasArgument(0, method_call))); +// auto right_call = +// callExpr(callee(eq_fun), callExpr(hasArgument(1, method_call))); +// +// RewriteRule R = applyFirst({makeRule(two_calls, two_calls_action), +// makeRule(left_call, left_call_action), +// makeRule(right_call, right_call_action)}); +// ``` +RewriteRule applyFirst(ArrayRef Rules); + +/// Replaces a portion of the source text with \p Replacement. +ASTEdit change(RangeSelector Target, TextGenerator Replacement); + +/// Replaces the entirety of a RewriteRule's match with \p Replacement. For +/// example, to replace a function call, one could write: +/// \code +/// makeRule(callExpr(callee(functionDecl(hasName("foo")))), +/// change(text("bar()"))) +/// \endcode +inline ASTEdit change(TextGenerator Replacement) { + return change(node(RewriteRule::RootID), std::move(Replacement)); +} + +/// Inserts \p Replacement before \p S, leaving the source selected by \S +/// unchanged. +inline ASTEdit insertBefore(RangeSelector S, TextGenerator Replacement) { + return change(before(std::move(S)), std::move(Replacement)); +} + +/// Inserts \p Replacement after \p S, leaving the source selected by \S +/// unchanged. +inline ASTEdit insertAfter(RangeSelector S, TextGenerator Replacement) { + return change(after(std::move(S)), std::move(Replacement)); +} + +/// Removes the source selected by \p S. +inline ASTEdit remove(RangeSelector S) { + return change(std::move(S), + [](const ast_matchers::MatchFinder::MatchResult &) + -> Expected { return ""; }); +} + +/// The following three functions are a low-level part of the RewriteRule +/// API. We expose them for use in implementing the fixtures that interpret +/// RewriteRule, like Transformer and TransfomerTidy, or for more advanced +/// users. +// +// FIXME: These functions are really public, if advanced, elements of the +// RewriteRule API. Recast them as such. Or, just declare these functions +// public and well-supported and move them out of `detail`. +namespace detail { +/// Builds a single matcher for the rule, covering all of the rule's cases. +/// Only supports Rules whose cases' matchers share the same base "kind" +/// (`Stmt`, `Decl`, etc.) Deprecated: use `buildMatchers` instead, which +/// supports mixing matchers of different kinds. +ast_matchers::internal::DynTypedMatcher buildMatcher(const RewriteRule &Rule); + +/// Builds a set of matchers that cover the rule (one for each distinct node +/// matcher base kind: Stmt, Decl, etc.). Node-matchers for `QualType` and +/// `Type` are not permitted, since such nodes carry no source location +/// information and are therefore not relevant for rewriting. If any such +/// matchers are included, will return an empty vector. +std::vector +buildMatchers(const RewriteRule &Rule); + +/// Gets the beginning location of the source matched by a rewrite rule. If the +/// match occurs within a macro expansion, returns the beginning of the +/// expansion point. `Result` must come from the matching of a rewrite rule. +SourceLocation +getRuleMatchLoc(const ast_matchers::MatchFinder::MatchResult &Result); + +/// Returns the \c Case of \c Rule that was selected in the match result. +/// Assumes a matcher built with \c buildMatcher. +const RewriteRule::Case & +findSelectedCase(const ast_matchers::MatchFinder::MatchResult &Result, + const RewriteRule &Rule); + +/// A source "transformation," represented by a character range in the source to +/// be replaced and a corresponding replacement string. +struct Transformation { + CharSourceRange Range; + std::string Replacement; +}; + +/// Attempts to translate `Edits`, which are in terms of AST nodes bound in the +/// match `Result`, into Transformations, which are in terms of the source code +/// text. +/// +/// Returns an empty vector if any of the edits apply to portions of the source +/// that are ineligible for rewriting (certain interactions with macros, for +/// example). Fails if any invariants are violated relating to bound nodes in +/// the match. However, it does not fail in the case of conflicting edits -- +/// conflict handling is left to clients. We recommend use of the \c +/// AtomicChange or \c Replacements classes for assistance in detecting such +/// conflicts. +Expected> +translateEdits(const ast_matchers::MatchFinder::MatchResult &Result, + llvm::ArrayRef Edits); +} // namespace detail +} // namespace transformer + +namespace tooling { +// DEPRECATED: These are temporary aliases supporting client migration to the +// `transformer` namespace. +/// Wraps a string as a TextGenerator. +using TextGenerator = transformer::TextGenerator; + +inline TextGenerator text(std::string M) { + return [M](const ast_matchers::MatchFinder::MatchResult &) + -> Expected { return M; }; +} + +using transformer::addInclude; +using transformer::applyFirst; +using transformer::change; +using transformer::insertAfter; +using transformer::insertBefore; +using transformer::makeRule; +using transformer::remove; +using transformer::RewriteRule; +using transformer::IncludeFormat; +namespace detail { +using namespace transformer::detail; +} // namespace detail +} // namespace tooling +} // namespace clang + +#endif // LLVM_CLANG_TOOLING_TRANSFORMER_REWRITE_RULE_H_ diff --git a/include/clang/Tooling/Transformer/SourceCode.h b/include/clang/Tooling/Transformer/SourceCode.h new file mode 100644 index 0000000000000..bc9cc3d2a2580 --- /dev/null +++ b/include/clang/Tooling/Transformer/SourceCode.h @@ -0,0 +1,90 @@ +//===--- SourceCode.h - Source code manipulation routines -------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file provides functions that simplify extraction of source code. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLING_TRANSFORMER_SOURCE_CODE_H +#define LLVM_CLANG_TOOLING_TRANSFORMER_SOURCE_CODE_H + +#include "clang/AST/ASTContext.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Basic/TokenKinds.h" + +namespace clang { +namespace tooling { + +/// Extends \p Range to include the token \p Next, if it immediately follows the +/// end of the range. Otherwise, returns \p Range unchanged. +CharSourceRange maybeExtendRange(CharSourceRange Range, tok::TokenKind Next, + ASTContext &Context); + +/// Returns the source range spanning the node, extended to include \p Next, if +/// it immediately follows \p Node. Otherwise, returns the normal range of \p +/// Node. See comments on `getExtendedText()` for examples. +template +CharSourceRange getExtendedRange(const T &Node, tok::TokenKind Next, + ASTContext &Context) { + return maybeExtendRange(CharSourceRange::getTokenRange(Node.getSourceRange()), + Next, Context); +} + +/// Returns the source-code text in the specified range. +StringRef getText(CharSourceRange Range, const ASTContext &Context); + +/// Returns the source-code text corresponding to \p Node. +template +StringRef getText(const T &Node, const ASTContext &Context) { + return getText(CharSourceRange::getTokenRange(Node.getSourceRange()), + Context); +} + +/// Returns the source text of the node, extended to include \p Next, if it +/// immediately follows the node. Otherwise, returns the text of just \p Node. +/// +/// For example, given statements S1 and S2 below: +/// \code +/// { +/// // S1: +/// if (!x) return foo(); +/// // S2: +/// if (!x) { return 3; } +/// } +/// \endcode +/// then +/// \code +/// getText(S1, Context) = "if (!x) return foo()" +/// getExtendedText(S1, tok::TokenKind::semi, Context) +/// = "if (!x) return foo();" +/// getExtendedText(*S1.getThen(), tok::TokenKind::semi, Context) +/// = "return foo();" +/// getExtendedText(*S2.getThen(), tok::TokenKind::semi, Context) +/// = getText(S2, Context) = "{ return 3; }" +/// \endcode +template +StringRef getExtendedText(const T &Node, tok::TokenKind Next, + ASTContext &Context) { + return getText(getExtendedRange(Node, Next, Context), Context); +} + +// Attempts to resolve the given range to one that can be edited by a rewrite; +// generally, one that starts and ends within a particular file. It supports +// a limited set of cases involving source locations in macro expansions. +llvm::Optional +getRangeForEdit(const CharSourceRange &EditRange, const SourceManager &SM, + const LangOptions &LangOpts); + +inline llvm::Optional +getRangeForEdit(const CharSourceRange &EditRange, const ASTContext &Context) { + return getRangeForEdit(EditRange, Context.getSourceManager(), + Context.getLangOpts()); +} +} // namespace tooling +} // namespace clang +#endif // LLVM_CLANG_TOOLING_TRANSFORMER_SOURCE_CODE_H diff --git a/include/clang/Tooling/Transformer/SourceCodeBuilders.h b/include/clang/Tooling/Transformer/SourceCodeBuilders.h new file mode 100644 index 0000000000000..6c79a7588f28d --- /dev/null +++ b/include/clang/Tooling/Transformer/SourceCodeBuilders.h @@ -0,0 +1,86 @@ +//===--- SourceCodeBuilders.h - Source-code building facilities -*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file collects facilities for generating source code strings. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLING_TRANSFORMER_SOURCE_CODE_BUILDERS_H_ +#define LLVM_CLANG_TOOLING_TRANSFORMER_SOURCE_CODE_BUILDERS_H_ + +#include "clang/AST/ASTContext.h" +#include "clang/AST/Expr.h" +#include + +namespace clang { +namespace tooling { + +/// \name Code analysis utilities. +/// @{ +/// Ignores implicit object-construction expressions in addition to the normal +/// implicit expressions that are ignored. +const Expr *reallyIgnoreImplicit(const Expr &E); + +/// Determines whether printing this expression in *any* expression requires +/// parentheses to preserve its meaning. This analyses is necessarily +/// conservative because it lacks information about the target context. +bool mayEverNeedParens(const Expr &E); + +/// Determines whether printing this expression to the left of a dot or arrow +/// operator requires a parentheses to preserve its meaning. Given that +/// dot/arrow are (effectively) the highest precedence, this is equivalent to +/// asking whether it ever needs parens. +inline bool needParensBeforeDotOrArrow(const Expr &E) { + return mayEverNeedParens(E); +} + +/// Determines whether printing this expression to the right of a unary operator +/// requires a parentheses to preserve its meaning. +bool needParensAfterUnaryOperator(const Expr &E); +/// @} + +/// \name Basic code-string generation utilities. +/// @{ + +/// Builds source for an expression, adding parens if needed for unambiguous +/// parsing. +llvm::Optional buildParens(const Expr &E, + const ASTContext &Context); + +/// Builds idiomatic source for the dereferencing of `E`: prefix with `*` but +/// simplify when it already begins with `&`. \returns empty string on failure. +llvm::Optional buildDereference(const Expr &E, + const ASTContext &Context); + +/// Builds idiomatic source for taking the address of `E`: prefix with `&` but +/// simplify when it already begins with `*`. \returns empty string on failure. +llvm::Optional buildAddressOf(const Expr &E, + const ASTContext &Context); + +/// Adds a dot to the end of the given expression, but adds parentheses when +/// needed by the syntax, and simplifies to `->` when possible, e.g.: +/// +/// `x` becomes `x.` +/// `*a` becomes `a->` +/// `a+b` becomes `(a+b).` +llvm::Optional buildDot(const Expr &E, const ASTContext &Context); + +/// Adds an arrow to the end of the given expression, but adds parentheses +/// when needed by the syntax, and simplifies to `.` when possible, e.g.: +/// +/// `x` becomes `x->` +/// `&a` becomes `a.` +/// `a+b` becomes `(a+b)->` +llvm::Optional buildArrow(const Expr &E, + const ASTContext &Context); +/// @} + +} // namespace tooling +} // namespace clang +#endif // LLVM_CLANG_TOOLING_TRANSFORMER_SOURCE_CODE_BUILDERS_H_ diff --git a/include/clang/Tooling/Transformer/Stencil.h b/include/clang/Tooling/Transformer/Stencil.h new file mode 100644 index 0000000000000..66d1388f97106 --- /dev/null +++ b/include/clang/Tooling/Transformer/Stencil.h @@ -0,0 +1,221 @@ +//===--- Stencil.h - Stencil class ------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file defines the *Stencil* abstraction: a code-generating object, +/// parameterized by named references to (bound) AST nodes. Given a match +/// result, a stencil can be evaluated to a string of source code. +/// +/// A stencil is similar in spirit to a format string: it is composed of a +/// series of raw text strings, references to nodes (the parameters) and helper +/// code-generation operations. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLING_TRANSFORMER_STENCIL_H_ +#define LLVM_CLANG_TOOLING_TRANSFORMER_STENCIL_H_ + +#include "clang/AST/ASTContext.h" +#include "clang/AST/ASTTypeTraits.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Tooling/Transformer/MatchConsumer.h" +#include "clang/Tooling/Transformer/RangeSelector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Error.h" +#include +#include + +namespace clang { +namespace transformer { +/// A stencil is represented as a sequence of "parts" that can each individually +/// generate a code string based on a match result. The different kinds of +/// parts include (raw) text, references to bound nodes and assorted operations +/// on bound nodes. +/// +/// Users can create custom Stencil operations by implementing this interface. +class StencilPartInterface { +public: + virtual ~StencilPartInterface() = default; + + /// Evaluates this part to a string and appends it to \c Result. \c Result is + /// undefined in the case of an error. + virtual llvm::Error eval(const ast_matchers::MatchFinder::MatchResult &Match, + std::string *Result) const = 0; + + /// Constructs a string representation of the StencilPart. StencilParts + /// generated by the `selection` and `run` functions do not have a unique + /// string representation. + virtual std::string toString() const = 0; + +protected: + StencilPartInterface() = default; + + // Since this is an abstract class, copying/assigning only make sense for + // derived classes implementing `clone()`. + StencilPartInterface(const StencilPartInterface &) = default; + StencilPartInterface &operator=(const StencilPartInterface &) = default; +}; + +/// A copyable facade for a std::unique_ptr. Copies result +/// in a copy of the underlying pointee object. +class StencilPart { +public: + explicit StencilPart(std::shared_ptr Impl) + : Impl(std::move(Impl)) {} + + /// See `StencilPartInterface::eval()`. + llvm::Error eval(const ast_matchers::MatchFinder::MatchResult &Match, + std::string *Result) const { + return Impl->eval(Match, Result); + } + + std::string toString() const { + if (Impl == nullptr) + return ""; + return Impl->toString(); + } + +private: + std::shared_ptr Impl; +}; + +/// A sequence of code fragments, references to parameters and code-generation +/// operations that together can be evaluated to (a fragment of) source code, +/// given a match result. +class Stencil { +public: + Stencil() = default; + + /// Composes a stencil from a series of parts. + template static Stencil cat(Ts &&... Parts) { + Stencil S; + S.Parts = {wrap(std::forward(Parts))...}; + return S; + } + + /// Appends data from a \p OtherStencil to this stencil. + void append(Stencil OtherStencil); + + // Evaluates the stencil given a match result. Requires that the nodes in the + // result includes any ids referenced in the stencil. References to missing + // nodes will result in an invalid_argument error. + llvm::Expected + eval(const ast_matchers::MatchFinder::MatchResult &Match) const; + + // Allow Stencils to operate as std::function, for compatibility with + // Transformer's TextGenerator. + llvm::Expected + operator()(const ast_matchers::MatchFinder::MatchResult &Result) const { + return eval(Result); + } + + /// Constructs a string representation of the Stencil. The string is not + /// guaranteed to be unique. + std::string toString() const { + std::vector PartStrings; + PartStrings.reserve(Parts.size()); + for (const auto &Part : Parts) + PartStrings.push_back(Part.toString()); + return llvm::join(PartStrings, ", "); + } + +private: + static StencilPart wrap(llvm::StringRef Text); + static StencilPart wrap(RangeSelector Selector); + static StencilPart wrap(StencilPart Part) { return Part; } + + std::vector Parts; +}; + +// +// Functions for conveniently building stencils. +// + +/// Convenience wrapper for Stencil::cat that can be imported with a using decl. +template Stencil cat(Ts &&... Parts) { + return Stencil::cat(std::forward(Parts)...); +} + +/// \returns exactly the text provided. +StencilPart text(llvm::StringRef Text); + +/// \returns the source corresponding to the selected range. +StencilPart selection(RangeSelector Selector); + +/// Generates the source of the expression bound to \p Id, wrapping it in +/// parentheses if it may parse differently depending on context. For example, a +/// binary operation is always wrapped, while a variable reference is never +/// wrapped. +StencilPart expression(llvm::StringRef Id); + +/// Constructs an idiomatic dereferencing of the expression bound to \p ExprId. +/// \p ExprId is wrapped in parentheses, if needed. +StencilPart deref(llvm::StringRef ExprId); + +/// Constructs an expression that idiomatically takes the address of the +/// expression bound to \p ExprId. \p ExprId is wrapped in parentheses, if +/// needed. +StencilPart addressOf(llvm::StringRef ExprId); + +/// Constructs a `MemberExpr` that accesses the named member (\p Member) of the +/// object bound to \p BaseId. The access is constructed idiomatically: if \p +/// BaseId is bound to `e` and \p Member identifies member `m`, then returns +/// `e->m`, when e is a pointer, `e2->m` when e = `*e2` and `e.m` otherwise. +/// Additionally, `e` is wrapped in parentheses, if needed. +StencilPart access(llvm::StringRef BaseId, StencilPart Member); +inline StencilPart access(llvm::StringRef BaseId, llvm::StringRef Member) { + return access(BaseId, text(Member)); +} + +/// Chooses between the two stencil parts, based on whether \p ID is bound in +/// the match. +StencilPart ifBound(llvm::StringRef Id, StencilPart TruePart, + StencilPart FalsePart); + +/// Chooses between the two strings, based on whether \p ID is bound in the +/// match. +inline StencilPart ifBound(llvm::StringRef Id, llvm::StringRef TrueText, + llvm::StringRef FalseText) { + return ifBound(Id, text(TrueText), text(FalseText)); +} + +/// Wraps a MatchConsumer in a StencilPart, so that it can be used in a Stencil. +/// This supports user-defined extensions to the Stencil language. +StencilPart run(MatchConsumer C); + +/// For debug use only; semantics are not guaranteed. +/// +/// \returns the string resulting from calling the node's print() method. +StencilPart dPrint(llvm::StringRef Id); +} // namespace transformer + +namespace tooling { +// DEPRECATED: These are temporary aliases supporting client migration to the +// `transformer` namespace. +using Stencil = transformer::Stencil; +using StencilPart = transformer::StencilPart; +namespace stencil { +using transformer::access; +using transformer::addressOf; +using transformer::cat; +using transformer::deref; +using transformer::dPrint; +using transformer::expression; +using transformer::ifBound; +using transformer::run; +using transformer::selection; +using transformer::text; +/// \returns the source corresponding to the identified node. +/// FIXME: Deprecated. Write `selection(node(Id))` instead. +inline transformer::StencilPart node(llvm::StringRef Id) { + return selection(tooling::node(Id)); +} +} // namespace stencil +} // namespace tooling +} // namespace clang +#endif // LLVM_CLANG_TOOLING_TRANSFORMER_STENCIL_H_ diff --git a/include/clang/Tooling/Transformer/Transformer.h b/include/clang/Tooling/Transformer/Transformer.h new file mode 100644 index 0000000000000..31feacba5e286 --- /dev/null +++ b/include/clang/Tooling/Transformer/Transformer.h @@ -0,0 +1,52 @@ +//===--- Transformer.h - Transformer class ----------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLING_TRANSFORMER_TRANSFORMER_H_ +#define LLVM_CLANG_TOOLING_TRANSFORMER_TRANSFORMER_H_ + +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Tooling/Refactoring/AtomicChange.h" +#include "clang/Tooling/Transformer/RewriteRule.h" +#include "llvm/Support/Error.h" +#include +#include + +namespace clang { +namespace tooling { +/// Handles the matcher and callback registration for a single `RewriteRule`, as +/// defined by the arguments of the constructor. +class Transformer : public ast_matchers::MatchFinder::MatchCallback { +public: + using ChangeConsumer = + std::function Change)>; + + /// \param Consumer Receives each rewrite or error. Will not necessarily be + /// called for each match; for example, if the rewrite is not applicable + /// because of macros, but doesn't fail. Note that clients are responsible + /// for handling the case that independent \c AtomicChanges conflict with each + /// other. + Transformer(transformer::RewriteRule Rule, ChangeConsumer Consumer) + : Rule(std::move(Rule)), Consumer(std::move(Consumer)) {} + + /// N.B. Passes `this` pointer to `MatchFinder`. So, this object should not + /// be moved after this call. + void registerMatchers(ast_matchers::MatchFinder *MatchFinder); + + /// Not called directly by users -- called by the framework, via base class + /// pointer. + void run(const ast_matchers::MatchFinder::MatchResult &Result) override; + +private: + transformer::RewriteRule Rule; + /// Receives each successful rewrites as an \c AtomicChange. + ChangeConsumer Consumer; +}; +} // namespace tooling +} // namespace clang + +#endif // LLVM_CLANG_TOOLING_TRANSFORMER_TRANSFORMER_H_ diff --git a/include/clang/module.modulemap b/include/clang/module.modulemap index 1f32ffe0c1598..2cbe865bce879 100644 --- a/include/clang/module.modulemap +++ b/include/clang/module.modulemap @@ -18,9 +18,9 @@ module Clang_AST { umbrella "AST" textual header "AST/BuiltinTypes.def" + textual header "AST/CXXRecordDeclDefinitionBits.def" textual header "AST/OperationKinds.def" textual header "AST/TypeLocNodes.def" - textual header "AST/TypeNodes.def" module * { export * } } @@ -31,9 +31,11 @@ module Clang_Basic { requires cplusplus umbrella "Basic" + textual header "Basic/AArch64SVEACLETypes.def" textual header "Basic/BuiltinsAArch64.def" textual header "Basic/BuiltinsAMDGPU.def" textual header "Basic/BuiltinsARM.def" + textual header "Basic/BuiltinsBPF.def" textual header "Basic/Builtins.def" textual header "Basic/BuiltinsHexagon.def" textual header "Basic/BuiltinsLe64.def" @@ -100,7 +102,7 @@ module Clang_Frontend { requires cplusplus umbrella "Frontend" - textual header "Frontend/LangStandards.def" + textual header "Basic/LangStandards.def" module * { export * } } diff --git a/lib/ARCMigrate/ARCMT.cpp b/lib/ARCMigrate/ARCMT.cpp index 568e06f21fba6..a9018c1c4bdf7 100644 --- a/lib/ARCMigrate/ARCMT.cpp +++ b/lib/ARCMigrate/ARCMT.cpp @@ -139,7 +139,7 @@ public: } // Non-ARC warnings are ignored. - Diags.setLastDiagnosticIgnored(); + Diags.setLastDiagnosticIgnored(true); } }; @@ -453,8 +453,8 @@ public: std::unique_ptr CreateASTConsumer(CompilerInstance &CI, StringRef InFile) override { CI.getPreprocessor().addPPCallbacks( - llvm::make_unique(ARCMTMacroLocs)); - return llvm::make_unique(); + std::make_unique(ARCMTMacroLocs)); + return std::make_unique(); } }; diff --git a/lib/ARCMigrate/FileRemapper.cpp b/lib/ARCMigrate/FileRemapper.cpp index 1a4862d09aa6c..a031fe22ac132 100644 --- a/lib/ARCMigrate/FileRemapper.cpp +++ b/lib/ARCMigrate/FileRemapper.cpp @@ -78,26 +78,26 @@ bool FileRemapper::initFromFile(StringRef filePath, DiagnosticsEngine &Diag, Diag); StringRef toFilename = lines[idx+2]; - const FileEntry *origFE = FileMgr->getFile(fromFilename); + llvm::ErrorOr origFE = FileMgr->getFile(fromFilename); if (!origFE) { if (ignoreIfFilesChanged) continue; return report("File does not exist: " + fromFilename, Diag); } - const FileEntry *newFE = FileMgr->getFile(toFilename); + llvm::ErrorOr newFE = FileMgr->getFile(toFilename); if (!newFE) { if (ignoreIfFilesChanged) continue; return report("File does not exist: " + toFilename, Diag); } - if ((uint64_t)origFE->getModificationTime() != timeModified) { + if ((uint64_t)(*origFE)->getModificationTime() != timeModified) { if (ignoreIfFilesChanged) continue; return report("File was modified: " + fromFilename, Diag); } - pairs.push_back(std::make_pair(origFE, newFE)); + pairs.push_back(std::make_pair(*origFE, *newFE)); } for (unsigned i = 0, e = pairs.size(); i != e; ++i) @@ -121,7 +121,7 @@ bool FileRemapper::flushToFile(StringRef outputPath, DiagnosticsEngine &Diag) { std::error_code EC; std::string infoFile = outputPath; - llvm::raw_fd_ostream infoOut(infoFile, EC, llvm::sys::fs::F_None); + llvm::raw_fd_ostream infoOut(infoFile, EC, llvm::sys::fs::OF_None); if (EC) return report(EC.message(), Diag); @@ -152,9 +152,11 @@ bool FileRemapper::flushToFile(StringRef outputPath, DiagnosticsEngine &Diag) { newOut.write(mem->getBufferStart(), mem->getBufferSize()); newOut.close(); - const FileEntry *newE = FileMgr->getFile(tempPath); - remap(origFE, newE); - infoOut << newE->getName() << '\n'; + auto newE = FileMgr->getFile(tempPath); + if (newE) { + remap(origFE, *newE); + infoOut << (*newE)->getName() << '\n'; + } } } @@ -175,7 +177,7 @@ bool FileRemapper::overwriteOriginal(DiagnosticsEngine &Diag, Diag); std::error_code EC; - llvm::raw_fd_ostream Out(origFE->getName(), EC, llvm::sys::fs::F_None); + llvm::raw_fd_ostream Out(origFE->getName(), EC, llvm::sys::fs::OF_None); if (EC) return report(EC.message(), Diag); @@ -224,7 +226,9 @@ void FileRemapper::remap(const FileEntry *file, const FileEntry *newfile) { } const FileEntry *FileRemapper::getOriginalFile(StringRef filePath) { - const FileEntry *file = FileMgr->getFile(filePath); + const FileEntry *file = nullptr; + if (auto fileOrErr = FileMgr->getFile(filePath)) + file = *fileOrErr; // If we are updating a file that overridden an original file, // actually update the original file. llvm::DenseMap::iterator diff --git a/lib/ARCMigrate/ObjCMT.cpp b/lib/ARCMigrate/ObjCMT.cpp index 7126a0873ea0c..4abb04fef5b85 100644 --- a/lib/ARCMigrate/ObjCMT.cpp +++ b/lib/ARCMigrate/ObjCMT.cpp @@ -208,10 +208,10 @@ ObjCMigrateAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { CI.getPreprocessor().addPPCallbacks(std::unique_ptr(PPRec)); std::vector> Consumers; Consumers.push_back(WrapperFrontendAction::CreateASTConsumer(CI, InFile)); - Consumers.push_back(llvm::make_unique( + Consumers.push_back(std::make_unique( MigrateDir, ObjCMigAction, Remapper, CompInst->getFileManager(), PPRec, CompInst->getPreprocessor(), false, None)); - return llvm::make_unique(std::move(Consumers)); + return std::make_unique(std::move(Consumers)); } bool ObjCMigrateAction::BeginInvocation(CompilerInstance &CI) { @@ -1951,7 +1951,7 @@ void ObjCMigrateASTConsumer::HandleTranslationUnit(ASTContext &Ctx) { if (IsOutputFile) { std::error_code EC; - llvm::raw_fd_ostream OS(MigrateDir, EC, llvm::sys::fs::F_None); + llvm::raw_fd_ostream OS(MigrateDir, EC, llvm::sys::fs::OF_None); if (EC) { DiagnosticsEngine &Diags = Ctx.getDiagnostics(); Diags.Report(Diags.getCustomDiagID(DiagnosticsEngine::Error, "%0")) @@ -2034,7 +2034,7 @@ MigrateSourceAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { CI.getPreprocessor().addPPCallbacks(std::unique_ptr(PPRec)); std::vector WhiteList = getWhiteListFilenames(CI.getFrontendOpts().ObjCMTWhiteListPath); - return llvm::make_unique( + return std::make_unique( CI.getFrontendOpts().OutputFile, ObjCMTAction, Remapper, CI.getFileManager(), PPRec, CI.getPreprocessor(), /*isOutputFile=*/true, WhiteList); @@ -2141,10 +2141,11 @@ private: StringRef Val = ValueString->getValue(ValueStorage); if (Key == "file") { - const FileEntry *FE = FileMgr.getFile(Val); - if (!FE) + auto FE = FileMgr.getFile(Val); + if (FE) + Entry.File = *FE; + else Ignore = true; - Entry.File = FE; } else if (Key == "offset") { if (Val.getAsInteger(10, Entry.Offset)) Ignore = true; diff --git a/lib/ARCMigrate/PlistReporter.cpp b/lib/ARCMigrate/PlistReporter.cpp index 6d7fcb053b482..d01563b2974dd 100644 --- a/lib/ARCMigrate/PlistReporter.cpp +++ b/lib/ARCMigrate/PlistReporter.cpp @@ -56,7 +56,7 @@ void arcmt::writeARCDiagsToPlist(const std::string &outPath, } std::error_code EC; - llvm::raw_fd_ostream o(outPath, EC, llvm::sys::fs::F_Text); + llvm::raw_fd_ostream o(outPath, EC, llvm::sys::fs::OF_Text); if (EC) { llvm::errs() << "error: could not create file: " << outPath << '\n'; return; diff --git a/lib/AST/APValue.cpp b/lib/AST/APValue.cpp index 1993bba9bd1a1..50f8d05dacb4f 100644 --- a/lib/AST/APValue.cpp +++ b/lib/AST/APValue.cpp @@ -42,6 +42,14 @@ APValue::LValueBase::LValueBase(const ValueDecl *P, unsigned I, unsigned V) APValue::LValueBase::LValueBase(const Expr *P, unsigned I, unsigned V) : Ptr(P), Local{I, V} {} +APValue::LValueBase APValue::LValueBase::getDynamicAlloc(DynamicAllocLValue LV, + QualType Type) { + LValueBase Base; + Base.Ptr = LV; + Base.DynamicAllocType = Type.getAsOpaquePtr(); + return Base; +} + APValue::LValueBase APValue::LValueBase::getTypeInfo(TypeInfoLValue LV, QualType TypeInfo) { LValueBase Base; @@ -51,11 +59,12 @@ APValue::LValueBase APValue::LValueBase::getTypeInfo(TypeInfoLValue LV, } unsigned APValue::LValueBase::getCallIndex() const { - return is() ? 0 : Local.CallIndex; + return (is() || is()) ? 0 + : Local.CallIndex; } unsigned APValue::LValueBase::getVersion() const { - return is() ? 0 : Local.Version; + return (is() || is()) ? 0 : Local.Version; } QualType APValue::LValueBase::getTypeInfoType() const { @@ -63,6 +72,11 @@ QualType APValue::LValueBase::getTypeInfoType() const { return QualType::getFromOpaquePtr(TypeInfoType); } +QualType APValue::LValueBase::getDynamicAllocType() const { + assert(is() && "not a dynamic allocation lvalue"); + return QualType::getFromOpaquePtr(DynamicAllocType); +} + namespace clang { bool operator==(const APValue::LValueBase &LHS, const APValue::LValueBase &RHS) { @@ -111,7 +125,7 @@ llvm::DenseMapInfo::getTombstoneKey() { namespace clang { llvm::hash_code hash_value(const APValue::LValueBase &Base) { - if (Base.is()) + if (Base.is() || Base.is()) return llvm::hash_value(Base.getOpaqueValue()); return llvm::hash_combine(Base.getOpaqueValue(), Base.getCallIndex(), Base.getVersion()); @@ -479,7 +493,7 @@ void APValue::printPretty(raw_ostream &Out, const ASTContext &Ctx, return; case APValue::Vector: { Out << '{'; - QualType ElemTy = Ty->getAs()->getElementType(); + QualType ElemTy = Ty->castAs()->getElementType(); getVectorElt(0).printPretty(Out, Ctx, ElemTy); for (unsigned i = 1; i != getVectorLength(); ++i) { Out << ", "; @@ -528,13 +542,18 @@ void APValue::printPretty(raw_ostream &Out, const ASTContext &Ctx, S = CharUnits::One(); } Out << '&'; - } else if (!IsReference) + } else if (!IsReference) { Out << '&'; + } if (const ValueDecl *VD = Base.dyn_cast()) Out << *VD; else if (TypeInfoLValue TI = Base.dyn_cast()) { TI.print(Out, Ctx.getPrintingPolicy()); + } else if (DynamicAllocLValue DA = Base.dyn_cast()) { + Out << "{*new " + << Base.getDynamicAllocType().stream(Ctx.getPrintingPolicy()) << "#" + << DA.getIndex() << "}"; } else { assert(Base.get() != nullptr && "Expecting non-null Expr"); @@ -563,10 +582,17 @@ void APValue::printPretty(raw_ostream &Out, const ASTContext &Ctx, } else if (TypeInfoLValue TI = Base.dyn_cast()) { TI.print(Out, Ctx.getPrintingPolicy()); ElemTy = Base.getTypeInfoType(); + } else if (DynamicAllocLValue DA = Base.dyn_cast()) { + Out << "{*new " + << Base.getDynamicAllocType().stream(Ctx.getPrintingPolicy()) << "#" + << DA.getIndex() << "}"; + ElemTy = Base.getDynamicAllocType(); } else { const Expr *E = Base.get(); assert(E != nullptr && "Expecting non-null Expr"); E->printPretty(Out, nullptr, Ctx.getPrintingPolicy()); + // FIXME: This is wrong if E is a MaterializeTemporaryExpr with an lvalue + // adjustment. ElemTy = E->getType(); } @@ -626,7 +652,7 @@ void APValue::printPretty(raw_ostream &Out, const ASTContext &Ctx, } case APValue::Struct: { Out << '{'; - const RecordDecl *RD = Ty->getAs()->getDecl(); + const RecordDecl *RD = Ty->castAs()->getDecl(); bool First = true; if (unsigned N = getStructNumBases()) { const CXXRecordDecl *CD = cast(RD); diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 0d69eb90abaf6..cda51ec755a8d 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -12,6 +12,7 @@ #include "clang/AST/ASTContext.h" #include "CXXABI.h" +#include "Interp/Context.h" #include "clang/AST/APValue.h" #include "clang/AST/ASTMutationListener.h" #include "clang/AST/ASTTypeTraits.h" @@ -98,62 +99,60 @@ enum FloatingRank { Float16Rank, HalfRank, FloatRank, DoubleRank, LongDoubleRank, Float128Rank }; -RawComment *ASTContext::getRawCommentForDeclNoCache(const Decl *D) const { +/// \returns location that is relevant when searching for Doc comments related +/// to \p D. +static SourceLocation getDeclLocForCommentSearch(const Decl *D, + SourceManager &SourceMgr) { assert(D); - // If we already tried to load comments but there are none, - // we won't find anything. - if (CommentsLoaded && Comments.getComments().empty()) - return nullptr; - // User can not attach documentation to implicit declarations. if (D->isImplicit()) - return nullptr; + return {}; // User can not attach documentation to implicit instantiations. if (const auto *FD = dyn_cast(D)) { if (FD->getTemplateSpecializationKind() == TSK_ImplicitInstantiation) - return nullptr; + return {}; } if (const auto *VD = dyn_cast(D)) { if (VD->isStaticDataMember() && VD->getTemplateSpecializationKind() == TSK_ImplicitInstantiation) - return nullptr; + return {}; } if (const auto *CRD = dyn_cast(D)) { if (CRD->getTemplateSpecializationKind() == TSK_ImplicitInstantiation) - return nullptr; + return {}; } if (const auto *CTSD = dyn_cast(D)) { TemplateSpecializationKind TSK = CTSD->getSpecializationKind(); if (TSK == TSK_ImplicitInstantiation || TSK == TSK_Undeclared) - return nullptr; + return {}; } if (const auto *ED = dyn_cast(D)) { if (ED->getTemplateSpecializationKind() == TSK_ImplicitInstantiation) - return nullptr; + return {}; } if (const auto *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 nullptr; + return {}; } // TODO: handle comments for function parameters properly. if (isa(D)) - return nullptr; + return {}; // TODO: we could look up template parameter documentation in the template // documentation. if (isa(D) || isa(D) || isa(D)) - return nullptr; + return {}; // Find declaration location. // For Objective-C declarations we generally don't expect to have multiple @@ -161,20 +160,19 @@ RawComment *ASTContext::getRawCommentForDeclNoCache(const Decl *D) const { // location". // For all other declarations multiple declarators are used quite frequently, // so we use the location of the identifier as the "declaration location". - SourceLocation DeclLoc; if (isa(D) || isa(D) || isa(D) || isa(D) || isa(D)) - DeclLoc = D->getBeginLoc(); + return D->getBeginLoc(); else { - DeclLoc = D->getLocation(); + const SourceLocation DeclLoc = D->getLocation(); if (DeclLoc.isMacroID()) { if (isa(D)) { // If location of the typedef name is in a macro, it is because being // declared via a macro. Try using declaration's starting location as // the "declaration location". - DeclLoc = D->getBeginLoc(); + return D->getBeginLoc(); } else if (const auto *TD = dyn_cast(D)) { // If location of the tag decl is inside a macro, but the spelling of // the tag name comes from a macro argument, it looks like a special @@ -183,102 +181,73 @@ RawComment *ASTContext::getRawCommentForDeclNoCache(const Decl *D) const { // attach the comment to the tag decl. if (SourceMgr.isMacroArgExpansion(DeclLoc) && TD->isCompleteDefinition()) - DeclLoc = SourceMgr.getExpansionLoc(DeclLoc); + return SourceMgr.getExpansionLoc(DeclLoc); } } + return DeclLoc; } + return {}; +} + +RawComment *ASTContext::getRawCommentForDeclNoCacheImpl( + const Decl *D, const SourceLocation RepresentativeLocForDecl, + const std::map &CommentsInTheFile) const { // If the declaration doesn't map directly to a location in a file, we // can't find the comment. - if (DeclLoc.isInvalid() || !DeclLoc.isFileID()) + if (RepresentativeLocForDecl.isInvalid() || + !RepresentativeLocForDecl.isFileID()) return nullptr; - if (!CommentsLoaded && ExternalSource) { - ExternalSource->ReadComments(); - -#ifndef NDEBUG - ArrayRef RawComments = Comments.getComments(); - assert(std::is_sorted(RawComments.begin(), RawComments.end(), - BeforeThanCompare(SourceMgr))); -#endif - - CommentsLoaded = true; - } - - ArrayRef RawComments = Comments.getComments(); // If there are no comments anywhere, we won't find anything. - if (RawComments.empty()) + if (CommentsInTheFile.empty()) return nullptr; - // Find the comment that occurs just after this declaration. - ArrayRef::iterator Comment; - { - // When searching for comments during parsing, the comment we are looking - // for is usually among the last two comments we parsed -- check them - // first. - RawComment CommentAtDeclLoc( - SourceMgr, SourceRange(DeclLoc), LangOpts.CommentOpts, false); - BeforeThanCompare Compare(SourceMgr); - ArrayRef::iterator MaybeBeforeDecl = RawComments.end() - 1; - bool Found = Compare(*MaybeBeforeDecl, &CommentAtDeclLoc); - if (!Found && RawComments.size() >= 2) { - MaybeBeforeDecl--; - Found = Compare(*MaybeBeforeDecl, &CommentAtDeclLoc); - } - - if (Found) { - Comment = MaybeBeforeDecl + 1; - assert(Comment == - llvm::lower_bound(RawComments, &CommentAtDeclLoc, Compare)); - } else { - // Slow path. - Comment = llvm::lower_bound(RawComments, &CommentAtDeclLoc, Compare); - } - } - // Decompose the location for the declaration and find the beginning of the // file buffer. - std::pair DeclLocDecomp = SourceMgr.getDecomposedLoc(DeclLoc); + const std::pair DeclLocDecomp = + SourceMgr.getDecomposedLoc(RepresentativeLocForDecl); + + // Slow path. + auto OffsetCommentBehindDecl = + CommentsInTheFile.lower_bound(DeclLocDecomp.second); // First check whether we have a trailing comment. - if (Comment != RawComments.end() && - ((*Comment)->isDocumentation() || LangOpts.CommentOpts.ParseAllComments) - && (*Comment)->isTrailingComment() && - (isa(D) || isa(D) || isa(D) || - isa(D) || isa(D))) { - std::pair CommentBeginDecomp - = SourceMgr.getDecomposedLoc((*Comment)->getSourceRange().getBegin()); - // Check that Doxygen trailing comment comes after the declaration, starts - // on the same line and in the same file as the declaration. - if (DeclLocDecomp.first == CommentBeginDecomp.first && - SourceMgr.getLineNumber(DeclLocDecomp.first, DeclLocDecomp.second) - == SourceMgr.getLineNumber(CommentBeginDecomp.first, - CommentBeginDecomp.second)) { - (**Comment).setAttached(); - return *Comment; + if (OffsetCommentBehindDecl != CommentsInTheFile.end()) { + RawComment *CommentBehindDecl = OffsetCommentBehindDecl->second; + if ((CommentBehindDecl->isDocumentation() || + LangOpts.CommentOpts.ParseAllComments) && + CommentBehindDecl->isTrailingComment() && + (isa(D) || isa(D) || isa(D) || + isa(D) || isa(D))) { + + // Check that Doxygen trailing comment comes after the declaration, starts + // on the same line and in the same file as the declaration. + if (SourceMgr.getLineNumber(DeclLocDecomp.first, DeclLocDecomp.second) == + Comments.getCommentBeginLine(CommentBehindDecl, DeclLocDecomp.first, + OffsetCommentBehindDecl->first)) { + return CommentBehindDecl; + } } } // The comment just after the declaration was not a trailing comment. // Let's look at the previous comment. - if (Comment == RawComments.begin()) + if (OffsetCommentBehindDecl == CommentsInTheFile.begin()) return nullptr; - --Comment; + + auto OffsetCommentBeforeDecl = --OffsetCommentBehindDecl; + RawComment *CommentBeforeDecl = OffsetCommentBeforeDecl->second; // Check that we actually have a non-member Doxygen comment. - if (!((*Comment)->isDocumentation() || + if (!(CommentBeforeDecl->isDocumentation() || LangOpts.CommentOpts.ParseAllComments) || - (*Comment)->isTrailingComment()) + CommentBeforeDecl->isTrailingComment()) return nullptr; // Decompose the end of the comment. - std::pair CommentEndDecomp - = SourceMgr.getDecomposedLoc((*Comment)->getSourceRange().getEnd()); - - // If the comment and the declaration aren't in the same file, then they - // aren't related. - if (DeclLocDecomp.first != CommentEndDecomp.first) - return nullptr; + const unsigned CommentEndOffset = + Comments.getCommentEndOffset(CommentBeforeDecl); // Get the corresponding buffer. bool Invalid = false; @@ -288,26 +257,49 @@ RawComment *ASTContext::getRawCommentForDeclNoCache(const Decl *D) const { return nullptr; // Extract text between the comment and declaration. - StringRef Text(Buffer + CommentEndDecomp.second, - DeclLocDecomp.second - CommentEndDecomp.second); + StringRef Text(Buffer + CommentEndOffset, + DeclLocDecomp.second - CommentEndOffset); // There should be no other declarations or preprocessor directives between // comment and declaration. if (Text.find_first_of(";{}#@") != StringRef::npos) return nullptr; - (**Comment).setAttached(); - return *Comment; + return CommentBeforeDecl; +} + +RawComment *ASTContext::getRawCommentForDeclNoCache(const Decl *D) const { + const SourceLocation DeclLoc = getDeclLocForCommentSearch(D, SourceMgr); + + // If the declaration doesn't map directly to a location in a file, we + // can't find the comment. + if (DeclLoc.isInvalid() || !DeclLoc.isFileID()) + return nullptr; + + if (ExternalSource && !CommentsLoaded) { + ExternalSource->ReadComments(); + CommentsLoaded = true; + } + + if (Comments.empty()) + return nullptr; + + const FileID File = SourceMgr.getDecomposedLoc(DeclLoc).first; + const auto CommentsInThisFile = Comments.getCommentsInFile(File); + if (!CommentsInThisFile || CommentsInThisFile->empty()) + return nullptr; + + return getRawCommentForDeclNoCacheImpl(D, DeclLoc, *CommentsInThisFile); } /// If we have a 'templated' declaration for a template, adjust 'D' to /// refer to the actual template. /// If we have an implicit instantiation, adjust 'D' to refer to template. -static const Decl *adjustDeclToTemplate(const Decl *D) { - if (const auto *FD = dyn_cast(D)) { +static const Decl &adjustDeclToTemplate(const Decl &D) { + if (const auto *FD = dyn_cast(&D)) { // Is this function declaration part of a function template? if (const FunctionTemplateDecl *FTD = FD->getDescribedFunctionTemplate()) - return FTD; + return *FTD; // Nothing to do if function is not an implicit instantiation. if (FD->getTemplateSpecializationKind() != TSK_ImplicitInstantiation) @@ -315,28 +307,28 @@ static const Decl *adjustDeclToTemplate(const Decl *D) { // Function is an implicit instantiation of a function template? if (const FunctionTemplateDecl *FTD = FD->getPrimaryTemplate()) - return FTD; + return *FTD; // Function is instantiated from a member definition of a class template? if (const FunctionDecl *MemberDecl = FD->getInstantiatedFromMemberFunction()) - return MemberDecl; + return *MemberDecl; return D; } - if (const auto *VD = dyn_cast(D)) { + if (const auto *VD = dyn_cast(&D)) { // Static data member is instantiated from a member definition of a class // template? if (VD->isStaticDataMember()) if (const VarDecl *MemberDecl = VD->getInstantiatedFromStaticDataMember()) - return MemberDecl; + return *MemberDecl; return D; } - if (const auto *CRD = dyn_cast(D)) { + if (const auto *CRD = dyn_cast(&D)) { // Is this class declaration part of a class template? if (const ClassTemplateDecl *CTD = CRD->getDescribedClassTemplate()) - return CTD; + return *CTD; // Class is an implicit instantiation of a class template or partial // specialization? @@ -346,23 +338,23 @@ static const Decl *adjustDeclToTemplate(const Decl *D) { llvm::PointerUnion PU = CTSD->getSpecializedTemplateOrPartial(); - return PU.is() ? - static_cast(PU.get()) : - static_cast( - PU.get()); + return PU.is() + ? *static_cast(PU.get()) + : *static_cast( + PU.get()); } // Class is instantiated from a member definition of a class template? if (const MemberSpecializationInfo *Info = - CRD->getMemberSpecializationInfo()) - return Info->getInstantiatedFrom(); + CRD->getMemberSpecializationInfo()) + return *Info->getInstantiatedFrom(); return D; } - if (const auto *ED = dyn_cast(D)) { + if (const auto *ED = dyn_cast(&D)) { // Enum is instantiated from a member definition of a class template? if (const EnumDecl *MemberDecl = ED->getInstantiatedFromMemberEnum()) - return MemberDecl; + return *MemberDecl; return D; } @@ -373,72 +365,81 @@ static const Decl *adjustDeclToTemplate(const Decl *D) { const RawComment *ASTContext::getRawCommentForAnyRedecl( const Decl *D, const Decl **OriginalDecl) const { - D = adjustDeclToTemplate(D); + if (!D) { + if (OriginalDecl) + OriginalDecl = nullptr; + return nullptr; + } - // Check whether we have cached a comment for this declaration already. + D = &adjustDeclToTemplate(*D); + + // Any comment directly attached to D? { - llvm::DenseMap::iterator Pos = - RedeclComments.find(D); - if (Pos != RedeclComments.end()) { - const RawCommentAndCacheFlags &Raw = Pos->second; - if (Raw.getKind() != RawCommentAndCacheFlags::NoCommentInDecl) { - if (OriginalDecl) - *OriginalDecl = Raw.getOriginalDecl(); - return Raw.getRaw(); - } + auto DeclComment = DeclRawComments.find(D); + if (DeclComment != DeclRawComments.end()) { + if (OriginalDecl) + *OriginalDecl = D; + return DeclComment->second; } } - // Search for comments attached to declarations in the redeclaration chain. - const RawComment *RC = nullptr; - const Decl *OriginalDeclForRC = nullptr; - for (auto I : D->redecls()) { - llvm::DenseMap::iterator Pos = - RedeclComments.find(I); - if (Pos != RedeclComments.end()) { - const RawCommentAndCacheFlags &Raw = Pos->second; - if (Raw.getKind() != RawCommentAndCacheFlags::NoCommentInDecl) { - RC = Raw.getRaw(); - OriginalDeclForRC = Raw.getOriginalDecl(); - break; - } - } else { - RC = getRawCommentForDeclNoCache(I); - OriginalDeclForRC = I; - RawCommentAndCacheFlags Raw; - if (RC) { - // Call order swapped to work around ICE in VS2015 RTM (Release Win32) - // https://connect.microsoft.com/VisualStudio/feedback/details/1741530 - Raw.setKind(RawCommentAndCacheFlags::FromDecl); - Raw.setRaw(RC); - } else - Raw.setKind(RawCommentAndCacheFlags::NoCommentInDecl); - Raw.setOriginalDecl(I); - RedeclComments[I] = Raw; - if (RC) - break; + // Any comment attached to any redeclaration of D? + const Decl *CanonicalD = D->getCanonicalDecl(); + if (!CanonicalD) + return nullptr; + + { + auto RedeclComment = RedeclChainComments.find(CanonicalD); + if (RedeclComment != RedeclChainComments.end()) { + if (OriginalDecl) + *OriginalDecl = RedeclComment->second; + auto CommentAtRedecl = DeclRawComments.find(RedeclComment->second); + assert(CommentAtRedecl != DeclRawComments.end() && + "This decl is supposed to have comment attached."); + return CommentAtRedecl->second; } } - // If we found a comment, it should be a documentation comment. - assert(!RC || RC->isDocumentation() || LangOpts.CommentOpts.ParseAllComments); + // Any redeclarations of D that we haven't checked for comments yet? + // We can't use DenseMap::iterator directly since it'd get invalid. + auto LastCheckedRedecl = [this, CanonicalD]() -> const Decl * { + auto LookupRes = CommentlessRedeclChains.find(CanonicalD); + if (LookupRes != CommentlessRedeclChains.end()) + return LookupRes->second; + return nullptr; + }(); + + for (const auto Redecl : D->redecls()) { + assert(Redecl); + // Skip all redeclarations that have been checked previously. + if (LastCheckedRedecl) { + if (LastCheckedRedecl == Redecl) { + LastCheckedRedecl = nullptr; + } + continue; + } + const RawComment *RedeclComment = getRawCommentForDeclNoCache(Redecl); + if (RedeclComment) { + cacheRawCommentForDecl(*Redecl, *RedeclComment); + if (OriginalDecl) + *OriginalDecl = Redecl; + return RedeclComment; + } + CommentlessRedeclChains[CanonicalD] = Redecl; + } if (OriginalDecl) - *OriginalDecl = OriginalDeclForRC; - - // Update cache for every declaration in the redeclaration chain. - RawCommentAndCacheFlags Raw; - Raw.setRaw(RC); - Raw.setKind(RawCommentAndCacheFlags::FromRedecl); - Raw.setOriginalDecl(OriginalDeclForRC); - - for (auto I : D->redecls()) { - RawCommentAndCacheFlags &R = RedeclComments[I]; - if (R.getKind() == RawCommentAndCacheFlags::NoCommentInDecl) - R = Raw; - } + *OriginalDecl = nullptr; + return nullptr; +} - return RC; +void ASTContext::cacheRawCommentForDecl(const Decl &OriginalD, + const RawComment &Comment) const { + assert(Comment.isDocumentation() || LangOpts.CommentOpts.ParseAllComments); + DeclRawComments.try_emplace(&OriginalD, &Comment); + const Decl *const CanonicalDecl = OriginalD.getCanonicalDecl(); + RedeclChainComments.try_emplace(CanonicalDecl, &OriginalD); + CommentlessRedeclChains.erase(CanonicalDecl); } static void addRedeclaredMethods(const ObjCMethodDecl *ObjCMethod, @@ -458,6 +459,52 @@ static void addRedeclaredMethods(const ObjCMethodDecl *ObjCMethod, } } +void ASTContext::attachCommentsToJustParsedDecls(ArrayRef Decls, + const Preprocessor *PP) { + if (Comments.empty() || Decls.empty()) + return; + + // See if there are any new comments that are not attached to a decl. + // The location doesn't have to be precise - we care only about the file. + const FileID File = + SourceMgr.getDecomposedLoc((*Decls.begin())->getLocation()).first; + auto CommentsInThisFile = Comments.getCommentsInFile(File); + if (!CommentsInThisFile || CommentsInThisFile->empty() || + CommentsInThisFile->rbegin()->second->isAttached()) + return; + + // There is at least one comment not attached to a decl. + // Maybe it should be attached to one of Decls? + // + // Note that this way we pick up not only comments that precede the + // declaration, but also comments that *follow* the declaration -- thanks to + // the lookahead in the lexer: we've consumed the semicolon and looked + // ahead through comments. + + for (const Decl *D : Decls) { + assert(D); + if (D->isInvalidDecl()) + continue; + + D = &adjustDeclToTemplate(*D); + + const SourceLocation DeclLoc = getDeclLocForCommentSearch(D, SourceMgr); + + if (DeclLoc.isInvalid() || !DeclLoc.isFileID()) + continue; + + if (DeclRawComments.count(D) > 0) + continue; + + if (RawComment *const DocComment = + getRawCommentForDeclNoCacheImpl(D, DeclLoc, *CommentsInThisFile)) { + cacheRawCommentForDecl(*D, *DocComment); + comments::FullComment *FC = DocComment->parse(*this, PP, D); + ParsedComments[D->getCanonicalDecl()] = FC; + } + } +} + comments::FullComment *ASTContext::cloneFullComment(comments::FullComment *FC, const Decl *D) const { auto *ThisDeclInfo = new (*this) comments::DeclInfo; @@ -481,9 +528,9 @@ comments::FullComment *ASTContext::getLocalCommentForDeclUncached(const Decl *D) comments::FullComment *ASTContext::getCommentForDecl( const Decl *D, const Preprocessor *PP) const { - if (D->isInvalidDecl()) + if (!D || D->isInvalidDecl()) return nullptr; - D = adjustDeclToTemplate(D); + D = &adjustDeclToTemplate(*D); const Decl *Canonical = D->getCanonicalDecl(); llvm::DenseMap::iterator Pos = @@ -498,7 +545,7 @@ comments::FullComment *ASTContext::getCommentForDecl( return Pos->second; } - const Decl *OriginalDecl; + const Decl *OriginalDecl = nullptr; const RawComment *RC = getRawCommentForAnyRedecl(D, &OriginalDecl); if (!RC) { @@ -577,7 +624,7 @@ comments::FullComment *ASTContext::getCommentForDecl( // should parse the comment in context of that other Decl. This is important // because comments can contain references to parameter names which can be // different across redeclarations. - if (D != OriginalDecl) + if (D != OriginalDecl && OriginalDecl) return getCommentForDecl(OriginalDecl, PP); comments::FullComment *FC = RC->parse(*this, PP, D); @@ -691,7 +738,7 @@ ASTContext::getCanonicalTemplateTemplateParmDecl( cast(*P))); } - assert(!TTP->getRequiresClause() && + assert(!TTP->getTemplateParameters()->getRequiresClause() && "Unexpected requires-clause on template template-parameter"); Expr *const CanonRequiresClause = nullptr; @@ -737,6 +784,13 @@ CXXABI *ASTContext::createCXXABI(const TargetInfo &T) { llvm_unreachable("Invalid CXXABI type!"); } +interp::Context &ASTContext::getInterpContext() { + if (!InterpContext) { + InterpContext.reset(new interp::Context(*this)); + } + return *InterpContext.get(); +} + static const LangASMap *getAddressSpaceMap(const TargetInfo &T, const LangOptions &LOpts) { if (LOpts.FakeAddressSpaceMap) { @@ -775,7 +829,8 @@ static bool isAddrSpaceMapManglingEnabled(const TargetInfo &TI, ASTContext::ASTContext(LangOptions &LOpts, SourceManager &SM, IdentifierTable &idents, SelectorTable &sels, Builtin::Context &builtins) - : FunctionProtoTypes(this_()), TemplateSpecializationTypes(this_()), + : ConstantArrayTypes(this_()), FunctionProtoTypes(this_()), + TemplateSpecializationTypes(this_()), DependentTemplateSpecializationTypes(this_()), SubstTemplateTemplateParmPacks(this_()), SourceMgr(SM), LangOpts(LOpts), SanitizerBL(new SanitizerBlacklist(LangOpts.SanitizerBlacklistFiles, SM)), @@ -923,7 +978,7 @@ void ASTContext::PrintStats() const { unsigned counts[] = { #define TYPE(Name, Parent) 0, #define ABSTRACT_TYPE(Name, Parent) -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" 0 // Extra }; @@ -943,7 +998,7 @@ void ASTContext::PrintStats() const { TotalBytes += counts[Idx] * sizeof(Name##Type); \ ++Idx; #define ABSTRACT_TYPE(Name, Parent) -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" llvm::errs() << "Total bytes = " << TotalBytes << "\n"; @@ -1298,6 +1353,12 @@ void ASTContext::InitBuiltinTypes(const TargetInfo &Target, #include "clang/Basic/OpenCLExtensionTypes.def" } + if (Target.hasAArch64SVETypes()) { +#define SVE_TYPE(Name, Id, SingletonId) \ + InitBuiltinType(SingletonId, BuiltinType::Id); +#include "clang/Basic/AArch64SVEACLETypes.def" + } + // Builtin type for __objc_yes and __objc_no ObjCBuiltinBoolTy = (Target.useSignedCharForObjCBool() ? SignedCharTy : BoolTy); @@ -1515,10 +1576,9 @@ void ASTContext::addedLocalImportDecl(ImportDecl *Import) { /// getFloatTypeSemantics - Return the APFloat 'semantics' for the specified /// scalar floating point type. const llvm::fltSemantics &ASTContext::getFloatTypeSemantics(QualType T) const { - const auto *BT = T->getAs(); - assert(BT && "Not a floating point type!"); - switch (BT->getKind()) { - default: llvm_unreachable("Not a floating point type!"); + switch (T->castAs()->getKind()) { + default: + llvm_unreachable("Not a floating point type!"); case BuiltinType::Float16: case BuiltinType::Half: return Target->getHalfFormat(); @@ -1749,7 +1809,7 @@ TypeInfo ASTContext::getTypeInfoImpl(const Type *T) const { case Type::Class: \ assert(!T->isDependentType() && "should not see dependent types here"); \ return getTypeInfo(cast(T)->desugar().getTypePtr()); -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" llvm_unreachable("Should not see dependent types"); case Type::FunctionNoProto: @@ -1968,6 +2028,25 @@ TypeInfo ASTContext::getTypeInfoImpl(const Type *T) const { Width = Target->getPointerWidth(AS); Align = Target->getPointerAlign(AS); break; + // The SVE types are effectively target-specific. The length of an + // SVE_VECTOR_TYPE is only known at runtime, but it is always a multiple + // of 128 bits. There is one predicate bit for each vector byte, so the + // length of an SVE_PREDICATE_TYPE is always a multiple of 16 bits. + // + // Because the length is only known at runtime, we use a dummy value + // of 0 for the static length. The alignment values are those defined + // by the Procedure Call Standard for the Arm Architecture. +#define SVE_VECTOR_TYPE(Name, Id, SingletonId, ElKind, ElBits, IsSigned, IsFP)\ + case BuiltinType::Id: \ + Width = 0; \ + Align = 128; \ + break; +#define SVE_PREDICATE_TYPE(Name, Id, SingletonId, ElKind) \ + case BuiltinType::Id: \ + Width = 0; \ + Align = 16; \ + break; +#include "clang/Basic/AArch64SVEACLETypes.def" } break; case Type::ObjCObjectPointer: @@ -2364,7 +2443,7 @@ structHasUniqueObjectRepresentations(const ASTContext &Context, // have tail padding, so just make sure there isn't an error. if (!isStructEmpty(Base.getType())) { llvm::Optional Size = structHasUniqueObjectRepresentations( - Context, Base.getType()->getAs()->getDecl()); + Context, Base.getType()->castAs()->getDecl()); if (!Size) return llvm::None; Bases.emplace_back(Base.getType(), Size.getValue()); @@ -2455,7 +2534,7 @@ bool ASTContext::hasUniqueObjectRepresentations(QualType Ty) const { } if (Ty->isRecordType()) { - const RecordDecl *Record = Ty->getAs()->getDecl(); + const RecordDecl *Record = Ty->castAs()->getDecl(); if (Record->isInvalidDecl()) return false; @@ -2790,7 +2869,7 @@ QualType ASTContext::getFunctionTypeWithExceptionSpec( // Anything else must be a function type. Rebuild it with the new exception // specification. - const auto *Proto = Orig->getAs(); + const auto *Proto = Orig->castAs(); return getFunctionType( Proto->getReturnType(), Proto->getParamTypes(), Proto->getExtProtoInfo().withExceptionSpec(ESI)); @@ -3087,31 +3166,38 @@ QualType ASTContext::getMemberPointerType(QualType T, const Type *Cls) const { /// array of the specified element type. QualType ASTContext::getConstantArrayType(QualType EltTy, const llvm::APInt &ArySizeIn, + const Expr *SizeExpr, ArrayType::ArraySizeModifier ASM, unsigned IndexTypeQuals) const { assert((EltTy->isDependentType() || EltTy->isIncompleteType() || EltTy->isConstantSizeType()) && "Constant array of VLAs is illegal!"); + // We only need the size as part of the type if it's instantiation-dependent. + if (SizeExpr && !SizeExpr->isInstantiationDependent()) + SizeExpr = nullptr; + // Convert the array size into a canonical width matching the pointer size for // the target. llvm::APInt ArySize(ArySizeIn); ArySize = ArySize.zextOrTrunc(Target->getMaxPointerWidth()); llvm::FoldingSetNodeID ID; - ConstantArrayType::Profile(ID, EltTy, ArySize, ASM, IndexTypeQuals); + ConstantArrayType::Profile(ID, *this, EltTy, ArySize, SizeExpr, ASM, + IndexTypeQuals); void *InsertPos = nullptr; if (ConstantArrayType *ATP = ConstantArrayTypes.FindNodeOrInsertPos(ID, InsertPos)) return QualType(ATP, 0); - // If the element type isn't canonical or has qualifiers, this won't - // be a canonical type either, so fill in the canonical type field. + // If the element type isn't canonical or has qualifiers, or the array bound + // is instantiation-dependent, this won't be a canonical type either, so fill + // in the canonical type field. QualType Canon; - if (!EltTy.isCanonical() || EltTy.hasLocalQualifiers()) { + if (!EltTy.isCanonical() || EltTy.hasLocalQualifiers() || SizeExpr) { SplitQualType canonSplit = getCanonicalType(EltTy).split(); - Canon = getConstantArrayType(QualType(canonSplit.Ty, 0), ArySize, + Canon = getConstantArrayType(QualType(canonSplit.Ty, 0), ArySize, nullptr, ASM, IndexTypeQuals); Canon = getQualifiedType(Canon, canonSplit.Quals); @@ -3121,8 +3207,11 @@ QualType ASTContext::getConstantArrayType(QualType EltTy, assert(!NewIP && "Shouldn't be in the map!"); (void)NewIP; } - auto *New = new (*this,TypeAlignment) - ConstantArrayType(EltTy, Canon, ArySize, ASM, IndexTypeQuals); + void *Mem = Allocate( + ConstantArrayType::totalSizeToAlloc(SizeExpr ? 1 : 0), + TypeAlignment); + auto *New = new (Mem) + ConstantArrayType(EltTy, Canon, ArySize, SizeExpr, ASM, IndexTypeQuals); ConstantArrayTypes.InsertNode(New, InsertPos); Types.push_back(New); return QualType(New, 0); @@ -3143,7 +3232,7 @@ QualType ASTContext::getVariableArrayDecayedType(QualType type) const { #define TYPE(Class, Base) #define ABSTRACT_TYPE(Class, Base) #define NON_CANONICAL_TYPE(Class, Base) case Type::Class: -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" llvm_unreachable("didn't desugar past all non-canonical types?"); // These types should never be variably-modified. @@ -3219,6 +3308,7 @@ QualType ASTContext::getVariableArrayDecayedType(QualType type) const { result = getConstantArrayType( getVariableArrayDecayedType(cat->getElementType()), cat->getSize(), + cat->getSizeExpr(), cat->getSizeModifier(), cat->getIndexTypeCVRQualifiers()); break; @@ -4624,8 +4714,7 @@ ASTContext::applyObjCProtocolQualifiers(QualType type, QualType ASTContext::getObjCTypeParamType(const ObjCTypeParamDecl *Decl, - ArrayRef protocols, - QualType Canonical) const { + ArrayRef protocols) const { // Look in the folding set for an existing type. llvm::FoldingSetNodeID ID; ObjCTypeParamType::Profile(ID, Decl, protocols); @@ -4634,16 +4723,14 @@ ASTContext::getObjCTypeParamType(const ObjCTypeParamDecl *Decl, ObjCTypeParamTypes.FindNodeOrInsertPos(ID, InsertPos)) return QualType(TypeParam, 0); - if (Canonical.isNull()) { - // We canonicalize to the underlying type. - Canonical = getCanonicalType(Decl->getUnderlyingType()); - if (!protocols.empty()) { - // Apply the protocol qualifers. - bool hasError; - Canonical = getCanonicalType(applyObjCProtocolQualifiers( - Canonical, protocols, hasError, true /*allowOnPointerType*/)); - assert(!hasError && "Error when apply protocol qualifier to bound type"); - } + // We canonicalize to the underlying type. + QualType Canonical = getCanonicalType(Decl->getUnderlyingType()); + if (!protocols.empty()) { + // Apply the protocol qualifers. + bool hasError; + Canonical = getCanonicalType(applyObjCProtocolQualifiers( + Canonical, protocols, hasError, true /*allowOnPointerType*/)); + assert(!hasError && "Error when apply protocol qualifier to bound type"); } unsigned size = sizeof(ObjCTypeParamType); @@ -5114,7 +5201,7 @@ QualType ASTContext::getUnqualifiedArrayType(QualType type, if (const auto *CAT = dyn_cast(AT)) { return getConstantArrayType(unqualElementType, CAT->getSize(), - CAT->getSizeModifier(), 0); + CAT->getSizeExpr(), CAT->getSizeModifier(), 0); } if (const auto *IAT = dyn_cast(AT)) { @@ -5487,6 +5574,7 @@ const ArrayType *ASTContext::getAsArrayType(QualType T) const { if (const auto *CAT = dyn_cast(ATy)) return cast(getConstantArrayType(NewEltTy, CAT->getSize(), + CAT->getSizeExpr(), CAT->getSizeModifier(), CAT->getIndexTypeCVRQualifiers())); if (const auto *IAT = dyn_cast(ATy)) @@ -5599,8 +5687,7 @@ static FloatingRank getFloatingRank(QualType T) { if (const auto *CT = T->getAs()) return getFloatingRank(CT->getElementType()); - assert(T->getAs() && "getFloatingRank(): not a floating type"); - switch (T->getAs()->getKind()) { + switch (T->castAs()->getKind()) { default: llvm_unreachable("getFloatingRank(): not a floating type"); case BuiltinType::Float16: return Float16Rank; case BuiltinType::Half: return HalfRank; @@ -5977,12 +6064,10 @@ QualType ASTContext::getObjCSuperType() const { } void ASTContext::setCFConstantStringType(QualType T) { - const auto *TD = T->getAs(); - assert(TD && "Invalid CFConstantStringType"); + const auto *TD = T->castAs(); CFConstantStringTypeDecl = cast(TD->getDecl()); const auto *TagType = - CFConstantStringTypeDecl->getUnderlyingType()->getAs(); - assert(TagType && "Invalid CFConstantStringType"); + CFConstantStringTypeDecl->getUnderlyingType()->castAs(); CFConstantStringTagDecl = TagType->getDecl(); } @@ -6238,14 +6323,14 @@ std::string ASTContext::getObjCEncodingForBlock(const BlockExpr *Expr) const { const BlockDecl *Decl = Expr->getBlockDecl(); QualType BlockTy = - Expr->getType()->getAs()->getPointeeType(); + Expr->getType()->castAs()->getPointeeType(); + QualType BlockReturnTy = BlockTy->castAs()->getReturnType(); // Encode result type. if (getLangOpts().EncodeExtendedBlockSig) - getObjCEncodingForMethodParameter( - Decl::OBJC_TQ_None, BlockTy->getAs()->getReturnType(), S, - true /*Extended*/); + getObjCEncodingForMethodParameter(Decl::OBJC_TQ_None, BlockReturnTy, S, + true /*Extended*/); else - getObjCEncodingForType(BlockTy->getAs()->getReturnType(), S); + getObjCEncodingForType(BlockReturnTy, S); // Compute size of all parameters. // Start with computing size of a pointer in number of bytes. // FIXME: There might(should) be a better way of doing this computation! @@ -6556,8 +6641,9 @@ void ASTContext::getObjCEncodingForPropertyType(QualType T, /*Field=*/nullptr); } -static char getObjCEncodingForPrimitiveKind(const ASTContext *C, - BuiltinType::Kind kind) { +static char getObjCEncodingForPrimitiveType(const ASTContext *C, + const BuiltinType *BT) { + BuiltinType::Kind kind = BT->getKind(); switch (kind) { case BuiltinType::Void: return 'v'; case BuiltinType::Bool: return 'B'; @@ -6617,6 +6703,17 @@ static char getObjCEncodingForPrimitiveKind(const ASTContext *C, // FIXME: potentially need @encodes for these! return ' '; +#define SVE_TYPE(Name, Id, SingletonId) \ + case BuiltinType::Id: +#include "clang/Basic/AArch64SVEACLETypes.def" + { + DiagnosticsEngine &Diags = C->getDiagnostics(); + unsigned DiagID = Diags.getCustomDiagID( + DiagnosticsEngine::Error, "cannot yet @encode type %0"); + Diags.Report(DiagID) << BT->getName(C->getPrintingPolicy()); + return ' '; + } + case BuiltinType::ObjCId: case BuiltinType::ObjCClass: case BuiltinType::ObjCSel: @@ -6653,7 +6750,7 @@ static char ObjCEncodingForEnumType(const ASTContext *C, const EnumType *ET) { // The encoding of a fixed enum type matches its fixed underlying type. const auto *BT = Enum->getIntegerType()->castAs(); - return getObjCEncodingForPrimitiveKind(C, BT->getKind()); + return getObjCEncodingForPrimitiveType(C, BT); } static void EncodeBitField(const ASTContext *Ctx, std::string& S, @@ -6693,7 +6790,7 @@ static void EncodeBitField(const ASTContext *Ctx, std::string& S, S += ObjCEncodingForEnumType(Ctx, ET); else { const auto *BT = T->castAs(); - S += getObjCEncodingForPrimitiveKind(Ctx, BT->getKind()); + S += getObjCEncodingForPrimitiveType(Ctx, BT); } } S += llvm::utostr(FD->getBitWidthValue(*Ctx)); @@ -6711,7 +6808,7 @@ void ASTContext::getObjCEncodingForTypeImpl(QualType T, std::string &S, if (FD && FD->isBitField()) return EncodeBitField(this, S, T, FD); if (const auto *BT = dyn_cast(CT)) - S += getObjCEncodingForPrimitiveKind(this, BT->getKind()); + S += getObjCEncodingForPrimitiveType(this, BT); else S += ObjCEncodingForEnumType(this, cast(CT)); return; @@ -6760,8 +6857,8 @@ void ASTContext::getObjCEncodingForTypeImpl(QualType T, std::string &S, } } else if (Options.IsOutermostType()) { QualType P = PointeeTy; - while (P->getAs()) - P = P->getAs()->getPointeeType(); + while (auto PT = P->getAs()) + P = PT->getPointeeType(); if (P.isConstQualified()) { isReadOnly = true; S += 'r'; @@ -7033,7 +7130,7 @@ void ASTContext::getObjCEncodingForTypeImpl(QualType T, std::string &S, case Type::KIND: #define NON_CANONICAL_UNLESS_DEPENDENT_TYPE(KIND, BASE) \ case Type::KIND: -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" llvm_unreachable("@encode for dependent type!"); } llvm_unreachable("bad type kind!"); @@ -7384,7 +7481,7 @@ static TypedefDecl *CreatePowerABIBuiltinVaListDecl(const ASTContext *Context) { llvm::APInt Size(Context->getTypeSize(Context->getSizeType()), 1); QualType VaListTagArrayType = Context->getConstantArrayType(VaListTagTypedefType, - Size, ArrayType::Normal, 0); + Size, nullptr, ArrayType::Normal, 0); return Context->buildImplicitTypedef(VaListTagArrayType, "__builtin_va_list"); } @@ -7437,16 +7534,16 @@ CreateX86_64ABIBuiltinVaListDecl(const ASTContext *Context) { // typedef struct __va_list_tag __builtin_va_list[1]; llvm::APInt Size(Context->getTypeSize(Context->getSizeType()), 1); - QualType VaListTagArrayType = - Context->getConstantArrayType(VaListTagType, Size, ArrayType::Normal, 0); + QualType VaListTagArrayType = Context->getConstantArrayType( + VaListTagType, Size, nullptr, ArrayType::Normal, 0); return Context->buildImplicitTypedef(VaListTagArrayType, "__builtin_va_list"); } static TypedefDecl *CreatePNaClABIBuiltinVaListDecl(const ASTContext *Context) { // typedef int __builtin_va_list[4]; llvm::APInt Size(Context->getTypeSize(Context->getSizeType()), 4); - QualType IntArrayType = - Context->getConstantArrayType(Context->IntTy, Size, ArrayType::Normal, 0); + QualType IntArrayType = Context->getConstantArrayType( + Context->IntTy, Size, nullptr, ArrayType::Normal, 0); return Context->buildImplicitTypedef(IntArrayType, "__builtin_va_list"); } @@ -7540,8 +7637,8 @@ CreateSystemZBuiltinVaListDecl(const ASTContext *Context) { // typedef __va_list_tag __builtin_va_list[1]; llvm::APInt Size(Context->getTypeSize(Context->getSizeType()), 1); - QualType VaListTagArrayType = - Context->getConstantArrayType(VaListTagType, Size, ArrayType::Normal, 0); + QualType VaListTagArrayType = Context->getConstantArrayType( + VaListTagType, Size, nullptr, ArrayType::Normal, 0); return Context->buildImplicitTypedef(VaListTagArrayType, "__builtin_va_list"); } @@ -7816,7 +7913,7 @@ Qualifiers::GC ASTContext::getObjCGCAttrKind(QualType Ty) const { if (Ty->isObjCObjectPointerType() || Ty->isBlockPointerType()) return Qualifiers::Strong; else if (Ty->isPointerType()) - return getObjCGCAttrKind(Ty->getAs()->getPointeeType()); + return getObjCGCAttrKind(Ty->castAs()->getPointeeType()); } else { // It's not valid to set GC attributes on anything that isn't a // pointer. @@ -7853,8 +7950,8 @@ bool ASTContext::areCompatibleVectorTypes(QualType FirstVec, // Treat Neon vector types and most AltiVec vector types as if they are the // equivalent GCC vector types. - const auto *First = FirstVec->getAs(); - const auto *Second = SecondVec->getAs(); + const auto *First = FirstVec->castAs(); + const auto *Second = SecondVec->castAs(); if (First->getNumElements() == Second->getNumElements() && hasSameType(First->getElementType(), Second->getElementType()) && First->getVectorKind() != VectorType::AltiVecPixel && @@ -7866,6 +7963,28 @@ bool ASTContext::areCompatibleVectorTypes(QualType FirstVec, return false; } +bool ASTContext::hasDirectOwnershipQualifier(QualType Ty) const { + while (true) { + // __strong id + if (const AttributedType *Attr = dyn_cast(Ty)) { + if (Attr->getAttrKind() == attr::ObjCOwnership) + return true; + + Ty = Attr->getModifiedType(); + + // X *__strong (...) + } else if (const ParenType *Paren = dyn_cast(Ty)) { + Ty = Paren->getInnerType(); + + // We do not want to look through typedefs, typeof(expr), + // typeof(type), or any other way that the type is somehow + // abstracted. + } else { + return false; + } + } +} + //===----------------------------------------------------------------------===// // ObjCQualifiedIdTypesAreCompatible - Compatibility testing for qualified id's. //===----------------------------------------------------------------------===// @@ -7885,15 +8004,11 @@ ASTContext::ProtocolCompatibleWithProtocol(ObjCProtocolDecl *lProto, /// ObjCQualifiedClassTypesAreCompatible - compare Class and /// Class. -bool ASTContext::ObjCQualifiedClassTypesAreCompatible(QualType lhs, - QualType rhs) { - const auto *lhsQID = lhs->getAs(); - const auto *rhsOPT = rhs->getAs(); - assert((lhsQID && rhsOPT) && "ObjCQualifiedClassTypesAreCompatible"); - - for (auto *lhsProto : lhsQID->quals()) { +bool ASTContext::ObjCQualifiedClassTypesAreCompatible( + const ObjCObjectPointerType *lhs, const ObjCObjectPointerType *rhs) { + for (auto *lhsProto : lhs->quals()) { bool match = false; - for (auto *rhsProto : rhsOPT->quals()) { + for (auto *rhsProto : rhs->quals()) { if (ProtocolCompatibleWithProtocol(lhsProto, rhsProto)) { match = true; break; @@ -7907,26 +8022,24 @@ bool ASTContext::ObjCQualifiedClassTypesAreCompatible(QualType lhs, /// ObjCQualifiedIdTypesAreCompatible - We know that one of lhs/rhs is an /// ObjCQualifiedIDType. -bool ASTContext::ObjCQualifiedIdTypesAreCompatible(QualType lhs, QualType rhs, - bool compare) { - // Allow id and an 'id' or void* type in all cases. - if (lhs->isVoidPointerType() || - lhs->isObjCIdType() || lhs->isObjCClassType()) - return true; - else if (rhs->isVoidPointerType() || - rhs->isObjCIdType() || rhs->isObjCClassType()) +bool ASTContext::ObjCQualifiedIdTypesAreCompatible( + const ObjCObjectPointerType *lhs, const ObjCObjectPointerType *rhs, + bool compare) { + // Allow id and an 'id' in all cases. + if (lhs->isObjCIdType() || rhs->isObjCIdType()) return true; - if (const ObjCObjectPointerType *lhsQID = lhs->getAsObjCQualifiedIdType()) { - const auto *rhsOPT = rhs->getAs(); - - if (!rhsOPT) return false; + // Don't allow id to convert to Class or Class in either direction. + if (lhs->isObjCClassType() || lhs->isObjCQualifiedClassType() || + rhs->isObjCClassType() || rhs->isObjCQualifiedClassType()) + return false; - if (rhsOPT->qual_empty()) { + if (lhs->isObjCQualifiedIdType()) { + if (rhs->qual_empty()) { // If the RHS is a unqualified interface pointer "NSString*", // make sure we check the class hierarchy. - if (ObjCInterfaceDecl *rhsID = rhsOPT->getInterfaceDecl()) { - for (auto *I : lhsQID->quals()) { + if (ObjCInterfaceDecl *rhsID = rhs->getInterfaceDecl()) { + for (auto *I : lhs->quals()) { // when comparing an id

on lhs with a static type on rhs, // see if static class implements all of id's protocols, directly or // through its super class and categories. @@ -7938,13 +8051,13 @@ bool ASTContext::ObjCQualifiedIdTypesAreCompatible(QualType lhs, QualType rhs, return true; } // Both the right and left sides have qualifiers. - for (auto *lhsProto : lhsQID->quals()) { + for (auto *lhsProto : lhs->quals()) { bool match = false; // when comparing an id

on lhs with a static type on rhs, // see if static class implements all of id's protocols, directly or // through its super class and categories. - for (auto *rhsProto : rhsOPT->quals()) { + for (auto *rhsProto : rhs->quals()) { if (ProtocolCompatibleWithProtocol(lhsProto, rhsProto) || (compare && ProtocolCompatibleWithProtocol(rhsProto, lhsProto))) { match = true; @@ -7953,8 +8066,8 @@ bool ASTContext::ObjCQualifiedIdTypesAreCompatible(QualType lhs, QualType rhs, } // If the RHS is a qualified interface pointer "NSString

*", // make sure we check the class hierarchy. - if (ObjCInterfaceDecl *rhsID = rhsOPT->getInterfaceDecl()) { - for (auto *I : lhsQID->quals()) { + if (ObjCInterfaceDecl *rhsID = rhs->getInterfaceDecl()) { + for (auto *I : lhs->quals()) { // when comparing an id

on lhs with a static type on rhs, // see if static class implements all of id's protocols, directly or // through its super class and categories. @@ -7971,13 +8084,11 @@ bool ASTContext::ObjCQualifiedIdTypesAreCompatible(QualType lhs, QualType rhs, return true; } - const ObjCObjectPointerType *rhsQID = rhs->getAsObjCQualifiedIdType(); - assert(rhsQID && "One of the LHS/RHS should be id"); + assert(rhs->isObjCQualifiedIdType() && "One of the LHS/RHS should be id"); - if (const ObjCObjectPointerType *lhsOPT = - lhs->getAsObjCInterfacePointerType()) { + if (lhs->getInterfaceType()) { // If both the right and left sides have qualifiers. - for (auto *lhsProto : lhsOPT->quals()) { + for (auto *lhsProto : lhs->quals()) { bool match = false; // when comparing an id

on rhs with a static type on lhs, @@ -7985,7 +8096,7 @@ bool ASTContext::ObjCQualifiedIdTypesAreCompatible(QualType lhs, QualType rhs, // through its super class and categories. // First, lhs protocols in the qualifier list must be found, direct // or indirect in rhs's qualifier list or it is a mismatch. - for (auto *rhsProto : rhsQID->quals()) { + for (auto *rhsProto : rhs->quals()) { if (ProtocolCompatibleWithProtocol(lhsProto, rhsProto) || (compare && ProtocolCompatibleWithProtocol(rhsProto, lhsProto))) { match = true; @@ -7998,17 +8109,17 @@ bool ASTContext::ObjCQualifiedIdTypesAreCompatible(QualType lhs, QualType rhs, // Static class's protocols, or its super class or category protocols // must be found, direct or indirect in rhs's qualifier list or it is a mismatch. - if (ObjCInterfaceDecl *lhsID = lhsOPT->getInterfaceDecl()) { + if (ObjCInterfaceDecl *lhsID = lhs->getInterfaceDecl()) { llvm::SmallPtrSet LHSInheritedProtocols; CollectInheritedProtocols(lhsID, LHSInheritedProtocols); // This is rather dubious but matches gcc's behavior. If lhs has // no type qualifier and its class has no static protocol(s) // assume that it is mismatch. - if (LHSInheritedProtocols.empty() && lhsOPT->qual_empty()) + if (LHSInheritedProtocols.empty() && lhs->qual_empty()) return false; for (auto *lhsProto : LHSInheritedProtocols) { bool match = false; - for (auto *rhsProto : rhsQID->quals()) { + for (auto *rhsProto : rhs->quals()) { if (ProtocolCompatibleWithProtocol(lhsProto, rhsProto) || (compare && ProtocolCompatibleWithProtocol(rhsProto, lhsProto))) { match = true; @@ -8032,9 +8143,8 @@ bool ASTContext::canAssignObjCInterfaces(const ObjCObjectPointerType *LHSOPT, const ObjCObjectType* LHS = LHSOPT->getObjectType(); const ObjCObjectType* RHS = RHSOPT->getObjectType(); - // If either type represents the built-in 'id' or 'Class' types, return true. - if (LHS->isObjCUnqualifiedIdOrClass() || - RHS->isObjCUnqualifiedIdOrClass()) + // If either type represents the built-in 'id' type, return true. + if (LHS->isObjCUnqualifiedId() || RHS->isObjCUnqualifiedId()) return true; // Function object that propagates a successful result or handles @@ -8052,15 +8162,20 @@ bool ASTContext::canAssignObjCInterfaces(const ObjCObjectPointerType *LHSOPT, LHSOPT->stripObjCKindOfTypeAndQuals(*this)); }; + // Casts from or to id

are allowed when the other side has compatible + // protocols. if (LHS->isObjCQualifiedId() || RHS->isObjCQualifiedId()) { - return finish(ObjCQualifiedIdTypesAreCompatible(QualType(LHSOPT,0), - QualType(RHSOPT,0), - false)); + return finish(ObjCQualifiedIdTypesAreCompatible(LHSOPT, RHSOPT, false)); } + // Verify protocol compatibility for casts from Class to Class. if (LHS->isObjCQualifiedClass() && RHS->isObjCQualifiedClass()) { - return finish(ObjCQualifiedClassTypesAreCompatible(QualType(LHSOPT,0), - QualType(RHSOPT,0))); + return finish(ObjCQualifiedClassTypesAreCompatible(LHSOPT, RHSOPT)); + } + + // Casts from Class to Class, or vice-versa, are allowed. + if (LHS->isObjCClass() && RHS->isObjCClass()) { + return true; } // If we have 2 user-defined types, fall into that path. @@ -8108,9 +8223,9 @@ bool ASTContext::canAssignObjCInterfacesInBlockPointer( } if (LHSOPT->isObjCQualifiedIdType() || RHSOPT->isObjCQualifiedIdType()) - return finish(ObjCQualifiedIdTypesAreCompatible(QualType(LHSOPT,0), - QualType(RHSOPT,0), - false)); + return finish(ObjCQualifiedIdTypesAreCompatible( + (BlockReturnType ? LHSOPT : RHSOPT), + (BlockReturnType ? RHSOPT : LHSOPT), false)); const ObjCInterfaceType* LHS = LHSOPT->getInterfaceType(); const ObjCInterfaceType* RHS = RHSOPT->getInterfaceType(); @@ -8834,7 +8949,7 @@ QualType ASTContext::mergeTypes(QualType LHS, QualType RHS, #define NON_CANONICAL_UNLESS_DEPENDENT_TYPE(Class, Base) case Type::Class: #define NON_CANONICAL_TYPE(Class, Base) case Type::Class: #define DEPENDENT_TYPE(Class, Base) case Type::Class: -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" llvm_unreachable("Non-canonical and dependent types shouldn't get here"); case Type::Auto: @@ -8854,8 +8969,8 @@ QualType ASTContext::mergeTypes(QualType LHS, QualType RHS, case Type::Pointer: { // Merge two pointer types, while trying to preserve typedef info - QualType LHSPointee = LHS->getAs()->getPointeeType(); - QualType RHSPointee = RHS->getAs()->getPointeeType(); + QualType LHSPointee = LHS->castAs()->getPointeeType(); + QualType RHSPointee = RHS->castAs()->getPointeeType(); if (Unqualified) { LHSPointee = LHSPointee.getUnqualifiedType(); RHSPointee = RHSPointee.getUnqualifiedType(); @@ -8873,8 +8988,8 @@ QualType ASTContext::mergeTypes(QualType LHS, QualType RHS, case Type::BlockPointer: { // Merge two block pointer types, while trying to preserve typedef info - QualType LHSPointee = LHS->getAs()->getPointeeType(); - QualType RHSPointee = RHS->getAs()->getPointeeType(); + QualType LHSPointee = LHS->castAs()->getPointeeType(); + QualType RHSPointee = RHS->castAs()->getPointeeType(); if (Unqualified) { LHSPointee = LHSPointee.getUnqualifiedType(); RHSPointee = RHSPointee.getUnqualifiedType(); @@ -8906,8 +9021,8 @@ QualType ASTContext::mergeTypes(QualType LHS, QualType RHS, case Type::Atomic: { // Merge two pointer types, while trying to preserve typedef info - QualType LHSValue = LHS->getAs()->getValueType(); - QualType RHSValue = RHS->getAs()->getValueType(); + QualType LHSValue = LHS->castAs()->getValueType(); + QualType RHSValue = RHS->castAs()->getValueType(); if (Unqualified) { LHSValue = LHSValue.getUnqualifiedType(); RHSValue = RHSValue.getUnqualifiedType(); @@ -8975,10 +9090,14 @@ QualType ASTContext::mergeTypes(QualType LHS, QualType RHS, return LHS; if (RCAT && getCanonicalType(RHSElem) == getCanonicalType(ResultType)) return RHS; - if (LCAT) return getConstantArrayType(ResultType, LCAT->getSize(), - ArrayType::ArraySizeModifier(), 0); - if (RCAT) return getConstantArrayType(ResultType, RCAT->getSize(), - ArrayType::ArraySizeModifier(), 0); + if (LCAT) + return getConstantArrayType(ResultType, LCAT->getSize(), + LCAT->getSizeExpr(), + ArrayType::ArraySizeModifier(), 0); + if (RCAT) + return getConstantArrayType(ResultType, RCAT->getSize(), + RCAT->getSizeExpr(), + ArrayType::ArraySizeModifier(), 0); if (LVAT && getCanonicalType(LHSElem) == getCanonicalType(ResultType)) return LHS; if (RVAT && getCanonicalType(RHSElem) == getCanonicalType(ResultType)) @@ -9013,34 +9132,30 @@ QualType ASTContext::mergeTypes(QualType LHS, QualType RHS, return {}; case Type::Vector: // FIXME: The merged type should be an ExtVector! - if (areCompatVectorTypes(LHSCan->getAs(), - RHSCan->getAs())) + if (areCompatVectorTypes(LHSCan->castAs(), + RHSCan->castAs())) return LHS; return {}; case Type::ObjCObject: { // Check if the types are assignment compatible. // FIXME: This should be type compatibility, e.g. whether // "LHS x; RHS x;" at global scope is legal. - const auto *LHSIface = LHS->getAs(); - const auto *RHSIface = RHS->getAs(); - if (canAssignObjCInterfaces(LHSIface, RHSIface)) + if (canAssignObjCInterfaces(LHS->castAs(), + RHS->castAs())) return LHS; - return {}; } case Type::ObjCObjectPointer: if (OfBlockPointer) { if (canAssignObjCInterfacesInBlockPointer( - LHS->getAs(), - RHS->getAs(), - BlockReturnType)) + LHS->castAs(), + RHS->castAs(), BlockReturnType)) return LHS; return {}; } - if (canAssignObjCInterfaces(LHS->getAs(), - RHS->getAs())) + if (canAssignObjCInterfaces(LHS->castAs(), + RHS->castAs())) return LHS; - return {}; case Type::Pipe: assert(LHS != RHS && @@ -9125,7 +9240,7 @@ QualType ASTContext::mergeObjCGCQualifiers(QualType LHS, QualType RHS) { if (ResReturnType == NewReturnType || ResReturnType == OldReturnType) { // id foo(); ... __strong id foo(); or: __strong id foo(); ... id foo(); // In either case, use OldReturnType to build the new function type. - const auto *F = LHS->getAs(); + const auto *F = LHS->castAs(); if (const auto *FPT = cast(F)) { FunctionProtoType::ExtProtoInfo EPI = FPT->getExtProtoInfo(); EPI.ExtInfo = getFunctionExtInfo(LHS); @@ -9166,8 +9281,8 @@ QualType ASTContext::mergeObjCGCQualifiers(QualType LHS, QualType RHS) { } if (LHSCan->isObjCObjectPointerType() && RHSCan->isObjCObjectPointerType()) { - QualType LHSBaseQT = LHS->getAs()->getPointeeType(); - QualType RHSBaseQT = RHS->getAs()->getPointeeType(); + QualType LHSBaseQT = LHS->castAs()->getPointeeType(); + QualType RHSBaseQT = RHS->castAs()->getPointeeType(); QualType ResQT = mergeObjCGCQualifiers(LHSBaseQT, RHSBaseQT); if (ResQT == LHSBaseQT) return LHS; @@ -9203,9 +9318,7 @@ QualType ASTContext::getCorrespondingUnsignedType(QualType T) const { if (const auto *ETy = T->getAs()) T = ETy->getDecl()->getIntegerType(); - const auto *BTy = T->getAs(); - assert(BTy && "Unexpected signed integer or fixed point type"); - switch (BTy->getKind()) { + switch (T->castAs()->getKind()) { case BuiltinType::Char_S: case BuiltinType::SChar: return UnsignedCharTy; @@ -9860,7 +9973,7 @@ bool ASTContext::DeclMustBeEmitted(const Decl *D) { return !D->getDeclContext()->isDependentContext(); else if (isa(D)) return !D->getDeclContext()->isDependentContext(); - else if (isa(D)) + else if (isa(D) || isa(D)) return !D->getDeclContext()->isDependentContext(); else if (isa(D)) return true; @@ -9963,7 +10076,7 @@ bool ASTContext::DeclMustBeEmitted(const Decl *D) { return false; // Variables that have destruction with side-effects are required. - if (VD->getType().isDestructedType()) + if (VD->needsDestruction(*this)) return true; // Variables that have initialization with side-effects are required. @@ -10035,7 +10148,7 @@ CallingConv ASTContext::getDefaultCallingConvention(bool IsVariadic, break; } } - return Target->getDefaultCallingConv(TargetInfo::CCMT_Unknown); + return Target->getDefaultCallingConv(); } bool ASTContext::isNearlyEmpty(const CXXRecordDecl *RD) const { @@ -10153,6 +10266,16 @@ ASTContext::getManglingNumberContext(const DeclContext *DC) { return *MCtx; } +MangleNumberingContext & +ASTContext::getManglingNumberContext(NeedExtraManglingDecl_t, const Decl *D) { + assert(LangOpts.CPlusPlus); // We don't need mangling numbers for plain C. + std::unique_ptr &MCtx = + ExtraMangleNumberingContexts[D]; + if (!MCtx) + MCtx = createMangleNumberingContext(); + return *MCtx; +} + std::unique_ptr ASTContext::createMangleNumberingContext() const { return ABI->createMangleNumberingContext(); @@ -10226,7 +10349,7 @@ QualType ASTContext::getStringLiteralArrayType(QualType EltTy, // Get an array type for the string, according to C99 6.4.5. This includes // the null terminator character. - return getConstantArrayType(EltTy, llvm::APInt(32, Length + 1), + return getConstantArrayType(EltTy, llvm::APInt(32, Length + 1), nullptr, ArrayType::Normal, /*IndexTypeQuals*/ 0); } @@ -10389,7 +10512,7 @@ ASTContext::getParents(const ast_type_traits::DynTypedNode &Node) { if (!Parents) // We build the parent map for the traversal scope (usually whole TU), as // hasAncestor can escape any subtree. - Parents = llvm::make_unique(*this); + Parents = std::make_unique(*this); return Parents->getParents(Node); } @@ -10446,8 +10569,7 @@ QualType ASTContext::getCorrespondingSaturatedType(QualType Ty) const { if (Ty->isSaturatedFixedPointType()) return Ty; - const auto &BT = Ty->getAs(); - switch (BT->getKind()) { + switch (Ty->castAs()->getKind()) { default: llvm_unreachable("Not a fixed point type!"); case BuiltinType::ShortAccum: @@ -10499,9 +10621,8 @@ clang::LazyGenerationalUpdatePtr< unsigned char ASTContext::getFixedPointScale(QualType Ty) const { assert(Ty->isFixedPointType()); - const auto *BT = Ty->getAs(); const TargetInfo &Target = getTargetInfo(); - switch (BT->getKind()) { + switch (Ty->castAs()->getKind()) { default: llvm_unreachable("Not a fixed point type!"); case BuiltinType::ShortAccum: @@ -10546,9 +10667,8 @@ unsigned char ASTContext::getFixedPointScale(QualType Ty) const { unsigned char ASTContext::getFixedPointIBits(QualType Ty) const { assert(Ty->isFixedPointType()); - const auto *BT = Ty->getAs(); const TargetInfo &Target = getTargetInfo(); - switch (BT->getKind()) { + switch (Ty->castAs()->getKind()) { default: llvm_unreachable("Not a fixed point type!"); case BuiltinType::ShortAccum: @@ -10613,9 +10733,8 @@ APFixedPoint ASTContext::getFixedPointMin(QualType Ty) const { QualType ASTContext::getCorrespondingSignedFixedPointType(QualType Ty) const { assert(Ty->isUnsignedFixedPointType() && "Expected unsigned fixed point type"); - const auto *BTy = Ty->getAs(); - switch (BTy->getKind()) { + switch (Ty->castAs()->getKind()) { case BuiltinType::UShortAccum: return ShortAccumTy; case BuiltinType::UAccum: diff --git a/lib/AST/ASTDiagnostic.cpp b/lib/AST/ASTDiagnostic.cpp index 15df865852941..30985441031de 100644 --- a/lib/AST/ASTDiagnostic.cpp +++ b/lib/AST/ASTDiagnostic.cpp @@ -154,7 +154,7 @@ Underlying = CTy->desugar(); \ } \ break; \ } -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" } // If it wasn't sugared, we're done. diff --git a/lib/AST/ASTImporter.cpp b/lib/AST/ASTImporter.cpp index 9d5dd84161dec..54acca7dc62cc 100644 --- a/lib/AST/ASTImporter.cpp +++ b/lib/AST/ASTImporter.cpp @@ -80,6 +80,7 @@ namespace clang { using ExpectedExpr = llvm::Expected; using ExpectedDecl = llvm::Expected; using ExpectedSLoc = llvm::Expected; + using ExpectedName = llvm::Expected; std::string ImportError::toString() const { // FIXME: Improve error texts. @@ -426,6 +427,9 @@ namespace clang { Error ImportFunctionDeclBody(FunctionDecl *FromFD, FunctionDecl *ToFD); + Error ImportDefaultArgOfParmVarDecl(const ParmVarDecl *FromParam, + ParmVarDecl *ToParam); + template bool hasSameVisibilityContext(T *Found, T *From); @@ -635,7 +639,8 @@ namespace clang { return ImportArrayChecked(InContainer.begin(), InContainer.end(), Obegin); } - void ImportOverrides(CXXMethodDecl *ToMethod, CXXMethodDecl *FromMethod); + Error ImportOverriddenMethods(CXXMethodDecl *ToMethod, + CXXMethodDecl *FromMethod); Expected FindFunctionTemplateSpecialization( FunctionDecl *FromFD); @@ -927,6 +932,27 @@ Expected ASTNodeImporter::import(const LambdaCapture &From) { EllipsisLoc); } +template +bool ASTNodeImporter::hasSameVisibilityContext(T *Found, T *From) { + if (From->hasExternalFormalLinkage()) + return Found->hasExternalFormalLinkage(); + if (Importer.GetFromTU(Found) != From->getTranslationUnitDecl()) + return false; + if (From->isInAnonymousNamespace()) + return Found->isInAnonymousNamespace(); + else + return !Found->isInAnonymousNamespace() && + !Found->hasExternalFormalLinkage(); +} + +template <> +bool ASTNodeImporter::hasSameVisibilityContext(TypedefNameDecl *Found, + TypedefNameDecl *From) { + if (From->isInAnonymousNamespace() && Found->isInAnonymousNamespace()) + return Importer.GetFromTU(Found) == From->getTranslationUnitDecl(); + return From->isInAnonymousNamespace() == Found->isInAnonymousNamespace(); +} + } // namespace clang //---------------------------------------------------------------------------- @@ -959,6 +985,10 @@ ExpectedType ASTNodeImporter::VisitBuiltinType(const BuiltinType *T) { case BuiltinType::Id: \ return Importer.getToContext().Id##Ty; #include "clang/Basic/OpenCLExtensionTypes.def" +#define SVE_TYPE(Name, Id, SingletonId) \ + case BuiltinType::Id: \ + return Importer.getToContext().SingletonId; +#include "clang/Basic/AArch64SVEACLETypes.def" #define SHARED_SINGLETON_TYPE(Expansion) #define BUILTIN_TYPE(Id, SingletonId) \ case BuiltinType::Id: return Importer.getToContext().SingletonId; @@ -1068,14 +1098,16 @@ ASTNodeImporter::VisitMemberPointerType(const MemberPointerType *T) { ExpectedType ASTNodeImporter::VisitConstantArrayType(const ConstantArrayType *T) { - ExpectedType ToElementTypeOrErr = import(T->getElementType()); - if (!ToElementTypeOrErr) - return ToElementTypeOrErr.takeError(); + QualType ToElementType; + const Expr *ToSizeExpr; + if (auto Imp = importSeq(T->getElementType(), T->getSizeExpr())) + std::tie(ToElementType, ToSizeExpr) = *Imp; + else + return Imp.takeError(); - return Importer.getToContext().getConstantArrayType(*ToElementTypeOrErr, - T->getSize(), - T->getSizeModifier(), - T->getIndexTypeCVRQualifiers()); + return Importer.getToContext().getConstantArrayType( + ToElementType, T->getSize(), ToSizeExpr, T->getSizeModifier(), + T->getIndexTypeCVRQualifiers()); } ExpectedType @@ -1645,7 +1677,6 @@ ASTNodeImporter::ImportDeclContext(DeclContext *FromDC, bool ForceImport) { bool AccumulateChildErrors = isa(FromDC); Error ChildErrors = Error::success(); - llvm::SmallVector ImportedDecls; for (auto *From : FromDC->decls()) { ExpectedDecl ImportedOrErr = import(From); if (!ImportedOrErr) { @@ -1657,6 +1688,59 @@ ASTNodeImporter::ImportDeclContext(DeclContext *FromDC, bool ForceImport) { } } + // We reorder declarations in RecordDecls because they may have another order + // in the "to" context than they have in the "from" context. This may happen + // e.g when we import a class like this: + // struct declToImport { + // int a = c + b; + // int b = 1; + // int c = 2; + // }; + // During the import of `a` we import first the dependencies in sequence, + // thus the order would be `c`, `b`, `a`. We will get the normal order by + // first removing the already imported members and then adding them in the + // order as they apper in the "from" context. + // + // Keeping field order is vital because it determines structure layout. + // + // Here and below, we cannot call field_begin() method and its callers on + // ToDC if it has an external storage. Calling field_begin() will + // automatically load all the fields by calling + // LoadFieldsFromExternalStorage(). LoadFieldsFromExternalStorage() would + // call ASTImporter::Import(). This is because the ExternalASTSource + // interface in LLDB is implemented by the means of the ASTImporter. However, + // calling an import at this point would result in an uncontrolled import, we + // must avoid that. + const auto *FromRD = dyn_cast(FromDC); + if (!FromRD) + return ChildErrors; + + auto ToDCOrErr = Importer.ImportContext(FromDC); + if (!ToDCOrErr) { + consumeError(std::move(ChildErrors)); + return ToDCOrErr.takeError(); + } + + DeclContext *ToDC = *ToDCOrErr; + // Remove all declarations, which may be in wrong order in the + // lexical DeclContext and then add them in the proper order. + for (auto *D : FromRD->decls()) { + if (isa(D) || isa(D) || isa(D)) { + assert(D && "DC contains a null decl"); + Decl *ToD = Importer.GetAlreadyImportedOrNull(D); + // Remove only the decls which we successfully imported. + if (ToD) { + assert(ToDC == ToD->getLexicalDeclContext() && ToDC->containsDecl(ToD)); + // Remove the decl from its wrong place in the linked list. + ToDC->removeDecl(ToD); + // Add the decl to the end of the linked list. + // This time it will be at the proper place because the enclosing for + // loop iterates in the original (good) order of the decls. + ToDC->addDeclInternal(ToD); + } + } + } + return ChildErrors; } @@ -1752,71 +1836,10 @@ Error ASTNodeImporter::ImportDefinition( struct CXXRecordDecl::DefinitionData &ToData = ToCXX->data(); struct CXXRecordDecl::DefinitionData &FromData = FromCXX->data(); - ToData.UserDeclaredConstructor = FromData.UserDeclaredConstructor; - ToData.UserDeclaredSpecialMembers = FromData.UserDeclaredSpecialMembers; - ToData.Aggregate = FromData.Aggregate; - ToData.PlainOldData = FromData.PlainOldData; - ToData.Empty = FromData.Empty; - ToData.Polymorphic = FromData.Polymorphic; - ToData.Abstract = FromData.Abstract; - ToData.IsStandardLayout = FromData.IsStandardLayout; - ToData.IsCXX11StandardLayout = FromData.IsCXX11StandardLayout; - ToData.HasBasesWithFields = FromData.HasBasesWithFields; - ToData.HasBasesWithNonStaticDataMembers = - FromData.HasBasesWithNonStaticDataMembers; - ToData.HasPrivateFields = FromData.HasPrivateFields; - ToData.HasProtectedFields = FromData.HasProtectedFields; - ToData.HasPublicFields = FromData.HasPublicFields; - ToData.HasMutableFields = FromData.HasMutableFields; - ToData.HasVariantMembers = FromData.HasVariantMembers; - ToData.HasOnlyCMembers = FromData.HasOnlyCMembers; - ToData.HasInClassInitializer = FromData.HasInClassInitializer; - ToData.HasUninitializedReferenceMember - = FromData.HasUninitializedReferenceMember; - ToData.HasUninitializedFields = FromData.HasUninitializedFields; - ToData.HasInheritedConstructor = FromData.HasInheritedConstructor; - ToData.HasInheritedAssignment = FromData.HasInheritedAssignment; - ToData.NeedOverloadResolutionForCopyConstructor - = FromData.NeedOverloadResolutionForCopyConstructor; - ToData.NeedOverloadResolutionForMoveConstructor - = FromData.NeedOverloadResolutionForMoveConstructor; - ToData.NeedOverloadResolutionForMoveAssignment - = FromData.NeedOverloadResolutionForMoveAssignment; - ToData.NeedOverloadResolutionForDestructor - = FromData.NeedOverloadResolutionForDestructor; - ToData.DefaultedCopyConstructorIsDeleted - = FromData.DefaultedCopyConstructorIsDeleted; - ToData.DefaultedMoveConstructorIsDeleted - = FromData.DefaultedMoveConstructorIsDeleted; - ToData.DefaultedMoveAssignmentIsDeleted - = FromData.DefaultedMoveAssignmentIsDeleted; - ToData.DefaultedDestructorIsDeleted = FromData.DefaultedDestructorIsDeleted; - ToData.HasTrivialSpecialMembers = FromData.HasTrivialSpecialMembers; - ToData.HasIrrelevantDestructor = FromData.HasIrrelevantDestructor; - ToData.HasConstexprNonCopyMoveConstructor - = FromData.HasConstexprNonCopyMoveConstructor; - ToData.HasDefaultedDefaultConstructor - = FromData.HasDefaultedDefaultConstructor; - ToData.DefaultedDefaultConstructorIsConstexpr - = FromData.DefaultedDefaultConstructorIsConstexpr; - ToData.HasConstexprDefaultConstructor - = FromData.HasConstexprDefaultConstructor; - ToData.HasNonLiteralTypeFieldsOrBases - = FromData.HasNonLiteralTypeFieldsOrBases; - // ComputedVisibleConversions not imported. - ToData.UserProvidedDefaultConstructor - = FromData.UserProvidedDefaultConstructor; - ToData.DeclaredSpecialMembers = FromData.DeclaredSpecialMembers; - ToData.ImplicitCopyConstructorCanHaveConstParamForVBase - = FromData.ImplicitCopyConstructorCanHaveConstParamForVBase; - ToData.ImplicitCopyConstructorCanHaveConstParamForNonVBase - = FromData.ImplicitCopyConstructorCanHaveConstParamForNonVBase; - ToData.ImplicitCopyAssignmentHasConstParam - = FromData.ImplicitCopyAssignmentHasConstParam; - ToData.HasDeclaredCopyConstructorWithConstParam - = FromData.HasDeclaredCopyConstructorWithConstParam; - ToData.HasDeclaredCopyAssignmentWithConstParam - = FromData.HasDeclaredCopyAssignmentWithConstParam; + + #define FIELD(Name, Width, Merge) \ + ToData.Name = FromData.Name; + #include "clang/AST/CXXRecordDeclDefinitionBits.def" // Copy over the data stored in RecordDeclBits ToCXX->setArgPassingRestrictions(FromCXX->getArgPassingRestrictions()); @@ -2188,11 +2211,13 @@ ExpectedDecl ASTNodeImporter::VisitNamespaceDecl(NamespaceDecl *D) { } if (!ConflictingDecls.empty()) { - Name = Importer.HandleNameConflict(Name, DC, Decl::IDNS_Namespace, - ConflictingDecls.data(), - ConflictingDecls.size()); - if (!Name) - return make_error(ImportError::NameConflict); + ExpectedName NameOrErr = Importer.HandleNameConflict( + Name, DC, Decl::IDNS_Namespace, ConflictingDecls.data(), + ConflictingDecls.size()); + if (NameOrErr) + Name = NameOrErr.get(); + else + return NameOrErr.takeError(); } } @@ -2281,6 +2306,9 @@ ASTNodeImporter::VisitTypedefNameDecl(TypedefNameDecl *D, bool IsAlias) { // If this typedef is not in block scope, determine whether we've // seen a typedef with the same name (that we can merge with) or any // other entity by that name (which name lookup could conflict with). + // Note: Repeated typedefs are not valid in C99: + // 'typedef int T; typedef int T;' is invalid + // We do not care about this now. if (!DC->isFunctionOrMethod()) { SmallVector ConflictingDecls; unsigned IDNS = Decl::IDNS_Ordinary; @@ -2289,6 +2317,9 @@ ASTNodeImporter::VisitTypedefNameDecl(TypedefNameDecl *D, bool IsAlias) { if (!FoundDecl->isInIdentifierNamespace(IDNS)) continue; if (auto *FoundTypedef = dyn_cast(FoundDecl)) { + if (!hasSameVisibilityContext(FoundTypedef, D)) + continue; + QualType FromUT = D->getUnderlyingType(); QualType FoundUT = FoundTypedef->getUnderlyingType(); if (Importer.IsStructurallyEquivalent(FromUT, FoundUT)) { @@ -2296,21 +2327,21 @@ ASTNodeImporter::VisitTypedefNameDecl(TypedefNameDecl *D, bool IsAlias) { // already have a complete underlying type then return with that. if (!FromUT->isIncompleteType() && !FoundUT->isIncompleteType()) return Importer.MapImported(D, FoundTypedef); + // FIXME Handle redecl chain. When you do that make consistent changes + // in ASTImporterLookupTable too. + } else { + ConflictingDecls.push_back(FoundDecl); } - // FIXME Handle redecl chain. When you do that make consistent changes - // in ASTImporterLookupTable too. - break; } - - ConflictingDecls.push_back(FoundDecl); } if (!ConflictingDecls.empty()) { - Name = Importer.HandleNameConflict(Name, DC, IDNS, - ConflictingDecls.data(), - ConflictingDecls.size()); - if (!Name) - return make_error(ImportError::NameConflict); + ExpectedName NameOrErr = Importer.HandleNameConflict( + Name, DC, IDNS, ConflictingDecls.data(), ConflictingDecls.size()); + if (NameOrErr) + Name = NameOrErr.get(); + else + return NameOrErr.takeError(); } } @@ -2383,11 +2414,12 @@ ASTNodeImporter::VisitTypeAliasTemplateDecl(TypeAliasTemplateDecl *D) { } if (!ConflictingDecls.empty()) { - Name = Importer.HandleNameConflict(Name, DC, IDNS, - ConflictingDecls.data(), - ConflictingDecls.size()); - if (!Name) - return make_error(ImportError::NameConflict); + ExpectedName NameOrErr = Importer.HandleNameConflict( + Name, DC, IDNS, ConflictingDecls.data(), ConflictingDecls.size()); + if (NameOrErr) + Name = NameOrErr.get(); + else + return NameOrErr.takeError(); } } @@ -2491,17 +2523,18 @@ ExpectedDecl ASTNodeImporter::VisitEnumDecl(EnumDecl *D) { continue; if (IsStructuralMatch(D, FoundEnum)) return Importer.MapImported(D, FoundEnum); + ConflictingDecls.push_back(FoundDecl); } - - ConflictingDecls.push_back(FoundDecl); } if (!ConflictingDecls.empty()) { - Name = Importer.HandleNameConflict(SearchName, DC, IDNS, - ConflictingDecls.data(), - ConflictingDecls.size()); - if (!Name) - return make_error(ImportError::NameConflict); + ExpectedName NameOrErr = Importer.HandleNameConflict( + SearchName, DC, IDNS, ConflictingDecls.data(), + ConflictingDecls.size()); + if (NameOrErr) + Name = NameOrErr.get(); + else + return NameOrErr.takeError(); } } @@ -2546,10 +2579,10 @@ ExpectedDecl ASTNodeImporter::VisitRecordDecl(RecordDecl *D) { } // Import the major distinguishing characteristics of this record. - DeclContext *DC, *LexicalDC; + DeclContext *DC = nullptr, *LexicalDC = nullptr; DeclarationName Name; SourceLocation Loc; - NamedDecl *ToD; + NamedDecl *ToD = nullptr; if (Error Err = ImportDeclParts(D, DC, LexicalDC, Name, ToD, Loc)) return std::move(Err); if (ToD) @@ -2568,7 +2601,7 @@ ExpectedDecl ASTNodeImporter::VisitRecordDecl(RecordDecl *D) { // We may already have a record of the same name; try to find and match it. RecordDecl *PrevDecl = nullptr; - if (!DC->isFunctionOrMethod()) { + if (!DC->isFunctionOrMethod() && !D->isLambda()) { SmallVector ConflictingDecls; auto FoundDecls = Importer.findDeclsInToCtx(DC, SearchName); @@ -2626,17 +2659,18 @@ ExpectedDecl ASTNodeImporter::VisitRecordDecl(RecordDecl *D) { PrevDecl = FoundRecord->getMostRecentDecl(); break; } - } - - ConflictingDecls.push_back(FoundDecl); + ConflictingDecls.push_back(FoundDecl); + } // kind is RecordDecl } // for if (!ConflictingDecls.empty() && SearchName) { - Name = Importer.HandleNameConflict(SearchName, DC, IDNS, - ConflictingDecls.data(), - ConflictingDecls.size()); - if (!Name) - return make_error(ImportError::NameConflict); + ExpectedName NameOrErr = Importer.HandleNameConflict( + SearchName, DC, IDNS, ConflictingDecls.data(), + ConflictingDecls.size()); + if (NameOrErr) + Name = NameOrErr.get(); + else + return NameOrErr.takeError(); } } @@ -2660,7 +2694,8 @@ ExpectedDecl ASTNodeImporter::VisitRecordDecl(RecordDecl *D) { ExpectedDecl CDeclOrErr = import(DCXX->getLambdaContextDecl()); if (!CDeclOrErr) return CDeclOrErr.takeError(); - D2CXX->setLambdaMangling(DCXX->getLambdaManglingNumber(), *CDeclOrErr); + D2CXX->setLambdaMangling(DCXX->getLambdaManglingNumber(), *CDeclOrErr, + DCXX->hasKnownLambdaInternalLinkage()); } else if (DCXX->isInjectedClassName()) { // We have to be careful to do a similar dance to the one in // Sema::ActOnStartCXXMemberDeclarations @@ -2795,17 +2830,17 @@ ExpectedDecl ASTNodeImporter::VisitEnumConstantDecl(EnumConstantDecl *D) { if (auto *FoundEnumConstant = dyn_cast(FoundDecl)) { if (IsStructuralMatch(D, FoundEnumConstant)) return Importer.MapImported(D, FoundEnumConstant); + ConflictingDecls.push_back(FoundDecl); } - - ConflictingDecls.push_back(FoundDecl); } if (!ConflictingDecls.empty()) { - Name = Importer.HandleNameConflict(Name, DC, IDNS, - ConflictingDecls.data(), - ConflictingDecls.size()); - if (!Name) - return make_error(ImportError::NameConflict); + ExpectedName NameOrErr = Importer.HandleNameConflict( + Name, DC, IDNS, ConflictingDecls.data(), ConflictingDecls.size()); + if (NameOrErr) + Name = NameOrErr.get(); + else + return NameOrErr.takeError(); } } @@ -2956,19 +2991,6 @@ Error ASTNodeImporter::ImportFunctionDeclBody(FunctionDecl *FromFD, return Error::success(); } -template -bool ASTNodeImporter::hasSameVisibilityContext(T *Found, T *From) { - if (From->hasExternalFormalLinkage()) - return Found->hasExternalFormalLinkage(); - if (Importer.GetFromTU(Found) != From->getTranslationUnitDecl()) - return false; - if (From->isInAnonymousNamespace()) - return Found->isInAnonymousNamespace(); - else - return !Found->isInAnonymousNamespace() && - !Found->hasExternalFormalLinkage(); -} - ExpectedDecl ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) { SmallVector Redecls = getCanonicalForwardRedeclChain(D); @@ -3043,17 +3065,17 @@ ExpectedDecl ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) { << Name << D->getType() << FoundFunction->getType(); Importer.ToDiag(FoundFunction->getLocation(), diag::note_odr_value_here) << FoundFunction->getType(); + ConflictingDecls.push_back(FoundDecl); } - - ConflictingDecls.push_back(FoundDecl); } if (!ConflictingDecls.empty()) { - Name = Importer.HandleNameConflict(Name, DC, IDNS, - ConflictingDecls.data(), - ConflictingDecls.size()); - if (!Name) - return make_error(ImportError::NameConflict); + ExpectedName NameOrErr = Importer.HandleNameConflict( + Name, DC, IDNS, ConflictingDecls.data(), ConflictingDecls.size()); + if (NameOrErr) + Name = NameOrErr.get(); + else + return NameOrErr.takeError(); } } @@ -3066,9 +3088,19 @@ ExpectedDecl ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) { if (FoundByLookup) { if (isa(FoundByLookup)) { if (D->getLexicalDeclContext() == D->getDeclContext()) { - if (!D->doesThisDeclarationHaveABody()) + if (!D->doesThisDeclarationHaveABody()) { + if (FunctionTemplateDecl *DescribedD = + D->getDescribedFunctionTemplate()) { + // Handle a "templated" function together with its described + // template. This avoids need for a similar check at import of the + // described template. + assert(FoundByLookup->getDescribedFunctionTemplate() && + "Templated function mapped to non-templated?"); + Importer.MapImported(DescribedD, + FoundByLookup->getDescribedFunctionTemplate()); + } return Importer.MapImported(D, FoundByLookup); - else { + } else { // Let's continue and build up the redecl chain in this case. // FIXME Merge the functions into one decl. } @@ -3154,7 +3186,7 @@ ExpectedDecl ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) { if (GetImportedOrCreateDecl( ToFunction, D, Importer.getToContext(), cast(DC), ToInnerLocStart, NameInfo, T, TInfo, D->isInlineSpecified(), - D->isImplicit())) + D->isImplicit(), D->getConstexprKind())) return ToFunction; CXXDestructorDecl *ToDtor = cast(ToFunction); @@ -3203,29 +3235,15 @@ ExpectedDecl ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) { // decl and its redeclarations may be required. } - // Import Ctor initializers. - if (auto *FromConstructor = dyn_cast(D)) { - if (unsigned NumInitializers = FromConstructor->getNumCtorInitializers()) { - SmallVector CtorInitializers(NumInitializers); - // Import first, then allocate memory and copy if there was no error. - if (Error Err = ImportContainerChecked( - FromConstructor->inits(), CtorInitializers)) - return std::move(Err); - auto **Memory = - new (Importer.getToContext()) CXXCtorInitializer *[NumInitializers]; - std::copy(CtorInitializers.begin(), CtorInitializers.end(), Memory); - auto *ToCtor = cast(ToFunction); - ToCtor->setCtorInitializers(Memory); - ToCtor->setNumCtorInitializers(NumInitializers); - } - } - ToFunction->setQualifierInfo(ToQualifierLoc); ToFunction->setAccess(D->getAccess()); ToFunction->setLexicalDeclContext(LexicalDC); ToFunction->setVirtualAsWritten(D->isVirtualAsWritten()); ToFunction->setTrivial(D->isTrivial()); ToFunction->setPure(D->isPure()); + ToFunction->setDefaulted(D->isDefaulted()); + ToFunction->setExplicitlyDefaulted(D->isExplicitlyDefaulted()); + ToFunction->setDeletedAsWritten(D->isDeletedAsWritten()); ToFunction->setRangeEnd(ToEndLoc); // Set the parameters. @@ -3260,6 +3278,23 @@ ExpectedDecl ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) { return ToFTOrErr.takeError(); } + // Import Ctor initializers. + if (auto *FromConstructor = dyn_cast(D)) { + if (unsigned NumInitializers = FromConstructor->getNumCtorInitializers()) { + SmallVector CtorInitializers(NumInitializers); + // Import first, then allocate memory and copy if there was no error. + if (Error Err = ImportContainerChecked( + FromConstructor->inits(), CtorInitializers)) + return std::move(Err); + auto **Memory = + new (Importer.getToContext()) CXXCtorInitializer *[NumInitializers]; + std::copy(CtorInitializers.begin(), CtorInitializers.end(), Memory); + auto *ToCtor = cast(ToFunction); + ToCtor->setCtorInitializers(Memory); + ToCtor->setNumCtorInitializers(NumInitializers); + } + } + if (D->doesThisDeclarationHaveABody()) { Error Err = ImportFunctionDeclBody(D, ToFunction); @@ -3292,7 +3327,9 @@ ExpectedDecl ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) { } if (auto *FromCXXMethod = dyn_cast(D)) - ImportOverrides(cast(ToFunction), FromCXXMethod); + if (Error Err = ImportOverriddenMethods(cast(ToFunction), + FromCXXMethod)) + return std::move(Err); // Import the rest of the chain. I.e. import all subsequent declarations. for (++RedeclIt; RedeclIt != Redecls.end(); ++RedeclIt) { @@ -3686,17 +3723,17 @@ ExpectedDecl ASTNodeImporter::VisitVarDecl(VarDecl *D) { << Name << D->getType() << FoundVar->getType(); Importer.ToDiag(FoundVar->getLocation(), diag::note_odr_value_here) << FoundVar->getType(); + ConflictingDecls.push_back(FoundDecl); } - - ConflictingDecls.push_back(FoundDecl); } if (!ConflictingDecls.empty()) { - Name = Importer.HandleNameConflict(Name, DC, IDNS, - ConflictingDecls.data(), - ConflictingDecls.size()); - if (!Name) - return make_error(ImportError::NameConflict); + ExpectedName NameOrErr = Importer.HandleNameConflict( + Name, DC, IDNS, ConflictingDecls.data(), ConflictingDecls.size()); + if (NameOrErr) + Name = NameOrErr.get(); + else + return NameOrErr.takeError(); } } @@ -3772,6 +3809,28 @@ ExpectedDecl ASTNodeImporter::VisitImplicitParamDecl(ImplicitParamDecl *D) { return ToParm; } +Error ASTNodeImporter::ImportDefaultArgOfParmVarDecl( + const ParmVarDecl *FromParam, ParmVarDecl *ToParam) { + ToParam->setHasInheritedDefaultArg(FromParam->hasInheritedDefaultArg()); + ToParam->setKNRPromoted(FromParam->isKNRPromoted()); + + if (FromParam->hasUninstantiatedDefaultArg()) { + if (auto ToDefArgOrErr = import(FromParam->getUninstantiatedDefaultArg())) + ToParam->setUninstantiatedDefaultArg(*ToDefArgOrErr); + else + return ToDefArgOrErr.takeError(); + } else if (FromParam->hasUnparsedDefaultArg()) { + ToParam->setUnparsedDefaultArg(); + } else if (FromParam->hasDefaultArg()) { + if (auto ToDefArgOrErr = import(FromParam->getDefaultArg())) + ToParam->setDefaultArg(*ToDefArgOrErr); + else + return ToDefArgOrErr.takeError(); + } + + return Error::success(); +} + ExpectedDecl ASTNodeImporter::VisitParmVarDecl(ParmVarDecl *D) { // Parameters are created in the translation unit's context, then moved // into the function declaration's context afterward. @@ -3798,23 +3857,11 @@ ExpectedDecl ASTNodeImporter::VisitParmVarDecl(ParmVarDecl *D) { /*DefaultArg*/ nullptr)) return ToParm; - // Set the default argument. - ToParm->setHasInheritedDefaultArg(D->hasInheritedDefaultArg()); - ToParm->setKNRPromoted(D->isKNRPromoted()); - - if (D->hasUninstantiatedDefaultArg()) { - if (auto ToDefArgOrErr = import(D->getUninstantiatedDefaultArg())) - ToParm->setUninstantiatedDefaultArg(*ToDefArgOrErr); - else - return ToDefArgOrErr.takeError(); - } else if (D->hasUnparsedDefaultArg()) { - ToParm->setUnparsedDefaultArg(); - } else if (D->hasDefaultArg()) { - if (auto ToDefArgOrErr = import(D->getDefaultArg())) - ToParm->setDefaultArg(*ToDefArgOrErr); - else - return ToDefArgOrErr.takeError(); - } + // Set the default argument. It should be no problem if it was already done. + // Do not import the default expression before GetImportedOrCreateDecl call + // to avoid possible infinite import loop because circular dependency. + if (Error Err = ImportDefaultArgOfParmVarDecl(D, ToParm)) + return std::move(Err); if (D->isObjCMethodParameter()) { ToParm->setObjCMethodScopeInfo(D->getFunctionScopeIndex()); @@ -5016,25 +5063,27 @@ ExpectedDecl ASTNodeImporter::VisitClassTemplateDecl(ClassTemplateDecl *D) { if (IsStructuralMatch(D, FoundTemplate)) { ClassTemplateDecl *TemplateWithDef = getTemplateDefinition(FoundTemplate); - if (D->isThisDeclarationADefinition() && TemplateWithDef) { + if (D->isThisDeclarationADefinition() && TemplateWithDef) return Importer.MapImported(D, TemplateWithDef); - } - FoundByLookup = FoundTemplate; - break; + if (!FoundByLookup) + FoundByLookup = FoundTemplate; + // Search in all matches because there may be multiple decl chains, + // see ASTTests test ImportExistingFriendClassTemplateDef. + continue; } + ConflictingDecls.push_back(FoundDecl); } - - ConflictingDecls.push_back(FoundDecl); } if (!ConflictingDecls.empty()) { - Name = Importer.HandleNameConflict(Name, DC, Decl::IDNS_Ordinary, - ConflictingDecls.data(), - ConflictingDecls.size()); + ExpectedName NameOrErr = Importer.HandleNameConflict( + Name, DC, Decl::IDNS_Ordinary, ConflictingDecls.data(), + ConflictingDecls.size()); + if (NameOrErr) + Name = NameOrErr.get(); + else + return NameOrErr.takeError(); } - - if (!Name) - return make_error(ImportError::NameConflict); } CXXRecordDecl *FromTemplated = D->getTemplatedDecl(); @@ -5307,22 +5356,20 @@ ExpectedDecl ASTNodeImporter::VisitVarTemplateDecl(VarTemplateDecl *D) { FoundTemplate->getTemplatedDecl()); return Importer.MapImported(D, FoundTemplate); } + ConflictingDecls.push_back(FoundDecl); } - - ConflictingDecls.push_back(FoundDecl); } if (!ConflictingDecls.empty()) { - Name = Importer.HandleNameConflict(Name, DC, Decl::IDNS_Ordinary, - ConflictingDecls.data(), - ConflictingDecls.size()); + ExpectedName NameOrErr = Importer.HandleNameConflict( + Name, DC, Decl::IDNS_Ordinary, ConflictingDecls.data(), + ConflictingDecls.size()); + if (NameOrErr) + Name = NameOrErr.get(); + else + return NameOrErr.takeError(); } - if (!Name) - // FIXME: Is it possible to get other error than name conflict? - // (Put this `if` into the previous `if`?) - return make_error(ImportError::NameConflict); - VarDecl *DTemplated = D->getTemplatedDecl(); // Import the type. @@ -5533,17 +5580,16 @@ ASTNodeImporter::VisitFunctionTemplateDecl(FunctionTemplateDecl *D) { continue; if (auto *FoundTemplate = dyn_cast(FoundDecl)) { - if (FoundTemplate->hasExternalFormalLinkage() && - D->hasExternalFormalLinkage()) { - if (IsStructuralMatch(D, FoundTemplate)) { - FunctionTemplateDecl *TemplateWithDef = - getTemplateDefinition(FoundTemplate); - if (D->isThisDeclarationADefinition() && TemplateWithDef) { - return Importer.MapImported(D, TemplateWithDef); - } - FoundByLookup = FoundTemplate; - break; - } + if (!hasSameVisibilityContext(FoundTemplate, D)) + continue; + if (IsStructuralMatch(D, FoundTemplate)) { + FunctionTemplateDecl *TemplateWithDef = + getTemplateDefinition(FoundTemplate); + if (D->isThisDeclarationADefinition() && TemplateWithDef) + return Importer.MapImported(D, TemplateWithDef); + + FoundByLookup = FoundTemplate; + break; // TODO: handle conflicting names } } @@ -6868,8 +6914,23 @@ ExpectedStmt ASTNodeImporter::VisitCXXDefaultArgExpr(CXXDefaultArgExpr *E) { if (!UsedContextOrErr) return UsedContextOrErr.takeError(); - return CXXDefaultArgExpr::Create( - Importer.getToContext(), *ToUsedLocOrErr, *ToParamOrErr, *UsedContextOrErr); + // Import the default arg if it was not imported yet. + // This is needed because it can happen that during the import of the + // default expression (from VisitParmVarDecl) the same ParmVarDecl is + // encountered here. The default argument for a ParmVarDecl is set in the + // ParmVarDecl only after it is imported (set in VisitParmVarDecl if not here, + // see VisitParmVarDecl). + ParmVarDecl *ToParam = *ToParamOrErr; + if (!ToParam->getDefaultArg()) { + Optional FromParam = Importer.getImportedFromDecl(ToParam); + assert(FromParam && "ParmVarDecl was not imported?"); + + if (Error Err = ImportDefaultArgOfParmVarDecl(*FromParam, ToParam)) + return std::move(Err); + } + + return CXXDefaultArgExpr::Create(Importer.getToContext(), *ToUsedLocOrErr, + *ToParamOrErr, *UsedContextOrErr); } ExpectedStmt @@ -7701,15 +7762,18 @@ ExpectedStmt ASTNodeImporter::VisitCXXTypeidExpr(CXXTypeidExpr *E) { *ToTypeOrErr, *ToExprOperandOrErr, *ToSourceRangeOrErr); } -void ASTNodeImporter::ImportOverrides(CXXMethodDecl *ToMethod, - CXXMethodDecl *FromMethod) { +Error ASTNodeImporter::ImportOverriddenMethods(CXXMethodDecl *ToMethod, + CXXMethodDecl *FromMethod) { + Error ImportErrors = Error::success(); for (auto *FromOverriddenMethod : FromMethod->overridden_methods()) { if (auto ImportedOrErr = import(FromOverriddenMethod)) ToMethod->getCanonicalDecl()->addOverriddenMethod(cast( (*ImportedOrErr)->getCanonicalDecl())); else - consumeError(ImportedOrErr.takeError()); + ImportErrors = + joinErrors(std::move(ImportErrors), ImportedOrErr.takeError()); } + return ImportErrors; } ASTImporter::ASTImporter(ASTContext &ToContext, FileManager &ToFileManager, @@ -7718,7 +7782,7 @@ ASTImporter::ASTImporter(ASTContext &ToContext, FileManager &ToFileManager, std::shared_ptr SharedState) : SharedState(SharedState), ToContext(ToContext), FromContext(FromContext), ToFileManager(ToFileManager), FromFileManager(FromFileManager), - Minimal(MinimalImport) { + Minimal(MinimalImport), ODRHandling(ODRHandlingType::Conservative) { // Create a default state without the lookup table: LLDB case. if (!SharedState) { @@ -8390,13 +8454,13 @@ Expected ASTImporter::Import(FileID FromID, bool IsBuiltin) { // disk again // FIXME: We definitely want to re-use the existing MemoryBuffer, rather // than mmap the files several times. - const FileEntry *Entry = + auto Entry = ToFileManager.getFile(Cache->OrigEntry->getName()); // FIXME: The filename may be a virtual name that does probably not // point to a valid file and we get no Entry here. In this case try with // the memory buffer below. if (Entry) - ToID = ToSM.createFileID(Entry, *ToIncludeLoc, + ToID = ToSM.createFileID(*Entry, *ToIncludeLoc, FromSLoc.getFile().getFileCharacteristic()); } } @@ -8404,8 +8468,9 @@ Expected ASTImporter::Import(FileID FromID, bool IsBuiltin) { if (ToID.isInvalid() || IsBuiltin) { // FIXME: We want to re-use the existing MemoryBuffer! bool Invalid = true; - const llvm::MemoryBuffer *FromBuf = Cache->getBuffer( - FromContext.getDiagnostics(), FromSM, SourceLocation{}, &Invalid); + const llvm::MemoryBuffer *FromBuf = + Cache->getBuffer(FromContext.getDiagnostics(), + FromSM.getFileManager(), SourceLocation{}, &Invalid); if (!FromBuf || Invalid) // FIXME: Use a new error kind? return llvm::make_error(ImportError::Unknown); @@ -8421,6 +8486,10 @@ Expected ASTImporter::Import(FileID FromID, bool IsBuiltin) { assert(ToID.isValid() && "Unexpected invalid fileID was created."); ImportedFileIDs[FromID] = ToID; + + if (FileIDImportHandler) + FileIDImportHandler(ToID, FromID); + return ToID; } @@ -8640,12 +8709,17 @@ Expected ASTImporter::Import(Selector FromSel) { return ToContext.Selectors.getSelector(FromSel.getNumArgs(), Idents.data()); } -DeclarationName ASTImporter::HandleNameConflict(DeclarationName Name, - DeclContext *DC, - unsigned IDNS, - NamedDecl **Decls, - unsigned NumDecls) { - return Name; +Expected ASTImporter::HandleNameConflict(DeclarationName Name, + DeclContext *DC, + unsigned IDNS, + NamedDecl **Decls, + unsigned NumDecls) { + if (ODRHandling == ODRHandlingType::Conservative) + // Report error at any name conflict. + return make_error(ImportError::NameConflict); + else + // Allow to create the new Decl with the same name. + return Name; } DiagnosticBuilder ASTImporter::ToDiag(SourceLocation Loc, unsigned DiagID) { diff --git a/lib/AST/ASTStructuralEquivalence.cpp b/lib/AST/ASTStructuralEquivalence.cpp index 912db3c130c51..db48405055cda 100644 --- a/lib/AST/ASTStructuralEquivalence.cpp +++ b/lib/AST/ASTStructuralEquivalence.cpp @@ -235,12 +235,21 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, const TemplateName &N1, const TemplateName &N2) { - if (N1.getKind() != N2.getKind()) + TemplateDecl *TemplateDeclN1 = N1.getAsTemplateDecl(); + TemplateDecl *TemplateDeclN2 = N2.getAsTemplateDecl(); + if (TemplateDeclN1 && TemplateDeclN2) { + if (!IsStructurallyEquivalent(Context, TemplateDeclN1, TemplateDeclN2)) + return false; + // If the kind is different we compare only the template decl. + if (N1.getKind() != N2.getKind()) + return true; + } else if (TemplateDeclN1 || TemplateDeclN2) + return false; + else if (N1.getKind() != N2.getKind()) return false; + + // Check for special case incompatibilities. switch (N1.getKind()) { - case TemplateName::Template: - return IsStructurallyEquivalent(Context, N1.getAsTemplateDecl(), - N2.getAsTemplateDecl()); case TemplateName::OverloadedTemplate: { OverloadedTemplateStorage *OS1 = N1.getAsOverloadedTemplate(), @@ -259,14 +268,6 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, return TN1->getDeclName() == TN2->getDeclName(); } - case TemplateName::QualifiedTemplate: { - QualifiedTemplateName *QN1 = N1.getAsQualifiedTemplateName(), - *QN2 = N2.getAsQualifiedTemplateName(); - return IsStructurallyEquivalent(Context, QN1->getDecl(), QN2->getDecl()) && - IsStructurallyEquivalent(Context, QN1->getQualifier(), - QN2->getQualifier()); - } - case TemplateName::DependentTemplate: { DependentTemplateName *DN1 = N1.getAsDependentTemplateName(), *DN2 = N2.getAsDependentTemplateName(); @@ -281,15 +282,6 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, return false; } - case TemplateName::SubstTemplateTemplateParm: { - SubstTemplateTemplateParmStorage *TS1 = N1.getAsSubstTemplateTemplateParm(), - *TS2 = N2.getAsSubstTemplateTemplateParm(); - return IsStructurallyEquivalent(Context, TS1->getParameter(), - TS2->getParameter()) && - IsStructurallyEquivalent(Context, TS1->getReplacement(), - TS2->getReplacement()); - } - case TemplateName::SubstTemplateTemplateParmPack: { SubstTemplateTemplateParmPackStorage *P1 = N1.getAsSubstTemplateTemplateParmPack(), @@ -299,8 +291,16 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, IsStructurallyEquivalent(Context, P1->getParameterPack(), P2->getParameterPack()); } + + case TemplateName::Template: + case TemplateName::QualifiedTemplate: + case TemplateName::SubstTemplateTemplateParm: + // It is sufficient to check value of getAsTemplateDecl. + break; + } - return false; + + return true; } /// Determine whether two template arguments are equivalent. @@ -1574,20 +1574,24 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, Decl *D1, Decl *D2) { // FIXME: Check for known structural equivalences via a callback of some sort. + D1 = D1->getCanonicalDecl(); + D2 = D2->getCanonicalDecl(); + std::pair P{D1, D2}; + // Check whether we already know that these two declarations are not // structurally equivalent. - if (Context.NonEquivalentDecls.count( - std::make_pair(D1->getCanonicalDecl(), D2->getCanonicalDecl()))) + if (Context.NonEquivalentDecls.count(P)) return false; - // Determine whether we've already produced a tentative equivalence for D1. - Decl *&EquivToD1 = Context.TentativeEquivalences[D1->getCanonicalDecl()]; - if (EquivToD1) - return EquivToD1 == D2->getCanonicalDecl(); + // Check if a check for these declarations is already pending. + // If yes D1 and D2 will be checked later (from DeclsToCheck), + // or these are already checked (and equivalent). + bool Inserted = Context.VisitedDecls.insert(P).second; + if (!Inserted) + return true; + + Context.DeclsToCheck.push(P); - // Produce a tentative equivalence D1 <-> D2, which will be checked later. - EquivToD1 = D2->getCanonicalDecl(); - Context.DeclsToCheck.push_back(D1->getCanonicalDecl()); return true; } @@ -1703,11 +1707,13 @@ bool StructuralEquivalenceContext::IsEquivalent(Decl *D1, Decl *D2) { // Ensure that the implementation functions (all static functions in this TU) // never call the public ASTStructuralEquivalence::IsEquivalent() functions, // because that will wreak havoc the internal state (DeclsToCheck and - // TentativeEquivalences members) and can cause faulty behaviour. For - // instance, some leaf declarations can be stated and cached as inequivalent - // as a side effect of one inequivalent element in the DeclsToCheck list. + // VisitedDecls members) and can cause faulty behaviour. + // In other words: Do not start a graph search from a new node with the + // internal data of another search in progress. + // FIXME: Better encapsulation and separation of internal and public + // functionality. assert(DeclsToCheck.empty()); - assert(TentativeEquivalences.empty()); + assert(VisitedDecls.empty()); if (!::IsStructurallyEquivalent(*this, D1, D2)) return false; @@ -1717,7 +1723,7 @@ bool StructuralEquivalenceContext::IsEquivalent(Decl *D1, Decl *D2) { bool StructuralEquivalenceContext::IsEquivalent(QualType T1, QualType T2) { assert(DeclsToCheck.empty()); - assert(TentativeEquivalences.empty()); + assert(VisitedDecls.empty()); if (!::IsStructurallyEquivalent(*this, T1, T2)) return false; @@ -1876,11 +1882,11 @@ bool StructuralEquivalenceContext::CheckKindSpecificEquivalence( bool StructuralEquivalenceContext::Finish() { while (!DeclsToCheck.empty()) { // Check the next declaration. - Decl *D1 = DeclsToCheck.front(); - DeclsToCheck.pop_front(); + std::pair P = DeclsToCheck.front(); + DeclsToCheck.pop(); - Decl *D2 = TentativeEquivalences[D1]; - assert(D2 && "Unrecorded tentative equivalence?"); + Decl *D1 = P.first; + Decl *D2 = P.second; bool Equivalent = CheckCommonEquivalence(D1, D2) && CheckKindSpecificEquivalence(D1, D2); @@ -1888,8 +1894,8 @@ bool StructuralEquivalenceContext::Finish() { if (!Equivalent) { // Note that these two declarations are not equivalent (and we already // know about it). - NonEquivalentDecls.insert( - std::make_pair(D1->getCanonicalDecl(), D2->getCanonicalDecl())); + NonEquivalentDecls.insert(P); + return true; } } diff --git a/lib/AST/ASTTypeTraits.cpp b/lib/AST/ASTTypeTraits.cpp index ba1581bd3f6b6..6b7f6ec51086f 100644 --- a/lib/AST/ASTTypeTraits.cpp +++ b/lib/AST/ASTTypeTraits.cpp @@ -15,6 +15,7 @@ #include "clang/AST/ASTTypeTraits.h" #include "clang/AST/ASTContext.h" #include "clang/AST/DeclCXX.h" +#include "clang/AST/NestedNameSpecifier.h" namespace clang { namespace ast_type_traits { @@ -36,7 +37,7 @@ const ASTNodeKind::KindInfo ASTNodeKind::AllKindInfo[] = { #include "clang/AST/StmtNodes.inc" { NKI_None, "Type" }, #define TYPE(DERIVED, BASE) { NKI_##BASE, #DERIVED "Type" }, -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" { NKI_None, "OMPClause" }, #define OPENMP_CLAUSE(TextualSpelling, Class) {NKI_OMPClause, #Class}, #include "clang/Basic/OpenMPKinds.def" @@ -103,7 +104,7 @@ ASTNodeKind ASTNodeKind::getFromNode(const Type &T) { #define TYPE(Class, Base) \ case Type::Class: return ASTNodeKind(NKI_##Class##Type); #define ABSTRACT_TYPE(Class, Base) -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" } llvm_unreachable("invalid type kind"); } @@ -115,6 +116,8 @@ ASTNodeKind ASTNodeKind::getFromNode(const OMPClause &C) { #include "clang/Basic/OpenMPKinds.def" case OMPC_threadprivate: case OMPC_uniform: + case OMPC_device_type: + case OMPC_match: case OMPC_unknown: llvm_unreachable("unexpected OpenMP clause kind"); } @@ -129,9 +132,12 @@ void DynTypedNode::print(llvm::raw_ostream &OS, TN->print(OS, PP); else if (const NestedNameSpecifier *NNS = get()) NNS->print(OS, PP); - else if (const NestedNameSpecifierLoc *NNSL = get()) - NNSL->getNestedNameSpecifier()->print(OS, PP); - else if (const QualType *QT = get()) + else if (const NestedNameSpecifierLoc *NNSL = get()) { + if (const NestedNameSpecifier *NNS = NNSL->getNestedNameSpecifier()) + NNS->print(OS, PP); + else + OS << "(empty NestedNameSpecifierLoc)"; + } else if (const QualType *QT = get()) QT->print(OS, PP); else if (const TypeLoc *TL = get()) TL->getType().print(OS, PP); diff --git a/lib/AST/CXXInheritance.cpp b/lib/AST/CXXInheritance.cpp index ecf451b175afb..a3a3794b2edd4 100644 --- a/lib/AST/CXXInheritance.cpp +++ b/lib/AST/CXXInheritance.cpp @@ -44,7 +44,7 @@ void CXXBasePaths::ComputeDeclsFound() { Decls.insert(Path->Decls.front()); NumDeclsFound = Decls.size(); - DeclsFound = llvm::make_unique(NumDeclsFound); + DeclsFound = std::make_unique(NumDeclsFound); std::copy(Decls.begin(), Decls.end(), DeclsFound.get()); } diff --git a/lib/AST/Comment.cpp b/lib/AST/Comment.cpp index 25339c7901e30..23dc7ba93591e 100644 --- a/lib/AST/Comment.cpp +++ b/lib/AST/Comment.cpp @@ -13,10 +13,25 @@ #include "clang/AST/DeclTemplate.h" #include "clang/Basic/CharInfo.h" #include "llvm/Support/ErrorHandling.h" +#include namespace clang { namespace comments { +// Check that no comment class has a non-trival destructor. They are allocated +// with a BumpPtrAllocator and therefore their destructor is not executed. +#define ABSTRACT_COMMENT(COMMENT) +#define COMMENT(CLASS, PARENT) \ + static_assert(std::is_trivially_destructible::value, \ + #CLASS " should be trivially destructible!"); +#include "clang/AST/CommentNodes.inc" +#undef COMMENT +#undef ABSTRACT_COMMENT + +// DeclInfo is also allocated with a BumpPtrAllocator. +static_assert(std::is_trivially_destructible::value, + "DeclInfo should be trivially destructible!"); + const char *Comment::getCommentKindName() const { switch (getCommentKind()) { case NoCommentKind: return "NoCommentKind"; diff --git a/lib/AST/CommentLexer.cpp b/lib/AST/CommentLexer.cpp index 19485f6018c02..c1ea3eab075e2 100644 --- a/lib/AST/CommentLexer.cpp +++ b/lib/AST/CommentLexer.cpp @@ -850,17 +850,14 @@ again: } StringRef Lexer::getSpelling(const Token &Tok, - const SourceManager &SourceMgr, - bool *Invalid) const { + const SourceManager &SourceMgr) const { SourceLocation Loc = Tok.getLocation(); std::pair LocInfo = SourceMgr.getDecomposedLoc(Loc); bool InvalidTemp = false; StringRef File = SourceMgr.getBufferData(LocInfo.first, &InvalidTemp); - if (InvalidTemp) { - *Invalid = true; + if (InvalidTemp) return StringRef(); - } const char *Begin = File.data() + LocInfo.second; return StringRef(Begin, Tok.getLength()); diff --git a/lib/AST/CommentParser.cpp b/lib/AST/CommentParser.cpp index c7f8aa7e16a0a..29983b0a16c3c 100644 --- a/lib/AST/CommentParser.cpp +++ b/lib/AST/CommentParser.cpp @@ -422,6 +422,12 @@ InlineCommandComment *Parser::parseInlineCommand() { IC = S.actOnInlineCommand(CommandTok.getLocation(), CommandTok.getEndLocation(), CommandTok.getCommandID()); + + Diag(CommandTok.getEndLocation().getLocWithOffset(1), + diag::warn_doc_inline_contents_no_argument) + << CommandTok.is(tok::at_command) + << Traits.getCommandInfo(CommandTok.getCommandID())->Name + << SourceRange(CommandTok.getLocation(), CommandTok.getEndLocation()); } Retokenizer.putBackLeftoverTokens(); diff --git a/lib/AST/CommentSema.cpp b/lib/AST/CommentSema.cpp index 067b3ae4222e3..69d61dc55162b 100644 --- a/lib/AST/CommentSema.cpp +++ b/lib/AST/CommentSema.cpp @@ -588,6 +588,8 @@ void Sema::checkReturnsCommand(const BlockCommandComment *Command) { if (isObjCPropertyDecl()) return; if (isFunctionDecl() || isFunctionOrBlockPointerVarLikeDecl()) { + assert(!ThisDeclInfo->ReturnType.isNull() && + "should have a valid return type"); if (ThisDeclInfo->ReturnType->isVoidType()) { unsigned DiagKind; switch (ThisDeclInfo->CommentDecl->getKind()) { @@ -873,6 +875,12 @@ bool Sema::isFunctionOrBlockPointerVarLikeDecl() { // can be ignored. if (QT->getAs()) return false; + if (const auto *P = QT->getAs()) + if (P->getPointeeType()->getAs()) + return false; + if (const auto *P = QT->getAs()) + if (P->getPointeeType()->getAs()) + return false; return QT->isFunctionPointerType() || QT->isBlockPointerType(); } diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 21cf9da18a8b2..80235d8496d22 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -1385,7 +1385,8 @@ LinkageInfo LinkageComputer::computeLVForDecl(const NamedDecl *D, case Decl::CXXRecord: { const auto *Record = cast(D); if (Record->isLambda()) { - if (!Record->getLambdaManglingNumber()) { + if (Record->hasKnownLambdaInternalLinkage() || + !Record->getLambdaManglingNumber()) { // This lambda has no mangling number, so it's internal. return getInternalLinkageFor(D); } @@ -1402,7 +1403,8 @@ LinkageInfo LinkageComputer::computeLVForDecl(const NamedDecl *D, // }; const CXXRecordDecl *OuterMostLambda = getOutermostEnclosingLambda(Record); - if (!OuterMostLambda->getLambdaManglingNumber()) + if (OuterMostLambda->hasKnownLambdaInternalLinkage() || + !OuterMostLambda->getLambdaManglingNumber()) return getInternalLinkageFor(D); return getLVForClosure( @@ -1558,6 +1560,24 @@ void NamedDecl::printQualifiedName(raw_ostream &OS) const { void NamedDecl::printQualifiedName(raw_ostream &OS, const PrintingPolicy &P) const { + if (getDeclContext()->isFunctionOrMethod()) { + // We do not print '(anonymous)' for function parameters without name. + printName(OS); + return; + } + printNestedNameSpecifier(OS, P); + if (getDeclName() || isa(this)) + OS << *this; + else + OS << "(anonymous)"; +} + +void NamedDecl::printNestedNameSpecifier(raw_ostream &OS) const { + printNestedNameSpecifier(OS, getASTContext().getPrintingPolicy()); +} + +void NamedDecl::printNestedNameSpecifier(raw_ostream &OS, + const PrintingPolicy &P) const { const DeclContext *Ctx = getDeclContext(); // For ObjC methods and properties, look through categories and use the @@ -1571,10 +1591,8 @@ void NamedDecl::printQualifiedName(raw_ostream &OS, Ctx = ID; } - if (Ctx->isFunctionOrMethod()) { - printName(OS); + if (Ctx->isFunctionOrMethod()) return; - } using ContextsTy = SmallVector; ContextsTy Contexts; @@ -1644,11 +1662,6 @@ void NamedDecl::printQualifiedName(raw_ostream &OS, } OS << "::"; } - - if (getDeclName() || isa(this)) - OS << *this; - else - OS << "(anonymous)"; } void NamedDecl::getNameForDiagnostic(raw_ostream &OS, @@ -2220,6 +2233,22 @@ Stmt **VarDecl::getInitAddress() { return Init.getAddrOfPtr1(); } +VarDecl *VarDecl::getInitializingDeclaration() { + VarDecl *Def = nullptr; + for (auto I : redecls()) { + if (I->hasInit()) + return I; + + if (I->isThisDeclarationADefinition()) { + if (isStaticDataMember()) + return I; + else + Def = I; + } + } + return Def; +} + bool VarDecl::isOutOfLine() const { if (Decl::isOutOfLine()) return true; @@ -2565,6 +2594,18 @@ bool VarDecl::isNoDestroy(const ASTContext &Ctx) const { !hasAttr())); } +QualType::DestructionKind +VarDecl::needsDestruction(const ASTContext &Ctx) const { + if (EvaluatedStmt *Eval = Init.dyn_cast()) + if (Eval->HasConstantDestruction) + return QualType::DK_none; + + if (isNoDestroy(Ctx)) + return QualType::DK_none; + + return getType().isDestructedType(); +} + MemberSpecializationInfo *VarDecl::getMemberSpecializationInfo() const { if (isStaticDataMember()) // FIXME: Remove ? @@ -2950,8 +2991,7 @@ bool FunctionDecl::isReplaceableGlobalAllocationFunction(bool *IsAligned) const Ty = Ty->getPointeeType(); if (Ty.getCVRQualifiers() != Qualifiers::Const) return false; - const CXXRecordDecl *RD = Ty->getAsCXXRecordDecl(); - if (RD && isNamed(RD, "nothrow_t") && RD->isInStdNamespace()) + if (Ty->isNothrowT()) Consume(); } @@ -3235,6 +3275,9 @@ bool FunctionDecl::doesDeclarationForceExternallyVisibleDefinition() const { return true; } + if (Context.getLangOpts().CPlusPlus) + return false; + if (Context.getLangOpts().GNUInline || hasAttr()) { // With GNU inlining, a declaration with 'inline' but not 'extern', forces // an externally visible definition. @@ -3263,9 +3306,6 @@ bool FunctionDecl::doesDeclarationForceExternallyVisibleDefinition() const { return FoundBody; } - if (Context.getLangOpts().CPlusPlus) - return false; - // C99 6.7.4p6: // [...] If all of the file scope declarations for a function in a // translation unit include the inline function specifier without extern, @@ -3332,7 +3372,8 @@ SourceRange FunctionDecl::getExceptionSpecSourceRange() const { /// an externally visible symbol, but "extern inline" will not create an /// externally visible symbol. bool FunctionDecl::isInlineDefinitionExternallyVisible() const { - assert((doesThisDeclarationHaveABody() || willHaveBody()) && + assert((doesThisDeclarationHaveABody() || willHaveBody() || + hasAttr()) && "Must be a function definition"); assert(isInlined() && "Function must be inline"); ASTContext &Context = getASTContext(); @@ -3344,6 +3385,8 @@ bool FunctionDecl::isInlineDefinitionExternallyVisible() const { // If it's not the case that both 'inline' and 'extern' are // specified on the definition, then this inline definition is // externally visible. + if (Context.getLangOpts().CPlusPlus) + return false; if (!(isInlineSpecified() && getStorageClass() == SC_Extern)) return true; diff --git a/lib/AST/DeclBase.cpp b/lib/AST/DeclBase.cpp index fd80e1532eb5d..77a3a4c679a1a 100644 --- a/lib/AST/DeclBase.cpp +++ b/lib/AST/DeclBase.cpp @@ -12,6 +12,7 @@ #include "clang/AST/DeclBase.h" #include "clang/AST/ASTContext.h" +#include "clang/AST/ASTLambda.h" #include "clang/AST/ASTMutationListener.h" #include "clang/AST/Attr.h" #include "clang/AST/AttrIterator.h" @@ -99,7 +100,7 @@ void *Decl::operator new(std::size_t Size, const ASTContext &Ctx, // Ensure required alignment of the resulting object by adding extra // padding at the start if required. size_t ExtraAlign = - llvm::OffsetToAlignment(sizeof(Module *), alignof(Decl)); + llvm::offsetToAlignment(sizeof(Module *), llvm::Align(alignof(Decl))); auto *Buffer = reinterpret_cast( ::operator new(ExtraAlign + sizeof(Module *) + Size + Extra, Ctx)); Buffer += ExtraAlign; @@ -958,11 +959,11 @@ const FunctionType *Decl::getFunctionType(bool BlocksToo) const { return nullptr; if (Ty->isFunctionPointerType()) - Ty = Ty->getAs()->getPointeeType(); + Ty = Ty->castAs()->getPointeeType(); else if (Ty->isFunctionReferenceType()) - Ty = Ty->getAs()->getPointeeType(); + Ty = Ty->castAs()->getPointeeType(); else if (BlocksToo && Ty->isBlockPointerType()) - Ty = Ty->getAs()->getPointeeType(); + Ty = Ty->castAs()->getPointeeType(); return Ty->getAs(); } @@ -1043,6 +1044,12 @@ DeclContext *DeclContext::getLookupParent() { getLexicalParent()->getRedeclContext()->isRecord()) return getLexicalParent(); + // A lookup within the call operator of a lambda never looks in the lambda + // class; instead, skip to the context in which that closure type is + // declared. + if (isLambdaCallOperator(this)) + return getParent()->getParent(); + return getParent(); } diff --git a/lib/AST/DeclCXX.cpp b/lib/AST/DeclCXX.cpp index 59710a55498f2..12ec44fa02791 100644 --- a/lib/AST/DeclCXX.cpp +++ b/lib/AST/DeclCXX.cpp @@ -95,14 +95,16 @@ CXXRecordDecl::DefinitionData::DefinitionData(CXXRecordDecl *D) HasDefaultedDefaultConstructor(false), DefaultedDefaultConstructorIsConstexpr(true), HasConstexprDefaultConstructor(false), - HasNonLiteralTypeFieldsOrBases(false), ComputedVisibleConversions(false), + DefaultedDestructorIsConstexpr(true), + HasNonLiteralTypeFieldsOrBases(false), UserProvidedDefaultConstructor(false), DeclaredSpecialMembers(0), ImplicitCopyConstructorCanHaveConstParamForVBase(true), ImplicitCopyConstructorCanHaveConstParamForNonVBase(true), ImplicitCopyAssignmentHasConstParam(true), HasDeclaredCopyConstructorWithConstParam(false), HasDeclaredCopyAssignmentWithConstParam(false), IsLambda(false), - IsParsingBaseSpecifiers(false), HasODRHash(false), Definition(D) {} + IsParsingBaseSpecifiers(false), ComputedVisibleConversions(false), + HasODRHash(false), Definition(D) {} CXXBaseSpecifier *CXXRecordDecl::DefinitionData::getBasesSlowCase() const { return Bases.get(Definition->getASTContext().getExternalSource()); @@ -217,7 +219,7 @@ CXXRecordDecl::setBases(CXXBaseSpecifier const * const *Bases, if (BaseType->isDependentType()) continue; auto *BaseClassDecl = - cast(BaseType->getAs()->getDecl()); + cast(BaseType->castAs()->getDecl()); // C++2a [class]p7: // A standard-layout class is a class that: @@ -325,10 +327,12 @@ CXXRecordDecl::setBases(CXXBaseSpecifier const * const *Bases, data().IsStandardLayout = false; data().IsCXX11StandardLayout = false; - // C++11 [dcl.constexpr]p4: - // In the definition of a constexpr constructor [...] - // -- the class shall not have any virtual base classes + // C++20 [dcl.constexpr]p3: + // In the definition of a constexpr function [...] + // -- if the function is a constructor or destructor, + // its class shall not have any virtual base classes data().DefaultedDefaultConstructorIsConstexpr = false; + data().DefaultedDestructorIsConstexpr = false; // C++1z [class.copy]p8: // The implicitly-declared copy constructor for a class X will have @@ -520,6 +524,19 @@ void CXXRecordDecl::addedClassSubobject(CXXRecordDecl *Subobj) { data().NeedOverloadResolutionForMoveConstructor = true; data().NeedOverloadResolutionForDestructor = true; } + + // C++2a [dcl.constexpr]p4: + // The definition of a constexpr destructor [shall] satisfy the + // following requirement: + // -- for every subobject of class type or (possibly multi-dimensional) + // array thereof, that class type shall have a constexpr destructor + if (!Subobj->hasConstexprDestructor()) + data().DefaultedDestructorIsConstexpr = false; +} + +bool CXXRecordDecl::hasConstexprDestructor() const { + auto *Dtor = getDestructor(); + return Dtor ? Dtor->isConstexpr() : defaultedDestructorIsConstexpr(); } bool CXXRecordDecl::hasAnyDependentBases() const { @@ -1263,7 +1280,8 @@ void CXXRecordDecl::addedMember(Decl *D) { } else { // Base element type of field is a non-class type. if (!T->isLiteralType(Context) || - (!Field->hasInClassInitializer() && !isUnion())) + (!Field->hasInClassInitializer() && !isUnion() && + !Context.getLangOpts().CPlusPlus2a)) data().DefaultedDefaultConstructorIsConstexpr = false; // C++11 [class.copy]p23: @@ -1382,17 +1400,29 @@ static bool allLookupResultsAreTheSame(const DeclContext::lookup_result &R) { } #endif -CXXMethodDecl* CXXRecordDecl::getLambdaCallOperator() const { - if (!isLambda()) return nullptr; +static NamedDecl* getLambdaCallOperatorHelper(const CXXRecordDecl &RD) { + if (!RD.isLambda()) return nullptr; DeclarationName Name = - getASTContext().DeclarationNames.getCXXOperatorName(OO_Call); - DeclContext::lookup_result Calls = lookup(Name); + RD.getASTContext().DeclarationNames.getCXXOperatorName(OO_Call); + DeclContext::lookup_result Calls = RD.lookup(Name); assert(!Calls.empty() && "Missing lambda call operator!"); assert(allLookupResultsAreTheSame(Calls) && "More than one lambda call operator!"); + return Calls.front(); +} + +FunctionTemplateDecl* CXXRecordDecl::getDependentLambdaCallOperator() const { + NamedDecl *CallOp = getLambdaCallOperatorHelper(*this); + return dyn_cast_or_null(CallOp); +} + +CXXMethodDecl *CXXRecordDecl::getLambdaCallOperator() const { + NamedDecl *CallOp = getLambdaCallOperatorHelper(*this); + + if (CallOp == nullptr) + return nullptr; - NamedDecl *CallOp = Calls.front(); if (const auto *CallOpTmpl = dyn_cast(CallOp)) return cast(CallOpTmpl->getTemplatedDecl()); @@ -1880,7 +1910,7 @@ bool CXXRecordDecl::mayBeAbstract() const { for (const auto &B : bases()) { const auto *BaseDecl = - cast(B.getType()->getAs()->getDecl()); + cast(B.getType()->castAs()->getDecl()); if (BaseDecl->isAbstract()) return true; } @@ -2067,10 +2097,15 @@ CXXMethodDecl *CXXMethodDecl::getDevirtualizedMethod(const Expr *Base, if (DevirtualizedMethod->hasAttr()) return DevirtualizedMethod; - // Similarly, if the class itself is marked 'final' it can't be overridden - // and we can therefore devirtualize the member function call. + // Similarly, if the class itself or its destructor is marked 'final', + // the class can't be derived from and we can therefore devirtualize the + // member function call. if (BestDynamicDecl->hasAttr()) return DevirtualizedMethod; + if (const auto *dtor = BestDynamicDecl->getDestructor()) { + if (dtor->hasAttr()) + return DevirtualizedMethod; + } if (const auto *DRE = dyn_cast(Base)) { if (const auto *VD = dyn_cast(DRE->getDecl())) @@ -2532,7 +2567,7 @@ bool CXXConstructorDecl::isConvertingConstructor(bool AllowExplicit) const { return false; return (getNumParams() == 0 && - getType()->getAs()->isVariadic()) || + getType()->castAs()->isVariadic()) || (getNumParams() == 1) || (getNumParams() > 1 && (getParamDecl(1)->hasDefaultArg() || @@ -2565,20 +2600,19 @@ CXXDestructorDecl * CXXDestructorDecl::CreateDeserialized(ASTContext &C, unsigned ID) { return new (C, ID) CXXDestructorDecl(C, nullptr, SourceLocation(), DeclarationNameInfo(), - QualType(), nullptr, false, false); + QualType(), nullptr, false, false, CSK_unspecified); } -CXXDestructorDecl * -CXXDestructorDecl::Create(ASTContext &C, CXXRecordDecl *RD, - SourceLocation StartLoc, - const DeclarationNameInfo &NameInfo, - QualType T, TypeSourceInfo *TInfo, - bool isInline, bool isImplicitlyDeclared) { +CXXDestructorDecl *CXXDestructorDecl::Create( + ASTContext &C, CXXRecordDecl *RD, SourceLocation StartLoc, + const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo, + bool isInline, bool isImplicitlyDeclared, ConstexprSpecKind ConstexprKind) { assert(NameInfo.getName().getNameKind() == DeclarationName::CXXDestructorName && "Name must refer to a destructor"); - return new (C, RD) CXXDestructorDecl(C, RD, StartLoc, NameInfo, T, TInfo, - isInline, isImplicitlyDeclared); + return new (C, RD) + CXXDestructorDecl(C, RD, StartLoc, NameInfo, T, TInfo, isInline, + isImplicitlyDeclared, ConstexprKind); } void CXXDestructorDecl::setOperatorDelete(FunctionDecl *OD, Expr *ThisArg) { diff --git a/lib/AST/DeclPrinter.cpp b/lib/AST/DeclPrinter.cpp index f5c69944034a5..608b0b44072b7 100644 --- a/lib/AST/DeclPrinter.cpp +++ b/lib/AST/DeclPrinter.cpp @@ -1001,12 +1001,19 @@ void DeclPrinter::VisitCXXRecordDecl(CXXRecordDecl *D) { void DeclPrinter::VisitLinkageSpecDecl(LinkageSpecDecl *D) { const char *l; - if (D->getLanguage() == LinkageSpecDecl::lang_c) + switch (D->getLanguage()) { + case LinkageSpecDecl::lang_c: l = "C"; - else { - assert(D->getLanguage() == LinkageSpecDecl::lang_cxx && - "unknown language in linkage specification"); + break; + case LinkageSpecDecl::lang_cxx_14: + l = "C++14"; + break; + case LinkageSpecDecl::lang_cxx_11: + l = "C++11"; + break; + case LinkageSpecDecl::lang_cxx: l = "C++"; + break; } Out << "extern \"" << l << "\" "; diff --git a/lib/AST/DeclTemplate.cpp b/lib/AST/DeclTemplate.cpp index 40c39c845db63..7e013c6c54d8f 100644 --- a/lib/AST/DeclTemplate.cpp +++ b/lib/AST/DeclTemplate.cpp @@ -70,6 +70,8 @@ TemplateParameterList::TemplateParameterList(SourceLocation TemplateLoc, } if (RequiresClause) { *getTrailingObjects() = RequiresClause; + if (RequiresClause->containsUnexpandedParameterPack()) + ContainsUnexpandedParameterPack = true; } } @@ -136,6 +138,18 @@ static void AdoptTemplateParameterList(TemplateParameterList *Params, } } +void TemplateParameterList:: +getAssociatedConstraints(llvm::SmallVectorImpl &AC) const { + // TODO: Concepts: Collect immediately-introduced constraints. + if (HasRequiresClause) + AC.push_back(getRequiresClause()); +} + +bool TemplateParameterList::hasAssociatedConstraints() const { + // TODO: Concepts: Regard immediately-introduced constraints. + return HasRequiresClause; +} + namespace clang { void *allocateDefaultArgStorageChain(const ASTContext &C) { @@ -144,6 +158,28 @@ void *allocateDefaultArgStorageChain(const ASTContext &C) { } // namespace clang +//===----------------------------------------------------------------------===// +// TemplateDecl Implementation +//===----------------------------------------------------------------------===// + +TemplateDecl::TemplateDecl(Kind DK, DeclContext *DC, SourceLocation L, + DeclarationName Name, TemplateParameterList *Params, + NamedDecl *Decl) + : NamedDecl(DK, DC, L, Name), TemplatedDecl(Decl), TemplateParams(Params) {} + +void TemplateDecl::anchor() {} + +void TemplateDecl:: +getAssociatedConstraints(llvm::SmallVectorImpl &AC) const { + // TODO: Concepts: Append function trailing requires clause. + TemplateParams->getAssociatedConstraints(AC); +} + +bool TemplateDecl::hasAssociatedConstraints() const { + // TODO: Concepts: Regard function trailing requires clause. + return TemplateParams->hasAssociatedConstraints(); +} + //===----------------------------------------------------------------------===// // RedeclarableTemplateDecl Implementation //===----------------------------------------------------------------------===// @@ -344,19 +380,10 @@ ClassTemplateDecl *ClassTemplateDecl::Create(ASTContext &C, SourceLocation L, DeclarationName Name, TemplateParameterList *Params, - NamedDecl *Decl, - Expr *AssociatedConstraints) { + NamedDecl *Decl) { AdoptTemplateParameterList(Params, cast(Decl)); - if (!AssociatedConstraints) { - return new (C, DC) ClassTemplateDecl(C, DC, L, Name, Params, Decl); - } - - auto *const CTDI = new (C) ConstrainedTemplateDeclInfo; - auto *const New = - new (C, DC) ClassTemplateDecl(CTDI, C, DC, L, Name, Params, Decl); - New->setAssociatedConstraints(AssociatedConstraints); - return New; + return new (C, DC) ClassTemplateDecl(C, DC, L, Name, Params, Decl); } ClassTemplateDecl *ClassTemplateDecl::CreateDeserialized(ASTContext &C, @@ -510,20 +537,24 @@ SourceRange TemplateTypeParmDecl::getSourceRange() const { if (hasDefaultArgument() && !defaultArgumentWasInherited()) return SourceRange(getBeginLoc(), getDefaultArgumentInfo()->getTypeLoc().getEndLoc()); - else - return TypeDecl::getSourceRange(); + // TypeDecl::getSourceRange returns a range containing name location, which is + // wrong for unnamed template parameters. e.g: + // it will return <[[typename>]] instead of <[[typename]]> + else if (getDeclName().isEmpty()) + return SourceRange(getBeginLoc()); + return TypeDecl::getSourceRange(); } unsigned TemplateTypeParmDecl::getDepth() const { - return getTypeForDecl()->getAs()->getDepth(); + return getTypeForDecl()->castAs()->getDepth(); } unsigned TemplateTypeParmDecl::getIndex() const { - return getTypeForDecl()->getAs()->getIndex(); + return getTypeForDecl()->castAs()->getIndex(); } bool TemplateTypeParmDecl::isParameterPack() const { - return getTypeForDecl()->getAs()->isParameterPack(); + return getTypeForDecl()->castAs()->isParameterPack(); } //===----------------------------------------------------------------------===// @@ -703,12 +734,6 @@ FunctionTemplateSpecializationInfo *FunctionTemplateSpecializationInfo::Create( FD, Template, TSK, TemplateArgs, ArgsAsWritten, POI, MSInfo); } -//===----------------------------------------------------------------------===// -// TemplateDecl Implementation -//===----------------------------------------------------------------------===// - -void TemplateDecl::anchor() {} - //===----------------------------------------------------------------------===// // ClassTemplateSpecializationDecl Implementation //===----------------------------------------------------------------------===// diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index 6ef77b8aee684..3438c3aadc6b8 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -85,8 +85,8 @@ const Expr *Expr::skipRValueSubobjectAdjustments( CE->getCastKind() == CK_UncheckedDerivedToBase) && E->getType()->isRecordType()) { E = CE->getSubExpr(); - CXXRecordDecl *Derived - = cast(E->getType()->getAs()->getDecl()); + auto *Derived = + cast(E->getType()->castAs()->getDecl()); Adjustments.push_back(SubobjectAdjustment(CE, Derived)); continue; } @@ -185,6 +185,12 @@ bool Expr::isKnownToHaveBooleanValue() const { return CO->getTrueExpr()->isKnownToHaveBooleanValue() && CO->getFalseExpr()->isKnownToHaveBooleanValue(); + if (isa(E)) + return true; + + if (const auto *OVE = dyn_cast(E)) + return OVE->getSourceExpr()->isKnownToHaveBooleanValue(); + return false; } @@ -2563,13 +2569,31 @@ bool Expr::isUnusedResultAWarning(const Expr *&WarnE, SourceLocation &Loc, case CXXTemporaryObjectExprClass: case CXXConstructExprClass: { if (const CXXRecordDecl *Type = getType()->getAsCXXRecordDecl()) { - if (Type->hasAttr()) { + const auto *WarnURAttr = Type->getAttr(); + if (Type->hasAttr() || + (WarnURAttr && WarnURAttr->IsCXX11NoDiscard())) { WarnE = this; Loc = getBeginLoc(); R1 = getSourceRange(); return true; } } + + const auto *CE = cast(this); + if (const CXXConstructorDecl *Ctor = CE->getConstructor()) { + const auto *WarnURAttr = Ctor->getAttr(); + if (WarnURAttr && WarnURAttr->IsCXX11NoDiscard()) { + WarnE = this; + Loc = getBeginLoc(); + R1 = getSourceRange(); + + if (unsigned NumArgs = CE->getNumArgs()) + R2 = SourceRange(CE->getArg(0)->getBeginLoc(), + CE->getArg(NumArgs - 1)->getEndLoc()); + return true; + } + } + return false; } @@ -3181,7 +3205,7 @@ bool Expr::isConstantInitializer(ASTContext &Ctx, bool IsForRef, if (ILE->getType()->isRecordType()) { unsigned ElementNo = 0; - RecordDecl *RD = ILE->getType()->getAs()->getDecl(); + RecordDecl *RD = ILE->getType()->castAs()->getDecl(); for (const auto *Field : RD->fields()) { // If this is a union, skip all the fields that aren't being initialized. if (RD->isUnion() && ILE->getInitializedFieldInUnion() != Field) @@ -3379,6 +3403,7 @@ bool Expr::HasSideEffects(const ASTContext &Ctx, case CXXUuidofExprClass: case OpaqueValueExprClass: case SourceLocExprClass: + case ConceptSpecializationExprClass: // These never have a side-effect. return false; @@ -3448,6 +3473,7 @@ bool Expr::HasSideEffects(const ASTContext &Ctx, case ArrayInitLoopExprClass: case ParenListExprClass: case CXXPseudoDestructorExprClass: + case CXXRewrittenBinaryOperatorClass: case CXXStdInitializerListExprClass: case SubstNonTypeTemplateParmExprClass: case MaterializeTemporaryExprClass: @@ -3897,6 +3923,112 @@ bool Expr::refersToGlobalRegisterVar() const { return false; } +bool Expr::isSameComparisonOperand(const Expr* E1, const Expr* E2) { + E1 = E1->IgnoreParens(); + E2 = E2->IgnoreParens(); + + if (E1->getStmtClass() != E2->getStmtClass()) + return false; + + switch (E1->getStmtClass()) { + default: + return false; + case CXXThisExprClass: + return true; + case DeclRefExprClass: { + // DeclRefExpr without an ImplicitCastExpr can happen for integral + // template parameters. + const auto *DRE1 = cast(E1); + const auto *DRE2 = cast(E2); + return DRE1->isRValue() && DRE2->isRValue() && + DRE1->getDecl() == DRE2->getDecl(); + } + case ImplicitCastExprClass: { + // Peel off implicit casts. + while (true) { + const auto *ICE1 = dyn_cast(E1); + const auto *ICE2 = dyn_cast(E2); + if (!ICE1 || !ICE2) + return false; + if (ICE1->getCastKind() != ICE2->getCastKind()) + return false; + E1 = ICE1->getSubExpr()->IgnoreParens(); + E2 = ICE2->getSubExpr()->IgnoreParens(); + // The final cast must be one of these types. + if (ICE1->getCastKind() == CK_LValueToRValue || + ICE1->getCastKind() == CK_ArrayToPointerDecay || + ICE1->getCastKind() == CK_FunctionToPointerDecay) { + break; + } + } + + const auto *DRE1 = dyn_cast(E1); + const auto *DRE2 = dyn_cast(E2); + if (DRE1 && DRE2) + return declaresSameEntity(DRE1->getDecl(), DRE2->getDecl()); + + const auto *Ivar1 = dyn_cast(E1); + const auto *Ivar2 = dyn_cast(E2); + if (Ivar1 && Ivar2) { + return Ivar1->isFreeIvar() && Ivar2->isFreeIvar() && + declaresSameEntity(Ivar1->getDecl(), Ivar2->getDecl()); + } + + const auto *Array1 = dyn_cast(E1); + const auto *Array2 = dyn_cast(E2); + if (Array1 && Array2) { + if (!isSameComparisonOperand(Array1->getBase(), Array2->getBase())) + return false; + + auto Idx1 = Array1->getIdx(); + auto Idx2 = Array2->getIdx(); + const auto Integer1 = dyn_cast(Idx1); + const auto Integer2 = dyn_cast(Idx2); + if (Integer1 && Integer2) { + if (!llvm::APInt::isSameValue(Integer1->getValue(), + Integer2->getValue())) + return false; + } else { + if (!isSameComparisonOperand(Idx1, Idx2)) + return false; + } + + return true; + } + + // Walk the MemberExpr chain. + while (isa(E1) && isa(E2)) { + const auto *ME1 = cast(E1); + const auto *ME2 = cast(E2); + if (!declaresSameEntity(ME1->getMemberDecl(), ME2->getMemberDecl())) + return false; + if (const auto *D = dyn_cast(ME1->getMemberDecl())) + if (D->isStaticDataMember()) + return true; + E1 = ME1->getBase()->IgnoreParenImpCasts(); + E2 = ME2->getBase()->IgnoreParenImpCasts(); + } + + if (isa(E1) && isa(E2)) + return true; + + // A static member variable can end the MemberExpr chain with either + // a MemberExpr or a DeclRefExpr. + auto getAnyDecl = [](const Expr *E) -> const ValueDecl * { + if (const auto *DRE = dyn_cast(E)) + return DRE->getDecl(); + if (const auto *ME = dyn_cast(E)) + return ME->getMemberDecl(); + return nullptr; + }; + + const ValueDecl *VD1 = getAnyDecl(E1); + const ValueDecl *VD2 = getAnyDecl(E2); + return declaresSameEntity(VD1, VD2); + } + } +} + /// isArrow - Return true if the base expression is a pointer to vector, /// return false if the base expression is a vector. bool ExtVectorElementExpr::isArrow() const { diff --git a/lib/AST/ExprCXX.cpp b/lib/AST/ExprCXX.cpp index b30f785ba8f54..904928bdf2861 100644 --- a/lib/AST/ExprCXX.cpp +++ b/lib/AST/ExprCXX.cpp @@ -58,6 +58,76 @@ bool CXXOperatorCallExpr::isInfixBinaryOp() const { } } +CXXRewrittenBinaryOperator::DecomposedForm +CXXRewrittenBinaryOperator::getDecomposedForm() const { + DecomposedForm Result = {}; + const Expr *E = getSemanticForm()->IgnoreImplicit(); + + // Remove an outer '!' if it exists (only happens for a '!=' rewrite). + bool SkippedNot = false; + if (auto *NotEq = dyn_cast(E)) { + assert(NotEq->getOpcode() == UO_LNot); + E = NotEq->getSubExpr()->IgnoreImplicit(); + SkippedNot = true; + } + + // Decompose the outer binary operator. + if (auto *BO = dyn_cast(E)) { + assert(!SkippedNot || BO->getOpcode() == BO_EQ); + Result.Opcode = SkippedNot ? BO_NE : BO->getOpcode(); + Result.LHS = BO->getLHS(); + Result.RHS = BO->getRHS(); + Result.InnerBinOp = BO; + } else if (auto *BO = dyn_cast(E)) { + assert(!SkippedNot || BO->getOperator() == OO_EqualEqual); + assert(BO->isInfixBinaryOp()); + switch (BO->getOperator()) { + case OO_Less: Result.Opcode = BO_LT; break; + case OO_LessEqual: Result.Opcode = BO_LE; break; + case OO_Greater: Result.Opcode = BO_GT; break; + case OO_GreaterEqual: Result.Opcode = BO_GE; break; + case OO_Spaceship: Result.Opcode = BO_Cmp; break; + case OO_EqualEqual: Result.Opcode = SkippedNot ? BO_NE : BO_EQ; break; + default: llvm_unreachable("unexpected binop in rewritten operator expr"); + } + Result.LHS = BO->getArg(0); + Result.RHS = BO->getArg(1); + Result.InnerBinOp = BO; + } else { + llvm_unreachable("unexpected rewritten operator form"); + } + + // Put the operands in the right order for == and !=, and canonicalize the + // <=> subexpression onto the LHS for all other forms. + if (isReversed()) + std::swap(Result.LHS, Result.RHS); + + // If this isn't a spaceship rewrite, we're done. + if (Result.Opcode == BO_EQ || Result.Opcode == BO_NE) + return Result; + + // Otherwise, we expect a <=> to now be on the LHS. + E = Result.LHS->IgnoreImplicit(); + if (auto *BO = dyn_cast(E)) { + assert(BO->getOpcode() == BO_Cmp); + Result.LHS = BO->getLHS(); + Result.RHS = BO->getRHS(); + Result.InnerBinOp = BO; + } else if (auto *BO = dyn_cast(E)) { + assert(BO->getOperator() == OO_Spaceship); + Result.LHS = BO->getArg(0); + Result.RHS = BO->getArg(1); + Result.InnerBinOp = BO; + } else { + llvm_unreachable("unexpected rewritten operator form"); + } + + // Put the comparison operands in the right order. + if (isReversed()) + std::swap(Result.LHS, Result.RHS); + return Result; +} + bool CXXTypeidExpr::isPotentiallyEvaluated() const { if (isTypeOperand()) return false; @@ -124,6 +194,8 @@ CXXNewExpr::CXXNewExpr(bool IsGlobalNew, FunctionDecl *OperatorNew, if (ArraySize) { if (Expr *SizeExpr = *ArraySize) { + if (SizeExpr->isValueDependent()) + ExprBits.ValueDependent = true; if (SizeExpr->isInstantiationDependent()) ExprBits.InstantiationDependent = true; if (SizeExpr->containsUnexpandedParameterPack()) @@ -134,6 +206,8 @@ CXXNewExpr::CXXNewExpr(bool IsGlobalNew, FunctionDecl *OperatorNew, } if (Initializer) { + if (Initializer->isValueDependent()) + ExprBits.ValueDependent = true; if (Initializer->isInstantiationDependent()) ExprBits.InstantiationDependent = true; if (Initializer->containsUnexpandedParameterPack()) @@ -143,6 +217,8 @@ CXXNewExpr::CXXNewExpr(bool IsGlobalNew, FunctionDecl *OperatorNew, } for (unsigned I = 0; I != PlacementArgs.size(); ++I) { + if (PlacementArgs[I]->isValueDependent()) + ExprBits.ValueDependent = true; if (PlacementArgs[I]->isInstantiationDependent()) ExprBits.InstantiationDependent = true; if (PlacementArgs[I]->containsUnexpandedParameterPack()) @@ -245,7 +321,7 @@ QualType CXXDeleteExpr::getDestroyedType() const { if (ArgType->isDependentType() && !ArgType->isPointerType()) return QualType(); - return ArgType->getAs()->getPointeeType(); + return ArgType->castAs()->getPointeeType(); } // CXXPseudoDestructorExpr @@ -651,6 +727,13 @@ Expr *CXXMemberCallExpr::getImplicitObjectArgument() const { return nullptr; } +QualType CXXMemberCallExpr::getObjectType() const { + QualType Ty = getImplicitObjectArgument()->getType(); + if (Ty->isPointerType()) + Ty = Ty->getPointeeType(); + return Ty; +} + CXXMethodDecl *CXXMemberCallExpr::getMethodDecl() const { if (const auto *MemExpr = dyn_cast(getCallee()->IgnoreParens())) return cast(MemExpr->getMemberDecl()); @@ -1205,6 +1288,11 @@ CXXMethodDecl *LambdaExpr::getCallOperator() const { return Record->getLambdaCallOperator(); } +FunctionTemplateDecl *LambdaExpr::getDependentCallOperator() const { + CXXRecordDecl *Record = getLambdaClass(); + return Record->getDependentLambdaCallOperator(); +} + TemplateParameterList *LambdaExpr::getTemplateParameterList() const { CXXRecordDecl *Record = getLambdaClass(); return Record->getGenericLambdaTemplateParameterList(); @@ -1494,11 +1582,8 @@ CXXRecordDecl *UnresolvedMemberExpr::getNamingClass() { // Otherwise the naming class must have been the base class. else { QualType BaseType = getBaseType().getNonReferenceType(); - if (isArrow()) { - const auto *PT = BaseType->getAs(); - assert(PT && "base of arrow member access is not pointer"); - BaseType = PT->getPointeeType(); - } + if (isArrow()) + BaseType = BaseType->castAs()->getPointeeType(); Record = BaseType->getAsCXXRecordDecl(); assert(Record && "base of member expression does not name record"); @@ -1665,3 +1750,82 @@ CUDAKernelCallExpr *CUDAKernelCallExpr::CreateEmpty(const ASTContext &Ctx, alignof(CUDAKernelCallExpr)); return new (Mem) CUDAKernelCallExpr(NumArgs, Empty); } + +ConceptSpecializationExpr::ConceptSpecializationExpr(ASTContext &C, + NestedNameSpecifierLoc NNS, SourceLocation TemplateKWLoc, + SourceLocation ConceptNameLoc, NamedDecl *FoundDecl, + ConceptDecl *NamedConcept, const ASTTemplateArgumentListInfo *ArgsAsWritten, + ArrayRef ConvertedArgs, Optional IsSatisfied) + : Expr(ConceptSpecializationExprClass, C.BoolTy, VK_RValue, OK_Ordinary, + /*TypeDependent=*/false, + // All the flags below are set in setTemplateArguments. + /*ValueDependent=*/!IsSatisfied.hasValue(), + /*InstantiationDependent=*/false, + /*ContainsUnexpandedParameterPacks=*/false), + NestedNameSpec(NNS), TemplateKWLoc(TemplateKWLoc), + ConceptNameLoc(ConceptNameLoc), FoundDecl(FoundDecl), + NamedConcept(NamedConcept, IsSatisfied ? *IsSatisfied : true), + NumTemplateArgs(ConvertedArgs.size()) { + + setTemplateArguments(ArgsAsWritten, ConvertedArgs); +} + +ConceptSpecializationExpr::ConceptSpecializationExpr(EmptyShell Empty, + unsigned NumTemplateArgs) + : Expr(ConceptSpecializationExprClass, Empty), + NumTemplateArgs(NumTemplateArgs) { } + +void ConceptSpecializationExpr::setTemplateArguments( + const ASTTemplateArgumentListInfo *ArgsAsWritten, + ArrayRef Converted) { + assert(Converted.size() == NumTemplateArgs); + assert(!this->ArgsAsWritten && "setTemplateArguments can only be used once"); + this->ArgsAsWritten = ArgsAsWritten; + std::uninitialized_copy(Converted.begin(), Converted.end(), + getTrailingObjects()); + bool IsInstantiationDependent = false; + bool ContainsUnexpandedParameterPack = false; + for (const TemplateArgumentLoc& LocInfo : ArgsAsWritten->arguments()) { + if (LocInfo.getArgument().isInstantiationDependent()) + IsInstantiationDependent = true; + if (LocInfo.getArgument().containsUnexpandedParameterPack()) + ContainsUnexpandedParameterPack = true; + if (ContainsUnexpandedParameterPack && IsInstantiationDependent) + break; + } + + // Currently guaranteed by the fact concepts can only be at namespace-scope. + assert(!NestedNameSpec || + (!NestedNameSpec.getNestedNameSpecifier()->isInstantiationDependent() && + !NestedNameSpec.getNestedNameSpecifier() + ->containsUnexpandedParameterPack())); + setInstantiationDependent(IsInstantiationDependent); + setContainsUnexpandedParameterPack(ContainsUnexpandedParameterPack); + assert((!isValueDependent() || isInstantiationDependent()) && + "should not be value-dependent"); +} + +ConceptSpecializationExpr * +ConceptSpecializationExpr::Create(ASTContext &C, NestedNameSpecifierLoc NNS, + SourceLocation TemplateKWLoc, + SourceLocation ConceptNameLoc, + NamedDecl *FoundDecl, + ConceptDecl *NamedConcept, + const ASTTemplateArgumentListInfo *ArgsAsWritten, + ArrayRef ConvertedArgs, + Optional IsSatisfied) { + void *Buffer = C.Allocate(totalSizeToAlloc( + ConvertedArgs.size())); + return new (Buffer) ConceptSpecializationExpr(C, NNS, TemplateKWLoc, + ConceptNameLoc, FoundDecl, + NamedConcept, ArgsAsWritten, + ConvertedArgs, IsSatisfied); +} + +ConceptSpecializationExpr * +ConceptSpecializationExpr::Create(ASTContext &C, EmptyShell Empty, + unsigned NumTemplateArgs) { + void *Buffer = C.Allocate(totalSizeToAlloc( + NumTemplateArgs)); + return new (Buffer) ConceptSpecializationExpr(Empty, NumTemplateArgs); +} diff --git a/lib/AST/ExprClassification.cpp b/lib/AST/ExprClassification.cpp index c61ee703aca8e..9dbf6fe9e0f06 100644 --- a/lib/AST/ExprClassification.cpp +++ b/lib/AST/ExprClassification.cpp @@ -192,6 +192,7 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) { case Expr::NoInitExprClass: case Expr::DesignatedInitUpdateExprClass: case Expr::SourceLocExprClass: + case Expr::ConceptSpecializationExprClass: return Cl::CL_PRValue; case Expr::ConstantExprClass: @@ -306,6 +307,10 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) { case Expr::CUDAKernelCallExprClass: return ClassifyUnnamed(Ctx, cast(E)->getCallReturnType(Ctx)); + case Expr::CXXRewrittenBinaryOperatorClass: + return ClassifyInternal( + Ctx, cast(E)->getSemanticForm()); + // __builtin_choose_expr is equivalent to the chosen expression. case Expr::ChooseExprClass: return ClassifyInternal(Ctx, cast(E)->getChosenSubExpr()); diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index f01b42e7ff761..42c746e602858 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -32,15 +32,21 @@ // //===----------------------------------------------------------------------===// +#include +#include +#include "Interp/Context.h" +#include "Interp/Frame.h" +#include "Interp/State.h" #include "clang/AST/APValue.h" #include "clang/AST/ASTContext.h" #include "clang/AST/ASTDiagnostic.h" #include "clang/AST/ASTLambda.h" +#include "clang/AST/CXXInheritance.h" #include "clang/AST/CharUnits.h" #include "clang/AST/CurrentSourceLocExprScope.h" -#include "clang/AST/CXXInheritance.h" #include "clang/AST/Expr.h" #include "clang/AST/OSLog.h" +#include "clang/AST/OptionalDiagnostic.h" #include "clang/AST/RecordLayout.h" #include "clang/AST/StmtVisitor.h" #include "clang/AST/TypeLoc.h" @@ -51,8 +57,6 @@ #include "llvm/ADT/SmallBitVector.h" #include "llvm/Support/SaveAndRestore.h" #include "llvm/Support/raw_ostream.h" -#include -#include #define DEBUG_TYPE "exprconstant" @@ -62,12 +66,10 @@ using llvm::APSInt; using llvm::APFloat; using llvm::Optional; -static bool IsGlobalLValue(APValue::LValueBase B); - namespace { struct LValue; - struct CallStackFrame; - struct EvalInfo; + class CallStackFrame; + class EvalInfo; using SourceLocExprScopeGuard = CurrentSourceLocExprScope::SourceLocExprScopeGuard; @@ -94,6 +96,9 @@ namespace { if (B.is()) return B.getTypeInfoType(); + if (B.is()) + return B.getDynamicAllocType(); + const Expr *Base = B.get(); // For a materialized temporary, the type of the temporary we materialized @@ -130,6 +135,14 @@ namespace { return E.getAsBaseOrMember().getInt(); } + /// Given an expression, determine the type used to store the result of + /// evaluating that expression. + static QualType getStorageType(const ASTContext &Ctx, const Expr *E) { + if (E->isRValue()) + return E->getType(); + return Ctx.getLValueReferenceType(E->getType()); + } + /// Given a CallExpr, try to get the alloc_size attribute. May return null. static const AllocSizeAttr *getAllocSizeAttr(const CallExpr *CE) { const FunctionDecl *Callee = CE->getDirectCallee(); @@ -222,12 +235,6 @@ namespace { return MostDerivedLength; } - // The order of this enum is important for diagnostics. - enum CheckSubobjectKind { - CSK_Base, CSK_Derived, CSK_Field, CSK_ArrayToPointer, CSK_ArrayIndex, - CSK_Real, CSK_Imag - }; - /// A path from a glvalue to a subobject of that glvalue. struct SubobjectDesignator { /// True if the subobject was named in a manner not supported by C++11. Such @@ -480,7 +487,8 @@ namespace { }; /// A stack frame in the constexpr call stack. - struct CallStackFrame { + class CallStackFrame : public interp::Frame { + public: EvalInfo &Info; /// Parent - The caller of this stack frame. @@ -573,7 +581,26 @@ namespace { return 0; } - APValue &createTemporary(const void *Key, bool IsLifetimeExtended); + /// Allocate storage for an object of type T in this stack frame. + /// Populates LV with a handle to the created object. Key identifies + /// the temporary within the stack frame, and must not be reused without + /// bumping the temporary version number. + template + APValue &createTemporary(const KeyT *Key, QualType T, + bool IsLifetimeExtended, LValue &LV); + + void describe(llvm::raw_ostream &OS) override; + + Frame *getCaller() const override { return Caller; } + SourceLocation getCallLocation() const override { return CallLoc; } + const FunctionDecl *getCallee() const override { return Callee; } + + bool isStdFunction() const { + for (const DeclContext *DC = Callee; DC; DC = DC->getParent()) + if (DC->isStdNamespace()) + return true; + return false; + } }; /// Temporarily override 'this'. @@ -591,71 +618,42 @@ namespace { CallStackFrame &Frame; const LValue *OldThis; }; +} - /// A partial diagnostic which we might know in advance that we are not going - /// to emit. - class OptionalDiagnostic { - PartialDiagnostic *Diag; - - public: - explicit OptionalDiagnostic(PartialDiagnostic *Diag = nullptr) - : Diag(Diag) {} - - template - OptionalDiagnostic &operator<<(const T &v) { - if (Diag) - *Diag << v; - return *this; - } - - OptionalDiagnostic &operator<<(const APSInt &I) { - if (Diag) { - SmallVector Buffer; - I.toString(Buffer); - *Diag << StringRef(Buffer.data(), Buffer.size()); - } - return *this; - } - - OptionalDiagnostic &operator<<(const APFloat &F) { - if (Diag) { - // FIXME: Force the precision of the source value down so we don't - // print digits which are usually useless (we don't really care here if - // we truncate a digit by accident in edge cases). Ideally, - // APFloat::toString would automatically print the shortest - // representation which rounds to the correct value, but it's a bit - // tricky to implement. - unsigned precision = - llvm::APFloat::semanticsPrecision(F.getSemantics()); - precision = (precision * 59 + 195) / 196; - SmallVector Buffer; - F.toString(Buffer, precision); - *Diag << StringRef(Buffer.data(), Buffer.size()); - } - return *this; - } - - OptionalDiagnostic &operator<<(const APFixedPoint &FX) { - if (Diag) { - SmallVector Buffer; - FX.toString(Buffer); - *Diag << StringRef(Buffer.data(), Buffer.size()); - } - return *this; - } - }; +static bool HandleDestruction(EvalInfo &Info, const Expr *E, + const LValue &This, QualType ThisType); +static bool HandleDestruction(EvalInfo &Info, SourceLocation Loc, + APValue::LValueBase LVBase, APValue &Value, + QualType T); +namespace { /// A cleanup, and a flag indicating whether it is lifetime-extended. class Cleanup { llvm::PointerIntPair Value; + APValue::LValueBase Base; + QualType T; public: - Cleanup(APValue *Val, bool IsLifetimeExtended) - : Value(Val, IsLifetimeExtended) {} + Cleanup(APValue *Val, APValue::LValueBase Base, QualType T, + bool IsLifetimeExtended) + : Value(Val, IsLifetimeExtended), Base(Base), T(T) {} bool isLifetimeExtended() const { return Value.getInt(); } - void endLifetime() { + bool endLifetime(EvalInfo &Info, bool RunDestructors) { + if (RunDestructors) { + SourceLocation Loc; + if (const ValueDecl *VD = Base.dyn_cast()) + Loc = VD->getLocation(); + else if (const Expr *E = Base.dyn_cast()) + Loc = E->getExprLoc(); + return HandleDestruction(Info, Loc, Base, *Value.getPointer(), T); + } *Value.getPointer() = APValue(); + return true; + } + + bool hasSideEffect() { + return T.isDestructedType(); } }; @@ -671,7 +669,13 @@ namespace { return llvm::hash_combine(Obj.Base, Obj.Path); } }; - enum class ConstructionPhase { None, Bases, AfterBases }; + enum class ConstructionPhase { + None, + Bases, + AfterBases, + Destroying, + DestroyingBases + }; } namespace llvm { @@ -693,6 +697,37 @@ template<> struct DenseMapInfo { } namespace { + /// A dynamically-allocated heap object. + struct DynAlloc { + /// The value of this heap-allocated object. + APValue Value; + /// The allocating expression; used for diagnostics. Either a CXXNewExpr + /// or a CallExpr (the latter is for direct calls to operator new inside + /// std::allocator::allocate). + const Expr *AllocExpr = nullptr; + + enum Kind { + New, + ArrayNew, + StdAllocator + }; + + /// Get the kind of the allocation. This must match between allocation + /// and deallocation. + Kind getKind() const { + if (auto *NE = dyn_cast(AllocExpr)) + return NE->isArray() ? ArrayNew : New; + assert(isa(AllocExpr)); + return StdAllocator; + } + }; + + struct DynAllocOrder { + bool operator()(DynamicAllocLValue L, DynamicAllocLValue R) const { + return L.getIndex() < R.getIndex(); + } + }; + /// EvalInfo - This is a private struct used by the evaluator to capture /// information about a subexpression as it is folded. It retains information /// about the AST context, but also maintains information about the folded @@ -707,7 +742,8 @@ namespace { /// rules. For example, the RHS of (0 && foo()) is not evaluated. We can /// evaluate the expression regardless of what the RHS is, but C only allows /// certain things in certain situations. - struct EvalInfo { + class EvalInfo : public interp::State { + public: ASTContext &Ctx; /// EvalStatus - Contains information about the evaluation. @@ -727,6 +763,13 @@ namespace { /// we will evaluate. unsigned StepsLeft; + /// Force the use of the experimental new constant interpreter, bailing out + /// with an error if a feature is not supported. + bool ForceNewConstInterp; + + /// Enable the experimental new constant interpreter. + bool EnableNewConstInterp; + /// BottomFrame - The frame in which evaluation started. This must be /// initialized after CurrentCall and CallStackDepth. CallStackFrame BottomFrame; @@ -739,6 +782,15 @@ namespace { /// evaluated, if any. APValue::LValueBase EvaluatingDecl; + enum class EvaluatingDeclKind { + None, + /// We're evaluating the construction of EvaluatingDecl. + Ctor, + /// We're evaluating the destruction of EvaluatingDecl. + Dtor, + }; + EvaluatingDeclKind IsEvaluatingDecl = EvaluatingDeclKind::None; + /// EvaluatingDeclValue - This is the value being constructed for the /// declaration whose initializer is being evaluated, if any. APValue *EvaluatingDeclValue; @@ -747,6 +799,14 @@ namespace { llvm::DenseMap ObjectsUnderConstruction; + /// Current heap allocations, along with the location where each was + /// allocated. We use std::map here because we need stable addresses + /// for the stored APValues. + std::map HeapAllocs; + + /// The number of heap allocations performed so far in this evaluation. + unsigned NumHeapAllocs = 0; + struct EvaluatingConstructorRAII { EvalInfo &EI; ObjectUnderConstruction Object; @@ -768,9 +828,29 @@ namespace { } }; + struct EvaluatingDestructorRAII { + EvalInfo &EI; + ObjectUnderConstruction Object; + bool DidInsert; + EvaluatingDestructorRAII(EvalInfo &EI, ObjectUnderConstruction Object) + : EI(EI), Object(Object) { + DidInsert = EI.ObjectsUnderConstruction + .insert({Object, ConstructionPhase::Destroying}) + .second; + } + void startedDestroyingBases() { + EI.ObjectsUnderConstruction[Object] = + ConstructionPhase::DestroyingBases; + } + ~EvaluatingDestructorRAII() { + if (DidInsert) + EI.ObjectsUnderConstruction.erase(Object); + } + }; + ConstructionPhase - isEvaluatingConstructor(APValue::LValueBase Base, - ArrayRef Path) { + isEvaluatingCtorDtor(APValue::LValueBase Base, + ArrayRef Path) { return ObjectsUnderConstruction.lookup({Base, Path}); } @@ -794,76 +874,74 @@ namespace { /// constant value. bool InConstantContext; + /// Whether we're checking that an expression is a potential constant + /// expression. If so, do not fail on constructs that could become constant + /// later on (such as a use of an undefined global). + bool CheckingPotentialConstantExpression = false; + + /// Whether we're checking for an expression that has undefined behavior. + /// If so, we will produce warnings if we encounter an operation that is + /// always undefined. + bool CheckingForUndefinedBehavior = false; + enum EvaluationMode { /// Evaluate as a constant expression. Stop if we find that the expression /// is not a constant expression. EM_ConstantExpression, - /// Evaluate as a potential constant expression. Keep going if we hit a - /// construct that we can't evaluate yet (because we don't yet know the - /// value of something) but stop if we hit something that could never be - /// a constant expression. - EM_PotentialConstantExpression, + /// Evaluate as a constant expression. Stop if we find that the expression + /// is not a constant expression. Some expressions can be retried in the + /// optimizer if we don't constant fold them here, but in an unevaluated + /// context we try to fold them immediately since the optimizer never + /// gets a chance to look at it. + EM_ConstantExpressionUnevaluated, /// Fold the expression to a constant. Stop if we hit a side-effect that /// we can't model. EM_ConstantFold, - /// Evaluate the expression looking for integer overflow and similar - /// issues. Don't worry about side-effects, and try to visit all - /// subexpressions. - EM_EvaluateForOverflow, - /// Evaluate in any way we know how. Don't worry about side-effects that /// can't be modeled. EM_IgnoreSideEffects, - - /// Evaluate as a constant expression. Stop if we find that the expression - /// is not a constant expression. Some expressions can be retried in the - /// optimizer if we don't constant fold them here, but in an unevaluated - /// context we try to fold them immediately since the optimizer never - /// gets a chance to look at it. - EM_ConstantExpressionUnevaluated, - - /// Evaluate as a potential constant expression. Keep going if we hit a - /// construct that we can't evaluate yet (because we don't yet know the - /// value of something) but stop if we hit something that could never be - /// a constant expression. Some expressions can be retried in the - /// optimizer if we don't constant fold them here, but in an unevaluated - /// context we try to fold them immediately since the optimizer never - /// gets a chance to look at it. - EM_PotentialConstantExpressionUnevaluated, } EvalMode; /// Are we checking whether the expression is a potential constant /// expression? - bool checkingPotentialConstantExpression() const { - return EvalMode == EM_PotentialConstantExpression || - EvalMode == EM_PotentialConstantExpressionUnevaluated; + bool checkingPotentialConstantExpression() const override { + return CheckingPotentialConstantExpression; } /// Are we checking an expression for overflow? // FIXME: We should check for any kind of undefined or suspicious behavior // in such constructs, not just overflow. - bool checkingForOverflow() { return EvalMode == EM_EvaluateForOverflow; } + bool checkingForUndefinedBehavior() const override { + return CheckingForUndefinedBehavior; + } EvalInfo(const ASTContext &C, Expr::EvalStatus &S, EvaluationMode Mode) - : Ctx(const_cast(C)), EvalStatus(S), CurrentCall(nullptr), - CallStackDepth(0), NextCallIndex(1), - StepsLeft(getLangOpts().ConstexprStepLimit), - BottomFrame(*this, SourceLocation(), nullptr, nullptr, nullptr), - EvaluatingDecl((const ValueDecl *)nullptr), - EvaluatingDeclValue(nullptr), HasActiveDiagnostic(false), - HasFoldFailureDiagnostic(false), - InConstantContext(false), EvalMode(Mode) {} - - void setEvaluatingDecl(APValue::LValueBase Base, APValue &Value) { + : Ctx(const_cast(C)), EvalStatus(S), CurrentCall(nullptr), + CallStackDepth(0), NextCallIndex(1), + StepsLeft(getLangOpts().ConstexprStepLimit), + ForceNewConstInterp(getLangOpts().ForceNewConstInterp), + EnableNewConstInterp(ForceNewConstInterp || + getLangOpts().EnableNewConstInterp), + BottomFrame(*this, SourceLocation(), nullptr, nullptr, nullptr), + EvaluatingDecl((const ValueDecl *)nullptr), + EvaluatingDeclValue(nullptr), HasActiveDiagnostic(false), + HasFoldFailureDiagnostic(false), InConstantContext(false), + EvalMode(Mode) {} + + ~EvalInfo() { + discardCleanups(); + } + + void setEvaluatingDecl(APValue::LValueBase Base, APValue &Value, + EvaluatingDeclKind EDK = EvaluatingDeclKind::Ctor) { EvaluatingDecl = Base; + IsEvaluatingDecl = EDK; EvaluatingDeclValue = &Value; } - const LangOptions &getLangOpts() const { return Ctx.getLangOpts(); } - bool CheckCallLimit(SourceLocation Loc) { // Don't perform any constexpr calls (other than the call we're checking) // when checking a potential constant expression. @@ -906,133 +984,124 @@ namespace { return true; } - private: - /// Add a diagnostic to the diagnostics list. - PartialDiagnostic &addDiag(SourceLocation Loc, diag::kind DiagId) { - PartialDiagnostic PD(DiagId, Ctx.getDiagAllocator()); - EvalStatus.Diag->push_back(std::make_pair(Loc, PD)); - return EvalStatus.Diag->back().second; + APValue *createHeapAlloc(const Expr *E, QualType T, LValue &LV); + + Optional lookupDynamicAlloc(DynamicAllocLValue DA) { + Optional Result; + auto It = HeapAllocs.find(DA); + if (It != HeapAllocs.end()) + Result = &It->second; + return Result; } - /// Add notes containing a call stack to the current point of evaluation. - void addCallStack(unsigned Limit); + /// Information about a stack frame for std::allocator::[de]allocate. + struct StdAllocatorCaller { + unsigned FrameIndex; + QualType ElemType; + explicit operator bool() const { return FrameIndex != 0; }; + }; - private: - OptionalDiagnostic Diag(SourceLocation Loc, diag::kind DiagId, - unsigned ExtraNotes, bool IsCCEDiag) { + StdAllocatorCaller getStdAllocatorCaller(StringRef FnName) const { + for (const CallStackFrame *Call = CurrentCall; Call != &BottomFrame; + Call = Call->Caller) { + const auto *MD = dyn_cast_or_null(Call->Callee); + if (!MD) + continue; + const IdentifierInfo *FnII = MD->getIdentifier(); + if (!FnII || !FnII->isStr(FnName)) + continue; - if (EvalStatus.Diag) { - // If we have a prior diagnostic, it will be noting that the expression - // isn't a constant expression. This diagnostic is more important, - // unless we require this evaluation to produce a constant expression. - // - // FIXME: We might want to show both diagnostics to the user in - // EM_ConstantFold mode. - if (!EvalStatus.Diag->empty()) { - switch (EvalMode) { - case EM_ConstantFold: - case EM_IgnoreSideEffects: - case EM_EvaluateForOverflow: - if (!HasFoldFailureDiagnostic) - break; - // We've already failed to fold something. Keep that diagnostic. - LLVM_FALLTHROUGH; - case EM_ConstantExpression: - case EM_PotentialConstantExpression: - case EM_ConstantExpressionUnevaluated: - case EM_PotentialConstantExpressionUnevaluated: - HasActiveDiagnostic = false; - return OptionalDiagnostic(); - } - } + const auto *CTSD = + dyn_cast(MD->getParent()); + if (!CTSD) + continue; - unsigned CallStackNotes = CallStackDepth - 1; - unsigned Limit = Ctx.getDiagnostics().getConstexprBacktraceLimit(); - if (Limit) - CallStackNotes = std::min(CallStackNotes, Limit + 1); - if (checkingPotentialConstantExpression()) - CallStackNotes = 0; - - HasActiveDiagnostic = true; - HasFoldFailureDiagnostic = !IsCCEDiag; - EvalStatus.Diag->clear(); - EvalStatus.Diag->reserve(1 + ExtraNotes + CallStackNotes); - addDiag(Loc, DiagId); - if (!checkingPotentialConstantExpression()) - addCallStack(Limit); - return OptionalDiagnostic(&(*EvalStatus.Diag)[0].second); + const IdentifierInfo *ClassII = CTSD->getIdentifier(); + const TemplateArgumentList &TAL = CTSD->getTemplateArgs(); + if (CTSD->isInStdNamespace() && ClassII && + ClassII->isStr("allocator") && TAL.size() >= 1 && + TAL[0].getKind() == TemplateArgument::Type) + return {Call->Index, TAL[0].getAsType()}; } - HasActiveDiagnostic = false; - return OptionalDiagnostic(); - } - public: - // Diagnose that the evaluation could not be folded (FF => FoldFailure) - OptionalDiagnostic - FFDiag(SourceLocation Loc, - diag::kind DiagId = diag::note_invalid_subexpr_in_const_expr, - unsigned ExtraNotes = 0) { - return Diag(Loc, DiagId, ExtraNotes, false); - } - OptionalDiagnostic FFDiag(const Expr *E, diag::kind DiagId - = diag::note_invalid_subexpr_in_const_expr, - unsigned ExtraNotes = 0) { - if (EvalStatus.Diag) - return Diag(E->getExprLoc(), DiagId, ExtraNotes, /*IsCCEDiag*/false); - HasActiveDiagnostic = false; - return OptionalDiagnostic(); + return {}; + } + + void performLifetimeExtension() { + // Disable the cleanups for lifetime-extended temporaries. + CleanupStack.erase( + std::remove_if(CleanupStack.begin(), CleanupStack.end(), + [](Cleanup &C) { return C.isLifetimeExtended(); }), + CleanupStack.end()); + } + + /// Throw away any remaining cleanups at the end of evaluation. If any + /// cleanups would have had a side-effect, note that as an unmodeled + /// side-effect and return false. Otherwise, return true. + bool discardCleanups() { + for (Cleanup &C : CleanupStack) + if (C.hasSideEffect()) + if (!noteSideEffect()) + return false; + return true; } - /// Diagnose that the evaluation does not produce a C++11 core constant - /// expression. - /// - /// FIXME: Stop evaluating if we're in EM_ConstantExpression or - /// EM_PotentialConstantExpression mode and we produce one of these. - OptionalDiagnostic CCEDiag(SourceLocation Loc, diag::kind DiagId - = diag::note_invalid_subexpr_in_const_expr, - unsigned ExtraNotes = 0) { - // Don't override a previous diagnostic. Don't bother collecting - // diagnostics if we're evaluating for overflow. - if (!EvalStatus.Diag || !EvalStatus.Diag->empty()) { - HasActiveDiagnostic = false; - return OptionalDiagnostic(); + private: + interp::Frame *getCurrentFrame() override { return CurrentCall; } + const interp::Frame *getBottomFrame() const override { return &BottomFrame; } + + bool hasActiveDiagnostic() override { return HasActiveDiagnostic; } + void setActiveDiagnostic(bool Flag) override { HasActiveDiagnostic = Flag; } + + void setFoldFailureDiagnostic(bool Flag) override { + HasFoldFailureDiagnostic = Flag; + } + + Expr::EvalStatus &getEvalStatus() const override { return EvalStatus; } + + ASTContext &getCtx() const override { return Ctx; } + + // If we have a prior diagnostic, it will be noting that the expression + // isn't a constant expression. This diagnostic is more important, + // unless we require this evaluation to produce a constant expression. + // + // FIXME: We might want to show both diagnostics to the user in + // EM_ConstantFold mode. + bool hasPriorDiagnostic() override { + if (!EvalStatus.Diag->empty()) { + switch (EvalMode) { + case EM_ConstantFold: + case EM_IgnoreSideEffects: + if (!HasFoldFailureDiagnostic) + break; + // We've already failed to fold something. Keep that diagnostic. + LLVM_FALLTHROUGH; + case EM_ConstantExpression: + case EM_ConstantExpressionUnevaluated: + setActiveDiagnostic(false); + return true; + } } - return Diag(Loc, DiagId, ExtraNotes, true); - } - OptionalDiagnostic CCEDiag(const Expr *E, diag::kind DiagId - = diag::note_invalid_subexpr_in_const_expr, - unsigned ExtraNotes = 0) { - return CCEDiag(E->getExprLoc(), DiagId, ExtraNotes); - } - /// Add a note to a prior diagnostic. - OptionalDiagnostic Note(SourceLocation Loc, diag::kind DiagId) { - if (!HasActiveDiagnostic) - return OptionalDiagnostic(); - return OptionalDiagnostic(&addDiag(Loc, DiagId)); + return false; } - /// Add a stack of notes to a prior diagnostic. - void addNotes(ArrayRef Diags) { - if (HasActiveDiagnostic) { - EvalStatus.Diag->insert(EvalStatus.Diag->end(), - Diags.begin(), Diags.end()); - } - } + unsigned getCallStackDepth() override { return CallStackDepth; } + public: /// Should we continue evaluation after encountering a side-effect that we /// couldn't model? bool keepEvaluatingAfterSideEffect() { switch (EvalMode) { - case EM_PotentialConstantExpression: - case EM_PotentialConstantExpressionUnevaluated: - case EM_EvaluateForOverflow: case EM_IgnoreSideEffects: return true; case EM_ConstantExpression: case EM_ConstantExpressionUnevaluated: case EM_ConstantFold: - return false; + // By default, assume any side effect might be valid in some other + // evaluation of this expression from a different context. + return checkingPotentialConstantExpression() || + checkingForUndefinedBehavior(); } llvm_unreachable("Missed EvalMode case"); } @@ -1047,16 +1116,13 @@ namespace { /// Should we continue evaluation after encountering undefined behavior? bool keepEvaluatingAfterUndefinedBehavior() { switch (EvalMode) { - case EM_EvaluateForOverflow: case EM_IgnoreSideEffects: case EM_ConstantFold: return true; - case EM_PotentialConstantExpression: - case EM_PotentialConstantExpressionUnevaluated: case EM_ConstantExpression: case EM_ConstantExpressionUnevaluated: - return false; + return checkingForUndefinedBehavior(); } llvm_unreachable("Missed EvalMode case"); } @@ -1064,28 +1130,24 @@ namespace { /// Note that we hit something that was technically undefined behavior, but /// that we can evaluate past it (such as signed overflow or floating-point /// division by zero.) - bool noteUndefinedBehavior() { + bool noteUndefinedBehavior() override { EvalStatus.HasUndefinedBehavior = true; return keepEvaluatingAfterUndefinedBehavior(); } /// Should we continue evaluation as much as possible after encountering a /// construct which can't be reduced to a value? - bool keepEvaluatingAfterFailure() { + bool keepEvaluatingAfterFailure() const override { if (!StepsLeft) return false; switch (EvalMode) { - case EM_PotentialConstantExpression: - case EM_PotentialConstantExpressionUnevaluated: - case EM_EvaluateForOverflow: - return true; - case EM_ConstantExpression: case EM_ConstantExpressionUnevaluated: case EM_ConstantFold: case EM_IgnoreSideEffects: - return false; + return checkingPotentialConstantExpression() || + checkingForUndefinedBehavior(); } llvm_unreachable("Missed EvalMode case"); } @@ -1142,9 +1204,7 @@ namespace { Info.EvalStatus.Diag->empty() && !Info.EvalStatus.HasSideEffects), OldMode(Info.EvalMode) { - if (Enabled && - (Info.EvalMode == EvalInfo::EM_ConstantExpression || - Info.EvalMode == EvalInfo::EM_ConstantExpressionUnevaluated)) + if (Enabled) Info.EvalMode = EvalInfo::EM_ConstantFold; } void keepDiagnostics() { Enabled = false; } @@ -1163,8 +1223,7 @@ namespace { EvalInfo::EvaluationMode OldMode; explicit IgnoreSideEffectsRAII(EvalInfo &Info) : Info(Info), OldMode(Info.EvalMode) { - if (!Info.checkingPotentialConstantExpression()) - Info.EvalMode = EvalInfo::EM_IgnoreSideEffects; + Info.EvalMode = EvalInfo::EM_IgnoreSideEffects; } ~IgnoreSideEffectsRAII() { Info.EvalMode = OldMode; } @@ -1230,29 +1289,45 @@ namespace { // temporaries created in different iterations of a loop. Info.CurrentCall->pushTempVersion(); } + bool destroy(bool RunDestructors = true) { + bool OK = cleanup(Info, RunDestructors, OldStackSize); + OldStackSize = -1U; + return OK; + } ~ScopeRAII() { + if (OldStackSize != -1U) + destroy(false); // Body moved to a static method to encourage the compiler to inline away // instances of this class. - cleanup(Info, OldStackSize); Info.CurrentCall->popTempVersion(); } private: - static void cleanup(EvalInfo &Info, unsigned OldStackSize) { - unsigned NewEnd = OldStackSize; - for (unsigned I = OldStackSize, N = Info.CleanupStack.size(); - I != N; ++I) { - if (IsFullExpression && Info.CleanupStack[I].isLifetimeExtended()) { - // Full-expression cleanup of a lifetime-extended temporary: nothing - // to do, just move this cleanup to the right place in the stack. - std::swap(Info.CleanupStack[I], Info.CleanupStack[NewEnd]); - ++NewEnd; - } else { - // End the lifetime of the object. - Info.CleanupStack[I].endLifetime(); + static bool cleanup(EvalInfo &Info, bool RunDestructors, + unsigned OldStackSize) { + assert(OldStackSize <= Info.CleanupStack.size() && + "running cleanups out of order?"); + + // Run all cleanups for a block scope, and non-lifetime-extended cleanups + // for a full-expression scope. + bool Success = true; + for (unsigned I = Info.CleanupStack.size(); I > OldStackSize; --I) { + if (!(IsFullExpression && + Info.CleanupStack[I - 1].isLifetimeExtended())) { + if (!Info.CleanupStack[I - 1].endLifetime(Info, RunDestructors)) { + Success = false; + break; + } } } - Info.CleanupStack.erase(Info.CleanupStack.begin() + NewEnd, - Info.CleanupStack.end()); + + // Compact lifetime-extended cleanups. + auto NewEnd = Info.CleanupStack.begin() + OldStackSize; + if (IsFullExpression) + NewEnd = + std::remove_if(NewEnd, Info.CleanupStack.end(), + [](Cleanup &C) { return !C.isLifetimeExtended(); }); + Info.CleanupStack.erase(NewEnd, Info.CleanupStack.end()); + return Success; } }; typedef ScopeRAII BlockScopeRAII; @@ -1312,74 +1387,14 @@ CallStackFrame::~CallStackFrame() { Info.CurrentCall = Caller; } -APValue &CallStackFrame::createTemporary(const void *Key, - bool IsLifetimeExtended) { - unsigned Version = Info.CurrentCall->getTempVersion(); - APValue &Result = Temporaries[MapKeyTy(Key, Version)]; - assert(Result.isAbsent() && "temporary created multiple times"); - Info.CleanupStack.push_back(Cleanup(&Result, IsLifetimeExtended)); - return Result; -} - -static void describeCall(CallStackFrame *Frame, raw_ostream &Out); - -void EvalInfo::addCallStack(unsigned Limit) { - // Determine which calls to skip, if any. - unsigned ActiveCalls = CallStackDepth - 1; - unsigned SkipStart = ActiveCalls, SkipEnd = SkipStart; - if (Limit && Limit < ActiveCalls) { - SkipStart = Limit / 2 + Limit % 2; - SkipEnd = ActiveCalls - Limit / 2; - } - - // Walk the call stack and add the diagnostics. - unsigned CallIdx = 0; - for (CallStackFrame *Frame = CurrentCall; Frame != &BottomFrame; - Frame = Frame->Caller, ++CallIdx) { - // Skip this call? - if (CallIdx >= SkipStart && CallIdx < SkipEnd) { - if (CallIdx == SkipStart) { - // Note that we're skipping calls. - addDiag(Frame->CallLoc, diag::note_constexpr_calls_suppressed) - << unsigned(ActiveCalls - Limit); - } - continue; - } - - // Use a different note for an inheriting constructor, because from the - // user's perspective it's not really a function at all. - if (auto *CD = dyn_cast_or_null(Frame->Callee)) { - if (CD->isInheritingConstructor()) { - addDiag(Frame->CallLoc, diag::note_constexpr_inherited_ctor_call_here) - << CD->getParent(); - continue; - } - } - - SmallVector Buffer; - llvm::raw_svector_ostream Out(Buffer); - describeCall(Frame, Out); - addDiag(Frame->CallLoc, diag::note_constexpr_call_here) << Out.str(); - } +static bool isRead(AccessKinds AK) { + return AK == AK_Read || AK == AK_ReadObjectRepresentation; } -/// Kinds of access we can perform on an object, for diagnostics. Note that -/// we consider a member function call to be a kind of access, even though -/// it is not formally an access of the object, because it has (largely) the -/// same set of semantic restrictions. -enum AccessKinds { - AK_Read, - AK_Assign, - AK_Increment, - AK_Decrement, - AK_MemberCall, - AK_DynamicCast, - AK_TypeId, -}; - static bool isModification(AccessKinds AK) { switch (AK) { case AK_Read: + case AK_ReadObjectRepresentation: case AK_MemberCall: case AK_DynamicCast: case AK_TypeId: @@ -1387,14 +1402,20 @@ static bool isModification(AccessKinds AK) { case AK_Assign: case AK_Increment: case AK_Decrement: + case AK_Construct: + case AK_Destroy: return true; } llvm_unreachable("unknown access kind"); } +static bool isAnyAccess(AccessKinds AK) { + return isRead(AK) || isModification(AK); +} + /// Is this an access per the C++ definition? static bool isFormalAccess(AccessKinds AK) { - return AK == AK_Read || isModification(AK); + return isAnyAccess(AK) && AK != AK_Construct && AK != AK_Destroy; } namespace { @@ -1490,9 +1511,10 @@ namespace { IsNullPtr = false; } - void setNull(QualType PointerTy, uint64_t TargetVal) { + void setNull(ASTContext &Ctx, QualType PointerTy) { Base = (Expr *)nullptr; - Offset = CharUnits::fromQuantity(TargetVal); + Offset = + CharUnits::fromQuantity(Ctx.getTargetNullPointerValue(PointerTy)); InvalidBase = false; Designator = SubobjectDesignator(PointerTy->getPointeeType()); IsNullPtr = true; @@ -1502,6 +1524,12 @@ namespace { set(B, true); } + std::string toString(ASTContext &Ctx, QualType T) const { + APValue Printable; + moveInto(Printable); + return Printable.getAsString(Ctx, T); + } + private: // Check that this LValue is not based on a null pointer. If it is, produce // a diagnostic and mark the designator as invalid. @@ -1724,15 +1752,6 @@ static bool EvaluateFixedPoint(const Expr *E, APFixedPoint &Result, // Misc utilities //===----------------------------------------------------------------------===// -/// A helper function to create a temporary and set an LValue. -template -static APValue &createTemporary(const KeyTy *Key, bool IsLifetimeExtended, - LValue &LV, CallStackFrame &Frame) { - LV.set({Key, Frame.Info.CurrentCall->Index, - Frame.Info.CurrentCall->getTempVersion()}); - return Frame.createTemporary(Key, IsLifetimeExtended); -} - /// Negate an APSInt in place, converting it to a signed form if necessary, and /// preserving its value (by extending by up to one bit as needed). static void negateAsSigned(APSInt &Int) { @@ -1743,37 +1762,74 @@ static void negateAsSigned(APSInt &Int) { Int = -Int; } +template +APValue &CallStackFrame::createTemporary(const KeyT *Key, QualType T, + bool IsLifetimeExtended, LValue &LV) { + unsigned Version = getTempVersion(); + APValue::LValueBase Base(Key, Index, Version); + LV.set(Base); + APValue &Result = Temporaries[MapKeyTy(Key, Version)]; + assert(Result.isAbsent() && "temporary created multiple times"); + + // If we're creating a temporary immediately in the operand of a speculative + // evaluation, don't register a cleanup to be run outside the speculative + // evaluation context, since we won't actually be able to initialize this + // object. + if (Index <= Info.SpeculativeEvaluationDepth) { + if (T.isDestructedType()) + Info.noteSideEffect(); + } else { + Info.CleanupStack.push_back(Cleanup(&Result, Base, T, IsLifetimeExtended)); + } + return Result; +} + +APValue *EvalInfo::createHeapAlloc(const Expr *E, QualType T, LValue &LV) { + if (NumHeapAllocs > DynamicAllocLValue::getMaxIndex()) { + FFDiag(E, diag::note_constexpr_heap_alloc_limit_exceeded); + return nullptr; + } + + DynamicAllocLValue DA(NumHeapAllocs++); + LV.set(APValue::LValueBase::getDynamicAlloc(DA, T)); + auto Result = HeapAllocs.emplace(std::piecewise_construct, + std::forward_as_tuple(DA), std::tuple<>()); + assert(Result.second && "reused a heap alloc index?"); + Result.first->second.AllocExpr = E; + return &Result.first->second.Value; +} + /// Produce a string describing the given constexpr call. -static void describeCall(CallStackFrame *Frame, raw_ostream &Out) { +void CallStackFrame::describe(raw_ostream &Out) { unsigned ArgIndex = 0; - bool IsMemberCall = isa(Frame->Callee) && - !isa(Frame->Callee) && - cast(Frame->Callee)->isInstance(); + bool IsMemberCall = isa(Callee) && + !isa(Callee) && + cast(Callee)->isInstance(); if (!IsMemberCall) - Out << *Frame->Callee << '('; + Out << *Callee << '('; - if (Frame->This && IsMemberCall) { + if (This && IsMemberCall) { APValue Val; - Frame->This->moveInto(Val); - Val.printPretty(Out, Frame->Info.Ctx, - Frame->This->Designator.MostDerivedType); + This->moveInto(Val); + Val.printPretty(Out, Info.Ctx, + This->Designator.MostDerivedType); // FIXME: Add parens around Val if needed. - Out << "->" << *Frame->Callee << '('; + Out << "->" << *Callee << '('; IsMemberCall = false; } - for (FunctionDecl::param_const_iterator I = Frame->Callee->param_begin(), - E = Frame->Callee->param_end(); I != E; ++I, ++ArgIndex) { + for (FunctionDecl::param_const_iterator I = Callee->param_begin(), + E = Callee->param_end(); I != E; ++I, ++ArgIndex) { if (ArgIndex > (unsigned)IsMemberCall) Out << ", "; const ParmVarDecl *Param = *I; - const APValue &Arg = Frame->Arguments[ArgIndex]; - Arg.printPretty(Out, Frame->Info.Ctx, Param->getType()); + const APValue &Arg = Arguments[ArgIndex]; + Arg.printPretty(Out, Info.Ctx, Param->getType()); if (ArgIndex == 0 && IsMemberCall) - Out << "->" << *Frame->Callee << '('; + Out << "->" << *Callee << '('; } Out << ')'; @@ -1813,7 +1869,7 @@ static bool IsGlobalLValue(APValue::LValueBase B) { return isa(D); } - if (B.is()) + if (B.is() || B.is()) return true; const Expr *E = B.get(); @@ -1912,15 +1968,39 @@ static void NoteLValueLocation(EvalInfo &Info, APValue::LValueBase Base) { Info.Note(VD->getLocation(), diag::note_declared_at); else if (const Expr *E = Base.dyn_cast()) Info.Note(E->getExprLoc(), diag::note_constexpr_temporary_here); + else if (DynamicAllocLValue DA = Base.dyn_cast()) { + // FIXME: Produce a note for dangling pointers too. + if (Optional Alloc = Info.lookupDynamicAlloc(DA)) + Info.Note((*Alloc)->AllocExpr->getExprLoc(), + diag::note_constexpr_dynamic_alloc_here); + } // We have no information to show for a typeid(T) object. } +enum class CheckEvaluationResultKind { + ConstantExpression, + FullyInitialized, +}; + +/// Materialized temporaries that we've already checked to determine if they're +/// initializsed by a constant expression. +using CheckedTemporaries = + llvm::SmallPtrSet; + +static bool CheckEvaluationResult(CheckEvaluationResultKind CERK, + EvalInfo &Info, SourceLocation DiagLoc, + QualType Type, const APValue &Value, + Expr::ConstExprUsage Usage, + SourceLocation SubobjectLoc, + CheckedTemporaries &CheckedTemps); + /// Check that this reference or pointer core constant expression is a valid /// value for an address or reference constant expression. Return true if we /// can fold this expression, whether or not it's a constant expression. static bool CheckLValueConstantExpression(EvalInfo &Info, SourceLocation Loc, QualType Type, const LValue &LVal, - Expr::ConstExprUsage Usage) { + Expr::ConstExprUsage Usage, + CheckedTemporaries &CheckedTemps) { bool IsReferenceType = Type->isReferenceType(); APValue::LValueBase Base = LVal.getLValueBase(); @@ -1946,14 +2026,23 @@ static bool CheckLValueConstantExpression(EvalInfo &Info, SourceLocation Loc, LVal.getLValueCallIndex() == 0) && "have call index for global lvalue"); + if (Base.is()) { + Info.FFDiag(Loc, diag::note_constexpr_dynamic_alloc) + << IsReferenceType << !Designator.Entries.empty(); + NoteLValueLocation(Info, Base); + return false; + } + if (const ValueDecl *VD = Base.dyn_cast()) { if (const VarDecl *Var = dyn_cast(VD)) { // Check if this is a thread-local variable. if (Var->getTLSKind()) + // FIXME: Diagnostic! return false; // A dllimport variable never acts like a constant. if (Usage == Expr::EvaluateForCodeGen && Var->hasAttr()) + // FIXME: Diagnostic! return false; } if (const auto *FD = dyn_cast(VD)) { @@ -1969,6 +2058,25 @@ static bool CheckLValueConstantExpression(EvalInfo &Info, SourceLocation Loc, // perform initialization with the address of the thunk. if (Info.getLangOpts().CPlusPlus && Usage == Expr::EvaluateForCodeGen && FD->hasAttr()) + // FIXME: Diagnostic! + return false; + } + } else if (const auto *MTE = dyn_cast_or_null( + Base.dyn_cast())) { + if (CheckedTemps.insert(MTE).second) { + QualType TempType = getType(Base); + if (TempType.isDestructedType()) { + Info.FFDiag(MTE->getExprLoc(), + diag::note_constexpr_unsupported_tempoarary_nontrivial_dtor) + << TempType; + return false; + } + + APValue *V = Info.Ctx.getMaterializedTemporaryValue(MTE, false); + assert(V && "evasluation result refers to uninitialised temporary"); + if (!CheckEvaluationResult(CheckEvaluationResultKind::ConstantExpression, + Info, MTE->getExprLoc(), TempType, *V, + Usage, SourceLocation(), CheckedTemps)) return false; } } @@ -2043,14 +2151,12 @@ static bool CheckLiteralType(EvalInfo &Info, const Expr *E, return false; } -/// Check that this core constant expression value is a valid value for a -/// constant expression. If not, report an appropriate diagnostic. Does not -/// check that the expression is of literal type. -static bool -CheckConstantExpression(EvalInfo &Info, SourceLocation DiagLoc, QualType Type, - const APValue &Value, - Expr::ConstExprUsage Usage = Expr::EvaluateForCodeGen, - SourceLocation SubobjectLoc = SourceLocation()) { +static bool CheckEvaluationResult(CheckEvaluationResultKind CERK, + EvalInfo &Info, SourceLocation DiagLoc, + QualType Type, const APValue &Value, + Expr::ConstExprUsage Usage, + SourceLocation SubobjectLoc, + CheckedTemporaries &CheckedTemps) { if (!Value.hasValue()) { Info.FFDiag(DiagLoc, diag::note_constexpr_uninitialized) << true << Type; @@ -2070,30 +2176,31 @@ CheckConstantExpression(EvalInfo &Info, SourceLocation DiagLoc, QualType Type, if (Value.isArray()) { QualType EltTy = Type->castAsArrayTypeUnsafe()->getElementType(); for (unsigned I = 0, N = Value.getArrayInitializedElts(); I != N; ++I) { - if (!CheckConstantExpression(Info, DiagLoc, EltTy, - Value.getArrayInitializedElt(I), Usage, - SubobjectLoc)) + if (!CheckEvaluationResult(CERK, Info, DiagLoc, EltTy, + Value.getArrayInitializedElt(I), Usage, + SubobjectLoc, CheckedTemps)) return false; } if (!Value.hasArrayFiller()) return true; - return CheckConstantExpression(Info, DiagLoc, EltTy, Value.getArrayFiller(), - Usage, SubobjectLoc); + return CheckEvaluationResult(CERK, Info, DiagLoc, EltTy, + Value.getArrayFiller(), Usage, SubobjectLoc, + CheckedTemps); } if (Value.isUnion() && Value.getUnionField()) { - return CheckConstantExpression(Info, DiagLoc, - Value.getUnionField()->getType(), - Value.getUnionValue(), Usage, - Value.getUnionField()->getLocation()); + return CheckEvaluationResult( + CERK, Info, DiagLoc, Value.getUnionField()->getType(), + Value.getUnionValue(), Usage, Value.getUnionField()->getLocation(), + CheckedTemps); } if (Value.isStruct()) { RecordDecl *RD = Type->castAs()->getDecl(); if (const CXXRecordDecl *CD = dyn_cast(RD)) { unsigned BaseIndex = 0; for (const CXXBaseSpecifier &BS : CD->bases()) { - if (!CheckConstantExpression(Info, DiagLoc, BS.getType(), - Value.getStructBase(BaseIndex), Usage, - BS.getBeginLoc())) + if (!CheckEvaluationResult(CERK, Info, DiagLoc, BS.getType(), + Value.getStructBase(BaseIndex), Usage, + BS.getBeginLoc(), CheckedTemps)) return false; ++BaseIndex; } @@ -2102,26 +2209,66 @@ CheckConstantExpression(EvalInfo &Info, SourceLocation DiagLoc, QualType Type, if (I->isUnnamedBitfield()) continue; - if (!CheckConstantExpression(Info, DiagLoc, I->getType(), - Value.getStructField(I->getFieldIndex()), - Usage, I->getLocation())) + if (!CheckEvaluationResult(CERK, Info, DiagLoc, I->getType(), + Value.getStructField(I->getFieldIndex()), + Usage, I->getLocation(), CheckedTemps)) return false; } } - if (Value.isLValue()) { + if (Value.isLValue() && + CERK == CheckEvaluationResultKind::ConstantExpression) { LValue LVal; LVal.setFrom(Info.Ctx, Value); - return CheckLValueConstantExpression(Info, DiagLoc, Type, LVal, Usage); + return CheckLValueConstantExpression(Info, DiagLoc, Type, LVal, Usage, + CheckedTemps); } - if (Value.isMemberPointer()) + if (Value.isMemberPointer() && + CERK == CheckEvaluationResultKind::ConstantExpression) return CheckMemberPointerConstantExpression(Info, DiagLoc, Type, Value, Usage); // Everything else is fine. return true; } +/// Check that this core constant expression value is a valid value for a +/// constant expression. If not, report an appropriate diagnostic. Does not +/// check that the expression is of literal type. +static bool +CheckConstantExpression(EvalInfo &Info, SourceLocation DiagLoc, QualType Type, + const APValue &Value, + Expr::ConstExprUsage Usage = Expr::EvaluateForCodeGen) { + CheckedTemporaries CheckedTemps; + return CheckEvaluationResult(CheckEvaluationResultKind::ConstantExpression, + Info, DiagLoc, Type, Value, Usage, + SourceLocation(), CheckedTemps); +} + +/// Check that this evaluated value is fully-initialized and can be loaded by +/// an lvalue-to-rvalue conversion. +static bool CheckFullyInitialized(EvalInfo &Info, SourceLocation DiagLoc, + QualType Type, const APValue &Value) { + CheckedTemporaries CheckedTemps; + return CheckEvaluationResult( + CheckEvaluationResultKind::FullyInitialized, Info, DiagLoc, Type, Value, + Expr::EvaluateForCodeGen, SourceLocation(), CheckedTemps); +} + +/// Enforce C++2a [expr.const]/4.17, which disallows new-expressions unless +/// "the allocated storage is deallocated within the evaluation". +static bool CheckMemoryLeaks(EvalInfo &Info) { + if (!Info.HeapAllocs.empty()) { + // We can still fold to a constant despite a compile-time memory leak, + // so long as the heap allocation isn't referenced in the result (we check + // that in CheckConstantExpression). + Info.CCEDiag(Info.HeapAllocs.begin()->second.AllocExpr, + diag::note_constexpr_memory_leak) + << unsigned(Info.HeapAllocs.size() - 1); + } + return true; +} + static bool EvalPointerValueAsBool(const APValue &Value, bool &Result) { // A null base expression indicates a null pointer. These are always // evaluatable, and they are false unless the offset is zero. @@ -2323,7 +2470,7 @@ static bool CheckedIntArithmetic(EvalInfo &Info, const Expr *E, APSInt Value(Op(LHS.extend(BitWidth), RHS.extend(BitWidth)), false); Result = Value.trunc(LHS.getBitWidth()); if (Result.extend(BitWidth) != Value) { - if (Info.checkingForOverflow()) + if (Info.checkingForUndefinedBehavior()) Info.Ctx.getDiagnostics().Report(E->getExprLoc(), diag::warn_integer_constant_overflow) << Result.toString(10) << E->getType(); @@ -2813,9 +2960,10 @@ static APSInt extractStringLiteralCharacter(EvalInfo &Info, const Expr *Lit, // FIXME: This is inefficient; we should probably introduce something similar // to the LLVM ConstantDataArray to make this cheaper. static void expandStringLiteral(EvalInfo &Info, const StringLiteral *S, - APValue &Result) { - const ConstantArrayType *CAT = - Info.Ctx.getAsConstantArrayType(S->getType()); + APValue &Result, + QualType AllocType = QualType()) { + const ConstantArrayType *CAT = Info.Ctx.getAsConstantArrayType( + AllocType.isNull() ? S->getType() : AllocType); assert(CAT && "string literal isn't an array"); QualType CharType = CAT->getElementType(); assert(CharType->isIntegerType() && "unexpected character type"); @@ -2879,8 +3027,8 @@ static bool isReadByLvalueToRvalueConversion(QualType T) { /// Diagnose an attempt to read from any unreadable field within the specified /// type, which might be a class type. -static bool diagnoseUnreadableFields(EvalInfo &Info, const Expr *E, - QualType T) { +static bool diagnoseMutableFields(EvalInfo &Info, const Expr *E, AccessKinds AK, + QualType T) { CXXRecordDecl *RD = T->getBaseElementTypeUnsafe()->getAsCXXRecordDecl(); if (!RD) return false; @@ -2895,17 +3043,17 @@ static bool diagnoseUnreadableFields(EvalInfo &Info, const Expr *E, // FIXME: Add core issue number for the union case. if (Field->isMutable() && (RD->isUnion() || isReadByLvalueToRvalueConversion(Field->getType()))) { - Info.FFDiag(E, diag::note_constexpr_ltor_mutable, 1) << Field; + Info.FFDiag(E, diag::note_constexpr_access_mutable, 1) << AK << Field; Info.Note(Field->getLocation(), diag::note_declared_at); return true; } - if (diagnoseUnreadableFields(Info, E, Field->getType())) + if (diagnoseMutableFields(Info, E, AK, Field->getType())) return true; } for (auto &BaseSpec : RD->bases()) - if (diagnoseUnreadableFields(Info, E, BaseSpec.getType())) + if (diagnoseMutableFields(Info, E, AK, BaseSpec.getType())) return true; // All mutable fields were empty, and thus not actually read. @@ -2913,7 +3061,8 @@ static bool diagnoseUnreadableFields(EvalInfo &Info, const Expr *E, } static bool lifetimeStartedInEvaluation(EvalInfo &Info, - APValue::LValueBase Base) { + APValue::LValueBase Base, + bool MutableSubobject = false) { // A temporary we created. if (Base.getCallIndex()) return true; @@ -2922,19 +3071,42 @@ static bool lifetimeStartedInEvaluation(EvalInfo &Info, if (!Evaluating) return false; - // The variable whose initializer we're evaluating. - if (auto *BaseD = Base.dyn_cast()) - if (declaresSameEntity(Evaluating, BaseD)) - return true; + auto *BaseD = Base.dyn_cast(); - // A temporary lifetime-extended by the variable whose initializer we're - // evaluating. - if (auto *BaseE = Base.dyn_cast()) - if (auto *BaseMTE = dyn_cast(BaseE)) - if (declaresSameEntity(BaseMTE->getExtendingDecl(), Evaluating)) - return true; + switch (Info.IsEvaluatingDecl) { + case EvalInfo::EvaluatingDeclKind::None: + return false; - return false; + case EvalInfo::EvaluatingDeclKind::Ctor: + // The variable whose initializer we're evaluating. + if (BaseD) + return declaresSameEntity(Evaluating, BaseD); + + // A temporary lifetime-extended by the variable whose initializer we're + // evaluating. + if (auto *BaseE = Base.dyn_cast()) + if (auto *BaseMTE = dyn_cast(BaseE)) + return declaresSameEntity(BaseMTE->getExtendingDecl(), Evaluating); + return false; + + case EvalInfo::EvaluatingDeclKind::Dtor: + // C++2a [expr.const]p6: + // [during constant destruction] the lifetime of a and its non-mutable + // subobjects (but not its mutable subobjects) [are] considered to start + // within e. + // + // FIXME: We can meaningfully extend this to cover non-const objects, but + // we will need special handling: we should be able to access only + // subobjects of such objects that are themselves declared const. + if (!BaseD || + !(BaseD->getType().isConstQualified() || + BaseD->getType()->isReferenceType()) || + MutableSubobject) + return false; + return declaresSameEntity(Evaluating, BaseD); + } + + llvm_unreachable("unknown evaluating decl kind"); } namespace { @@ -2952,13 +3124,13 @@ struct CompleteObject { CompleteObject(APValue::LValueBase Base, APValue *Value, QualType Type) : Base(Base), Value(Value), Type(Type) {} - bool mayReadMutableMembers(EvalInfo &Info) const { + bool mayAccessMutableMembers(EvalInfo &Info, AccessKinds AK) const { // In C++14 onwards, it is permitted to read a mutable member whose // lifetime began within the evaluation. // FIXME: Should we also allow this in C++11? if (!Info.getLangOpts().CPlusPlus14) return false; - return lifetimeStartedInEvaluation(Info, Base); + return lifetimeStartedInEvaluation(Info, Base, /*MutableSubobject*/true); } explicit operator bool() const { return !Type.isNull(); } @@ -3006,19 +3178,22 @@ findSubobject(EvalInfo &Info, const Expr *E, const CompleteObject &Obj, // Walk the designator's path to find the subobject. for (unsigned I = 0, N = Sub.Entries.size(); /**/; ++I) { // Reading an indeterminate value is undefined, but assigning over one is OK. - if (O->isAbsent() || (O->isIndeterminate() && handler.AccessKind != AK_Assign)) { + if ((O->isAbsent() && !(handler.AccessKind == AK_Construct && I == N)) || + (O->isIndeterminate() && handler.AccessKind != AK_Construct && + handler.AccessKind != AK_Assign && + handler.AccessKind != AK_ReadObjectRepresentation)) { if (!Info.checkingPotentialConstantExpression()) Info.FFDiag(E, diag::note_constexpr_access_uninit) << handler.AccessKind << O->isIndeterminate(); return handler.failed(); } - // C++ [class.ctor]p5: + // C++ [class.ctor]p5, C++ [class.dtor]p5: // const and volatile semantics are not applied on an object under - // construction. + // {con,de}struction. if ((ObjType.isConstQualified() || ObjType.isVolatileQualified()) && ObjType->isRecordType() && - Info.isEvaluatingConstructor( + Info.isEvaluatingCtorDtor( Obj.Base, llvm::makeArrayRef(Sub.Entries.begin(), Sub.Entries.begin() + I)) != ConstructionPhase::None) { @@ -3061,9 +3236,9 @@ findSubobject(EvalInfo &Info, const Expr *E, const CompleteObject &Obj, // things we need to check: if there are any mutable subobjects, we // cannot perform this read. (This only happens when performing a trivial // copy or assignment.) - if (ObjType->isRecordType() && handler.AccessKind == AK_Read && - !Obj.mayReadMutableMembers(Info) && - diagnoseUnreadableFields(Info, E, ObjType)) + if (ObjType->isRecordType() && + !Obj.mayAccessMutableMembers(Info, handler.AccessKind) && + diagnoseMutableFields(Info, E, handler.AccessKind, ObjType)) return handler.failed(); } @@ -3101,7 +3276,7 @@ findSubobject(EvalInfo &Info, const Expr *E, const CompleteObject &Obj, if (O->getArrayInitializedElts() > Index) O = &O->getArrayInitializedElt(Index); - else if (handler.AccessKind != AK_Read) { + else if (!isRead(handler.AccessKind)) { expandArray(*O, Index); O = &O->getArrayInitializedElt(Index); } else @@ -3131,10 +3306,10 @@ findSubobject(EvalInfo &Info, const Expr *E, const CompleteObject &Obj, : O->getComplexFloatReal(), ObjType); } } else if (const FieldDecl *Field = getAsField(Sub.Entries[I])) { - if (Field->isMutable() && handler.AccessKind == AK_Read && - !Obj.mayReadMutableMembers(Info)) { - Info.FFDiag(E, diag::note_constexpr_ltor_mutable, 1) - << Field; + if (Field->isMutable() && + !Obj.mayAccessMutableMembers(Info, handler.AccessKind)) { + Info.FFDiag(E, diag::note_constexpr_access_mutable, 1) + << handler.AccessKind << Field; Info.Note(Field->getLocation(), diag::note_declared_at); return handler.failed(); } @@ -3145,9 +3320,18 @@ findSubobject(EvalInfo &Info, const Expr *E, const CompleteObject &Obj, const FieldDecl *UnionField = O->getUnionField(); if (!UnionField || UnionField->getCanonicalDecl() != Field->getCanonicalDecl()) { - Info.FFDiag(E, diag::note_constexpr_access_inactive_union_member) - << handler.AccessKind << Field << !UnionField << UnionField; - return handler.failed(); + if (I == N - 1 && handler.AccessKind == AK_Construct) { + // Placement new onto an inactive union member makes it active. + O->setUnion(Field, APValue()); + } else { + // FIXME: If O->getUnionValue() is absent, report that there's no + // active union member rather than reporting the prior active union + // member. We'll need to fix nullptr_t to not use APValue() as its + // representation first. + Info.FFDiag(E, diag::note_constexpr_access_inactive_union_member) + << handler.AccessKind << Field << !UnionField << UnionField; + return handler.failed(); + } } O = &O->getUnionValue(); } else @@ -3171,15 +3355,17 @@ findSubobject(EvalInfo &Info, const Expr *E, const CompleteObject &Obj, namespace { struct ExtractSubobjectHandler { EvalInfo &Info; + const Expr *E; APValue &Result; - - static const AccessKinds AccessKind = AK_Read; + const AccessKinds AccessKind; typedef bool result_type; bool failed() { return false; } bool found(APValue &Subobj, QualType SubobjType) { Result = Subobj; - return true; + if (AccessKind == AK_ReadObjectRepresentation) + return true; + return CheckFullyInitialized(Info, E->getExprLoc(), SubobjType, Result); } bool found(APSInt &Value, QualType SubobjType) { Result = APValue(Value); @@ -3192,14 +3378,13 @@ struct ExtractSubobjectHandler { }; } // 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 }; + const SubobjectDesignator &Sub, APValue &Result, + AccessKinds AK = AK_Read) { + assert(AK == AK_Read || AK == AK_ReadObjectRepresentation); + ExtractSubobjectHandler Handler = {Info, E, Result, AK}; return findSubobject(Info, E, Obj, Sub, Handler); } @@ -3345,13 +3530,13 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E, } } - bool IsAccess = isFormalAccess(AK); + bool IsAccess = isAnyAccess(AK); // 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 (IsAccess && LValType.isVolatileQualified()) { + if (isFormalAccess(AK) && LValType.isVolatileQualified()) { if (Info.getLangOpts().CPlusPlus) Info.FFDiag(E, diag::note_constexpr_access_volatile_type) << AK << LValType; @@ -3386,8 +3571,7 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E, // the variable we're reading must be const. if (!Frame) { if (Info.getLangOpts().CPlusPlus14 && - declaresSameEntity( - VD, Info.EvaluatingDecl.dyn_cast())) { + lifetimeStartedInEvaluation(Info, LVal.Base)) { // OK, we can read and modify an object if we're in the process of // evaluating its initializer, because its lifetime began in this // evaluation. @@ -3446,6 +3630,14 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E, if (!evaluateVarDeclInit(Info, E, VD, Frame, BaseVal, &LVal)) return CompleteObject(); + } else if (DynamicAllocLValue DA = LVal.Base.dyn_cast()) { + Optional Alloc = Info.lookupDynamicAlloc(DA); + if (!Alloc) { + Info.FFDiag(E, diag::note_constexpr_access_deleted_object) << AK; + return CompleteObject(); + } + return CompleteObject(LVal.Base, &(*Alloc)->Value, + LVal.Base.getDynamicAllocType()); } else { const Expr *Base = LVal.Base.dyn_cast(); @@ -3469,11 +3661,14 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E, // int x = ++r; // constexpr int k = r; // Therefore we use the C++14 rules in C++11 too. - const ValueDecl *VD = Info.EvaluatingDecl.dyn_cast(); - const ValueDecl *ED = MTE->getExtendingDecl(); + // + // Note that temporaries whose lifetimes began while evaluating a + // variable's constructor are not usable while evaluating the + // corresponding destructor, not even if they're of const-qualified + // types. if (!(BaseType.isConstQualified() && BaseType->isIntegralOrEnumerationType()) && - !(VD && VD->getCanonicalDecl() == ED->getCanonicalDecl())) { + !lifetimeStartedInEvaluation(Info, LVal.Base)) { if (!IsAccess) return CompleteObject(LVal.getLValueBase(), nullptr, BaseType); Info.FFDiag(E, diag::note_constexpr_access_static_temporary, 1) << AK; @@ -3525,15 +3720,22 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E, /// 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) { +/// \param WantObjectRepresentation - If true, we're looking for the object +/// representation rather than the value, and in particular, +/// there is no requirement that the result be fully initialized. +static bool +handleLValueToRValueConversion(EvalInfo &Info, const Expr *Conv, QualType Type, + const LValue &LVal, APValue &RVal, + bool WantObjectRepresentation = false) { 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(); + AccessKinds AK = + WantObjectRepresentation ? AK_ReadObjectRepresentation : AK_Read; + if (Base && !LVal.getLValueCallIndex() && !Type.isVolatileQualified()) { if (const CompoundLiteralExpr *CLE = dyn_cast(Base)) { // In C99, a CompoundLiteralExpr is an lvalue, and we defer evaluating the @@ -3547,7 +3749,7 @@ static bool handleLValueToRValueConversion(EvalInfo &Info, const Expr *Conv, if (!Evaluate(Lit, Info, CLE->getInitializer())) return false; CompleteObject LitObj(LVal.Base, &Lit, Base->getType()); - return extractSubobject(Info, Conv, LitObj, LVal.Designator, RVal); + return extractSubobject(Info, Conv, LitObj, LVal.Designator, RVal, AK); } else if (isa(Base) || isa(Base)) { // Special-case character extraction so we don't have to construct an // APValue for the whole string. @@ -3562,7 +3764,7 @@ static bool handleLValueToRValueConversion(EvalInfo &Info, const Expr *Conv, } if (LVal.Designator.isOnePastTheEnd()) { if (Info.getLangOpts().CPlusPlus11) - Info.FFDiag(Conv, diag::note_constexpr_access_past_end) << AK_Read; + Info.FFDiag(Conv, diag::note_constexpr_access_past_end) << AK; else Info.FFDiag(Conv); return false; @@ -3573,8 +3775,8 @@ static bool handleLValueToRValueConversion(EvalInfo &Info, const Expr *Conv, } } - CompleteObject Obj = findCompleteObject(Info, Conv, AK_Read, LVal, Type); - return Obj && extractSubobject(Info, Conv, Obj, LVal.Designator, RVal); + CompleteObject Obj = findCompleteObject(Info, Conv, AK, LVal, Type); + return Obj && extractSubobject(Info, Conv, Obj, LVal.Designator, RVal, AK); } /// Perform an assignment of Val to LVal. Takes ownership of Val. @@ -3866,7 +4068,7 @@ static bool handleIncDec(EvalInfo &Info, const Expr *E, const LValue &LVal, /// Build an lvalue for the object argument of a member function call. static bool EvaluateObjectArgument(EvalInfo &Info, const Expr *Object, LValue &This) { - if (Object->getType()->isPointerType()) + if (Object->getType()->isPointerType() && Object->isRValue()) return EvaluatePointer(Object, This, Info); if (Object->isGLValue()) @@ -4028,6 +4230,40 @@ static bool HandleBaseToDerivedCast(EvalInfo &Info, const CastExpr *E, return CastToDerivedClass(Info, E, Result, TargetType, NewEntriesSize); } +/// Get the value to use for a default-initialized object of type T. +static APValue getDefaultInitValue(QualType T) { + if (auto *RD = T->getAsCXXRecordDecl()) { + if (RD->isUnion()) + return APValue((const FieldDecl*)nullptr); + + APValue Struct(APValue::UninitStruct(), RD->getNumBases(), + std::distance(RD->field_begin(), RD->field_end())); + + unsigned Index = 0; + for (CXXRecordDecl::base_class_const_iterator I = RD->bases_begin(), + End = RD->bases_end(); I != End; ++I, ++Index) + Struct.getStructBase(Index) = getDefaultInitValue(I->getType()); + + for (const auto *I : RD->fields()) { + if (I->isUnnamedBitfield()) + continue; + Struct.getStructField(I->getFieldIndex()) = + getDefaultInitValue(I->getType()); + } + return Struct; + } + + if (auto *AT = + dyn_cast_or_null(T->getAsArrayTypeUnsafe())) { + APValue Array(APValue::UninitArray(), 0, AT->getSize().getZExtValue()); + if (Array.hasArrayFiller()) + Array.getArrayFiller() = getDefaultInitValue(AT->getElementType()); + return Array; + } + + return APValue::IndeterminateValue(); +} + namespace { enum EvalStmtResult { /// Evaluation failed. @@ -4051,14 +4287,13 @@ static bool EvaluateVarDecl(EvalInfo &Info, const VarDecl *VD) { return true; LValue Result; - APValue &Val = createTemporary(VD, true, Result, *Info.CurrentCall); + APValue &Val = + Info.CurrentCall->createTemporary(VD, VD->getType(), true, Result); const Expr *InitE = VD->getInit(); if (!InitE) { - Info.FFDiag(VD->getBeginLoc(), diag::note_constexpr_uninitialized) - << false << VD->getType(); - Val = APValue(); - return false; + Val = getDefaultInitValue(VD->getType()); + return true; } if (InitE->isValueDependent()) @@ -4095,7 +4330,9 @@ static bool EvaluateCond(EvalInfo &Info, const VarDecl *CondDecl, FullExpressionRAII Scope(Info); if (CondDecl && !EvaluateDecl(Info, CondDecl)) return false; - return EvaluateAsBooleanCondition(Cond, Result, Info); + if (!EvaluateAsBooleanCondition(Cond, Result, Info)) + return false; + return Scope.destroy(); } namespace { @@ -4131,7 +4368,12 @@ static EvalStmtResult EvaluateLoopBody(StmtResult &Result, EvalInfo &Info, const Stmt *Body, const SwitchCase *Case = nullptr) { BlockScopeRAII Scope(Info); - switch (EvalStmtResult ESR = EvaluateStmt(Result, Info, Body, Case)) { + + EvalStmtResult ESR = EvaluateStmt(Result, Info, Body, Case); + if (ESR != ESR_Failed && ESR != ESR_CaseNotFound && !Scope.destroy()) + ESR = ESR_Failed; + + switch (ESR) { case ESR_Break: return ESR_Succeeded; case ESR_Succeeded: @@ -4153,17 +4395,23 @@ static EvalStmtResult EvaluateSwitch(StmtResult &Result, EvalInfo &Info, // Evaluate the switch condition. APSInt Value; { - FullExpressionRAII Scope(Info); if (const Stmt *Init = SS->getInit()) { EvalStmtResult ESR = EvaluateStmt(Result, Info, Init); - if (ESR != ESR_Succeeded) + if (ESR != ESR_Succeeded) { + if (ESR != ESR_Failed && !Scope.destroy()) + ESR = ESR_Failed; return ESR; + } } + + FullExpressionRAII CondScope(Info); if (SS->getConditionVariable() && !EvaluateDecl(Info, SS->getConditionVariable())) return ESR_Failed; if (!EvaluateInteger(SS->getCond(), Value, Info)) return ESR_Failed; + if (!CondScope.destroy()) + return ESR_Failed; } // Find the switch case corresponding to the value of the condition. @@ -4187,10 +4435,14 @@ static EvalStmtResult EvaluateSwitch(StmtResult &Result, EvalInfo &Info, } if (!Found) - return ESR_Succeeded; + return Scope.destroy() ? ESR_Succeeded : ESR_Failed; // Search the switch body for the switch case and evaluate it from there. - switch (EvalStmtResult ESR = EvaluateStmt(Result, Info, SS->getBody(), Found)) { + EvalStmtResult ESR = EvaluateStmt(Result, Info, SS->getBody(), Found); + if (ESR != ESR_Failed && ESR != ESR_CaseNotFound && !Scope.destroy()) + return ESR_Failed; + + switch (ESR) { case ESR_Break: return ESR_Succeeded; case ESR_Succeeded: @@ -4217,10 +4469,6 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info, // If we're hunting down a 'case' or 'default' label, recurse through // substatements until we hit the label. if (Case) { - // FIXME: We don't start the lifetime of objects whose initialization we - // jump over. However, such objects must be of class type with a trivial - // default constructor that initialize all subobjects, so must be empty, - // so this almost never matters. switch (S->getStmtClass()) { case Stmt::CompoundStmtClass: // FIXME: Precompute which substatement of a compound statement we @@ -4246,10 +4494,35 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info, // preceded by our switch label. BlockScopeRAII Scope(Info); + // Step into the init statement in case it brings an (uninitialized) + // variable into scope. + if (const Stmt *Init = IS->getInit()) { + EvalStmtResult ESR = EvaluateStmt(Result, Info, Init, Case); + if (ESR != ESR_CaseNotFound) { + assert(ESR != ESR_Succeeded); + return ESR; + } + } + + // Condition variable must be initialized if it exists. + // FIXME: We can skip evaluating the body if there's a condition + // variable, as there can't be any case labels within it. + // (The same is true for 'for' statements.) + EvalStmtResult ESR = EvaluateStmt(Result, Info, IS->getThen(), Case); - if (ESR != ESR_CaseNotFound || !IS->getElse()) + if (ESR == ESR_Failed) + return ESR; + if (ESR != ESR_CaseNotFound) + return Scope.destroy() ? ESR : ESR_Failed; + if (!IS->getElse()) + return ESR_CaseNotFound; + + ESR = EvaluateStmt(Result, Info, IS->getElse(), Case); + if (ESR == ESR_Failed) return ESR; - return EvaluateStmt(Result, Info, IS->getElse(), Case); + if (ESR != ESR_CaseNotFound) + return Scope.destroy() ? ESR : ESR_Failed; + return ESR_CaseNotFound; } case Stmt::WhileStmtClass: { @@ -4262,21 +4535,47 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info, case Stmt::ForStmtClass: { const ForStmt *FS = cast(S); + BlockScopeRAII Scope(Info); + + // Step into the init statement in case it brings an (uninitialized) + // variable into scope. + if (const Stmt *Init = FS->getInit()) { + EvalStmtResult ESR = EvaluateStmt(Result, Info, Init, Case); + if (ESR != ESR_CaseNotFound) { + assert(ESR != ESR_Succeeded); + return ESR; + } + } + EvalStmtResult ESR = EvaluateLoopBody(Result, Info, FS->getBody(), Case); if (ESR != ESR_Continue) return ESR; if (FS->getInc()) { FullExpressionRAII IncScope(Info); - if (!EvaluateIgnoredValue(Info, FS->getInc())) + if (!EvaluateIgnoredValue(Info, FS->getInc()) || !IncScope.destroy()) return ESR_Failed; } break; } - case Stmt::DeclStmtClass: - // FIXME: If the variable has initialization that can't be jumped over, - // bail out of any immediately-surrounding compound-statement too. + case Stmt::DeclStmtClass: { + // Start the lifetime of any uninitialized variables we encounter. They + // might be used by the selected branch of the switch. + const DeclStmt *DS = cast(S); + for (const auto *D : DS->decls()) { + if (const auto *VD = dyn_cast(D)) { + if (VD->hasLocalStorage() && !VD->getInit()) + if (!EvaluateVarDecl(Info, VD)) + return ESR_Failed; + // FIXME: If the variable has initialization that can't be jumped + // over, bail out of any immediately-surrounding compound-statement + // too. There can't be any case labels here. + } + } + return ESR_CaseNotFound; + } + default: return ESR_CaseNotFound; } @@ -4287,8 +4586,10 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info, if (const Expr *E = dyn_cast(S)) { // Don't bother evaluating beyond an expression-statement which couldn't // be evaluated. + // FIXME: Do we need the FullExpressionRAII object here? + // VisitExprWithCleanups should create one when necessary. FullExpressionRAII Scope(Info); - if (!EvaluateIgnoredValue(Info, E)) + if (!EvaluateIgnoredValue(Info, E) || !Scope.destroy()) return ESR_Failed; return ESR_Succeeded; } @@ -4301,12 +4602,12 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info, case Stmt::DeclStmtClass: { const DeclStmt *DS = cast(S); - for (const auto *DclIt : DS->decls()) { + for (const auto *D : DS->decls()) { // Each declaration initialization is its own full-expression. - // FIXME: This isn't quite right; if we're performing aggregate - // initialization, each braced subexpression is its own full-expression. FullExpressionRAII Scope(Info); - if (!EvaluateDecl(Info, DclIt) && !Info.noteFailure()) + if (!EvaluateDecl(Info, D) && !Info.noteFailure()) + return ESR_Failed; + if (!Scope.destroy()) return ESR_Failed; } return ESR_Succeeded; @@ -4320,7 +4621,7 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info, ? EvaluateInPlace(Result.Value, Info, *Result.Slot, RetExpr) : Evaluate(Result.Value, Info, RetExpr))) return ESR_Failed; - return ESR_Returned; + return Scope.destroy() ? ESR_Returned : ESR_Failed; } case Stmt::CompoundStmtClass: { @@ -4331,10 +4632,15 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info, EvalStmtResult ESR = EvaluateStmt(Result, Info, BI, Case); if (ESR == ESR_Succeeded) Case = nullptr; - else if (ESR != ESR_CaseNotFound) + else if (ESR != ESR_CaseNotFound) { + if (ESR != ESR_Failed && !Scope.destroy()) + return ESR_Failed; return ESR; + } } - return Case ? ESR_CaseNotFound : ESR_Succeeded; + if (Case) + return ESR_CaseNotFound; + return Scope.destroy() ? ESR_Succeeded : ESR_Failed; } case Stmt::IfStmtClass: { @@ -4344,8 +4650,11 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info, BlockScopeRAII Scope(Info); if (const Stmt *Init = IS->getInit()) { EvalStmtResult ESR = EvaluateStmt(Result, Info, Init); - if (ESR != ESR_Succeeded) + if (ESR != ESR_Succeeded) { + if (ESR != ESR_Failed && !Scope.destroy()) + return ESR_Failed; return ESR; + } } bool Cond; if (!EvaluateCond(Info, IS->getConditionVariable(), IS->getCond(), Cond)) @@ -4353,10 +4662,13 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info, if (const Stmt *SubStmt = Cond ? IS->getThen() : IS->getElse()) { EvalStmtResult ESR = EvaluateStmt(Result, Info, SubStmt); - if (ESR != ESR_Succeeded) + if (ESR != ESR_Succeeded) { + if (ESR != ESR_Failed && !Scope.destroy()) + return ESR_Failed; return ESR; + } } - return ESR_Succeeded; + return Scope.destroy() ? ESR_Succeeded : ESR_Failed; } case Stmt::WhileStmtClass: { @@ -4371,8 +4683,13 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info, break; EvalStmtResult ESR = EvaluateLoopBody(Result, Info, WS->getBody()); - if (ESR != ESR_Continue) + if (ESR != ESR_Continue) { + if (ESR != ESR_Failed && !Scope.destroy()) + return ESR_Failed; return ESR; + } + if (!Scope.destroy()) + return ESR_Failed; } return ESR_Succeeded; } @@ -4387,7 +4704,8 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info, Case = nullptr; FullExpressionRAII CondScope(Info); - if (!EvaluateAsBooleanCondition(DS->getCond(), Continue, Info)) + if (!EvaluateAsBooleanCondition(DS->getCond(), Continue, Info) || + !CondScope.destroy()) return ESR_Failed; } while (Continue); return ESR_Succeeded; @@ -4395,14 +4713,17 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info, case Stmt::ForStmtClass: { const ForStmt *FS = cast(S); - BlockScopeRAII Scope(Info); + BlockScopeRAII ForScope(Info); if (FS->getInit()) { EvalStmtResult ESR = EvaluateStmt(Result, Info, FS->getInit()); - if (ESR != ESR_Succeeded) + if (ESR != ESR_Succeeded) { + if (ESR != ESR_Failed && !ForScope.destroy()) + return ESR_Failed; return ESR; + } } while (true) { - BlockScopeRAII Scope(Info); + BlockScopeRAII IterScope(Info); bool Continue = true; if (FS->getCond() && !EvaluateCond(Info, FS->getConditionVariable(), FS->getCond(), Continue)) @@ -4411,16 +4732,22 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info, break; EvalStmtResult ESR = EvaluateLoopBody(Result, Info, FS->getBody()); - if (ESR != ESR_Continue) + if (ESR != ESR_Continue) { + if (ESR != ESR_Failed && (!IterScope.destroy() || !ForScope.destroy())) + return ESR_Failed; return ESR; + } if (FS->getInc()) { FullExpressionRAII IncScope(Info); - if (!EvaluateIgnoredValue(Info, FS->getInc())) + if (!EvaluateIgnoredValue(Info, FS->getInc()) || !IncScope.destroy()) return ESR_Failed; } + + if (!IterScope.destroy()) + return ESR_Failed; } - return ESR_Succeeded; + return ForScope.destroy() ? ESR_Succeeded : ESR_Failed; } case Stmt::CXXForRangeStmtClass: { @@ -4430,22 +4757,34 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info, // Evaluate the init-statement if present. if (FS->getInit()) { EvalStmtResult ESR = EvaluateStmt(Result, Info, FS->getInit()); - if (ESR != ESR_Succeeded) + if (ESR != ESR_Succeeded) { + if (ESR != ESR_Failed && !Scope.destroy()) + return ESR_Failed; return ESR; + } } // Initialize the __range variable. EvalStmtResult ESR = EvaluateStmt(Result, Info, FS->getRangeStmt()); - if (ESR != ESR_Succeeded) + if (ESR != ESR_Succeeded) { + if (ESR != ESR_Failed && !Scope.destroy()) + return ESR_Failed; return ESR; + } // Create the __begin and __end iterators. ESR = EvaluateStmt(Result, Info, FS->getBeginStmt()); - if (ESR != ESR_Succeeded) + if (ESR != ESR_Succeeded) { + if (ESR != ESR_Failed && !Scope.destroy()) + return ESR_Failed; return ESR; + } ESR = EvaluateStmt(Result, Info, FS->getEndStmt()); - if (ESR != ESR_Succeeded) + if (ESR != ESR_Succeeded) { + if (ESR != ESR_Failed && !Scope.destroy()) + return ESR_Failed; return ESR; + } while (true) { // Condition: __begin != __end. @@ -4461,20 +4800,29 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info, // User's variable declaration, initialized by *__begin. BlockScopeRAII InnerScope(Info); ESR = EvaluateStmt(Result, Info, FS->getLoopVarStmt()); - if (ESR != ESR_Succeeded) + if (ESR != ESR_Succeeded) { + if (ESR != ESR_Failed && (!InnerScope.destroy() || !Scope.destroy())) + return ESR_Failed; return ESR; + } // Loop body. ESR = EvaluateLoopBody(Result, Info, FS->getBody()); - if (ESR != ESR_Continue) + if (ESR != ESR_Continue) { + if (ESR != ESR_Failed && (!InnerScope.destroy() || !Scope.destroy())) + return ESR_Failed; return ESR; + } // Increment: ++__begin if (!EvaluateIgnoredValue(Info, FS->getInc())) return ESR_Failed; + + if (!InnerScope.destroy()) + return ESR_Failed; } - return ESR_Succeeded; + return Scope.destroy() ? ESR_Succeeded : ESR_Failed; } case Stmt::SwitchStmtClass: @@ -4649,9 +4997,13 @@ static bool checkDynamicType(EvalInfo &Info, const Expr *E, const LValue &This, /// Check that the pointee of the 'this' pointer in a member function call is /// either within its lifetime or in its period of construction or destruction. -static bool checkNonVirtualMemberCallThisPointer(EvalInfo &Info, const Expr *E, - const LValue &This) { - return checkDynamicType(Info, E, This, AK_MemberCall, false); +static bool +checkNonVirtualMemberCallThisPointer(EvalInfo &Info, const Expr *E, + const LValue &This, + const CXXMethodDecl *NamedMember) { + return checkDynamicType( + Info, E, This, + isa(NamedMember) ? AK_Destroy : AK_MemberCall, false); } struct DynamicType { @@ -4699,16 +5051,19 @@ static Optional ComputeDynamicType(EvalInfo &Info, const Expr *E, ArrayRef Path = This.Designator.Entries; for (unsigned PathLength = This.Designator.MostDerivedPathLength; PathLength <= Path.size(); ++PathLength) { - switch (Info.isEvaluatingConstructor(This.getLValueBase(), - Path.slice(0, PathLength))) { + switch (Info.isEvaluatingCtorDtor(This.getLValueBase(), + Path.slice(0, PathLength))) { case ConstructionPhase::Bases: - // We're constructing a base class. This is not the dynamic type. + case ConstructionPhase::DestroyingBases: + // We're constructing or destroying a base class. This is not the dynamic + // type. break; case ConstructionPhase::None: case ConstructionPhase::AfterBases: - // We've finished constructing the base classes, so this is the dynamic - // type. + case ConstructionPhase::Destroying: + // We've finished constructing the base classes and not yet started + // destroying them again, so this is the dynamic type. return DynamicType{getBaseClassType(This.Designator, PathLength), PathLength}; } @@ -4725,8 +5080,9 @@ static Optional ComputeDynamicType(EvalInfo &Info, const Expr *E, static const CXXMethodDecl *HandleVirtualDispatch( EvalInfo &Info, const Expr *E, LValue &This, const CXXMethodDecl *Found, llvm::SmallVectorImpl &CovariantAdjustmentPath) { - Optional DynType = - ComputeDynamicType(Info, E, This, AK_MemberCall); + Optional DynType = ComputeDynamicType( + Info, E, This, + isa(Found) ? AK_Destroy : AK_MemberCall); if (!DynType) return nullptr; @@ -4862,8 +5218,7 @@ static bool HandleDynamicCast(EvalInfo &Info, const ExplicitCastExpr *E, if (!E->isGLValue()) { // The value of a failed cast to pointer type is the null pointer value // of the required result type. - auto TargetVal = Info.Ctx.getTargetNullPointerValue(E->getType()); - Ptr.setNull(E->getType(), TargetVal); + Ptr.setNull(Info.Ctx, E->getType()); return true; } @@ -4928,39 +5283,6 @@ struct StartLifetimeOfUnionMemberHandler { static const AccessKinds AccessKind = AK_Assign; - APValue getDefaultInitValue(QualType SubobjType) { - if (auto *RD = SubobjType->getAsCXXRecordDecl()) { - if (RD->isUnion()) - return APValue((const FieldDecl*)nullptr); - - APValue Struct(APValue::UninitStruct(), RD->getNumBases(), - std::distance(RD->field_begin(), RD->field_end())); - - unsigned Index = 0; - for (CXXRecordDecl::base_class_const_iterator I = RD->bases_begin(), - End = RD->bases_end(); I != End; ++I, ++Index) - Struct.getStructBase(Index) = getDefaultInitValue(I->getType()); - - for (const auto *I : RD->fields()) { - if (I->isUnnamedBitfield()) - continue; - Struct.getStructField(I->getFieldIndex()) = - getDefaultInitValue(I->getType()); - } - return Struct; - } - - if (auto *AT = dyn_cast_or_null( - SubobjType->getAsArrayTypeUnsafe())) { - APValue Array(APValue::UninitArray(), 0, AT->getSize().getZExtValue()); - if (Array.hasArrayFiller()) - Array.getArrayFiller() = getDefaultInitValue(AT->getElementType()); - return Array; - } - - return APValue::IndeterminateValue(); - } - typedef bool result_type; bool failed() { return false; } bool found(APValue &Subobj, QualType SubobjType) { @@ -4973,7 +5295,8 @@ struct StartLifetimeOfUnionMemberHandler { // * No variant members' lifetimes begin // * All scalar subobjects whose lifetimes begin have indeterminate values assert(SubobjType->isUnionType()); - if (!declaresSameEntity(Subobj.getUnionField(), Field)) + if (!declaresSameEntity(Subobj.getUnionField(), Field) || + !Subobj.getUnionValue().hasValue()) Subobj.setUnion(Field, getDefaultInitValue(Field->getType())); return true; } @@ -5005,7 +5328,9 @@ static bool HandleUnionActiveMemberChange(EvalInfo &Info, const Expr *LHSExpr, // -- If E is of the form A.B, S(E) contains the elements of S(A)... if (auto *ME = dyn_cast(E)) { auto *FD = dyn_cast(ME->getMemberDecl()); - if (!FD) + // Note that we can't implicitly start the lifetime of a reference, + // so we don't need to proceed any further if we reach one. + if (!FD || FD->getType()->isReferenceType()) break; // ... and also contains A.B if B names a union member @@ -5116,18 +5441,18 @@ static bool EvaluateArgs(ArrayRef Args, ArgVector &ArgValues, } } } - for (ArrayRef::iterator I = Args.begin(), E = Args.end(); - I != E; ++I) { - if (!Evaluate(ArgValues[I - Args.begin()], Info, *I)) { + for (unsigned Idx = 0; Idx < Args.size(); Idx++) { + if (!Evaluate(ArgValues[Idx], Info, Args[Idx])) { // If we're checking for a potential constant expression, evaluate all // initializers even if some of them fail. if (!Info.noteFailure()) return false; Success = false; } else if (!ForbiddenNullArgs.empty() && - ForbiddenNullArgs[I - Args.begin()] && - ArgValues[I - Args.begin()].isNullPointer()) { - Info.CCEDiag(*I, diag::note_non_null_attribute_failed); + ForbiddenNullArgs[Idx] && + ArgValues[Idx].isLValue() && + ArgValues[Idx].isNullPointer()) { + Info.CCEDiag(Args[Idx], diag::note_non_null_attribute_failed); if (!Info.noteFailure()) return false; Success = false; @@ -5166,8 +5491,8 @@ static bool HandleFunctionCall(SourceLocation CallLoc, LValue RHS; RHS.setFrom(Info.Ctx, ArgValues[0]); APValue RHSValue; - if (!handleLValueToRValueConversion(Info, Args[0], Args[0]->getType(), - RHS, RHSValue)) + if (!handleLValueToRValueConversion(Info, Args[0], Args[0]->getType(), RHS, + RHSValue, MD->getParent()->isUnion())) return false; if (Info.getLangOpts().CPlusPlus2a && MD->isTrivial() && !HandleUnionActiveMemberChange(Info, Args[0], *This)) @@ -5230,7 +5555,8 @@ static bool HandleConstructorCall(const Expr *E, const LValue &This, CXXConstructorDecl::init_const_iterator I = Definition->init_begin(); { FullExpressionRAII InitScope(Info); - if (!EvaluateInPlace(Result, Info, This, (*I)->getInit())) + if (!EvaluateInPlace(Result, Info, This, (*I)->getInit()) || + !InitScope.destroy()) return false; } return EvaluateStmt(Ret, Info, Definition->getBody()) != ESR_Failed; @@ -5251,7 +5577,7 @@ static bool HandleConstructorCall(const Expr *E, const LValue &This, RHS.setFrom(Info.Ctx, ArgValues[0]); return handleLValueToRValueConversion( Info, E, Definition->getParamDecl(0)->getType().getNonReferenceType(), - RHS, Result); + RHS, Result, Definition->getParent()->isUnion()); } // Reserve space for the struct members. @@ -5270,6 +5596,25 @@ static bool HandleConstructorCall(const Expr *E, const LValue &This, #ifndef NDEBUG CXXRecordDecl::base_class_const_iterator BaseIt = RD->bases_begin(); #endif + CXXRecordDecl::field_iterator FieldIt = RD->field_begin(); + auto SkipToField = [&](FieldDecl *FD, bool Indirect) { + // We might be initializing the same field again if this is an indirect + // field initialization. + if (FieldIt == RD->field_end() || + FieldIt->getFieldIndex() > FD->getFieldIndex()) { + assert(Indirect && "fields out of order?"); + return; + } + + // Default-initialize any fields with no explicit initializer. + for (; !declaresSameEntity(*FieldIt, FD); ++FieldIt) { + assert(FieldIt != RD->field_end() && "missing field?"); + if (!FieldIt->isUnnamedBitfield()) + Result.getStructField(FieldIt->getFieldIndex()) = + getDefaultInitValue(FieldIt->getType()); + } + ++FieldIt; + }; for (const auto *I : Definition->inits()) { LValue Subobject = This; LValue SubobjectParent = This; @@ -5298,6 +5643,7 @@ static bool HandleConstructorCall(const Expr *E, const LValue &This, Result = APValue(FD); Value = &Result.getUnionValue(); } else { + SkipToField(FD, false); Value = &Result.getStructField(FD->getFieldIndex()); } } else if (IndirectFieldDecl *IFD = I->getIndirectMember()) { @@ -5317,8 +5663,10 @@ static bool HandleConstructorCall(const Expr *E, const LValue &This, if (CD->isUnion()) *Value = APValue(FD); else - *Value = APValue(APValue::UninitStruct(), CD->getNumBases(), - std::distance(CD->field_begin(), CD->field_end())); + // FIXME: This immediately starts the lifetime of all members of an + // anonymous struct. It would be preferable to strictly start member + // lifetime in initialization order. + *Value = getDefaultInitValue(Info.Ctx.getRecordType(CD)); } // Store Subobject as its parent before updating it for the last element // in the chain. @@ -5328,50 +5676,438 @@ static bool HandleConstructorCall(const Expr *E, const LValue &This, return false; if (CD->isUnion()) Value = &Value->getUnionValue(); - else + else { + if (C == IndirectFieldChain.front() && !RD->isUnion()) + SkipToField(FD, true); Value = &Value->getStructField(FD->getFieldIndex()); + } } } else { llvm_unreachable("unknown base initializer kind"); } - // Need to override This for implicit field initializers as in this case - // This refers to innermost anonymous struct/union containing initializer, - // not to currently constructed class. - const Expr *Init = I->getInit(); - ThisOverrideRAII ThisOverride(*Info.CurrentCall, &SubobjectParent, - isa(Init)); - FullExpressionRAII InitScope(Info); - if (!EvaluateInPlace(*Value, Info, Subobject, Init) || - (FD && FD->isBitField() && - !truncateBitfieldValue(Info, Init, *Value, FD))) { - // If we're checking for a potential constant expression, evaluate all - // initializers even if some of them fail. - if (!Info.noteFailure()) - return false; - Success = false; + // Need to override This for implicit field initializers as in this case + // This refers to innermost anonymous struct/union containing initializer, + // not to currently constructed class. + const Expr *Init = I->getInit(); + ThisOverrideRAII ThisOverride(*Info.CurrentCall, &SubobjectParent, + isa(Init)); + FullExpressionRAII InitScope(Info); + if (!EvaluateInPlace(*Value, Info, Subobject, Init) || + (FD && FD->isBitField() && + !truncateBitfieldValue(Info, Init, *Value, FD))) { + // If we're checking for a potential constant expression, evaluate all + // initializers even if some of them fail. + if (!Info.noteFailure()) + return false; + Success = false; + } + + // This is the point at which the dynamic type of the object becomes this + // class type. + if (I->isBaseInitializer() && BasesSeen == RD->getNumBases()) + EvalObj.finishedConstructingBases(); + } + + // Default-initialize any remaining fields. + if (!RD->isUnion()) { + for (; FieldIt != RD->field_end(); ++FieldIt) { + if (!FieldIt->isUnnamedBitfield()) + Result.getStructField(FieldIt->getFieldIndex()) = + getDefaultInitValue(FieldIt->getType()); + } + } + + return Success && + EvaluateStmt(Ret, Info, Definition->getBody()) != ESR_Failed && + LifetimeExtendedScope.destroy(); +} + +static bool HandleConstructorCall(const Expr *E, const LValue &This, + ArrayRef Args, + const CXXConstructorDecl *Definition, + EvalInfo &Info, APValue &Result) { + ArgVector ArgValues(Args.size()); + if (!EvaluateArgs(Args, ArgValues, Info, Definition)) + return false; + + return HandleConstructorCall(E, This, ArgValues.data(), Definition, + Info, Result); +} + +static bool HandleDestructionImpl(EvalInfo &Info, SourceLocation CallLoc, + const LValue &This, APValue &Value, + QualType T) { + // Objects can only be destroyed while they're within their lifetimes. + // FIXME: We have no representation for whether an object of type nullptr_t + // is in its lifetime; it usually doesn't matter. Perhaps we should model it + // as indeterminate instead? + if (Value.isAbsent() && !T->isNullPtrType()) { + APValue Printable; + This.moveInto(Printable); + Info.FFDiag(CallLoc, diag::note_constexpr_destroy_out_of_lifetime) + << Printable.getAsString(Info.Ctx, Info.Ctx.getLValueReferenceType(T)); + return false; + } + + // Invent an expression for location purposes. + // FIXME: We shouldn't need to do this. + OpaqueValueExpr LocE(CallLoc, Info.Ctx.IntTy, VK_RValue); + + // For arrays, destroy elements right-to-left. + if (const ConstantArrayType *CAT = Info.Ctx.getAsConstantArrayType(T)) { + uint64_t Size = CAT->getSize().getZExtValue(); + QualType ElemT = CAT->getElementType(); + + LValue ElemLV = This; + ElemLV.addArray(Info, &LocE, CAT); + if (!HandleLValueArrayAdjustment(Info, &LocE, ElemLV, ElemT, Size)) + return false; + + // Ensure that we have actual array elements available to destroy; the + // destructors might mutate the value, so we can't run them on the array + // filler. + if (Size && Size > Value.getArrayInitializedElts()) + expandArray(Value, Value.getArraySize() - 1); + + for (; Size != 0; --Size) { + APValue &Elem = Value.getArrayInitializedElt(Size - 1); + if (!HandleLValueArrayAdjustment(Info, &LocE, ElemLV, ElemT, -1) || + !HandleDestructionImpl(Info, CallLoc, ElemLV, Elem, ElemT)) + return false; + } + + // End the lifetime of this array now. + Value = APValue(); + return true; + } + + const CXXRecordDecl *RD = T->getAsCXXRecordDecl(); + if (!RD) { + if (T.isDestructedType()) { + Info.FFDiag(CallLoc, diag::note_constexpr_unsupported_destruction) << T; + return false; + } + + Value = APValue(); + return true; + } + + if (RD->getNumVBases()) { + Info.FFDiag(CallLoc, diag::note_constexpr_virtual_base) << RD; + return false; + } + + const CXXDestructorDecl *DD = RD->getDestructor(); + if (!DD && !RD->hasTrivialDestructor()) { + Info.FFDiag(CallLoc); + return false; + } + + if (!DD || DD->isTrivial() || + (RD->isAnonymousStructOrUnion() && RD->isUnion())) { + // A trivial destructor just ends the lifetime of the object. Check for + // this case before checking for a body, because we might not bother + // building a body for a trivial destructor. Note that it doesn't matter + // whether the destructor is constexpr in this case; all trivial + // destructors are constexpr. + // + // If an anonymous union would be destroyed, some enclosing destructor must + // have been explicitly defined, and the anonymous union destruction should + // have no effect. + Value = APValue(); + return true; + } + + if (!Info.CheckCallLimit(CallLoc)) + return false; + + const FunctionDecl *Definition = nullptr; + const Stmt *Body = DD->getBody(Definition); + + if (!CheckConstexprFunction(Info, CallLoc, DD, Definition, Body)) + return false; + + CallStackFrame Frame(Info, CallLoc, Definition, &This, nullptr); + + // We're now in the period of destruction of this object. + unsigned BasesLeft = RD->getNumBases(); + EvalInfo::EvaluatingDestructorRAII EvalObj( + Info, + ObjectUnderConstruction{This.getLValueBase(), This.Designator.Entries}); + if (!EvalObj.DidInsert) { + // C++2a [class.dtor]p19: + // the behavior is undefined if the destructor is invoked for an object + // whose lifetime has ended + // (Note that formally the lifetime ends when the period of destruction + // begins, even though certain uses of the object remain valid until the + // period of destruction ends.) + Info.FFDiag(CallLoc, diag::note_constexpr_double_destroy); + return false; + } + + // FIXME: Creating an APValue just to hold a nonexistent return value is + // wasteful. + APValue RetVal; + StmtResult Ret = {RetVal, nullptr}; + if (EvaluateStmt(Ret, Info, Definition->getBody()) == ESR_Failed) + return false; + + // A union destructor does not implicitly destroy its members. + if (RD->isUnion()) + return true; + + const ASTRecordLayout &Layout = Info.Ctx.getASTRecordLayout(RD); + + // We don't have a good way to iterate fields in reverse, so collect all the + // fields first and then walk them backwards. + SmallVector Fields(RD->field_begin(), RD->field_end()); + for (const FieldDecl *FD : llvm::reverse(Fields)) { + if (FD->isUnnamedBitfield()) + continue; + + LValue Subobject = This; + if (!HandleLValueMember(Info, &LocE, Subobject, FD, &Layout)) + return false; + + APValue *SubobjectValue = &Value.getStructField(FD->getFieldIndex()); + if (!HandleDestructionImpl(Info, CallLoc, Subobject, *SubobjectValue, + FD->getType())) + return false; + } + + if (BasesLeft != 0) + EvalObj.startedDestroyingBases(); + + // Destroy base classes in reverse order. + for (const CXXBaseSpecifier &Base : llvm::reverse(RD->bases())) { + --BasesLeft; + + QualType BaseType = Base.getType(); + LValue Subobject = This; + if (!HandleLValueDirectBase(Info, &LocE, Subobject, RD, + BaseType->getAsCXXRecordDecl(), &Layout)) + return false; + + APValue *SubobjectValue = &Value.getStructBase(BasesLeft); + if (!HandleDestructionImpl(Info, CallLoc, Subobject, *SubobjectValue, + BaseType)) + return false; + } + assert(BasesLeft == 0 && "NumBases was wrong?"); + + // The period of destruction ends now. The object is gone. + Value = APValue(); + return true; +} + +namespace { +struct DestroyObjectHandler { + EvalInfo &Info; + const Expr *E; + const LValue &This; + const AccessKinds AccessKind; + + typedef bool result_type; + bool failed() { return false; } + bool found(APValue &Subobj, QualType SubobjType) { + return HandleDestructionImpl(Info, E->getExprLoc(), This, Subobj, + SubobjType); + } + bool found(APSInt &Value, QualType SubobjType) { + Info.FFDiag(E, diag::note_constexpr_destroy_complex_elem); + return false; + } + bool found(APFloat &Value, QualType SubobjType) { + Info.FFDiag(E, diag::note_constexpr_destroy_complex_elem); + return false; + } +}; +} + +/// Perform a destructor or pseudo-destructor call on the given object, which +/// might in general not be a complete object. +static bool HandleDestruction(EvalInfo &Info, const Expr *E, + const LValue &This, QualType ThisType) { + CompleteObject Obj = findCompleteObject(Info, E, AK_Destroy, This, ThisType); + DestroyObjectHandler Handler = {Info, E, This, AK_Destroy}; + return Obj && findSubobject(Info, E, Obj, This.Designator, Handler); +} + +/// Destroy and end the lifetime of the given complete object. +static bool HandleDestruction(EvalInfo &Info, SourceLocation Loc, + APValue::LValueBase LVBase, APValue &Value, + QualType T) { + // If we've had an unmodeled side-effect, we can't rely on mutable state + // (such as the object we're about to destroy) being correct. + if (Info.EvalStatus.HasSideEffects) + return false; + + LValue LV; + LV.set({LVBase}); + return HandleDestructionImpl(Info, Loc, LV, Value, T); +} + +/// Perform a call to 'perator new' or to `__builtin_operator_new'. +static bool HandleOperatorNewCall(EvalInfo &Info, const CallExpr *E, + LValue &Result) { + if (Info.checkingPotentialConstantExpression() || + Info.SpeculativeEvaluationDepth) + return false; + + // This is permitted only within a call to std::allocator::allocate. + auto Caller = Info.getStdAllocatorCaller("allocate"); + if (!Caller) { + Info.FFDiag(E->getExprLoc(), Info.getLangOpts().CPlusPlus2a + ? diag::note_constexpr_new_untyped + : diag::note_constexpr_new); + return false; + } + + QualType ElemType = Caller.ElemType; + if (ElemType->isIncompleteType() || ElemType->isFunctionType()) { + Info.FFDiag(E->getExprLoc(), + diag::note_constexpr_new_not_complete_object_type) + << (ElemType->isIncompleteType() ? 0 : 1) << ElemType; + return false; + } + + APSInt ByteSize; + if (!EvaluateInteger(E->getArg(0), ByteSize, Info)) + return false; + bool IsNothrow = false; + for (unsigned I = 1, N = E->getNumArgs(); I != N; ++I) { + EvaluateIgnoredValue(Info, E->getArg(I)); + IsNothrow |= E->getType()->isNothrowT(); + } + + CharUnits ElemSize; + if (!HandleSizeof(Info, E->getExprLoc(), ElemType, ElemSize)) + return false; + APInt Size, Remainder; + APInt ElemSizeAP(ByteSize.getBitWidth(), ElemSize.getQuantity()); + APInt::udivrem(ByteSize, ElemSizeAP, Size, Remainder); + if (Remainder != 0) { + // This likely indicates a bug in the implementation of 'std::allocator'. + Info.FFDiag(E->getExprLoc(), diag::note_constexpr_operator_new_bad_size) + << ByteSize << APSInt(ElemSizeAP, true) << ElemType; + return false; + } + + if (ByteSize.getActiveBits() > ConstantArrayType::getMaxSizeBits(Info.Ctx)) { + if (IsNothrow) { + Result.setNull(Info.Ctx, E->getType()); + return true; } - // This is the point at which the dynamic type of the object becomes this - // class type. - if (I->isBaseInitializer() && BasesSeen == RD->getNumBases()) - EvalObj.finishedConstructingBases(); + Info.FFDiag(E, diag::note_constexpr_new_too_large) << APSInt(Size, true); + return false; } - return Success && - EvaluateStmt(Ret, Info, Definition->getBody()) != ESR_Failed; + QualType AllocType = Info.Ctx.getConstantArrayType(ElemType, Size, nullptr, + ArrayType::Normal, 0); + APValue *Val = Info.createHeapAlloc(E, AllocType, Result); + *Val = APValue(APValue::UninitArray(), 0, Size.getZExtValue()); + Result.addArray(Info, E, cast(AllocType)); + return true; } -static bool HandleConstructorCall(const Expr *E, const LValue &This, - ArrayRef Args, - const CXXConstructorDecl *Definition, - EvalInfo &Info, APValue &Result) { - ArgVector ArgValues(Args.size()); - if (!EvaluateArgs(Args, ArgValues, Info, Definition)) +static bool hasVirtualDestructor(QualType T) { + if (CXXRecordDecl *RD = T->getAsCXXRecordDecl()) + if (CXXDestructorDecl *DD = RD->getDestructor()) + return DD->isVirtual(); + return false; +} + +static const FunctionDecl *getVirtualOperatorDelete(QualType T) { + if (CXXRecordDecl *RD = T->getAsCXXRecordDecl()) + if (CXXDestructorDecl *DD = RD->getDestructor()) + return DD->isVirtual() ? DD->getOperatorDelete() : nullptr; + return nullptr; +} + +/// Check that the given object is a suitable pointer to a heap allocation that +/// still exists and is of the right kind for the purpose of a deletion. +/// +/// On success, returns the heap allocation to deallocate. On failure, produces +/// a diagnostic and returns None. +static Optional CheckDeleteKind(EvalInfo &Info, const Expr *E, + const LValue &Pointer, + DynAlloc::Kind DeallocKind) { + auto PointerAsString = [&] { + return Pointer.toString(Info.Ctx, Info.Ctx.VoidPtrTy); + }; + + DynamicAllocLValue DA = Pointer.Base.dyn_cast(); + if (!DA) { + Info.FFDiag(E, diag::note_constexpr_delete_not_heap_alloc) + << PointerAsString(); + if (Pointer.Base) + NoteLValueLocation(Info, Pointer.Base); + return None; + } + + Optional Alloc = Info.lookupDynamicAlloc(DA); + if (!Alloc) { + Info.FFDiag(E, diag::note_constexpr_double_delete); + return None; + } + + QualType AllocType = Pointer.Base.getDynamicAllocType(); + if (DeallocKind != (*Alloc)->getKind()) { + Info.FFDiag(E, diag::note_constexpr_new_delete_mismatch) + << DeallocKind << (*Alloc)->getKind() << AllocType; + NoteLValueLocation(Info, Pointer.Base); + return None; + } + + bool Subobject = false; + if (DeallocKind == DynAlloc::New) { + Subobject = Pointer.Designator.MostDerivedPathLength != 0 || + Pointer.Designator.isOnePastTheEnd(); + } else { + Subobject = Pointer.Designator.Entries.size() != 1 || + Pointer.Designator.Entries[0].getAsArrayIndex() != 0; + } + if (Subobject) { + Info.FFDiag(E, diag::note_constexpr_delete_subobject) + << PointerAsString() << Pointer.Designator.isOnePastTheEnd(); + return None; + } + + return Alloc; +} + +// Perform a call to 'operator delete' or '__builtin_operator_delete'. +bool HandleOperatorDeleteCall(EvalInfo &Info, const CallExpr *E) { + if (Info.checkingPotentialConstantExpression() || + Info.SpeculativeEvaluationDepth) return false; - return HandleConstructorCall(E, This, ArgValues.data(), Definition, - Info, Result); + // This is permitted only within a call to std::allocator::deallocate. + if (!Info.getStdAllocatorCaller("deallocate")) { + Info.FFDiag(E->getExprLoc()); + return true; + } + + LValue Pointer; + if (!EvaluatePointer(E->getArg(0), Pointer, Info)) + return false; + for (unsigned I = 1, N = E->getNumArgs(); I != N; ++I) + EvaluateIgnoredValue(Info, E->getArg(I)); + + if (Pointer.Designator.Invalid) + return false; + + // Deleting a null pointer has no effect. + if (Pointer.isNullPointer()) + return true; + + if (!CheckDeleteKind(Info, E, Pointer, DynAlloc::StdAllocator)) + return false; + + Info.HeapAllocs.erase(Pointer.Base.get()); + return true; } //===----------------------------------------------------------------------===// @@ -5706,9 +6442,8 @@ class BufferToAPValueConverter { QualType RepresentationType = Ty->getDecl()->getIntegerType(); assert(!RepresentationType.isNull() && "enum forward decl should be caught by Sema"); - const BuiltinType *AsBuiltin = - RepresentationType.getCanonicalType()->getAs(); - assert(AsBuiltin && "non-integral enum underlying type?"); + const auto *AsBuiltin = + RepresentationType.getCanonicalType()->castAs(); // Recurse into the underlying type. Treat std::byte transparently as // unsigned char. return visit(AsBuiltin, Offset, /*EnumTy=*/Ty); @@ -5752,7 +6487,7 @@ class BufferToAPValueConverter { #define NON_CANONICAL_UNLESS_DEPENDENT(Class, Base) \ case Type::Class: \ llvm_unreachable("either dependent or not canonical!"); -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" } llvm_unreachable("Unhandled Type::TypeClass"); } @@ -5843,9 +6578,9 @@ static bool handleLValueToRValueBitCast(EvalInfo &Info, APValue &DestValue, LValue SourceLValue; APValue SourceRValue; SourceLValue.setFrom(Info.Ctx, SourceValue); - if (!handleLValueToRValueConversion(Info, BCE, - BCE->getSubExpr()->getType().withConst(), - SourceLValue, SourceRValue)) + if (!handleLValueToRValueConversion( + Info, BCE, BCE->getSubExpr()->getType().withConst(), SourceLValue, + SourceRValue, /*WantObjectRepresentation=*/true)) return false; // Read out SourceValue into a char buffer. @@ -5984,10 +6719,16 @@ public: 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. - bool VisitExprWithCleanups(const ExprWithCleanups *E) - { return StmtVisitorTy::Visit(E->getSubExpr()); } + bool VisitExprWithCleanups(const ExprWithCleanups *E) { + FullExpressionRAII Scope(Info); + return StmtVisitorTy::Visit(E->getSubExpr()) && Scope.destroy(); + } + + // Temporaries are registered when created, so we don't care about + // CXXBindTemporaryExpr. + bool VisitCXXBindTemporaryExpr(const CXXBindTemporaryExpr *E) { + return StmtVisitorTy::Visit(E->getSubExpr()); + } bool VisitCXXReinterpretCastExpr(const CXXReinterpretCastExpr *E) { CCEDiag(E, diag::note_constexpr_invalid_cast) << 0; @@ -6024,10 +6765,18 @@ public: } } + bool VisitCXXRewrittenBinaryOperator(const CXXRewrittenBinaryOperator *E) { + return StmtVisitorTy::Visit(E->getSemanticForm()); + } + bool VisitBinaryConditionalOperator(const BinaryConditionalOperator *E) { // Evaluate and cache the common expression. We treat it as a temporary, // even though it's not quite the same thing. - if (!Evaluate(Info.CurrentCall->createTemporary(E->getOpaqueValue(), false), + LValue CommonLV; + if (!Evaluate(Info.CurrentCall->createTemporary( + E->getOpaqueValue(), + getStorageType(Info.Ctx, E->getOpaqueValue()), false, + CommonLV), Info, E->getCommon())) return false; @@ -6047,6 +6796,8 @@ public: // Always assume __builtin_constant_p(...) ? ... : ... is a potential // constant expression; we can't check whether it's potentially foldable. + // FIXME: We should instead treat __builtin_constant_p as non-constant if + // it would return 'false' in this mode. if (Info.checkingPotentialConstantExpression() && IsBcpCall) return false; @@ -6104,11 +6855,21 @@ public: HasQualifier = ME->hasQualifier(); } else if (const BinaryOperator *BE = dyn_cast(Callee)) { // Indirect bound member calls ('.*' or '->*'). - Member = dyn_cast_or_null( - HandleMemberPointerAccess(Info, BE, ThisVal, false)); + const ValueDecl *D = + HandleMemberPointerAccess(Info, BE, ThisVal, false); + if (!D) + return false; + Member = dyn_cast(D); if (!Member) return Error(Callee); This = &ThisVal; + } else if (const auto *PDE = dyn_cast(Callee)) { + if (!Info.getLangOpts().CPlusPlus2a) + Info.CCEDiag(PDE, diag::note_constexpr_pseudo_destructor); + // FIXME: If pseudo-destructor calls ever start ending the lifetime of + // their callee, we should start calling HandleDestruction here. + // For now, we just evaluate the object argument and discard it. + return EvaluateObjectArgument(Info, PDE->getBase(), ThisVal); } else return Error(Callee); FD = Member; @@ -6177,6 +6938,17 @@ public: FD = cast(CorrespondingCallOpSpecialization); } else FD = LambdaCallOp; + } else if (FD->isReplaceableGlobalAllocationFunction()) { + if (FD->getDeclName().getCXXOverloadedOperator() == OO_New || + FD->getDeclName().getCXXOverloadedOperator() == OO_Array_New) { + LValue Ptr; + if (!HandleOperatorNewCall(Info, E, Ptr)) + return false; + Ptr.moveInto(Result); + return true; + } else { + return HandleOperatorDeleteCall(Info, E); + } } } else return Error(E); @@ -6192,11 +6964,20 @@ public: return false; } else { // Check that the 'this' pointer points to an object of the right type. - if (!checkNonVirtualMemberCallThisPointer(Info, E, *This)) + // FIXME: If this is an assignment operator call, we may need to change + // the active union member before we check this. + if (!checkNonVirtualMemberCallThisPointer(Info, E, *This, NamedMember)) return false; } } + // Destructor calls are different enough that they have their own codepath. + if (auto *DD = dyn_cast(FD)) { + assert(This && "no 'this' pointer for destructor call"); + return HandleDestruction(Info, E, *This, + Info.Ctx.getRecordType(DD->getParent())); + } + const FunctionDecl *Definition = nullptr; Stmt *Body = FD->getBody(Definition); @@ -6329,14 +7110,14 @@ public: bool VisitStmtExpr(const StmtExpr *E) { // We will have checked the full-expressions inside the statement expression // when they were completed, and don't need to check them again now. - if (Info.checkingForOverflow()) + if (Info.checkingForUndefinedBehavior()) return Error(E); - BlockScopeRAII Scope(Info); const CompoundStmt *CS = E->getSubStmt(); if (CS->body_empty()) return true; + BlockScopeRAII Scope(Info); for (CompoundStmt::const_body_iterator BI = CS->body_begin(), BE = CS->body_end(); /**/; ++BI) { @@ -6347,7 +7128,7 @@ public: diag::note_constexpr_stmt_expr_unsupported); return false; } - return this->Visit(FinalExpr); + return this->Visit(FinalExpr) && Scope.destroy(); } APValue ReturnValue; @@ -6440,7 +7221,7 @@ public: const ValueDecl *MD = E->getMemberDecl(); if (const FieldDecl *FD = dyn_cast(E->getMemberDecl())) { - assert(BaseTy->getAs()->getDecl()->getCanonicalDecl() == + assert(BaseTy->castAs()->getDecl()->getCanonicalDecl() == FD->getParent()->getCanonicalDecl() && "record / field mismatch"); (void)BaseTy; if (!HandleLValueMember(this->Info, E, Result, FD)) @@ -6696,16 +7477,14 @@ bool LValueExprEvaluator::VisitMaterializeTemporaryExpr( *Value = APValue(); Result.set(E); } else { - Value = &createTemporary(E, E->getStorageDuration() == SD_Automatic, Result, - *Info.CurrentCall); + Value = &Info.CurrentCall->createTemporary( + E, E->getType(), E->getStorageDuration() == SD_Automatic, Result); } QualType Type = Inner->getType(); // Materialize the temporary itself. - if (!EvaluateInPlace(*Value, Info, Result, Inner) || - (E->getStorageDuration() == SD_Static && - !CheckConstantExpression(Info, E->getExprLoc(), Type, *Value))) { + if (!EvaluateInPlace(*Value, Info, Result, Inner)) { *Value = APValue(); return false; } @@ -7035,8 +7814,7 @@ public: return true; } bool ZeroInitialization(const Expr *E) { - auto TargetVal = Info.Ctx.getTargetNullPointerValue(E->getType()); - Result.setNull(E->getType(), TargetVal); + Result.setNull(Info.Ctx, E->getType()); return true; } @@ -7097,6 +7875,8 @@ public: return true; } + bool VisitCXXNewExpr(const CXXNewExpr *E); + bool VisitSourceLocExpr(const SourceLocExpr *E) { assert(E->isStringType() && "SourceLocExpr isn't a pointer type?"); APValue LValResult = E->EvaluateInContext( @@ -7161,12 +7941,22 @@ bool PointerExprEvaluator::VisitCastExpr(const CastExpr *E) { // permitted in constant expressions in C++11. Bitcasts from cv void* are // also static_casts, but we disallow them as a resolution to DR1312. if (!E->getType()->isVoidPointerType()) { - Result.Designator.setInvalid(); - if (SubExpr->getType()->isVoidPointerType()) - CCEDiag(E, diag::note_constexpr_invalid_cast) - << 3 << SubExpr->getType(); - else - CCEDiag(E, diag::note_constexpr_invalid_cast) << 2; + if (!Result.InvalidBase && !Result.Designator.Invalid && + !Result.IsNullPtr && + Info.Ctx.hasSameUnqualifiedType(Result.Designator.getType(Info.Ctx), + E->getType()->getPointeeType()) && + Info.getStdAllocatorCaller("allocate")) { + // Inside a call to std::allocator::allocate and friends, we permit + // casting from void* back to cv1 T* for a pointer that points to a + // cv2 T. + } else { + Result.Designator.setInvalid(); + if (SubExpr->getType()->isVoidPointerType()) + CCEDiag(E, diag::note_constexpr_invalid_cast) + << 3 << SubExpr->getType(); + else + CCEDiag(E, diag::note_constexpr_invalid_cast) << 2; + } } if (E->getCastKind() == CK_AddressSpaceConversion && Result.IsNullPtr) ZeroInitialization(E); @@ -7229,8 +8019,8 @@ bool PointerExprEvaluator::VisitCastExpr(const CastExpr *E) { if (!evaluateLValue(SubExpr, Result)) return false; } else { - APValue &Value = createTemporary(SubExpr, false, Result, - *Info.CurrentCall); + APValue &Value = Info.CurrentCall->createTemporary( + SubExpr, SubExpr->getType(), false, Result); if (!EvaluateInPlace(Value, Info, Result, SubExpr)) return false; } @@ -7403,6 +8193,8 @@ bool PointerExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, return true; } + case Builtin::BI__builtin_operator_new: + return HandleOperatorNewCall(Info, E, Result); case Builtin::BI__builtin_launder: return evaluatePointer(E->getArg(0), Result); case Builtin::BIstrchr: @@ -7638,6 +8430,8 @@ bool PointerExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, while (true) { APValue Val; + // FIXME: Set WantObjectRepresentation to true if we're copying a + // char-like type? if (!handleLValueToRValueConversion(Info, E, T, Src, Val) || !handleAssignment(Info, E, Dest, T, Val)) return false; @@ -7652,10 +8446,208 @@ bool PointerExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, } default: - return visitNonBuiltinCallExpr(E); + break; } + + return visitNonBuiltinCallExpr(E); } +static bool EvaluateArrayNewInitList(EvalInfo &Info, LValue &This, + APValue &Result, const InitListExpr *ILE, + QualType AllocType); + +bool PointerExprEvaluator::VisitCXXNewExpr(const CXXNewExpr *E) { + if (!Info.getLangOpts().CPlusPlus2a) + Info.CCEDiag(E, diag::note_constexpr_new); + + // We cannot speculatively evaluate a delete expression. + if (Info.SpeculativeEvaluationDepth) + return false; + + FunctionDecl *OperatorNew = E->getOperatorNew(); + + bool IsNothrow = false; + bool IsPlacement = false; + if (OperatorNew->isReservedGlobalPlacementOperator() && + Info.CurrentCall->isStdFunction() && !E->isArray()) { + // FIXME Support array placement new. + assert(E->getNumPlacementArgs() == 1); + if (!EvaluatePointer(E->getPlacementArg(0), Result, Info)) + return false; + if (Result.Designator.Invalid) + return false; + IsPlacement = true; + } else if (!OperatorNew->isReplaceableGlobalAllocationFunction()) { + Info.FFDiag(E, diag::note_constexpr_new_non_replaceable) + << isa(OperatorNew) << OperatorNew; + return false; + } else if (E->getNumPlacementArgs()) { + // The only new-placement list we support is of the form (std::nothrow). + // + // FIXME: There is no restriction on this, but it's not clear that any + // other form makes any sense. We get here for cases such as: + // + // new (std::align_val_t{N}) X(int) + // + // (which should presumably be valid only if N is a multiple of + // alignof(int), and in any case can't be deallocated unless N is + // alignof(X) and X has new-extended alignment). + if (E->getNumPlacementArgs() != 1 || + !E->getPlacementArg(0)->getType()->isNothrowT()) + return Error(E, diag::note_constexpr_new_placement); + + LValue Nothrow; + if (!EvaluateLValue(E->getPlacementArg(0), Nothrow, Info)) + return false; + IsNothrow = true; + } + + const Expr *Init = E->getInitializer(); + const InitListExpr *ResizedArrayILE = nullptr; + + QualType AllocType = E->getAllocatedType(); + if (Optional ArraySize = E->getArraySize()) { + const Expr *Stripped = *ArraySize; + for (; auto *ICE = dyn_cast(Stripped); + Stripped = ICE->getSubExpr()) + if (ICE->getCastKind() != CK_NoOp && + ICE->getCastKind() != CK_IntegralCast) + break; + + llvm::APSInt ArrayBound; + if (!EvaluateInteger(Stripped, ArrayBound, Info)) + return false; + + // C++ [expr.new]p9: + // The expression is erroneous if: + // -- [...] its value before converting to size_t [or] applying the + // second standard conversion sequence is less than zero + if (ArrayBound.isSigned() && ArrayBound.isNegative()) { + if (IsNothrow) + return ZeroInitialization(E); + + Info.FFDiag(*ArraySize, diag::note_constexpr_new_negative) + << ArrayBound << (*ArraySize)->getSourceRange(); + return false; + } + + // -- its value is such that the size of the allocated object would + // exceed the implementation-defined limit + if (ConstantArrayType::getNumAddressingBits(Info.Ctx, AllocType, + ArrayBound) > + ConstantArrayType::getMaxSizeBits(Info.Ctx)) { + if (IsNothrow) + return ZeroInitialization(E); + + Info.FFDiag(*ArraySize, diag::note_constexpr_new_too_large) + << ArrayBound << (*ArraySize)->getSourceRange(); + return false; + } + + // -- the new-initializer is a braced-init-list and the number of + // array elements for which initializers are provided [...] + // exceeds the number of elements to initialize + if (Init) { + auto *CAT = Info.Ctx.getAsConstantArrayType(Init->getType()); + assert(CAT && "unexpected type for array initializer"); + + unsigned Bits = + std::max(CAT->getSize().getBitWidth(), ArrayBound.getBitWidth()); + llvm::APInt InitBound = CAT->getSize().zextOrSelf(Bits); + llvm::APInt AllocBound = ArrayBound.zextOrSelf(Bits); + if (InitBound.ugt(AllocBound)) { + if (IsNothrow) + return ZeroInitialization(E); + + Info.FFDiag(*ArraySize, diag::note_constexpr_new_too_small) + << AllocBound.toString(10, /*Signed=*/false) + << InitBound.toString(10, /*Signed=*/false) + << (*ArraySize)->getSourceRange(); + return false; + } + + // If the sizes differ, we must have an initializer list, and we need + // special handling for this case when we initialize. + if (InitBound != AllocBound) + ResizedArrayILE = cast(Init); + } + + AllocType = Info.Ctx.getConstantArrayType(AllocType, ArrayBound, nullptr, + ArrayType::Normal, 0); + } else { + assert(!AllocType->isArrayType() && + "array allocation with non-array new"); + } + + APValue *Val; + if (IsPlacement) { + AccessKinds AK = AK_Construct; + struct FindObjectHandler { + EvalInfo &Info; + const Expr *E; + QualType AllocType; + const AccessKinds AccessKind; + APValue *Value; + + typedef bool result_type; + bool failed() { return false; } + bool found(APValue &Subobj, QualType SubobjType) { + // FIXME: Reject the cases where [basic.life]p8 would not permit the + // old name of the object to be used to name the new object. + if (!Info.Ctx.hasSameUnqualifiedType(SubobjType, AllocType)) { + Info.FFDiag(E, diag::note_constexpr_placement_new_wrong_type) << + SubobjType << AllocType; + return false; + } + Value = &Subobj; + return true; + } + bool found(APSInt &Value, QualType SubobjType) { + Info.FFDiag(E, diag::note_constexpr_construct_complex_elem); + return false; + } + bool found(APFloat &Value, QualType SubobjType) { + Info.FFDiag(E, diag::note_constexpr_construct_complex_elem); + return false; + } + } Handler = {Info, E, AllocType, AK, nullptr}; + + CompleteObject Obj = findCompleteObject(Info, E, AK, Result, AllocType); + if (!Obj || !findSubobject(Info, E, Obj, Result.Designator, Handler)) + return false; + + Val = Handler.Value; + + // [basic.life]p1: + // The lifetime of an object o of type T ends when [...] the storage + // which the object occupies is [...] reused by an object that is not + // nested within o (6.6.2). + *Val = APValue(); + } else { + // Perform the allocation and obtain a pointer to the resulting object. + Val = Info.createHeapAlloc(E, AllocType, Result); + if (!Val) + return false; + } + + if (ResizedArrayILE) { + if (!EvaluateArrayNewInitList(Info, Result, *Val, ResizedArrayILE, + AllocType)) + return false; + } else if (Init) { + if (!EvaluateInPlace(*Val, Info, Result, Init)) + return false; + } else { + *Val = getDefaultInitValue(AllocType); + } + + // Array new returns a pointer to the first element, not a pointer to the + // array. + if (auto *AT = AllocType->getAsArrayTypeUnsafe()) + Result.addArray(Info, E, cast(AT)); + + return true; +} //===----------------------------------------------------------------------===// // Member Pointer Evaluation //===----------------------------------------------------------------------===// @@ -7779,7 +8771,6 @@ namespace { bool VisitCXXInheritedCtorInitExpr(const CXXInheritedCtorInitExpr *E); bool VisitCXXConstructExpr(const CXXConstructExpr *E, QualType T); bool VisitCXXStdInitializerListExpr(const CXXStdInitializerListExpr *E); - bool VisitBinCmp(const BinaryOperator *E); }; } @@ -8013,15 +9004,11 @@ bool RecordExprEvaluator::VisitCXXConstructExpr(const CXXConstructExpr *E, if (Result.hasValue()) return true; - // We can get here in two different ways: - // 1) We're performing value-initialization, and should zero-initialize - // the object, or - // 2) We're performing default-initialization of an object with a trivial - // constexpr default constructor, in which case we should start the - // lifetimes of all the base subobjects (there can be no data member - // subobjects in this case) per [basic.life]p1. - // Either way, ZeroInitialization is appropriate. - return ZeroInitialization(E, T); + if (ZeroInit) + return ZeroInitialization(E, T); + + Result = getDefaultInitValue(T); + return true; } const FunctionDecl *Definition = nullptr; @@ -8121,9 +9108,8 @@ bool RecordExprEvaluator::VisitCXXStdInitializerListExpr( bool RecordExprEvaluator::VisitLambdaExpr(const LambdaExpr *E) { const CXXRecordDecl *ClosureClass = E->getLambdaClass(); - if (ClosureClass->isInvalidDecl()) return false; - - if (Info.checkingPotentialConstantExpression()) return true; + if (ClosureClass->isInvalidDecl()) + return false; const size_t NumFields = std::distance(ClosureClass->field_begin(), ClosureClass->field_end()); @@ -8183,7 +9169,8 @@ public: /// Visit an expression which constructs the value of this temporary. bool VisitConstructExpr(const Expr *E) { - APValue &Value = createTemporary(E, false, Result, *Info.CurrentCall); + APValue &Value = + Info.CurrentCall->createTemporary(E, E->getType(), false, Result); return EvaluateInPlace(Value, Info, Result, E); } @@ -8383,7 +9370,7 @@ VectorExprEvaluator::VisitInitListExpr(const InitListExpr *E) { bool VectorExprEvaluator::ZeroInitialization(const Expr *E) { - const VectorType *VT = E->getType()->getAs(); + const auto *VT = E->getType()->castAs(); QualType EltTy = VT->getElementType(); APValue ZeroElement; if (EltTy->isIntegerType()) @@ -8441,14 +9428,16 @@ namespace { bool VisitCallExpr(const CallExpr *E) { return handleCallExpr(E, Result, &This); } - bool VisitInitListExpr(const InitListExpr *E); + bool VisitInitListExpr(const InitListExpr *E, + QualType AllocType = QualType()); bool VisitArrayInitLoopExpr(const ArrayInitLoopExpr *E); bool VisitCXXConstructExpr(const CXXConstructExpr *E); bool VisitCXXConstructExpr(const CXXConstructExpr *E, const LValue &Subobject, APValue *Value, QualType Type); - bool VisitStringLiteral(const StringLiteral *E) { - expandStringLiteral(Info, E, Result); + bool VisitStringLiteral(const StringLiteral *E, + QualType AllocType = QualType()) { + expandStringLiteral(Info, E, Result, AllocType); return true; } }; @@ -8460,6 +9449,15 @@ static bool EvaluateArray(const Expr *E, const LValue &This, return ArrayExprEvaluator(Info, This, Result).Visit(E); } +static bool EvaluateArrayNewInitList(EvalInfo &Info, LValue &This, + APValue &Result, const InitListExpr *ILE, + QualType AllocType) { + assert(ILE->isRValue() && ILE->getType()->isArrayType() && + "not an array rvalue"); + return ArrayExprEvaluator(Info, This, Result) + .VisitInitListExpr(ILE, AllocType); +} + // Return true iff the given array filler may depend on the element index. static bool MaybeElementDependentArrayFiller(const Expr *FillerExpr) { // For now, just whitelist non-class value-initialization and initialization @@ -8476,15 +9474,23 @@ static bool MaybeElementDependentArrayFiller(const Expr *FillerExpr) { return true; } -bool ArrayExprEvaluator::VisitInitListExpr(const InitListExpr *E) { - const ConstantArrayType *CAT = Info.Ctx.getAsConstantArrayType(E->getType()); +bool ArrayExprEvaluator::VisitInitListExpr(const InitListExpr *E, + QualType AllocType) { + const ConstantArrayType *CAT = Info.Ctx.getAsConstantArrayType( + AllocType.isNull() ? E->getType() : AllocType); if (!CAT) return Error(E); // C++11 [dcl.init.string]p1: A char array [...] can be initialized by [...] // an appropriately-typed string literal enclosed in braces. - if (E->isStringLiteralInit()) - return Visit(E->getInit(0)); + if (E->isStringLiteralInit()) { + auto *SL = dyn_cast(E->getInit(0)->IgnoreParens()); + // FIXME: Support ObjCEncodeExpr here once we support it in + // ArrayExprEvaluator generally. + if (!SL) + return Error(E); + return VisitStringLiteral(SL, AllocType); + } bool Success = true; @@ -8543,8 +9549,12 @@ bool ArrayExprEvaluator::VisitInitListExpr(const InitListExpr *E) { } bool ArrayExprEvaluator::VisitArrayInitLoopExpr(const ArrayInitLoopExpr *E) { + LValue CommonLV; if (E->getCommonExpr() && - !Evaluate(Info.CurrentCall->createTemporary(E->getCommonExpr(), false), + !Evaluate(Info.CurrentCall->createTemporary( + E->getCommonExpr(), + getStorageType(Info.Ctx, E->getCommonExpr()), false, + CommonLV), Info, E->getCommonExpr()->getSourceExpr())) return false; @@ -8762,6 +9772,7 @@ public: bool VisitCXXNoexceptExpr(const CXXNoexceptExpr *E); bool VisitSizeOfPackExpr(const SizeOfPackExpr *E); bool VisitSourceLocExpr(const SourceLocExpr *E); + bool VisitConceptSpecializationExpr(const ConceptSpecializationExpr *E); // FIXME: Missing: array subscript of vector, member of vector }; @@ -8944,7 +9955,7 @@ EvaluateBuiltinClassifyType(QualType T, const LangOptions &LangOpts) { #define DEPENDENT_TYPE(ID, BASE) case Type::ID: #define NON_CANONICAL_TYPE(ID, BASE) case Type::ID: #define NON_CANONICAL_UNLESS_DEPENDENT_TYPE(ID, BASE) case Type::ID: -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" case Type::Auto: case Type::DeducedTemplateSpecialization: llvm_unreachable("unexpected non-canonical or dependent type"); @@ -9008,6 +10019,9 @@ EvaluateBuiltinClassifyType(QualType T, const LangOptions &LangOpts) { case BuiltinType::OCLClkEvent: case BuiltinType::OCLQueue: case BuiltinType::OCLReserveID: +#define SVE_TYPE(Name, Id, SingletonId) \ + case BuiltinType::Id: +#include "clang/Basic/AArch64SVEACLETypes.def" return GCCTypeClass::None; case BuiltinType::Dependent: @@ -9161,6 +10175,8 @@ static QualType getObjectType(APValue::LValueBase B) { return E->getType(); } else if (B.is()) { return B.getTypeInfoType(); + } else if (B.is()) { + return B.getDynamicAllocType(); } return QualType(); @@ -9499,14 +10515,11 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, // size of the referenced object. switch (Info.EvalMode) { case EvalInfo::EM_ConstantExpression: - case EvalInfo::EM_PotentialConstantExpression: case EvalInfo::EM_ConstantFold: - case EvalInfo::EM_EvaluateForOverflow: case EvalInfo::EM_IgnoreSideEffects: // Leave it to IR generation. return Error(E); case EvalInfo::EM_ConstantExpressionUnevaluated: - case EvalInfo::EM_PotentialConstantExpressionUnevaluated: // Reduce it to a constant now. return Success((Type & 2) ? 0 : -1, E); } @@ -10834,7 +11847,7 @@ bool IntExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) { Info.CCEDiag(E, diag::note_constexpr_pointer_subtraction_not_same_array); QualType Type = E->getLHS()->getType(); - QualType ElementType = Type->getAs()->getPointeeType(); + QualType ElementType = Type->castAs()->getPointeeType(); CharUnits ElementSize; if (!HandleSizeof(Info, E->getExprLoc(), ElementType, ElementSize)) @@ -11242,6 +12255,12 @@ bool IntExprEvaluator::VisitCXXNoexceptExpr(const CXXNoexceptExpr *E) { return Success(E->getValue(), E); } +bool IntExprEvaluator::VisitConceptSpecializationExpr( + const ConceptSpecializationExpr *E) { + return Success(E->isSatisfied(), E); +} + + bool FixedPointExprEvaluator::VisitUnaryOperator(const UnaryOperator *E) { switch (E->getOpcode()) { default: @@ -11731,9 +12750,9 @@ bool ComplexExprEvaluator::VisitCastExpr(const CastExpr *E) { if (!Visit(E->getSubExpr())) return false; - QualType To = E->getType()->getAs()->getElementType(); + QualType To = E->getType()->castAs()->getElementType(); QualType From - = E->getSubExpr()->getType()->getAs()->getElementType(); + = E->getSubExpr()->getType()->castAs()->getElementType(); return HandleFloatToFloatCast(Info, E, From, To, Result.FloatReal) && HandleFloatToFloatCast(Info, E, From, To, Result.FloatImag); @@ -11743,9 +12762,9 @@ bool ComplexExprEvaluator::VisitCastExpr(const CastExpr *E) { if (!Visit(E->getSubExpr())) return false; - QualType To = E->getType()->getAs()->getElementType(); + QualType To = E->getType()->castAs()->getElementType(); QualType From - = E->getSubExpr()->getType()->getAs()->getElementType(); + = E->getSubExpr()->getType()->castAs()->getElementType(); Result.makeComplexInt(); return HandleFloatToIntCast(Info, E, From, Result.FloatReal, To, Result.IntReal) && @@ -11767,9 +12786,9 @@ bool ComplexExprEvaluator::VisitCastExpr(const CastExpr *E) { if (!Visit(E->getSubExpr())) return false; - QualType To = E->getType()->getAs()->getElementType(); + QualType To = E->getType()->castAs()->getElementType(); QualType From - = E->getSubExpr()->getType()->getAs()->getElementType(); + = E->getSubExpr()->getType()->castAs()->getElementType(); Result.IntReal = HandleIntToIntCast(Info, E, To, From, Result.IntReal); Result.IntImag = HandleIntToIntCast(Info, E, To, From, Result.IntImag); @@ -12143,17 +13162,98 @@ public: bool VisitCallExpr(const CallExpr *E) { switch (E->getBuiltinCallee()) { - default: - return ExprEvaluatorBaseTy::VisitCallExpr(E); case Builtin::BI__assume: case Builtin::BI__builtin_assume: // The argument is not evaluated! return true; + + case Builtin::BI__builtin_operator_delete: + return HandleOperatorDeleteCall(Info, E); + + default: + break; } + + return ExprEvaluatorBaseTy::VisitCallExpr(E); } + + bool VisitCXXDeleteExpr(const CXXDeleteExpr *E); }; } // end anonymous namespace +bool VoidExprEvaluator::VisitCXXDeleteExpr(const CXXDeleteExpr *E) { + // We cannot speculatively evaluate a delete expression. + if (Info.SpeculativeEvaluationDepth) + return false; + + FunctionDecl *OperatorDelete = E->getOperatorDelete(); + if (!OperatorDelete->isReplaceableGlobalAllocationFunction()) { + Info.FFDiag(E, diag::note_constexpr_new_non_replaceable) + << isa(OperatorDelete) << OperatorDelete; + return false; + } + + const Expr *Arg = E->getArgument(); + + LValue Pointer; + if (!EvaluatePointer(Arg, Pointer, Info)) + return false; + if (Pointer.Designator.Invalid) + return false; + + // Deleting a null pointer has no effect. + if (Pointer.isNullPointer()) { + // This is the only case where we need to produce an extension warning: + // the only other way we can succeed is if we find a dynamic allocation, + // and we will have warned when we allocated it in that case. + if (!Info.getLangOpts().CPlusPlus2a) + Info.CCEDiag(E, diag::note_constexpr_new); + return true; + } + + Optional Alloc = CheckDeleteKind( + Info, E, Pointer, E->isArrayForm() ? DynAlloc::ArrayNew : DynAlloc::New); + if (!Alloc) + return false; + QualType AllocType = Pointer.Base.getDynamicAllocType(); + + // For the non-array case, the designator must be empty if the static type + // does not have a virtual destructor. + if (!E->isArrayForm() && Pointer.Designator.Entries.size() != 0 && + !hasVirtualDestructor(Arg->getType()->getPointeeType())) { + Info.FFDiag(E, diag::note_constexpr_delete_base_nonvirt_dtor) + << Arg->getType()->getPointeeType() << AllocType; + return false; + } + + // For a class type with a virtual destructor, the selected operator delete + // is the one looked up when building the destructor. + if (!E->isArrayForm() && !E->isGlobalDelete()) { + const FunctionDecl *VirtualDelete = getVirtualOperatorDelete(AllocType); + if (VirtualDelete && + !VirtualDelete->isReplaceableGlobalAllocationFunction()) { + Info.FFDiag(E, diag::note_constexpr_new_non_replaceable) + << isa(VirtualDelete) << VirtualDelete; + return false; + } + } + + if (!HandleDestruction(Info, E->getExprLoc(), Pointer.getLValueBase(), + (*Alloc)->Value, AllocType)) + return false; + + if (!Info.HeapAllocs.erase(Pointer.Base.dyn_cast())) { + // The element was already erased. This means the destructor call also + // deleted the object. + // FIXME: This probably results in undefined behavior before we get this + // far, and should be diagnosed elsewhere first. + Info.FFDiag(E, diag::note_constexpr_double_delete); + return false; + } + + return true; +} + static bool EvaluateVoid(const Expr *E, EvalInfo &Info) { assert(E->isRValue() && E->getType()->isVoidType()); return VoidExprEvaluator(Info).Visit(E); @@ -12203,13 +13303,14 @@ static bool Evaluate(APValue &Result, EvalInfo &Info, const Expr *E) { return true; } else if (T->isArrayType()) { LValue LV; - APValue &Value = createTemporary(E, false, LV, *Info.CurrentCall); + APValue &Value = + Info.CurrentCall->createTemporary(E, T, false, LV); if (!EvaluateArray(E, LV, Value, Info)) return false; Result = Value; } else if (T->isRecordType()) { LValue LV; - APValue &Value = createTemporary(E, false, LV, *Info.CurrentCall); + APValue &Value = Info.CurrentCall->createTemporary(E, T, false, LV); if (!EvaluateRecord(E, LV, Value, Info)) return false; Result = Value; @@ -12223,7 +13324,7 @@ static bool Evaluate(APValue &Result, EvalInfo &Info, const Expr *E) { QualType Unqual = T.getAtomicUnqualifiedType(); if (Unqual->isArrayType() || Unqual->isRecordType()) { LValue LV; - APValue &Value = createTemporary(E, false, LV, *Info.CurrentCall); + APValue &Value = Info.CurrentCall->createTemporary(E, Unqual, false, LV); if (!EvaluateAtomic(E, &LV, Value, Info)) return false; } else { @@ -12273,6 +13374,18 @@ static bool EvaluateInPlace(APValue &Result, EvalInfo &Info, const LValue &This, /// EvaluateAsRValue - Try to evaluate this expression, performing an implicit /// lvalue-to-rvalue cast if it is an lvalue. static bool EvaluateAsRValue(EvalInfo &Info, const Expr *E, APValue &Result) { + if (Info.EnableNewConstInterp) { + auto &InterpCtx = Info.Ctx.getInterpContext(); + switch (InterpCtx.evaluateAsRValue(Info, E, Result)) { + case interp::InterpResult::Success: + return true; + case interp::InterpResult::Fail: + return false; + case interp::InterpResult::Bail: + break; + } + } + if (E->getType().isNull()) return false; @@ -12290,7 +13403,8 @@ static bool EvaluateAsRValue(EvalInfo &Info, const Expr *E, APValue &Result) { } // Check this core constant expression is a constant expression. - return CheckConstantExpression(Info, E->getExprLoc(), E->getType(), Result); + return CheckConstantExpression(Info, E->getExprLoc(), E->getType(), Result) && + CheckMemoryLeaks(Info); } static bool FastEvaluateAsRValue(const Expr *Exp, Expr::EvalResult &Result, @@ -12439,10 +13553,12 @@ bool Expr::EvaluateAsLValue(EvalResult &Result, const ASTContext &Ctx, EvalInfo Info(Ctx, Result, EvalInfo::EM_ConstantFold); Info.InConstantContext = InConstantContext; LValue LV; - if (!EvaluateLValue(this, LV, Info) || Result.HasSideEffects || + CheckedTemporaries CheckedTemps; + if (!EvaluateLValue(this, LV, Info) || !Info.discardCleanups() || + Result.HasSideEffects || !CheckLValueConstantExpression(Info, getExprLoc(), Ctx.getLValueReferenceType(getType()), LV, - Expr::EvaluateForCodeGen)) + Expr::EvaluateForCodeGen, CheckedTemps)) return false; LV.moveInto(Result.Val); @@ -12458,11 +13574,15 @@ bool Expr::EvaluateAsConstantExpr(EvalResult &Result, ConstExprUsage Usage, EvalInfo Info(Ctx, Result, EM); Info.InConstantContext = true; - if (!::Evaluate(Result.Val, Info, this)) + if (!::Evaluate(Result.Val, Info, this) || Result.HasSideEffects) return false; - return CheckConstantExpression(Info, getExprLoc(), getType(), Result.Val, - Usage); + if (!Info.discardCleanups()) + llvm_unreachable("Unhandled cleanup; missing full expression marker?"); + + return CheckConstantExpression(Info, getExprLoc(), getStorageType(Ctx, this), + Result.Val, Usage) && + CheckMemoryLeaks(Info); } bool Expr::EvaluateAsInitializer(APValue &Value, const ASTContext &Ctx, @@ -12480,11 +13600,29 @@ bool Expr::EvaluateAsInitializer(APValue &Value, const ASTContext &Ctx, Expr::EvalStatus EStatus; EStatus.Diag = &Notes; - EvalInfo InitInfo(Ctx, EStatus, VD->isConstexpr() + EvalInfo Info(Ctx, EStatus, VD->isConstexpr() ? EvalInfo::EM_ConstantExpression : EvalInfo::EM_ConstantFold); - InitInfo.setEvaluatingDecl(VD, Value); - InitInfo.InConstantContext = true; + Info.setEvaluatingDecl(VD, Value); + Info.InConstantContext = true; + + SourceLocation DeclLoc = VD->getLocation(); + QualType DeclTy = VD->getType(); + + if (Info.EnableNewConstInterp) { + auto &InterpCtx = const_cast(Ctx).getInterpContext(); + switch (InterpCtx.evaluateAsInitializer(Info, VD, Value)) { + case interp::InterpResult::Fail: + // Bail out if an error was encountered. + return false; + case interp::InterpResult::Success: + // Evaluation succeeded and value was set. + return CheckConstantExpression(Info, DeclLoc, DeclTy, Value); + case interp::InterpResult::Bail: + // Evaluate the value again for the tree evaluator to use. + break; + } + } LValue LVal; LVal.set(VD); @@ -12494,20 +13632,62 @@ bool Expr::EvaluateAsInitializer(APValue &Value, const ASTContext &Ctx, // zero-initialized before any other initialization takes place. // This behavior is not present in C. if (Ctx.getLangOpts().CPlusPlus && !VD->hasLocalStorage() && - !VD->getType()->isReferenceType()) { - ImplicitValueInitExpr VIE(VD->getType()); - if (!EvaluateInPlace(Value, InitInfo, LVal, &VIE, + !DeclTy->isReferenceType()) { + ImplicitValueInitExpr VIE(DeclTy); + if (!EvaluateInPlace(Value, Info, LVal, &VIE, /*AllowNonLiteralTypes=*/true)) return false; } - if (!EvaluateInPlace(Value, InitInfo, LVal, this, + if (!EvaluateInPlace(Value, Info, LVal, this, /*AllowNonLiteralTypes=*/true) || EStatus.HasSideEffects) return false; - return CheckConstantExpression(InitInfo, VD->getLocation(), VD->getType(), - Value); + // At this point, any lifetime-extended temporaries are completely + // initialized. + Info.performLifetimeExtension(); + + if (!Info.discardCleanups()) + llvm_unreachable("Unhandled cleanup; missing full expression marker?"); + + return CheckConstantExpression(Info, DeclLoc, DeclTy, Value) && + CheckMemoryLeaks(Info); +} + +bool VarDecl::evaluateDestruction( + SmallVectorImpl &Notes) const { + assert(getEvaluatedValue() && !getEvaluatedValue()->isAbsent() && + "cannot evaluate destruction of non-constant-initialized variable"); + + Expr::EvalStatus EStatus; + EStatus.Diag = &Notes; + + // Make a copy of the value for the destructor to mutate. + APValue DestroyedValue = *getEvaluatedValue(); + + EvalInfo Info(getASTContext(), EStatus, EvalInfo::EM_ConstantExpression); + Info.setEvaluatingDecl(this, DestroyedValue, + EvalInfo::EvaluatingDeclKind::Dtor); + Info.InConstantContext = true; + + SourceLocation DeclLoc = getLocation(); + QualType DeclTy = getType(); + + LValue LVal; + LVal.set(this); + + // FIXME: Consider storing whether this variable has constant destruction in + // the EvaluatedStmt so that CodeGen can query it. + if (!HandleDestruction(Info, DeclLoc, LVal.Base, DestroyedValue, DeclTy) || + EStatus.HasSideEffects) + return false; + + if (!Info.discardCleanups()) + llvm_unreachable("Unhandled cleanup; missing full expression marker?"); + + ensureEvaluatedStmt()->HasConstantDestruction = true; + return true; } /// isEvaluatable - Call EvaluateAsRValue to see if this expression can be @@ -12546,8 +13726,9 @@ APSInt Expr::EvaluateKnownConstIntCheckOverflow( EvalResult EVResult; EVResult.Diag = Diag; - EvalInfo Info(Ctx, EVResult, EvalInfo::EM_EvaluateForOverflow); + EvalInfo Info(Ctx, EVResult, EvalInfo::EM_IgnoreSideEffects); Info.InConstantContext = true; + Info.CheckingForUndefinedBehavior = true; bool Result = ::EvaluateAsRValue(Info, this, EVResult.Val); (void)Result; @@ -12564,7 +13745,8 @@ void Expr::EvaluateForOverflow(const ASTContext &Ctx) const { bool IsConst; EvalResult EVResult; if (!FastEvaluateAsRValue(this, EVResult, Ctx, IsConst)) { - EvalInfo Info(Ctx, EVResult, EvalInfo::EM_EvaluateForOverflow); + EvalInfo Info(Ctx, EVResult, EvalInfo::EM_IgnoreSideEffects); + Info.CheckingForUndefinedBehavior = true; (void)::EvaluateAsRValue(Info, this, EVResult.Val); } } @@ -12752,6 +13934,7 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext &Ctx) { case Expr::CXXBoolLiteralExprClass: case Expr::CXXScalarValueInitExprClass: case Expr::TypeTraitExprClass: + case Expr::ConceptSpecializationExprClass: case Expr::ArrayTypeTraitExprClass: case Expr::ExpressionTraitExprClass: case Expr::CXXNoexceptExprClass: @@ -12766,6 +13949,9 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext &Ctx) { return CheckEvalInICE(E, Ctx); return ICEDiag(IK_NotICE, E->getBeginLoc()); } + case Expr::CXXRewrittenBinaryOperatorClass: + return CheckICE(cast(E)->getSemanticForm(), + Ctx); case Expr::DeclRefExprClass: { if (isa(cast(E)->getDecl())) return NoDiag(); @@ -13111,7 +14297,11 @@ bool Expr::isCXX11ConstantExpr(const ASTContext &Ctx, APValue *Result, EvalInfo Info(Ctx, Status, EvalInfo::EM_ConstantExpression); APValue Scratch; - bool IsConstExpr = ::EvaluateAsRValue(Info, this, Result ? *Result : Scratch); + bool IsConstExpr = + ::EvaluateAsRValue(Info, this, Result ? *Result : Scratch) && + // FIXME: We don't produce a diagnostic for this, but the callers that + // call us on arbitrary full-expressions should generally not care. + Info.discardCleanups() && !Status.HasSideEffects; if (!Diags.empty()) { IsConstExpr = false; @@ -13163,7 +14353,8 @@ bool Expr::EvaluateWithSubstitution(APValue &Value, ASTContext &Ctx, // Build fake call to Callee. CallStackFrame Frame(Info, Callee->getLocation(), Callee, ThisPtr, ArgValues.data()); - return Evaluate(Value, Info, this) && !Info.EvalStatus.HasSideEffects; + return Evaluate(Value, Info, this) && Info.discardCleanups() && + !Info.EvalStatus.HasSideEffects; } bool Expr::isPotentialConstantExpr(const FunctionDecl *FD, @@ -13178,9 +14369,21 @@ bool Expr::isPotentialConstantExpr(const FunctionDecl *FD, Expr::EvalStatus Status; Status.Diag = &Diags; - EvalInfo Info(FD->getASTContext(), Status, - EvalInfo::EM_PotentialConstantExpression); + EvalInfo Info(FD->getASTContext(), Status, EvalInfo::EM_ConstantExpression); Info.InConstantContext = true; + Info.CheckingPotentialConstantExpression = true; + + // The constexpr VM attempts to compile all methods to bytecode here. + if (Info.EnableNewConstInterp) { + auto &InterpCtx = Info.Ctx.getInterpContext(); + switch (InterpCtx.isPotentialConstantExpr(Info, FD)) { + case interp::InterpResult::Success: + case interp::InterpResult::Fail: + return Diags.empty(); + case interp::InterpResult::Bail: + break; + } + } const CXXMethodDecl *MD = dyn_cast(FD); const CXXRecordDecl *RD = MD ? MD->getParent()->getCanonicalDecl() : nullptr; @@ -13219,8 +14422,9 @@ bool Expr::isPotentialConstantExprUnevaluated(Expr *E, Status.Diag = &Diags; EvalInfo Info(FD->getASTContext(), Status, - EvalInfo::EM_PotentialConstantExpressionUnevaluated); + EvalInfo::EM_ConstantExpressionUnevaluated); Info.InConstantContext = true; + Info.CheckingPotentialConstantExpression = true; // Fabricate a call stack frame to give the arguments a plausible cover story. ArrayRef Args; diff --git a/lib/AST/ExternalASTMerger.cpp b/lib/AST/ExternalASTMerger.cpp index 61e657da7c919..f678c2dd3b59e 100644 --- a/lib/AST/ExternalASTMerger.cpp +++ b/lib/AST/ExternalASTMerger.cpp @@ -101,21 +101,103 @@ private: ExternalASTMerger &Parent; ASTImporter Reverse; const ExternalASTMerger::OriginMap &FromOrigins; - + /// @see ExternalASTMerger::ImporterSource::Temporary + bool TemporarySource; + /// Map of imported declarations back to the declarations they originated + /// from. + llvm::DenseMap ToOrigin; + /// @see ExternalASTMerger::ImporterSource::Merger + ExternalASTMerger *SourceMerger; llvm::raw_ostream &logs() { return Parent.logs(); } public: LazyASTImporter(ExternalASTMerger &_Parent, ASTContext &ToContext, - FileManager &ToFileManager, ASTContext &FromContext, - FileManager &FromFileManager, - const ExternalASTMerger::OriginMap &_FromOrigins) - : ASTImporter(ToContext, ToFileManager, FromContext, FromFileManager, - /*MinimalImport=*/true), - Parent(_Parent), Reverse(FromContext, FromFileManager, ToContext, - ToFileManager, /*MinimalImport=*/true), FromOrigins(_FromOrigins) {} + FileManager &ToFileManager, + const ExternalASTMerger::ImporterSource &S, + std::shared_ptr SharedState) + : ASTImporter(ToContext, ToFileManager, S.getASTContext(), + S.getFileManager(), + /*MinimalImport=*/true, SharedState), + Parent(_Parent), + Reverse(S.getASTContext(), S.getFileManager(), ToContext, ToFileManager, + /*MinimalImport=*/true), + FromOrigins(S.getOriginMap()), TemporarySource(S.isTemporary()), + SourceMerger(S.getMerger()) {} + + llvm::Expected ImportImpl(Decl *FromD) override { + if (!TemporarySource || !SourceMerger) + return ASTImporter::ImportImpl(FromD); + + // If we get here, then this source is importing from a temporary ASTContext + // that also has another ExternalASTMerger attached. It could be + // possible that the current ExternalASTMerger and the temporary ASTContext + // share a common ImporterSource, which means that the temporary + // AST could contain declarations that were imported from a source + // that this ExternalASTMerger can access directly. Instead of importing + // such declarations from the temporary ASTContext, they should instead + // be directly imported by this ExternalASTMerger from the original + // source. This way the ExternalASTMerger can safely do a minimal import + // without creating incomplete declarations originated from a temporary + // ASTContext. If we would try to complete such declarations later on, we + // would fail to do so as their temporary AST could be deleted (which means + // that the missing parts of the minimally imported declaration in that + // ASTContext were also deleted). + // + // The following code tracks back any declaration that needs to be + // imported from the temporary ASTContext to a persistent ASTContext. + // Then the ExternalASTMerger tries to import from the persistent + // ASTContext directly by using the associated ASTImporter. If that + // succeeds, this ASTImporter just maps the declarations imported by + // the other (persistent) ASTImporter to this (temporary) ASTImporter. + // The steps can be visualized like this: + // + // Target AST <--- 3. Indirect import --- Persistent AST + // ^ of persistent decl ^ + // | | + // 1. Current import 2. Tracking back to persistent decl + // 4. Map persistent decl | + // & pretend we imported. | + // | | + // Temporary AST -------------------------------' + + // First, ask the ExternalASTMerger of the source where the temporary + // declaration originated from. + Decl *Persistent = SourceMerger->FindOriginalDecl(FromD); + // FromD isn't from a persistent AST, so just do a normal import. + if (!Persistent) + return ASTImporter::ImportImpl(FromD); + // Now ask the current ExternalASTMerger to try import the persistent + // declaration into the target. + ASTContext &PersistentCtx = Persistent->getASTContext(); + ASTImporter &OtherImporter = Parent.ImporterForOrigin(PersistentCtx); + // Check that we never end up in the current Importer again. + assert((&PersistentCtx != &getFromContext()) && (&OtherImporter != this) && + "Delegated to same Importer?"); + auto DeclOrErr = OtherImporter.Import(Persistent); + // Errors when importing the persistent decl are treated as if we + // had errors with importing the temporary decl. + if (!DeclOrErr) + return DeclOrErr.takeError(); + Decl *D = *DeclOrErr; + // Tell the current ASTImporter that this has already been imported + // to prevent any further queries for the temporary decl. + MapImported(FromD, D); + return D; + } + + /// Implements the ASTImporter interface for tracking back a declaration + /// to its original declaration it came from. + Decl *GetOriginalDecl(Decl *To) override { + auto It = ToOrigin.find(To); + if (It != ToOrigin.end()) + return It->second; + return nullptr; + } /// Whenever a DeclContext is imported, ensure that ExternalASTSource's origin /// map is kept up to date. Also set the appropriate flags. void Imported(Decl *From, Decl *To) override { + ToOrigin[To] = From; + if (auto *ToDC = dyn_cast(To)) { const bool LoggingEnabled = Parent.LoggingEnabled(); if (LoggingEnabled) @@ -314,28 +396,40 @@ void ExternalASTMerger::RecordOriginImpl(const DeclContext *ToDC, DCOrigin Origi ExternalASTMerger::ExternalASTMerger(const ImporterTarget &Target, llvm::ArrayRef Sources) : LogStream(&llvm::nulls()), Target(Target) { + SharedState = std::make_shared( + *Target.AST.getTranslationUnitDecl()); AddSources(Sources); } +Decl *ExternalASTMerger::FindOriginalDecl(Decl *D) { + assert(&D->getASTContext() == &Target.AST); + for (const auto &I : Importers) + if (auto Result = I->GetOriginalDecl(D)) + return Result; + return nullptr; +} + void ExternalASTMerger::AddSources(llvm::ArrayRef Sources) { for (const ImporterSource &S : Sources) { - assert(&S.AST != &Target.AST); - Importers.push_back(llvm::make_unique( - *this, Target.AST, Target.FM, S.AST, S.FM, S.OM)); + assert(&S.getASTContext() != &Target.AST); + // Check that the associated merger actually imports into the source AST. + assert(!S.getMerger() || &S.getMerger()->Target.AST == &S.getASTContext()); + Importers.push_back(std::make_unique( + *this, Target.AST, Target.FM, S, SharedState)); } } void ExternalASTMerger::RemoveSources(llvm::ArrayRef Sources) { if (LoggingEnabled()) for (const ImporterSource &S : Sources) - logs() << "(ExternalASTMerger*)" << (void*)this - << " removing source (ASTContext*)" << (void*)&S.AST + logs() << "(ExternalASTMerger*)" << (void *)this + << " removing source (ASTContext*)" << (void *)&S.getASTContext() << "\n"; Importers.erase( std::remove_if(Importers.begin(), Importers.end(), [&Sources](std::unique_ptr &Importer) -> bool { for (const ImporterSource &S : Sources) { - if (&Importer->getFromContext() == &S.AST) + if (&Importer->getFromContext() == &S.getASTContext()) return true; } return false; @@ -345,7 +439,7 @@ void ExternalASTMerger::RemoveSources(llvm::ArrayRef Sources) { std::pair Origin = *OI; bool Erase = false; for (const ImporterSource &S : Sources) { - if (&S.AST == Origin.second.AST) { + if (&S.getASTContext() == Origin.second.AST) { Erase = true; break; } diff --git a/lib/AST/FormatString.cpp b/lib/AST/FormatString.cpp index 578d5bc567338..fcc0b3b11e259 100644 --- a/lib/AST/FormatString.cpp +++ b/lib/AST/FormatString.cpp @@ -359,6 +359,7 @@ ArgType::matchesType(ASTContext &C, QualType argTy) const { case BuiltinType::SChar: case BuiltinType::UChar: case BuiltinType::Char_U: + case BuiltinType::Bool: return Match; } return NoMatch; @@ -386,6 +387,9 @@ ArgType::matchesType(ASTContext &C, QualType argTy) const { case BuiltinType::SChar: case BuiltinType::Char_U: case BuiltinType::UChar: + case BuiltinType::Bool: + if (T == C.UnsignedShortTy || T == C.ShortTy) + return NoMatchTypeConfusion; return T == C.UnsignedCharTy || T == C.SignedCharTy ? Match : NoMatch; case BuiltinType::Short: diff --git a/lib/AST/FormatStringParsing.h b/lib/AST/FormatStringParsing.h index 9da829adcb494..764e5d46394d7 100644 --- a/lib/AST/FormatStringParsing.h +++ b/lib/AST/FormatStringParsing.h @@ -1,3 +1,16 @@ +//===----- FormatStringParsing.h - Format String Parsing --------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This provides some shared functions between printf and scanf format string +// parsing code. +// +//===----------------------------------------------------------------------===// + #ifndef LLVM_CLANG_LIB_ANALYSIS_FORMATSTRINGPARSING_H #define LLVM_CLANG_LIB_ANALYSIS_FORMATSTRINGPARSING_H diff --git a/lib/AST/InheritViz.cpp b/lib/AST/InheritViz.cpp index 4b3d5bee5631e..2ed0ce1c79c5b 100644 --- a/lib/AST/InheritViz.cpp +++ b/lib/AST/InheritViz.cpp @@ -90,8 +90,8 @@ void InheritanceHierarchyWriter::WriteNode(QualType Type, bool FromVirtual) { Out << " \"];\n"; // Display the base classes. - const CXXRecordDecl *Decl - = static_cast(Type->getAs()->getDecl()); + const auto *Decl = + static_cast(Type->castAs()->getDecl()); for (const auto &Base : Decl->bases()) { QualType CanonBaseType = Context.getCanonicalType(Base.getType()); diff --git a/lib/AST/Interp/Block.cpp b/lib/AST/Interp/Block.cpp new file mode 100644 index 0000000000000..5fc93eb39f4ea --- /dev/null +++ b/lib/AST/Interp/Block.cpp @@ -0,0 +1,87 @@ +//===--- Block.cpp - Allocated blocks for the interpreter -------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Defines the classes describing allocated blocks. +// +//===----------------------------------------------------------------------===// + +#include "Block.h" +#include "Pointer.h" + +using namespace clang; +using namespace clang::interp; + + + +void Block::addPointer(Pointer *P) { + if (IsStatic) + return; + if (Pointers) + Pointers->Prev = P; + P->Next = Pointers; + P->Prev = nullptr; + Pointers = P; +} + +void Block::removePointer(Pointer *P) { + if (IsStatic) + return; + if (Pointers == P) + Pointers = P->Next; + if (P->Prev) + P->Prev->Next = P->Next; + if (P->Next) + P->Next->Prev = P->Prev; +} + +void Block::cleanup() { + if (Pointers == nullptr && IsDead) + (reinterpret_cast(this + 1) - 1)->free(); +} + +void Block::movePointer(Pointer *From, Pointer *To) { + if (IsStatic) + return; + To->Prev = From->Prev; + if (To->Prev) + To->Prev->Next = To; + To->Next = From->Next; + if (To->Next) + To->Next->Prev = To; + if (Pointers == From) + Pointers = To; + + From->Prev = nullptr; + From->Next = nullptr; +} + +DeadBlock::DeadBlock(DeadBlock *&Root, Block *Blk) + : Root(Root), B(Blk->Desc, Blk->IsStatic, Blk->IsExtern, /*isDead=*/true) { + // Add the block to the chain of dead blocks. + if (Root) + Root->Prev = this; + + Next = Root; + Prev = nullptr; + Root = this; + + // Transfer pointers. + B.Pointers = Blk->Pointers; + for (Pointer *P = Blk->Pointers; P; P = P->Next) + P->Pointee = &B; +} + +void DeadBlock::free() { + if (Prev) + Prev->Next = Next; + if (Next) + Next->Prev = Prev; + if (Root == this) + Root = Next; + ::free(this); +} diff --git a/lib/AST/Interp/Block.h b/lib/AST/Interp/Block.h new file mode 100644 index 0000000000000..97fb9a3ca0961 --- /dev/null +++ b/lib/AST/Interp/Block.h @@ -0,0 +1,140 @@ +//===--- Block.h - Allocated blocks for the interpreter ---------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Defines the classes describing allocated blocks. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_AST_INTERP_BLOCK_H +#define LLVM_CLANG_AST_INTERP_BLOCK_H + +#include "Descriptor.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/Expr.h" +#include "clang/AST/ComparisonCategories.h" +#include "llvm/ADT/PointerUnion.h" +#include "llvm/Support/raw_ostream.h" + +namespace clang { +namespace interp { +class Block; +class DeadBlock; +class Context; +class InterpState; +class Pointer; +class Function; +enum PrimType : unsigned; + +/// A memory block, either on the stack or in the heap. +/// +/// The storage described by the block immediately follows it in memory. +class Block { +public: + // Creates a new block. + Block(const llvm::Optional &DeclID, Descriptor *Desc, + bool IsStatic = false, bool IsExtern = false) + : DeclID(DeclID), IsStatic(IsStatic), IsExtern(IsExtern), Desc(Desc) {} + + Block(Descriptor *Desc, bool IsStatic = false, bool IsExtern = false) + : DeclID((unsigned)-1), IsStatic(IsStatic), IsExtern(IsExtern), + Desc(Desc) {} + + /// Returns the block's descriptor. + Descriptor *getDescriptor() const { return Desc; } + /// Checks if the block has any live pointers. + bool hasPointers() const { return Pointers; } + /// Checks if the block is extern. + bool isExtern() const { return IsExtern; } + /// Checks if the block has static storage duration. + bool isStatic() const { return IsStatic; } + /// Checks if the block is temporary. + bool isTemporary() const { return Desc->IsTemporary; } + /// Returns the size of the block. + InterpSize getSize() const { return Desc->getAllocSize(); } + /// Returns the declaration ID. + llvm::Optional getDeclID() const { return DeclID; } + + /// Returns a pointer to the stored data. + char *data() { return reinterpret_cast(this + 1); } + + /// Returns a view over the data. + template + T &deref() { return *reinterpret_cast(data()); } + + /// Invokes the constructor. + void invokeCtor() { + std::memset(data(), 0, getSize()); + if (Desc->CtorFn) + Desc->CtorFn(this, data(), Desc->IsConst, Desc->IsMutable, + /*isActive=*/true, Desc); + } + +protected: + friend class Pointer; + friend class DeadBlock; + friend class InterpState; + + Block(Descriptor *Desc, bool IsExtern, bool IsStatic, bool IsDead) + : IsStatic(IsStatic), IsExtern(IsExtern), IsDead(true), Desc(Desc) {} + + // Deletes a dead block at the end of its lifetime. + void cleanup(); + + // Pointer chain management. + void addPointer(Pointer *P); + void removePointer(Pointer *P); + void movePointer(Pointer *From, Pointer *To); + + /// Start of the chain of pointers. + Pointer *Pointers = nullptr; + /// Unique identifier of the declaration. + llvm::Optional DeclID; + /// Flag indicating if the block has static storage duration. + bool IsStatic = false; + /// Flag indicating if the block is an extern. + bool IsExtern = false; + /// Flag indicating if the pointer is dead. + bool IsDead = false; + /// Pointer to the stack slot descriptor. + Descriptor *Desc; +}; + +/// Descriptor for a dead block. +/// +/// Dead blocks are chained in a double-linked list to deallocate them +/// whenever pointers become dead. +class DeadBlock { +public: + /// Copies the block. + DeadBlock(DeadBlock *&Root, Block *Blk); + + /// Returns a pointer to the stored data. + char *data() { return B.data(); } + +private: + friend class Block; + friend class InterpState; + + void free(); + + /// Root pointer of the list. + DeadBlock *&Root; + /// Previous block in the list. + DeadBlock *Prev; + /// Next block in the list. + DeadBlock *Next; + + /// Actual block storing data and tracking pointers. + Block B; +}; + +} // namespace interp +} // namespace clang + +#endif diff --git a/lib/AST/Interp/Boolean.h b/lib/AST/Interp/Boolean.h new file mode 100644 index 0000000000000..3e6c8b5da9f05 --- /dev/null +++ b/lib/AST/Interp/Boolean.h @@ -0,0 +1,148 @@ +//===--- Boolean.h - Wrapper for boolean types for the VM -------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_AST_INTERP_BOOLEAN_H +#define LLVM_CLANG_AST_INTERP_BOOLEAN_H + +#include +#include +#include "Integral.h" +#include "clang/AST/APValue.h" +#include "clang/AST/ComparisonCategories.h" +#include "llvm/ADT/APSInt.h" +#include "llvm/Support/MathExtras.h" +#include "llvm/Support/raw_ostream.h" + +namespace clang { +namespace interp { + +/// Wrapper around boolean types. +class Boolean { + private: + /// Underlying boolean. + bool V; + + /// Construct a wrapper from a boolean. + explicit Boolean(bool V) : V(V) {} + + public: + /// Zero-initializes a boolean. + Boolean() : V(false) {} + + bool operator<(Boolean RHS) const { return V < RHS.V; } + bool operator>(Boolean RHS) const { return V > RHS.V; } + bool operator<=(Boolean RHS) const { return V <= RHS.V; } + bool operator>=(Boolean RHS) const { return V >= RHS.V; } + bool operator==(Boolean RHS) const { return V == RHS.V; } + bool operator!=(Boolean RHS) const { return V != RHS.V; } + + bool operator>(unsigned RHS) const { return static_cast(V) > RHS; } + + Boolean operator-() const { return Boolean(V); } + Boolean operator~() const { return Boolean(true); } + + explicit operator unsigned() const { return V; } + explicit operator int64_t() const { return V; } + explicit operator uint64_t() const { return V; } + + APSInt toAPSInt() const { + return APSInt(APInt(1, static_cast(V), false), true); + } + APSInt toAPSInt(unsigned NumBits) const { + return APSInt(toAPSInt().zextOrTrunc(NumBits), true); + } + APValue toAPValue() const { return APValue(toAPSInt()); } + + Boolean toUnsigned() const { return *this; } + + constexpr static unsigned bitWidth() { return true; } + bool isZero() const { return !V; } + bool isMin() const { return isZero(); } + + constexpr static bool isMinusOne() { return false; } + + constexpr static bool isSigned() { return false; } + + constexpr static bool isNegative() { return false; } + constexpr static bool isPositive() { return !isNegative(); } + + ComparisonCategoryResult compare(const Boolean &RHS) const { + return Compare(V, RHS.V); + } + + unsigned countLeadingZeros() const { return V ? 0 : 1; } + + Boolean truncate(unsigned TruncBits) const { return *this; } + + void print(llvm::raw_ostream &OS) const { OS << (V ? "true" : "false"); } + + static Boolean min(unsigned NumBits) { return Boolean(false); } + static Boolean max(unsigned NumBits) { return Boolean(true); } + + template + static typename std::enable_if::value, Boolean>::type + from(T Value) { + return Boolean(Value != 0); + } + + template + static typename std::enable_if::type from( + Integral Value) { + return Boolean(!Value.isZero()); + } + + template + static Boolean from(Integral<0, SrcSign> Value) { + return Boolean(!Value.isZero()); + } + + static Boolean zero() { return from(false); } + + template + static Boolean from(T Value, unsigned NumBits) { + return Boolean(Value); + } + + static bool inRange(int64_t Value, unsigned NumBits) { + return Value == 0 || Value == 1; + } + + static bool increment(Boolean A, Boolean *R) { + *R = Boolean(true); + return false; + } + + static bool decrement(Boolean A, Boolean *R) { + llvm_unreachable("Cannot decrement booleans"); + } + + static bool add(Boolean A, Boolean B, unsigned OpBits, Boolean *R) { + *R = Boolean(A.V || B.V); + return false; + } + + static bool sub(Boolean A, Boolean B, unsigned OpBits, Boolean *R) { + *R = Boolean(A.V ^ B.V); + return false; + } + + static bool mul(Boolean A, Boolean B, unsigned OpBits, Boolean *R) { + *R = Boolean(A.V && B.V); + return false; + } +}; + +inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Boolean &B) { + B.print(OS); + return OS; +} + +} // namespace interp +} // namespace clang + +#endif diff --git a/lib/AST/Interp/ByteCodeEmitter.cpp b/lib/AST/Interp/ByteCodeEmitter.cpp new file mode 100644 index 0000000000000..7a4569820a1d1 --- /dev/null +++ b/lib/AST/Interp/ByteCodeEmitter.cpp @@ -0,0 +1,175 @@ +//===--- ByteCodeEmitter.cpp - Instruction emitter for the VM ---*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "ByteCodeEmitter.h" +#include "Context.h" +#include "Opcode.h" +#include "Program.h" +#include "clang/AST/DeclCXX.h" + +using namespace clang; +using namespace clang::interp; + +using APSInt = llvm::APSInt; +using Error = llvm::Error; + +Expected ByteCodeEmitter::compileFunc(const FunctionDecl *F) { + // Do not try to compile undefined functions. + if (!F->isDefined(F) || (!F->hasBody() && F->willHaveBody())) + return nullptr; + + // Set up argument indices. + unsigned ParamOffset = 0; + SmallVector ParamTypes; + llvm::DenseMap ParamDescriptors; + + // If the return is not a primitive, a pointer to the storage where the value + // is initialized in is passed as the first argument. + QualType Ty = F->getReturnType(); + if (!Ty->isVoidType() && !Ctx.classify(Ty)) { + ParamTypes.push_back(PT_Ptr); + ParamOffset += align(primSize(PT_Ptr)); + } + + // Assign descriptors to all parameters. + // Composite objects are lowered to pointers. + for (const ParmVarDecl *PD : F->parameters()) { + PrimType Ty; + if (llvm::Optional T = Ctx.classify(PD->getType())) { + Ty = *T; + } else { + Ty = PT_Ptr; + } + + Descriptor *Desc = P.createDescriptor(PD, Ty); + ParamDescriptors.insert({ParamOffset, {Ty, Desc}}); + Params.insert({PD, ParamOffset}); + ParamOffset += align(primSize(Ty)); + ParamTypes.push_back(Ty); + } + + // Create a handle over the emitted code. + Function *Func = P.createFunction(F, ParamOffset, std::move(ParamTypes), + std::move(ParamDescriptors)); + // Compile the function body. + if (!F->isConstexpr() || !visitFunc(F)) { + // Return a dummy function if compilation failed. + if (BailLocation) + return llvm::make_error(*BailLocation); + else + return Func; + } else { + // Create scopes from descriptors. + llvm::SmallVector Scopes; + for (auto &DS : Descriptors) { + Scopes.emplace_back(std::move(DS)); + } + + // Set the function's code. + Func->setCode(NextLocalOffset, std::move(Code), std::move(SrcMap), + std::move(Scopes)); + return Func; + } +} + +Scope::Local ByteCodeEmitter::createLocal(Descriptor *D) { + NextLocalOffset += sizeof(Block); + unsigned Location = NextLocalOffset; + NextLocalOffset += align(D->getAllocSize()); + return {Location, D}; +} + +void ByteCodeEmitter::emitLabel(LabelTy Label) { + const size_t Target = Code.size(); + LabelOffsets.insert({Label, Target}); + auto It = LabelRelocs.find(Label); + if (It != LabelRelocs.end()) { + for (unsigned Reloc : It->second) { + using namespace llvm::support; + + /// Rewrite the operand of all jumps to this label. + void *Location = Code.data() + Reloc - sizeof(int32_t); + const int32_t Offset = Target - static_cast(Reloc); + endian::write(Location, Offset); + } + LabelRelocs.erase(It); + } +} + +int32_t ByteCodeEmitter::getOffset(LabelTy Label) { + // Compute the PC offset which the jump is relative to. + const int64_t Position = Code.size() + sizeof(Opcode) + sizeof(int32_t); + + // If target is known, compute jump offset. + auto It = LabelOffsets.find(Label); + if (It != LabelOffsets.end()) { + return It->second - Position; + } + + // Otherwise, record relocation and return dummy offset. + LabelRelocs[Label].push_back(Position); + return 0ull; +} + +bool ByteCodeEmitter::bail(const SourceLocation &Loc) { + if (!BailLocation) + BailLocation = Loc; + return false; +} + +template +bool ByteCodeEmitter::emitOp(Opcode Op, const Tys &... Args, const SourceInfo &SI) { + bool Success = true; + + /// Helper to write bytecode and bail out if 32-bit offsets become invalid. + auto emit = [this, &Success](const char *Data, size_t Size) { + if (Code.size() + Size > std::numeric_limits::max()) { + Success = false; + return; + } + Code.insert(Code.end(), Data, Data + Size); + }; + + /// The opcode is followed by arguments. The source info is + /// attached to the address after the opcode. + emit(reinterpret_cast(&Op), sizeof(Opcode)); + if (SI) + SrcMap.emplace_back(Code.size(), SI); + + /// The initializer list forces the expression to be evaluated + /// for each argument in the variadic template, in order. + (void)std::initializer_list{ + (emit(reinterpret_cast(&Args), sizeof(Args)), 0)...}; + + return Success; +} + +bool ByteCodeEmitter::jumpTrue(const LabelTy &Label) { + return emitJt(getOffset(Label), SourceInfo{}); +} + +bool ByteCodeEmitter::jumpFalse(const LabelTy &Label) { + return emitJf(getOffset(Label), SourceInfo{}); +} + +bool ByteCodeEmitter::jump(const LabelTy &Label) { + return emitJmp(getOffset(Label), SourceInfo{}); +} + +bool ByteCodeEmitter::fallthrough(const LabelTy &Label) { + emitLabel(Label); + return true; +} + +//===----------------------------------------------------------------------===// +// Opcode emitters +//===----------------------------------------------------------------------===// + +#define GET_LINK_IMPL +#include "Opcodes.inc" +#undef GET_LINK_IMPL diff --git a/lib/AST/Interp/ByteCodeEmitter.h b/lib/AST/Interp/ByteCodeEmitter.h new file mode 100644 index 0000000000000..03452a350c960 --- /dev/null +++ b/lib/AST/Interp/ByteCodeEmitter.h @@ -0,0 +1,112 @@ +//===--- ByteCodeEmitter.h - Instruction emitter for the VM ---------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Defines the instruction emitters. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_AST_INTERP_LINKEMITTER_H +#define LLVM_CLANG_AST_INTERP_LINKEMITTER_H + +#include "ByteCodeGenError.h" +#include "Context.h" +#include "InterpStack.h" +#include "InterpState.h" +#include "PrimType.h" +#include "Program.h" +#include "Source.h" +#include "llvm/Support/Error.h" + +namespace clang { +namespace interp { +class Context; +class SourceInfo; +enum Opcode : uint32_t; + +/// An emitter which links the program to bytecode for later use. +class ByteCodeEmitter { +protected: + using LabelTy = uint32_t; + using AddrTy = uintptr_t; + using Local = Scope::Local; + +public: + /// Compiles the function into the module. + llvm::Expected compileFunc(const FunctionDecl *F); + +protected: + ByteCodeEmitter(Context &Ctx, Program &P) : Ctx(Ctx), P(P) {} + + virtual ~ByteCodeEmitter() {} + + /// Define a label. + void emitLabel(LabelTy Label); + /// Create a label. + LabelTy getLabel() { return ++NextLabel; } + + /// Methods implemented by the compiler. + virtual bool visitFunc(const FunctionDecl *E) = 0; + virtual bool visitExpr(const Expr *E) = 0; + virtual bool visitDecl(const VarDecl *E) = 0; + + /// Bails out if a given node cannot be compiled. + bool bail(const Stmt *S) { return bail(S->getBeginLoc()); } + bool bail(const Decl *D) { return bail(D->getBeginLoc()); } + bool bail(const SourceLocation &Loc); + + /// Emits jumps. + bool jumpTrue(const LabelTy &Label); + bool jumpFalse(const LabelTy &Label); + bool jump(const LabelTy &Label); + bool fallthrough(const LabelTy &Label); + + /// Callback for local registration. + Local createLocal(Descriptor *D); + + /// Parameter indices. + llvm::DenseMap Params; + /// Local descriptors. + llvm::SmallVector, 2> Descriptors; + +private: + /// Current compilation context. + Context &Ctx; + /// Program to link to. + Program &P; + /// Index of the next available label. + LabelTy NextLabel = 0; + /// Offset of the next local variable. + unsigned NextLocalOffset = 0; + /// Location of a failure. + llvm::Optional BailLocation; + /// Label information for linker. + llvm::DenseMap LabelOffsets; + /// Location of label relocations. + llvm::DenseMap> LabelRelocs; + /// Program code. + std::vector Code; + /// Opcode to expression mapping. + SourceMap SrcMap; + + /// Returns the offset for a jump or records a relocation. + int32_t getOffset(LabelTy Label); + + /// Emits an opcode. + template + bool emitOp(Opcode Op, const Tys &... Args, const SourceInfo &L); + +protected: +#define GET_LINK_PROTO +#include "Opcodes.inc" +#undef GET_LINK_PROTO +}; + +} // namespace interp +} // namespace clang + +#endif diff --git a/lib/AST/Interp/ByteCodeExprGen.cpp b/lib/AST/Interp/ByteCodeExprGen.cpp new file mode 100644 index 0000000000000..5c8cb4274260f --- /dev/null +++ b/lib/AST/Interp/ByteCodeExprGen.cpp @@ -0,0 +1,580 @@ +//===--- ByteCodeExprGen.cpp - Code generator for expressions ---*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "ByteCodeExprGen.h" +#include "ByteCodeEmitter.h" +#include "ByteCodeGenError.h" +#include "Context.h" +#include "Function.h" +#include "PrimType.h" +#include "Program.h" +#include "State.h" + +using namespace clang; +using namespace clang::interp; + +using APSInt = llvm::APSInt; +template using Expected = llvm::Expected; +template using Optional = llvm::Optional; + +namespace clang { +namespace interp { + +/// Scope used to handle temporaries in toplevel variable declarations. +template class DeclScope final : public LocalScope { +public: + DeclScope(ByteCodeExprGen *Ctx, const VarDecl *VD) + : LocalScope(Ctx), Scope(Ctx->P, VD) {} + + void addExtended(const Scope::Local &Local) override { + return this->addLocal(Local); + } + +private: + Program::DeclScope Scope; +}; + +/// Scope used to handle initialization methods. +template class OptionScope { +public: + using InitFnRef = typename ByteCodeExprGen::InitFnRef; + using ChainedInitFnRef = std::function; + + /// Root constructor, compiling or discarding primitives. + OptionScope(ByteCodeExprGen *Ctx, bool NewDiscardResult) + : Ctx(Ctx), OldDiscardResult(Ctx->DiscardResult), + OldInitFn(std::move(Ctx->InitFn)) { + Ctx->DiscardResult = NewDiscardResult; + Ctx->InitFn = llvm::Optional{}; + } + + /// Root constructor, setting up compilation state. + OptionScope(ByteCodeExprGen *Ctx, InitFnRef NewInitFn) + : Ctx(Ctx), OldDiscardResult(Ctx->DiscardResult), + OldInitFn(std::move(Ctx->InitFn)) { + Ctx->DiscardResult = true; + Ctx->InitFn = NewInitFn; + } + + /// Extends the chain of initialisation pointers. + OptionScope(ByteCodeExprGen *Ctx, ChainedInitFnRef NewInitFn) + : Ctx(Ctx), OldDiscardResult(Ctx->DiscardResult), + OldInitFn(std::move(Ctx->InitFn)) { + assert(OldInitFn && "missing initializer"); + Ctx->InitFn = [this, NewInitFn] { return NewInitFn(*OldInitFn); }; + } + + ~OptionScope() { + Ctx->DiscardResult = OldDiscardResult; + Ctx->InitFn = std::move(OldInitFn); + } + +private: + /// Parent context. + ByteCodeExprGen *Ctx; + /// Old discard flag to restore. + bool OldDiscardResult; + /// Old pointer emitter to restore. + llvm::Optional OldInitFn; +}; + +} // namespace interp +} // namespace clang + +template +bool ByteCodeExprGen::VisitCastExpr(const CastExpr *CE) { + auto *SubExpr = CE->getSubExpr(); + switch (CE->getCastKind()) { + + case CK_LValueToRValue: { + return dereference( + CE->getSubExpr(), DerefKind::Read, + [](PrimType) { + // Value loaded - nothing to do here. + return true; + }, + [this, CE](PrimType T) { + // Pointer on stack - dereference it. + if (!this->emitLoadPop(T, CE)) + return false; + return DiscardResult ? this->emitPop(T, CE) : true; + }); + } + + case CK_ArrayToPointerDecay: + case CK_AtomicToNonAtomic: + case CK_ConstructorConversion: + case CK_FunctionToPointerDecay: + case CK_NonAtomicToAtomic: + case CK_NoOp: + case CK_UserDefinedConversion: + return this->Visit(SubExpr); + + case CK_ToVoid: + return discard(SubExpr); + + default: { + // TODO: implement other casts. + return this->bail(CE); + } + } +} + +template +bool ByteCodeExprGen::VisitIntegerLiteral(const IntegerLiteral *LE) { + if (DiscardResult) + return true; + + auto Val = LE->getValue(); + QualType LitTy = LE->getType(); + if (Optional T = classify(LitTy)) + return emitConst(*T, getIntWidth(LitTy), LE->getValue(), LE); + return this->bail(LE); +} + +template +bool ByteCodeExprGen::VisitParenExpr(const ParenExpr *PE) { + return this->Visit(PE->getSubExpr()); +} + +template +bool ByteCodeExprGen::VisitBinaryOperator(const BinaryOperator *BO) { + const Expr *LHS = BO->getLHS(); + const Expr *RHS = BO->getRHS(); + + // Deal with operations which have composite or void types. + switch (BO->getOpcode()) { + case BO_Comma: + if (!discard(LHS)) + return false; + if (!this->Visit(RHS)) + return false; + return true; + default: + break; + } + + // Typecheck the args. + Optional LT = classify(LHS->getType()); + Optional RT = classify(RHS->getType()); + if (!LT || !RT) { + return this->bail(BO); + } + + if (Optional T = classify(BO->getType())) { + if (!visit(LHS)) + return false; + if (!visit(RHS)) + return false; + + auto Discard = [this, T, BO](bool Result) { + if (!Result) + return false; + return DiscardResult ? this->emitPop(*T, BO) : true; + }; + + switch (BO->getOpcode()) { + case BO_EQ: + return Discard(this->emitEQ(*LT, BO)); + case BO_NE: + return Discard(this->emitNE(*LT, BO)); + case BO_LT: + return Discard(this->emitLT(*LT, BO)); + case BO_LE: + return Discard(this->emitLE(*LT, BO)); + case BO_GT: + return Discard(this->emitGT(*LT, BO)); + case BO_GE: + return Discard(this->emitGE(*LT, BO)); + case BO_Sub: + return Discard(this->emitSub(*T, BO)); + case BO_Add: + return Discard(this->emitAdd(*T, BO)); + case BO_Mul: + return Discard(this->emitMul(*T, BO)); + default: + return this->bail(BO); + } + } + + return this->bail(BO); +} + +template +bool ByteCodeExprGen::discard(const Expr *E) { + OptionScope Scope(this, /*discardResult=*/true); + return this->Visit(E); +} + +template +bool ByteCodeExprGen::visit(const Expr *E) { + OptionScope Scope(this, /*discardResult=*/false); + return this->Visit(E); +} + +template +bool ByteCodeExprGen::visitBool(const Expr *E) { + if (Optional T = classify(E->getType())) { + return visit(E); + } else { + return this->bail(E); + } +} + +template +bool ByteCodeExprGen::visitZeroInitializer(PrimType T, const Expr *E) { + switch (T) { + case PT_Bool: + return this->emitZeroBool(E); + case PT_Sint8: + return this->emitZeroSint8(E); + case PT_Uint8: + return this->emitZeroUint8(E); + case PT_Sint16: + return this->emitZeroSint16(E); + case PT_Uint16: + return this->emitZeroUint16(E); + case PT_Sint32: + return this->emitZeroSint32(E); + case PT_Uint32: + return this->emitZeroUint32(E); + case PT_Sint64: + return this->emitZeroSint64(E); + case PT_Uint64: + return this->emitZeroUint64(E); + case PT_Ptr: + return this->emitNullPtr(E); + } + llvm_unreachable("unknown primitive type"); +} + +template +bool ByteCodeExprGen::dereference( + const Expr *LV, DerefKind AK, llvm::function_ref Direct, + llvm::function_ref Indirect) { + if (Optional T = classify(LV->getType())) { + if (!LV->refersToBitField()) { + // Only primitive, non bit-field types can be dereferenced directly. + if (auto *DE = dyn_cast(LV)) { + if (!DE->getDecl()->getType()->isReferenceType()) { + if (auto *PD = dyn_cast(DE->getDecl())) + return dereferenceParam(LV, *T, PD, AK, Direct, Indirect); + if (auto *VD = dyn_cast(DE->getDecl())) + return dereferenceVar(LV, *T, VD, AK, Direct, Indirect); + } + } + } + + if (!visit(LV)) + return false; + return Indirect(*T); + } + + return false; +} + +template +bool ByteCodeExprGen::dereferenceParam( + const Expr *LV, PrimType T, const ParmVarDecl *PD, DerefKind AK, + llvm::function_ref Direct, + llvm::function_ref Indirect) { + auto It = this->Params.find(PD); + if (It != this->Params.end()) { + unsigned Idx = It->second; + switch (AK) { + case DerefKind::Read: + return DiscardResult ? true : this->emitGetParam(T, Idx, LV); + + case DerefKind::Write: + if (!Direct(T)) + return false; + if (!this->emitSetParam(T, Idx, LV)) + return false; + return DiscardResult ? true : this->emitGetPtrParam(Idx, LV); + + case DerefKind::ReadWrite: + if (!this->emitGetParam(T, Idx, LV)) + return false; + if (!Direct(T)) + return false; + if (!this->emitSetParam(T, Idx, LV)) + return false; + return DiscardResult ? true : this->emitGetPtrParam(Idx, LV); + } + return true; + } + + // If the param is a pointer, we can dereference a dummy value. + if (!DiscardResult && T == PT_Ptr && AK == DerefKind::Read) { + if (auto Idx = P.getOrCreateDummy(PD)) + return this->emitGetPtrGlobal(*Idx, PD); + return false; + } + + // Value cannot be produced - try to emit pointer and do stuff with it. + return visit(LV) && Indirect(T); +} + +template +bool ByteCodeExprGen::dereferenceVar( + const Expr *LV, PrimType T, const VarDecl *VD, DerefKind AK, + llvm::function_ref Direct, + llvm::function_ref Indirect) { + auto It = Locals.find(VD); + if (It != Locals.end()) { + const auto &L = It->second; + switch (AK) { + case DerefKind::Read: + if (!this->emitGetLocal(T, L.Offset, LV)) + return false; + return DiscardResult ? this->emitPop(T, LV) : true; + + case DerefKind::Write: + if (!Direct(T)) + return false; + if (!this->emitSetLocal(T, L.Offset, LV)) + return false; + return DiscardResult ? true : this->emitGetPtrLocal(L.Offset, LV); + + case DerefKind::ReadWrite: + if (!this->emitGetLocal(T, L.Offset, LV)) + return false; + if (!Direct(T)) + return false; + if (!this->emitSetLocal(T, L.Offset, LV)) + return false; + return DiscardResult ? true : this->emitGetPtrLocal(L.Offset, LV); + } + } else if (auto Idx = getGlobalIdx(VD)) { + switch (AK) { + case DerefKind::Read: + if (!this->emitGetGlobal(T, *Idx, LV)) + return false; + return DiscardResult ? this->emitPop(T, LV) : true; + + case DerefKind::Write: + if (!Direct(T)) + return false; + if (!this->emitSetGlobal(T, *Idx, LV)) + return false; + return DiscardResult ? true : this->emitGetPtrGlobal(*Idx, LV); + + case DerefKind::ReadWrite: + if (!this->emitGetGlobal(T, *Idx, LV)) + return false; + if (!Direct(T)) + return false; + if (!this->emitSetGlobal(T, *Idx, LV)) + return false; + return DiscardResult ? true : this->emitGetPtrGlobal(*Idx, LV); + } + } + + // If the declaration is a constant value, emit it here even + // though the declaration was not evaluated in the current scope. + // The access mode can only be read in this case. + if (!DiscardResult && AK == DerefKind::Read) { + if (VD->hasLocalStorage() && VD->hasInit() && !VD->isConstexpr()) { + QualType VT = VD->getType(); + if (VT.isConstQualified() && VT->isFundamentalType()) + return this->Visit(VD->getInit()); + } + } + + // Value cannot be produced - try to emit pointer. + return visit(LV) && Indirect(T); +} + +template +bool ByteCodeExprGen::emitConst(PrimType T, unsigned NumBits, + const APInt &Value, const Expr *E) { + switch (T) { + case PT_Sint8: + return this->emitConstSint8(Value.getSExtValue(), E); + case PT_Uint8: + return this->emitConstUint8(Value.getZExtValue(), E); + case PT_Sint16: + return this->emitConstSint16(Value.getSExtValue(), E); + case PT_Uint16: + return this->emitConstUint16(Value.getZExtValue(), E); + case PT_Sint32: + return this->emitConstSint32(Value.getSExtValue(), E); + case PT_Uint32: + return this->emitConstUint32(Value.getZExtValue(), E); + case PT_Sint64: + return this->emitConstSint64(Value.getSExtValue(), E); + case PT_Uint64: + return this->emitConstUint64(Value.getZExtValue(), E); + case PT_Bool: + return this->emitConstBool(Value.getBoolValue(), E); + case PT_Ptr: + llvm_unreachable("Invalid integral type"); + break; + } + llvm_unreachable("unknown primitive type"); +} + +template +unsigned ByteCodeExprGen::allocateLocalPrimitive(DeclTy &&Src, + PrimType Ty, + bool IsConst, + bool IsExtended) { + Descriptor *D = P.createDescriptor(Src, Ty, IsConst, Src.is()); + Scope::Local Local = this->createLocal(D); + if (auto *VD = dyn_cast_or_null(Src.dyn_cast())) + Locals.insert({VD, Local}); + VarScope->add(Local, IsExtended); + return Local.Offset; +} + +template +llvm::Optional +ByteCodeExprGen::allocateLocal(DeclTy &&Src, bool IsExtended) { + QualType Ty; + + const ValueDecl *Key = nullptr; + bool IsTemporary = false; + if (auto *VD = dyn_cast_or_null(Src.dyn_cast())) { + Key = VD; + Ty = VD->getType(); + } + if (auto *E = Src.dyn_cast()) { + IsTemporary = true; + Ty = E->getType(); + } + + Descriptor *D = P.createDescriptor(Src, Ty.getTypePtr(), + Ty.isConstQualified(), IsTemporary); + if (!D) + return {}; + + Scope::Local Local = this->createLocal(D); + if (Key) + Locals.insert({Key, Local}); + VarScope->add(Local, IsExtended); + return Local.Offset; +} + +template +bool ByteCodeExprGen::visitInitializer( + const Expr *Init, InitFnRef InitFn) { + OptionScope Scope(this, InitFn); + return this->Visit(Init); +} + +template +bool ByteCodeExprGen::getPtrVarDecl(const VarDecl *VD, const Expr *E) { + // Generate a pointer to the local, loading refs. + if (Optional Idx = getGlobalIdx(VD)) { + if (VD->getType()->isReferenceType()) + return this->emitGetGlobalPtr(*Idx, E); + else + return this->emitGetPtrGlobal(*Idx, E); + } + return this->bail(VD); +} + +template +llvm::Optional +ByteCodeExprGen::getGlobalIdx(const VarDecl *VD) { + if (VD->isConstexpr()) { + // Constexpr decl - it must have already been defined. + return P.getGlobal(VD); + } + if (!VD->hasLocalStorage()) { + // Not constexpr, but a global var - can have pointer taken. + Program::DeclScope Scope(P, VD); + return P.getOrCreateGlobal(VD); + } + return {}; +} + +template +const RecordType *ByteCodeExprGen::getRecordTy(QualType Ty) { + if (auto *PT = dyn_cast(Ty)) + return PT->getPointeeType()->getAs(); + else + return Ty->getAs(); +} + +template +Record *ByteCodeExprGen::getRecord(QualType Ty) { + if (auto *RecordTy = getRecordTy(Ty)) { + return getRecord(RecordTy->getDecl()); + } + return nullptr; +} + +template +Record *ByteCodeExprGen::getRecord(const RecordDecl *RD) { + return P.getOrCreateRecord(RD); +} + +template +bool ByteCodeExprGen::visitExpr(const Expr *Exp) { + ExprScope RootScope(this); + if (!visit(Exp)) + return false; + + if (Optional T = classify(Exp)) + return this->emitRet(*T, Exp); + else + return this->emitRetValue(Exp); +} + +template +bool ByteCodeExprGen::visitDecl(const VarDecl *VD) { + const Expr *Init = VD->getInit(); + + if (Optional I = P.createGlobal(VD)) { + if (Optional T = classify(VD->getType())) { + { + // Primitive declarations - compute the value and set it. + DeclScope LocalScope(this, VD); + if (!visit(Init)) + return false; + } + + // If the declaration is global, save the value for later use. + if (!this->emitDup(*T, VD)) + return false; + if (!this->emitInitGlobal(*T, *I, VD)) + return false; + return this->emitRet(*T, VD); + } else { + { + // Composite declarations - allocate storage and initialize it. + DeclScope LocalScope(this, VD); + if (!visitGlobalInitializer(Init, *I)) + return false; + } + + // Return a pointer to the global. + if (!this->emitGetPtrGlobal(*I, VD)) + return false; + return this->emitRetValue(VD); + } + } + + return this->bail(VD); +} + +template +void ByteCodeExprGen::emitCleanup() { + for (VariableScope *C = VarScope; C; C = C->getParent()) + C->emitDestruction(); +} + +namespace clang { +namespace interp { + +template class ByteCodeExprGen; +template class ByteCodeExprGen; + +} // namespace interp +} // namespace clang diff --git a/lib/AST/Interp/ByteCodeExprGen.h b/lib/AST/Interp/ByteCodeExprGen.h new file mode 100644 index 0000000000000..1d0e34fc991f2 --- /dev/null +++ b/lib/AST/Interp/ByteCodeExprGen.h @@ -0,0 +1,331 @@ +//===--- ByteCodeExprGen.h - Code generator for expressions -----*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Defines the constexpr bytecode compiler. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_AST_INTERP_BYTECODEEXPRGEN_H +#define LLVM_CLANG_AST_INTERP_BYTECODEEXPRGEN_H + +#include "ByteCodeEmitter.h" +#include "EvalEmitter.h" +#include "Pointer.h" +#include "PrimType.h" +#include "Record.h" +#include "clang/AST/Decl.h" +#include "clang/AST/Expr.h" +#include "clang/AST/StmtVisitor.h" +#include "llvm/ADT/Optional.h" + +namespace clang { +class QualType; + +namespace interp { +class Function; +class State; + +template class LocalScope; +template class RecordScope; +template class VariableScope; +template class DeclScope; +template class OptionScope; + +/// Compilation context for expressions. +template +class ByteCodeExprGen : public ConstStmtVisitor, bool>, + public Emitter { +protected: + // Emitters for opcodes of various arities. + using NullaryFn = bool (ByteCodeExprGen::*)(const SourceInfo &); + using UnaryFn = bool (ByteCodeExprGen::*)(PrimType, const SourceInfo &); + using BinaryFn = bool (ByteCodeExprGen::*)(PrimType, PrimType, + const SourceInfo &); + + // Aliases for types defined in the emitter. + using LabelTy = typename Emitter::LabelTy; + using AddrTy = typename Emitter::AddrTy; + + // Reference to a function generating the pointer of an initialized object.s + using InitFnRef = std::function; + + /// Current compilation context. + Context &Ctx; + /// Program to link to. + Program &P; + +public: + /// Initializes the compiler and the backend emitter. + template + ByteCodeExprGen(Context &Ctx, Program &P, Tys &&... Args) + : Emitter(Ctx, P, Args...), Ctx(Ctx), P(P) {} + + // Expression visitors - result returned on stack. + bool VisitCastExpr(const CastExpr *E); + bool VisitIntegerLiteral(const IntegerLiteral *E); + bool VisitParenExpr(const ParenExpr *E); + bool VisitBinaryOperator(const BinaryOperator *E); + +protected: + bool visitExpr(const Expr *E) override; + bool visitDecl(const VarDecl *VD) override; + +protected: + /// Emits scope cleanup instructions. + void emitCleanup(); + + /// Returns a record type from a record or pointer type. + const RecordType *getRecordTy(QualType Ty); + + /// Returns a record from a record or pointer type. + Record *getRecord(QualType Ty); + Record *getRecord(const RecordDecl *RD); + + /// Returns the size int bits of an integer. + unsigned getIntWidth(QualType Ty) { + auto &ASTContext = Ctx.getASTContext(); + return ASTContext.getIntWidth(Ty); + } + + /// Returns the value of CHAR_BIT. + unsigned getCharBit() const { + auto &ASTContext = Ctx.getASTContext(); + return ASTContext.getTargetInfo().getCharWidth(); + } + + /// Classifies a type. + llvm::Optional classify(const Expr *E) const { + return E->isGLValue() ? PT_Ptr : classify(E->getType()); + } + llvm::Optional classify(QualType Ty) const { + return Ctx.classify(Ty); + } + + /// Checks if a pointer needs adjustment. + bool needsAdjust(QualType Ty) const { + return true; + } + + /// Classifies a known primitive type + PrimType classifyPrim(QualType Ty) const { + if (auto T = classify(Ty)) { + return *T; + } + llvm_unreachable("not a primitive type"); + } + + /// Evaluates an expression for side effects and discards the result. + bool discard(const Expr *E); + /// Evaluates an expression and places result on stack. + bool visit(const Expr *E); + /// Compiles an initializer for a local. + bool visitInitializer(const Expr *E, InitFnRef GenPtr); + + /// Visits an expression and converts it to a boolean. + bool visitBool(const Expr *E); + + /// Visits an initializer for a local. + bool visitLocalInitializer(const Expr *Init, unsigned I) { + return visitInitializer(Init, [this, I, Init] { + return this->emitGetPtrLocal(I, Init); + }); + } + + /// Visits an initializer for a global. + bool visitGlobalInitializer(const Expr *Init, unsigned I) { + return visitInitializer(Init, [this, I, Init] { + return this->emitGetPtrGlobal(I, Init); + }); + } + + /// Visits a delegated initializer. + bool visitThisInitializer(const Expr *I) { + return visitInitializer(I, [this, I] { return this->emitThis(I); }); + } + + /// Creates a local primitive value. + unsigned allocateLocalPrimitive(DeclTy &&Decl, PrimType Ty, bool IsMutable, + bool IsExtended = false); + + /// Allocates a space storing a local given its type. + llvm::Optional allocateLocal(DeclTy &&Decl, + bool IsExtended = false); + +private: + friend class VariableScope; + friend class LocalScope; + friend class RecordScope; + friend class DeclScope; + friend class OptionScope; + + /// Emits a zero initializer. + bool visitZeroInitializer(PrimType T, const Expr *E); + + enum class DerefKind { + /// Value is read and pushed to stack. + Read, + /// Direct method generates a value which is written. Returns pointer. + Write, + /// Direct method receives the value, pushes mutated value. Returns pointer. + ReadWrite, + }; + + /// Method to directly load a value. If the value can be fetched directly, + /// the direct handler is called. Otherwise, a pointer is left on the stack + /// and the indirect handler is expected to operate on that. + bool dereference(const Expr *LV, DerefKind AK, + llvm::function_ref Direct, + llvm::function_ref Indirect); + bool dereferenceParam(const Expr *LV, PrimType T, const ParmVarDecl *PD, + DerefKind AK, + llvm::function_ref Direct, + llvm::function_ref Indirect); + bool dereferenceVar(const Expr *LV, PrimType T, const VarDecl *PD, + DerefKind AK, llvm::function_ref Direct, + llvm::function_ref Indirect); + + /// Emits an APInt constant. + bool emitConst(PrimType T, unsigned NumBits, const llvm::APInt &Value, + const Expr *E); + + /// Emits an integer constant. + template bool emitConst(const Expr *E, T Value) { + QualType Ty = E->getType(); + unsigned NumBits = getIntWidth(Ty); + APInt WrappedValue(NumBits, Value, std::is_signed::value); + return emitConst(*Ctx.classify(Ty), NumBits, WrappedValue, E); + } + + /// Returns a pointer to a variable declaration. + bool getPtrVarDecl(const VarDecl *VD, const Expr *E); + + /// Returns the index of a global. + llvm::Optional getGlobalIdx(const VarDecl *VD); + + /// Emits the initialized pointer. + bool emitInitFn() { + assert(InitFn && "missing initializer"); + return (*InitFn)(); + } + +protected: + /// Variable to storage mapping. + llvm::DenseMap Locals; + + /// OpaqueValueExpr to location mapping. + llvm::DenseMap OpaqueExprs; + + /// Current scope. + VariableScope *VarScope = nullptr; + + /// Current argument index. + llvm::Optional ArrayIndex; + + /// Flag indicating if return value is to be discarded. + bool DiscardResult = false; + + /// Expression being initialized. + llvm::Optional InitFn = {}; +}; + +extern template class ByteCodeExprGen; +extern template class ByteCodeExprGen; + +/// Scope chain managing the variable lifetimes. +template class VariableScope { +public: + virtual ~VariableScope() { Ctx->VarScope = this->Parent; } + + void add(const Scope::Local &Local, bool IsExtended) { + if (IsExtended) + this->addExtended(Local); + else + this->addLocal(Local); + } + + virtual void addLocal(const Scope::Local &Local) { + if (this->Parent) + this->Parent->addLocal(Local); + } + + virtual void addExtended(const Scope::Local &Local) { + if (this->Parent) + this->Parent->addExtended(Local); + } + + virtual void emitDestruction() {} + + VariableScope *getParent() { return Parent; } + +protected: + VariableScope(ByteCodeExprGen *Ctx) + : Ctx(Ctx), Parent(Ctx->VarScope) { + Ctx->VarScope = this; + } + + /// ByteCodeExprGen instance. + ByteCodeExprGen *Ctx; + /// Link to the parent scope. + VariableScope *Parent; +}; + +/// Scope for local variables. +/// +/// When the scope is destroyed, instructions are emitted to tear down +/// all variables declared in this scope. +template class LocalScope : public VariableScope { +public: + LocalScope(ByteCodeExprGen *Ctx) : VariableScope(Ctx) {} + + ~LocalScope() override { this->emitDestruction(); } + + void addLocal(const Scope::Local &Local) override { + if (!Idx.hasValue()) { + Idx = this->Ctx->Descriptors.size(); + this->Ctx->Descriptors.emplace_back(); + } + + this->Ctx->Descriptors[*Idx].emplace_back(Local); + } + + void emitDestruction() override { + if (!Idx.hasValue()) + return; + this->Ctx->emitDestroy(*Idx, SourceInfo{}); + } + +protected: + /// Index of the scope in the chain. + Optional Idx; +}; + +/// Scope for storage declared in a compound statement. +template class BlockScope final : public LocalScope { +public: + BlockScope(ByteCodeExprGen *Ctx) : LocalScope(Ctx) {} + + void addExtended(const Scope::Local &Local) override { + llvm_unreachable("Cannot create temporaries in full scopes"); + } +}; + +/// Expression scope which tracks potentially lifetime extended +/// temporaries which are hoisted to the parent scope on exit. +template class ExprScope final : public LocalScope { +public: + ExprScope(ByteCodeExprGen *Ctx) : LocalScope(Ctx) {} + + void addExtended(const Scope::Local &Local) override { + this->Parent->addLocal(Local); + } +}; + +} // namespace interp +} // namespace clang + +#endif diff --git a/lib/AST/Interp/ByteCodeGenError.cpp b/lib/AST/Interp/ByteCodeGenError.cpp new file mode 100644 index 0000000000000..5fd3d77c38420 --- /dev/null +++ b/lib/AST/Interp/ByteCodeGenError.cpp @@ -0,0 +1,14 @@ +//===--- ByteCodeGenError.h - Byte code generation error --------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "ByteCodeGenError.h" + +using namespace clang; +using namespace clang::interp; + +char ByteCodeGenError::ID; diff --git a/lib/AST/Interp/ByteCodeGenError.h b/lib/AST/Interp/ByteCodeGenError.h new file mode 100644 index 0000000000000..a4fa4917705dd --- /dev/null +++ b/lib/AST/Interp/ByteCodeGenError.h @@ -0,0 +1,46 @@ +//===--- ByteCodeGenError.h - Byte code generation error ----------*- C -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_AST_INTERP_BYTECODEGENERROR_H +#define LLVM_CLANG_AST_INTERP_BYTECODEGENERROR_H + +#include "clang/AST/Decl.h" +#include "clang/AST/Stmt.h" +#include "clang/Basic/SourceLocation.h" +#include "llvm/Support/Error.h" + +namespace clang { +namespace interp { + +/// Error thrown by the compiler. +struct ByteCodeGenError : public llvm::ErrorInfo { +public: + ByteCodeGenError(SourceLocation Loc) : Loc(Loc) {} + ByteCodeGenError(const Stmt *S) : ByteCodeGenError(S->getBeginLoc()) {} + ByteCodeGenError(const Decl *D) : ByteCodeGenError(D->getBeginLoc()) {} + + void log(raw_ostream &OS) const override { OS << "unimplemented feature"; } + + const SourceLocation &getLoc() const { return Loc; } + + static char ID; + +private: + // Start of the item where the error occurred. + SourceLocation Loc; + + // Users are not expected to use error_code. + std::error_code convertToErrorCode() const override { + return llvm::inconvertibleErrorCode(); + } +}; + +} // namespace interp +} // namespace clang + +#endif diff --git a/lib/AST/Interp/ByteCodeStmtGen.cpp b/lib/AST/Interp/ByteCodeStmtGen.cpp new file mode 100644 index 0000000000000..c71301598bde6 --- /dev/null +++ b/lib/AST/Interp/ByteCodeStmtGen.cpp @@ -0,0 +1,265 @@ +//===--- ByteCodeStmtGen.cpp - Code generator for expressions ---*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "ByteCodeStmtGen.h" +#include "ByteCodeEmitter.h" +#include "ByteCodeGenError.h" +#include "Context.h" +#include "Function.h" +#include "PrimType.h" +#include "Program.h" +#include "State.h" + +using namespace clang; +using namespace clang::interp; + +template using Expected = llvm::Expected; +template using Optional = llvm::Optional; + +namespace clang { +namespace interp { + +/// Scope managing label targets. +template class LabelScope { +public: + virtual ~LabelScope() { } + +protected: + LabelScope(ByteCodeStmtGen *Ctx) : Ctx(Ctx) {} + /// ByteCodeStmtGen instance. + ByteCodeStmtGen *Ctx; +}; + +/// Sets the context for break/continue statements. +template class LoopScope final : public LabelScope { +public: + using LabelTy = typename ByteCodeStmtGen::LabelTy; + using OptLabelTy = typename ByteCodeStmtGen::OptLabelTy; + + LoopScope(ByteCodeStmtGen *Ctx, LabelTy BreakLabel, + LabelTy ContinueLabel) + : LabelScope(Ctx), OldBreakLabel(Ctx->BreakLabel), + OldContinueLabel(Ctx->ContinueLabel) { + this->Ctx->BreakLabel = BreakLabel; + this->Ctx->ContinueLabel = ContinueLabel; + } + + ~LoopScope() { + this->Ctx->BreakLabel = OldBreakLabel; + this->Ctx->ContinueLabel = OldContinueLabel; + } + +private: + OptLabelTy OldBreakLabel; + OptLabelTy OldContinueLabel; +}; + +// Sets the context for a switch scope, mapping labels. +template class SwitchScope final : public LabelScope { +public: + using LabelTy = typename ByteCodeStmtGen::LabelTy; + using OptLabelTy = typename ByteCodeStmtGen::OptLabelTy; + using CaseMap = typename ByteCodeStmtGen::CaseMap; + + SwitchScope(ByteCodeStmtGen *Ctx, CaseMap &&CaseLabels, + LabelTy BreakLabel, OptLabelTy DefaultLabel) + : LabelScope(Ctx), OldBreakLabel(Ctx->BreakLabel), + OldDefaultLabel(this->Ctx->DefaultLabel), + OldCaseLabels(std::move(this->Ctx->CaseLabels)) { + this->Ctx->BreakLabel = BreakLabel; + this->Ctx->DefaultLabel = DefaultLabel; + this->Ctx->CaseLabels = std::move(CaseLabels); + } + + ~SwitchScope() { + this->Ctx->BreakLabel = OldBreakLabel; + this->Ctx->DefaultLabel = OldDefaultLabel; + this->Ctx->CaseLabels = std::move(OldCaseLabels); + } + +private: + OptLabelTy OldBreakLabel; + OptLabelTy OldDefaultLabel; + CaseMap OldCaseLabels; +}; + +} // namespace interp +} // namespace clang + +template +bool ByteCodeStmtGen::visitFunc(const FunctionDecl *F) { + // Classify the return type. + ReturnType = this->classify(F->getReturnType()); + + // Set up fields and context if a constructor. + if (auto *MD = dyn_cast(F)) + return this->bail(MD); + + if (auto *Body = F->getBody()) + if (!visitStmt(Body)) + return false; + + // Emit a guard return to protect against a code path missing one. + if (F->getReturnType()->isVoidType()) + return this->emitRetVoid(SourceInfo{}); + else + return this->emitNoRet(SourceInfo{}); +} + +template +bool ByteCodeStmtGen::visitStmt(const Stmt *S) { + switch (S->getStmtClass()) { + case Stmt::CompoundStmtClass: + return visitCompoundStmt(cast(S)); + case Stmt::DeclStmtClass: + return visitDeclStmt(cast(S)); + case Stmt::ReturnStmtClass: + return visitReturnStmt(cast(S)); + case Stmt::IfStmtClass: + return visitIfStmt(cast(S)); + case Stmt::NullStmtClass: + return true; + default: { + if (auto *Exp = dyn_cast(S)) + return this->discard(Exp); + return this->bail(S); + } + } +} + +template +bool ByteCodeStmtGen::visitCompoundStmt( + const CompoundStmt *CompoundStmt) { + BlockScope Scope(this); + for (auto *InnerStmt : CompoundStmt->body()) + if (!visitStmt(InnerStmt)) + return false; + return true; +} + +template +bool ByteCodeStmtGen::visitDeclStmt(const DeclStmt *DS) { + for (auto *D : DS->decls()) { + // Variable declarator. + if (auto *VD = dyn_cast(D)) { + if (!visitVarDecl(VD)) + return false; + continue; + } + + // Decomposition declarator. + if (auto *DD = dyn_cast(D)) { + return this->bail(DD); + } + } + + return true; +} + +template +bool ByteCodeStmtGen::visitReturnStmt(const ReturnStmt *RS) { + if (const Expr *RE = RS->getRetValue()) { + ExprScope RetScope(this); + if (ReturnType) { + // Primitive types are simply returned. + if (!this->visit(RE)) + return false; + this->emitCleanup(); + return this->emitRet(*ReturnType, RS); + } else { + // RVO - construct the value in the return location. + auto ReturnLocation = [this, RE] { return this->emitGetParamPtr(0, RE); }; + if (!this->visitInitializer(RE, ReturnLocation)) + return false; + this->emitCleanup(); + return this->emitRetVoid(RS); + } + } else { + this->emitCleanup(); + if (!this->emitRetVoid(RS)) + return false; + return true; + } +} + +template +bool ByteCodeStmtGen::visitIfStmt(const IfStmt *IS) { + BlockScope IfScope(this); + if (auto *CondInit = IS->getInit()) + if (!visitStmt(IS->getInit())) + return false; + + if (const DeclStmt *CondDecl = IS->getConditionVariableDeclStmt()) + if (!visitDeclStmt(CondDecl)) + return false; + + if (!this->visitBool(IS->getCond())) + return false; + + if (const Stmt *Else = IS->getElse()) { + LabelTy LabelElse = this->getLabel(); + LabelTy LabelEnd = this->getLabel(); + if (!this->jumpFalse(LabelElse)) + return false; + if (!visitStmt(IS->getThen())) + return false; + if (!this->jump(LabelEnd)) + return false; + this->emitLabel(LabelElse); + if (!visitStmt(Else)) + return false; + this->emitLabel(LabelEnd); + } else { + LabelTy LabelEnd = this->getLabel(); + if (!this->jumpFalse(LabelEnd)) + return false; + if (!visitStmt(IS->getThen())) + return false; + this->emitLabel(LabelEnd); + } + + return true; +} + +template +bool ByteCodeStmtGen::visitVarDecl(const VarDecl *VD) { + auto DT = VD->getType(); + + if (!VD->hasLocalStorage()) { + // No code generation required. + return true; + } + + // Integers, pointers, primitives. + if (Optional T = this->classify(DT)) { + auto Off = this->allocateLocalPrimitive(VD, *T, DT.isConstQualified()); + // Compile the initialiser in its own scope. + { + ExprScope Scope(this); + if (!this->visit(VD->getInit())) + return false; + } + // Set the value. + return this->emitSetLocal(*T, Off, VD); + } else { + // Composite types - allocate storage and initialize it. + if (auto Off = this->allocateLocal(VD)) { + return this->visitLocalInitializer(VD->getInit(), *Off); + } else { + return this->bail(VD); + } + } +} + +namespace clang { +namespace interp { + +template class ByteCodeStmtGen; + +} // namespace interp +} // namespace clang diff --git a/lib/AST/Interp/ByteCodeStmtGen.h b/lib/AST/Interp/ByteCodeStmtGen.h new file mode 100644 index 0000000000000..d9c0b64ed4b82 --- /dev/null +++ b/lib/AST/Interp/ByteCodeStmtGen.h @@ -0,0 +1,89 @@ +//===--- ByteCodeStmtGen.h - Code generator for expressions -----*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Defines the constexpr bytecode compiler. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_AST_INTERP_BYTECODESTMTGEN_H +#define LLVM_CLANG_AST_INTERP_BYTECODESTMTGEN_H + +#include "ByteCodeEmitter.h" +#include "ByteCodeExprGen.h" +#include "EvalEmitter.h" +#include "Pointer.h" +#include "PrimType.h" +#include "Record.h" +#include "clang/AST/Decl.h" +#include "clang/AST/Expr.h" +#include "clang/AST/StmtVisitor.h" +#include "llvm/ADT/Optional.h" + +namespace clang { +class QualType; + +namespace interp { +class Function; +class State; + +template class LoopScope; +template class SwitchScope; +template class LabelScope; + +/// Compilation context for statements. +template +class ByteCodeStmtGen : public ByteCodeExprGen { + using LabelTy = typename Emitter::LabelTy; + using AddrTy = typename Emitter::AddrTy; + using OptLabelTy = llvm::Optional; + using CaseMap = llvm::DenseMap; + +public: + template + ByteCodeStmtGen(Tys&&... Args) + : ByteCodeExprGen(std::forward(Args)...) {} + +protected: + bool visitFunc(const FunctionDecl *F) override; + +private: + friend class LabelScope; + friend class LoopScope; + friend class SwitchScope; + + // Statement visitors. + bool visitStmt(const Stmt *S); + bool visitCompoundStmt(const CompoundStmt *S); + bool visitDeclStmt(const DeclStmt *DS); + bool visitReturnStmt(const ReturnStmt *RS); + bool visitIfStmt(const IfStmt *IS); + + /// Compiles a variable declaration. + bool visitVarDecl(const VarDecl *VD); + +private: + /// Type of the expression returned by the function. + llvm::Optional ReturnType; + + /// Switch case mapping. + CaseMap CaseLabels; + + /// Point to break to. + OptLabelTy BreakLabel; + /// Point to continue to. + OptLabelTy ContinueLabel; + /// Default case label. + OptLabelTy DefaultLabel; +}; + +extern template class ByteCodeExprGen; + +} // namespace interp +} // namespace clang + +#endif diff --git a/lib/AST/Interp/Context.cpp b/lib/AST/Interp/Context.cpp new file mode 100644 index 0000000000000..4f8f7b96e7c32 --- /dev/null +++ b/lib/AST/Interp/Context.cpp @@ -0,0 +1,148 @@ +//===--- Context.cpp - Context for the constexpr VM -------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "Context.h" +#include "ByteCodeEmitter.h" +#include "ByteCodeExprGen.h" +#include "ByteCodeStmtGen.h" +#include "EvalEmitter.h" +#include "Interp.h" +#include "InterpFrame.h" +#include "InterpStack.h" +#include "PrimType.h" +#include "Program.h" +#include "clang/AST/Expr.h" + +using namespace clang; +using namespace clang::interp; + +Context::Context(ASTContext &Ctx) + : Ctx(Ctx), ForceInterp(getLangOpts().ForceNewConstInterp), + P(new Program(*this)) {} + +Context::~Context() {} + +InterpResult Context::isPotentialConstantExpr(State &Parent, + const FunctionDecl *FD) { + Function *Func = P->getFunction(FD); + if (!Func) { + if (auto R = ByteCodeStmtGen(*this, *P).compileFunc(FD)) { + Func = *R; + } else if (ForceInterp) { + handleAllErrors(R.takeError(), [&Parent](ByteCodeGenError &Err) { + Parent.FFDiag(Err.getLoc(), diag::err_experimental_clang_interp_failed); + }); + return InterpResult::Fail; + } else { + consumeError(R.takeError()); + return InterpResult::Bail; + } + } + + if (!Func->isConstexpr()) + return InterpResult::Fail; + + APValue Dummy; + return Run(Parent, Func, Dummy); +} + +InterpResult Context::evaluateAsRValue(State &Parent, const Expr *E, + APValue &Result) { + ByteCodeExprGen C(*this, *P, Parent, Stk, Result); + return Check(Parent, C.interpretExpr(E)); +} + +InterpResult Context::evaluateAsInitializer(State &Parent, const VarDecl *VD, + APValue &Result) { + ByteCodeExprGen C(*this, *P, Parent, Stk, Result); + return Check(Parent, C.interpretDecl(VD)); +} + +const LangOptions &Context::getLangOpts() const { return Ctx.getLangOpts(); } + +llvm::Optional Context::classify(QualType T) { + if (T->isReferenceType() || T->isPointerType()) { + return PT_Ptr; + } + + if (T->isBooleanType()) + return PT_Bool; + + if (T->isSignedIntegerOrEnumerationType()) { + switch (Ctx.getIntWidth(T)) { + case 64: + return PT_Sint64; + case 32: + return PT_Sint32; + case 16: + return PT_Sint16; + case 8: + return PT_Sint8; + default: + return {}; + } + } + + if (T->isUnsignedIntegerOrEnumerationType()) { + switch (Ctx.getIntWidth(T)) { + case 64: + return PT_Uint64; + case 32: + return PT_Uint32; + case 16: + return PT_Uint16; + case 8: + return PT_Uint8; + default: + return {}; + } + } + + if (T->isNullPtrType()) + return PT_Ptr; + + if (auto *AT = dyn_cast(T)) + return classify(AT->getValueType()); + + return {}; +} + +unsigned Context::getCharBit() const { + return Ctx.getTargetInfo().getCharWidth(); +} + +InterpResult Context::Run(State &Parent, Function *Func, APValue &Result) { + InterpResult Flag; + { + InterpState State(Parent, *P, Stk, *this); + State.Current = new InterpFrame(State, Func, nullptr, {}, {}); + if (Interpret(State, Result)) { + Flag = InterpResult::Success; + } else { + Flag = InterpResult::Fail; + } + } + + if (Flag != InterpResult::Success) + Stk.clear(); + return Flag; +} + +InterpResult Context::Check(State &Parent, llvm::Expected &&R) { + if (R) { + return *R ? InterpResult::Success : InterpResult::Fail; + } else if (ForceInterp) { + handleAllErrors(R.takeError(), [&Parent](ByteCodeGenError &Err) { + Parent.FFDiag(Err.getLoc(), diag::err_experimental_clang_interp_failed); + }); + return InterpResult::Fail; + } else { + consumeError(R.takeError()); + return InterpResult::Bail; + } +} diff --git a/lib/AST/Interp/Context.h b/lib/AST/Interp/Context.h new file mode 100644 index 0000000000000..96368b6e5f02f --- /dev/null +++ b/lib/AST/Interp/Context.h @@ -0,0 +1,100 @@ +//===--- Context.h - Context for the constexpr VM ---------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Defines the constexpr execution context. +// +// The execution context manages cached bytecode and the global context. +// It invokes the compiler and interpreter, propagating errors. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_AST_INTERP_CONTEXT_H +#define LLVM_CLANG_AST_INTERP_CONTEXT_H + +#include "Context.h" +#include "InterpStack.h" +#include "clang/AST/APValue.h" +#include "llvm/ADT/PointerIntPair.h" + +namespace clang { +class ASTContext; +class LangOptions; +class Stmt; +class FunctionDecl; +class VarDecl; + +namespace interp { +class Function; +class Program; +class State; +enum PrimType : unsigned; + +/// Wrapper around interpreter termination results. +enum class InterpResult { + /// Interpreter successfully computed a value. + Success, + /// Interpreter encountered an error and quit. + Fail, + /// Interpreter encountered an unimplemented feature, AST fallback. + Bail, +}; + +/// Holds all information required to evaluate constexpr code in a module. +class Context { +public: + /// Initialises the constexpr VM. + Context(ASTContext &Ctx); + + /// Cleans up the constexpr VM. + ~Context(); + + /// Checks if a function is a potential constant expression. + InterpResult isPotentialConstantExpr(State &Parent, + const FunctionDecl *FnDecl); + + /// Evaluates a toplevel expression as an rvalue. + InterpResult evaluateAsRValue(State &Parent, const Expr *E, APValue &Result); + + /// Evaluates a toplevel initializer. + InterpResult evaluateAsInitializer(State &Parent, const VarDecl *VD, + APValue &Result); + + /// Returns the AST context. + ASTContext &getASTContext() const { return Ctx; } + /// Returns the language options. + const LangOptions &getLangOpts() const; + /// Returns the interpreter stack. + InterpStack &getStack() { return Stk; } + /// Returns CHAR_BIT. + unsigned getCharBit() const; + + /// Classifies an expression. + llvm::Optional classify(QualType T); + +private: + /// Runs a function. + InterpResult Run(State &Parent, Function *Func, APValue &Result); + + /// Checks a result fromt the interpreter. + InterpResult Check(State &Parent, llvm::Expected &&R); + +private: + /// Current compilation context. + ASTContext &Ctx; + /// Flag to indicate if the use of the interpreter is mandatory. + bool ForceInterp; + /// Interpreter stack, shared across invocations. + InterpStack Stk; + /// Constexpr program. + std::unique_ptr P; +}; + +} // namespace interp +} // namespace clang + +#endif diff --git a/lib/AST/Interp/Descriptor.cpp b/lib/AST/Interp/Descriptor.cpp new file mode 100644 index 0000000000000..5c1a8a9cf3066 --- /dev/null +++ b/lib/AST/Interp/Descriptor.cpp @@ -0,0 +1,292 @@ +//===--- Descriptor.cpp - Types for the constexpr VM ------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "Descriptor.h" +#include "Pointer.h" +#include "PrimType.h" +#include "Record.h" + +using namespace clang; +using namespace clang::interp; + +template +static void ctorTy(Block *, char *Ptr, bool, bool, bool, Descriptor *) { + new (Ptr) T(); +} + +template static void dtorTy(Block *, char *Ptr, Descriptor *) { + reinterpret_cast(Ptr)->~T(); +} + +template +static void moveTy(Block *, char *Src, char *Dst, Descriptor *) { + auto *SrcPtr = reinterpret_cast(Src); + auto *DstPtr = reinterpret_cast(Dst); + new (DstPtr) T(std::move(*SrcPtr)); +} + +template +static void ctorArrayTy(Block *, char *Ptr, bool, bool, bool, Descriptor *D) { + for (unsigned I = 0, NE = D->getNumElems(); I < NE; ++I) { + new (&reinterpret_cast(Ptr)[I]) T(); + } +} + +template +static void dtorArrayTy(Block *, char *Ptr, Descriptor *D) { + for (unsigned I = 0, NE = D->getNumElems(); I < NE; ++I) { + reinterpret_cast(Ptr)[I].~T(); + } +} + +template +static void moveArrayTy(Block *, char *Src, char *Dst, Descriptor *D) { + for (unsigned I = 0, NE = D->getNumElems(); I < NE; ++I) { + auto *SrcPtr = &reinterpret_cast(Src)[I]; + auto *DstPtr = &reinterpret_cast(Dst)[I]; + new (DstPtr) T(std::move(*SrcPtr)); + } +} + +static void ctorArrayDesc(Block *B, char *Ptr, bool IsConst, bool IsMutable, + bool IsActive, Descriptor *D) { + const unsigned NumElems = D->getNumElems(); + const unsigned ElemSize = + D->ElemDesc->getAllocSize() + sizeof(InlineDescriptor); + + unsigned ElemOffset = 0; + for (unsigned I = 0; I < NumElems; ++I, ElemOffset += ElemSize) { + auto *ElemPtr = Ptr + ElemOffset; + auto *Desc = reinterpret_cast(ElemPtr); + auto *ElemLoc = reinterpret_cast(Desc + 1); + auto *SD = D->ElemDesc; + + Desc->Offset = ElemOffset + sizeof(InlineDescriptor); + Desc->Desc = SD; + Desc->IsInitialized = true; + Desc->IsBase = false; + Desc->IsActive = IsActive; + Desc->IsConst = IsConst || D->IsConst; + Desc->IsMutable = IsMutable || D->IsMutable; + if (auto Fn = D->ElemDesc->CtorFn) + Fn(B, ElemLoc, Desc->IsConst, Desc->IsMutable, IsActive, D->ElemDesc); + } +} + +static void dtorArrayDesc(Block *B, char *Ptr, Descriptor *D) { + const unsigned NumElems = D->getNumElems(); + const unsigned ElemSize = + D->ElemDesc->getAllocSize() + sizeof(InlineDescriptor); + + unsigned ElemOffset = 0; + for (unsigned I = 0; I < NumElems; ++I, ElemOffset += ElemSize) { + auto *ElemPtr = Ptr + ElemOffset; + auto *Desc = reinterpret_cast(ElemPtr); + auto *ElemLoc = reinterpret_cast(Desc + 1); + if (auto Fn = D->ElemDesc->DtorFn) + Fn(B, ElemLoc, D->ElemDesc); + } +} + +static void moveArrayDesc(Block *B, char *Src, char *Dst, Descriptor *D) { + const unsigned NumElems = D->getNumElems(); + const unsigned ElemSize = + D->ElemDesc->getAllocSize() + sizeof(InlineDescriptor); + + unsigned ElemOffset = 0; + for (unsigned I = 0; I < NumElems; ++I, ElemOffset += ElemSize) { + auto *SrcPtr = Src + ElemOffset; + auto *DstPtr = Dst + ElemOffset; + + auto *SrcDesc = reinterpret_cast(SrcPtr); + auto *SrcElemLoc = reinterpret_cast(SrcDesc + 1); + auto *DstDesc = reinterpret_cast(DstPtr); + auto *DstElemLoc = reinterpret_cast(DstDesc + 1); + + *DstDesc = *SrcDesc; + if (auto Fn = D->ElemDesc->MoveFn) + Fn(B, SrcElemLoc, DstElemLoc, D->ElemDesc); + } +} + +static void ctorRecord(Block *B, char *Ptr, bool IsConst, bool IsMutable, + bool IsActive, Descriptor *D) { + const bool IsUnion = D->ElemRecord->isUnion(); + auto CtorSub = [=](unsigned SubOff, Descriptor *F, bool IsBase) { + auto *Desc = reinterpret_cast(Ptr + SubOff) - 1; + Desc->Offset = SubOff; + Desc->Desc = F; + Desc->IsInitialized = (B->isStatic() || F->IsArray) && !IsBase; + Desc->IsBase = IsBase; + Desc->IsActive = IsActive && !IsUnion; + Desc->IsConst = IsConst || F->IsConst; + Desc->IsMutable = IsMutable || F->IsMutable; + if (auto Fn = F->CtorFn) + Fn(B, Ptr + SubOff, Desc->IsConst, Desc->IsMutable, Desc->IsActive, F); + }; + for (const auto &B : D->ElemRecord->bases()) + CtorSub(B.Offset, B.Desc, /*isBase=*/true); + for (const auto &F : D->ElemRecord->fields()) + CtorSub(F.Offset, F.Desc, /*isBase=*/false); + for (const auto &V : D->ElemRecord->virtual_bases()) + CtorSub(V.Offset, V.Desc, /*isBase=*/true); +} + +static void dtorRecord(Block *B, char *Ptr, Descriptor *D) { + auto DtorSub = [=](unsigned SubOff, Descriptor *F) { + if (auto Fn = F->DtorFn) + Fn(B, Ptr + SubOff, F); + }; + for (const auto &F : D->ElemRecord->bases()) + DtorSub(F.Offset, F.Desc); + for (const auto &F : D->ElemRecord->fields()) + DtorSub(F.Offset, F.Desc); + for (const auto &F : D->ElemRecord->virtual_bases()) + DtorSub(F.Offset, F.Desc); +} + +static void moveRecord(Block *B, char *Src, char *Dst, Descriptor *D) { + for (const auto &F : D->ElemRecord->fields()) { + auto FieldOff = F.Offset; + auto FieldDesc = F.Desc; + + *(reinterpret_cast(Dst + FieldOff) - 1) = FieldDesc; + if (auto Fn = FieldDesc->MoveFn) + Fn(B, Src + FieldOff, Dst + FieldOff, FieldDesc); + } +} + +static BlockCtorFn getCtorPrim(PrimType Type) { + COMPOSITE_TYPE_SWITCH(Type, return ctorTy, return nullptr); +} + +static BlockDtorFn getDtorPrim(PrimType Type) { + COMPOSITE_TYPE_SWITCH(Type, return dtorTy, return nullptr); +} + +static BlockMoveFn getMovePrim(PrimType Type) { + COMPOSITE_TYPE_SWITCH(Type, return moveTy, return nullptr); +} + +static BlockCtorFn getCtorArrayPrim(PrimType Type) { + COMPOSITE_TYPE_SWITCH(Type, return ctorArrayTy, return nullptr); +} + +static BlockDtorFn getDtorArrayPrim(PrimType Type) { + COMPOSITE_TYPE_SWITCH(Type, return dtorArrayTy, return nullptr); +} + +static BlockMoveFn getMoveArrayPrim(PrimType Type) { + COMPOSITE_TYPE_SWITCH(Type, return moveArrayTy, return nullptr); +} + +Descriptor::Descriptor(const DeclTy &D, PrimType Type, bool IsConst, + bool IsTemporary, bool IsMutable) + : Source(D), ElemSize(primSize(Type)), Size(ElemSize), AllocSize(Size), + IsConst(IsConst), IsMutable(IsMutable), IsTemporary(IsTemporary), + CtorFn(getCtorPrim(Type)), DtorFn(getDtorPrim(Type)), + MoveFn(getMovePrim(Type)) { + assert(Source && "Missing source"); +} + +Descriptor::Descriptor(const DeclTy &D, PrimType Type, size_t NumElems, + bool IsConst, bool IsTemporary, bool IsMutable) + : Source(D), ElemSize(primSize(Type)), Size(ElemSize * NumElems), + AllocSize(align(Size) + sizeof(InitMap *)), IsConst(IsConst), + IsMutable(IsMutable), IsTemporary(IsTemporary), IsArray(true), + CtorFn(getCtorArrayPrim(Type)), DtorFn(getDtorArrayPrim(Type)), + MoveFn(getMoveArrayPrim(Type)) { + assert(Source && "Missing source"); +} + +Descriptor::Descriptor(const DeclTy &D, PrimType Type, bool IsTemporary, + UnknownSize) + : Source(D), ElemSize(primSize(Type)), Size(UnknownSizeMark), + AllocSize(alignof(void *)), IsConst(true), IsMutable(false), + IsTemporary(IsTemporary), IsArray(true), CtorFn(getCtorArrayPrim(Type)), + DtorFn(getDtorArrayPrim(Type)), MoveFn(getMoveArrayPrim(Type)) { + assert(Source && "Missing source"); +} + +Descriptor::Descriptor(const DeclTy &D, Descriptor *Elem, unsigned NumElems, + bool IsConst, bool IsTemporary, bool IsMutable) + : Source(D), ElemSize(Elem->getAllocSize() + sizeof(InlineDescriptor)), + Size(ElemSize * NumElems), + AllocSize(std::max(alignof(void *), Size)), ElemDesc(Elem), + IsConst(IsConst), IsMutable(IsMutable), IsTemporary(IsTemporary), + IsArray(true), CtorFn(ctorArrayDesc), DtorFn(dtorArrayDesc), + MoveFn(moveArrayDesc) { + assert(Source && "Missing source"); +} + +Descriptor::Descriptor(const DeclTy &D, Descriptor *Elem, bool IsTemporary, + UnknownSize) + : Source(D), ElemSize(Elem->getAllocSize() + sizeof(InlineDescriptor)), + Size(UnknownSizeMark), AllocSize(alignof(void *)), ElemDesc(Elem), + IsConst(true), IsMutable(false), IsTemporary(IsTemporary), IsArray(true), + CtorFn(ctorArrayDesc), DtorFn(dtorArrayDesc), MoveFn(moveArrayDesc) { + assert(Source && "Missing source"); +} + +Descriptor::Descriptor(const DeclTy &D, Record *R, bool IsConst, + bool IsTemporary, bool IsMutable) + : Source(D), ElemSize(std::max(alignof(void *), R->getFullSize())), + Size(ElemSize), AllocSize(Size), ElemRecord(R), IsConst(IsConst), + IsMutable(IsMutable), IsTemporary(IsTemporary), CtorFn(ctorRecord), + DtorFn(dtorRecord), MoveFn(moveRecord) { + assert(Source && "Missing source"); +} + +QualType Descriptor::getType() const { + if (auto *E = asExpr()) + return E->getType(); + if (auto *D = asValueDecl()) + return D->getType(); + llvm_unreachable("Invalid descriptor type"); +} + +SourceLocation Descriptor::getLocation() const { + if (auto *D = Source.dyn_cast()) + return D->getLocation(); + if (auto *E = Source.dyn_cast()) + return E->getExprLoc(); + llvm_unreachable("Invalid descriptor type"); +} + +InitMap::InitMap(unsigned N) : UninitFields(N) { + for (unsigned I = 0; I < N / PER_FIELD; ++I) { + data()[I] = 0; + } +} + +InitMap::T *InitMap::data() { + auto *Start = reinterpret_cast(this) + align(sizeof(InitMap)); + return reinterpret_cast(Start); +} + +bool InitMap::initialize(unsigned I) { + unsigned Bucket = I / PER_FIELD; + unsigned Mask = 1ull << static_cast(I % PER_FIELD); + if (!(data()[Bucket] & Mask)) { + data()[Bucket] |= Mask; + UninitFields -= 1; + } + return UninitFields == 0; +} + +bool InitMap::isInitialized(unsigned I) { + unsigned Bucket = I / PER_FIELD; + unsigned Mask = 1ull << static_cast(I % PER_FIELD); + return data()[Bucket] & Mask; +} + +InitMap *InitMap::allocate(unsigned N) { + const size_t NumFields = ((N + PER_FIELD - 1) / PER_FIELD); + const size_t Size = align(sizeof(InitMap)) + NumFields * PER_FIELD; + return new (malloc(Size)) InitMap(N); +} diff --git a/lib/AST/Interp/Descriptor.h b/lib/AST/Interp/Descriptor.h new file mode 100644 index 0000000000000..b260b7600974d --- /dev/null +++ b/lib/AST/Interp/Descriptor.h @@ -0,0 +1,220 @@ +//===--- Descriptor.h - Types for the constexpr VM --------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Defines descriptors which characterise allocations. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_AST_INTERP_DESCRIPTOR_H +#define LLVM_CLANG_AST_INTERP_DESCRIPTOR_H + +#include "clang/AST/Decl.h" +#include "clang/AST/Expr.h" + +namespace clang { +namespace interp { +class Block; +class Record; +struct Descriptor; +enum PrimType : unsigned; + +using DeclTy = llvm::PointerUnion; + +/// Invoked whenever a block is created. The constructor method fills in the +/// inline descriptors of all fields and array elements. It also initializes +/// all the fields which contain non-trivial types. +using BlockCtorFn = void (*)(Block *Storage, char *FieldPtr, bool IsConst, + bool IsMutable, bool IsActive, + Descriptor *FieldDesc); + +/// Invoked when a block is destroyed. Invokes the destructors of all +/// non-trivial nested fields of arrays and records. +using BlockDtorFn = void (*)(Block *Storage, char *FieldPtr, + Descriptor *FieldDesc); + +/// Invoked when a block with pointers referencing it goes out of scope. Such +/// blocks are persisted: the move function copies all inline descriptors and +/// non-trivial fields, as existing pointers might need to reference those +/// descriptors. Data is not copied since it cannot be legally read. +using BlockMoveFn = void (*)(Block *Storage, char *SrcFieldPtr, + char *DstFieldPtr, Descriptor *FieldDesc); + +/// Object size as used by the interpreter. +using InterpSize = unsigned; + +/// Describes a memory block created by an allocation site. +struct Descriptor { +private: + /// Original declaration, used to emit the error message. + const DeclTy Source; + /// Size of an element, in host bytes. + const InterpSize ElemSize; + /// Size of the storage, in host bytes. + const InterpSize Size; + /// Size of the allocation (storage + metadata), in host bytes. + const InterpSize AllocSize; + + /// Value to denote arrays of unknown size. + static constexpr unsigned UnknownSizeMark = (unsigned)-1; + +public: + /// Token to denote structures of unknown size. + struct UnknownSize {}; + + /// Pointer to the record, if block contains records. + Record *const ElemRecord = nullptr; + /// Descriptor of the array element. + Descriptor *const ElemDesc = nullptr; + /// Flag indicating if the block is mutable. + const bool IsConst = false; + /// Flag indicating if a field is mutable. + const bool IsMutable = false; + /// Flag indicating if the block is a temporary. + const bool IsTemporary = false; + /// Flag indicating if the block is an array. + const bool IsArray = false; + + /// Storage management methods. + const BlockCtorFn CtorFn = nullptr; + const BlockDtorFn DtorFn = nullptr; + const BlockMoveFn MoveFn = nullptr; + + /// Allocates a descriptor for a primitive. + Descriptor(const DeclTy &D, PrimType Type, bool IsConst, bool IsTemporary, + bool IsMutable); + + /// Allocates a descriptor for an array of primitives. + Descriptor(const DeclTy &D, PrimType Type, size_t NumElems, bool IsConst, + bool IsTemporary, bool IsMutable); + + /// Allocates a descriptor for an array of primitives of unknown size. + Descriptor(const DeclTy &D, PrimType Type, bool IsTemporary, UnknownSize); + + /// Allocates a descriptor for an array of composites. + Descriptor(const DeclTy &D, Descriptor *Elem, unsigned NumElems, bool IsConst, + bool IsTemporary, bool IsMutable); + + /// Allocates a descriptor for an array of composites of unknown size. + Descriptor(const DeclTy &D, Descriptor *Elem, bool IsTemporary, UnknownSize); + + /// Allocates a descriptor for a record. + Descriptor(const DeclTy &D, Record *R, bool IsConst, bool IsTemporary, + bool IsMutable); + + QualType getType() const; + SourceLocation getLocation() const; + + const Decl *asDecl() const { return Source.dyn_cast(); } + const Expr *asExpr() const { return Source.dyn_cast(); } + + const ValueDecl *asValueDecl() const { + return dyn_cast_or_null(asDecl()); + } + + const FieldDecl *asFieldDecl() const { + return dyn_cast_or_null(asDecl()); + } + + const RecordDecl *asRecordDecl() const { + return dyn_cast_or_null(asDecl()); + } + + /// Returns the size of the object without metadata. + unsigned getSize() const { + assert(!isUnknownSizeArray() && "Array of unknown size"); + return Size; + } + + /// Returns the allocated size, including metadata. + unsigned getAllocSize() const { return AllocSize; } + /// returns the size of an element when the structure is viewed as an array. + unsigned getElemSize() const { return ElemSize; } + + /// Returns the number of elements stored in the block. + unsigned getNumElems() const { + return Size == UnknownSizeMark ? 0 : (getSize() / getElemSize()); + } + + /// Checks if the descriptor is of an array of primitives. + bool isPrimitiveArray() const { return IsArray && !ElemDesc; } + /// Checks if the descriptor is of an array of zero size. + bool isZeroSizeArray() const { return Size == 0; } + /// Checks if the descriptor is of an array of unknown size. + bool isUnknownSizeArray() const { return Size == UnknownSizeMark; } + + /// Checks if the descriptor is of a primitive. + bool isPrimitive() const { return !IsArray && !ElemRecord; } + + /// Checks if the descriptor is of an array. + bool isArray() const { return IsArray; } +}; + +/// Inline descriptor embedded in structures and arrays. +/// +/// Such descriptors precede all composite array elements and structure fields. +/// If the base of a pointer is not zero, the base points to the end of this +/// structure. The offset field is used to traverse the pointer chain up +/// to the root structure which allocated the object. +struct InlineDescriptor { + /// Offset inside the structure/array. + unsigned Offset; + + /// Flag indicating if the storage is constant or not. + /// Relevant for primitive fields. + unsigned IsConst : 1; + /// For primitive fields, it indicates if the field was initialized. + /// Primitive fields in static storage are always initialized. + /// Arrays are always initialized, even though their elements might not be. + /// Base classes are initialized after the constructor is invoked. + unsigned IsInitialized : 1; + /// Flag indicating if the field is an embedded base class. + unsigned IsBase : 1; + /// Flag indicating if the field is the active member of a union. + unsigned IsActive : 1; + /// Flag indicating if the field is mutable (if in a record). + unsigned IsMutable : 1; + + Descriptor *Desc; +}; + +/// Bitfield tracking the initialisation status of elements of primitive arrays. +/// A pointer to this is embedded at the end of all primitive arrays. +/// If the map was not yet created and nothing was initialied, the pointer to +/// this structure is 0. If the object was fully initialized, the pointer is -1. +struct InitMap { +private: + /// Type packing bits. + using T = uint64_t; + /// Bits stored in a single field. + static constexpr uint64_t PER_FIELD = sizeof(T) * CHAR_BIT; + + /// Initializes the map with no fields set. + InitMap(unsigned N); + + /// Returns a pointer to storage. + T *data(); + +public: + /// Initializes an element. Returns true when object if fully initialized. + bool initialize(unsigned I); + + /// Checks if an element was initialized. + bool isInitialized(unsigned I); + + /// Allocates a map holding N elements. + static InitMap *allocate(unsigned N); + +private: + /// Number of fields initialized. + unsigned UninitFields; +}; + +} // namespace interp +} // namespace clang + +#endif diff --git a/lib/AST/Interp/Disasm.cpp b/lib/AST/Interp/Disasm.cpp new file mode 100644 index 0000000000000..e77a825eb1f23 --- /dev/null +++ b/lib/AST/Interp/Disasm.cpp @@ -0,0 +1,69 @@ +//===--- Disasm.cpp - Disassembler for bytecode functions -------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Dump method for Function which disassembles the bytecode. +// +//===----------------------------------------------------------------------===// + +#include "Function.h" +#include "Opcode.h" +#include "PrimType.h" +#include "Program.h" +#include "clang/AST/DeclCXX.h" +#include "llvm/Support/Compiler.h" + +using namespace clang; +using namespace clang::interp; + +LLVM_DUMP_METHOD void Function::dump() const { dump(llvm::errs()); } + +LLVM_DUMP_METHOD void Function::dump(llvm::raw_ostream &OS) const { + if (F) { + if (auto *Cons = dyn_cast(F)) { + const std::string &Name = Cons->getParent()->getNameAsString(); + OS << Name << "::" << Name << ":\n"; + } else { + OS << F->getNameAsString() << ":\n"; + } + } else { + OS << "<>\n"; + } + + OS << "frame size: " << getFrameSize() << "\n"; + OS << "arg size: " << getArgSize() << "\n"; + OS << "rvo: " << hasRVO() << "\n"; + + auto PrintName = [&OS](const char *Name) { + OS << Name; + for (long I = 0, N = strlen(Name); I < 30 - N; ++I) { + OS << ' '; + } + }; + + for (CodePtr Start = getCodeBegin(), PC = Start; PC != getCodeEnd();) { + size_t Addr = PC - Start; + auto Op = PC.read(); + OS << llvm::format("%8d", Addr) << " "; + switch (Op) { +#define GET_DISASM +#include "Opcodes.inc" +#undef GET_DISASM + } + } +} + +LLVM_DUMP_METHOD void Program::dump() const { dump(llvm::errs()); } + +LLVM_DUMP_METHOD void Program::dump(llvm::raw_ostream &OS) const { + for (auto &Func : Funcs) { + Func.second->dump(); + } + for (auto &Anon : AnonFuncs) { + Anon->dump(); + } +} diff --git a/lib/AST/Interp/EvalEmitter.cpp b/lib/AST/Interp/EvalEmitter.cpp new file mode 100644 index 0000000000000..22e8695b92112 --- /dev/null +++ b/lib/AST/Interp/EvalEmitter.cpp @@ -0,0 +1,253 @@ +//===--- EvalEmitter.cpp - Instruction emitter for the VM -------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "EvalEmitter.h" +#include "Context.h" +#include "Interp.h" +#include "Opcode.h" +#include "Program.h" +#include "clang/AST/DeclCXX.h" + +using namespace clang; +using namespace clang::interp; + +using APSInt = llvm::APSInt; +template using Expected = llvm::Expected; + +EvalEmitter::EvalEmitter(Context &Ctx, Program &P, State &Parent, + InterpStack &Stk, APValue &Result) + : Ctx(Ctx), P(P), S(Parent, P, Stk, Ctx, this), Result(Result) { + // Create a dummy frame for the interpreter which does not have locals. + S.Current = new InterpFrame(S, nullptr, nullptr, CodePtr(), Pointer()); +} + +llvm::Expected EvalEmitter::interpretExpr(const Expr *E) { + if (this->visitExpr(E)) + return true; + if (BailLocation) + return llvm::make_error(*BailLocation); + return false; +} + +llvm::Expected EvalEmitter::interpretDecl(const VarDecl *VD) { + if (this->visitDecl(VD)) + return true; + if (BailLocation) + return llvm::make_error(*BailLocation); + return false; +} + +void EvalEmitter::emitLabel(LabelTy Label) { + CurrentLabel = Label; +} + +EvalEmitter::LabelTy EvalEmitter::getLabel() { return NextLabel++; } + +Scope::Local EvalEmitter::createLocal(Descriptor *D) { + // Allocate memory for a local. + auto Memory = std::make_unique(sizeof(Block) + D->getAllocSize()); + auto *B = new (Memory.get()) Block(D, /*isStatic=*/false); + B->invokeCtor(); + + // Register the local. + unsigned Off = Locals.size(); + Locals.insert({Off, std::move(Memory)}); + return {Off, D}; +} + +bool EvalEmitter::bail(const SourceLocation &Loc) { + if (!BailLocation) + BailLocation = Loc; + return false; +} + +bool EvalEmitter::jumpTrue(const LabelTy &Label) { + if (isActive()) { + if (S.Stk.pop()) + ActiveLabel = Label; + } + return true; +} + +bool EvalEmitter::jumpFalse(const LabelTy &Label) { + if (isActive()) { + if (!S.Stk.pop()) + ActiveLabel = Label; + } + return true; +} + +bool EvalEmitter::jump(const LabelTy &Label) { + if (isActive()) + CurrentLabel = ActiveLabel = Label; + return true; +} + +bool EvalEmitter::fallthrough(const LabelTy &Label) { + if (isActive()) + ActiveLabel = Label; + CurrentLabel = Label; + return true; +} + +template bool EvalEmitter::emitRet(const SourceInfo &Info) { + if (!isActive()) + return true; + using T = typename PrimConv::T; + return ReturnValue(S.Stk.pop(), Result); +} + +bool EvalEmitter::emitRetVoid(const SourceInfo &Info) { return true; } + +bool EvalEmitter::emitRetValue(const SourceInfo &Info) { + // Method to recursively traverse composites. + std::function Composite; + Composite = [this, &Composite](QualType Ty, const Pointer &Ptr, APValue &R) { + if (auto *AT = Ty->getAs()) + Ty = AT->getValueType(); + + if (auto *RT = Ty->getAs()) { + auto *Record = Ptr.getRecord(); + assert(Record && "Missing record descriptor"); + + bool Ok = true; + if (RT->getDecl()->isUnion()) { + const FieldDecl *ActiveField = nullptr; + APValue Value; + for (auto &F : Record->fields()) { + const Pointer &FP = Ptr.atField(F.Offset); + QualType FieldTy = F.Decl->getType(); + if (FP.isActive()) { + if (llvm::Optional T = Ctx.classify(FieldTy)) { + TYPE_SWITCH(*T, Ok &= ReturnValue(FP.deref(), Value)); + } else { + Ok &= Composite(FieldTy, FP, Value); + } + break; + } + } + R = APValue(ActiveField, Value); + } else { + unsigned NF = Record->getNumFields(); + unsigned NB = Record->getNumBases(); + unsigned NV = Ptr.isBaseClass() ? 0 : Record->getNumVirtualBases(); + + R = APValue(APValue::UninitStruct(), NB, NF); + + for (unsigned I = 0; I < NF; ++I) { + const Record::Field *FD = Record->getField(I); + QualType FieldTy = FD->Decl->getType(); + const Pointer &FP = Ptr.atField(FD->Offset); + APValue &Value = R.getStructField(I); + + if (llvm::Optional T = Ctx.classify(FieldTy)) { + TYPE_SWITCH(*T, Ok &= ReturnValue(FP.deref(), Value)); + } else { + Ok &= Composite(FieldTy, FP, Value); + } + } + + for (unsigned I = 0; I < NB; ++I) { + const Record::Base *BD = Record->getBase(I); + QualType BaseTy = Ctx.getASTContext().getRecordType(BD->Decl); + const Pointer &BP = Ptr.atField(BD->Offset); + Ok &= Composite(BaseTy, BP, R.getStructBase(I)); + } + + for (unsigned I = 0; I < NV; ++I) { + const Record::Base *VD = Record->getVirtualBase(I); + QualType VirtBaseTy = Ctx.getASTContext().getRecordType(VD->Decl); + const Pointer &VP = Ptr.atField(VD->Offset); + Ok &= Composite(VirtBaseTy, VP, R.getStructBase(NB + I)); + } + } + return Ok; + } + if (auto *AT = Ty->getAsArrayTypeUnsafe()) { + const size_t NumElems = Ptr.getNumElems(); + QualType ElemTy = AT->getElementType(); + R = APValue(APValue::UninitArray{}, NumElems, NumElems); + + bool Ok = true; + for (unsigned I = 0; I < NumElems; ++I) { + APValue &Slot = R.getArrayInitializedElt(I); + const Pointer &EP = Ptr.atIndex(I); + if (llvm::Optional T = Ctx.classify(ElemTy)) { + TYPE_SWITCH(*T, Ok &= ReturnValue(EP.deref(), Slot)); + } else { + Ok &= Composite(ElemTy, EP.narrow(), Slot); + } + } + return Ok; + } + llvm_unreachable("invalid value to return"); + }; + + // Return the composite type. + const auto &Ptr = S.Stk.pop(); + return Composite(Ptr.getType(), Ptr, Result); +} + +bool EvalEmitter::emitGetPtrLocal(uint32_t I, const SourceInfo &Info) { + if (!isActive()) + return true; + + auto It = Locals.find(I); + assert(It != Locals.end() && "Missing local variable"); + S.Stk.push(reinterpret_cast(It->second.get())); + return true; +} + +template +bool EvalEmitter::emitGetLocal(uint32_t I, const SourceInfo &Info) { + if (!isActive()) + return true; + + using T = typename PrimConv::T; + + auto It = Locals.find(I); + assert(It != Locals.end() && "Missing local variable"); + auto *B = reinterpret_cast(It->second.get()); + S.Stk.push(*reinterpret_cast(B + 1)); + return true; +} + +template +bool EvalEmitter::emitSetLocal(uint32_t I, const SourceInfo &Info) { + if (!isActive()) + return true; + + using T = typename PrimConv::T; + + auto It = Locals.find(I); + assert(It != Locals.end() && "Missing local variable"); + auto *B = reinterpret_cast(It->second.get()); + *reinterpret_cast(B + 1) = S.Stk.pop(); + return true; +} + +bool EvalEmitter::emitDestroy(uint32_t I, const SourceInfo &Info) { + if (!isActive()) + return true; + + for (auto &Local : Descriptors[I]) { + auto It = Locals.find(Local.Offset); + assert(It != Locals.end() && "Missing local variable"); + S.deallocate(reinterpret_cast(It->second.get())); + } + + return true; +} + +//===----------------------------------------------------------------------===// +// Opcode evaluators +//===----------------------------------------------------------------------===// + +#define GET_EVAL_IMPL +#include "Opcodes.inc" +#undef GET_EVAL_IMPL diff --git a/lib/AST/Interp/EvalEmitter.h b/lib/AST/Interp/EvalEmitter.h new file mode 100644 index 0000000000000..eec2ff8ee753f --- /dev/null +++ b/lib/AST/Interp/EvalEmitter.h @@ -0,0 +1,129 @@ +//===--- EvalEmitter.h - Instruction emitter for the VM ---------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Defines the instruction emitters. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_AST_INTERP_EVALEMITTER_H +#define LLVM_CLANG_AST_INTERP_EVALEMITTER_H + +#include "ByteCodeGenError.h" +#include "Context.h" +#include "InterpStack.h" +#include "InterpState.h" +#include "PrimType.h" +#include "Program.h" +#include "Source.h" +#include "llvm/Support/Error.h" + +namespace clang { +class FunctionDecl; +namespace interp { +class Context; +class Function; +class InterpState; +class Program; +class SourceInfo; +enum Opcode : uint32_t; + +/// An emitter which evaluates opcodes as they are emitted. +class EvalEmitter : public SourceMapper { +public: + using LabelTy = uint32_t; + using AddrTy = uintptr_t; + using Local = Scope::Local; + + llvm::Expected interpretExpr(const Expr *E); + llvm::Expected interpretDecl(const VarDecl *VD); + +protected: + EvalEmitter(Context &Ctx, Program &P, State &Parent, InterpStack &Stk, + APValue &Result); + + virtual ~EvalEmitter() {} + + /// Define a label. + void emitLabel(LabelTy Label); + /// Create a label. + LabelTy getLabel(); + + /// Methods implemented by the compiler. + virtual bool visitExpr(const Expr *E) = 0; + virtual bool visitDecl(const VarDecl *VD) = 0; + + bool bail(const Stmt *S) { return bail(S->getBeginLoc()); } + bool bail(const Decl *D) { return bail(D->getBeginLoc()); } + bool bail(const SourceLocation &Loc); + + /// Emits jumps. + bool jumpTrue(const LabelTy &Label); + bool jumpFalse(const LabelTy &Label); + bool jump(const LabelTy &Label); + bool fallthrough(const LabelTy &Label); + + /// Callback for registering a local. + Local createLocal(Descriptor *D); + + /// Returns the source location of the current opcode. + SourceInfo getSource(Function *F, CodePtr PC) const override { + return F ? F->getSource(PC) : CurrentSource; + } + + /// Parameter indices. + llvm::DenseMap Params; + /// Local descriptors. + llvm::SmallVector, 2> Descriptors; + +private: + /// Current compilation context. + Context &Ctx; + /// Current program. + Program &P; + /// Callee evaluation state. + InterpState S; + /// Location to write the result to. + APValue &Result; + + /// Temporaries which require storage. + llvm::DenseMap> Locals; + + // The emitter always tracks the current instruction and sets OpPC to a token + // value which is mapped to the location of the opcode being evaluated. + CodePtr OpPC; + /// Location of a failure. + llvm::Optional BailLocation; + /// Location of the current instruction. + SourceInfo CurrentSource; + + /// Next label ID to generate - first label is 1. + LabelTy NextLabel = 1; + /// Label being executed - 0 is the entry label. + LabelTy CurrentLabel = 0; + /// Active block which should be executed. + LabelTy ActiveLabel = 0; + + /// Since expressions can only jump forward, predicated execution is + /// used to deal with if-else statements. + bool isActive() { return CurrentLabel == ActiveLabel; } + + /// Helper to invoke a method. + bool ExecuteCall(Function *F, Pointer &&This, const SourceInfo &Info); + /// Helper to emit a diagnostic on a missing method. + bool ExecuteNoCall(const FunctionDecl *F, const SourceInfo &Info); + +protected: +#define GET_EVAL_PROTO +#include "Opcodes.inc" +#undef GET_EVAL_PROTO +}; + +} // namespace interp +} // namespace clang + +#endif diff --git a/lib/AST/Interp/Frame.cpp b/lib/AST/Interp/Frame.cpp new file mode 100644 index 0000000000000..16134aa1db36c --- /dev/null +++ b/lib/AST/Interp/Frame.cpp @@ -0,0 +1,14 @@ +//===--- Frame.cpp - Call frame for the VM and AST Walker -------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "Frame.h" + +using namespace clang; +using namespace clang::interp; + +Frame::~Frame() {} diff --git a/lib/AST/Interp/Frame.h b/lib/AST/Interp/Frame.h new file mode 100644 index 0000000000000..b9a0ea9412f83 --- /dev/null +++ b/lib/AST/Interp/Frame.h @@ -0,0 +1,45 @@ +//===--- Frame.h - Call frame for the VM and AST Walker ---------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Defines the base class of interpreter and evaluator stack frames. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_AST_INTERP_FRAME_H +#define LLVM_CLANG_AST_INTERP_FRAME_H + +#include "clang/Basic/SourceLocation.h" +#include "llvm/Support/raw_ostream.h" + +namespace clang { +class FunctionDecl; + +namespace interp { + +/// Base class for stack frames, shared between VM and walker. +class Frame { +public: + virtual ~Frame(); + + /// Generates a human-readable description of the call site. + virtual void describe(llvm::raw_ostream &OS) = 0; + + /// Returns a pointer to the caller frame. + virtual Frame *getCaller() const = 0; + + /// Returns the location of the call site. + virtual SourceLocation getCallLocation() const = 0; + + /// Returns the called function's declaration. + virtual const FunctionDecl *getCallee() const = 0; +}; + +} // namespace interp +} // namespace clang + +#endif diff --git a/lib/AST/Interp/Function.cpp b/lib/AST/Interp/Function.cpp new file mode 100644 index 0000000000000..0ed13a92aa38d --- /dev/null +++ b/lib/AST/Interp/Function.cpp @@ -0,0 +1,48 @@ +//===--- Function.h - Bytecode function for the VM --------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "Function.h" +#include "Program.h" +#include "Opcode.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" + +using namespace clang; +using namespace clang::interp; + +Function::Function(Program &P, const FunctionDecl *F, unsigned ArgSize, + llvm::SmallVector &&ParamTypes, + llvm::DenseMap &&Params) + : P(P), Loc(F->getBeginLoc()), F(F), ArgSize(ArgSize), + ParamTypes(std::move(ParamTypes)), Params(std::move(Params)) {} + +CodePtr Function::getCodeBegin() const { return Code.data(); } + +CodePtr Function::getCodeEnd() const { return Code.data() + Code.size(); } + +Function::ParamDescriptor Function::getParamDescriptor(unsigned Offset) const { + auto It = Params.find(Offset); + assert(It != Params.end() && "Invalid parameter offset"); + return It->second; +} + +SourceInfo Function::getSource(CodePtr PC) const { + unsigned Offset = PC - getCodeBegin(); + using Elem = std::pair; + auto It = std::lower_bound(SrcMap.begin(), SrcMap.end(), Elem{Offset, {}}, + [](Elem A, Elem B) { return A.first < B.first; }); + if (It == SrcMap.end() || It->first != Offset) + llvm::report_fatal_error("missing source location"); + return It->second; +} + +bool Function::isVirtual() const { + if (auto *M = dyn_cast(F)) + return M->isVirtual(); + return false; +} diff --git a/lib/AST/Interp/Function.h b/lib/AST/Interp/Function.h new file mode 100644 index 0000000000000..28531f04b6e95 --- /dev/null +++ b/lib/AST/Interp/Function.h @@ -0,0 +1,163 @@ +//===--- Function.h - Bytecode function for the VM --------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Defines the Function class which holds all bytecode function-specific data. +// +// The scope class which describes local variables is also defined here. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_AST_INTERP_FUNCTION_H +#define LLVM_CLANG_AST_INTERP_FUNCTION_H + +#include "Pointer.h" +#include "Source.h" +#include "clang/AST/Decl.h" +#include "llvm/Support/raw_ostream.h" + +namespace clang { +namespace interp { +class Program; +class ByteCodeEmitter; +enum PrimType : uint32_t; + +/// Describes a scope block. +/// +/// The block gathers all the descriptors of the locals defined in this block. +class Scope { +public: + /// Information about a local's storage. + struct Local { + /// Offset of the local in frame. + unsigned Offset; + /// Descriptor of the local. + Descriptor *Desc; + }; + + using LocalVectorTy = llvm::SmallVector; + + Scope(LocalVectorTy &&Descriptors) : Descriptors(std::move(Descriptors)) {} + + llvm::iterator_range locals() { + return llvm::make_range(Descriptors.begin(), Descriptors.end()); + } + +private: + /// Object descriptors in this block. + LocalVectorTy Descriptors; +}; + +/// Bytecode function. +/// +/// Contains links to the bytecode of the function, as well as metadata +/// describing all arguments and stack-local variables. +class Function { +public: + using ParamDescriptor = std::pair; + + /// Returns the size of the function's local stack. + unsigned getFrameSize() const { return FrameSize; } + /// Returns the size of the argument stackx + unsigned getArgSize() const { return ArgSize; } + + /// Returns a pointer to the start of the code. + CodePtr getCodeBegin() const; + /// Returns a pointer to the end of the code. + CodePtr getCodeEnd() const; + + /// Returns the original FunctionDecl. + const FunctionDecl *getDecl() const { return F; } + + /// Returns the lcoation. + SourceLocation getLoc() const { return Loc; } + + /// Returns a parameter descriptor. + ParamDescriptor getParamDescriptor(unsigned Offset) const; + + /// Checks if the first argument is a RVO pointer. + bool hasRVO() const { return ParamTypes.size() != Params.size(); } + + /// Range over the scope blocks. + llvm::iterator_range::iterator> scopes() { + return llvm::make_range(Scopes.begin(), Scopes.end()); + } + + /// Range over argument types. + using arg_reverse_iterator = SmallVectorImpl::reverse_iterator; + llvm::iterator_range args_reverse() { + return llvm::make_range(ParamTypes.rbegin(), ParamTypes.rend()); + } + + /// Returns a specific scope. + Scope &getScope(unsigned Idx) { return Scopes[Idx]; } + + /// Returns the source information at a given PC. + SourceInfo getSource(CodePtr PC) const; + + /// Checks if the function is valid to call in constexpr. + bool isConstexpr() const { return IsValid; } + + /// Checks if the function is virtual. + bool isVirtual() const; + + /// Checks if the function is a constructor. + bool isConstructor() const { return isa(F); } + +private: + /// Construct a function representing an actual function. + Function(Program &P, const FunctionDecl *F, unsigned ArgSize, + llvm::SmallVector &&ParamTypes, + llvm::DenseMap &&Params); + + /// Sets the code of a function. + void setCode(unsigned NewFrameSize, std::vector &&NewCode, SourceMap &&NewSrcMap, + llvm::SmallVector &&NewScopes) { + FrameSize = NewFrameSize; + Code = std::move(NewCode); + SrcMap = std::move(NewSrcMap); + Scopes = std::move(NewScopes); + IsValid = true; + } + +private: + friend class Program; + friend class ByteCodeEmitter; + + /// Program reference. + Program &P; + /// Location of the executed code. + SourceLocation Loc; + /// Declaration this function was compiled from. + const FunctionDecl *F; + /// Local area size: storage + metadata. + unsigned FrameSize; + /// Size of the argument stack. + unsigned ArgSize; + /// Program code. + std::vector Code; + /// Opcode-to-expression mapping. + SourceMap SrcMap; + /// List of block descriptors. + llvm::SmallVector Scopes; + /// List of argument types. + llvm::SmallVector ParamTypes; + /// Map from byte offset to parameter descriptor. + llvm::DenseMap Params; + /// Flag to indicate if the function is valid. + bool IsValid = false; + +public: + /// Dumps the disassembled bytecode to \c llvm::errs(). + void dump() const; + void dump(llvm::raw_ostream &OS) const; +}; + +} // namespace interp +} // namespace clang + +#endif diff --git a/lib/AST/Interp/Integral.h b/lib/AST/Interp/Integral.h new file mode 100644 index 0000000000000..7cc788070de84 --- /dev/null +++ b/lib/AST/Interp/Integral.h @@ -0,0 +1,269 @@ +//===--- Integral.h - Wrapper for numeric types for the VM ------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Defines the VM types and helpers operating on types. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_AST_INTERP_INTEGRAL_H +#define LLVM_CLANG_AST_INTERP_INTEGRAL_H + +#include "clang/AST/ComparisonCategories.h" +#include "clang/AST/APValue.h" +#include "llvm/ADT/APSInt.h" +#include "llvm/Support/MathExtras.h" +#include "llvm/Support/raw_ostream.h" +#include +#include + +namespace clang { +namespace interp { + +using APInt = llvm::APInt; +using APSInt = llvm::APSInt; + +/// Helper to compare two comparable types. +template +ComparisonCategoryResult Compare(const T &X, const T &Y) { + if (X < Y) + return ComparisonCategoryResult::Less; + if (X > Y) + return ComparisonCategoryResult::Greater; + return ComparisonCategoryResult::Equal; +} + +// Helper structure to select the representation. +template struct Repr; +template <> struct Repr<8, false> { using Type = uint8_t; }; +template <> struct Repr<16, false> { using Type = uint16_t; }; +template <> struct Repr<32, false> { using Type = uint32_t; }; +template <> struct Repr<64, false> { using Type = uint64_t; }; +template <> struct Repr<8, true> { using Type = int8_t; }; +template <> struct Repr<16, true> { using Type = int16_t; }; +template <> struct Repr<32, true> { using Type = int32_t; }; +template <> struct Repr<64, true> { using Type = int64_t; }; + +/// Wrapper around numeric types. +/// +/// These wrappers are required to shared an interface between APSint and +/// builtin primitive numeral types, while optimising for storage and +/// allowing methods operating on primitive type to compile to fast code. +template class Integral { +private: + template friend class Integral; + + // The primitive representing the integral. + using T = typename Repr::Type; + T V; + + /// Primitive representing limits. + static const auto Min = std::numeric_limits::min(); + static const auto Max = std::numeric_limits::max(); + + /// Construct an integral from anything that is convertible to storage. + template explicit Integral(T V) : V(V) {} + +public: + /// Zero-initializes an integral. + Integral() : V(0) {} + + /// Constructs an integral from another integral. + template + explicit Integral(Integral V) : V(V.V) {} + + /// Construct an integral from a value based on signedness. + explicit Integral(const APSInt &V) + : V(V.isSigned() ? V.getSExtValue() : V.getZExtValue()) {} + + bool operator<(Integral RHS) const { return V < RHS.V; } + bool operator>(Integral RHS) const { return V > RHS.V; } + bool operator<=(Integral RHS) const { return V <= RHS.V; } + bool operator>=(Integral RHS) const { return V >= RHS.V; } + bool operator==(Integral RHS) const { return V == RHS.V; } + bool operator!=(Integral RHS) const { return V != RHS.V; } + + bool operator>(unsigned RHS) const { + return V >= 0 && static_cast(V) > RHS; + } + + Integral operator-() const { return Integral(-V); } + Integral operator~() const { return Integral(~V); } + + template + explicit operator Integral() const { + return Integral(V); + } + + explicit operator unsigned() const { return V; } + explicit operator int64_t() const { return V; } + explicit operator uint64_t() const { return V; } + + APSInt toAPSInt() const { + return APSInt(APInt(Bits, static_cast(V), Signed), !Signed); + } + APSInt toAPSInt(unsigned NumBits) const { + if (Signed) + return APSInt(toAPSInt().sextOrTrunc(NumBits), !Signed); + else + return APSInt(toAPSInt().zextOrTrunc(NumBits), !Signed); + } + APValue toAPValue() const { return APValue(toAPSInt()); } + + Integral toUnsigned() const { + return Integral(*this); + } + + constexpr static unsigned bitWidth() { return Bits; } + + bool isZero() const { return !V; } + + bool isMin() const { return *this == min(bitWidth()); } + + bool isMinusOne() const { return Signed && V == T(-1); } + + constexpr static bool isSigned() { return Signed; } + + bool isNegative() const { return V < T(0); } + bool isPositive() const { return !isNegative(); } + + ComparisonCategoryResult compare(const Integral &RHS) const { + return Compare(V, RHS.V); + } + + unsigned countLeadingZeros() const { return llvm::countLeadingZeros(V); } + + Integral truncate(unsigned TruncBits) const { + if (TruncBits >= Bits) + return *this; + const T BitMask = (T(1) << T(TruncBits)) - 1; + const T SignBit = T(1) << (TruncBits - 1); + const T ExtMask = ~BitMask; + return Integral((V & BitMask) | (Signed && (V & SignBit) ? ExtMask : 0)); + } + + void print(llvm::raw_ostream &OS) const { OS << V; } + + static Integral min(unsigned NumBits) { + return Integral(Min); + } + static Integral max(unsigned NumBits) { + return Integral(Max); + } + + template + static typename std::enable_if::value, Integral>::type + from(T Value) { + return Integral(Value); + } + + template + static typename std::enable_if::type + from(Integral Value) { + return Integral(Value.V); + } + + template static Integral from(Integral<0, SrcSign> Value) { + if (SrcSign) + return Integral(Value.V.getSExtValue()); + else + return Integral(Value.V.getZExtValue()); + } + + static Integral zero() { return from(0); } + + template static Integral from(T Value, unsigned NumBits) { + return Integral(Value); + } + + static bool inRange(int64_t Value, unsigned NumBits) { + return CheckRange(Value); + } + + static bool increment(Integral A, Integral *R) { + return add(A, Integral(T(1)), A.bitWidth(), R); + } + + static bool decrement(Integral A, Integral *R) { + return sub(A, Integral(T(1)), A.bitWidth(), R); + } + + static bool add(Integral A, Integral B, unsigned OpBits, Integral *R) { + return CheckAddUB(A.V, B.V, R->V); + } + + static bool sub(Integral A, Integral B, unsigned OpBits, Integral *R) { + return CheckSubUB(A.V, B.V, R->V); + } + + static bool mul(Integral A, Integral B, unsigned OpBits, Integral *R) { + return CheckMulUB(A.V, B.V, R->V); + } + +private: + template + static typename std::enable_if::value, bool>::type + CheckAddUB(T A, T B, T &R) { + return llvm::AddOverflow(A, B, R); + } + + template + static typename std::enable_if::value, bool>::type + CheckAddUB(T A, T B, T &R) { + R = A + B; + return false; + } + + template + static typename std::enable_if::value, bool>::type + CheckSubUB(T A, T B, T &R) { + return llvm::SubOverflow(A, B, R); + } + + template + static typename std::enable_if::value, bool>::type + CheckSubUB(T A, T B, T &R) { + R = A - B; + return false; + } + + template + static typename std::enable_if::value, bool>::type + CheckMulUB(T A, T B, T &R) { + return llvm::MulOverflow(A, B, R); + } + + template + static typename std::enable_if::value, bool>::type + CheckMulUB(T A, T B, T &R) { + R = A * B; + return false; + } + + template + static typename std::enable_if::value, bool>::type + CheckRange(int64_t V) { + return Min <= V && V <= Max; + } + + template + static typename std::enable_if::value, bool>::type + CheckRange(int64_t V) { + return V >= 0 && static_cast(V) <= Max; + } +}; + +template +llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, Integral I) { + I.print(OS); + return OS; +} + +} // namespace interp +} // namespace clang + +#endif diff --git a/lib/AST/Interp/Interp.cpp b/lib/AST/Interp/Interp.cpp new file mode 100644 index 0000000000000..1a8109cedf769 --- /dev/null +++ b/lib/AST/Interp/Interp.cpp @@ -0,0 +1,417 @@ +//===--- InterpState.cpp - Interpreter for the constexpr VM -----*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "Interp.h" +#include +#include +#include "Function.h" +#include "InterpFrame.h" +#include "InterpStack.h" +#include "Opcode.h" +#include "PrimType.h" +#include "Program.h" +#include "State.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/ASTDiagnostic.h" +#include "clang/AST/CXXInheritance.h" +#include "clang/AST/Expr.h" +#include "clang/AST/ExprCXX.h" +#include "llvm/ADT/APSInt.h" + +using namespace clang; +using namespace clang::interp; + +//===----------------------------------------------------------------------===// +// Ret +//===----------------------------------------------------------------------===// + +template ::T> +static bool Ret(InterpState &S, CodePtr &PC, APValue &Result) { + S.CallStackDepth--; + const T &Ret = S.Stk.pop(); + + assert(S.Current->getFrameOffset() == S.Stk.size() && "Invalid frame"); + if (!S.checkingPotentialConstantExpression()) + S.Current->popArgs(); + + if (InterpFrame *Caller = S.Current->Caller) { + PC = S.Current->getRetPC(); + delete S.Current; + S.Current = Caller; + S.Stk.push(Ret); + } else { + delete S.Current; + S.Current = nullptr; + if (!ReturnValue(Ret, Result)) + return false; + } + return true; +} + +static bool RetVoid(InterpState &S, CodePtr &PC, APValue &Result) { + S.CallStackDepth--; + + assert(S.Current->getFrameOffset() == S.Stk.size() && "Invalid frame"); + if (!S.checkingPotentialConstantExpression()) + S.Current->popArgs(); + + if (InterpFrame *Caller = S.Current->Caller) { + PC = S.Current->getRetPC(); + delete S.Current; + S.Current = Caller; + } else { + delete S.Current; + S.Current = nullptr; + } + return true; +} + +static bool RetValue(InterpState &S, CodePtr &Pt, APValue &Result) { + llvm::report_fatal_error("Interpreter cannot return values"); +} + +//===----------------------------------------------------------------------===// +// Jmp, Jt, Jf +//===----------------------------------------------------------------------===// + +static bool Jmp(InterpState &S, CodePtr &PC, int32_t Offset) { + PC += Offset; + return true; +} + +static bool Jt(InterpState &S, CodePtr &PC, int32_t Offset) { + if (S.Stk.pop()) { + PC += Offset; + } + return true; +} + +static bool Jf(InterpState &S, CodePtr &PC, int32_t Offset) { + if (!S.Stk.pop()) { + PC += Offset; + } + return true; +} + +static bool CheckInitialized(InterpState &S, CodePtr OpPC, const Pointer &Ptr, + AccessKinds AK) { + if (Ptr.isInitialized()) + return true; + if (!S.checkingPotentialConstantExpression()) { + const SourceInfo &Loc = S.Current->getSource(OpPC); + S.FFDiag(Loc, diag::note_constexpr_access_uninit) << AK << false; + } + return false; +} + +static bool CheckActive(InterpState &S, CodePtr OpPC, const Pointer &Ptr, + AccessKinds AK) { + if (Ptr.isActive()) + return true; + + // Get the inactive field descriptor. + const FieldDecl *InactiveField = Ptr.getField(); + + // Walk up the pointer chain to find the union which is not active. + Pointer U = Ptr.getBase(); + while (!U.isActive()) { + U = U.getBase(); + } + + // Find the active field of the union. + Record *R = U.getRecord(); + assert(R && R->isUnion() && "Not a union"); + const FieldDecl *ActiveField = nullptr; + for (unsigned I = 0, N = R->getNumFields(); I < N; ++I) { + const Pointer &Field = U.atField(R->getField(I)->Offset); + if (Field.isActive()) { + ActiveField = Field.getField(); + break; + } + } + + const SourceInfo &Loc = S.Current->getSource(OpPC); + S.FFDiag(Loc, diag::note_constexpr_access_inactive_union_member) + << AK << InactiveField << !ActiveField << ActiveField; + return false; +} + +static bool CheckTemporary(InterpState &S, CodePtr OpPC, const Pointer &Ptr, + AccessKinds AK) { + if (auto ID = Ptr.getDeclID()) { + if (!Ptr.isStaticTemporary()) + return true; + + if (Ptr.getDeclDesc()->getType().isConstQualified()) + return true; + + if (S.P.getCurrentDecl() == ID) + return true; + + const SourceInfo &E = S.Current->getSource(OpPC); + S.FFDiag(E, diag::note_constexpr_access_static_temporary, 1) << AK; + S.Note(Ptr.getDeclLoc(), diag::note_constexpr_temporary_here); + return false; + } + return true; +} + +static bool CheckGlobal(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { + if (auto ID = Ptr.getDeclID()) { + if (!Ptr.isStatic()) + return true; + + if (S.P.getCurrentDecl() == ID) + return true; + + S.FFDiag(S.Current->getLocation(OpPC), diag::note_constexpr_modify_global); + return false; + } + return true; +} + +namespace clang { +namespace interp { + +bool CheckExtern(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { + if (!Ptr.isExtern()) + return true; + + if (!S.checkingPotentialConstantExpression()) { + auto *VD = Ptr.getDeclDesc()->asValueDecl(); + const SourceInfo &Loc = S.Current->getSource(OpPC); + S.FFDiag(Loc, diag::note_constexpr_ltor_non_constexpr, 1) << VD; + S.Note(VD->getLocation(), diag::note_declared_at); + } + return false; +} + +bool CheckArray(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { + if (!Ptr.isUnknownSizeArray()) + return true; + const SourceInfo &E = S.Current->getSource(OpPC); + S.FFDiag(E, diag::note_constexpr_unsized_array_indexed); + return false; +} + +bool CheckLive(InterpState &S, CodePtr OpPC, const Pointer &Ptr, + AccessKinds AK) { + const auto &Src = S.Current->getSource(OpPC); + if (Ptr.isZero()) { + + if (Ptr.isField()) + S.FFDiag(Src, diag::note_constexpr_null_subobject) << CSK_Field; + else + S.FFDiag(Src, diag::note_constexpr_access_null) << AK; + + return false; + } + + if (!Ptr.isLive()) { + bool IsTemp = Ptr.isTemporary(); + + S.FFDiag(Src, diag::note_constexpr_lifetime_ended, 1) << AK << !IsTemp; + + if (IsTemp) + S.Note(Ptr.getDeclLoc(), diag::note_constexpr_temporary_here); + else + S.Note(Ptr.getDeclLoc(), diag::note_declared_at); + + return false; + } + + return true; +} + +bool CheckNull(InterpState &S, CodePtr OpPC, const Pointer &Ptr, + CheckSubobjectKind CSK) { + if (!Ptr.isZero()) + return true; + const SourceInfo &Loc = S.Current->getSource(OpPC); + S.FFDiag(Loc, diag::note_constexpr_null_subobject) << CSK; + return false; +} + +bool CheckRange(InterpState &S, CodePtr OpPC, const Pointer &Ptr, + AccessKinds AK) { + if (!Ptr.isOnePastEnd()) + return true; + const SourceInfo &Loc = S.Current->getSource(OpPC); + S.FFDiag(Loc, diag::note_constexpr_access_past_end) << AK; + return false; +} + +bool CheckRange(InterpState &S, CodePtr OpPC, const Pointer &Ptr, + CheckSubobjectKind CSK) { + if (!Ptr.isElementPastEnd()) + return true; + const SourceInfo &Loc = S.Current->getSource(OpPC); + S.FFDiag(Loc, diag::note_constexpr_past_end_subobject) << CSK; + return false; +} + +bool CheckConst(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { + assert(Ptr.isLive() && "Pointer is not live"); + if (!Ptr.isConst()) { + return true; + } + + const QualType Ty = Ptr.getType(); + const SourceInfo &Loc = S.Current->getSource(OpPC); + S.FFDiag(Loc, diag::note_constexpr_modify_const_type) << Ty; + return false; +} + +bool CheckMutable(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { + assert(Ptr.isLive() && "Pointer is not live"); + if (!Ptr.isMutable()) { + return true; + } + + const SourceInfo &Loc = S.Current->getSource(OpPC); + const FieldDecl *Field = Ptr.getField(); + S.FFDiag(Loc, diag::note_constexpr_access_mutable, 1) << AK_Read << Field; + S.Note(Field->getLocation(), diag::note_declared_at); + return false; +} + +bool CheckLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { + if (!CheckLive(S, OpPC, Ptr, AK_Read)) + return false; + if (!CheckExtern(S, OpPC, Ptr)) + return false; + if (!CheckRange(S, OpPC, Ptr, AK_Read)) + return false; + if (!CheckInitialized(S, OpPC, Ptr, AK_Read)) + return false; + if (!CheckActive(S, OpPC, Ptr, AK_Read)) + return false; + if (!CheckTemporary(S, OpPC, Ptr, AK_Read)) + return false; + if (!CheckMutable(S, OpPC, Ptr)) + return false; + return true; +} + +bool CheckStore(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { + if (!CheckLive(S, OpPC, Ptr, AK_Assign)) + return false; + if (!CheckExtern(S, OpPC, Ptr)) + return false; + if (!CheckRange(S, OpPC, Ptr, AK_Assign)) + return false; + if (!CheckGlobal(S, OpPC, Ptr)) + return false; + if (!CheckConst(S, OpPC, Ptr)) + return false; + return true; +} + +bool CheckInvoke(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { + if (!CheckLive(S, OpPC, Ptr, AK_MemberCall)) + return false; + if (!CheckExtern(S, OpPC, Ptr)) + return false; + if (!CheckRange(S, OpPC, Ptr, AK_MemberCall)) + return false; + return true; +} + +bool CheckInit(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { + if (!CheckLive(S, OpPC, Ptr, AK_Assign)) + return false; + if (!CheckRange(S, OpPC, Ptr, AK_Assign)) + return false; + return true; +} + +bool CheckCallable(InterpState &S, CodePtr OpPC, Function *F) { + const SourceLocation &Loc = S.Current->getLocation(OpPC); + + if (F->isVirtual()) { + if (!S.getLangOpts().CPlusPlus2a) { + S.CCEDiag(Loc, diag::note_constexpr_virtual_call); + return false; + } + } + + if (!F->isConstexpr()) { + if (S.getLangOpts().CPlusPlus11) { + const FunctionDecl *DiagDecl = F->getDecl(); + + // If this function is not constexpr because it is an inherited + // non-constexpr constructor, diagnose that directly. + auto *CD = dyn_cast(DiagDecl); + if (CD && CD->isInheritingConstructor()) { + auto *Inherited = CD->getInheritedConstructor().getConstructor(); + if (!Inherited->isConstexpr()) + DiagDecl = CD = Inherited; + } + + // FIXME: If DiagDecl is an implicitly-declared special member function + // or an inheriting constructor, we should be much more explicit about why + // it's not constexpr. + if (CD && CD->isInheritingConstructor()) + S.FFDiag(Loc, diag::note_constexpr_invalid_inhctor, 1) + << CD->getInheritedConstructor().getConstructor()->getParent(); + else + S.FFDiag(Loc, diag::note_constexpr_invalid_function, 1) + << DiagDecl->isConstexpr() << (bool)CD << DiagDecl; + S.Note(DiagDecl->getLocation(), diag::note_declared_at); + } else { + S.FFDiag(Loc, diag::note_invalid_subexpr_in_const_expr); + } + return false; + } + + return true; +} + +bool CheckThis(InterpState &S, CodePtr OpPC, const Pointer &This) { + if (!This.isZero()) + return true; + + const SourceInfo &Loc = S.Current->getSource(OpPC); + + bool IsImplicit = false; + if (auto *E = dyn_cast_or_null(Loc.asExpr())) + IsImplicit = E->isImplicit(); + + if (S.getLangOpts().CPlusPlus11) + S.FFDiag(Loc, diag::note_constexpr_this) << IsImplicit; + else + S.FFDiag(Loc); + + return false; +} + +bool CheckPure(InterpState &S, CodePtr OpPC, const CXXMethodDecl *MD) { + if (!MD->isPure()) + return true; + const SourceInfo &E = S.Current->getSource(OpPC); + S.FFDiag(E, diag::note_constexpr_pure_virtual_call, 1) << MD; + S.Note(MD->getLocation(), diag::note_declared_at); + return false; +} +bool Interpret(InterpState &S, APValue &Result) { + CodePtr PC = S.Current->getPC(); + + for (;;) { + auto Op = PC.read(); + CodePtr OpPC = PC; + + switch (Op) { +#define GET_INTERP +#include "Opcodes.inc" +#undef GET_INTERP + } + } +} + +} // namespace interp +} // namespace clang diff --git a/lib/AST/Interp/Interp.h b/lib/AST/Interp/Interp.h new file mode 100644 index 0000000000000..8934efa13b9ce --- /dev/null +++ b/lib/AST/Interp/Interp.h @@ -0,0 +1,960 @@ +//===--- Interp.h - Interpreter for the constexpr VM ------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Definition of the interpreter state and entry point. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_AST_INTERP_INTERP_H +#define LLVM_CLANG_AST_INTERP_INTERP_H + +#include +#include +#include "Function.h" +#include "InterpFrame.h" +#include "InterpStack.h" +#include "InterpState.h" +#include "Opcode.h" +#include "PrimType.h" +#include "Program.h" +#include "State.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/ASTDiagnostic.h" +#include "clang/AST/CXXInheritance.h" +#include "clang/AST/Expr.h" +#include "llvm/ADT/APFloat.h" +#include "llvm/ADT/APSInt.h" +#include "llvm/Support/Endian.h" + +namespace clang { +namespace interp { + +using APInt = llvm::APInt; +using APSInt = llvm::APSInt; + +/// Convers a value to an APValue. +template bool ReturnValue(const T &V, APValue &R) { + R = V.toAPValue(); + return true; +} + +/// Checks if the variable has externally defined storage. +bool CheckExtern(InterpState &S, CodePtr OpPC, const Pointer &Ptr); + +/// Checks if the array is offsetable. +bool CheckArray(InterpState &S, CodePtr OpPC, const Pointer &Ptr); + +/// Checks if a pointer is live and accesible. +bool CheckLive(InterpState &S, CodePtr OpPC, const Pointer &Ptr, + AccessKinds AK); +/// Checks if a pointer is null. +bool CheckNull(InterpState &S, CodePtr OpPC, const Pointer &Ptr, + CheckSubobjectKind CSK); + +/// Checks if a pointer is in range. +bool CheckRange(InterpState &S, CodePtr OpPC, const Pointer &Ptr, + AccessKinds AK); + +/// Checks if a field from which a pointer is going to be derived is valid. +bool CheckRange(InterpState &S, CodePtr OpPC, const Pointer &Ptr, + CheckSubobjectKind CSK); + +/// Checks if a pointer points to const storage. +bool CheckConst(InterpState &S, CodePtr OpPC, const Pointer &Ptr); + +/// Checks if a pointer points to a mutable field. +bool CheckMutable(InterpState &S, CodePtr OpPC, const Pointer &Ptr); + +/// Checks if a value can be loaded from a block. +bool CheckLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr); + +/// Checks if a value can be stored in a block. +bool CheckStore(InterpState &S, CodePtr OpPC, const Pointer &Ptr); + +/// Checks if a method can be invoked on an object. +bool CheckInvoke(InterpState &S, CodePtr OpPC, const Pointer &Ptr); + +/// Checks if a value can be initialized. +bool CheckInit(InterpState &S, CodePtr OpPC, const Pointer &Ptr); + +/// Checks if a method can be called. +bool CheckCallable(InterpState &S, CodePtr OpPC, Function *F); + +/// Checks the 'this' pointer. +bool CheckThis(InterpState &S, CodePtr OpPC, const Pointer &This); + +/// Checks if a method is pure virtual. +bool CheckPure(InterpState &S, CodePtr OpPC, const CXXMethodDecl *MD); + +template inline bool IsTrue(const T &V) { return !V.isZero(); } + +//===----------------------------------------------------------------------===// +// Add, Sub, Mul +//===----------------------------------------------------------------------===// + +template class OpAP> +bool AddSubMulHelper(InterpState &S, CodePtr OpPC, unsigned Bits, const T &LHS, + const T &RHS) { + // Fast path - add the numbers with fixed width. + T Result; + if (!OpFW(LHS, RHS, Bits, &Result)) { + S.Stk.push(Result); + return true; + } + + // If for some reason evaluation continues, use the truncated results. + S.Stk.push(Result); + + // Slow path - compute the result using another bit of precision. + APSInt Value = OpAP()(LHS.toAPSInt(Bits), RHS.toAPSInt(Bits)); + + // Report undefined behaviour, stopping if required. + const Expr *E = S.Current->getExpr(OpPC); + QualType Type = E->getType(); + if (S.checkingForUndefinedBehavior()) { + auto Trunc = Value.trunc(Result.bitWidth()).toString(10); + auto Loc = E->getExprLoc(); + S.report(Loc, diag::warn_integer_constant_overflow) << Trunc << Type; + return true; + } else { + S.CCEDiag(E, diag::note_constexpr_overflow) << Value << Type; + return S.noteUndefinedBehavior(); + } +} + +template ::T> +bool Add(InterpState &S, CodePtr OpPC) { + const T &RHS = S.Stk.pop(); + const T &LHS = S.Stk.pop(); + const unsigned Bits = RHS.bitWidth() + 1; + return AddSubMulHelper(S, OpPC, Bits, LHS, RHS); +} + +template ::T> +bool Sub(InterpState &S, CodePtr OpPC) { + const T &RHS = S.Stk.pop(); + const T &LHS = S.Stk.pop(); + const unsigned Bits = RHS.bitWidth() + 1; + return AddSubMulHelper(S, OpPC, Bits, LHS, RHS); +} + +template ::T> +bool Mul(InterpState &S, CodePtr OpPC) { + const T &RHS = S.Stk.pop(); + const T &LHS = S.Stk.pop(); + const unsigned Bits = RHS.bitWidth() * 2; + return AddSubMulHelper(S, OpPC, Bits, LHS, RHS); +} + +//===----------------------------------------------------------------------===// +// EQ, NE, GT, GE, LT, LE +//===----------------------------------------------------------------------===// + +using CompareFn = llvm::function_ref; + +template +bool CmpHelper(InterpState &S, CodePtr OpPC, CompareFn Fn) { + using BoolT = PrimConv::T; + const T &RHS = S.Stk.pop(); + const T &LHS = S.Stk.pop(); + S.Stk.push(BoolT::from(Fn(LHS.compare(RHS)))); + return true; +} + +template +bool CmpHelperEQ(InterpState &S, CodePtr OpPC, CompareFn Fn) { + return CmpHelper(S, OpPC, Fn); +} + +template <> +inline bool CmpHelper(InterpState &S, CodePtr OpPC, CompareFn Fn) { + using BoolT = PrimConv::T; + const Pointer &RHS = S.Stk.pop(); + const Pointer &LHS = S.Stk.pop(); + + if (!Pointer::hasSameBase(LHS, RHS)) { + const SourceInfo &Loc = S.Current->getSource(OpPC); + S.FFDiag(Loc, diag::note_invalid_subexpr_in_const_expr); + return false; + } else { + unsigned VL = LHS.getByteOffset(); + unsigned VR = RHS.getByteOffset(); + S.Stk.push(BoolT::from(Fn(Compare(VL, VR)))); + return true; + } +} + +template <> +inline bool CmpHelperEQ(InterpState &S, CodePtr OpPC, CompareFn Fn) { + using BoolT = PrimConv::T; + const Pointer &RHS = S.Stk.pop(); + const Pointer &LHS = S.Stk.pop(); + + if (LHS.isZero() || RHS.isZero()) { + if (LHS.isZero() && RHS.isZero()) + S.Stk.push(BoolT::from(Fn(ComparisonCategoryResult::Equal))); + else + S.Stk.push(BoolT::from(Fn(ComparisonCategoryResult::Nonequal))); + return true; + } + + if (!Pointer::hasSameBase(LHS, RHS)) { + S.Stk.push(BoolT::from(Fn(ComparisonCategoryResult::Unordered))); + return true; + } else { + unsigned VL = LHS.getByteOffset(); + unsigned VR = RHS.getByteOffset(); + S.Stk.push(BoolT::from(Fn(Compare(VL, VR)))); + return true; + } +} + +template ::T> +bool EQ(InterpState &S, CodePtr OpPC) { + return CmpHelperEQ(S, OpPC, [](ComparisonCategoryResult R) { + return R == ComparisonCategoryResult::Equal; + }); +} + +template ::T> +bool NE(InterpState &S, CodePtr OpPC) { + return CmpHelperEQ(S, OpPC, [](ComparisonCategoryResult R) { + return R != ComparisonCategoryResult::Equal; + }); +} + +template ::T> +bool LT(InterpState &S, CodePtr OpPC) { + return CmpHelper(S, OpPC, [](ComparisonCategoryResult R) { + return R == ComparisonCategoryResult::Less; + }); +} + +template ::T> +bool LE(InterpState &S, CodePtr OpPC) { + return CmpHelper(S, OpPC, [](ComparisonCategoryResult R) { + return R == ComparisonCategoryResult::Less || + R == ComparisonCategoryResult::Equal; + }); +} + +template ::T> +bool GT(InterpState &S, CodePtr OpPC) { + return CmpHelper(S, OpPC, [](ComparisonCategoryResult R) { + return R == ComparisonCategoryResult::Greater; + }); +} + +template ::T> +bool GE(InterpState &S, CodePtr OpPC) { + return CmpHelper(S, OpPC, [](ComparisonCategoryResult R) { + return R == ComparisonCategoryResult::Greater || + R == ComparisonCategoryResult::Equal; + }); +} + +//===----------------------------------------------------------------------===// +// InRange +//===----------------------------------------------------------------------===// + +template ::T> +bool InRange(InterpState &S, CodePtr OpPC) { + const T RHS = S.Stk.pop(); + const T LHS = S.Stk.pop(); + const T Value = S.Stk.pop(); + + S.Stk.push(LHS <= Value && Value <= RHS); + return true; +} + +//===----------------------------------------------------------------------===// +// Dup, Pop, Test +//===----------------------------------------------------------------------===// + +template ::T> +bool Dup(InterpState &S, CodePtr OpPC) { + S.Stk.push(S.Stk.peek()); + return true; +} + +template ::T> +bool Pop(InterpState &S, CodePtr OpPC) { + S.Stk.pop(); + return true; +} + +//===----------------------------------------------------------------------===// +// Const +//===----------------------------------------------------------------------===// + +template ::T> +bool Const(InterpState &S, CodePtr OpPC, const T &Arg) { + S.Stk.push(Arg); + return true; +} + +//===----------------------------------------------------------------------===// +// Get/Set Local/Param/Global/This +//===----------------------------------------------------------------------===// + +template ::T> +bool GetLocal(InterpState &S, CodePtr OpPC, uint32_t I) { + S.Stk.push(S.Current->getLocal(I)); + return true; +} + +template ::T> +bool SetLocal(InterpState &S, CodePtr OpPC, uint32_t I) { + S.Current->setLocal(I, S.Stk.pop()); + return true; +} + +template ::T> +bool GetParam(InterpState &S, CodePtr OpPC, uint32_t I) { + if (S.checkingPotentialConstantExpression()) { + return false; + } + S.Stk.push(S.Current->getParam(I)); + return true; +} + +template ::T> +bool SetParam(InterpState &S, CodePtr OpPC, uint32_t I) { + S.Current->setParam(I, S.Stk.pop()); + return true; +} + +template ::T> +bool GetField(InterpState &S, CodePtr OpPC, uint32_t I) { + const Pointer &Obj = S.Stk.peek(); + if (!CheckNull(S, OpPC, Obj, CSK_Field)) + return false; + if (!CheckRange(S, OpPC, Obj, CSK_Field)) + return false; + const Pointer &Field = Obj.atField(I); + if (!CheckLoad(S, OpPC, Field)) + return false; + S.Stk.push(Field.deref()); + return true; +} + +template ::T> +bool SetField(InterpState &S, CodePtr OpPC, uint32_t I) { + const T &Value = S.Stk.pop(); + const Pointer &Obj = S.Stk.peek(); + if (!CheckNull(S, OpPC, Obj, CSK_Field)) + return false; + if (!CheckRange(S, OpPC, Obj, CSK_Field)) + return false; + const Pointer &Field = Obj.atField(I); + if (!CheckStore(S, OpPC, Field)) + return false; + Field.deref() = Value; + return true; +} + +template ::T> +bool GetFieldPop(InterpState &S, CodePtr OpPC, uint32_t I) { + const Pointer &Obj = S.Stk.pop(); + if (!CheckNull(S, OpPC, Obj, CSK_Field)) + return false; + if (!CheckRange(S, OpPC, Obj, CSK_Field)) + return false; + const Pointer &Field = Obj.atField(I); + if (!CheckLoad(S, OpPC, Field)) + return false; + S.Stk.push(Field.deref()); + return true; +} + +template ::T> +bool GetThisField(InterpState &S, CodePtr OpPC, uint32_t I) { + if (S.checkingPotentialConstantExpression()) + return false; + const Pointer &This = S.Current->getThis(); + if (!CheckThis(S, OpPC, This)) + return false; + const Pointer &Field = This.atField(I); + if (!CheckLoad(S, OpPC, Field)) + return false; + S.Stk.push(Field.deref()); + return true; +} + +template ::T> +bool SetThisField(InterpState &S, CodePtr OpPC, uint32_t I) { + if (S.checkingPotentialConstantExpression()) + return false; + const T &Value = S.Stk.pop(); + const Pointer &This = S.Current->getThis(); + if (!CheckThis(S, OpPC, This)) + return false; + const Pointer &Field = This.atField(I); + if (!CheckStore(S, OpPC, Field)) + return false; + Field.deref() = Value; + return true; +} + +template ::T> +bool GetGlobal(InterpState &S, CodePtr OpPC, uint32_t I) { + auto *B = S.P.getGlobal(I); + if (B->isExtern()) + return false; + S.Stk.push(B->deref()); + return true; +} + +template ::T> +bool SetGlobal(InterpState &S, CodePtr OpPC, uint32_t I) { + // TODO: emit warning. + return false; +} + +template ::T> +bool InitGlobal(InterpState &S, CodePtr OpPC, uint32_t I) { + S.P.getGlobal(I)->deref() = S.Stk.pop(); + return true; +} + +template ::T> +bool InitThisField(InterpState &S, CodePtr OpPC, uint32_t I) { + if (S.checkingPotentialConstantExpression()) + return false; + const Pointer &This = S.Current->getThis(); + if (!CheckThis(S, OpPC, This)) + return false; + const Pointer &Field = This.atField(I); + Field.deref() = S.Stk.pop(); + Field.initialize(); + return true; +} + +template ::T> +bool InitThisBitField(InterpState &S, CodePtr OpPC, const Record::Field *F) { + if (S.checkingPotentialConstantExpression()) + return false; + const Pointer &This = S.Current->getThis(); + if (!CheckThis(S, OpPC, This)) + return false; + const Pointer &Field = This.atField(F->Offset); + const auto &Value = S.Stk.pop(); + Field.deref() = Value.truncate(F->Decl->getBitWidthValue(S.getCtx())); + Field.initialize(); + return true; +} + +template ::T> +bool InitThisFieldActive(InterpState &S, CodePtr OpPC, uint32_t I) { + if (S.checkingPotentialConstantExpression()) + return false; + const Pointer &This = S.Current->getThis(); + if (!CheckThis(S, OpPC, This)) + return false; + const Pointer &Field = This.atField(I); + Field.deref() = S.Stk.pop(); + Field.activate(); + Field.initialize(); + return true; +} + +template ::T> +bool InitField(InterpState &S, CodePtr OpPC, uint32_t I) { + const T &Value = S.Stk.pop(); + const Pointer &Field = S.Stk.pop().atField(I); + Field.deref() = Value; + Field.activate(); + Field.initialize(); + return true; +} + +template ::T> +bool InitBitField(InterpState &S, CodePtr OpPC, const Record::Field *F) { + const T &Value = S.Stk.pop(); + const Pointer &Field = S.Stk.pop().atField(F->Offset); + Field.deref() = Value.truncate(F->Decl->getBitWidthValue(S.getCtx())); + Field.activate(); + Field.initialize(); + return true; +} + +template ::T> +bool InitFieldActive(InterpState &S, CodePtr OpPC, uint32_t I) { + const T &Value = S.Stk.pop(); + const Pointer &Ptr = S.Stk.pop(); + const Pointer &Field = Ptr.atField(I); + Field.deref() = Value; + Field.activate(); + Field.initialize(); + return true; +} + +//===----------------------------------------------------------------------===// +// GetPtr Local/Param/Global/Field/This +//===----------------------------------------------------------------------===// + +inline bool GetPtrLocal(InterpState &S, CodePtr OpPC, uint32_t I) { + S.Stk.push(S.Current->getLocalPointer(I)); + return true; +} + +inline bool GetPtrParam(InterpState &S, CodePtr OpPC, uint32_t I) { + if (S.checkingPotentialConstantExpression()) { + return false; + } + S.Stk.push(S.Current->getParamPointer(I)); + return true; +} + +inline bool GetPtrGlobal(InterpState &S, CodePtr OpPC, uint32_t I) { + S.Stk.push(S.P.getPtrGlobal(I)); + return true; +} + +inline bool GetPtrField(InterpState &S, CodePtr OpPC, uint32_t Off) { + const Pointer &Ptr = S.Stk.pop(); + if (!CheckNull(S, OpPC, Ptr, CSK_Field)) + return false; + if (!CheckExtern(S, OpPC, Ptr)) + return false; + if (!CheckRange(S, OpPC, Ptr, CSK_Field)) + return false; + S.Stk.push(Ptr.atField(Off)); + return true; +} + +inline bool GetPtrThisField(InterpState &S, CodePtr OpPC, uint32_t Off) { + if (S.checkingPotentialConstantExpression()) + return false; + const Pointer &This = S.Current->getThis(); + if (!CheckThis(S, OpPC, This)) + return false; + S.Stk.push(This.atField(Off)); + return true; +} + +inline bool GetPtrActiveField(InterpState &S, CodePtr OpPC, uint32_t Off) { + const Pointer &Ptr = S.Stk.pop(); + if (!CheckNull(S, OpPC, Ptr, CSK_Field)) + return false; + if (!CheckRange(S, OpPC, Ptr, CSK_Field)) + return false; + Pointer Field = Ptr.atField(Off); + Ptr.deactivate(); + Field.activate(); + S.Stk.push(std::move(Field)); + return true; +} + +inline bool GetPtrActiveThisField(InterpState &S, CodePtr OpPC, uint32_t Off) { + if (S.checkingPotentialConstantExpression()) + return false; + const Pointer &This = S.Current->getThis(); + if (!CheckThis(S, OpPC, This)) + return false; + Pointer Field = This.atField(Off); + This.deactivate(); + Field.activate(); + S.Stk.push(std::move(Field)); + return true; +} + +inline bool GetPtrBase(InterpState &S, CodePtr OpPC, uint32_t Off) { + const Pointer &Ptr = S.Stk.pop(); + if (!CheckNull(S, OpPC, Ptr, CSK_Base)) + return false; + S.Stk.push(Ptr.atField(Off)); + return true; +} + +inline bool GetPtrThisBase(InterpState &S, CodePtr OpPC, uint32_t Off) { + if (S.checkingPotentialConstantExpression()) + return false; + const Pointer &This = S.Current->getThis(); + if (!CheckThis(S, OpPC, This)) + return false; + S.Stk.push(This.atField(Off)); + return true; +} + +inline bool VirtBaseHelper(InterpState &S, CodePtr OpPC, const RecordDecl *Decl, + const Pointer &Ptr) { + Pointer Base = Ptr; + while (Base.isBaseClass()) + Base = Base.getBase(); + + auto *Field = Base.getRecord()->getVirtualBase(Decl); + S.Stk.push(Base.atField(Field->Offset)); + return true; +} + +inline bool GetPtrVirtBase(InterpState &S, CodePtr OpPC, const RecordDecl *D) { + const Pointer &Ptr = S.Stk.pop(); + if (!CheckNull(S, OpPC, Ptr, CSK_Base)) + return false; + return VirtBaseHelper(S, OpPC, D, Ptr); +} + +inline bool GetPtrThisVirtBase(InterpState &S, CodePtr OpPC, + const RecordDecl *D) { + if (S.checkingPotentialConstantExpression()) + return false; + const Pointer &This = S.Current->getThis(); + if (!CheckThis(S, OpPC, This)) + return false; + return VirtBaseHelper(S, OpPC, D, S.Current->getThis()); +} + +//===----------------------------------------------------------------------===// +// Load, Store, Init +//===----------------------------------------------------------------------===// + +template ::T> +bool Load(InterpState &S, CodePtr OpPC) { + const Pointer &Ptr = S.Stk.peek(); + if (!CheckLoad(S, OpPC, Ptr)) + return false; + S.Stk.push(Ptr.deref()); + return true; +} + +template ::T> +bool LoadPop(InterpState &S, CodePtr OpPC) { + const Pointer &Ptr = S.Stk.pop(); + if (!CheckLoad(S, OpPC, Ptr)) + return false; + S.Stk.push(Ptr.deref()); + return true; +} + +template ::T> +bool Store(InterpState &S, CodePtr OpPC) { + const T &Value = S.Stk.pop(); + const Pointer &Ptr = S.Stk.peek(); + if (!CheckStore(S, OpPC, Ptr)) + return false; + Ptr.deref() = Value; + return true; +} + +template ::T> +bool StorePop(InterpState &S, CodePtr OpPC) { + const T &Value = S.Stk.pop(); + const Pointer &Ptr = S.Stk.pop(); + if (!CheckStore(S, OpPC, Ptr)) + return false; + Ptr.deref() = Value; + return true; +} + +template ::T> +bool StoreBitField(InterpState &S, CodePtr OpPC) { + const T &Value = S.Stk.pop(); + const Pointer &Ptr = S.Stk.peek(); + if (!CheckStore(S, OpPC, Ptr)) + return false; + if (auto *FD = Ptr.getField()) { + Ptr.deref() = Value.truncate(FD->getBitWidthValue(S.getCtx())); + } else { + Ptr.deref() = Value; + } + return true; +} + +template ::T> +bool StoreBitFieldPop(InterpState &S, CodePtr OpPC) { + const T &Value = S.Stk.pop(); + const Pointer &Ptr = S.Stk.pop(); + if (!CheckStore(S, OpPC, Ptr)) + return false; + if (auto *FD = Ptr.getField()) { + Ptr.deref() = Value.truncate(FD->getBitWidthValue(S.getCtx())); + } else { + Ptr.deref() = Value; + } + return true; +} + +template ::T> +bool InitPop(InterpState &S, CodePtr OpPC) { + const T &Value = S.Stk.pop(); + const Pointer &Ptr = S.Stk.pop(); + if (!CheckInit(S, OpPC, Ptr)) + return false; + Ptr.initialize(); + new (&Ptr.deref()) T(Value); + return true; +} + +template ::T> +bool InitElem(InterpState &S, CodePtr OpPC, uint32_t Idx) { + const T &Value = S.Stk.pop(); + const Pointer &Ptr = S.Stk.peek().atIndex(Idx); + if (!CheckInit(S, OpPC, Ptr)) + return false; + Ptr.initialize(); + new (&Ptr.deref()) T(Value); + return true; +} + +template ::T> +bool InitElemPop(InterpState &S, CodePtr OpPC, uint32_t Idx) { + const T &Value = S.Stk.pop(); + const Pointer &Ptr = S.Stk.pop().atIndex(Idx); + if (!CheckInit(S, OpPC, Ptr)) + return false; + Ptr.initialize(); + new (&Ptr.deref()) T(Value); + return true; +} + +//===----------------------------------------------------------------------===// +// AddOffset, SubOffset +//===----------------------------------------------------------------------===// + +template bool OffsetHelper(InterpState &S, CodePtr OpPC) { + // Fetch the pointer and the offset. + const T &Offset = S.Stk.pop(); + const Pointer &Ptr = S.Stk.pop(); + if (!CheckNull(S, OpPC, Ptr, CSK_ArrayIndex)) + return false; + if (!CheckRange(S, OpPC, Ptr, CSK_ArrayToPointer)) + return false; + + // Get a version of the index comparable to the type. + T Index = T::from(Ptr.getIndex(), Offset.bitWidth()); + // A zero offset does not change the pointer, but in the case of an array + // it has to be adjusted to point to the first element instead of the array. + if (Offset.isZero()) { + S.Stk.push(Index.isZero() ? Ptr.atIndex(0) : Ptr); + return true; + } + // Arrays of unknown bounds cannot have pointers into them. + if (!CheckArray(S, OpPC, Ptr)) + return false; + + // Compute the largest index into the array. + unsigned MaxIndex = Ptr.getNumElems(); + + // Helper to report an invalid offset, computed as APSInt. + auto InvalidOffset = [&]() { + const unsigned Bits = Offset.bitWidth(); + APSInt APOffset(Offset.toAPSInt().extend(Bits + 2), false); + APSInt APIndex(Index.toAPSInt().extend(Bits + 2), false); + APSInt NewIndex = Add ? (APIndex + APOffset) : (APIndex - APOffset); + S.CCEDiag(S.Current->getSource(OpPC), diag::note_constexpr_array_index) + << NewIndex + << /*array*/ static_cast(!Ptr.inArray()) + << static_cast(MaxIndex); + return false; + }; + + // If the new offset would be negative, bail out. + if (Add && Offset.isNegative() && (Offset.isMin() || -Offset > Index)) + return InvalidOffset(); + if (!Add && Offset.isPositive() && Index < Offset) + return InvalidOffset(); + + // If the new offset would be out of bounds, bail out. + unsigned MaxOffset = MaxIndex - Ptr.getIndex(); + if (Add && Offset.isPositive() && Offset > MaxOffset) + return InvalidOffset(); + if (!Add && Offset.isNegative() && (Offset.isMin() || -Offset > MaxOffset)) + return InvalidOffset(); + + // Offset is valid - compute it on unsigned. + int64_t WideIndex = static_cast(Index); + int64_t WideOffset = static_cast(Offset); + int64_t Result = Add ? (WideIndex + WideOffset) : (WideIndex - WideOffset); + S.Stk.push(Ptr.atIndex(static_cast(Result))); + return true; +} + +template ::T> +bool AddOffset(InterpState &S, CodePtr OpPC) { + return OffsetHelper(S, OpPC); +} + +template ::T> +bool SubOffset(InterpState &S, CodePtr OpPC) { + return OffsetHelper(S, OpPC); +} + + +//===----------------------------------------------------------------------===// +// Destroy +//===----------------------------------------------------------------------===// + +inline bool Destroy(InterpState &S, CodePtr OpPC, uint32_t I) { + S.Current->destroy(I); + return true; +} + +//===----------------------------------------------------------------------===// +// Cast, CastFP +//===----------------------------------------------------------------------===// + +template bool Cast(InterpState &S, CodePtr OpPC) { + using T = typename PrimConv::T; + using U = typename PrimConv::T; + S.Stk.push(U::from(S.Stk.pop())); + return true; +} + +//===----------------------------------------------------------------------===// +// Zero, Nullptr +//===----------------------------------------------------------------------===// + +template ::T> +bool Zero(InterpState &S, CodePtr OpPC) { + S.Stk.push(T::zero()); + return true; +} + +template ::T> +inline bool Null(InterpState &S, CodePtr OpPC) { + S.Stk.push(); + return true; +} + +//===----------------------------------------------------------------------===// +// This, ImplicitThis +//===----------------------------------------------------------------------===// + +inline bool This(InterpState &S, CodePtr OpPC) { + // Cannot read 'this' in this mode. + if (S.checkingPotentialConstantExpression()) { + return false; + } + + const Pointer &This = S.Current->getThis(); + if (!CheckThis(S, OpPC, This)) + return false; + + S.Stk.push(This); + return true; +} + +//===----------------------------------------------------------------------===// +// Shr, Shl +//===----------------------------------------------------------------------===// + +template ::T> +unsigned Trunc(InterpState &S, CodePtr OpPC, unsigned Bits, const T &V) { + // C++11 [expr.shift]p1: Shift width must be less than the bit width of + // the shifted type. + if (Bits > 1 && V >= T::from(Bits, V.bitWidth())) { + const Expr *E = S.Current->getExpr(OpPC); + const APSInt Val = V.toAPSInt(); + QualType Ty = E->getType(); + S.CCEDiag(E, diag::note_constexpr_large_shift) << Val << Ty << Bits; + return Bits; + } else { + return static_cast(V); + } +} + +template ::T> +inline bool ShiftRight(InterpState &S, CodePtr OpPC, const T &V, unsigned RHS) { + if (RHS >= V.bitWidth()) { + S.Stk.push(T::from(0, V.bitWidth())); + } else { + S.Stk.push(T::from(V >> RHS, V.bitWidth())); + } + return true; +} + +template ::T> +inline bool ShiftLeft(InterpState &S, CodePtr OpPC, const T &V, unsigned RHS) { + if (V.isSigned() && !S.getLangOpts().CPlusPlus2a) { + // C++11 [expr.shift]p2: A signed left shift must have a non-negative + // operand, and must not overflow the corresponding unsigned type. + // C++2a [expr.shift]p2: E1 << E2 is the unique value congruent to + // E1 x 2^E2 module 2^N. + if (V.isNegative()) { + const Expr *E = S.Current->getExpr(OpPC); + S.CCEDiag(E, diag::note_constexpr_lshift_of_negative) << V.toAPSInt(); + } else if (V.countLeadingZeros() < RHS) { + S.CCEDiag(S.Current->getExpr(OpPC), diag::note_constexpr_lshift_discards); + } + } + + if (V.bitWidth() == 1) { + S.Stk.push(V); + } else if (RHS >= V.bitWidth()) { + S.Stk.push(T::from(0, V.bitWidth())); + } else { + S.Stk.push(T::from(V.toUnsigned() << RHS, V.bitWidth())); + } + return true; +} + +template +inline bool Shr(InterpState &S, CodePtr OpPC) { + const auto &RHS = S.Stk.pop::T>(); + const auto &LHS = S.Stk.pop::T>(); + const unsigned Bits = LHS.bitWidth(); + + if (RHS.isSigned() && RHS.isNegative()) { + const SourceInfo &Loc = S.Current->getSource(OpPC); + S.CCEDiag(Loc, diag::note_constexpr_negative_shift) << RHS.toAPSInt(); + return ShiftLeft(S, OpPC, LHS, Trunc(S, OpPC, Bits, -RHS)); + } else { + return ShiftRight(S, OpPC, LHS, Trunc(S, OpPC, Bits, RHS)); + } +} + +template +inline bool Shl(InterpState &S, CodePtr OpPC) { + const auto &RHS = S.Stk.pop::T>(); + const auto &LHS = S.Stk.pop::T>(); + const unsigned Bits = LHS.bitWidth(); + + if (RHS.isSigned() && RHS.isNegative()) { + const SourceInfo &Loc = S.Current->getSource(OpPC); + S.CCEDiag(Loc, diag::note_constexpr_negative_shift) << RHS.toAPSInt(); + return ShiftRight(S, OpPC, LHS, Trunc(S, OpPC, Bits, -RHS)); + } else { + return ShiftLeft(S, OpPC, LHS, Trunc(S, OpPC, Bits, RHS)); + } +} + +//===----------------------------------------------------------------------===// +// NoRet +//===----------------------------------------------------------------------===// + +inline bool NoRet(InterpState &S, CodePtr OpPC) { + SourceLocation EndLoc = S.Current->getCallee()->getEndLoc(); + S.FFDiag(EndLoc, diag::note_constexpr_no_return); + return false; +} + +//===----------------------------------------------------------------------===// +// NarrowPtr, ExpandPtr +//===----------------------------------------------------------------------===// + +inline bool NarrowPtr(InterpState &S, CodePtr OpPC) { + const Pointer &Ptr = S.Stk.pop(); + S.Stk.push(Ptr.narrow()); + return true; +} + +inline bool ExpandPtr(InterpState &S, CodePtr OpPC) { + const Pointer &Ptr = S.Stk.pop(); + S.Stk.push(Ptr.expand()); + return true; +} + +/// Interpreter entry point. +bool Interpret(InterpState &S, APValue &Result); + +} // namespace interp +} // namespace clang + +#endif diff --git a/lib/AST/Interp/InterpFrame.cpp b/lib/AST/Interp/InterpFrame.cpp new file mode 100644 index 0000000000000..9d01bf0333fe1 --- /dev/null +++ b/lib/AST/Interp/InterpFrame.cpp @@ -0,0 +1,193 @@ +//===--- InterpFrame.cpp - Call Frame implementation for the VM -*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "InterpFrame.h" +#include "Function.h" +#include "Interp.h" +#include "InterpStack.h" +#include "PrimType.h" +#include "Program.h" +#include "clang/AST/DeclCXX.h" + +using namespace clang; +using namespace clang::interp; + +InterpFrame::InterpFrame(InterpState &S, Function *Func, InterpFrame *Caller, + CodePtr RetPC, Pointer &&This) + : Caller(Caller), S(S), Func(Func), This(std::move(This)), RetPC(RetPC), + ArgSize(Func ? Func->getArgSize() : 0), + Args(static_cast(S.Stk.top())), FrameOffset(S.Stk.size()) { + if (Func) { + if (unsigned FrameSize = Func->getFrameSize()) { + Locals = std::make_unique(FrameSize); + for (auto &Scope : Func->scopes()) { + for (auto &Local : Scope.locals()) { + Block *B = new (localBlock(Local.Offset)) Block(Local.Desc); + B->invokeCtor(); + } + } + } + } +} + +InterpFrame::~InterpFrame() { + if (Func && Func->isConstructor() && This.isBaseClass()) + This.initialize(); + for (auto &Param : Params) + S.deallocate(reinterpret_cast(Param.second.get())); +} + +void InterpFrame::destroy(unsigned Idx) { + for (auto &Local : Func->getScope(Idx).locals()) { + S.deallocate(reinterpret_cast(localBlock(Local.Offset))); + } +} + +void InterpFrame::popArgs() { + for (PrimType Ty : Func->args_reverse()) + TYPE_SWITCH(Ty, S.Stk.discard()); +} + +template +static void print(llvm::raw_ostream &OS, const T &V, ASTContext &, QualType) { + OS << V; +} + +template <> +void print(llvm::raw_ostream &OS, const Pointer &P, ASTContext &Ctx, + QualType Ty) { + if (P.isZero()) { + OS << "nullptr"; + return; + } + + auto printDesc = [&OS, &Ctx](Descriptor *Desc) { + if (auto *D = Desc->asDecl()) { + // Subfields or named values. + if (auto *VD = dyn_cast(D)) { + OS << *VD; + return; + } + // Base classes. + if (isa(D)) { + return; + } + } + // Temporary expression. + if (auto *E = Desc->asExpr()) { + E->printPretty(OS, nullptr, Ctx.getPrintingPolicy()); + return; + } + llvm_unreachable("Invalid descriptor type"); + }; + + if (!Ty->isReferenceType()) + OS << "&"; + llvm::SmallVector Levels; + for (Pointer F = P; !F.isRoot(); ) { + Levels.push_back(F); + F = F.isArrayElement() ? F.getArray().expand() : F.getBase(); + } + + printDesc(P.getDeclDesc()); + for (auto It = Levels.rbegin(); It != Levels.rend(); ++It) { + if (It->inArray()) { + OS << "[" << It->expand().getIndex() << "]"; + continue; + } + if (auto Index = It->getIndex()) { + OS << " + " << Index; + continue; + } + OS << "."; + printDesc(It->getFieldDesc()); + } +} + +void InterpFrame::describe(llvm::raw_ostream &OS) { + const FunctionDecl *F = getCallee(); + auto *M = dyn_cast(F); + if (M && M->isInstance() && !isa(F)) { + print(OS, This, S.getCtx(), S.getCtx().getRecordType(M->getParent())); + OS << "->"; + } + OS << *F << "("; + unsigned Off = Func->hasRVO() ? primSize(PT_Ptr) : 0; + for (unsigned I = 0, N = F->getNumParams(); I < N; ++I) { + QualType Ty = F->getParamDecl(I)->getType(); + + PrimType PrimTy; + if (llvm::Optional T = S.Ctx.classify(Ty)) { + PrimTy = *T; + } else { + PrimTy = PT_Ptr; + } + + TYPE_SWITCH(PrimTy, print(OS, stackRef(Off), S.getCtx(), Ty)); + Off += align(primSize(PrimTy)); + if (I + 1 != N) + OS << ", "; + } + OS << ")"; +} + +Frame *InterpFrame::getCaller() const { + if (Caller->Caller) + return Caller; + return S.getSplitFrame(); +} + +SourceLocation InterpFrame::getCallLocation() const { + if (!Caller->Func) + return S.getLocation(nullptr, {}); + return S.getLocation(Caller->Func, RetPC - sizeof(uintptr_t)); +} + +const FunctionDecl *InterpFrame::getCallee() const { + return Func->getDecl(); +} + +Pointer InterpFrame::getLocalPointer(unsigned Offset) { + assert(Offset < Func->getFrameSize() && "Invalid local offset."); + return Pointer( + reinterpret_cast(Locals.get() + Offset - sizeof(Block))); +} + +Pointer InterpFrame::getParamPointer(unsigned Off) { + // Return the block if it was created previously. + auto Pt = Params.find(Off); + if (Pt != Params.end()) { + return Pointer(reinterpret_cast(Pt->second.get())); + } + + // Allocate memory to store the parameter and the block metadata. + const auto &Desc = Func->getParamDescriptor(Off); + size_t BlockSize = sizeof(Block) + Desc.second->getAllocSize(); + auto Memory = std::make_unique(BlockSize); + auto *B = new (Memory.get()) Block(Desc.second); + + // Copy the initial value. + TYPE_SWITCH(Desc.first, new (B->data()) T(stackRef(Off))); + + // Record the param. + Params.insert({Off, std::move(Memory)}); + return Pointer(B); +} + +SourceInfo InterpFrame::getSource(CodePtr PC) const { + return S.getSource(Func, PC); +} + +const Expr *InterpFrame::getExpr(CodePtr PC) const { + return S.getExpr(Func, PC); +} + +SourceLocation InterpFrame::getLocation(CodePtr PC) const { + return S.getLocation(Func, PC); +} + diff --git a/lib/AST/Interp/InterpFrame.h b/lib/AST/Interp/InterpFrame.h new file mode 100644 index 0000000000000..b8391b0bcf92c --- /dev/null +++ b/lib/AST/Interp/InterpFrame.h @@ -0,0 +1,153 @@ +//===--- InterpFrame.h - Call Frame implementation for the VM ---*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Defines the class storing information about stack frames in the interpreter. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_AST_INTERP_INTERPFRAME_H +#define LLVM_CLANG_AST_INTERP_INTERPFRAME_H + +#include "Frame.h" +#include "Pointer.h" +#include "Program.h" +#include "State.h" +#include +#include + +namespace clang { +namespace interp { +class Function; +class InterpState; + +/// Frame storing local variables. +class InterpFrame final : public Frame { +public: + /// The frame of the previous function. + InterpFrame *Caller; + + /// Creates a new frame for a method call. + InterpFrame(InterpState &S, Function *Func, InterpFrame *Caller, + CodePtr RetPC, Pointer &&This); + + /// Destroys the frame, killing all live pointers to stack slots. + ~InterpFrame(); + + /// Invokes the destructors for a scope. + void destroy(unsigned Idx); + + /// Pops the arguments off the stack. + void popArgs(); + + /// Describes the frame with arguments for diagnostic purposes. + void describe(llvm::raw_ostream &OS); + + /// Returns the parent frame object. + Frame *getCaller() const; + + /// Returns the location of the call to the frame. + SourceLocation getCallLocation() const; + + /// Returns the caller. + const FunctionDecl *getCallee() const; + + /// Returns the current function. + Function *getFunction() const { return Func; } + + /// Returns the offset on the stack at which the frame starts. + size_t getFrameOffset() const { return FrameOffset; } + + /// Returns the value of a local variable. + template const T &getLocal(unsigned Offset) { + return localRef(Offset); + } + + /// Mutates a local variable. + template void setLocal(unsigned Offset, const T &Value) { + localRef(Offset) = Value; + } + + /// Returns a pointer to a local variables. + Pointer getLocalPointer(unsigned Offset); + + /// Returns the value of an argument. + template const T &getParam(unsigned Offset) { + auto Pt = Params.find(Offset); + if (Pt == Params.end()) { + return stackRef(Offset); + } else { + return Pointer(reinterpret_cast(Pt->second.get())).deref(); + } + } + + /// Mutates a local copy of a parameter. + template void setParam(unsigned Offset, const T &Value) { + getParamPointer(Offset).deref() = Value; + } + + /// Returns a pointer to an argument - lazily creates a block. + Pointer getParamPointer(unsigned Offset); + + /// Returns the 'this' pointer. + const Pointer &getThis() const { return This; } + + /// Checks if the frame is a root frame - return should quit the interpreter. + bool isRoot() const { return !Func; } + + /// Returns the PC of the frame's code start. + CodePtr getPC() const { return Func->getCodeBegin(); } + + /// Returns the return address of the frame. + CodePtr getRetPC() const { return RetPC; } + + /// Map a location to a source. + virtual SourceInfo getSource(CodePtr PC) const; + const Expr *getExpr(CodePtr PC) const; + SourceLocation getLocation(CodePtr PC) const; + +private: + /// Returns an original argument from the stack. + template const T &stackRef(unsigned Offset) { + return *reinterpret_cast(Args - ArgSize + Offset); + } + + /// Returns an offset to a local. + template T &localRef(unsigned Offset) { + return *reinterpret_cast(Locals.get() + Offset); + } + + /// Returns a pointer to a local's block. + void *localBlock(unsigned Offset) { + return Locals.get() + Offset - sizeof(Block); + } + +private: + /// Reference to the interpreter state. + InterpState &S; + /// Reference to the function being executed. + Function *Func; + /// Current object pointer for methods. + Pointer This; + /// Return address. + CodePtr RetPC; + /// The size of all the arguments. + const unsigned ArgSize; + /// Pointer to the arguments in the callee's frame. + char *Args = nullptr; + /// Fixed, initial storage for known local variables. + std::unique_ptr Locals; + /// Offset on the stack at entry. + const size_t FrameOffset; + /// Mapping from arg offsets to their argument blocks. + llvm::DenseMap> Params; +}; + +} // namespace interp +} // namespace clang + +#endif diff --git a/lib/AST/Interp/InterpStack.cpp b/lib/AST/Interp/InterpStack.cpp new file mode 100644 index 0000000000000..5c803f3d94244 --- /dev/null +++ b/lib/AST/Interp/InterpStack.cpp @@ -0,0 +1,78 @@ +//===--- InterpStack.cpp - Stack implementation for the VM ------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include +#include +#include "InterpStack.h" + +using namespace clang; +using namespace clang::interp; + +InterpStack::~InterpStack() { + clear(); +} + +void InterpStack::clear() { + if (Chunk && Chunk->Next) + free(Chunk->Next); + if (Chunk) + free(Chunk); + Chunk = nullptr; + StackSize = 0; +} + +void *InterpStack::grow(size_t Size) { + assert(Size < ChunkSize - sizeof(StackChunk) && "Object too large"); + + if (!Chunk || sizeof(StackChunk) + Chunk->size() + Size > ChunkSize) { + if (Chunk && Chunk->Next) { + Chunk = Chunk->Next; + } else { + StackChunk *Next = new (malloc(ChunkSize)) StackChunk(Chunk); + if (Chunk) + Chunk->Next = Next; + Chunk = Next; + } + } + + auto *Object = reinterpret_cast(Chunk->End); + Chunk->End += Size; + StackSize += Size; + return Object; +} + +void *InterpStack::peek(size_t Size) { + assert(Chunk && "Stack is empty!"); + + StackChunk *Ptr = Chunk; + while (Size > Ptr->size()) { + Size -= Ptr->size(); + Ptr = Ptr->Prev; + assert(Ptr && "Offset too large"); + } + + return reinterpret_cast(Ptr->End - Size); +} + +void InterpStack::shrink(size_t Size) { + assert(Chunk && "Chunk is empty!"); + + while (Size > Chunk->size()) { + Size -= Chunk->size(); + if (Chunk->Next) { + free(Chunk->Next); + Chunk->Next = nullptr; + } + Chunk->End = Chunk->start(); + Chunk = Chunk->Prev; + assert(Chunk && "Offset too large"); + } + + Chunk->End -= Size; + StackSize -= Size; +} diff --git a/lib/AST/Interp/InterpStack.h b/lib/AST/Interp/InterpStack.h new file mode 100644 index 0000000000000..127adb6b8ebab --- /dev/null +++ b/lib/AST/Interp/InterpStack.h @@ -0,0 +1,113 @@ +//===--- InterpStack.h - Stack implementation for the VM --------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Defines the upwards-growing stack used by the interpreter. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_AST_INTERP_INTERPSTACK_H +#define LLVM_CLANG_AST_INTERP_INTERPSTACK_H + +#include + +namespace clang { +namespace interp { + +/// Stack frame storing temporaries and parameters. +class InterpStack final { +public: + InterpStack() {} + + /// Destroys the stack, freeing up storage. + ~InterpStack(); + + /// Constructs a value in place on the top of the stack. + template void push(Tys &&... Args) { + new (grow(aligned_size())) T(std::forward(Args)...); + } + + /// Returns the value from the top of the stack and removes it. + template T pop() { + auto *Ptr = &peek(); + auto Value = std::move(*Ptr); + Ptr->~T(); + shrink(aligned_size()); + return Value; + } + + /// Discards the top value from the stack. + template void discard() { + auto *Ptr = &peek(); + Ptr->~T(); + shrink(aligned_size()); + } + + /// Returns a reference to the value on the top of the stack. + template T &peek() { + return *reinterpret_cast(peek(aligned_size())); + } + + /// Returns a pointer to the top object. + void *top() { return Chunk ? peek(0) : nullptr; } + + /// Returns the size of the stack in bytes. + size_t size() const { return StackSize; } + + /// Clears the stack without calling any destructors. + void clear(); + +private: + /// All stack slots are aligned to the native pointer alignment for storage. + /// The size of an object is rounded up to a pointer alignment multiple. + template constexpr size_t aligned_size() const { + constexpr size_t PtrAlign = alignof(void *); + return ((sizeof(T) + PtrAlign - 1) / PtrAlign) * PtrAlign; + } + + /// Grows the stack to accomodate a value and returns a pointer to it. + void *grow(size_t Size); + /// Returns a pointer from the top of the stack. + void *peek(size_t Size); + /// Shrinks the stack. + void shrink(size_t Size); + + /// Allocate stack space in 1Mb chunks. + static constexpr size_t ChunkSize = 1024 * 1024; + + /// Metadata for each stack chunk. + /// + /// The stack is composed of a linked list of chunks. Whenever an allocation + /// is out of bounds, a new chunk is linked. When a chunk becomes empty, + /// it is not immediately freed: a chunk is deallocated only when the + /// predecessor becomes empty. + struct StackChunk { + StackChunk *Next; + StackChunk *Prev; + char *End; + + StackChunk(StackChunk *Prev = nullptr) + : Next(nullptr), Prev(Prev), End(reinterpret_cast(this + 1)) {} + + /// Returns the size of the chunk, minus the header. + size_t size() { return End - start(); } + + /// Returns a pointer to the start of the data region. + char *start() { return reinterpret_cast(this + 1); } + }; + static_assert(sizeof(StackChunk) < ChunkSize, "Invalid chunk size"); + + /// First chunk on the stack. + StackChunk *Chunk = nullptr; + /// Total size of the stack. + size_t StackSize = 0; +}; + +} // namespace interp +} // namespace clang + +#endif diff --git a/lib/AST/Interp/InterpState.cpp b/lib/AST/Interp/InterpState.cpp new file mode 100644 index 0000000000000..25684f3c09397 --- /dev/null +++ b/lib/AST/Interp/InterpState.cpp @@ -0,0 +1,74 @@ +//===--- InterpState.cpp - Interpreter for the constexpr VM -----*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "InterpState.h" +#include +#include "Function.h" +#include "InterpFrame.h" +#include "InterpStack.h" +#include "Opcode.h" +#include "PrimType.h" +#include "Program.h" +#include "State.h" + +using namespace clang; +using namespace clang::interp; + +using APSInt = llvm::APSInt; + +InterpState::InterpState(State &Parent, Program &P, InterpStack &Stk, + Context &Ctx, SourceMapper *M) + : Parent(Parent), M(M), P(P), Stk(Stk), Ctx(Ctx), Current(nullptr), + CallStackDepth(Parent.getCallStackDepth() + 1) {} + +InterpState::~InterpState() { + while (Current) { + InterpFrame *Next = Current->Caller; + delete Current; + Current = Next; + } + + while (DeadBlocks) { + DeadBlock *Next = DeadBlocks->Next; + free(DeadBlocks); + DeadBlocks = Next; + } +} + +Frame *InterpState::getCurrentFrame() { + if (Current && Current->Caller) { + return Current; + } else { + return Parent.getCurrentFrame(); + } +} + +bool InterpState::reportOverflow(const Expr *E, const llvm::APSInt &Value) { + QualType Type = E->getType(); + CCEDiag(E, diag::note_constexpr_overflow) << Value << Type; + return noteUndefinedBehavior(); +} + +void InterpState::deallocate(Block *B) { + Descriptor *Desc = B->getDescriptor(); + if (B->hasPointers()) { + size_t Size = B->getSize(); + + // Allocate a new block, transferring over pointers. + char *Memory = reinterpret_cast(malloc(sizeof(DeadBlock) + Size)); + auto *D = new (Memory) DeadBlock(DeadBlocks, B); + + // Move data from one block to another. + if (Desc->MoveFn) + Desc->MoveFn(B, B->data(), D->data(), Desc); + } else { + // Free storage, if necessary. + if (Desc->DtorFn) + Desc->DtorFn(B, B->data(), Desc); + } +} diff --git a/lib/AST/Interp/InterpState.h b/lib/AST/Interp/InterpState.h new file mode 100644 index 0000000000000..c2209bbcbb925 --- /dev/null +++ b/lib/AST/Interp/InterpState.h @@ -0,0 +1,112 @@ +//===--- InterpState.h - Interpreter state for the constexpr VM -*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Definition of the interpreter state and entry point. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_AST_INTERP_INTERPSTATE_H +#define LLVM_CLANG_AST_INTERP_INTERPSTATE_H + +#include "Context.h" +#include "Function.h" +#include "InterpStack.h" +#include "State.h" +#include "clang/AST/APValue.h" +#include "clang/AST/ASTDiagnostic.h" +#include "clang/AST/Expr.h" +#include "clang/AST/OptionalDiagnostic.h" + +namespace clang { +namespace interp { +class Context; +class Function; +class InterpStack; +class InterpFrame; +class SourceMapper; + +/// Interpreter context. +class InterpState final : public State, public SourceMapper { +public: + InterpState(State &Parent, Program &P, InterpStack &Stk, Context &Ctx, + SourceMapper *M = nullptr); + + ~InterpState(); + + // Stack frame accessors. + Frame *getSplitFrame() { return Parent.getCurrentFrame(); } + Frame *getCurrentFrame() override; + unsigned getCallStackDepth() override { return CallStackDepth; } + const Frame *getBottomFrame() const override { + return Parent.getBottomFrame(); + } + + // Acces objects from the walker context. + Expr::EvalStatus &getEvalStatus() const override { + return Parent.getEvalStatus(); + } + ASTContext &getCtx() const override { return Parent.getCtx(); } + + // Forward status checks and updates to the walker. + bool checkingForUndefinedBehavior() const override { + return Parent.checkingForUndefinedBehavior(); + } + bool keepEvaluatingAfterFailure() const override { + return Parent.keepEvaluatingAfterFailure(); + } + bool checkingPotentialConstantExpression() const override { + return Parent.checkingPotentialConstantExpression(); + } + bool noteUndefinedBehavior() override { + return Parent.noteUndefinedBehavior(); + } + bool hasActiveDiagnostic() override { return Parent.hasActiveDiagnostic(); } + void setActiveDiagnostic(bool Flag) override { + Parent.setActiveDiagnostic(Flag); + } + void setFoldFailureDiagnostic(bool Flag) override { + Parent.setFoldFailureDiagnostic(Flag); + } + bool hasPriorDiagnostic() override { return Parent.hasPriorDiagnostic(); } + + /// Reports overflow and return true if evaluation should continue. + bool reportOverflow(const Expr *E, const llvm::APSInt &Value); + + /// Deallocates a pointer. + void deallocate(Block *B); + + /// Delegates source mapping to the mapper. + SourceInfo getSource(Function *F, CodePtr PC) const override { + return M ? M->getSource(F, PC) : F->getSource(PC); + } + +private: + /// AST Walker state. + State &Parent; + /// Dead block chain. + DeadBlock *DeadBlocks = nullptr; + /// Reference to the offset-source mapping. + SourceMapper *M; + +public: + /// Reference to the module containing all bytecode. + Program &P; + /// Temporary stack. + InterpStack &Stk; + /// Interpreter Context. + Context &Ctx; + /// The current frame. + InterpFrame *Current = nullptr; + /// Call stack depth. + unsigned CallStackDepth; +}; + +} // namespace interp +} // namespace clang + +#endif diff --git a/lib/AST/Interp/Opcode.h b/lib/AST/Interp/Opcode.h new file mode 100644 index 0000000000000..d2daa1ea52ac1 --- /dev/null +++ b/lib/AST/Interp/Opcode.h @@ -0,0 +1,30 @@ +//===--- Opcode.h - Opcodes for the constexpr VM ----------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Defines all opcodes executed by the VM and emitted by the compiler. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_AST_INTERP_OPCODE_H +#define LLVM_CLANG_AST_INTERP_OPCODE_H + +#include + +namespace clang { +namespace interp { + +enum Opcode : uint32_t { +#define GET_OPCODE_NAMES +#include "Opcodes.inc" +#undef GET_OPCODE_NAMES +}; + +} // namespace interp +} // namespace clang + +#endif diff --git a/lib/AST/Interp/Opcodes.td b/lib/AST/Interp/Opcodes.td new file mode 100644 index 0000000000000..4aba5f5cd83cd --- /dev/null +++ b/lib/AST/Interp/Opcodes.td @@ -0,0 +1,422 @@ +//===--- Opcodes.td - Opcode defitions for the constexpr VM -----*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Helper file used to generate opcodes, the interpreter and the disassembler. +// +//===----------------------------------------------------------------------===// + + +//===----------------------------------------------------------------------===// +// Types evaluated by the interpreter. +//===----------------------------------------------------------------------===// + +class Type; +def Bool : Type; +def Sint8 : Type; +def Uint8 : Type; +def Sint16 : Type; +def Uint16 : Type; +def Sint32 : Type; +def Uint32 : Type; +def Sint64 : Type; +def Uint64 : Type; +def Ptr : Type; + +//===----------------------------------------------------------------------===// +// Types transferred to the interpreter. +//===----------------------------------------------------------------------===// + +class ArgType { string Name = ?; } +def ArgSint8 : ArgType { let Name = "int8_t"; } +def ArgUint8 : ArgType { let Name = "uint8_t"; } +def ArgSint16 : ArgType { let Name = "int16_t"; } +def ArgUint16 : ArgType { let Name = "uint16_t"; } +def ArgSint32 : ArgType { let Name = "int32_t"; } +def ArgUint32 : ArgType { let Name = "uint32_t"; } +def ArgSint64 : ArgType { let Name = "int64_t"; } +def ArgUint64 : ArgType { let Name = "uint64_t"; } +def ArgBool : ArgType { let Name = "bool"; } + +def ArgFunction : ArgType { let Name = "Function *"; } +def ArgRecord : ArgType { let Name = "Record *"; } + +def ArgSema : ArgType { let Name = "const fltSemantics *"; } + +def ArgExpr : ArgType { let Name = "const Expr *"; } +def ArgFloatingLiteral : ArgType { let Name = "const FloatingLiteral *"; } +def ArgCXXMethodDecl : ArgType { let Name = "const CXXMethodDecl *"; } +def ArgFunctionDecl : ArgType { let Name = "const FunctionDecl *"; } +def ArgRecordDecl : ArgType { let Name = "const RecordDecl *"; } +def ArgCXXRecordDecl : ArgType { let Name = "const CXXRecordDecl *"; } +def ArgValueDecl : ArgType { let Name = "const ValueDecl *"; } +def ArgRecordField : ArgType { let Name = "const Record::Field *"; } + +//===----------------------------------------------------------------------===// +// Classes of types intructions operate on. +//===----------------------------------------------------------------------===// + +class TypeClass { + list Types; +} + +def AluTypeClass : TypeClass { + let Types = [Sint8, Uint8, Sint16, Uint16, Sint32, + Uint32, Sint64, Uint64, Bool]; +} + +def PtrTypeClass : TypeClass { + let Types = [Ptr]; +} + +def AllTypeClass : TypeClass { + let Types = !listconcat(AluTypeClass.Types, PtrTypeClass.Types); +} + +def ComparableTypeClass : TypeClass { + let Types = !listconcat(AluTypeClass.Types, [Ptr]); +} + +class SingletonTypeClass : TypeClass { + let Types = [Ty]; +} + +//===----------------------------------------------------------------------===// +// Record describing all opcodes. +//===----------------------------------------------------------------------===// + +class Opcode { + list Types = []; + list Args = []; + string Name = ""; + bit CanReturn = 0; + bit ChangesPC = 0; + bit HasCustomLink = 0; + bit HasCustomEval = 0; + bit HasGroup = 0; +} + +class AluOpcode : Opcode { + let Types = [AluTypeClass]; + let HasGroup = 1; +} + +//===----------------------------------------------------------------------===// +// Jump opcodes +//===----------------------------------------------------------------------===// + +class JumpOpcode : Opcode { + let Args = [ArgSint32]; + let ChangesPC = 1; + let HasCustomEval = 1; +} + +// [] -> [] +def Jmp : JumpOpcode; +// [Bool] -> [], jumps if true. +def Jt : JumpOpcode; +// [Bool] -> [], jumps if false. +def Jf : JumpOpcode; + +//===----------------------------------------------------------------------===// +// Returns +//===----------------------------------------------------------------------===// + +// [Value] -> [] +def Ret : Opcode { + let Types = [AllTypeClass]; + let ChangesPC = 1; + let CanReturn = 1; + let HasGroup = 1; + let HasCustomEval = 1; +} +// [] -> [] +def RetVoid : Opcode { + let CanReturn = 1; + let ChangesPC = 1; + let HasCustomEval = 1; +} +// [Value] -> [] +def RetValue : Opcode { + let CanReturn = 1; + let ChangesPC = 1; + let HasCustomEval = 1; +} +// [] -> EXIT +def NoRet : Opcode {} + +//===----------------------------------------------------------------------===// +// Frame management +//===----------------------------------------------------------------------===// + +// [] -> [] +def Destroy : Opcode { + let Args = [ArgUint32]; + let HasCustomEval = 1; +} + +//===----------------------------------------------------------------------===// +// Constants +//===----------------------------------------------------------------------===// + +class ConstOpcode : Opcode { + let Types = [SingletonTypeClass]; + let Args = [ArgTy]; + let Name = "Const"; +} + +// [] -> [Integer] +def ConstSint8 : ConstOpcode; +def ConstUint8 : ConstOpcode; +def ConstSint16 : ConstOpcode; +def ConstUint16 : ConstOpcode; +def ConstSint32 : ConstOpcode; +def ConstUint32 : ConstOpcode; +def ConstSint64 : ConstOpcode; +def ConstUint64 : ConstOpcode; +def ConstBool : ConstOpcode; + +// [] -> [Integer] +def Zero : Opcode { + let Types = [AluTypeClass]; +} + +// [] -> [Pointer] +def Null : Opcode { + let Types = [PtrTypeClass]; +} + +//===----------------------------------------------------------------------===// +// Pointer generation +//===----------------------------------------------------------------------===// + +// [] -> [Pointer] +def GetPtrLocal : Opcode { + // Offset of local. + let Args = [ArgUint32]; + bit HasCustomEval = 1; +} +// [] -> [Pointer] +def GetPtrParam : Opcode { + // Offset of parameter. + let Args = [ArgUint32]; +} +// [] -> [Pointer] +def GetPtrGlobal : Opcode { + // Index of global. + let Args = [ArgUint32]; +} +// [Pointer] -> [Pointer] +def GetPtrField : Opcode { + // Offset of field. + let Args = [ArgUint32]; +} +// [Pointer] -> [Pointer] +def GetPtrActiveField : Opcode { + // Offset of field. + let Args = [ArgUint32]; +} +// [] -> [Pointer] +def GetPtrActiveThisField : Opcode { + // Offset of field. + let Args = [ArgUint32]; +} +// [] -> [Pointer] +def GetPtrThisField : Opcode { + // Offset of field. + let Args = [ArgUint32]; +} +// [Pointer] -> [Pointer] +def GetPtrBase : Opcode { + // Offset of field, which is a base. + let Args = [ArgUint32]; +} +// [Pointer] -> [Pointer] +def GetPtrVirtBase : Opcode { + // RecordDecl of base class. + let Args = [ArgRecordDecl]; +} +// [] -> [Pointer] +def GetPtrThisBase : Opcode { + // Offset of field, which is a base. + let Args = [ArgUint32]; +} +// [] -> [Pointer] +def GetPtrThisVirtBase : Opcode { + // RecordDecl of base class. + let Args = [ArgRecordDecl]; +} +// [] -> [Pointer] +def This : Opcode; + +// [Pointer] -> [Pointer] +def NarrowPtr : Opcode; +// [Pointer] -> [Pointer] +def ExpandPtr : Opcode; + +//===----------------------------------------------------------------------===// +// Direct field accessors +//===----------------------------------------------------------------------===// + +class AccessOpcode : Opcode { + let Types = [AllTypeClass]; + let Args = [ArgUint32]; + let HasGroup = 1; +} + +class BitFieldOpcode : Opcode { + let Types = [AluTypeClass]; + let Args = [ArgRecordField]; + let HasGroup = 1; +} + +// [] -> [Pointer] +def GetLocal : AccessOpcode { let HasCustomEval = 1; } +// [] -> [Pointer] +def SetLocal : AccessOpcode { let HasCustomEval = 1; } + +// [] -> [Value] +def GetGlobal : AccessOpcode; +// [Value] -> [] +def InitGlobal : AccessOpcode; +// [Value] -> [] +def SetGlobal : AccessOpcode; + +// [] -> [Value] +def GetParam : AccessOpcode; +// [Value] -> [] +def SetParam : AccessOpcode; + +// [Pointer] -> [Pointer, Value] +def GetField : AccessOpcode; +// [Pointer] -> [Value] +def GetFieldPop : AccessOpcode; +// [] -> [Value] +def GetThisField : AccessOpcode; + +// [Pointer, Value] -> [Pointer] +def SetField : AccessOpcode; +// [Value] -> [] +def SetThisField : AccessOpcode; + +// [Value] -> [] +def InitThisField : AccessOpcode; +// [Value] -> [] +def InitThisFieldActive : AccessOpcode; +// [Value] -> [] +def InitThisBitField : BitFieldOpcode; +// [Pointer, Value] -> [] +def InitField : AccessOpcode; +// [Pointer, Value] -> [] +def InitBitField : BitFieldOpcode; +// [Pointer, Value] -> [] +def InitFieldActive : AccessOpcode; + +//===----------------------------------------------------------------------===// +// Pointer access +//===----------------------------------------------------------------------===// + +class LoadOpcode : Opcode { + let Types = [AllTypeClass]; + let HasGroup = 1; +} + +// [Pointer] -> [Pointer, Value] +def Load : LoadOpcode {} +// [Pointer] -> [Value] +def LoadPop : LoadOpcode {} + +class StoreOpcode : Opcode { + let Types = [AllTypeClass]; + let HasGroup = 1; +} + +class StoreBitFieldOpcode : Opcode { + let Types = [AluTypeClass]; + let HasGroup = 1; +} + +// [Pointer, Value] -> [Pointer] +def Store : StoreOpcode {} +// [Pointer, Value] -> [] +def StorePop : StoreOpcode {} + +// [Pointer, Value] -> [Pointer] +def StoreBitField : StoreBitFieldOpcode {} +// [Pointer, Value] -> [] +def StoreBitFieldPop : StoreBitFieldOpcode {} + +// [Pointer, Value] -> [] +def InitPop : StoreOpcode {} +// [Pointer, Value] -> [Pointer] +def InitElem : Opcode { + let Types = [AllTypeClass]; + let Args = [ArgUint32]; + let HasGroup = 1; +} +// [Pointer, Value] -> [] +def InitElemPop : Opcode { + let Types = [AllTypeClass]; + let Args = [ArgUint32]; + let HasGroup = 1; +} + +//===----------------------------------------------------------------------===// +// Pointer arithmetic. +//===----------------------------------------------------------------------===// + +// [Pointer, Integral] -> [Pointer] +def AddOffset : AluOpcode; +// [Pointer, Integral] -> [Pointer] +def SubOffset : AluOpcode; + +//===----------------------------------------------------------------------===// +// Binary operators. +//===----------------------------------------------------------------------===// + +// [Real, Real] -> [Real] +def Sub : AluOpcode; +def Add : AluOpcode; +def Mul : AluOpcode; + +//===----------------------------------------------------------------------===// +// Comparison opcodes. +//===----------------------------------------------------------------------===// + +class EqualityOpcode : Opcode { + let Types = [AllTypeClass]; + let HasGroup = 1; +} + +def EQ : EqualityOpcode; +def NE : EqualityOpcode; + +class ComparisonOpcode : Opcode { + let Types = [ComparableTypeClass]; + let HasGroup = 1; +} + +def LT : ComparisonOpcode; +def LE : ComparisonOpcode; +def GT : ComparisonOpcode; +def GE : ComparisonOpcode; + +//===----------------------------------------------------------------------===// +// Stack management. +//===----------------------------------------------------------------------===// + +// [Value] -> [] +def Pop : Opcode { + let Types = [AllTypeClass]; + let HasGroup = 1; +} + +// [Value] -> [Value, Value] +def Dup : Opcode { + let Types = [AllTypeClass]; + let HasGroup = 1; +} diff --git a/lib/AST/Interp/Pointer.cpp b/lib/AST/Interp/Pointer.cpp new file mode 100644 index 0000000000000..1a10723aaca53 --- /dev/null +++ b/lib/AST/Interp/Pointer.cpp @@ -0,0 +1,193 @@ +//===--- Pointer.cpp - Types for the constexpr VM ---------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "Pointer.h" +#include "Block.h" +#include "Function.h" +#include "PrimType.h" + +using namespace clang; +using namespace clang::interp; + +Pointer::Pointer(Block *Pointee) : Pointer(Pointee, 0, 0) {} + +Pointer::Pointer(const Pointer &P) : Pointer(P.Pointee, P.Base, P.Offset) {} + +Pointer::Pointer(Pointer &&P) + : Pointee(P.Pointee), Base(P.Base), Offset(P.Offset) { + if (Pointee) + Pointee->movePointer(&P, this); +} + +Pointer::Pointer(Block *Pointee, unsigned Base, unsigned Offset) + : Pointee(Pointee), Base(Base), Offset(Offset) { + assert((Base == RootPtrMark || Base % alignof(void *) == 0) && "wrong base"); + if (Pointee) + Pointee->addPointer(this); +} + +Pointer::~Pointer() { + if (Pointee) { + Pointee->removePointer(this); + Pointee->cleanup(); + } +} + +void Pointer::operator=(const Pointer &P) { + Block *Old = Pointee; + + if (Pointee) + Pointee->removePointer(this); + + Offset = P.Offset; + Base = P.Base; + + Pointee = P.Pointee; + if (Pointee) + Pointee->addPointer(this); + + if (Old) + Old->cleanup(); +} + +void Pointer::operator=(Pointer &&P) { + Block *Old = Pointee; + + if (Pointee) + Pointee->removePointer(this); + + Offset = P.Offset; + Base = P.Base; + + Pointee = P.Pointee; + if (Pointee) + Pointee->movePointer(&P, this); + + if (Old) + Old->cleanup(); +} + +APValue Pointer::toAPValue() const { + APValue::LValueBase Base; + llvm::SmallVector Path; + CharUnits Offset; + bool IsNullPtr; + bool IsOnePastEnd; + + if (isZero()) { + Base = static_cast(nullptr); + IsNullPtr = true; + IsOnePastEnd = false; + Offset = CharUnits::Zero(); + } else { + // Build the lvalue base from the block. + Descriptor *Desc = getDeclDesc(); + if (auto *VD = Desc->asValueDecl()) + Base = VD; + else if (auto *E = Desc->asExpr()) + Base = E; + else + llvm_unreachable("Invalid allocation type"); + + // Not a null pointer. + IsNullPtr = false; + + if (isUnknownSizeArray()) { + IsOnePastEnd = false; + Offset = CharUnits::Zero(); + } else { + // TODO: compute the offset into the object. + Offset = CharUnits::Zero(); + + // Build the path into the object. + Pointer Ptr = *this; + while (Ptr.isField()) { + if (Ptr.isArrayElement()) { + Path.push_back(APValue::LValuePathEntry::ArrayIndex(Ptr.getIndex())); + Ptr = Ptr.getArray(); + } else { + // TODO: figure out if base is virtual + bool IsVirtual = false; + + // Create a path entry for the field. + Descriptor *Desc = Ptr.getFieldDesc(); + if (auto *BaseOrMember = Desc->asDecl()) { + Path.push_back(APValue::LValuePathEntry({BaseOrMember, IsVirtual})); + Ptr = Ptr.getBase(); + continue; + } + llvm_unreachable("Invalid field type"); + } + } + + IsOnePastEnd = isOnePastEnd(); + } + } + + return APValue(Base, Offset, Path, IsOnePastEnd, IsNullPtr); +} + +bool Pointer::isInitialized() const { + assert(Pointee && "Cannot check if null pointer was initialized"); + Descriptor *Desc = getFieldDesc(); + if (Desc->isPrimitiveArray()) { + if (Pointee->IsStatic) + return true; + // Primitive array field are stored in a bitset. + InitMap *Map = getInitMap(); + if (!Map) + return false; + if (Map == (InitMap *)-1) + return true; + return Map->isInitialized(getIndex()); + } else { + // Field has its bit in an inline descriptor. + return Base == 0 || getInlineDesc()->IsInitialized; + } +} + +void Pointer::initialize() const { + assert(Pointee && "Cannot initialize null pointer"); + Descriptor *Desc = getFieldDesc(); + if (Desc->isPrimitiveArray()) { + if (!Pointee->IsStatic) { + // Primitive array initializer. + InitMap *&Map = getInitMap(); + if (Map == (InitMap *)-1) + return; + if (Map == nullptr) + Map = InitMap::allocate(Desc->getNumElems()); + if (Map->initialize(getIndex())) { + free(Map); + Map = (InitMap *)-1; + } + } + } else { + // Field has its bit in an inline descriptor. + assert(Base != 0 && "Only composite fields can be initialised"); + getInlineDesc()->IsInitialized = true; + } +} + +void Pointer::activate() const { + // Field has its bit in an inline descriptor. + assert(Base != 0 && "Only composite fields can be initialised"); + getInlineDesc()->IsActive = true; +} + +void Pointer::deactivate() const { + // TODO: this only appears in constructors, so nothing to deactivate. +} + +bool Pointer::hasSameBase(const Pointer &A, const Pointer &B) { + return A.Pointee == B.Pointee; +} + +bool Pointer::hasSameArray(const Pointer &A, const Pointer &B) { + return A.Base == B.Base && A.getFieldDesc()->IsArray; +} diff --git a/lib/AST/Interp/Pointer.h b/lib/AST/Interp/Pointer.h new file mode 100644 index 0000000000000..b8fa98e24faab --- /dev/null +++ b/lib/AST/Interp/Pointer.h @@ -0,0 +1,353 @@ +//===--- Pointer.h - Types for the constexpr VM -----------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Defines the classes responsible for pointer tracking. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_AST_INTERP_POINTER_H +#define LLVM_CLANG_AST_INTERP_POINTER_H + +#include "Block.h" +#include "Descriptor.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/Expr.h" +#include "clang/AST/ComparisonCategories.h" +#include "llvm/ADT/PointerUnion.h" +#include "llvm/Support/raw_ostream.h" + +namespace clang { +namespace interp { +class Block; +class DeadBlock; +class Context; +class InterpState; +class Pointer; +class Function; +enum PrimType : unsigned; + +/// A pointer to a memory block, live or dead. +/// +/// This object can be allocated into interpreter stack frames. If pointing to +/// a live block, it is a link in the chain of pointers pointing to the block. +class Pointer { +private: + static constexpr unsigned PastEndMark = (unsigned)-1; + static constexpr unsigned RootPtrMark = (unsigned)-1; + +public: + Pointer() {} + Pointer(Block *B); + Pointer(const Pointer &P); + Pointer(Pointer &&P); + ~Pointer(); + + void operator=(const Pointer &P); + void operator=(Pointer &&P); + + /// Converts the pointer to an APValue. + APValue toAPValue() const; + + /// Offsets a pointer inside an array. + Pointer atIndex(unsigned Idx) const { + if (Base == RootPtrMark) + return Pointer(Pointee, RootPtrMark, getDeclDesc()->getSize()); + unsigned Off = Idx * elemSize(); + if (getFieldDesc()->ElemDesc) + Off += sizeof(InlineDescriptor); + else + Off += sizeof(InitMap *); + return Pointer(Pointee, Base, Base + Off); + } + + /// Creates a pointer to a field. + Pointer atField(unsigned Off) const { + unsigned Field = Offset + Off; + return Pointer(Pointee, Field, Field); + } + + /// Restricts the scope of an array element pointer. + Pointer narrow() const { + // Null pointers cannot be narrowed. + if (isZero() || isUnknownSizeArray()) + return *this; + + // Pointer to an array of base types - enter block. + if (Base == RootPtrMark) + return Pointer(Pointee, 0, Offset == 0 ? Offset : PastEndMark); + + // Pointer is one past end - magic offset marks that. + if (isOnePastEnd()) + return Pointer(Pointee, Base, PastEndMark); + + // Primitive arrays are a bit special since they do not have inline + // descriptors. If Offset != Base, then the pointer already points to + // an element and there is nothing to do. Otherwise, the pointer is + // adjusted to the first element of the array. + if (inPrimitiveArray()) { + if (Offset != Base) + return *this; + return Pointer(Pointee, Base, Offset + sizeof(InitMap *)); + } + + // Pointer is to a field or array element - enter it. + if (Offset != Base) + return Pointer(Pointee, Offset, Offset); + + // Enter the first element of an array. + if (!getFieldDesc()->isArray()) + return *this; + + const unsigned NewBase = Base + sizeof(InlineDescriptor); + return Pointer(Pointee, NewBase, NewBase); + } + + /// Expands a pointer to the containing array, undoing narrowing. + Pointer expand() const { + if (isElementPastEnd()) { + // Revert to an outer one-past-end pointer. + unsigned Adjust; + if (inPrimitiveArray()) + Adjust = sizeof(InitMap *); + else + Adjust = sizeof(InlineDescriptor); + return Pointer(Pointee, Base, Base + getSize() + Adjust); + } + + // Do not step out of array elements. + if (Base != Offset) + return *this; + + // If at base, point to an array of base types. + if (Base == 0) + return Pointer(Pointee, RootPtrMark, 0); + + // Step into the containing array, if inside one. + unsigned Next = Base - getInlineDesc()->Offset; + Descriptor *Desc = Next == 0 ? getDeclDesc() : getDescriptor(Next)->Desc; + if (!Desc->IsArray) + return *this; + return Pointer(Pointee, Next, Offset); + } + + /// Checks if the pointer is null. + bool isZero() const { return Pointee == nullptr; } + /// Checks if the pointer is live. + bool isLive() const { return Pointee && !Pointee->IsDead; } + /// Checks if the item is a field in an object. + bool isField() const { return Base != 0 && Base != RootPtrMark; } + + /// Accessor for information about the declaration site. + Descriptor *getDeclDesc() const { return Pointee->Desc; } + SourceLocation getDeclLoc() const { return getDeclDesc()->getLocation(); } + + /// Returns a pointer to the object of which this pointer is a field. + Pointer getBase() const { + if (Base == RootPtrMark) { + assert(Offset == PastEndMark && "cannot get base of a block"); + return Pointer(Pointee, Base, 0); + } + assert(Offset == Base && "not an inner field"); + unsigned NewBase = Base - getInlineDesc()->Offset; + return Pointer(Pointee, NewBase, NewBase); + } + /// Returns the parent array. + Pointer getArray() const { + if (Base == RootPtrMark) { + assert(Offset != 0 && Offset != PastEndMark && "not an array element"); + return Pointer(Pointee, Base, 0); + } + assert(Offset != Base && "not an array element"); + return Pointer(Pointee, Base, Base); + } + + /// Accessors for information about the innermost field. + Descriptor *getFieldDesc() const { + if (Base == 0 || Base == RootPtrMark) + return getDeclDesc(); + return getInlineDesc()->Desc; + } + + /// Returns the type of the innermost field. + QualType getType() const { return getFieldDesc()->getType(); } + + /// Returns the element size of the innermost field. + size_t elemSize() const { + if (Base == RootPtrMark) + return getDeclDesc()->getSize(); + return getFieldDesc()->getElemSize(); + } + /// Returns the total size of the innermost field. + size_t getSize() const { return getFieldDesc()->getSize(); } + + /// Returns the offset into an array. + unsigned getOffset() const { + assert(Offset != PastEndMark && "invalid offset"); + if (Base == RootPtrMark) + return Offset; + + unsigned Adjust = 0; + if (Offset != Base) { + if (getFieldDesc()->ElemDesc) + Adjust = sizeof(InlineDescriptor); + else + Adjust = sizeof(InitMap *); + } + return Offset - Base - Adjust; + } + + /// Checks if the innermost field is an array. + bool inArray() const { return getFieldDesc()->IsArray; } + /// Checks if the structure is a primitive array. + bool inPrimitiveArray() const { return getFieldDesc()->isPrimitiveArray(); } + /// Checks if the structure is an array of unknown size. + bool isUnknownSizeArray() const { + return getFieldDesc()->isUnknownSizeArray(); + } + /// Checks if the pointer points to an array. + bool isArrayElement() const { return Base != Offset; } + /// Pointer points directly to a block. + bool isRoot() const { + return (Base == 0 || Base == RootPtrMark) && Offset == 0; + } + + /// Returns the record descriptor of a class. + Record *getRecord() const { return getFieldDesc()->ElemRecord; } + /// Returns the field information. + const FieldDecl *getField() const { return getFieldDesc()->asFieldDecl(); } + + /// Checks if the object is a union. + bool isUnion() const; + + /// Checks if the storage is extern. + bool isExtern() const { return Pointee->isExtern(); } + /// Checks if the storage is static. + bool isStatic() const { return Pointee->isStatic(); } + /// Checks if the storage is temporary. + bool isTemporary() const { return Pointee->isTemporary(); } + /// Checks if the storage is a static temporary. + bool isStaticTemporary() const { return isStatic() && isTemporary(); } + + /// Checks if the field is mutable. + bool isMutable() const { return Base != 0 && getInlineDesc()->IsMutable; } + /// Checks if an object was initialized. + bool isInitialized() const; + /// Checks if the object is active. + bool isActive() const { return Base == 0 || getInlineDesc()->IsActive; } + /// Checks if a structure is a base class. + bool isBaseClass() const { return isField() && getInlineDesc()->IsBase; } + + /// Checks if an object or a subfield is mutable. + bool isConst() const { + return Base == 0 ? getDeclDesc()->IsConst : getInlineDesc()->IsConst; + } + + /// Returns the declaration ID. + llvm::Optional getDeclID() const { return Pointee->getDeclID(); } + + /// Returns the byte offset from the start. + unsigned getByteOffset() const { + return Offset; + } + + /// Returns the number of elements. + unsigned getNumElems() const { return getSize() / elemSize(); } + + /// Returns the index into an array. + int64_t getIndex() const { + if (isElementPastEnd()) + return 1; + if (auto ElemSize = elemSize()) + return getOffset() / ElemSize; + return 0; + } + + /// Checks if the index is one past end. + bool isOnePastEnd() const { + return isElementPastEnd() || getSize() == getOffset(); + } + + /// Checks if the pointer is an out-of-bounds element pointer. + bool isElementPastEnd() const { return Offset == PastEndMark; } + + /// Dereferences the pointer, if it's live. + template T &deref() const { + assert(isLive() && "Invalid pointer"); + return *reinterpret_cast(Pointee->data() + Offset); + } + + /// Dereferences a primitive element. + template T &elem(unsigned I) const { + return reinterpret_cast(Pointee->data())[I]; + } + + /// Initializes a field. + void initialize() const; + /// Activats a field. + void activate() const; + /// Deactivates an entire strurcutre. + void deactivate() const; + + /// Checks if two pointers are comparable. + static bool hasSameBase(const Pointer &A, const Pointer &B); + /// Checks if two pointers can be subtracted. + static bool hasSameArray(const Pointer &A, const Pointer &B); + + /// Prints the pointer. + void print(llvm::raw_ostream &OS) const { + OS << "{" << Base << ", " << Offset << ", "; + if (Pointee) + OS << Pointee->getSize(); + else + OS << "nullptr"; + OS << "}"; + } + +private: + friend class Block; + friend class DeadBlock; + + Pointer(Block *Pointee, unsigned Base, unsigned Offset); + + /// Returns the embedded descriptor preceding a field. + InlineDescriptor *getInlineDesc() const { return getDescriptor(Base); } + + /// Returns a descriptor at a given offset. + InlineDescriptor *getDescriptor(unsigned Offset) const { + assert(Offset != 0 && "Not a nested pointer"); + return reinterpret_cast(Pointee->data() + Offset) - 1; + } + + /// Returns a reference to the pointer which stores the initialization map. + InitMap *&getInitMap() const { + return *reinterpret_cast(Pointee->data() + Base); + } + + /// The block the pointer is pointing to. + Block *Pointee = nullptr; + /// Start of the current subfield. + unsigned Base = 0; + /// Offset into the block. + unsigned Offset = 0; + + /// Previous link in the pointer chain. + Pointer *Prev = nullptr; + /// Next link in the pointer chain. + Pointer *Next = nullptr; +}; + +inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Pointer &P) { + P.print(OS); + return OS; +} + +} // namespace interp +} // namespace clang + +#endif diff --git a/lib/AST/Interp/PrimType.cpp b/lib/AST/Interp/PrimType.cpp new file mode 100644 index 0000000000000..082bfaf3c2079 --- /dev/null +++ b/lib/AST/Interp/PrimType.cpp @@ -0,0 +1,23 @@ +//===--- Type.cpp - Types for the constexpr VM ------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "PrimType.h" + +using namespace clang; +using namespace clang::interp; + +namespace clang { +namespace interp { + +size_t primSize(PrimType Type) { + TYPE_SWITCH(Type, return sizeof(T)); + llvm_unreachable("not a primitive type"); +} + +} // namespace interp +} // namespace clang diff --git a/lib/AST/Interp/PrimType.h b/lib/AST/Interp/PrimType.h new file mode 100644 index 0000000000000..f5f4f8e5c32d6 --- /dev/null +++ b/lib/AST/Interp/PrimType.h @@ -0,0 +1,115 @@ +//===--- PrimType.h - Types for the constexpr VM --------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Defines the VM types and helpers operating on types. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_AST_INTERP_TYPE_H +#define LLVM_CLANG_AST_INTERP_TYPE_H + +#include +#include +#include +#include "Boolean.h" +#include "Integral.h" +#include "Pointer.h" + +namespace clang { +namespace interp { + +/// Enumeration of the primitive types of the VM. +enum PrimType : unsigned { + PT_Sint8, + PT_Uint8, + PT_Sint16, + PT_Uint16, + PT_Sint32, + PT_Uint32, + PT_Sint64, + PT_Uint64, + PT_Bool, + PT_Ptr, +}; + +/// Mapping from primitive types to their representation. +template struct PrimConv; +template <> struct PrimConv { using T = Integral<8, true>; }; +template <> struct PrimConv { using T = Integral<8, false>; }; +template <> struct PrimConv { using T = Integral<16, true>; }; +template <> struct PrimConv { using T = Integral<16, false>; }; +template <> struct PrimConv { using T = Integral<32, true>; }; +template <> struct PrimConv { using T = Integral<32, false>; }; +template <> struct PrimConv { using T = Integral<64, true>; }; +template <> struct PrimConv { using T = Integral<64, false>; }; +template <> struct PrimConv { using T = Boolean; }; +template <> struct PrimConv { using T = Pointer; }; + +/// Returns the size of a primitive type in bytes. +size_t primSize(PrimType Type); + +/// Aligns a size to the pointer alignment. +constexpr size_t align(size_t Size) { + return ((Size + alignof(void *) - 1) / alignof(void *)) * alignof(void *); +} + +inline bool isPrimitiveIntegral(PrimType Type) { + switch (Type) { + case PT_Bool: + case PT_Sint8: + case PT_Uint8: + case PT_Sint16: + case PT_Uint16: + case PT_Sint32: + case PT_Uint32: + case PT_Sint64: + case PT_Uint64: + return true; + default: + return false; + } +} + +} // namespace interp +} // namespace clang + +/// Helper macro to simplify type switches. +/// The macro implicitly exposes a type T in the scope of the inner block. +#define TYPE_SWITCH_CASE(Name, B) \ + case Name: { using T = PrimConv::T; do {B;} while(0); break; } +#define TYPE_SWITCH(Expr, B) \ + switch (Expr) { \ + TYPE_SWITCH_CASE(PT_Sint8, B) \ + TYPE_SWITCH_CASE(PT_Uint8, B) \ + TYPE_SWITCH_CASE(PT_Sint16, B) \ + TYPE_SWITCH_CASE(PT_Uint16, B) \ + TYPE_SWITCH_CASE(PT_Sint32, B) \ + TYPE_SWITCH_CASE(PT_Uint32, B) \ + TYPE_SWITCH_CASE(PT_Sint64, B) \ + TYPE_SWITCH_CASE(PT_Uint64, B) \ + TYPE_SWITCH_CASE(PT_Bool, B) \ + TYPE_SWITCH_CASE(PT_Ptr, B) \ + } +#define COMPOSITE_TYPE_SWITCH(Expr, B, D) \ + switch (Expr) { \ + TYPE_SWITCH_CASE(PT_Ptr, B) \ + default: do { D; } while(0); break; \ + } +#define INT_TYPE_SWITCH(Expr, B) \ + switch (Expr) { \ + TYPE_SWITCH_CASE(PT_Sint8, B) \ + TYPE_SWITCH_CASE(PT_Uint8, B) \ + TYPE_SWITCH_CASE(PT_Sint16, B) \ + TYPE_SWITCH_CASE(PT_Uint16, B) \ + TYPE_SWITCH_CASE(PT_Sint32, B) \ + TYPE_SWITCH_CASE(PT_Uint32, B) \ + TYPE_SWITCH_CASE(PT_Sint64, B) \ + TYPE_SWITCH_CASE(PT_Uint64, B) \ + default: llvm_unreachable("not an integer"); \ + } +#endif diff --git a/lib/AST/Interp/Program.cpp b/lib/AST/Interp/Program.cpp new file mode 100644 index 0000000000000..fcbab0ea8172c --- /dev/null +++ b/lib/AST/Interp/Program.cpp @@ -0,0 +1,364 @@ +//===--- Program.cpp - Bytecode for the constexpr VM ------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "Program.h" +#include "ByteCodeStmtGen.h" +#include "Context.h" +#include "Function.h" +#include "Opcode.h" +#include "PrimType.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" + +using namespace clang; +using namespace clang::interp; + +unsigned Program::createGlobalString(const StringLiteral *S) { + const size_t CharWidth = S->getCharByteWidth(); + const size_t BitWidth = CharWidth * Ctx.getCharBit(); + + PrimType CharType; + switch (CharWidth) { + case 1: + CharType = PT_Sint8; + break; + case 2: + CharType = PT_Uint16; + break; + case 4: + CharType = PT_Uint32; + break; + default: + llvm_unreachable("unsupported character width"); + } + + // Create a descriptor for the string. + Descriptor *Desc = allocateDescriptor(S, CharType, S->getLength() + 1, + /*isConst=*/true, + /*isTemporary=*/false, + /*isMutable=*/false); + + // Allocate storage for the string. + // The byte length does not include the null terminator. + unsigned I = Globals.size(); + unsigned Sz = Desc->getAllocSize(); + auto *G = new (Allocator, Sz) Global(Desc, /*isStatic=*/true, + /*isExtern=*/false); + Globals.push_back(G); + + // Construct the string in storage. + const Pointer Ptr(G->block()); + for (unsigned I = 0, N = S->getLength(); I <= N; ++I) { + Pointer Field = Ptr.atIndex(I).narrow(); + const uint32_t CodePoint = I == N ? 0 : S->getCodeUnit(I); + switch (CharType) { + case PT_Sint8: { + using T = PrimConv::T; + Field.deref() = T::from(CodePoint, BitWidth); + break; + } + case PT_Uint16: { + using T = PrimConv::T; + Field.deref() = T::from(CodePoint, BitWidth); + break; + } + case PT_Uint32: { + using T = PrimConv::T; + Field.deref() = T::from(CodePoint, BitWidth); + break; + } + default: + llvm_unreachable("unsupported character type"); + } + } + return I; +} + +Pointer Program::getPtrGlobal(unsigned Idx) { + assert(Idx < Globals.size()); + return Pointer(Globals[Idx]->block()); +} + +llvm::Optional Program::getGlobal(const ValueDecl *VD) { + auto It = GlobalIndices.find(VD); + if (It != GlobalIndices.end()) + return It->second; + + // Find any previous declarations which were aleady evaluated. + llvm::Optional Index; + for (const Decl *P = VD; P; P = P->getPreviousDecl()) { + auto It = GlobalIndices.find(P); + if (It != GlobalIndices.end()) { + Index = It->second; + break; + } + } + + // Map the decl to the existing index. + if (Index) { + GlobalIndices[VD] = *Index; + return {}; + } + + return Index; +} + +llvm::Optional Program::getOrCreateGlobal(const ValueDecl *VD) { + if (auto Idx = getGlobal(VD)) + return Idx; + + if (auto Idx = createGlobal(VD)) { + GlobalIndices[VD] = *Idx; + return Idx; + } + return {}; +} + +llvm::Optional Program::getOrCreateDummy(const ParmVarDecl *PD) { + auto &ASTCtx = Ctx.getASTContext(); + + // Create a pointer to an incomplete array of the specified elements. + QualType ElemTy = PD->getType()->castAs()->getPointeeType(); + QualType Ty = ASTCtx.getIncompleteArrayType(ElemTy, ArrayType::Normal, 0); + + // Dedup blocks since they are immutable and pointers cannot be compared. + auto It = DummyParams.find(PD); + if (It != DummyParams.end()) + return It->second; + + if (auto Idx = createGlobal(PD, Ty, /*isStatic=*/true, /*isExtern=*/true)) { + DummyParams[PD] = *Idx; + return Idx; + } + return {}; +} + +llvm::Optional Program::createGlobal(const ValueDecl *VD) { + bool IsStatic, IsExtern; + if (auto *Var = dyn_cast(VD)) { + IsStatic = !Var->hasLocalStorage(); + IsExtern = !Var->getAnyInitializer(); + } else { + IsStatic = false; + IsExtern = true; + } + if (auto Idx = createGlobal(VD, VD->getType(), IsStatic, IsExtern)) { + for (const Decl *P = VD; P; P = P->getPreviousDecl()) + GlobalIndices[P] = *Idx; + return *Idx; + } + return {}; +} + +llvm::Optional Program::createGlobal(const Expr *E) { + return createGlobal(E, E->getType(), /*isStatic=*/true, /*isExtern=*/false); +} + +llvm::Optional Program::createGlobal(const DeclTy &D, QualType Ty, + bool IsStatic, bool IsExtern) { + // Create a descriptor for the global. + Descriptor *Desc; + const bool IsConst = Ty.isConstQualified(); + const bool IsTemporary = D.dyn_cast(); + if (auto T = Ctx.classify(Ty)) { + Desc = createDescriptor(D, *T, IsConst, IsTemporary); + } else { + Desc = createDescriptor(D, Ty.getTypePtr(), IsConst, IsTemporary); + } + if (!Desc) + return {}; + + // Allocate a block for storage. + unsigned I = Globals.size(); + + auto *G = new (Allocator, Desc->getAllocSize()) + Global(getCurrentDecl(), Desc, IsStatic, IsExtern); + G->block()->invokeCtor(); + + Globals.push_back(G); + + return I; +} + +Function *Program::getFunction(const FunctionDecl *F) { + F = F->getDefinition(); + auto It = Funcs.find(F); + return It == Funcs.end() ? nullptr : It->second.get(); +} + +llvm::Expected Program::getOrCreateFunction(const FunctionDecl *F) { + if (Function *Func = getFunction(F)) { + return Func; + } + + // Try to compile the function if it wasn't compiled yet. + if (const FunctionDecl *FD = F->getDefinition()) + return ByteCodeStmtGen(Ctx, *this).compileFunc(FD); + + // A relocation which traps if not resolved. + return nullptr; +} + +Record *Program::getOrCreateRecord(const RecordDecl *RD) { + // Use the actual definition as a key. + RD = RD->getDefinition(); + if (!RD) + return nullptr; + + // Deduplicate records. + auto It = Records.find(RD); + if (It != Records.end()) { + return It->second; + } + + // Number of bytes required by fields and base classes. + unsigned Size = 0; + // Number of bytes required by virtual base. + unsigned VirtSize = 0; + + // Helper to get a base descriptor. + auto GetBaseDesc = [this](const RecordDecl *BD, Record *BR) -> Descriptor * { + if (!BR) + return nullptr; + return allocateDescriptor(BD, BR, /*isConst=*/false, + /*isTemporary=*/false, + /*isMutable=*/false); + }; + + // Reserve space for base classes. + Record::BaseList Bases; + Record::VirtualBaseList VirtBases; + if (auto *CD = dyn_cast(RD)) { + for (const CXXBaseSpecifier &Spec : CD->bases()) { + if (Spec.isVirtual()) + continue; + + const RecordDecl *BD = Spec.getType()->castAs()->getDecl(); + Record *BR = getOrCreateRecord(BD); + if (Descriptor *Desc = GetBaseDesc(BD, BR)) { + Size += align(sizeof(InlineDescriptor)); + Bases.push_back({BD, Size, Desc, BR}); + Size += align(BR->getSize()); + continue; + } + return nullptr; + } + + for (const CXXBaseSpecifier &Spec : CD->vbases()) { + const RecordDecl *BD = Spec.getType()->castAs()->getDecl(); + Record *BR = getOrCreateRecord(BD); + + if (Descriptor *Desc = GetBaseDesc(BD, BR)) { + VirtSize += align(sizeof(InlineDescriptor)); + VirtBases.push_back({BD, VirtSize, Desc, BR}); + VirtSize += align(BR->getSize()); + continue; + } + return nullptr; + } + } + + // Reserve space for fields. + Record::FieldList Fields; + for (const FieldDecl *FD : RD->fields()) { + // Reserve space for the field's descriptor and the offset. + Size += align(sizeof(InlineDescriptor)); + + // Classify the field and add its metadata. + QualType FT = FD->getType(); + const bool IsConst = FT.isConstQualified(); + const bool IsMutable = FD->isMutable(); + Descriptor *Desc; + if (llvm::Optional T = Ctx.classify(FT)) { + Desc = createDescriptor(FD, *T, IsConst, /*isTemporary=*/false, + IsMutable); + } else { + Desc = createDescriptor(FD, FT.getTypePtr(), IsConst, + /*isTemporary=*/false, IsMutable); + } + if (!Desc) + return nullptr; + Fields.push_back({FD, Size, Desc}); + Size += align(Desc->getAllocSize()); + } + + Record *R = new (Allocator) Record(RD, std::move(Bases), std::move(Fields), + std::move(VirtBases), VirtSize, Size); + Records.insert({RD, R}); + return R; +} + +Descriptor *Program::createDescriptor(const DeclTy &D, const Type *Ty, + bool IsConst, bool IsTemporary, + bool IsMutable) { + // Classes and structures. + if (auto *RT = Ty->getAs()) { + if (auto *Record = getOrCreateRecord(RT->getDecl())) + return allocateDescriptor(D, Record, IsConst, IsTemporary, IsMutable); + } + + // Arrays. + if (auto ArrayType = Ty->getAsArrayTypeUnsafe()) { + QualType ElemTy = ArrayType->getElementType(); + // Array of well-known bounds. + if (auto CAT = dyn_cast(ArrayType)) { + size_t NumElems = CAT->getSize().getZExtValue(); + if (llvm::Optional T = Ctx.classify(ElemTy)) { + // Arrays of primitives. + unsigned ElemSize = primSize(*T); + if (std::numeric_limits::max() / ElemSize <= NumElems) { + return {}; + } + return allocateDescriptor(D, *T, NumElems, IsConst, IsTemporary, + IsMutable); + } else { + // Arrays of composites. In this case, the array is a list of pointers, + // followed by the actual elements. + Descriptor *Desc = + createDescriptor(D, ElemTy.getTypePtr(), IsConst, IsTemporary); + if (!Desc) + return nullptr; + InterpSize ElemSize = Desc->getAllocSize() + sizeof(InlineDescriptor); + if (std::numeric_limits::max() / ElemSize <= NumElems) + return {}; + return allocateDescriptor(D, Desc, NumElems, IsConst, IsTemporary, + IsMutable); + } + } + + // Array of unknown bounds - cannot be accessed and pointer arithmetic + // is forbidden on pointers to such objects. + if (isa(ArrayType)) { + if (llvm::Optional T = Ctx.classify(ElemTy)) { + return allocateDescriptor(D, *T, IsTemporary, + Descriptor::UnknownSize{}); + } else { + Descriptor *Desc = + createDescriptor(D, ElemTy.getTypePtr(), IsConst, IsTemporary); + if (!Desc) + return nullptr; + return allocateDescriptor(D, Desc, IsTemporary, + Descriptor::UnknownSize{}); + } + } + } + + // Atomic types. + if (auto *AT = Ty->getAs()) { + const Type *InnerTy = AT->getValueType().getTypePtr(); + return createDescriptor(D, InnerTy, IsConst, IsTemporary, IsMutable); + } + + // Complex types - represented as arrays of elements. + if (auto *CT = Ty->getAs()) { + PrimType ElemTy = *Ctx.classify(CT->getElementType()); + return allocateDescriptor(D, ElemTy, 2, IsConst, IsTemporary, IsMutable); + } + + return nullptr; +} diff --git a/lib/AST/Interp/Program.h b/lib/AST/Interp/Program.h new file mode 100644 index 0000000000000..5f0012db9b3f2 --- /dev/null +++ b/lib/AST/Interp/Program.h @@ -0,0 +1,220 @@ +//===--- Program.h - Bytecode for the constexpr VM --------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Defines a program which organises and links multiple bytecode functions. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_AST_INTERP_PROGRAM_H +#define LLVM_CLANG_AST_INTERP_PROGRAM_H + +#include +#include +#include "Function.h" +#include "Pointer.h" +#include "PrimType.h" +#include "Record.h" +#include "Source.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/PointerUnion.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Allocator.h" + +namespace clang { +class RecordDecl; +class Expr; +class FunctionDecl; +class Stmt; +class StringLiteral; +class VarDecl; + +namespace interp { +class Context; +class State; +class Record; +class Scope; + +/// The program contains and links the bytecode for all functions. +class Program { +public: + Program(Context &Ctx) : Ctx(Ctx) {} + + /// Emits a string literal among global data. + unsigned createGlobalString(const StringLiteral *S); + + /// Returns a pointer to a global. + Pointer getPtrGlobal(unsigned Idx); + + /// Returns the value of a global. + Block *getGlobal(unsigned Idx) { + assert(Idx < Globals.size()); + return Globals[Idx]->block(); + } + + /// Finds a global's index. + llvm::Optional getGlobal(const ValueDecl *VD); + + /// Returns or creates a global an creates an index to it. + llvm::Optional getOrCreateGlobal(const ValueDecl *VD); + + /// Returns or creates a dummy value for parameters. + llvm::Optional getOrCreateDummy(const ParmVarDecl *PD); + + /// Creates a global and returns its index. + llvm::Optional createGlobal(const ValueDecl *VD); + + /// Creates a global from a lifetime-extended temporary. + llvm::Optional createGlobal(const Expr *E); + + /// Creates a new function from a code range. + template + Function *createFunction(const FunctionDecl *Def, Ts &&... Args) { + auto *Func = new Function(*this, Def, std::forward(Args)...); + Funcs.insert({Def, std::unique_ptr(Func)}); + return Func; + } + /// Creates an anonymous function. + template + Function *createFunction(Ts &&... Args) { + auto *Func = new Function(*this, std::forward(Args)...); + AnonFuncs.emplace_back(Func); + return Func; + } + + /// Returns a function. + Function *getFunction(const FunctionDecl *F); + + /// Returns a pointer to a function if it exists and can be compiled. + /// If a function couldn't be compiled, an error is returned. + /// If a function was not yet defined, a null pointer is returned. + llvm::Expected getOrCreateFunction(const FunctionDecl *F); + + /// Returns a record or creates one if it does not exist. + Record *getOrCreateRecord(const RecordDecl *RD); + + /// Creates a descriptor for a primitive type. + Descriptor *createDescriptor(const DeclTy &D, PrimType Type, + bool IsConst = false, + bool IsTemporary = false, + bool IsMutable = false) { + return allocateDescriptor(D, Type, IsConst, IsTemporary, IsMutable); + } + + /// Creates a descriptor for a composite type. + Descriptor *createDescriptor(const DeclTy &D, const Type *Ty, + bool IsConst = false, bool IsTemporary = false, + bool IsMutable = false); + + /// Context to manage declaration lifetimes. + class DeclScope { + public: + DeclScope(Program &P, const VarDecl *VD) : P(P) { P.startDeclaration(VD); } + ~DeclScope() { P.endDeclaration(); } + + private: + Program &P; + }; + + /// Returns the current declaration ID. + llvm::Optional getCurrentDecl() const { + if (CurrentDeclaration == NoDeclaration) + return llvm::Optional{}; + return LastDeclaration; + } + +private: + friend class DeclScope; + + llvm::Optional createGlobal(const DeclTy &D, QualType Ty, + bool IsStatic, bool IsExtern); + + /// Reference to the VM context. + Context &Ctx; + /// Mapping from decls to cached bytecode functions. + llvm::DenseMap> Funcs; + /// List of anonymous functions. + std::vector> AnonFuncs; + + /// Function relocation locations. + llvm::DenseMap> Relocs; + + /// Custom allocator for global storage. + using PoolAllocTy = llvm::BumpPtrAllocatorImpl; + + /// Descriptor + storage for a global object. + /// + /// Global objects never go out of scope, thus they do not track pointers. + class Global { + public: + /// Create a global descriptor for string literals. + template + Global(Tys... Args) : B(std::forward(Args)...) {} + + /// Allocates the global in the pool, reserving storate for data. + void *operator new(size_t Meta, PoolAllocTy &Alloc, size_t Data) { + return Alloc.Allocate(Meta + Data, alignof(void *)); + } + + /// Return a pointer to the data. + char *data() { return B.data(); } + /// Return a pointer to the block. + Block *block() { return &B; } + + private: + /// Required metadata - does not actually track pointers. + Block B; + }; + + /// Allocator for globals. + PoolAllocTy Allocator; + + /// Global objects. + std::vector Globals; + /// Cached global indices. + llvm::DenseMap GlobalIndices; + + /// Mapping from decls to record metadata. + llvm::DenseMap Records; + + /// Dummy parameter to generate pointers from. + llvm::DenseMap DummyParams; + + /// Creates a new descriptor. + template + Descriptor *allocateDescriptor(Ts &&... Args) { + return new (Allocator) Descriptor(std::forward(Args)...); + } + + /// No declaration ID. + static constexpr unsigned NoDeclaration = (unsigned)-1; + /// Last declaration ID. + unsigned LastDeclaration = 0; + /// Current declaration ID. + unsigned CurrentDeclaration = NoDeclaration; + + /// Starts evaluating a declaration. + void startDeclaration(const VarDecl *Decl) { + LastDeclaration += 1; + CurrentDeclaration = LastDeclaration; + } + + /// Ends a global declaration. + void endDeclaration() { + CurrentDeclaration = NoDeclaration; + } + +public: + /// Dumps the disassembled bytecode to \c llvm::errs(). + void dump() const; + void dump(llvm::raw_ostream &OS) const; +}; + +} // namespace interp +} // namespace clang + +#endif diff --git a/lib/AST/Interp/Record.cpp b/lib/AST/Interp/Record.cpp new file mode 100644 index 0000000000000..f440c4705051e --- /dev/null +++ b/lib/AST/Interp/Record.cpp @@ -0,0 +1,46 @@ +//===--- Record.cpp - struct and class metadata for the VM ------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "Record.h" + +using namespace clang; +using namespace clang::interp; + +Record::Record(const RecordDecl *Decl, BaseList &&SrcBases, + FieldList &&SrcFields, VirtualBaseList &&SrcVirtualBases, + unsigned VirtualSize, unsigned BaseSize) + : Decl(Decl), Bases(std::move(SrcBases)), Fields(std::move(SrcFields)), + BaseSize(BaseSize), VirtualSize(VirtualSize) { + for (Base &V : SrcVirtualBases) + VirtualBases.push_back({ V.Decl, V.Offset + BaseSize, V.Desc, V.R }); + + for (Base &B : Bases) + BaseMap[B.Decl] = &B; + for (Field &F : Fields) + FieldMap[F.Decl] = &F; + for (Base &V : VirtualBases) + VirtualBaseMap[V.Decl] = &V; +} + +const Record::Field *Record::getField(const FieldDecl *FD) const { + auto It = FieldMap.find(FD); + assert(It != FieldMap.end() && "Missing field"); + return It->second; +} + +const Record::Base *Record::getBase(const RecordDecl *FD) const { + auto It = BaseMap.find(FD); + assert(It != BaseMap.end() && "Missing base"); + return It->second; +} + +const Record::Base *Record::getVirtualBase(const RecordDecl *FD) const { + auto It = VirtualBaseMap.find(FD); + assert(It != VirtualBaseMap.end() && "Missing virtual base"); + return It->second; +} diff --git a/lib/AST/Interp/Record.h b/lib/AST/Interp/Record.h new file mode 100644 index 0000000000000..9cdee9003752e --- /dev/null +++ b/lib/AST/Interp/Record.h @@ -0,0 +1,121 @@ +//===--- Record.h - struct and class metadata for the VM --------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// A record is part of a program to describe the layout and methods of a struct. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_AST_INTERP_RECORD_H +#define LLVM_CLANG_AST_INTERP_RECORD_H + +#include "Pointer.h" + +namespace clang { +namespace interp { +class Program; + +/// Structure/Class descriptor. +class Record { +public: + /// Describes a record field. + struct Field { + const FieldDecl *Decl; + unsigned Offset; + Descriptor *Desc; + }; + + /// Describes a base class. + struct Base { + const RecordDecl *Decl; + unsigned Offset; + Descriptor *Desc; + Record *R; + }; + + /// Mapping from identifiers to field descriptors. + using FieldList = llvm::SmallVector; + /// Mapping from identifiers to base classes. + using BaseList = llvm::SmallVector; + /// List of virtual base classes. + using VirtualBaseList = llvm::SmallVector; + +public: + /// Returns the underlying declaration. + const RecordDecl *getDecl() const { return Decl; } + /// Checks if the record is a union. + bool isUnion() const { return getDecl()->isUnion(); } + /// Returns the size of the record. + unsigned getSize() const { return BaseSize; } + /// Returns the full size of the record, including records. + unsigned getFullSize() const { return BaseSize + VirtualSize; } + /// Returns a field. + const Field *getField(const FieldDecl *FD) const; + /// Returns a base descriptor. + const Base *getBase(const RecordDecl *FD) const; + /// Returns a virtual base descriptor. + const Base *getVirtualBase(const RecordDecl *RD) const; + + using const_field_iter = FieldList::const_iterator; + llvm::iterator_range fields() const { + return llvm::make_range(Fields.begin(), Fields.end()); + } + + unsigned getNumFields() { return Fields.size(); } + Field *getField(unsigned I) { return &Fields[I]; } + + using const_base_iter = BaseList::const_iterator; + llvm::iterator_range bases() const { + return llvm::make_range(Bases.begin(), Bases.end()); + } + + unsigned getNumBases() { return Bases.size(); } + Base *getBase(unsigned I) { return &Bases[I]; } + + using const_virtual_iter = VirtualBaseList::const_iterator; + llvm::iterator_range virtual_bases() const { + return llvm::make_range(VirtualBases.begin(), VirtualBases.end()); + } + + unsigned getNumVirtualBases() { return VirtualBases.size(); } + Base *getVirtualBase(unsigned I) { return &VirtualBases[I]; } + +private: + /// Constructor used by Program to create record descriptors. + Record(const RecordDecl *, BaseList &&Bases, FieldList &&Fields, + VirtualBaseList &&VirtualBases, unsigned VirtualSize, + unsigned BaseSize); + +private: + friend class Program; + + /// Original declaration. + const RecordDecl *Decl; + /// List of all base classes. + BaseList Bases; + /// List of all the fields in the record. + FieldList Fields; + /// List o fall virtual bases. + VirtualBaseList VirtualBases; + + /// Mapping from declarations to bases. + llvm::DenseMap BaseMap; + /// Mapping from field identifiers to descriptors. + llvm::DenseMap FieldMap; + /// Mapping from declarations to virtual bases. + llvm::DenseMap VirtualBaseMap; + /// Mapping from + /// Size of the structure. + unsigned BaseSize; + /// Size of all virtual bases. + unsigned VirtualSize; +}; + +} // namespace interp +} // namespace clang + +#endif diff --git a/lib/AST/Interp/Source.cpp b/lib/AST/Interp/Source.cpp new file mode 100644 index 0000000000000..4bec878126384 --- /dev/null +++ b/lib/AST/Interp/Source.cpp @@ -0,0 +1,39 @@ +//===--- Source.cpp - Source expression tracking ----------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "Source.h" +#include "clang/AST/Expr.h" + +using namespace clang; +using namespace clang::interp; + +SourceLocation SourceInfo::getLoc() const { + if (const Expr *E = asExpr()) + return E->getExprLoc(); + if (const Stmt *S = asStmt()) + return S->getBeginLoc(); + if (const Decl *D = asDecl()) + return D->getBeginLoc(); + return SourceLocation(); +} + +const Expr *SourceInfo::asExpr() const { + if (auto *S = Source.dyn_cast()) + return dyn_cast(S); + return nullptr; +} + +const Expr *SourceMapper::getExpr(Function *F, CodePtr PC) const { + if (const Expr *E = getSource(F, PC).asExpr()) + return E; + llvm::report_fatal_error("missing source expression"); +} + +SourceLocation SourceMapper::getLocation(Function *F, CodePtr PC) const { + return getSource(F, PC).getLoc(); +} diff --git a/lib/AST/Interp/Source.h b/lib/AST/Interp/Source.h new file mode 100644 index 0000000000000..e591c3399d7c1 --- /dev/null +++ b/lib/AST/Interp/Source.h @@ -0,0 +1,118 @@ +//===--- Source.h - Source location provider for the VM --------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Defines a program which organises and links multiple bytecode functions. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_AST_INTERP_SOURCE_H +#define LLVM_CLANG_AST_INTERP_SOURCE_H + +#include "clang/AST/Decl.h" +#include "clang/AST/Stmt.h" +#include "llvm/Support/Endian.h" + +namespace clang { +namespace interp { +class Function; + +/// Pointer into the code segment. +class CodePtr { +public: + CodePtr() : Ptr(nullptr) {} + + CodePtr &operator+=(int32_t Offset) { + Ptr += Offset; + return *this; + } + + int32_t operator-(const CodePtr &RHS) const { + assert(Ptr != nullptr && RHS.Ptr != nullptr && "Invalid code pointer"); + return Ptr - RHS.Ptr; + } + + CodePtr operator-(size_t RHS) const { + assert(Ptr != nullptr && "Invalid code pointer"); + return CodePtr(Ptr - RHS); + } + + bool operator!=(const CodePtr &RHS) const { return Ptr != RHS.Ptr; } + + /// Reads data and advances the pointer. + template T read() { + T Value = ReadHelper(Ptr); + Ptr += sizeof(T); + return Value; + } + +private: + /// Constructor used by Function to generate pointers. + CodePtr(const char *Ptr) : Ptr(Ptr) {} + + /// Helper to decode a value or a pointer. + template + static typename std::enable_if::value, T>::type + ReadHelper(const char *Ptr) { + using namespace llvm::support; + return endian::read(Ptr); + } + + template + static typename std::enable_if::value, T>::type + ReadHelper(const char *Ptr) { + using namespace llvm::support; + auto Punned = endian::read(Ptr); + return reinterpret_cast(Punned); + } + +private: + friend class Function; + + /// Pointer into the code owned by a function. + const char *Ptr; +}; + +/// Describes the statement/declaration an opcode was generated from. +class SourceInfo { +public: + SourceInfo() {} + SourceInfo(const Stmt *E) : Source(E) {} + SourceInfo(const Decl *D) : Source(D) {} + + SourceLocation getLoc() const; + + const Stmt *asStmt() const { return Source.dyn_cast(); } + const Decl *asDecl() const { return Source.dyn_cast(); } + const Expr *asExpr() const; + + operator bool() const { return !Source.isNull(); } + +private: + llvm::PointerUnion Source; +}; + +using SourceMap = std::vector>; + +/// Interface for classes which map locations to sources. +class SourceMapper { +public: + virtual ~SourceMapper() {} + + /// Returns source information for a given PC in a function. + virtual SourceInfo getSource(Function *F, CodePtr PC) const = 0; + + /// Returns the expression if an opcode belongs to one, null otherwise. + const Expr *getExpr(Function *F, CodePtr PC) const; + /// Returns the location from which an opcode originates. + SourceLocation getLocation(Function *F, CodePtr PC) const; +}; + +} // namespace interp +} // namespace clang + +#endif diff --git a/lib/AST/Interp/State.cpp b/lib/AST/Interp/State.cpp new file mode 100644 index 0000000000000..692cc2e8d69bf --- /dev/null +++ b/lib/AST/Interp/State.cpp @@ -0,0 +1,158 @@ +//===--- State.cpp - State chain for the VM and AST Walker ------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "State.h" +#include "Frame.h" +#include "Program.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/CXXInheritance.h" + +using namespace clang; +using namespace clang::interp; + +State::~State() {} + +OptionalDiagnostic State::FFDiag(SourceLocation Loc, diag::kind DiagId, + unsigned ExtraNotes) { + return diag(Loc, DiagId, ExtraNotes, false); +} + +OptionalDiagnostic State::FFDiag(const Expr *E, diag::kind DiagId, + unsigned ExtraNotes) { + if (getEvalStatus().Diag) + return diag(E->getExprLoc(), DiagId, ExtraNotes, false); + setActiveDiagnostic(false); + return OptionalDiagnostic(); +} + +OptionalDiagnostic State::FFDiag(const SourceInfo &SI, diag::kind DiagId, + unsigned ExtraNotes) { + if (getEvalStatus().Diag) + return diag(SI.getLoc(), DiagId, ExtraNotes, false); + setActiveDiagnostic(false); + return OptionalDiagnostic(); +} + +OptionalDiagnostic State::CCEDiag(SourceLocation Loc, diag::kind DiagId, + unsigned ExtraNotes) { + // Don't override a previous diagnostic. Don't bother collecting + // diagnostics if we're evaluating for overflow. + if (!getEvalStatus().Diag || !getEvalStatus().Diag->empty()) { + setActiveDiagnostic(false); + return OptionalDiagnostic(); + } + return diag(Loc, DiagId, ExtraNotes, true); +} + +OptionalDiagnostic State::CCEDiag(const Expr *E, diag::kind DiagId, + unsigned ExtraNotes) { + return CCEDiag(E->getExprLoc(), DiagId, ExtraNotes); +} + +OptionalDiagnostic State::CCEDiag(const SourceInfo &SI, diag::kind DiagId, + unsigned ExtraNotes) { + return CCEDiag(SI.getLoc(), DiagId, ExtraNotes); +} + +OptionalDiagnostic State::Note(SourceLocation Loc, diag::kind DiagId) { + if (!hasActiveDiagnostic()) + return OptionalDiagnostic(); + return OptionalDiagnostic(&addDiag(Loc, DiagId)); +} + +void State::addNotes(ArrayRef Diags) { + if (hasActiveDiagnostic()) { + getEvalStatus().Diag->insert(getEvalStatus().Diag->end(), Diags.begin(), + Diags.end()); + } +} + +DiagnosticBuilder State::report(SourceLocation Loc, diag::kind DiagId) { + return getCtx().getDiagnostics().Report(Loc, DiagId); +} + +/// Add a diagnostic to the diagnostics list. +PartialDiagnostic &State::addDiag(SourceLocation Loc, diag::kind DiagId) { + PartialDiagnostic PD(DiagId, getCtx().getDiagAllocator()); + getEvalStatus().Diag->push_back(std::make_pair(Loc, PD)); + return getEvalStatus().Diag->back().second; +} + +OptionalDiagnostic State::diag(SourceLocation Loc, diag::kind DiagId, + unsigned ExtraNotes, bool IsCCEDiag) { + Expr::EvalStatus &EvalStatus = getEvalStatus(); + if (EvalStatus.Diag) { + if (hasPriorDiagnostic()) { + return OptionalDiagnostic(); + } + + unsigned CallStackNotes = getCallStackDepth() - 1; + unsigned Limit = getCtx().getDiagnostics().getConstexprBacktraceLimit(); + if (Limit) + CallStackNotes = std::min(CallStackNotes, Limit + 1); + if (checkingPotentialConstantExpression()) + CallStackNotes = 0; + + setActiveDiagnostic(true); + setFoldFailureDiagnostic(!IsCCEDiag); + EvalStatus.Diag->clear(); + EvalStatus.Diag->reserve(1 + ExtraNotes + CallStackNotes); + addDiag(Loc, DiagId); + if (!checkingPotentialConstantExpression()) { + addCallStack(Limit); + } + return OptionalDiagnostic(&(*EvalStatus.Diag)[0].second); + } + setActiveDiagnostic(false); + return OptionalDiagnostic(); +} + +const LangOptions &State::getLangOpts() const { return getCtx().getLangOpts(); } + +void State::addCallStack(unsigned Limit) { + // Determine which calls to skip, if any. + unsigned ActiveCalls = getCallStackDepth() - 1; + unsigned SkipStart = ActiveCalls, SkipEnd = SkipStart; + if (Limit && Limit < ActiveCalls) { + SkipStart = Limit / 2 + Limit % 2; + SkipEnd = ActiveCalls - Limit / 2; + } + + // Walk the call stack and add the diagnostics. + unsigned CallIdx = 0; + Frame *Top = getCurrentFrame(); + const Frame *Bottom = getBottomFrame(); + for (Frame *F = Top; F != Bottom; F = F->getCaller(), ++CallIdx) { + SourceLocation CallLocation = F->getCallLocation(); + + // Skip this call? + if (CallIdx >= SkipStart && CallIdx < SkipEnd) { + if (CallIdx == SkipStart) { + // Note that we're skipping calls. + addDiag(CallLocation, diag::note_constexpr_calls_suppressed) + << unsigned(ActiveCalls - Limit); + } + continue; + } + + // Use a different note for an inheriting constructor, because from the + // user's perspective it's not really a function at all. + if (auto *CD = dyn_cast_or_null(F->getCallee())) { + if (CD->isInheritingConstructor()) { + addDiag(CallLocation, diag::note_constexpr_inherited_ctor_call_here) + << CD->getParent(); + continue; + } + } + + SmallVector Buffer; + llvm::raw_svector_ostream Out(Buffer); + F->describe(Out); + addDiag(CallLocation, diag::note_constexpr_call_here) << Out.str(); + } +} diff --git a/lib/AST/Interp/State.h b/lib/AST/Interp/State.h new file mode 100644 index 0000000000000..d9a645a3eb3eb --- /dev/null +++ b/lib/AST/Interp/State.h @@ -0,0 +1,133 @@ +//===--- State.h - State chain for the VM and AST Walker --------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Defines the base class of the interpreter and evaluator state. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_AST_INTERP_STATE_H +#define LLVM_CLANG_AST_INTERP_STATE_H + +#include "clang/AST/ASTDiagnostic.h" +#include "clang/AST/Expr.h" +#include "clang/AST/OptionalDiagnostic.h" + +namespace clang { + +/// Kinds of access we can perform on an object, for diagnostics. Note that +/// we consider a member function call to be a kind of access, even though +/// it is not formally an access of the object, because it has (largely) the +/// same set of semantic restrictions. +enum AccessKinds { + AK_Read, + AK_ReadObjectRepresentation, + AK_Assign, + AK_Increment, + AK_Decrement, + AK_MemberCall, + AK_DynamicCast, + AK_TypeId, + AK_Construct, + AK_Destroy, +}; + +// The order of this enum is important for diagnostics. +enum CheckSubobjectKind { + CSK_Base, + CSK_Derived, + CSK_Field, + CSK_ArrayToPointer, + CSK_ArrayIndex, + CSK_Real, + CSK_Imag +}; + +namespace interp { +class Frame; +class SourceInfo; + +/// Interface for the VM to interact with the AST walker's context. +class State { +public: + virtual ~State(); + + virtual bool checkingForUndefinedBehavior() const = 0; + virtual bool checkingPotentialConstantExpression() const = 0; + virtual bool noteUndefinedBehavior() = 0; + virtual bool keepEvaluatingAfterFailure() const = 0; + virtual Frame *getCurrentFrame() = 0; + virtual const Frame *getBottomFrame() const = 0; + virtual bool hasActiveDiagnostic() = 0; + virtual void setActiveDiagnostic(bool Flag) = 0; + virtual void setFoldFailureDiagnostic(bool Flag) = 0; + virtual Expr::EvalStatus &getEvalStatus() const = 0; + virtual ASTContext &getCtx() const = 0; + virtual bool hasPriorDiagnostic() = 0; + virtual unsigned getCallStackDepth() = 0; + +public: + // Diagnose that the evaluation could not be folded (FF => FoldFailure) + OptionalDiagnostic + FFDiag(SourceLocation Loc, + diag::kind DiagId = diag::note_invalid_subexpr_in_const_expr, + unsigned ExtraNotes = 0); + + OptionalDiagnostic + FFDiag(const Expr *E, + diag::kind DiagId = diag::note_invalid_subexpr_in_const_expr, + unsigned ExtraNotes = 0); + + OptionalDiagnostic + FFDiag(const SourceInfo &SI, + diag::kind DiagId = diag::note_invalid_subexpr_in_const_expr, + unsigned ExtraNotes = 0); + + /// Diagnose that the evaluation does not produce a C++11 core constant + /// expression. + /// + /// FIXME: Stop evaluating if we're in EM_ConstantExpression or + /// EM_PotentialConstantExpression mode and we produce one of these. + OptionalDiagnostic + CCEDiag(SourceLocation Loc, + diag::kind DiagId = diag::note_invalid_subexpr_in_const_expr, + unsigned ExtraNotes = 0); + + OptionalDiagnostic + CCEDiag(const Expr *E, + diag::kind DiagId = diag::note_invalid_subexpr_in_const_expr, + unsigned ExtraNotes = 0); + + OptionalDiagnostic + CCEDiag(const SourceInfo &SI, + diag::kind DiagId = diag::note_invalid_subexpr_in_const_expr, + unsigned ExtraNotes = 0); + + /// Add a note to a prior diagnostic. + OptionalDiagnostic Note(SourceLocation Loc, diag::kind DiagId); + + /// Add a stack of notes to a prior diagnostic. + void addNotes(ArrayRef Diags); + + /// Directly reports a diagnostic message. + DiagnosticBuilder report(SourceLocation Loc, diag::kind DiagId); + + const LangOptions &getLangOpts() const; + +private: + void addCallStack(unsigned Limit); + + PartialDiagnostic &addDiag(SourceLocation Loc, diag::kind DiagId); + + OptionalDiagnostic diag(SourceLocation Loc, diag::kind DiagId, + unsigned ExtraNotes, bool IsCCEDiag); +}; + +} // namespace interp +} // namespace clang + +#endif diff --git a/lib/AST/ItaniumCXXABI.cpp b/lib/AST/ItaniumCXXABI.cpp index 727a905d08a14..069add8464ae8 100644 --- a/lib/AST/ItaniumCXXABI.cpp +++ b/lib/AST/ItaniumCXXABI.cpp @@ -19,10 +19,12 @@ #include "CXXABI.h" #include "clang/AST/ASTContext.h" #include "clang/AST/DeclCXX.h" +#include "clang/AST/Mangle.h" #include "clang/AST/MangleNumberingContext.h" #include "clang/AST/RecordLayout.h" #include "clang/AST/Type.h" #include "clang/Basic/TargetInfo.h" +#include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/iterator.h" using namespace clang; @@ -73,10 +75,33 @@ struct DecompositionDeclName { } namespace llvm { +template bool isDenseMapKeyEmpty(T V) { + return llvm::DenseMapInfo::isEqual( + V, llvm::DenseMapInfo::getEmptyKey()); +} +template bool isDenseMapKeyTombstone(T V) { + return llvm::DenseMapInfo::isEqual( + V, llvm::DenseMapInfo::getTombstoneKey()); +} + +template +Optional areDenseMapKeysEqualSpecialValues(T LHS, T RHS) { + bool LHSEmpty = isDenseMapKeyEmpty(LHS); + bool RHSEmpty = isDenseMapKeyEmpty(RHS); + if (LHSEmpty || RHSEmpty) + return LHSEmpty && RHSEmpty; + + bool LHSTombstone = isDenseMapKeyTombstone(LHS); + bool RHSTombstone = isDenseMapKeyTombstone(RHS); + if (LHSTombstone || RHSTombstone) + return LHSTombstone && RHSTombstone; + + return None; +} + template<> struct DenseMapInfo { using ArrayInfo = llvm::DenseMapInfo>; - using IdentInfo = llvm::DenseMapInfo; static DecompositionDeclName getEmptyKey() { return {ArrayInfo::getEmptyKey()}; } @@ -88,10 +113,10 @@ struct DenseMapInfo { return llvm::hash_combine_range(Key.begin(), Key.end()); } static bool isEqual(DecompositionDeclName LHS, DecompositionDeclName RHS) { - if (ArrayInfo::isEqual(LHS.Bindings, ArrayInfo::getEmptyKey())) - return ArrayInfo::isEqual(RHS.Bindings, ArrayInfo::getEmptyKey()); - if (ArrayInfo::isEqual(LHS.Bindings, ArrayInfo::getTombstoneKey())) - return ArrayInfo::isEqual(RHS.Bindings, ArrayInfo::getTombstoneKey()); + if (Optional Result = areDenseMapKeysEqualSpecialValues( + LHS.Bindings, RHS.Bindings)) + return *Result; + return LHS.Bindings.size() == RHS.Bindings.size() && std::equal(LHS.begin(), LHS.end(), RHS.begin()); } @@ -103,29 +128,32 @@ namespace { /// Keeps track of the mangled names of lambda expressions and block /// literals within a particular context. class ItaniumNumberingContext : public MangleNumberingContext { - llvm::DenseMap ManglingNumbers; + ItaniumMangleContext *Mangler; + llvm::StringMap LambdaManglingNumbers; + unsigned BlockManglingNumber = 0; llvm::DenseMap VarManglingNumbers; llvm::DenseMap TagManglingNumbers; llvm::DenseMap DecompsitionDeclManglingNumbers; public: + ItaniumNumberingContext(ItaniumMangleContext *Mangler) : Mangler(Mangler) {} + unsigned getManglingNumber(const CXXMethodDecl *CallOperator) override { - const FunctionProtoType *Proto = - CallOperator->getType()->getAs(); - ASTContext &Context = CallOperator->getASTContext(); + const CXXRecordDecl *Lambda = CallOperator->getParent(); + assert(Lambda->isLambda()); + + // Computation of the is non-trivial and subtle. Rather than + // duplicating it here, just mangle the directly. + llvm::SmallString<128> LambdaSig; + llvm::raw_svector_ostream Out(LambdaSig); + Mangler->mangleLambdaSig(Lambda, Out); - FunctionProtoType::ExtProtoInfo EPI; - EPI.Variadic = Proto->isVariadic(); - QualType Key = - Context.getFunctionType(Context.VoidTy, Proto->getParamTypes(), EPI); - Key = Context.getCanonicalType(Key); - return ++ManglingNumbers[Key->castAs()]; + return ++LambdaManglingNumbers[LambdaSig]; } unsigned getManglingNumber(const BlockDecl *BD) override { - const Type *Ty = nullptr; - return ++ManglingNumbers[Ty]; + return ++BlockManglingNumber; } unsigned getStaticLocalNumber(const VarDecl *VD) override { @@ -154,10 +182,13 @@ public: }; class ItaniumCXXABI : public CXXABI { +private: + std::unique_ptr Mangler; protected: ASTContext &Context; public: - ItaniumCXXABI(ASTContext &Ctx) : Context(Ctx) { } + ItaniumCXXABI(ASTContext &Ctx) + : Mangler(Ctx.createMangleContext()), Context(Ctx) {} MemberPointerInfo getMemberPointerInfo(const MemberPointerType *MPT) const override { @@ -177,7 +208,7 @@ public: if (!isVariadic && T.isWindowsGNUEnvironment() && T.getArch() == llvm::Triple::x86) return CC_X86ThisCall; - return CC_C; + return Context.getTargetInfo().getDefaultCallingConv(); } // We cheat and just check that the class has a vtable pointer, and that it's @@ -218,7 +249,8 @@ public: std::unique_ptr createMangleNumberingContext() const override { - return llvm::make_unique(); + return std::make_unique( + cast(Mangler.get())); } }; } diff --git a/lib/AST/ItaniumMangle.cpp b/lib/AST/ItaniumMangle.cpp index 6c813f09a4b3c..c55a901375787 100644 --- a/lib/AST/ItaniumMangle.cpp +++ b/lib/AST/ItaniumMangle.cpp @@ -170,6 +170,8 @@ public: void mangleStringLiteral(const StringLiteral *, raw_ostream &) override; + void mangleLambdaSig(const CXXRecordDecl *Lambda, raw_ostream &) override; + bool getNextDiscriminator(const NamedDecl *ND, unsigned &disc) { // Lambda closure types are already numbered. if (isLambda(ND)) @@ -424,6 +426,7 @@ public: void mangleName(const NamedDecl *ND); void mangleType(QualType T); void mangleNameOrStandardSubstitution(const NamedDecl *ND); + void mangleLambdaSig(const CXXRecordDecl *Lambda); private: @@ -513,7 +516,7 @@ private: #define ABSTRACT_TYPE(CLASS, PARENT) #define NON_CANONICAL_TYPE(CLASS, PARENT) #define TYPE(CLASS, PARENT) void mangleType(const CLASS##Type *T); -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" void mangleType(const TagType*); void mangleType(TemplateName); @@ -550,7 +553,7 @@ private: void mangleTemplateArgs(const TemplateArgumentList &AL); void mangleTemplateArg(TemplateArgument A); - void mangleTemplateParameter(unsigned Index); + void mangleTemplateParameter(unsigned Depth, unsigned Index); void mangleFunctionParam(const ParmVarDecl *parm); @@ -965,8 +968,8 @@ void CXXNameMangler::mangleUnscopedTemplateName( if (const auto *TTP = dyn_cast(ND)) { assert(!AdditionalAbiTags && "template template param cannot have abi tags"); - mangleTemplateParameter(TTP->getIndex()); - } else if (isa(ND)) { + mangleTemplateParameter(TTP->getDepth(), TTP->getIndex()); + } else if (isa(ND) || isa(ND)) { mangleUnscopedName(ND, AdditionalAbiTags); } else { mangleUnscopedName(ND->getTemplatedDecl(), AdditionalAbiTags); @@ -1321,7 +1324,7 @@ void CXXNameMangler::mangleUnqualifiedName(const NamedDecl *ND, if (const VarDecl *VD = dyn_cast(ND)) { // We must have an anonymous union or struct declaration. - const RecordDecl *RD = VD->getType()->getAs()->getDecl(); + const RecordDecl *RD = VD->getType()->castAs()->getDecl(); // Itanium C++ ABI 5.1.2: // @@ -1685,17 +1688,45 @@ void CXXNameMangler::mangleUnqualifiedBlock(const BlockDecl *Block) { // ::= Ty # template type parameter // ::= Tn # template non-type parameter // ::= Tt * E # template template parameter +// ::= Tp # template parameter pack void CXXNameMangler::mangleTemplateParamDecl(const NamedDecl *Decl) { - if (isa(Decl)) { + if (auto *Ty = dyn_cast(Decl)) { + if (Ty->isParameterPack()) + Out << "Tp"; Out << "Ty"; } else if (auto *Tn = dyn_cast(Decl)) { - Out << "Tn"; - mangleType(Tn->getType()); + if (Tn->isExpandedParameterPack()) { + for (unsigned I = 0, N = Tn->getNumExpansionTypes(); I != N; ++I) { + Out << "Tn"; + mangleType(Tn->getExpansionType(I)); + } + } else { + QualType T = Tn->getType(); + if (Tn->isParameterPack()) { + Out << "Tp"; + if (auto *PackExpansion = T->getAs()) + T = PackExpansion->getPattern(); + } + Out << "Tn"; + mangleType(T); + } } else if (auto *Tt = dyn_cast(Decl)) { - Out << "Tt"; - for (auto *Param : *Tt->getTemplateParameters()) - mangleTemplateParamDecl(Param); - Out << "E"; + if (Tt->isExpandedParameterPack()) { + for (unsigned I = 0, N = Tt->getNumExpansionTemplateParameters(); I != N; + ++I) { + Out << "Tt"; + for (auto *Param : *Tt->getExpansionTemplateParameters(I)) + mangleTemplateParamDecl(Param); + Out << "E"; + } + } else { + if (Tt->isParameterPack()) + Out << "Tp"; + Out << "Tt"; + for (auto *Param : *Tt->getTemplateParameters()) + mangleTemplateParamDecl(Param); + Out << "E"; + } } } @@ -1726,12 +1757,7 @@ void CXXNameMangler::mangleLambda(const CXXRecordDecl *Lambda) { } Out << "Ul"; - for (auto *D : Lambda->getLambdaExplicitTemplateParameters()) - mangleTemplateParamDecl(D); - const FunctionProtoType *Proto = Lambda->getLambdaTypeInfo()->getType()-> - getAs(); - mangleBareFunctionType(Proto, /*MangleReturnType=*/false, - Lambda->getLambdaStaticInvoker()); + mangleLambdaSig(Lambda); Out << "E"; // The number is omitted for the first closure type with a given @@ -1746,6 +1772,15 @@ void CXXNameMangler::mangleLambda(const CXXRecordDecl *Lambda) { Out << '_'; } +void CXXNameMangler::mangleLambdaSig(const CXXRecordDecl *Lambda) { + for (auto *D : Lambda->getLambdaExplicitTemplateParameters()) + mangleTemplateParamDecl(D); + const FunctionProtoType *Proto = Lambda->getLambdaTypeInfo()->getType()-> + getAs(); + mangleBareFunctionType(Proto, /*MangleReturnType=*/false, + Lambda->getLambdaStaticInvoker()); +} + void CXXNameMangler::manglePrefix(NestedNameSpecifier *qualifier) { switch (qualifier->getKind()) { case NestedNameSpecifier::Global: @@ -1852,10 +1887,10 @@ void CXXNameMangler::mangleTemplatePrefix(const TemplateDecl *ND, // ::= if (const auto *TTP = dyn_cast(ND)) { - mangleTemplateParameter(TTP->getIndex()); + mangleTemplateParameter(TTP->getDepth(), TTP->getIndex()); } else { manglePrefix(getEffectiveDeclContext(ND), NoFunction); - if (isa(ND)) + if (isa(ND) || isa(ND)) mangleUnqualifiedName(ND, nullptr); else mangleUnqualifiedName(ND->getTemplatedDecl(), nullptr); @@ -1885,8 +1920,8 @@ void CXXNameMangler::mangleType(TemplateName TN) { goto HaveDecl; HaveDecl: - if (isa(TD)) - mangleTemplateParameter(cast(TD)->getIndex()); + if (auto *TTP = dyn_cast(TD)) + mangleTemplateParameter(TTP->getDepth(), TTP->getIndex()); else mangleName(TD); break; @@ -2464,7 +2499,7 @@ void CXXNameMangler::mangleType(QualType T) { case Type::CLASS: \ mangleType(static_cast(ty)); \ break; -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" } } @@ -2671,6 +2706,15 @@ void CXXNameMangler::mangleType(const BuiltinType *T) { Out << type_name.size() << type_name; \ break; #include "clang/Basic/OpenCLExtensionTypes.def" + // The SVE types are effectively target-specific. The mangling scheme + // is defined in the appendices to the Procedure Call Standard for the + // Arm Architecture. +#define SVE_TYPE(Name, Id, SingletonId) \ + case BuiltinType::Id: \ + type_name = Name; \ + Out << 'u' << type_name.size() << type_name; \ + break; +#include "clang/Basic/AArch64SVEACLETypes.def" } } @@ -2955,7 +2999,7 @@ void CXXNameMangler::mangleType(const MemberPointerType *T) { // ::= void CXXNameMangler::mangleType(const TemplateTypeParmType *T) { - mangleTemplateParameter(T->getIndex()); + mangleTemplateParameter(T->getDepth(), T->getIndex()); } // ::= @@ -3526,7 +3570,7 @@ void CXXNameMangler::mangleDeclRefExpr(const NamedDecl *D) { case Decl::NonTypeTemplateParm: const NonTypeTemplateParmDecl *PD = cast(D); - mangleTemplateParameter(PD->getIndex()); + mangleTemplateParameter(PD->getDepth(), PD->getIndex()); break; } } @@ -4046,6 +4090,17 @@ recurse: break; } + case Expr::CXXRewrittenBinaryOperatorClass: { + // The mangled form represents the original syntax. + CXXRewrittenBinaryOperator::DecomposedForm Decomposed = + cast(E)->getDecomposedForm(); + mangleOperatorName(BinaryOperator::getOverloadedOperator(Decomposed.Opcode), + /*Arity=*/2); + mangleExpression(Decomposed.LHS); + mangleExpression(Decomposed.RHS); + break; + } + case Expr::ConditionalOperatorClass: { const ConditionalOperator *CO = cast(E); mangleOperatorName(OO_Conditional, /*Arity=*/3); @@ -4123,6 +4178,18 @@ recurse: mangleExpression(cast(E)->getSubExpr(), Arity); break; + + case Expr::ConceptSpecializationExprClass: { + // ::= L E # external name + Out << "L_Z"; + auto *CSE = cast(E); + mangleTemplateName(CSE->getNamedConcept(), + CSE->getTemplateArguments().data(), + CSE->getTemplateArguments().size()); + Out << 'E'; + break; + } + case Expr::DeclRefExprClass: mangleDeclRefExpr(cast(E)->getDecl()); break; @@ -4229,8 +4296,11 @@ recurse: } case Expr::GNUNullExprClass: - // FIXME: should this really be mangled the same as nullptr? - // fallthrough + // Mangle as if an integer literal 0. + Out << 'L'; + mangleType(E->getType()); + Out << "0E"; + break; case Expr::CXXNullPtrLiteralExprClass: { Out << "LDnE"; @@ -4255,13 +4325,13 @@ recurse: Out << "sZ"; const NamedDecl *Pack = SPE->getPack(); if (const TemplateTypeParmDecl *TTP = dyn_cast(Pack)) - mangleTemplateParameter(TTP->getIndex()); + mangleTemplateParameter(TTP->getDepth(), TTP->getIndex()); else if (const NonTypeTemplateParmDecl *NTTP = dyn_cast(Pack)) - mangleTemplateParameter(NTTP->getIndex()); + mangleTemplateParameter(NTTP->getDepth(), NTTP->getIndex()); else if (const TemplateTemplateParmDecl *TempTP = dyn_cast(Pack)) - mangleTemplateParameter(TempTP->getIndex()); + mangleTemplateParameter(TempTP->getDepth(), TempTP->getIndex()); else mangleFunctionParam(cast(Pack)); break; @@ -4548,13 +4618,21 @@ void CXXNameMangler::mangleTemplateArg(TemplateArgument A) { } } -void CXXNameMangler::mangleTemplateParameter(unsigned Index) { +void CXXNameMangler::mangleTemplateParameter(unsigned Depth, unsigned Index) { // ::= T_ # first template parameter // ::= T _ - if (Index == 0) - Out << "T_"; - else - Out << 'T' << (Index - 1) << '_'; + // ::= TL __ + // ::= TL _ + // _ + // + // The latter two manglings are from a proposal here: + // https://github.com/itanium-cxx-abi/cxx-abi/issues/31#issuecomment-528122117 + Out << 'T'; + if (Depth != 0) + Out << 'L' << (Depth - 1) << '_'; + if (Index != 0) + Out << (Index - 1); + Out << '_'; } void CXXNameMangler::mangleSeqID(unsigned SeqID) { @@ -5071,6 +5149,12 @@ void ItaniumMangleContextImpl::mangleStringLiteral(const StringLiteral *, raw_os llvm_unreachable("Can't mangle string literals"); } +void ItaniumMangleContextImpl::mangleLambdaSig(const CXXRecordDecl *Lambda, + raw_ostream &Out) { + CXXNameMangler Mangler(*this, Out); + Mangler.mangleLambdaSig(Lambda); +} + ItaniumMangleContext * ItaniumMangleContext::create(ASTContext &Context, DiagnosticsEngine &Diags) { return new ItaniumMangleContextImpl(Context, Diags); diff --git a/lib/AST/JSONNodeDumper.cpp b/lib/AST/JSONNodeDumper.cpp index 04b933b0fb306..f60d761c996c5 100644 --- a/lib/AST/JSONNodeDumper.cpp +++ b/lib/AST/JSONNodeDumper.cpp @@ -66,6 +66,10 @@ void JSONNodeDumper::Visit(const Stmt *S) { void JSONNodeDumper::Visit(const Type *T) { JOS.attribute("id", createPointerRepresentation(T)); + + if (!T) + return; + JOS.attribute("kind", (llvm::Twine(T->getTypeClassName()) + "Type").str()); JOS.attribute("type", createQualType(QualType(T, 0), /*Desugar*/ false)); attributeOnlyIfTrue("isDependent", T->isDependentType()); @@ -107,9 +111,14 @@ void JSONNodeDumper::Visit(const Decl *D) { if (const auto *ND = dyn_cast(D)) attributeOnlyIfTrue("isHidden", ND->isHidden()); - if (D->getLexicalDeclContext() != D->getDeclContext()) - JOS.attribute("parentDeclContext", - createPointerRepresentation(D->getDeclContext())); + if (D->getLexicalDeclContext() != D->getDeclContext()) { + // Because of multiple inheritance, a DeclContext pointer does not produce + // the same pointer representation as a Decl pointer that references the + // same AST Node. + const auto *ParentDeclContextDecl = dyn_cast(D->getDeclContext()); + JOS.attribute("parentDeclContextId", + createPointerRepresentation(ParentDeclContextDecl)); + } addPreviousDeclaration(D); InnerDeclVisitor::Visit(D); @@ -171,12 +180,30 @@ void JSONNodeDumper::Visit(const GenericSelectionExpr::ConstAssociation &A) { attributeOnlyIfTrue("selected", A.isSelected()); } +void JSONNodeDumper::writeIncludeStack(PresumedLoc Loc, bool JustFirst) { + if (Loc.isInvalid()) + return; + + JOS.attributeBegin("includedFrom"); + JOS.objectBegin(); + + if (!JustFirst) { + // Walk the stack recursively, then print out the presumed location. + writeIncludeStack(SM.getPresumedLoc(Loc.getIncludeLoc())); + } + + JOS.attribute("file", Loc.getFilename()); + JOS.objectEnd(); + JOS.attributeEnd(); +} + void JSONNodeDumper::writeBareSourceLocation(SourceLocation Loc, bool IsSpelling) { PresumedLoc Presumed = SM.getPresumedLoc(Loc); unsigned ActualLine = IsSpelling ? SM.getSpellingLineNumber(Loc) : SM.getExpansionLineNumber(Loc); if (Presumed.isValid()) { + JOS.attribute("offset", SM.getDecomposedLoc(Loc).second); if (LastLocFilename != Presumed.getFilename()) { JOS.attribute("file", Presumed.getFilename()); JOS.attribute("line", ActualLine); @@ -193,6 +220,12 @@ void JSONNodeDumper::writeBareSourceLocation(SourceLocation Loc, LastLocFilename = Presumed.getFilename(); LastLocPresumedLine = PresumedLine; LastLocLine = ActualLine; + + // Orthogonal to the file, line, and column de-duplication is whether the + // given location was a result of an include. If so, print where the + // include location came from. + writeIncludeStack(SM.getPresumedLoc(Presumed.getIncludeLoc()), + /*JustFirst*/ true); } } @@ -238,6 +271,8 @@ llvm::json::Object JSONNodeDumper::createQualType(QualType QT, bool Desugar) { SplitQualType DSQT = QT.getSplitDesugaredType(); if (DSQT != SQT) Ret["desugaredQualType"] = QualType::getAsString(DSQT, PrintPolicy); + if (const auto *TT = QT->getAs()) + Ret["typeAliasDeclId"] = createPointerRepresentation(TT->getDecl()); } return Ret; } @@ -275,7 +310,7 @@ llvm::json::Array JSONNodeDumper::createCastPath(const CastExpr *C) { for (auto I = C->path_begin(), E = C->path_end(); I != E; ++I) { const CXXBaseSpecifier *Base = *I; const auto *RD = - cast(Base->getType()->getAs()->getDecl()); + cast(Base->getType()->castAs()->getDecl()); llvm::json::Object Val{{"name", RD->getName()}}; if (Base->isVirtual()) @@ -839,6 +874,12 @@ void JSONNodeDumper::VisitLinkageSpecDecl(const LinkageSpecDecl *LSD) { switch (LSD->getLanguage()) { case LinkageSpecDecl::lang_c: Lang = "C"; break; case LinkageSpecDecl::lang_cxx: Lang = "C++"; break; + case LinkageSpecDecl::lang_cxx_11: + Lang = "C++11"; + break; + case LinkageSpecDecl::lang_cxx_14: + Lang = "C++14"; + break; } JOS.attribute("language", Lang); attributeOnlyIfTrue("hasBraces", LSD->hasBraces()); diff --git a/lib/AST/Mangle.cpp b/lib/AST/Mangle.cpp index 625282368a4d1..32d466cb57180 100644 --- a/lib/AST/Mangle.cpp +++ b/lib/AST/Mangle.cpp @@ -122,15 +122,21 @@ void MangleContext::mangleName(const NamedDecl *D, raw_ostream &Out) { if (const AsmLabelAttr *ALA = D->getAttr()) { // If we have an asm name, then we use it as the mangling. + // If the label isn't literal, or if this is an alias for an LLVM intrinsic, + // do not add a "\01" prefix. + if (!ALA->getIsLiteralLabel() || ALA->getLabel().startswith("llvm.")) { + Out << ALA->getLabel(); + return; + } + // Adding the prefix can cause problems when one file has a "foo" and // another has a "\01foo". That is known to happen on ELF with the // tricks normally used for producing aliases (PR9177). Fortunately the // llvm mangler on ELF is a nop, so we can just avoid adding the \01 - // marker. We also avoid adding the marker if this is an alias for an - // LLVM intrinsic. + // marker. char GlobalPrefix = getASTContext().getTargetInfo().getDataLayout().getGlobalPrefix(); - if (GlobalPrefix && !ALA->getLabel().startswith("llvm.")) + if (GlobalPrefix) Out << '\01'; // LLVM IR Marker for __asm("foo") Out << ALA->getLabel(); @@ -380,7 +386,7 @@ public: auto hasDefaultCXXMethodCC = [](ASTContext &C, const CXXMethodDecl *MD) { auto DefaultCC = C.getDefaultCallingConvention(/*IsVariadic=*/false, /*IsCXXMethod=*/true); - auto CC = MD->getType()->getAs()->getCallConv(); + auto CC = MD->getType()->castAs()->getCallConv(); return CC == DefaultCC; }; @@ -470,7 +476,7 @@ private: }; ASTNameGenerator::ASTNameGenerator(ASTContext &Ctx) - : Impl(llvm::make_unique(Ctx)) {} + : Impl(std::make_unique(Ctx)) {} ASTNameGenerator::~ASTNameGenerator() {} diff --git a/lib/AST/MicrosoftCXXABI.cpp b/lib/AST/MicrosoftCXXABI.cpp index 4dc4156df9ca4..074abba3d458b 100644 --- a/lib/AST/MicrosoftCXXABI.cpp +++ b/lib/AST/MicrosoftCXXABI.cpp @@ -82,7 +82,7 @@ public: if (!isVariadic && Context.getTargetInfo().getTriple().getArch() == llvm::Triple::x86) return CC_X86ThisCall; - return CC_C; + return Context.getTargetInfo().getDefaultCallingConv(); } bool isNearlyEmpty(const CXXRecordDecl *RD) const override { @@ -132,7 +132,7 @@ public: std::unique_ptr createMangleNumberingContext() const override { - return llvm::make_unique(); + return std::make_unique(); } }; } diff --git a/lib/AST/MicrosoftMangle.cpp b/lib/AST/MicrosoftMangle.cpp index 5e9358e24fc9d..f871a1b999006 100644 --- a/lib/AST/MicrosoftMangle.cpp +++ b/lib/AST/MicrosoftMangle.cpp @@ -27,11 +27,11 @@ #include "clang/Basic/DiagnosticOptions.h" #include "clang/Basic/TargetInfo.h" #include "llvm/ADT/StringExtras.h" -#include "llvm/Support/JamCRC.h" -#include "llvm/Support/xxhash.h" +#include "llvm/Support/CRC.h" #include "llvm/Support/MD5.h" #include "llvm/Support/MathExtras.h" #include "llvm/Support/StringSaver.h" +#include "llvm/Support/xxhash.h" using namespace clang; @@ -364,7 +364,7 @@ private: #define TYPE(CLASS, PARENT) void mangleType(const CLASS##Type *T, \ Qualifiers Quals, \ SourceRange Range); -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" #undef ABSTRACT_TYPE #undef NON_CANONICAL_TYPE #undef TYPE @@ -615,6 +615,8 @@ void MicrosoftCXXNameMangler::mangleMemberDataPointer(const CXXRecordDecl *RD, case MSInheritanceAttr::Keyword_multiple_inheritance: Code = '0'; break; case MSInheritanceAttr::Keyword_virtual_inheritance: Code = 'F'; break; case MSInheritanceAttr::Keyword_unspecified_inheritance: Code = 'G'; break; + case MSInheritanceAttr::SpellingNotCalculated: + llvm_unreachable("not reachable"); } Out << '$' << Code; @@ -646,6 +648,8 @@ MicrosoftCXXNameMangler::mangleMemberFunctionPointer(const CXXRecordDecl *RD, case MSInheritanceAttr::Keyword_multiple_inheritance: Code = 'H'; break; case MSInheritanceAttr::Keyword_virtual_inheritance: Code = 'I'; break; case MSInheritanceAttr::Keyword_unspecified_inheritance: Code = 'J'; break; + case MSInheritanceAttr::SpellingNotCalculated: + llvm_unreachable("not reachable"); } // If non-virtual, mangle the name. If virtual, mangle as a virtual memptr @@ -842,7 +846,7 @@ void MicrosoftCXXNameMangler::mangleUnqualifiedName(const NamedDecl *ND, TemplateArgStringStorage.save(TemplateMangling.str()); } } else { - Out << Found->second; // Outputs a StringRef. + Out << Found->second << '@'; // Outputs a StringRef. } } else { Out << Found->second; // Outputs a back reference (an int). @@ -868,16 +872,11 @@ void MicrosoftCXXNameMangler::mangleUnqualifiedName(const NamedDecl *ND, } if (const DecompositionDecl *DD = dyn_cast(ND)) { - // FIXME: Invented mangling for decomposition declarations: - // [X,Y,Z] - // where X,Y,Z are the names of the bindings. - llvm::SmallString<128> Name("["); - for (auto *BD : DD->bindings()) { - if (Name.size() > 1) - Name += ','; - Name += BD->getDeclName().getAsIdentifierInfo()->getName(); - } - Name += ']'; + // Decomposition declarations are considered anonymous, and get + // numbered with a $S prefix. + llvm::SmallString<64> Name("$S"); + // Get a unique id for the anonymous struct. + Name += llvm::utostr(Context.getAnonymousStructId(DD) + 1); mangleSourceName(Name); break; } @@ -1942,7 +1941,7 @@ void MicrosoftCXXNameMangler::mangleType(QualType T, SourceRange Range, case Type::CLASS: \ mangleType(cast(ty), Quals, Range); \ break; -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" #undef ABSTRACT_TYPE #undef NON_CANONICAL_TYPE #undef TYPE @@ -2109,6 +2108,9 @@ void MicrosoftCXXNameMangler::mangleType(const BuiltinType *T, Qualifiers, mangleArtificialTagType(TTK_Struct, "_Half", {"__clang"}); break; +#define SVE_TYPE(Name, Id, SingletonId) \ + case BuiltinType::Id: +#include "clang/Basic/AArch64SVEACLETypes.def" case BuiltinType::ShortAccum: case BuiltinType::Accum: case BuiltinType::LongAccum: diff --git a/lib/AST/NSAPI.cpp b/lib/AST/NSAPI.cpp index 5104dc59d621e..ae6ff04f5126d 100644 --- a/lib/AST/NSAPI.cpp +++ b/lib/AST/NSAPI.cpp @@ -75,17 +75,6 @@ Selector NSAPI::getNSStringSelector(NSStringMethodKind MK) const { return NSStringSelectors[MK]; } -Optional -NSAPI::getNSStringMethodKind(Selector Sel) const { - for (unsigned i = 0; i != NumNSStringMethods; ++i) { - NSStringMethodKind MK = NSStringMethodKind(i); - if (Sel == getNSStringSelector(MK)) - return MK; - } - - return None; -} - Selector NSAPI::getNSArraySelector(NSArrayMethodKind MK) const { if (NSArraySelectors[MK].isNull()) { Selector Sel; @@ -482,6 +471,9 @@ NSAPI::getNSNumberFactoryMethodKind(QualType T) const { case BuiltinType::OCLClkEvent: case BuiltinType::OCLQueue: case BuiltinType::OCLReserveID: +#define SVE_TYPE(Name, Id, SingletonId) \ + case BuiltinType::Id: +#include "clang/Basic/AArch64SVEACLETypes.def" case BuiltinType::BoundMember: case BuiltinType::Dependent: case BuiltinType::Overload: diff --git a/lib/AST/OpenMPClause.cpp b/lib/AST/OpenMPClause.cpp index 9d8a7ebc3023e..fe1334469d486 100644 --- a/lib/AST/OpenMPClause.cpp +++ b/lib/AST/OpenMPClause.cpp @@ -43,6 +43,8 @@ OMPClause::child_range OMPClause::used_children() { #include "clang/Basic/OpenMPKinds.def" case OMPC_threadprivate: case OMPC_uniform: + case OMPC_device_type: + case OMPC_match: case OMPC_unknown: break; } @@ -82,9 +84,16 @@ const OMPClauseWithPreInit *OMPClauseWithPreInit::get(const OMPClause *C) { return static_cast(C); case OMPC_device: return static_cast(C); + case OMPC_grainsize: + return static_cast(C); + case OMPC_num_tasks: + return static_cast(C); + case OMPC_final: + return static_cast(C); + case OMPC_priority: + return static_cast(C); case OMPC_default: case OMPC_proc_bind: - case OMPC_final: case OMPC_safelen: case OMPC_simdlen: case OMPC_allocator: @@ -110,10 +119,7 @@ const OMPClauseWithPreInit *OMPClauseWithPreInit::get(const OMPClause *C) { case OMPC_threads: case OMPC_simd: case OMPC_map: - case OMPC_priority: - case OMPC_grainsize: case OMPC_nogroup: - case OMPC_num_tasks: case OMPC_hint: case OMPC_defaultmap: case OMPC_unknown: @@ -127,6 +133,8 @@ const OMPClauseWithPreInit *OMPClauseWithPreInit::get(const OMPClause *C) { case OMPC_reverse_offload: case OMPC_dynamic_allocators: case OMPC_atomic_default_mem_order: + case OMPC_device_type: + case OMPC_match: break; } @@ -203,6 +211,8 @@ const OMPClauseWithPostUpdate *OMPClauseWithPostUpdate::get(const OMPClause *C) case OMPC_reverse_offload: case OMPC_dynamic_allocators: case OMPC_atomic_default_mem_order: + case OMPC_device_type: + case OMPC_match: break; } @@ -228,6 +238,30 @@ OMPClause::child_range OMPIfClause::used_children() { return child_range(&Condition, &Condition + 1); } +OMPClause::child_range OMPGrainsizeClause::used_children() { + if (Stmt **C = getAddrOfExprAsWritten(getPreInitStmt())) + return child_range(C, C + 1); + return child_range(&Grainsize, &Grainsize + 1); +} + +OMPClause::child_range OMPNumTasksClause::used_children() { + if (Stmt **C = getAddrOfExprAsWritten(getPreInitStmt())) + return child_range(C, C + 1); + return child_range(&NumTasks, &NumTasks + 1); +} + +OMPClause::child_range OMPFinalClause::used_children() { + if (Stmt **C = getAddrOfExprAsWritten(getPreInitStmt())) + return child_range(C, C + 1); + return child_range(&Condition, &Condition + 1); +} + +OMPClause::child_range OMPPriorityClause::used_children() { + if (Stmt **C = getAddrOfExprAsWritten(getPreInitStmt())) + return child_range(C, C + 1); + return child_range(&Priority, &Priority + 1); +} + OMPOrderedClause *OMPOrderedClause::Create(const ASTContext &C, Expr *Num, unsigned NumLoops, SourceLocation StartLoc, @@ -429,15 +463,23 @@ void OMPLinearClause::setFinals(ArrayRef FL) { std::copy(FL.begin(), FL.end(), getUpdates().end()); } +void OMPLinearClause::setUsedExprs(ArrayRef UE) { + assert( + UE.size() == varlist_size() + 1 && + "Number of used expressions is not the same as the preallocated buffer"); + std::copy(UE.begin(), UE.end(), getFinals().end() + 2); +} + OMPLinearClause *OMPLinearClause::Create( const ASTContext &C, SourceLocation StartLoc, SourceLocation LParenLoc, OpenMPLinearClauseKind Modifier, SourceLocation ModifierLoc, SourceLocation ColonLoc, SourceLocation EndLoc, ArrayRef VL, ArrayRef PL, ArrayRef IL, Expr *Step, Expr *CalcStep, Stmt *PreInit, Expr *PostUpdate) { - // Allocate space for 4 lists (Vars, Inits, Updates, Finals) and 2 expressions - // (Step and CalcStep). - void *Mem = C.Allocate(totalSizeToAlloc(5 * VL.size() + 2)); + // Allocate space for 5 lists (Vars, Inits, Updates, Finals), 2 expressions + // (Step and CalcStep), list of used expression + step. + void *Mem = + C.Allocate(totalSizeToAlloc(5 * VL.size() + 2 + VL.size() + 1)); OMPLinearClause *Clause = new (Mem) OMPLinearClause( StartLoc, LParenLoc, Modifier, ModifierLoc, ColonLoc, EndLoc, VL.size()); Clause->setVarRefs(VL); @@ -449,6 +491,8 @@ OMPLinearClause *OMPLinearClause::Create( nullptr); std::fill(Clause->getUpdates().end(), Clause->getUpdates().end() + VL.size(), nullptr); + std::fill(Clause->getUsedExprs().begin(), Clause->getUsedExprs().end(), + nullptr); Clause->setStep(Step); Clause->setCalcStep(CalcStep); Clause->setPreInitStmt(PreInit); @@ -458,12 +502,19 @@ OMPLinearClause *OMPLinearClause::Create( OMPLinearClause *OMPLinearClause::CreateEmpty(const ASTContext &C, unsigned NumVars) { - // Allocate space for 4 lists (Vars, Inits, Updates, Finals) and 2 expressions - // (Step and CalcStep). - void *Mem = C.Allocate(totalSizeToAlloc(5 * NumVars + 2)); + // Allocate space for 5 lists (Vars, Inits, Updates, Finals), 2 expressions + // (Step and CalcStep), list of used expression + step. + void *Mem = C.Allocate(totalSizeToAlloc(5 * NumVars + 2 + NumVars +1)); return new (Mem) OMPLinearClause(NumVars); } +OMPClause::child_range OMPLinearClause::used_children() { + // Range includes only non-nullptr elements. + return child_range( + reinterpret_cast(getUsedExprs().begin()), + reinterpret_cast(llvm::find(getUsedExprs(), nullptr))); +} + OMPAlignedClause * OMPAlignedClause::Create(const ASTContext &C, SourceLocation StartLoc, SourceLocation LParenLoc, SourceLocation ColonLoc, diff --git a/lib/AST/PrintfFormatString.cpp b/lib/AST/PrintfFormatString.cpp index a1207aae5aa25..bae60d4644078 100644 --- a/lib/AST/PrintfFormatString.cpp +++ b/lib/AST/PrintfFormatString.cpp @@ -463,6 +463,23 @@ bool clang::analyze_format_string::ParseFormatStringHasSArg(const char *I, return false; } +bool clang::analyze_format_string::parseFormatStringHasFormattingSpecifiers( + const char *Begin, const char *End, const LangOptions &LO, + const TargetInfo &Target) { + unsigned ArgIndex = 0; + // Keep looking for a formatting specifier until we have exhausted the string. + FormatStringHandler H; + while (Begin != End) { + const PrintfSpecifierResult &FSR = + ParsePrintfSpecifier(H, Begin, End, ArgIndex, LO, Target, false, false); + if (FSR.shouldStop()) + break; + if (FSR.hasValue()) + return true; + } + return false; +} + //===----------------------------------------------------------------------===// // Methods on PrintfSpecifier. //===----------------------------------------------------------------------===// @@ -769,6 +786,9 @@ bool PrintfSpecifier::fixType(QualType QT, const LangOptions &LangOpt, #define EXT_OPAQUE_TYPE(ExtType, Id, Ext) \ case BuiltinType::Id: #include "clang/Basic/OpenCLExtensionTypes.def" +#define SVE_TYPE(Name, Id, SingletonId) \ + case BuiltinType::Id: +#include "clang/Basic/AArch64SVEACLETypes.def" #define SIGNED_TYPE(Id, SingletonId) #define UNSIGNED_TYPE(Id, SingletonId) #define FLOATING_TYPE(Id, SingletonId) diff --git a/lib/AST/RawCommentList.cpp b/lib/AST/RawCommentList.cpp index df53b7fa10047..83e8a0b942a40 100644 --- a/lib/AST/RawCommentList.cpp +++ b/lib/AST/RawCommentList.cpp @@ -275,27 +275,25 @@ void RawCommentList::addComment(const RawComment &RC, if (RC.isInvalid()) return; - // Check if the comments are not in source order. - while (!Comments.empty() && - !SourceMgr.isBeforeInTranslationUnit(Comments.back()->getBeginLoc(), - RC.getBeginLoc())) { - // If they are, just pop a few last comments that don't fit. - // This happens if an \#include directive contains comments. - Comments.pop_back(); - } - // Ordinary comments are not interesting for us. if (RC.isOrdinary() && !CommentOpts.ParseAllComments) return; + std::pair Loc = + SourceMgr.getDecomposedLoc(RC.getBeginLoc()); + + const FileID CommentFile = Loc.first; + const unsigned CommentOffset = Loc.second; + // If this is the first Doxygen comment, save it (because there isn't // anything to merge it with). - if (Comments.empty()) { - Comments.push_back(new (Allocator) RawComment(RC)); + if (OrderedComments[CommentFile].empty()) { + OrderedComments[CommentFile][CommentOffset] = + new (Allocator) RawComment(RC); return; } - const RawComment &C1 = *Comments.back(); + const RawComment &C1 = *OrderedComments[CommentFile].rbegin()->second; const RawComment &C2 = RC; // Merge comments only if there is only whitespace between them. @@ -318,21 +316,43 @@ void RawCommentList::addComment(const RawComment &RC, onlyWhitespaceBetween(SourceMgr, C1.getEndLoc(), C2.getBeginLoc(), /*MaxNewlinesAllowed=*/1)) { SourceRange MergedRange(C1.getBeginLoc(), C2.getEndLoc()); - *Comments.back() = RawComment(SourceMgr, MergedRange, CommentOpts, true); + *OrderedComments[CommentFile].rbegin()->second = + RawComment(SourceMgr, MergedRange, CommentOpts, true); } else { - Comments.push_back(new (Allocator) RawComment(RC)); + OrderedComments[CommentFile][CommentOffset] = + new (Allocator) RawComment(RC); } } -void RawCommentList::addDeserializedComments(ArrayRef DeserializedComments) { - std::vector MergedComments; - MergedComments.reserve(Comments.size() + DeserializedComments.size()); +const std::map * +RawCommentList::getCommentsInFile(FileID File) const { + auto CommentsInFile = OrderedComments.find(File); + if (CommentsInFile == OrderedComments.end()) + return nullptr; + + return &CommentsInFile->second; +} + +bool RawCommentList::empty() const { return OrderedComments.empty(); } + +unsigned RawCommentList::getCommentBeginLine(RawComment *C, FileID File, + unsigned Offset) const { + auto Cached = CommentBeginLine.find(C); + if (Cached != CommentBeginLine.end()) + return Cached->second; + const unsigned Line = SourceMgr.getLineNumber(File, Offset); + CommentBeginLine[C] = Line; + return Line; +} - std::merge(Comments.begin(), Comments.end(), - DeserializedComments.begin(), DeserializedComments.end(), - std::back_inserter(MergedComments), - BeforeThanCompare(SourceMgr)); - std::swap(Comments, MergedComments); +unsigned RawCommentList::getCommentEndOffset(RawComment *C) const { + auto Cached = CommentEndOffset.find(C); + if (Cached != CommentEndOffset.end()) + return Cached->second; + const unsigned Offset = + SourceMgr.getDecomposedLoc(C->getSourceRange().getEnd()).second; + CommentEndOffset[C] = Offset; + return Offset; } std::string RawComment::getFormattedText(const SourceManager &SourceMgr, diff --git a/lib/AST/Stmt.cpp b/lib/AST/Stmt.cpp index 0a4d403106bd4..80a1451ac789b 100644 --- a/lib/AST/Stmt.cpp +++ b/lib/AST/Stmt.cpp @@ -41,6 +41,7 @@ #include #include #include +#include using namespace clang; @@ -83,6 +84,16 @@ const char *Stmt::getStmtClassName() const { #CLASS " should not be polymorphic!"); #include "clang/AST/StmtNodes.inc" +// Check that no statement / expression class has a non-trival destructor. +// Statements and expressions are allocated with the BumpPtrAllocator from +// ASTContext and therefore their destructor is not executed. +#define STMT(CLASS, PARENT) \ + static_assert(std::is_trivially_destructible::value, \ + #CLASS " should be trivially destructible!"); +// FIXME: InitListExpr is not trivially destructible due to its ASTVector. +#define INITLISTEXPR(CLASS, PARENT) +#include "clang/AST/StmtNodes.inc" + void Stmt::PrintStats() { // Ensure the table is primed. getStmtInfoTableEntry(Stmt::NullStmtClass); diff --git a/lib/AST/StmtOpenMP.cpp b/lib/AST/StmtOpenMP.cpp index 4e829897cebe6..da1364ebffc4c 100644 --- a/lib/AST/StmtOpenMP.cpp +++ b/lib/AST/StmtOpenMP.cpp @@ -72,6 +72,25 @@ void OMPLoopDirective::setFinals(ArrayRef A) { std::copy(A.begin(), A.end(), getFinals().begin()); } +void OMPLoopDirective::setDependentCounters(ArrayRef A) { + assert( + A.size() == getCollapsedNumber() && + "Number of dependent counters is not the same as the collapsed number"); + llvm::copy(A, getDependentCounters().begin()); +} + +void OMPLoopDirective::setDependentInits(ArrayRef A) { + assert(A.size() == getCollapsedNumber() && + "Number of dependent inits is not the same as the collapsed number"); + llvm::copy(A, getDependentInits().begin()); +} + +void OMPLoopDirective::setFinalsConditions(ArrayRef A) { + assert(A.size() == getCollapsedNumber() && + "Number of finals conditions is not the same as the collapsed number"); + llvm::copy(A, getFinalsConditions().begin()); +} + OMPParallelDirective *OMPParallelDirective::Create( const ASTContext &C, SourceLocation StartLoc, SourceLocation EndLoc, ArrayRef Clauses, Stmt *AssociatedStmt, bool HasCancel) { @@ -122,6 +141,9 @@ OMPSimdDirective::Create(const ASTContext &C, SourceLocation StartLoc, Dir->setInits(Exprs.Inits); Dir->setUpdates(Exprs.Updates); Dir->setFinals(Exprs.Finals); + Dir->setDependentCounters(Exprs.DependentCounters); + Dir->setDependentInits(Exprs.DependentInits); + Dir->setFinalsConditions(Exprs.FinalsConditions); Dir->setPreInits(Exprs.PreInits); return Dir; } @@ -170,6 +192,9 @@ OMPForDirective::Create(const ASTContext &C, SourceLocation StartLoc, Dir->setInits(Exprs.Inits); Dir->setUpdates(Exprs.Updates); Dir->setFinals(Exprs.Finals); + Dir->setDependentCounters(Exprs.DependentCounters); + Dir->setDependentInits(Exprs.DependentInits); + Dir->setFinalsConditions(Exprs.FinalsConditions); Dir->setPreInits(Exprs.PreInits); Dir->setHasCancel(HasCancel); return Dir; @@ -220,6 +245,9 @@ OMPForSimdDirective::Create(const ASTContext &C, SourceLocation StartLoc, Dir->setInits(Exprs.Inits); Dir->setUpdates(Exprs.Updates); Dir->setFinals(Exprs.Finals); + Dir->setDependentCounters(Exprs.DependentCounters); + Dir->setDependentInits(Exprs.DependentInits); + Dir->setFinalsConditions(Exprs.FinalsConditions); Dir->setPreInits(Exprs.PreInits); return Dir; } @@ -383,6 +411,9 @@ OMPParallelForDirective *OMPParallelForDirective::Create( Dir->setInits(Exprs.Inits); Dir->setUpdates(Exprs.Updates); Dir->setFinals(Exprs.Finals); + Dir->setDependentCounters(Exprs.DependentCounters); + Dir->setDependentInits(Exprs.DependentInits); + Dir->setFinalsConditions(Exprs.FinalsConditions); Dir->setPreInits(Exprs.PreInits); Dir->setHasCancel(HasCancel); return Dir; @@ -432,6 +463,9 @@ OMPParallelForSimdDirective *OMPParallelForSimdDirective::Create( Dir->setInits(Exprs.Inits); Dir->setUpdates(Exprs.Updates); Dir->setFinals(Exprs.Finals); + Dir->setDependentCounters(Exprs.DependentCounters); + Dir->setDependentInits(Exprs.DependentInits); + Dir->setFinalsConditions(Exprs.FinalsConditions); Dir->setPreInits(Exprs.PreInits); return Dir; } @@ -772,6 +806,9 @@ OMPTargetParallelForDirective *OMPTargetParallelForDirective::Create( Dir->setInits(Exprs.Inits); Dir->setUpdates(Exprs.Updates); Dir->setFinals(Exprs.Finals); + Dir->setDependentCounters(Exprs.DependentCounters); + Dir->setDependentInits(Exprs.DependentInits); + Dir->setFinalsConditions(Exprs.FinalsConditions); Dir->setPreInits(Exprs.PreInits); Dir->setHasCancel(HasCancel); return Dir; @@ -914,6 +951,9 @@ OMPTaskLoopDirective *OMPTaskLoopDirective::Create( Dir->setInits(Exprs.Inits); Dir->setUpdates(Exprs.Updates); Dir->setFinals(Exprs.Finals); + Dir->setDependentCounters(Exprs.DependentCounters); + Dir->setDependentInits(Exprs.DependentInits); + Dir->setFinalsConditions(Exprs.FinalsConditions); Dir->setPreInits(Exprs.PreInits); return Dir; } @@ -963,6 +1003,9 @@ OMPTaskLoopSimdDirective *OMPTaskLoopSimdDirective::Create( Dir->setInits(Exprs.Inits); Dir->setUpdates(Exprs.Updates); Dir->setFinals(Exprs.Finals); + Dir->setDependentCounters(Exprs.DependentCounters); + Dir->setDependentInits(Exprs.DependentInits); + Dir->setFinalsConditions(Exprs.FinalsConditions); Dir->setPreInits(Exprs.PreInits); return Dir; } @@ -978,6 +1021,167 @@ OMPTaskLoopSimdDirective::CreateEmpty(const ASTContext &C, unsigned NumClauses, return new (Mem) OMPTaskLoopSimdDirective(CollapsedNum, NumClauses); } +OMPMasterTaskLoopDirective *OMPMasterTaskLoopDirective::Create( + const ASTContext &C, SourceLocation StartLoc, SourceLocation EndLoc, + unsigned CollapsedNum, ArrayRef Clauses, Stmt *AssociatedStmt, + const HelperExprs &Exprs) { + unsigned Size = + llvm::alignTo(sizeof(OMPMasterTaskLoopDirective), alignof(OMPClause *)); + void *Mem = C.Allocate( + Size + sizeof(OMPClause *) * Clauses.size() + + sizeof(Stmt *) * numLoopChildren(CollapsedNum, OMPD_master_taskloop)); + OMPMasterTaskLoopDirective *Dir = new (Mem) OMPMasterTaskLoopDirective( + StartLoc, EndLoc, CollapsedNum, Clauses.size()); + Dir->setClauses(Clauses); + Dir->setAssociatedStmt(AssociatedStmt); + Dir->setIterationVariable(Exprs.IterationVarRef); + Dir->setLastIteration(Exprs.LastIteration); + Dir->setCalcLastIteration(Exprs.CalcLastIteration); + Dir->setPreCond(Exprs.PreCond); + Dir->setCond(Exprs.Cond); + Dir->setInit(Exprs.Init); + Dir->setInc(Exprs.Inc); + Dir->setIsLastIterVariable(Exprs.IL); + Dir->setLowerBoundVariable(Exprs.LB); + Dir->setUpperBoundVariable(Exprs.UB); + Dir->setStrideVariable(Exprs.ST); + Dir->setEnsureUpperBound(Exprs.EUB); + Dir->setNextLowerBound(Exprs.NLB); + Dir->setNextUpperBound(Exprs.NUB); + Dir->setNumIterations(Exprs.NumIterations); + Dir->setCounters(Exprs.Counters); + Dir->setPrivateCounters(Exprs.PrivateCounters); + Dir->setInits(Exprs.Inits); + Dir->setUpdates(Exprs.Updates); + Dir->setFinals(Exprs.Finals); + Dir->setDependentCounters(Exprs.DependentCounters); + Dir->setDependentInits(Exprs.DependentInits); + Dir->setFinalsConditions(Exprs.FinalsConditions); + Dir->setPreInits(Exprs.PreInits); + return Dir; +} + +OMPMasterTaskLoopDirective * +OMPMasterTaskLoopDirective::CreateEmpty(const ASTContext &C, + unsigned NumClauses, + unsigned CollapsedNum, EmptyShell) { + unsigned Size = + llvm::alignTo(sizeof(OMPMasterTaskLoopDirective), alignof(OMPClause *)); + void *Mem = C.Allocate( + Size + sizeof(OMPClause *) * NumClauses + + sizeof(Stmt *) * numLoopChildren(CollapsedNum, OMPD_master_taskloop)); + return new (Mem) OMPMasterTaskLoopDirective(CollapsedNum, NumClauses); +} + +OMPMasterTaskLoopSimdDirective *OMPMasterTaskLoopSimdDirective::Create( + const ASTContext &C, SourceLocation StartLoc, SourceLocation EndLoc, + unsigned CollapsedNum, ArrayRef Clauses, Stmt *AssociatedStmt, + const HelperExprs &Exprs) { + unsigned Size = llvm::alignTo(sizeof(OMPMasterTaskLoopSimdDirective), + alignof(OMPClause *)); + void *Mem = + C.Allocate(Size + sizeof(OMPClause *) * Clauses.size() + + sizeof(Stmt *) * + numLoopChildren(CollapsedNum, OMPD_master_taskloop_simd)); + auto *Dir = new (Mem) OMPMasterTaskLoopSimdDirective( + StartLoc, EndLoc, CollapsedNum, Clauses.size()); + Dir->setClauses(Clauses); + Dir->setAssociatedStmt(AssociatedStmt); + Dir->setIterationVariable(Exprs.IterationVarRef); + Dir->setLastIteration(Exprs.LastIteration); + Dir->setCalcLastIteration(Exprs.CalcLastIteration); + Dir->setPreCond(Exprs.PreCond); + Dir->setCond(Exprs.Cond); + Dir->setInit(Exprs.Init); + Dir->setInc(Exprs.Inc); + Dir->setIsLastIterVariable(Exprs.IL); + Dir->setLowerBoundVariable(Exprs.LB); + Dir->setUpperBoundVariable(Exprs.UB); + Dir->setStrideVariable(Exprs.ST); + Dir->setEnsureUpperBound(Exprs.EUB); + Dir->setNextLowerBound(Exprs.NLB); + Dir->setNextUpperBound(Exprs.NUB); + Dir->setNumIterations(Exprs.NumIterations); + Dir->setCounters(Exprs.Counters); + Dir->setPrivateCounters(Exprs.PrivateCounters); + Dir->setInits(Exprs.Inits); + Dir->setUpdates(Exprs.Updates); + Dir->setFinals(Exprs.Finals); + Dir->setDependentCounters(Exprs.DependentCounters); + Dir->setDependentInits(Exprs.DependentInits); + Dir->setFinalsConditions(Exprs.FinalsConditions); + Dir->setPreInits(Exprs.PreInits); + return Dir; +} + +OMPMasterTaskLoopSimdDirective * +OMPMasterTaskLoopSimdDirective::CreateEmpty(const ASTContext &C, + unsigned NumClauses, + unsigned CollapsedNum, EmptyShell) { + unsigned Size = llvm::alignTo(sizeof(OMPMasterTaskLoopSimdDirective), + alignof(OMPClause *)); + void *Mem = + C.Allocate(Size + sizeof(OMPClause *) * NumClauses + + sizeof(Stmt *) * + numLoopChildren(CollapsedNum, OMPD_master_taskloop_simd)); + return new (Mem) OMPMasterTaskLoopSimdDirective(CollapsedNum, NumClauses); +} + +OMPParallelMasterTaskLoopDirective *OMPParallelMasterTaskLoopDirective::Create( + const ASTContext &C, SourceLocation StartLoc, SourceLocation EndLoc, + unsigned CollapsedNum, ArrayRef Clauses, Stmt *AssociatedStmt, + const HelperExprs &Exprs) { + unsigned Size = llvm::alignTo(sizeof(OMPParallelMasterTaskLoopDirective), + alignof(OMPClause *)); + void *Mem = C.Allocate( + Size + sizeof(OMPClause *) * Clauses.size() + + sizeof(Stmt *) * + numLoopChildren(CollapsedNum, OMPD_parallel_master_taskloop)); + auto *Dir = new (Mem) OMPParallelMasterTaskLoopDirective( + StartLoc, EndLoc, CollapsedNum, Clauses.size()); + Dir->setClauses(Clauses); + Dir->setAssociatedStmt(AssociatedStmt); + Dir->setIterationVariable(Exprs.IterationVarRef); + Dir->setLastIteration(Exprs.LastIteration); + Dir->setCalcLastIteration(Exprs.CalcLastIteration); + Dir->setPreCond(Exprs.PreCond); + Dir->setCond(Exprs.Cond); + Dir->setInit(Exprs.Init); + Dir->setInc(Exprs.Inc); + Dir->setIsLastIterVariable(Exprs.IL); + Dir->setLowerBoundVariable(Exprs.LB); + Dir->setUpperBoundVariable(Exprs.UB); + Dir->setStrideVariable(Exprs.ST); + Dir->setEnsureUpperBound(Exprs.EUB); + Dir->setNextLowerBound(Exprs.NLB); + Dir->setNextUpperBound(Exprs.NUB); + Dir->setNumIterations(Exprs.NumIterations); + Dir->setCounters(Exprs.Counters); + Dir->setPrivateCounters(Exprs.PrivateCounters); + Dir->setInits(Exprs.Inits); + Dir->setUpdates(Exprs.Updates); + Dir->setFinals(Exprs.Finals); + Dir->setDependentCounters(Exprs.DependentCounters); + Dir->setDependentInits(Exprs.DependentInits); + Dir->setFinalsConditions(Exprs.FinalsConditions); + Dir->setPreInits(Exprs.PreInits); + return Dir; +} + +OMPParallelMasterTaskLoopDirective * +OMPParallelMasterTaskLoopDirective::CreateEmpty(const ASTContext &C, + unsigned NumClauses, + unsigned CollapsedNum, + EmptyShell) { + unsigned Size = llvm::alignTo(sizeof(OMPParallelMasterTaskLoopDirective), + alignof(OMPClause *)); + void *Mem = C.Allocate( + Size + sizeof(OMPClause *) * NumClauses + + sizeof(Stmt *) * + numLoopChildren(CollapsedNum, OMPD_parallel_master_taskloop)); + return new (Mem) OMPParallelMasterTaskLoopDirective(CollapsedNum, NumClauses); +} + OMPDistributeDirective *OMPDistributeDirective::Create( const ASTContext &C, SourceLocation StartLoc, SourceLocation EndLoc, unsigned CollapsedNum, ArrayRef Clauses, Stmt *AssociatedStmt, @@ -1011,6 +1215,9 @@ OMPDistributeDirective *OMPDistributeDirective::Create( Dir->setInits(Exprs.Inits); Dir->setUpdates(Exprs.Updates); Dir->setFinals(Exprs.Finals); + Dir->setDependentCounters(Exprs.DependentCounters); + Dir->setDependentInits(Exprs.DependentInits); + Dir->setFinalsConditions(Exprs.FinalsConditions); Dir->setPreInits(Exprs.PreInits); return Dir; } @@ -1089,6 +1296,9 @@ OMPDistributeParallelForDirective *OMPDistributeParallelForDirective::Create( Dir->setInits(Exprs.Inits); Dir->setUpdates(Exprs.Updates); Dir->setFinals(Exprs.Finals); + Dir->setDependentCounters(Exprs.DependentCounters); + Dir->setDependentInits(Exprs.DependentInits); + Dir->setFinalsConditions(Exprs.FinalsConditions); Dir->setPreInits(Exprs.PreInits); Dir->setCombinedLowerBoundVariable(Exprs.DistCombinedFields.LB); Dir->setCombinedUpperBoundVariable(Exprs.DistCombinedFields.UB); @@ -1157,6 +1367,9 @@ OMPDistributeParallelForSimdDirective::Create( Dir->setInits(Exprs.Inits); Dir->setUpdates(Exprs.Updates); Dir->setFinals(Exprs.Finals); + Dir->setDependentCounters(Exprs.DependentCounters); + Dir->setDependentInits(Exprs.DependentInits); + Dir->setFinalsConditions(Exprs.FinalsConditions); Dir->setPreInits(Exprs.PreInits); Dir->setCombinedLowerBoundVariable(Exprs.DistCombinedFields.LB); Dir->setCombinedUpperBoundVariable(Exprs.DistCombinedFields.UB); @@ -1219,6 +1432,9 @@ OMPDistributeSimdDirective *OMPDistributeSimdDirective::Create( Dir->setInits(Exprs.Inits); Dir->setUpdates(Exprs.Updates); Dir->setFinals(Exprs.Finals); + Dir->setDependentCounters(Exprs.DependentCounters); + Dir->setDependentInits(Exprs.DependentInits); + Dir->setFinalsConditions(Exprs.FinalsConditions); Dir->setPreInits(Exprs.PreInits); return Dir; } @@ -1271,6 +1487,9 @@ OMPTargetParallelForSimdDirective *OMPTargetParallelForSimdDirective::Create( Dir->setInits(Exprs.Inits); Dir->setUpdates(Exprs.Updates); Dir->setFinals(Exprs.Finals); + Dir->setDependentCounters(Exprs.DependentCounters); + Dir->setDependentInits(Exprs.DependentInits); + Dir->setFinalsConditions(Exprs.FinalsConditions); Dir->setPreInits(Exprs.PreInits); return Dir; } @@ -1315,6 +1534,9 @@ OMPTargetSimdDirective::Create(const ASTContext &C, SourceLocation StartLoc, Dir->setInits(Exprs.Inits); Dir->setUpdates(Exprs.Updates); Dir->setFinals(Exprs.Finals); + Dir->setDependentCounters(Exprs.DependentCounters); + Dir->setDependentInits(Exprs.DependentInits); + Dir->setFinalsConditions(Exprs.FinalsConditions); Dir->setPreInits(Exprs.PreInits); return Dir; } @@ -1363,6 +1585,9 @@ OMPTeamsDistributeDirective *OMPTeamsDistributeDirective::Create( Dir->setInits(Exprs.Inits); Dir->setUpdates(Exprs.Updates); Dir->setFinals(Exprs.Finals); + Dir->setDependentCounters(Exprs.DependentCounters); + Dir->setDependentInits(Exprs.DependentInits); + Dir->setFinalsConditions(Exprs.FinalsConditions); Dir->setPreInits(Exprs.PreInits); return Dir; } @@ -1414,6 +1639,9 @@ OMPTeamsDistributeSimdDirective *OMPTeamsDistributeSimdDirective::Create( Dir->setInits(Exprs.Inits); Dir->setUpdates(Exprs.Updates); Dir->setFinals(Exprs.Finals); + Dir->setDependentCounters(Exprs.DependentCounters); + Dir->setDependentInits(Exprs.DependentInits); + Dir->setFinalsConditions(Exprs.FinalsConditions); Dir->setPreInits(Exprs.PreInits); return Dir; } @@ -1471,6 +1699,9 @@ OMPTeamsDistributeParallelForSimdDirective::Create( Dir->setInits(Exprs.Inits); Dir->setUpdates(Exprs.Updates); Dir->setFinals(Exprs.Finals); + Dir->setDependentCounters(Exprs.DependentCounters); + Dir->setDependentInits(Exprs.DependentInits); + Dir->setFinalsConditions(Exprs.FinalsConditions); Dir->setPreInits(Exprs.PreInits); Dir->setCombinedLowerBoundVariable(Exprs.DistCombinedFields.LB); Dir->setCombinedUpperBoundVariable(Exprs.DistCombinedFields.UB); @@ -1540,6 +1771,9 @@ OMPTeamsDistributeParallelForDirective::Create( Dir->setInits(Exprs.Inits); Dir->setUpdates(Exprs.Updates); Dir->setFinals(Exprs.Finals); + Dir->setDependentCounters(Exprs.DependentCounters); + Dir->setDependentInits(Exprs.DependentInits); + Dir->setFinalsConditions(Exprs.FinalsConditions); Dir->setPreInits(Exprs.PreInits); Dir->setCombinedLowerBoundVariable(Exprs.DistCombinedFields.LB); Dir->setCombinedUpperBoundVariable(Exprs.DistCombinedFields.UB); @@ -1628,6 +1862,9 @@ OMPTargetTeamsDistributeDirective *OMPTargetTeamsDistributeDirective::Create( Dir->setInits(Exprs.Inits); Dir->setUpdates(Exprs.Updates); Dir->setFinals(Exprs.Finals); + Dir->setDependentCounters(Exprs.DependentCounters); + Dir->setDependentInits(Exprs.DependentInits); + Dir->setFinalsConditions(Exprs.FinalsConditions); Dir->setPreInits(Exprs.PreInits); return Dir; } @@ -1688,6 +1925,9 @@ OMPTargetTeamsDistributeParallelForDirective::Create( Dir->setInits(Exprs.Inits); Dir->setUpdates(Exprs.Updates); Dir->setFinals(Exprs.Finals); + Dir->setDependentCounters(Exprs.DependentCounters); + Dir->setDependentInits(Exprs.DependentInits); + Dir->setFinalsConditions(Exprs.FinalsConditions); Dir->setPreInits(Exprs.PreInits); Dir->setCombinedLowerBoundVariable(Exprs.DistCombinedFields.LB); Dir->setCombinedUpperBoundVariable(Exprs.DistCombinedFields.UB); @@ -1761,6 +2001,9 @@ OMPTargetTeamsDistributeParallelForSimdDirective::Create( Dir->setInits(Exprs.Inits); Dir->setUpdates(Exprs.Updates); Dir->setFinals(Exprs.Finals); + Dir->setDependentCounters(Exprs.DependentCounters); + Dir->setDependentInits(Exprs.DependentInits); + Dir->setFinalsConditions(Exprs.FinalsConditions); Dir->setPreInits(Exprs.PreInits); Dir->setCombinedLowerBoundVariable(Exprs.DistCombinedFields.LB); Dir->setCombinedUpperBoundVariable(Exprs.DistCombinedFields.UB); @@ -1826,6 +2069,9 @@ OMPTargetTeamsDistributeSimdDirective::Create( Dir->setInits(Exprs.Inits); Dir->setUpdates(Exprs.Updates); Dir->setFinals(Exprs.Finals); + Dir->setDependentCounters(Exprs.DependentCounters); + Dir->setDependentInits(Exprs.DependentInits); + Dir->setFinalsConditions(Exprs.FinalsConditions); Dir->setPreInits(Exprs.PreInits); return Dir; } diff --git a/lib/AST/StmtPrinter.cpp b/lib/AST/StmtPrinter.cpp index 46802d765e1f0..7759ff6c13898 100644 --- a/lib/AST/StmtPrinter.cpp +++ b/lib/AST/StmtPrinter.cpp @@ -823,6 +823,24 @@ void StmtPrinter::VisitOMPTaskLoopSimdDirective( PrintOMPExecutableDirective(Node); } +void StmtPrinter::VisitOMPMasterTaskLoopDirective( + OMPMasterTaskLoopDirective *Node) { + Indent() << "#pragma omp master taskloop"; + PrintOMPExecutableDirective(Node); +} + +void StmtPrinter::VisitOMPMasterTaskLoopSimdDirective( + OMPMasterTaskLoopSimdDirective *Node) { + Indent() << "#pragma omp master taskloop simd"; + PrintOMPExecutableDirective(Node); +} + +void StmtPrinter::VisitOMPParallelMasterTaskLoopDirective( + OMPParallelMasterTaskLoopDirective *Node) { + Indent() << "#pragma omp parallel master taskloop"; + PrintOMPExecutableDirective(Node); +} + void StmtPrinter::VisitOMPDistributeDirective(OMPDistributeDirective *Node) { Indent() << "#pragma omp distribute"; PrintOMPExecutableDirective(Node); @@ -1102,7 +1120,7 @@ void StmtPrinter::VisitIntegerLiteral(IntegerLiteral *Node) { OS << Node->getValue().toString(10, isSigned); // Emit suffixes. Integer literals are always a builtin integer type. - switch (Node->getType()->getAs()->getKind()) { + switch (Node->getType()->castAs()->getKind()) { default: llvm_unreachable("Unexpected type for integer literal!"); case BuiltinType::Char_S: case BuiltinType::Char_U: OS << "i8"; break; @@ -1123,7 +1141,7 @@ void StmtPrinter::VisitFixedPointLiteral(FixedPointLiteral *Node) { return; OS << Node->getValueAsString(/*Radix=*/10); - switch (Node->getType()->getAs()->getKind()) { + switch (Node->getType()->castAs()->getKind()) { default: llvm_unreachable("Unexpected type for fixed point literal!"); case BuiltinType::ShortFract: OS << "hr"; break; case BuiltinType::ShortAccum: OS << "hk"; break; @@ -1152,7 +1170,7 @@ static void PrintFloatingLiteral(raw_ostream &OS, FloatingLiteral *Node, return; // Emit suffixes. Float literals are always a builtin float type. - switch (Node->getType()->getAs()->getKind()) { + switch (Node->getType()->castAs()->getKind()) { default: llvm_unreachable("Unexpected type for float literal!"); case BuiltinType::Half: break; // FIXME: suffix? case BuiltinType::Double: break; // no suffix. @@ -1679,6 +1697,15 @@ void StmtPrinter::VisitCUDAKernelCallExpr(CUDAKernelCallExpr *Node) { OS << ")"; } +void StmtPrinter::VisitCXXRewrittenBinaryOperator( + CXXRewrittenBinaryOperator *Node) { + CXXRewrittenBinaryOperator::DecomposedForm Decomposed = + Node->getDecomposedForm(); + PrintExpr(const_cast(Decomposed.LHS)); + OS << ' ' << BinaryOperator::getOpcodeStr(Decomposed.Opcode) << ' '; + PrintExpr(const_cast(Decomposed.RHS)); +} + void StmtPrinter::VisitCXXNamedCastExpr(CXXNamedCastExpr *Node) { OS << Node->getCastName() << '<'; Node->getTypeAsWritten().print(OS, Policy); @@ -1952,7 +1979,7 @@ void StmtPrinter::VisitLambdaExpr(LambdaExpr *Node) { if (Node->isMutable()) OS << " mutable"; - auto *Proto = Method->getType()->getAs(); + auto *Proto = Method->getType()->castAs(); Proto->printExceptionSpecification(OS, Policy); // FIXME: Attributes @@ -2219,6 +2246,17 @@ void StmtPrinter::VisitCXXFoldExpr(CXXFoldExpr *E) { OS << ")"; } +void StmtPrinter::VisitConceptSpecializationExpr(ConceptSpecializationExpr *E) { + NestedNameSpecifierLoc NNS = E->getNestedNameSpecifierLoc(); + if (NNS) + NNS.getNestedNameSpecifier()->print(OS, Policy); + if (E->getTemplateKWLoc().isValid()) + OS << "template "; + OS << E->getFoundDecl()->getName(); + printTemplateArgumentList(OS, E->getTemplateArgsAsWritten()->arguments(), + Policy); +} + // C++ Coroutines TS void StmtPrinter::VisitCoroutineBodyStmt(CoroutineBodyStmt *S) { diff --git a/lib/AST/StmtProfile.cpp b/lib/AST/StmtProfile.cpp index f92c3dc60ba5c..d1e8565389325 100644 --- a/lib/AST/StmtProfile.cpp +++ b/lib/AST/StmtProfile.cpp @@ -440,6 +440,7 @@ void OMPClauseProfiler::VisitOMPIfClause(const OMPIfClause *C) { } void OMPClauseProfiler::VisitOMPFinalClause(const OMPFinalClause *C) { + VistOMPClauseWithPreInit(C); if (C->getCondition()) Profiler->VisitStmt(C->getCondition()); } @@ -736,14 +737,17 @@ void OMPClauseProfiler::VisitOMPThreadLimitClause( Profiler->VisitStmt(C->getThreadLimit()); } void OMPClauseProfiler::VisitOMPPriorityClause(const OMPPriorityClause *C) { + VistOMPClauseWithPreInit(C); if (C->getPriority()) Profiler->VisitStmt(C->getPriority()); } void OMPClauseProfiler::VisitOMPGrainsizeClause(const OMPGrainsizeClause *C) { + VistOMPClauseWithPreInit(C); if (C->getGrainsize()) Profiler->VisitStmt(C->getGrainsize()); } void OMPClauseProfiler::VisitOMPNumTasksClause(const OMPNumTasksClause *C) { + VistOMPClauseWithPreInit(C); if (C->getNumTasks()) Profiler->VisitStmt(C->getNumTasks()); } @@ -918,6 +922,21 @@ void StmtProfiler::VisitOMPTaskLoopSimdDirective( VisitOMPLoopDirective(S); } +void StmtProfiler::VisitOMPMasterTaskLoopDirective( + const OMPMasterTaskLoopDirective *S) { + VisitOMPLoopDirective(S); +} + +void StmtProfiler::VisitOMPMasterTaskLoopSimdDirective( + const OMPMasterTaskLoopSimdDirective *S) { + VisitOMPLoopDirective(S); +} + +void StmtProfiler::VisitOMPParallelMasterTaskLoopDirective( + const OMPParallelMasterTaskLoopDirective *S) { + VisitOMPLoopDirective(S); +} + void StmtProfiler::VisitOMPDistributeDirective( const OMPDistributeDirective *S) { VisitOMPLoopDirective(S); @@ -1297,6 +1316,14 @@ void StmtProfiler::VisitAtomicExpr(const AtomicExpr *S) { ID.AddInteger(S->getOp()); } +void StmtProfiler::VisitConceptSpecializationExpr( + const ConceptSpecializationExpr *S) { + VisitExpr(S); + VisitDecl(S->getFoundDecl()); + VisitTemplateArguments(S->getTemplateArgsAsWritten()->getTemplateArgs(), + S->getTemplateArgsAsWritten()->NumTemplateArgs); +} + static Stmt::StmtClass DecodeOperatorCall(const CXXOperatorCallExpr *S, UnaryOperatorKind &UnaryOp, BinaryOperatorKind &BinaryOp) { @@ -1530,6 +1557,16 @@ void StmtProfiler::VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *S) { ID.AddInteger(S->getOperator()); } +void StmtProfiler::VisitCXXRewrittenBinaryOperator( + const CXXRewrittenBinaryOperator *S) { + // If a rewritten operator were ever to be type-dependent, we should profile + // it following its syntactic operator. + assert(!S->isTypeDependent() && + "resolved rewritten operator should never be type-dependent"); + ID.AddBoolean(S->isReversed()); + VisitExpr(S->getSemanticForm()); +} + #if defined(_MSC_VER) && !defined(__clang__) #if _MSC_VER == 1911 #pragma optimize("", on) diff --git a/lib/AST/TemplateBase.cpp b/lib/AST/TemplateBase.cpp index cb4cbd2f76a12..db16c2a06b64f 100644 --- a/lib/AST/TemplateBase.cpp +++ b/lib/AST/TemplateBase.cpp @@ -370,7 +370,7 @@ TemplateArgument TemplateArgument::getPackExpansionPattern() const { switch (getKind()) { case Type: - return getAsType()->getAs()->getPattern(); + return getAsType()->castAs()->getPattern(); case Expression: return cast(getAsExpr())->getPattern(); diff --git a/lib/AST/TextNodeDumper.cpp b/lib/AST/TextNodeDumper.cpp index cba9091b10652..63a6510324f75 100644 --- a/lib/AST/TextNodeDumper.cpp +++ b/lib/AST/TextNodeDumper.cpp @@ -223,7 +223,6 @@ void TextNodeDumper::Visit(const Decl *D) { return; } - Context = &D->getASTContext(); { ColorScope Color(OS, ShowColors, DeclKindNameColor); OS << D->getDeclKindName() << "Decl"; @@ -637,8 +636,8 @@ static void dumpBasePath(raw_ostream &OS, const CastExpr *Node) { if (!First) OS << " -> "; - const CXXRecordDecl *RD = - cast(Base->getType()->getAs()->getDecl()); + const auto *RD = + cast(Base->getType()->castAs()->getDecl()); if (Base->isVirtual()) OS << "virtual "; @@ -688,7 +687,7 @@ void TextNodeDumper::VisitConstantExpr(const ConstantExpr *Node) { if (Node->getResultAPValueKind() != APValue::None) { ColorScope Color(OS, ShowColors, ValueColor); OS << " "; - Node->getAPValueResult().printPretty(OS, *Context, Node->getType()); + Node->getAPValueResult().dump(OS); } } @@ -1385,6 +1384,8 @@ void TextNodeDumper::VisitVarDecl(const VarDecl *D) { break; } } + if (D->needsDestruction(D->getASTContext())) + OS << " destroyed"; if (D->isParameterPack()) OS << " pack"; } @@ -1537,6 +1538,7 @@ void TextNodeDumper::VisitCXXRecordDecl(const CXXRecordDecl *D) { FLAG(isGenericLambda, generic); FLAG(isLambda, lambda); + FLAG(isAnonymousStructOrUnion, is_anonymous); FLAG(canPassInRegisters, pass_in_registers); FLAG(isEmpty, empty); FLAG(isAggregate, aggregate); @@ -1641,6 +1643,7 @@ void TextNodeDumper::VisitCXXRecordDecl(const CXXRecordDecl *D) { FLAG(hasTrivialDestructor, trivial); FLAG(hasNonTrivialDestructor, non_trivial); FLAG(hasUserDeclaredDestructor, user_declared); + FLAG(hasConstexprDestructor, constexpr); FLAG(needsImplicitDestructor, needs_implicit); FLAG(needsOverloadResolutionForDestructor, needs_overload_resolution); if (!D->needsOverloadResolutionForDestructor()) @@ -1766,6 +1769,12 @@ void TextNodeDumper::VisitLinkageSpecDecl(const LinkageSpecDecl *D) { case LinkageSpecDecl::lang_cxx: OS << " C++"; break; + case LinkageSpecDecl::lang_cxx_11: + OS << " C++11"; + break; + case LinkageSpecDecl::lang_cxx_14: + OS << " C++14"; + break; } } diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index ed75a0b5bcd85..4d54ea1061edc 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -50,6 +50,7 @@ #include #include #include +#include using namespace clang; @@ -74,11 +75,11 @@ const IdentifierInfo* QualType::getBaseTypeIdentifier() const { if (ty->isPointerType() || ty->isReferenceType()) return ty->getPointeeType().getBaseTypeIdentifier(); else if (ty->isRecordType()) - ND = ty->getAs()->getDecl(); + ND = ty->castAs()->getDecl(); else if (ty->isEnumeralType()) - ND = ty->getAs()->getDecl(); + ND = ty->castAs()->getDecl(); else if (ty->getTypeClass() == Type::Typedef) - ND = ty->getAs()->getDecl(); + ND = ty->castAs()->getDecl(); else if (ty->isArrayType()) return ty->castAsArrayTypeUnsafe()-> getElementType().getBaseTypeIdentifier(); @@ -108,6 +109,33 @@ bool QualType::isConstant(QualType T, const ASTContext &Ctx) { return T.getAddressSpace() == LangAS::opencl_constant; } +// C++ [temp.dep.type]p1: +// A type is dependent if it is... +// - an array type constructed from any dependent type or whose +// size is specified by a constant expression that is +// value-dependent, +ArrayType::ArrayType(TypeClass tc, QualType et, QualType can, + ArraySizeModifier sm, unsigned tq, const Expr *sz) + // Note, we need to check for DependentSizedArrayType explicitly here + // because we use a DependentSizedArrayType with no size expression as the + // type of a dependent array of unknown bound with a dependent braced + // initializer: + // + // template int arr[] = {N...}; + : Type(tc, can, + et->isDependentType() || (sz && sz->isValueDependent()) || + tc == DependentSizedArray, + et->isInstantiationDependentType() || + (sz && sz->isInstantiationDependent()) || + tc == DependentSizedArray, + (tc == VariableArray || et->isVariablyModifiedType()), + et->containsUnexpandedParameterPack() || + (sz && sz->containsUnexpandedParameterPack())), + ElementType(et) { + ArrayTypeBits.IndexTypeQuals = tq; + ArrayTypeBits.SizeModifier = sm; +} + unsigned ConstantArrayType::getNumAddressingBits(const ASTContext &Context, QualType ElementType, const llvm::APInt &NumElements) { @@ -155,14 +183,26 @@ unsigned ConstantArrayType::getMaxSizeBits(const ASTContext &Context) { return Bits; } +void ConstantArrayType::Profile(llvm::FoldingSetNodeID &ID, + const ASTContext &Context, QualType ET, + const llvm::APInt &ArraySize, + const Expr *SizeExpr, ArraySizeModifier SizeMod, + unsigned TypeQuals) { + ID.AddPointer(ET.getAsOpaquePtr()); + ID.AddInteger(ArraySize.getZExtValue()); + ID.AddInteger(SizeMod); + ID.AddInteger(TypeQuals); + ID.AddBoolean(SizeExpr != 0); + if (SizeExpr) + SizeExpr->Profile(ID, Context, true); +} + DependentSizedArrayType::DependentSizedArrayType(const ASTContext &Context, QualType et, QualType can, Expr *e, ArraySizeModifier sm, unsigned tq, SourceRange brackets) - : ArrayType(DependentSizedArray, et, can, sm, tq, - (et->containsUnexpandedParameterPack() || - (e && e->containsUnexpandedParameterPack()))), + : ArrayType(DependentSizedArray, et, can, sm, tq, e), Context(Context), SizeExpr((Stmt*) e), Brackets(brackets) {} void DependentSizedArrayType::Profile(llvm::FoldingSetNodeID &ID, @@ -297,7 +337,19 @@ QualType QualType::getSingleStepDesugaredTypeImpl(QualType type, #define TYPE(CLASS, BASE) \ static_assert(!std::is_polymorphic::value, \ #CLASS "Type should not be polymorphic!"); -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" + +// Check that no type class has a non-trival destructor. Types are +// allocated with the BumpPtrAllocator from ASTContext and therefore +// their destructor is not executed. +// +// FIXME: ConstantArrayType is not trivially destructible because of its +// APInt member. It should be replaced in favor of ASTContext allocation. +#define TYPE(CLASS, BASE) \ + static_assert(std::is_trivially_destructible::value || \ + std::is_same::value, \ + #CLASS "Type should be trivially destructible!"); +#include "clang/AST/TypeNodes.inc" QualType Type::getLocallyUnqualifiedSingleStepDesugaredType() const { switch (getTypeClass()) { @@ -308,7 +360,7 @@ QualType Type::getLocallyUnqualifiedSingleStepDesugaredType() const { if (!ty->isSugared()) return QualType(ty, 0); \ return ty->desugar(); \ } -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" } llvm_unreachable("bad type kind!"); } @@ -329,7 +381,7 @@ SplitQualType QualType::getSplitDesugaredType(QualType T) { Cur = Ty->desugar(); \ break; \ } -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" } } } @@ -357,7 +409,7 @@ SplitQualType QualType::getSplitUnqualifiedTypeImpl(QualType type) { next = ty->desugar(); \ break; \ } -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" } // Otherwise, split the underlying type. If that yields qualifiers, @@ -396,7 +448,7 @@ template static const T *getAsSugar(const Type *Cur) { Cur = Ty->desugar().getTypePtr(); \ break; \ } -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" } } } @@ -429,7 +481,7 @@ const Type *Type::getUnqualifiedDesugaredType() const { Cur = Ty->desugar().getTypePtr(); \ break; \ } -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" } } } @@ -611,6 +663,10 @@ ObjCTypeParamType::ObjCTypeParamType(const ObjCTypeParamDecl *D, initialize(protocols); } +QualType ObjCTypeParamType::desugar() const { + return getDecl()->getUnderlyingType(); +} + ObjCObjectType::ObjCObjectType(QualType Canonical, QualType Base, ArrayRef typeArgs, ArrayRef protocols, @@ -753,7 +809,7 @@ public: #define TYPE(Class, Base) #define DEPENDENT_TYPE(Class, Base) \ QualType Visit##Class##Type(const Class##Type *T) { return QualType(T, 0); } -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" #define TRIVIAL_TYPE_CLASS(Class) \ QualType Visit##Class##Type(const Class##Type *T) { return QualType(T, 0); } @@ -847,7 +903,7 @@ public: if (elementType.getAsOpaquePtr() == T->getElementType().getAsOpaquePtr()) return QualType(T, 0); - return Ctx.getConstantArrayType(elementType, T->getSize(), + return Ctx.getConstantArrayType(elementType, T->getSize(), T->getSizeExpr(), T->getSizeModifier(), T->getIndexTypeCVRQualifiers()); } @@ -2477,6 +2533,15 @@ bool QualType::isCXX11PODType(const ASTContext &Context) const { return false; } +bool Type::isNothrowT() const { + if (const auto *RD = getAsCXXRecordDecl()) { + IdentifierInfo *II = RD->getIdentifier(); + if (II && II->isStr("nothrow_t") && RD->isInStdNamespace()) + return true; + } + return false; +} + bool Type::isAlignValT() const { if (const auto *ET = getAs()) { IdentifierInfo *II = ET->getDecl()->getIdentifier(); @@ -2690,7 +2755,7 @@ const char *Type::getTypeClassName() const { switch (TypeBits.TC) { #define ABSTRACT_TYPE(Derived, Base) #define TYPE(Derived, Base) case Derived: return #Derived; -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" } llvm_unreachable("Invalid type class."); @@ -2841,6 +2906,10 @@ StringRef BuiltinType::getName(const PrintingPolicy &Policy) const { case Id: \ return #ExtType; #include "clang/Basic/OpenCLExtensionTypes.def" +#define SVE_TYPE(Name, Id, SingletonId) \ + case Id: \ + return Name; +#include "clang/Basic/AArch64SVEACLETypes.def" } llvm_unreachable("Invalid builtin type."); @@ -3556,14 +3625,15 @@ static CachedProperties computeCachedProperties(const Type *T) { switch (T->getTypeClass()) { #define TYPE(Class,Base) #define NON_CANONICAL_TYPE(Class,Base) case Type::Class: -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" llvm_unreachable("didn't expect a non-canonical type here"); #define TYPE(Class,Base) #define DEPENDENT_TYPE(Class,Base) case Type::Class: #define NON_CANONICAL_UNLESS_DEPENDENT_TYPE(Class,Base) case Type::Class: -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" // Treat instantiation-dependent types as external. + if (!T->isInstantiationDependentType()) T->dump(); assert(T->isInstantiationDependentType()); return CachedProperties(ExternalLinkage, false); @@ -3659,13 +3729,13 @@ LinkageInfo LinkageComputer::computeTypeLinkageInfo(const Type *T) { switch (T->getTypeClass()) { #define TYPE(Class,Base) #define NON_CANONICAL_TYPE(Class,Base) case Type::Class: -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" llvm_unreachable("didn't expect a non-canonical type here"); #define TYPE(Class,Base) #define DEPENDENT_TYPE(Class,Base) case Type::Class: #define NON_CANONICAL_UNLESS_DEPENDENT_TYPE(Class,Base) case Type::Class: -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" // Treat instantiation-dependent types as external. assert(T->isInstantiationDependentType()); return LinkageInfo::external(); @@ -3774,7 +3844,7 @@ bool Type::canHaveNullability(bool ResultIfUnknown) const { case Type::Class: \ llvm_unreachable("non-canonical type"); #define TYPE(Class, Parent) -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" // Pointer types. case Type::Pointer: @@ -3842,6 +3912,9 @@ bool Type::canHaveNullability(bool ResultIfUnknown) const { case BuiltinType::OCLClkEvent: case BuiltinType::OCLQueue: case BuiltinType::OCLReserveID: +#define SVE_TYPE(Name, Id, SingletonId) \ + case BuiltinType::Id: +#include "clang/Basic/AArch64SVEACLETypes.def" case BuiltinType::BuiltinFn: case BuiltinType::NullPtr: case BuiltinType::OMPArraySection: diff --git a/lib/AST/TypeLoc.cpp b/lib/AST/TypeLoc.cpp index abe4c4eb25e6f..e4788f32b2656 100644 --- a/lib/AST/TypeLoc.cpp +++ b/lib/AST/TypeLoc.cpp @@ -391,6 +391,9 @@ TypeSpecifierType BuiltinTypeLoc::getWrittenTypeSpec() const { case BuiltinType::OCLClkEvent: case BuiltinType::OCLQueue: case BuiltinType::OCLReserveID: +#define SVE_TYPE(Name, Id, SingletonId) \ + case BuiltinType::Id: +#include "clang/Basic/AArch64SVEACLETypes.def" case BuiltinType::BuiltinFn: case BuiltinType::OMPArraySection: return TST_unspecified; diff --git a/lib/AST/TypePrinter.cpp b/lib/AST/TypePrinter.cpp index 8d5c37299e5fb..dacbf9a96d814 100644 --- a/lib/AST/TypePrinter.cpp +++ b/lib/AST/TypePrinter.cpp @@ -125,7 +125,7 @@ namespace { #define TYPE(CLASS, PARENT) \ void print##CLASS##Before(const CLASS##Type *T, raw_ostream &OS); \ void print##CLASS##After(const CLASS##Type *T, raw_ostream &OS); -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" private: void printBefore(const Type *ty, Qualifiers qs, raw_ostream &OS); @@ -321,7 +321,7 @@ void TypePrinter::printBefore(const Type *T,Qualifiers Quals, raw_ostream &OS) { #define TYPE(CLASS, PARENT) case Type::CLASS: \ print##CLASS##Before(cast(T), OS); \ break; -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" } if (hasAfterQuals) { @@ -347,7 +347,7 @@ void TypePrinter::printAfter(const Type *T, Qualifiers Quals, raw_ostream &OS) { #define TYPE(CLASS, PARENT) case Type::CLASS: \ print##CLASS##After(cast(T), OS); \ break; -#include "clang/AST/TypeNodes.def" +#include "clang/AST/TypeNodes.inc" } } @@ -1204,7 +1204,8 @@ void TypePrinter::printTag(TagDecl *D, raw_ostream &OS) { // arguments. if (const auto *Spec = dyn_cast(D)) { ArrayRef Args; - if (TypeSourceInfo *TAW = Spec->getTypeAsWritten()) { + TypeSourceInfo *TAW = Spec->getTypeAsWritten(); + if (!Policy.PrintCanonicalTypes && TAW) { const TemplateSpecializationType *TST = cast(TAW->getType()); Args = TST->template_arguments(); @@ -1537,7 +1538,7 @@ void TypePrinter::printAttributedAfter(const AttributedType *T, QualType t = T->getEquivalentType(); while (!t->isFunctionType()) t = t->getPointeeType(); - OS << (t->getAs()->getCallConv() == CC_AAPCS ? + OS << (t->castAs()->getCallConv() == CC_AAPCS ? "\"aapcs\"" : "\"aapcs-vfp\""); OS << ')'; break; diff --git a/lib/AST/VTTBuilder.cpp b/lib/AST/VTTBuilder.cpp index 53d0ef09f14c5..d58e875177852 100644 --- a/lib/AST/VTTBuilder.cpp +++ b/lib/AST/VTTBuilder.cpp @@ -64,8 +64,8 @@ void VTTBuilder::LayoutSecondaryVTTs(BaseSubobject Base) { if (I.isVirtual()) continue; - const CXXRecordDecl *BaseDecl = - cast(I.getType()->getAs()->getDecl()); + const auto *BaseDecl = + cast(I.getType()->castAs()->getDecl()); const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD); CharUnits BaseOffset = Base.getBaseOffset() + @@ -90,8 +90,8 @@ VTTBuilder::LayoutSecondaryVirtualPointers(BaseSubobject Base, return; for (const auto &I : RD->bases()) { - const CXXRecordDecl *BaseDecl = - cast(I.getType()->getAs()->getDecl()); + const auto *BaseDecl = + cast(I.getType()->castAs()->getDecl()); // Itanium C++ ABI 2.6.2: // Secondary virtual pointers are present for all bases with either @@ -154,8 +154,8 @@ VTTBuilder::LayoutSecondaryVirtualPointers(BaseSubobject Base, void VTTBuilder::LayoutVirtualVTTs(const CXXRecordDecl *RD, VisitedVirtualBasesSetTy &VBases) { for (const auto &I : RD->bases()) { - const CXXRecordDecl *BaseDecl = - cast(I.getType()->getAs()->getDecl()); + const auto *BaseDecl = + cast(I.getType()->castAs()->getDecl()); // Check if this is a virtual base. if (I.isVirtual()) { diff --git a/lib/AST/VTableBuilder.cpp b/lib/AST/VTableBuilder.cpp index 0c699571555d5..5688042dadd91 100644 --- a/lib/AST/VTableBuilder.cpp +++ b/lib/AST/VTableBuilder.cpp @@ -2268,7 +2268,7 @@ CreateVTableLayout(const ItaniumVTableBuilder &Builder) { SmallVector VTableThunks(Builder.vtable_thunks_begin(), Builder.vtable_thunks_end()); - return llvm::make_unique( + return std::make_unique( Builder.VTableIndices, Builder.vtable_components(), VTableThunks, Builder.getAddressPoints()); } @@ -3253,7 +3253,7 @@ void MicrosoftVTableContext::computeVTablePaths(bool ForVBTables, // Base case: this subobject has its own vptr. if (ForVBTables ? Layout.hasOwnVBPtr() : Layout.hasOwnVFPtr()) - Paths.push_back(llvm::make_unique(RD)); + Paths.push_back(std::make_unique(RD)); // Recursive case: get all the vbtables from our bases and remove anything // that shares a virtual base. @@ -3276,7 +3276,7 @@ void MicrosoftVTableContext::computeVTablePaths(bool ForVBTables, continue; // Copy the path and adjust it as necessary. - auto P = llvm::make_unique(*BaseInfo); + auto P = std::make_unique(*BaseInfo); // We mangle Base into the path if the path would've been ambiguous and it // wasn't already extended with Base. @@ -3562,7 +3562,7 @@ void MicrosoftVTableContext::computeVTableRelatedInformation( const VTableLayout::AddressPointsMapTy EmptyAddressPointsMap; { - auto VFPtrs = llvm::make_unique(); + auto VFPtrs = std::make_unique(); computeVTablePaths(/*ForVBTables=*/false, RD, *VFPtrs); computeFullPathsForVFTables(Context, RD, *VFPtrs); VFPtrLocations[RD] = std::move(VFPtrs); @@ -3576,7 +3576,7 @@ void MicrosoftVTableContext::computeVTableRelatedInformation( assert(VFTableLayouts.count(id) == 0); SmallVector VTableThunks( Builder.vtable_thunks_begin(), Builder.vtable_thunks_end()); - VFTableLayouts[id] = llvm::make_unique( + VFTableLayouts[id] = std::make_unique( ArrayRef{0}, Builder.vtable_components(), VTableThunks, EmptyAddressPointsMap); Thunks.insert(Builder.thunks_begin(), Builder.thunks_end()); @@ -3668,7 +3668,7 @@ const VirtualBaseInfo &MicrosoftVTableContext::computeVBTableRelatedInformation( std::unique_ptr &Entry = VBaseInfo[RD]; if (Entry) return *Entry; - Entry = llvm::make_unique(); + Entry = std::make_unique(); VBI = Entry.get(); } diff --git a/lib/ASTMatchers/ASTMatchFinder.cpp b/lib/ASTMatchers/ASTMatchFinder.cpp index f407e0875ac47..c51fd630e64bd 100644 --- a/lib/ASTMatchers/ASTMatchFinder.cpp +++ b/lib/ASTMatchers/ASTMatchFinder.cpp @@ -374,6 +374,12 @@ public: return true; } + bool VisitObjCCompatibleAliasDecl(ObjCCompatibleAliasDecl *CAD) { + const ObjCInterfaceDecl *InterfaceDecl = CAD->getClassInterface(); + CompatibleAliases[InterfaceDecl].insert(CAD); + return true; + } + bool TraverseDecl(Decl *DeclNode); bool TraverseStmt(Stmt *StmtNode, DataRecursionQueue *Queue = nullptr); bool TraverseType(QualType TypeNode); @@ -430,7 +436,13 @@ public: bool classIsDerivedFrom(const CXXRecordDecl *Declaration, const Matcher &Base, - BoundNodesTreeBuilder *Builder) override; + BoundNodesTreeBuilder *Builder, + bool Directly) override; + + bool objcClassIsDerivedFrom(const ObjCInterfaceDecl *Declaration, + const Matcher &Base, + BoundNodesTreeBuilder *Builder, + bool Directly) override; // Implements ASTMatchFinder::matchesChildOf. bool matchesChildOf(const ast_type_traits::DynTypedNode &Node, @@ -762,6 +774,23 @@ private: return false; } + bool + objcClassHasMatchingCompatibilityAlias(const ObjCInterfaceDecl *InterfaceDecl, + const Matcher &Matcher, + BoundNodesTreeBuilder *Builder) { + auto Aliases = CompatibleAliases.find(InterfaceDecl); + if (Aliases == CompatibleAliases.end()) + return false; + for (const ObjCCompatibleAliasDecl *Alias : Aliases->second) { + BoundNodesTreeBuilder Result(*Builder); + if (Matcher.matches(*Alias, this, &Result)) { + *Builder = std::move(Result); + return true; + } + } + return false; + } + /// Bucket to record map. /// /// Used to get the appropriate bucket for each matcher. @@ -786,6 +815,11 @@ private: // Maps a canonical type to its TypedefDecls. llvm::DenseMap > TypeAliases; + // Maps an Objective-C interface to its ObjCCompatibleAliasDecls. + llvm::DenseMap> + CompatibleAliases; + // Maps (matcher, node) -> the match result for memoization. typedef std::map MemoizationMap; MemoizationMap ResultCache; @@ -812,12 +846,13 @@ getAsCXXRecordDeclOrPrimaryTemplate(const Type *TypeNode) { return nullptr; } -// Returns true if the given class is directly or indirectly derived +// Returns true if the given C++ class is directly or indirectly derived // from a base type with the given name. A class is not considered to be // derived from itself. bool MatchASTVisitor::classIsDerivedFrom(const CXXRecordDecl *Declaration, const Matcher &Base, - BoundNodesTreeBuilder *Builder) { + BoundNodesTreeBuilder *Builder, + bool Directly) { if (!Declaration->hasDefinition()) return false; for (const auto &It : Declaration->bases()) { @@ -842,12 +877,40 @@ bool MatchASTVisitor::classIsDerivedFrom(const CXXRecordDecl *Declaration, *Builder = std::move(Result); return true; } - if (classIsDerivedFrom(ClassDecl, Base, Builder)) + if (!Directly && classIsDerivedFrom(ClassDecl, Base, Builder, Directly)) return true; } return false; } +// Returns true if the given Objective-C class is directly or indirectly +// derived from a matching base class. A class is not considered to be derived +// from itself. +bool MatchASTVisitor::objcClassIsDerivedFrom( + const ObjCInterfaceDecl *Declaration, const Matcher &Base, + BoundNodesTreeBuilder *Builder, bool Directly) { + // Check if any of the superclasses of the class match. + for (const ObjCInterfaceDecl *ClassDecl = Declaration->getSuperClass(); + ClassDecl != nullptr; ClassDecl = ClassDecl->getSuperClass()) { + // Check if there are any matching compatibility aliases. + if (objcClassHasMatchingCompatibilityAlias(ClassDecl, Base, Builder)) + return true; + + // Check if there are any matching type aliases. + const Type *TypeNode = ClassDecl->getTypeForDecl(); + if (typeHasMatchingAlias(TypeNode, Base, Builder)) + return true; + + if (Base.matches(*ClassDecl, this, Builder)) + return true; + + if (Directly) + return false; + } + + return false; +} + bool MatchASTVisitor::TraverseDecl(Decl *DeclNode) { if (!DeclNode) { return true; @@ -1015,7 +1078,7 @@ bool MatchFinder::addDynamicMatcher(const internal::DynTypedMatcher &NodeMatch, } std::unique_ptr MatchFinder::newASTConsumer() { - return llvm::make_unique(this, ParsingDone); + return std::make_unique(this, ParsingDone); } void MatchFinder::match(const clang::ast_type_traits::DynTypedNode &Node, diff --git a/lib/ASTMatchers/Dynamic/Marshallers.h b/lib/ASTMatchers/Dynamic/Marshallers.h index fac2fc98e09cd..9f46108d1848d 100644 --- a/lib/ASTMatchers/Dynamic/Marshallers.h +++ b/lib/ASTMatchers/Dynamic/Marshallers.h @@ -729,7 +729,7 @@ std::unique_ptr makeMatcherAutoMarshall(ReturnType (*Func)(), StringRef MatcherName) { std::vector RetTypes; BuildReturnTypeVector::build(RetTypes); - return llvm::make_unique( + return std::make_unique( matcherMarshall0, reinterpret_cast(Func), MatcherName, RetTypes, None); } @@ -741,7 +741,7 @@ makeMatcherAutoMarshall(ReturnType (*Func)(ArgType1), StringRef MatcherName) { std::vector RetTypes; BuildReturnTypeVector::build(RetTypes); ArgKind AK = ArgTypeTraits::getKind(); - return llvm::make_unique( + return std::make_unique( matcherMarshall1, reinterpret_cast(Func), MatcherName, RetTypes, AK); } @@ -755,7 +755,7 @@ makeMatcherAutoMarshall(ReturnType (*Func)(ArgType1, ArgType2), BuildReturnTypeVector::build(RetTypes); ArgKind AKs[] = { ArgTypeTraits::getKind(), ArgTypeTraits::getKind() }; - return llvm::make_unique( + return std::make_unique( matcherMarshall2, reinterpret_cast(Func), MatcherName, RetTypes, AKs); } @@ -766,7 +766,7 @@ template makeMatcherAutoMarshall( ast_matchers::internal::VariadicFunction VarFunc, StringRef MatcherName) { - return llvm::make_unique(VarFunc, MatcherName); + return std::make_unique(VarFunc, MatcherName); } /// Overload for VariadicDynCastAllOfMatchers. @@ -778,7 +778,7 @@ std::unique_ptr makeMatcherAutoMarshall( ast_matchers::internal::VariadicDynCastAllOfMatcher VarFunc, StringRef MatcherName) { - return llvm::make_unique(VarFunc, MatcherName); + return std::make_unique(VarFunc, MatcherName); } /// Argument adaptative overload. @@ -791,7 +791,7 @@ std::unique_ptr makeMatcherAutoMarshall( std::vector> Overloads; AdaptativeOverloadCollector(MatcherName, Overloads); - return llvm::make_unique(Overloads); + return std::make_unique(Overloads); } template