aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/ARCMigrate/ARCMT.cpp6
-rw-r--r--lib/ARCMigrate/FileRemapper.cpp24
-rw-r--r--lib/ARCMigrate/ObjCMT.cpp15
-rw-r--r--lib/ARCMigrate/PlistReporter.cpp2
-rw-r--r--lib/AST/APValue.cpp38
-rw-r--r--lib/AST/ASTContext.cpp757
-rw-r--r--lib/AST/ASTDiagnostic.cpp2
-rw-r--r--lib/AST/ASTImporter.cpp542
-rw-r--r--lib/AST/ASTStructuralEquivalence.cpp90
-rw-r--r--lib/AST/ASTTypeTraits.cpp16
-rw-r--r--lib/AST/CXXInheritance.cpp2
-rw-r--r--lib/AST/Comment.cpp15
-rw-r--r--lib/AST/CommentLexer.cpp7
-rw-r--r--lib/AST/CommentParser.cpp6
-rw-r--r--lib/AST/CommentSema.cpp8
-rw-r--r--lib/AST/Decl.cpp75
-rw-r--r--lib/AST/DeclBase.cpp15
-rw-r--r--lib/AST/DeclCXX.cpp84
-rw-r--r--lib/AST/DeclPrinter.cpp15
-rw-r--r--lib/AST/DeclTemplate.cpp69
-rw-r--r--lib/AST/Expr.cpp140
-rw-r--r--lib/AST/ExprCXX.cpp176
-rw-r--r--lib/AST/ExprClassification.cpp5
-rw-r--r--lib/AST/ExprConstant.cpp2474
-rw-r--r--lib/AST/ExternalASTMerger.cpp124
-rw-r--r--lib/AST/FormatString.cpp4
-rw-r--r--lib/AST/FormatStringParsing.h13
-rw-r--r--lib/AST/InheritViz.cpp4
-rw-r--r--lib/AST/Interp/Block.cpp87
-rw-r--r--lib/AST/Interp/Block.h140
-rw-r--r--lib/AST/Interp/Boolean.h148
-rw-r--r--lib/AST/Interp/ByteCodeEmitter.cpp175
-rw-r--r--lib/AST/Interp/ByteCodeEmitter.h112
-rw-r--r--lib/AST/Interp/ByteCodeExprGen.cpp580
-rw-r--r--lib/AST/Interp/ByteCodeExprGen.h331
-rw-r--r--lib/AST/Interp/ByteCodeGenError.cpp14
-rw-r--r--lib/AST/Interp/ByteCodeGenError.h46
-rw-r--r--lib/AST/Interp/ByteCodeStmtGen.cpp265
-rw-r--r--lib/AST/Interp/ByteCodeStmtGen.h89
-rw-r--r--lib/AST/Interp/Context.cpp148
-rw-r--r--lib/AST/Interp/Context.h100
-rw-r--r--lib/AST/Interp/Descriptor.cpp292
-rw-r--r--lib/AST/Interp/Descriptor.h220
-rw-r--r--lib/AST/Interp/Disasm.cpp69
-rw-r--r--lib/AST/Interp/EvalEmitter.cpp253
-rw-r--r--lib/AST/Interp/EvalEmitter.h129
-rw-r--r--lib/AST/Interp/Frame.cpp14
-rw-r--r--lib/AST/Interp/Frame.h45
-rw-r--r--lib/AST/Interp/Function.cpp48
-rw-r--r--lib/AST/Interp/Function.h163
-rw-r--r--lib/AST/Interp/Integral.h269
-rw-r--r--lib/AST/Interp/Interp.cpp417
-rw-r--r--lib/AST/Interp/Interp.h960
-rw-r--r--lib/AST/Interp/InterpFrame.cpp193
-rw-r--r--lib/AST/Interp/InterpFrame.h153
-rw-r--r--lib/AST/Interp/InterpStack.cpp78
-rw-r--r--lib/AST/Interp/InterpStack.h113
-rw-r--r--lib/AST/Interp/InterpState.cpp74
-rw-r--r--lib/AST/Interp/InterpState.h112
-rw-r--r--lib/AST/Interp/Opcode.h30
-rw-r--r--lib/AST/Interp/Opcodes.td422
-rw-r--r--lib/AST/Interp/Pointer.cpp193
-rw-r--r--lib/AST/Interp/Pointer.h353
-rw-r--r--lib/AST/Interp/PrimType.cpp23
-rw-r--r--lib/AST/Interp/PrimType.h115
-rw-r--r--lib/AST/Interp/Program.cpp364
-rw-r--r--lib/AST/Interp/Program.h220
-rw-r--r--lib/AST/Interp/Record.cpp46
-rw-r--r--lib/AST/Interp/Record.h121
-rw-r--r--lib/AST/Interp/Source.cpp39
-rw-r--r--lib/AST/Interp/Source.h118
-rw-r--r--lib/AST/Interp/State.cpp158
-rw-r--r--lib/AST/Interp/State.h133
-rw-r--r--lib/AST/ItaniumCXXABI.cpp72
-rw-r--r--lib/AST/ItaniumMangle.cpp154
-rw-r--r--lib/AST/JSONNodeDumper.cpp49
-rw-r--r--lib/AST/Mangle.cpp16
-rw-r--r--lib/AST/MicrosoftCXXABI.cpp4
-rw-r--r--lib/AST/MicrosoftMangle.cpp32
-rw-r--r--lib/AST/NSAPI.cpp14
-rw-r--r--lib/AST/OpenMPClause.cpp71
-rw-r--r--lib/AST/PrintfFormatString.cpp20
-rw-r--r--lib/AST/RawCommentList.cpp64
-rw-r--r--lib/AST/Stmt.cpp11
-rw-r--r--lib/AST/StmtOpenMP.cpp246
-rw-r--r--lib/AST/StmtPrinter.cpp46
-rw-r--r--lib/AST/StmtProfile.cpp37
-rw-r--r--lib/AST/TemplateBase.cpp2
-rw-r--r--lib/AST/TextNodeDumper.cpp17
-rw-r--r--lib/AST/Type.cpp113
-rw-r--r--lib/AST/TypeLoc.cpp3
-rw-r--r--lib/AST/TypePrinter.cpp11
-rw-r--r--lib/AST/VTTBuilder.cpp12
-rw-r--r--lib/AST/VTableBuilder.cpp12
-rw-r--r--lib/ASTMatchers/ASTMatchFinder.cpp73
-rw-r--r--lib/ASTMatchers/Dynamic/Marshallers.h14
-rw-r--r--lib/ASTMatchers/Dynamic/Registry.cpp3
-rw-r--r--lib/Analysis/AnalysisDeclContext.cpp23
-rw-r--r--lib/Analysis/BodyFarm.cpp4
-rw-r--r--lib/Analysis/CFG.cpp432
-rw-r--r--lib/Analysis/CallGraph.cpp42
-rw-r--r--lib/Analysis/CloneDetection.cpp3
-rw-r--r--lib/Analysis/CocoaConventions.cpp4
-rw-r--r--lib/Analysis/Consumed.cpp12
-rw-r--r--lib/Analysis/PathDiagnostic.cpp (renamed from lib/StaticAnalyzer/Core/PathDiagnostic.cpp)289
-rw-r--r--lib/Analysis/ProgramPoint.cpp6
-rw-r--r--lib/Analysis/ReachableCode.cpp2
-rw-r--r--lib/Analysis/RetainSummaryManager.cpp2
-rw-r--r--lib/Analysis/ThreadSafety.cpp30
-rw-r--r--lib/Analysis/plugins/SampleAnalyzer/MainCallChecker.cpp4
-rw-r--r--lib/Basic/Attributes.cpp75
-rw-r--r--lib/Basic/FileManager.cpp257
-rw-r--r--lib/Basic/IdentifierTable.cpp15
-rw-r--r--lib/Basic/LangStandards.cpp (renamed from lib/Frontend/LangStandards.cpp)25
-rw-r--r--lib/Basic/Module.cpp4
-rw-r--r--lib/Basic/OpenMPKinds.cpp85
-rw-r--r--lib/Basic/SourceManager.cpp203
-rw-r--r--lib/Basic/Stack.cpp75
-rw-r--r--lib/Basic/TargetInfo.cpp6
-rw-r--r--lib/Basic/Targets.cpp23
-rw-r--r--lib/Basic/Targets/AArch64.cpp32
-rw-r--r--lib/Basic/Targets/AArch64.h1
-rw-r--r--lib/Basic/Targets/AMDGPU.cpp8
-rw-r--r--lib/Basic/Targets/ARM.cpp103
-rw-r--r--lib/Basic/Targets/BPF.cpp12
-rw-r--r--lib/Basic/Targets/BPF.h4
-rw-r--r--lib/Basic/Targets/OSTargets.h15
-rw-r--r--lib/Basic/Targets/PPC.cpp10
-rw-r--r--lib/Basic/Targets/PPC.h1
-rw-r--r--lib/Basic/Targets/RISCV.cpp63
-rw-r--r--lib/Basic/Targets/RISCV.h20
-rw-r--r--lib/Basic/Targets/SPIR.h2
-rw-r--r--lib/Basic/Targets/Sparc.h1
-rw-r--r--lib/Basic/Targets/SystemZ.cpp2
-rw-r--r--lib/Basic/Targets/X86.cpp20
-rw-r--r--lib/Basic/Targets/X86.h39
-rw-r--r--lib/Basic/TokenKinds.cpp20
-rw-r--r--lib/CodeGen/BackendUtil.cpp85
-rw-r--r--lib/CodeGen/CGAtomic.cpp9
-rw-r--r--lib/CodeGen/CGBlocks.cpp5
-rw-r--r--lib/CodeGen/CGBuiltin.cpp518
-rw-r--r--lib/CodeGen/CGCUDANV.cpp23
-rw-r--r--lib/CodeGen/CGCXX.cpp6
-rw-r--r--lib/CodeGen/CGCXXABI.cpp4
-rw-r--r--lib/CodeGen/CGCXXABI.h2
-rw-r--r--lib/CodeGen/CGCall.cpp84
-rw-r--r--lib/CodeGen/CGClass.cpp51
-rw-r--r--lib/CodeGen/CGCleanup.cpp11
-rw-r--r--lib/CodeGen/CGDebugInfo.cpp97
-rw-r--r--lib/CodeGen/CGDecl.cpp39
-rw-r--r--lib/CodeGen/CGDeclCXX.cpp15
-rw-r--r--lib/CodeGen/CGException.cpp8
-rw-r--r--lib/CodeGen/CGExpr.cpp90
-rw-r--r--lib/CodeGen/CGExprAgg.cpp26
-rw-r--r--lib/CodeGen/CGExprCXX.cpp45
-rw-r--r--lib/CodeGen/CGExprComplex.cpp4
-rw-r--r--lib/CodeGen/CGExprConstant.cpp10
-rw-r--r--lib/CodeGen/CGExprScalar.cpp223
-rw-r--r--lib/CodeGen/CGLoopInfo.cpp60
-rw-r--r--lib/CodeGen/CGLoopInfo.h12
-rw-r--r--lib/CodeGen/CGNonTrivialStruct.cpp2
-rw-r--r--lib/CodeGen/CGObjC.cpp4
-rw-r--r--lib/CodeGen/CGObjCGNU.cpp13
-rw-r--r--lib/CodeGen/CGObjCMac.cpp47
-rw-r--r--lib/CodeGen/CGOpenMPRuntime.cpp1214
-rw-r--r--lib/CodeGen/CGOpenMPRuntime.h106
-rw-r--r--lib/CodeGen/CGOpenMPRuntimeNVPTX.cpp128
-rw-r--r--lib/CodeGen/CGOpenMPRuntimeNVPTX.h12
-rw-r--r--lib/CodeGen/CGStmt.cpp72
-rw-r--r--lib/CodeGen/CGStmtOpenMP.cpp160
-rw-r--r--lib/CodeGen/CGVTables.cpp92
-rw-r--r--lib/CodeGen/CodeGenAction.cpp41
-rw-r--r--lib/CodeGen/CodeGenFunction.cpp56
-rw-r--r--lib/CodeGen/CodeGenFunction.h15
-rw-r--r--lib/CodeGen/CodeGenModule.cpp233
-rw-r--r--lib/CodeGen/CodeGenModule.h15
-rw-r--r--lib/CodeGen/CodeGenPGO.cpp2
-rw-r--r--lib/CodeGen/CodeGenPGO.h4
-rw-r--r--lib/CodeGen/CodeGenTypes.cpp25
-rw-r--r--lib/CodeGen/ConstantInitBuilder.cpp2
-rw-r--r--lib/CodeGen/CoverageMappingGen.cpp27
-rw-r--r--lib/CodeGen/CoverageMappingGen.h8
-rw-r--r--lib/CodeGen/EHScopeStack.h4
-rw-r--r--lib/CodeGen/ItaniumCXXABI.cpp246
-rw-r--r--lib/CodeGen/MicrosoftCXXABI.cpp17
-rw-r--r--lib/CodeGen/ModuleBuilder.cpp15
-rw-r--r--lib/CodeGen/ObjectFilePCHContainerOperations.cpp12
-rw-r--r--lib/CodeGen/TargetInfo.cpp362
-rw-r--r--lib/CrossTU/CrossTranslationUnit.cpp261
-rw-r--r--lib/DirectoryWatcher/default/DirectoryWatcher-not-implemented.cpp6
-rw-r--r--lib/DirectoryWatcher/linux/DirectoryWatcher-linux.cpp33
-rw-r--r--lib/DirectoryWatcher/mac/DirectoryWatcher-mac.cpp34
-rw-r--r--lib/DirectoryWatcher/windows/DirectoryWatcher-windows.cpp50
-rw-r--r--lib/Driver/Action.cpp14
-rw-r--r--lib/Driver/Compilation.cpp2
-rw-r--r--lib/Driver/Driver.cpp524
-rw-r--r--lib/Driver/DriverOptions.cpp17
-rw-r--r--lib/Driver/Phases.cpp1
-rw-r--r--lib/Driver/SanitizerArgs.cpp40
-rw-r--r--lib/Driver/ToolChain.cpp32
-rw-r--r--lib/Driver/ToolChains/AMDGPU.cpp2
-rw-r--r--lib/Driver/ToolChains/AVR.cpp2
-rw-r--r--lib/Driver/ToolChains/Ananas.cpp4
-rw-r--r--lib/Driver/ToolChains/Arch/AArch64.cpp1
-rw-r--r--lib/Driver/ToolChains/Arch/ARM.cpp50
-rw-r--r--lib/Driver/ToolChains/Arch/Mips.cpp25
-rw-r--r--lib/Driver/ToolChains/Arch/Mips.h3
-rw-r--r--lib/Driver/ToolChains/Arch/PPC.cpp4
-rw-r--r--lib/Driver/ToolChains/Arch/RISCV.cpp311
-rw-r--r--lib/Driver/ToolChains/Arch/RISCV.h3
-rw-r--r--lib/Driver/ToolChains/Arch/X86.cpp1
-rw-r--r--lib/Driver/ToolChains/BareMetal.cpp2
-rw-r--r--lib/Driver/ToolChains/Clang.cpp618
-rw-r--r--lib/Driver/ToolChains/Clang.h18
-rw-r--r--lib/Driver/ToolChains/CloudABI.cpp2
-rw-r--r--lib/Driver/ToolChains/CommonArgs.cpp356
-rw-r--r--lib/Driver/ToolChains/CommonArgs.h16
-rw-r--r--lib/Driver/ToolChains/CrossWindows.cpp4
-rw-r--r--lib/Driver/ToolChains/Cuda.cpp12
-rw-r--r--lib/Driver/ToolChains/Darwin.cpp41
-rw-r--r--lib/Driver/ToolChains/DragonFly.cpp4
-rw-r--r--lib/Driver/ToolChains/FreeBSD.cpp24
-rw-r--r--lib/Driver/ToolChains/FreeBSD.h2
-rw-r--r--lib/Driver/ToolChains/Fuchsia.cpp4
-rw-r--r--lib/Driver/ToolChains/Fuchsia.h4
-rw-r--r--lib/Driver/ToolChains/Gnu.cpp23
-rw-r--r--lib/Driver/ToolChains/HIP.cpp64
-rw-r--r--lib/Driver/ToolChains/HIP.h3
-rw-r--r--lib/Driver/ToolChains/Hexagon.cpp4
-rw-r--r--lib/Driver/ToolChains/InterfaceStubs.cpp37
-rw-r--r--lib/Driver/ToolChains/InterfaceStubs.h36
-rw-r--r--lib/Driver/ToolChains/Linux.cpp15
-rw-r--r--lib/Driver/ToolChains/MSP430.cpp2
-rw-r--r--lib/Driver/ToolChains/MSVC.cpp17
-rw-r--r--lib/Driver/ToolChains/MSVC.h14
-rw-r--r--lib/Driver/ToolChains/MinGW.cpp4
-rw-r--r--lib/Driver/ToolChains/MinGW.h3
-rw-r--r--lib/Driver/ToolChains/Minix.cpp4
-rw-r--r--lib/Driver/ToolChains/Myriad.cpp6
-rw-r--r--lib/Driver/ToolChains/NaCl.cpp2
-rw-r--r--lib/Driver/ToolChains/NetBSD.cpp25
-rw-r--r--lib/Driver/ToolChains/OpenBSD.cpp4
-rw-r--r--lib/Driver/ToolChains/PPCLinux.cpp5
-rw-r--r--lib/Driver/ToolChains/PS4CPU.cpp6
-rw-r--r--lib/Driver/ToolChains/RISCVToolchain.cpp9
-rw-r--r--lib/Driver/ToolChains/RISCVToolchain.h1
-rw-r--r--lib/Driver/ToolChains/Solaris.cpp30
-rw-r--r--lib/Driver/ToolChains/WebAssembly.cpp31
-rw-r--r--lib/Driver/ToolChains/XCore.cpp4
-rw-r--r--lib/Driver/Types.cpp131
-rw-r--r--lib/Driver/XRayArgs.cpp2
-rw-r--r--lib/Format/BreakableToken.cpp11
-rw-r--r--lib/Format/ContinuationIndenter.cpp23
-rw-r--r--lib/Format/Encoding.h3
-rw-r--r--lib/Format/Format.cpp246
-rw-r--r--lib/Format/FormatToken.h13
-rw-r--r--lib/Format/FormatTokenLexer.cpp20
-rw-r--r--lib/Format/FormatTokenLexer.h1
-rw-r--r--lib/Format/NamespaceEndCommentsFixer.cpp16
-rw-r--r--lib/Format/TokenAnnotator.cpp132
-rw-r--r--lib/Format/TokenAnnotator.h3
-rw-r--r--lib/Format/UnwrappedLineFormatter.cpp63
-rw-r--r--lib/Format/UnwrappedLineParser.cpp41
-rw-r--r--lib/Format/UnwrappedLineParser.h2
-rw-r--r--lib/Format/WhitespaceManager.cpp41
-rw-r--r--lib/Frontend/ASTConsumers.cpp8
-rw-r--r--lib/Frontend/ASTUnit.cpp74
-rw-r--r--lib/Frontend/ChainedIncludesSource.cpp2
-rw-r--r--lib/Frontend/CompilerInstance.cpp117
-rw-r--r--lib/Frontend/CompilerInvocation.cpp376
-rw-r--r--lib/Frontend/CreateInvocationFromCommandLine.cpp16
-rw-r--r--lib/Frontend/DependencyFile.cpp25
-rw-r--r--lib/Frontend/DependencyGraph.cpp4
-rw-r--r--lib/Frontend/FrontendAction.cpp29
-rw-r--r--lib/Frontend/FrontendActions.cpp61
-rw-r--r--lib/Frontend/FrontendOptions.cpp37
-rw-r--r--lib/Frontend/HeaderIncludeGen.cpp5
-rw-r--r--lib/Frontend/InitHeaderSearch.cpp19
-rw-r--r--lib/Frontend/InitPreprocessor.cpp79
-rw-r--r--lib/Frontend/InterfaceStubFunctionsConsumer.cpp106
-rw-r--r--lib/Frontend/ModuleDependencyCollector.cpp8
-rw-r--r--lib/Frontend/MultiplexConsumer.cpp4
-rw-r--r--lib/Frontend/PrecompiledPreamble.cpp24
-rw-r--r--lib/Frontend/PrintPreprocessedOutput.cpp2
-rw-r--r--lib/Frontend/Rewrite/FixItRewriter.cpp2
-rw-r--r--lib/Frontend/Rewrite/FrontendActions.cpp15
-rw-r--r--lib/Frontend/Rewrite/HTMLPrint.cpp2
-rw-r--r--lib/Frontend/Rewrite/InclusionRewriter.cpp180
-rw-r--r--lib/Frontend/Rewrite/RewriteModernObjC.cpp45
-rw-r--r--lib/Frontend/Rewrite/RewriteObjC.cpp23
-rw-r--r--lib/Frontend/SerializedDiagnosticPrinter.cpp8
-rw-r--r--lib/Frontend/TextDiagnostic.cpp11
-rw-r--r--lib/Frontend/VerifyDiagnosticConsumer.cpp11
-rw-r--r--lib/FrontendTool/ExecuteCompilerInvocation.cpp99
-rw-r--r--lib/Headers/__clang_cuda_intrinsics.h10
-rw-r--r--lib/Headers/altivec.h85
-rw-r--r--lib/Headers/arm_acle.h24
-rw-r--r--lib/Headers/avx512fintrin.h27
-rw-r--r--lib/Headers/bmiintrin.h175
-rw-r--r--lib/Headers/cpuid.h4
-rw-r--r--lib/Headers/emmintrin.h6
-rw-r--r--lib/Headers/ia32intrin.h68
-rw-r--r--lib/Headers/immintrin.h3
-rw-r--r--lib/Headers/opencl-c-base.h19
-rw-r--r--lib/Headers/opencl-c.h212
-rw-r--r--lib/Headers/ppc_wrappers/emmintrin.h6
-rw-r--r--lib/Headers/ppc_wrappers/mm_malloc.h6
-rw-r--r--lib/Headers/ppc_wrappers/mmintrin.h7
-rw-r--r--lib/Headers/ppc_wrappers/pmmintrin.h150
-rw-r--r--lib/Headers/ppc_wrappers/smmintrin.h85
-rw-r--r--lib/Headers/ppc_wrappers/tmmintrin.h495
-rw-r--r--lib/Headers/ppc_wrappers/xmmintrin.h6
-rw-r--r--lib/Index/CodegenNameGenerator.cpp36
-rw-r--r--lib/Index/IndexSymbol.cpp2
-rw-r--r--lib/Index/IndexingAction.cpp178
-rw-r--r--lib/Index/USRGeneration.cpp3
-rw-r--r--lib/Lex/DependencyDirectivesSourceMinimizer.cpp250
-rw-r--r--lib/Lex/HeaderMap.cpp8
-rw-r--r--lib/Lex/HeaderSearch.cpp348
-rw-r--r--lib/Lex/Lexer.cpp9
-rw-r--r--lib/Lex/MacroArgs.cpp20
-rw-r--r--lib/Lex/ModuleMap.cpp73
-rw-r--r--lib/Lex/PPDirectives.cpp391
-rw-r--r--lib/Lex/PPLexerChange.cpp25
-rw-r--r--lib/Lex/PPMacroExpansion.cpp52
-rw-r--r--lib/Lex/Pragma.cpp85
-rw-r--r--lib/Lex/Preprocessor.cpp13
-rw-r--r--lib/Lex/TokenLexer.cpp16
-rw-r--r--lib/Lex/UnicodeCharSets.h2
-rw-r--r--lib/Parse/ParseCXXInlineMethods.cpp2
-rw-r--r--lib/Parse/ParseDecl.cpp130
-rw-r--r--lib/Parse/ParseDeclCXX.cpp16
-rw-r--r--lib/Parse/ParseExpr.cpp39
-rw-r--r--lib/Parse/ParseExprCXX.cpp77
-rw-r--r--lib/Parse/ParseInit.cpp27
-rw-r--r--lib/Parse/ParseObjc.cpp4
-rw-r--r--lib/Parse/ParseOpenMP.cpp478
-rw-r--r--lib/Parse/ParsePragma.cpp136
-rw-r--r--lib/Parse/ParseStmt.cpp26
-rw-r--r--lib/Parse/ParseTemplate.cpp8
-rw-r--r--lib/Parse/ParseTentative.cpp7
-rw-r--r--lib/Parse/Parser.cpp47
-rw-r--r--lib/Rewrite/Rewriter.cpp11
-rw-r--r--lib/Sema/AnalysisBasedWarnings.cpp82
-rw-r--r--lib/Sema/DeclSpec.cpp15
-rw-r--r--lib/Sema/OpenCLBuiltins.td744
-rw-r--r--lib/Sema/ParsedAttr.cpp65
-rw-r--r--lib/Sema/Sema.cpp91
-rw-r--r--lib/Sema/SemaAccess.cpp6
-rw-r--r--lib/Sema/SemaAttr.cpp135
-rw-r--r--lib/Sema/SemaCUDA.cpp93
-rw-r--r--lib/Sema/SemaCXXScopeSpec.cpp2
-rw-r--r--lib/Sema/SemaCast.cpp17
-rw-r--r--lib/Sema/SemaChecking.cpp612
-rw-r--r--lib/Sema/SemaCodeComplete.cpp3
-rw-r--r--lib/Sema/SemaConcept.cpp125
-rw-r--r--lib/Sema/SemaCoroutine.cpp2
-rw-r--r--lib/Sema/SemaDecl.cpp884
-rw-r--r--lib/Sema/SemaDeclAttr.cpp1101
-rw-r--r--lib/Sema/SemaDeclCXX.cpp577
-rw-r--r--lib/Sema/SemaDeclObjC.cpp14
-rw-r--r--lib/Sema/SemaExceptionSpec.cpp23
-rw-r--r--lib/Sema/SemaExpr.cpp991
-rw-r--r--lib/Sema/SemaExprCXX.cpp295
-rw-r--r--lib/Sema/SemaExprMember.cpp20
-rw-r--r--lib/Sema/SemaExprObjC.cpp4
-rw-r--r--lib/Sema/SemaInit.cpp1210
-rw-r--r--lib/Sema/SemaLambda.cpp127
-rw-r--r--lib/Sema/SemaLookup.cpp853
-rw-r--r--lib/Sema/SemaModule.cpp2
-rw-r--r--lib/Sema/SemaObjCProperty.cpp14
-rw-r--r--lib/Sema/SemaOpenMP.cpp1880
-rw-r--r--lib/Sema/SemaOverload.cpp653
-rw-r--r--lib/Sema/SemaStmt.cpp151
-rw-r--r--lib/Sema/SemaStmtAsm.cpp32
-rw-r--r--lib/Sema/SemaStmtAttr.cpp31
-rw-r--r--lib/Sema/SemaTemplate.cpp274
-rw-r--r--lib/Sema/SemaTemplateDeduction.cpp47
-rw-r--r--lib/Sema/SemaTemplateInstantiate.cpp69
-rw-r--r--lib/Sema/SemaTemplateInstantiateDecl.cpp213
-rw-r--r--lib/Sema/SemaTemplateVariadic.cpp74
-rw-r--r--lib/Sema/SemaType.cpp214
-rw-r--r--lib/Sema/TreeTransform.h220
-rw-r--r--lib/Sema/TypeLocBuilder.cpp2
-rw-r--r--lib/Sema/TypeLocBuilder.h12
-rw-r--r--lib/Serialization/ASTCommon.cpp5
-rw-r--r--lib/Serialization/ASTReader.cpp223
-rw-r--r--lib/Serialization/ASTReaderDecl.cpp171
-rw-r--r--lib/Serialization/ASTReaderStmt.cpp86
-rw-r--r--lib/Serialization/ASTWriter.cpp174
-rw-r--r--lib/Serialization/ASTWriterDecl.cpp15
-rw-r--r--lib/Serialization/ASTWriterStmt.cpp50
-rw-r--r--lib/Serialization/GlobalModuleIndex.cpp49
-rw-r--r--lib/Serialization/ModuleManager.cpp35
-rw-r--r--lib/Serialization/PCHContainerOperations.cpp6
-rw-r--r--lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp3
-rw-r--r--lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp4
-rw-r--r--lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp11
-rw-r--r--lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp5
-rw-r--r--lib/StaticAnalyzer/Checkers/BoolAssignmentChecker.cpp4
-rw-r--r--lib/StaticAnalyzer/Checkers/CStringChecker.cpp72
-rw-r--r--lib/StaticAnalyzer/Checkers/CStringSyntaxChecker.cpp27
-rw-r--r--lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp23
-rw-r--r--lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp3
-rw-r--r--lib/StaticAnalyzer/Checkers/CastValueChecker.cpp445
-rw-r--r--lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp15
-rw-r--r--lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp2
-rw-r--r--lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp43
-rw-r--r--lib/StaticAnalyzer/Checkers/ChrootChecker.cpp2
-rw-r--r--lib/StaticAnalyzer/Checkers/CloneChecker.cpp6
-rw-r--r--lib/StaticAnalyzer/Checkers/ConversionChecker.cpp2
-rw-r--r--lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp106
-rw-r--r--lib/StaticAnalyzer/Checkers/DebugCheckers.cpp3
-rw-r--r--lib/StaticAnalyzer/Checkers/DeleteWithNonVirtualDtorChecker.cpp21
-rw-r--r--lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp6
-rw-r--r--lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp4
-rw-r--r--lib/StaticAnalyzer/Checkers/DynamicTypeChecker.cpp25
-rw-r--r--lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp54
-rw-r--r--lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp18
-rw-r--r--lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp4
-rw-r--r--lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp3
-rw-r--r--lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp264
-rw-r--r--lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp19
-rw-r--r--lib/StaticAnalyzer/Checkers/IteratorChecker.cpp51
-rw-r--r--lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp14
-rw-r--r--lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp42
-rw-r--r--lib/StaticAnalyzer/Checkers/MIGChecker.cpp5
-rw-r--r--lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.cpp24
-rw-r--r--lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.h6
-rw-r--r--lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp43
-rw-r--r--lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp3
-rw-r--r--lib/StaticAnalyzer/Checkers/MallocChecker.cpp1287
-rw-r--r--lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp2
-rw-r--r--lib/StaticAnalyzer/Checkers/MmapWriteExecChecker.cpp2
-rw-r--r--lib/StaticAnalyzer/Checkers/MoveChecker.cpp23
-rw-r--r--lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp8
-rw-r--r--lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp3
-rw-r--r--lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp31
-rw-r--r--lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp36
-rw-r--r--lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp8
-rw-r--r--lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp7
-rw-r--r--lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp2
-rw-r--r--lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp2
-rw-r--r--lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp20
-rw-r--r--lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp6
-rw-r--r--lib/StaticAnalyzer/Checkers/PaddingChecker.cpp5
-rw-r--r--lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp18
-rw-r--r--lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp3
-rw-r--r--lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp14
-rw-r--r--lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp10
-rw-r--r--lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h2
-rw-r--r--lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp61
-rw-r--r--lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.h18
-rw-r--r--lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp3
-rw-r--r--lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp3
-rw-r--r--lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp9
-rw-r--r--lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp22
-rw-r--r--lib/StaticAnalyzer/Checkers/StreamChecker.cpp8
-rw-r--r--lib/StaticAnalyzer/Checkers/Taint.cpp8
-rw-r--r--lib/StaticAnalyzer/Checkers/Taint.h6
-rw-r--r--lib/StaticAnalyzer/Checkers/TaintTesterChecker.cpp2
-rw-r--r--lib/StaticAnalyzer/Checkers/TestAfterDivZeroChecker.cpp16
-rw-r--r--lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp3
-rw-r--r--lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp7
-rw-r--r--lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp2
-rw-r--r--lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp2
-rw-r--r--lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp4
-rw-r--r--lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp6
-rw-r--r--lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp11
-rw-r--r--lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp8
-rw-r--r--lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp2
-rw-r--r--lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp4
-rw-r--r--lib/StaticAnalyzer/Checkers/ValistChecker.cpp44
-rw-r--r--lib/StaticAnalyzer/Checkers/VforkChecker.cpp2
-rw-r--r--lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp203
-rwxr-xr-xlib/StaticAnalyzer/Checkers/Yaml.h59
-rw-r--r--lib/StaticAnalyzer/Core/AnalysisManager.cpp4
-rw-r--r--lib/StaticAnalyzer/Core/AnalyzerOptions.cpp19
-rw-r--r--lib/StaticAnalyzer/Core/BugReporter.cpp1858
-rw-r--r--lib/StaticAnalyzer/Core/BugReporterVisitors.cpp660
-rw-r--r--lib/StaticAnalyzer/Core/CallEvent.cpp24
-rw-r--r--lib/StaticAnalyzer/Core/Checker.cpp8
-rw-r--r--lib/StaticAnalyzer/Core/CheckerHelpers.cpp2
-rw-r--r--lib/StaticAnalyzer/Core/CheckerManager.cpp2
-rw-r--r--lib/StaticAnalyzer/Core/CommonBugCategories.cpp1
-rw-r--r--lib/StaticAnalyzer/Core/DynamicType.cpp229
-rw-r--r--lib/StaticAnalyzer/Core/DynamicTypeMap.cpp97
-rw-r--r--lib/StaticAnalyzer/Core/Environment.cpp1
-rw-r--r--lib/StaticAnalyzer/Core/ExplodedGraph.cpp120
-rw-r--r--lib/StaticAnalyzer/Core/ExprEngine.cpp91
-rw-r--r--lib/StaticAnalyzer/Core/ExprEngineC.cpp3
-rw-r--r--lib/StaticAnalyzer/Core/ExprEngineCXX.cpp36
-rw-r--r--lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp5
-rw-r--r--lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp50
-rw-r--r--lib/StaticAnalyzer/Core/LoopUnrolling.cpp4
-rw-r--r--lib/StaticAnalyzer/Core/MemRegion.cpp5
-rw-r--r--lib/StaticAnalyzer/Core/PlistDiagnostics.cpp157
-rw-r--r--lib/StaticAnalyzer/Core/ProgramState.cpp2
-rw-r--r--lib/StaticAnalyzer/Core/RangeConstraintManager.cpp2
-rw-r--r--lib/StaticAnalyzer/Core/RegionStore.cpp89
-rw-r--r--lib/StaticAnalyzer/Core/SMTConstraintManager.cpp2
-rw-r--r--lib/StaticAnalyzer/Core/SarifDiagnostics.cpp123
-rw-r--r--lib/StaticAnalyzer/Core/Store.cpp2
-rw-r--r--lib/StaticAnalyzer/Core/WorkList.cpp12
-rw-r--r--lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp106
-rw-r--r--lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp23
-rw-r--r--lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp24
-rw-r--r--lib/StaticAnalyzer/Frontend/FrontendActions.cpp2
-rw-r--r--lib/StaticAnalyzer/Frontend/ModelInjector.cpp3
-rw-r--r--lib/Tooling/ASTDiff/ASTDiff.cpp16
-rw-r--r--lib/Tooling/AllTUsExecution.cpp5
-rw-r--r--lib/Tooling/ArgumentsAdjusters.cpp16
-rw-r--r--lib/Tooling/CommonOptionsParser.cpp2
-rw-r--r--lib/Tooling/CompilationDatabase.cpp4
-rw-r--r--lib/Tooling/Core/Replacement.cpp11
-rw-r--r--lib/Tooling/DependencyScanning/DependencyScanningFilesystem.cpp234
-rw-r--r--lib/Tooling/DependencyScanning/DependencyScanningService.cpp19
-rw-r--r--lib/Tooling/DependencyScanning/DependencyScanningTool.cpp71
-rw-r--r--lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp140
-rw-r--r--lib/Tooling/GuessTargetAndModeCompilationDatabase.cpp2
-rw-r--r--lib/Tooling/Inclusions/HeaderIncludes.cpp14
-rw-r--r--lib/Tooling/Inclusions/IncludeStyle.cpp1
-rw-r--r--lib/Tooling/InterpolatingCompilationDatabase.cpp33
-rw-r--r--lib/Tooling/Refactoring.cpp5
-rw-r--r--lib/Tooling/Refactoring/ASTSelectionRequirements.cpp2
-rw-r--r--lib/Tooling/Refactoring/Extract/Extract.cpp2
-rw-r--r--lib/Tooling/Refactoring/Extract/SourceExtraction.cpp8
-rw-r--r--lib/Tooling/Refactoring/Extract/SourceExtraction.h51
-rw-r--r--lib/Tooling/Refactoring/RefactoringActions.cpp4
-rw-r--r--lib/Tooling/Refactoring/Rename/RenamingAction.cpp4
-rw-r--r--lib/Tooling/Refactoring/Rename/SymbolOccurrences.cpp2
-rw-r--r--lib/Tooling/Refactoring/Rename/USRFindingAction.cpp6
-rw-r--r--lib/Tooling/Refactoring/SourceCode.cpp31
-rw-r--r--lib/Tooling/Refactoring/Stencil.cpp175
-rw-r--r--lib/Tooling/Refactoring/Transformer.cpp263
-rw-r--r--lib/Tooling/RefactoringCallbacks.cpp2
-rw-r--r--lib/Tooling/StandaloneExecution.cpp2
-rw-r--r--lib/Tooling/Syntax/BuildTree.cpp12
-rw-r--r--lib/Tooling/Syntax/Tokens.cpp17
-rw-r--r--lib/Tooling/Tooling.cpp84
-rw-r--r--lib/Tooling/Transformer/CMakeLists.txt18
-rw-r--r--lib/Tooling/Transformer/RangeSelector.cpp (renamed from lib/Tooling/Refactoring/RangeSelector.cpp)56
-rw-r--r--lib/Tooling/Transformer/RewriteRule.cpp178
-rw-r--r--lib/Tooling/Transformer/SourceCode.cpp65
-rw-r--r--lib/Tooling/Transformer/SourceCodeBuilders.cpp160
-rw-r--r--lib/Tooling/Transformer/Stencil.cpp318
-rw-r--r--lib/Tooling/Transformer/Transformer.cpp72
547 files changed, 37204 insertions, 14098 deletions
diff --git a/lib/ARCMigrate/ARCMT.cpp b/lib/ARCMigrate/ARCMT.cpp
index 568e06f21fba..a9018c1c4bdf 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<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
StringRef InFile) override {
CI.getPreprocessor().addPPCallbacks(
- llvm::make_unique<ARCMTMacroTrackerPPCallbacks>(ARCMTMacroLocs));
- return llvm::make_unique<ASTConsumer>();
+ std::make_unique<ARCMTMacroTrackerPPCallbacks>(ARCMTMacroLocs));
+ return std::make_unique<ASTConsumer>();
}
};
diff --git a/lib/ARCMigrate/FileRemapper.cpp b/lib/ARCMigrate/FileRemapper.cpp
index 1a4862d09aa6..a031fe22ac13 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<const FileEntry *> 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<const FileEntry *> 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<const FileEntry *, const FileEntry *>::iterator
diff --git a/lib/ARCMigrate/ObjCMT.cpp b/lib/ARCMigrate/ObjCMT.cpp
index 7126a0873ea0..4abb04fef5b8 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<PPCallbacks>(PPRec));
std::vector<std::unique_ptr<ASTConsumer>> Consumers;
Consumers.push_back(WrapperFrontendAction::CreateASTConsumer(CI, InFile));
- Consumers.push_back(llvm::make_unique<ObjCMigrateASTConsumer>(
+ Consumers.push_back(std::make_unique<ObjCMigrateASTConsumer>(
MigrateDir, ObjCMigAction, Remapper, CompInst->getFileManager(), PPRec,
CompInst->getPreprocessor(), false, None));
- return llvm::make_unique<MultiplexConsumer>(std::move(Consumers));
+ return std::make_unique<MultiplexConsumer>(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<PPCallbacks>(PPRec));
std::vector<std::string> WhiteList =
getWhiteListFilenames(CI.getFrontendOpts().ObjCMTWhiteListPath);
- return llvm::make_unique<ObjCMigrateASTConsumer>(
+ return std::make_unique<ObjCMigrateASTConsumer>(
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 6d7fcb053b48..d01563b2974d 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 1993bba9bd1a..50f8d05dacb4 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<TypeInfoLValue>() ? 0 : Local.CallIndex;
+ return (is<TypeInfoLValue>() || is<DynamicAllocLValue>()) ? 0
+ : Local.CallIndex;
}
unsigned APValue::LValueBase::getVersion() const {
- return is<TypeInfoLValue>() ? 0 : Local.Version;
+ return (is<TypeInfoLValue>() || is<DynamicAllocLValue>()) ? 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<DynamicAllocLValue>() && "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<clang::APValue::LValueBase>::getTombstoneKey() {
namespace clang {
llvm::hash_code hash_value(const APValue::LValueBase &Base) {
- if (Base.is<TypeInfoLValue>())
+ if (Base.is<TypeInfoLValue>() || Base.is<DynamicAllocLValue>())
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<VectorType>()->getElementType();
+ QualType ElemTy = Ty->castAs<VectorType>()->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<const ValueDecl*>())
Out << *VD;
else if (TypeInfoLValue TI = Base.dyn_cast<TypeInfoLValue>()) {
TI.print(Out, Ctx.getPrintingPolicy());
+ } else if (DynamicAllocLValue DA = Base.dyn_cast<DynamicAllocLValue>()) {
+ Out << "{*new "
+ << Base.getDynamicAllocType().stream(Ctx.getPrintingPolicy()) << "#"
+ << DA.getIndex() << "}";
} else {
assert(Base.get<const Expr *>() != 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<TypeInfoLValue>()) {
TI.print(Out, Ctx.getPrintingPolicy());
ElemTy = Base.getTypeInfoType();
+ } else if (DynamicAllocLValue DA = Base.dyn_cast<DynamicAllocLValue>()) {
+ Out << "{*new "
+ << Base.getDynamicAllocType().stream(Ctx.getPrintingPolicy()) << "#"
+ << DA.getIndex() << "}";
+ ElemTy = Base.getDynamicAllocType();
} else {
const Expr *E = Base.get<const Expr*>();
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<RecordType>()->getDecl();
+ const RecordDecl *RD = Ty->castAs<RecordType>()->getDecl();
bool First = true;
if (unsigned N = getStructNumBases()) {
const CXXRecordDecl *CD = cast<CXXRecordDecl>(RD);
diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp
index 0d69eb90abaf..cda51ec755a8 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<FunctionDecl>(D)) {
if (FD->getTemplateSpecializationKind() == TSK_ImplicitInstantiation)
- return nullptr;
+ return {};
}
if (const auto *VD = dyn_cast<VarDecl>(D)) {
if (VD->isStaticDataMember() &&
VD->getTemplateSpecializationKind() == TSK_ImplicitInstantiation)
- return nullptr;
+ return {};
}
if (const auto *CRD = dyn_cast<CXXRecordDecl>(D)) {
if (CRD->getTemplateSpecializationKind() == TSK_ImplicitInstantiation)
- return nullptr;
+ return {};
}
if (const auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(D)) {
TemplateSpecializationKind TSK = CTSD->getSpecializationKind();
if (TSK == TSK_ImplicitInstantiation ||
TSK == TSK_Undeclared)
- return nullptr;
+ return {};
}
if (const auto *ED = dyn_cast<EnumDecl>(D)) {
if (ED->getTemplateSpecializationKind() == TSK_ImplicitInstantiation)
- return nullptr;
+ return {};
}
if (const auto *TD = dyn_cast<TagDecl>(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<ParmVarDecl>(D))
- return nullptr;
+ return {};
// TODO: we could look up template parameter documentation in the template
// documentation.
if (isa<TemplateTypeParmDecl>(D) ||
isa<NonTypeTemplateParmDecl>(D) ||
isa<TemplateTemplateParmDecl>(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<ObjCMethodDecl>(D) || isa<ObjCContainerDecl>(D) ||
isa<ObjCPropertyDecl>(D) ||
isa<RedeclarableTemplateDecl>(D) ||
isa<ClassTemplateSpecializationDecl>(D))
- DeclLoc = D->getBeginLoc();
+ return D->getBeginLoc();
else {
- DeclLoc = D->getLocation();
+ const SourceLocation DeclLoc = D->getLocation();
if (DeclLoc.isMacroID()) {
if (isa<TypedefDecl>(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<TagDecl>(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<unsigned, RawComment *> &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<RawComment *> RawComments = Comments.getComments();
- assert(std::is_sorted(RawComments.begin(), RawComments.end(),
- BeforeThanCompare<RawComment>(SourceMgr)));
-#endif
-
- CommentsLoaded = true;
- }
-
- ArrayRef<RawComment *> 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<RawComment *>::iterator Comment;
- {
- // When searching for comments during parsing, the comment we are looking
- // for is usually among the last two comments we parsed -- check them
- // first.
- RawComment CommentAtDeclLoc(
- SourceMgr, SourceRange(DeclLoc), LangOpts.CommentOpts, false);
- BeforeThanCompare<RawComment> Compare(SourceMgr);
- ArrayRef<RawComment *>::iterator MaybeBeforeDecl = RawComments.end() - 1;
- bool Found = Compare(*MaybeBeforeDecl, &CommentAtDeclLoc);
- if (!Found && RawComments.size() >= 2) {
- MaybeBeforeDecl--;
- Found = Compare(*MaybeBeforeDecl, &CommentAtDeclLoc);
- }
-
- if (Found) {
- Comment = MaybeBeforeDecl + 1;
- assert(Comment ==
- 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<FileID, unsigned> DeclLocDecomp = SourceMgr.getDecomposedLoc(DeclLoc);
+ const std::pair<FileID, unsigned> 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<FieldDecl>(D) || isa<EnumConstantDecl>(D) || isa<VarDecl>(D) ||
- isa<ObjCMethodDecl>(D) || isa<ObjCPropertyDecl>(D))) {
- std::pair<FileID, unsigned> CommentBeginDecomp
- = SourceMgr.getDecomposedLoc((*Comment)->getSourceRange().getBegin());
- // Check that Doxygen trailing comment comes after the declaration, starts
- // on the same line and in the same file as the declaration.
- if (DeclLocDecomp.first == CommentBeginDecomp.first &&
- SourceMgr.getLineNumber(DeclLocDecomp.first, DeclLocDecomp.second)
- == SourceMgr.getLineNumber(CommentBeginDecomp.first,
- CommentBeginDecomp.second)) {
- (**Comment).setAttached();
- return *Comment;
+ if (OffsetCommentBehindDecl != CommentsInTheFile.end()) {
+ RawComment *CommentBehindDecl = OffsetCommentBehindDecl->second;
+ if ((CommentBehindDecl->isDocumentation() ||
+ LangOpts.CommentOpts.ParseAllComments) &&
+ CommentBehindDecl->isTrailingComment() &&
+ (isa<FieldDecl>(D) || isa<EnumConstantDecl>(D) || isa<VarDecl>(D) ||
+ isa<ObjCMethodDecl>(D) || isa<ObjCPropertyDecl>(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<FileID, unsigned> CommentEndDecomp
- = SourceMgr.getDecomposedLoc((*Comment)->getSourceRange().getEnd());
-
- // If the comment and the declaration aren't in the same file, then they
- // aren't related.
- if (DeclLocDecomp.first != CommentEndDecomp.first)
- return 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<FunctionDecl>(D)) {
+static const Decl &adjustDeclToTemplate(const Decl &D) {
+ if (const auto *FD = dyn_cast<FunctionDecl>(&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<VarDecl>(D)) {
+ if (const auto *VD = dyn_cast<VarDecl>(&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<CXXRecordDecl>(D)) {
+ if (const auto *CRD = dyn_cast<CXXRecordDecl>(&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<ClassTemplateDecl *,
ClassTemplatePartialSpecializationDecl *>
PU = CTSD->getSpecializedTemplateOrPartial();
- return PU.is<ClassTemplateDecl*>() ?
- static_cast<const Decl*>(PU.get<ClassTemplateDecl *>()) :
- static_cast<const Decl*>(
- PU.get<ClassTemplatePartialSpecializationDecl *>());
+ return PU.is<ClassTemplateDecl *>()
+ ? *static_cast<const Decl *>(PU.get<ClassTemplateDecl *>())
+ : *static_cast<const Decl *>(
+ PU.get<ClassTemplatePartialSpecializationDecl *>());
}
// 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<EnumDecl>(D)) {
+ if (const auto *ED = dyn_cast<EnumDecl>(&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<const Decl *, RawCommentAndCacheFlags>::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<const Decl *, RawCommentAndCacheFlags>::iterator Pos =
- RedeclComments.find(I);
- if (Pos != RedeclComments.end()) {
- const RawCommentAndCacheFlags &Raw = Pos->second;
- if (Raw.getKind() != RawCommentAndCacheFlags::NoCommentInDecl) {
- RC = Raw.getRaw();
- 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<Decl *> 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<const Decl *, comments::FullComment *>::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<TemplateTemplateParmDecl>(*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<BuiltinType>();
- assert(BT && "Not a floating point type!");
- switch (BT->getKind()) {
- default: llvm_unreachable("Not a floating point type!");
+ switch (T->castAs<BuiltinType>()->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<Class##Type>(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<int64_t> Size = structHasUniqueObjectRepresentations(
- Context, Base.getType()->getAs<RecordType>()->getDecl());
+ Context, Base.getType()->castAs<RecordType>()->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<RecordType>()->getDecl();
+ const RecordDecl *Record = Ty->castAs<RecordType>()->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<FunctionProtoType>();
+ const auto *Proto = Orig->castAs<FunctionProtoType>();
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<const Expr *>(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<ObjCProtocolDecl *> protocols,
- QualType Canonical) const {
+ ArrayRef<ObjCProtocolDecl *> 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<ConstantArrayType>(AT)) {
return getConstantArrayType(unqualElementType, CAT->getSize(),
- CAT->getSizeModifier(), 0);
+ CAT->getSizeExpr(), CAT->getSizeModifier(), 0);
}
if (const auto *IAT = dyn_cast<IncompleteArrayType>(AT)) {
@@ -5487,6 +5574,7 @@ const ArrayType *ASTContext::getAsArrayType(QualType T) const {
if (const auto *CAT = dyn_cast<ConstantArrayType>(ATy))
return cast<ArrayType>(getConstantArrayType(NewEltTy, CAT->getSize(),
+ CAT->getSizeExpr(),
CAT->getSizeModifier(),
CAT->getIndexTypeCVRQualifiers()));
if (const auto *IAT = dyn_cast<IncompleteArrayType>(ATy))
@@ -5599,8 +5687,7 @@ static FloatingRank getFloatingRank(QualType T) {
if (const auto *CT = T->getAs<ComplexType>())
return getFloatingRank(CT->getElementType());
- assert(T->getAs<BuiltinType>() && "getFloatingRank(): not a floating type");
- switch (T->getAs<BuiltinType>()->getKind()) {
+ switch (T->castAs<BuiltinType>()->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<TypedefType>();
- assert(TD && "Invalid CFConstantStringType");
+ const auto *TD = T->castAs<TypedefType>();
CFConstantStringTypeDecl = cast<TypedefDecl>(TD->getDecl());
const auto *TagType =
- CFConstantStringTypeDecl->getUnderlyingType()->getAs<RecordType>();
- assert(TagType && "Invalid CFConstantStringType");
+ CFConstantStringTypeDecl->getUnderlyingType()->castAs<RecordType>();
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<BlockPointerType>()->getPointeeType();
+ Expr->getType()->castAs<BlockPointerType>()->getPointeeType();
+ QualType BlockReturnTy = BlockTy->castAs<FunctionType>()->getReturnType();
// Encode result type.
if (getLangOpts().EncodeExtendedBlockSig)
- getObjCEncodingForMethodParameter(
- Decl::OBJC_TQ_None, BlockTy->getAs<FunctionType>()->getReturnType(), S,
- true /*Extended*/);
+ getObjCEncodingForMethodParameter(Decl::OBJC_TQ_None, BlockReturnTy, S,
+ true /*Extended*/);
else
- getObjCEncodingForType(BlockTy->getAs<FunctionType>()->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<BuiltinType>();
- 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<BuiltinType>();
- 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<BuiltinType>(CT))
- S += getObjCEncodingForPrimitiveKind(this, BT->getKind());
+ S += getObjCEncodingForPrimitiveType(this, BT);
else
S += ObjCEncodingForEnumType(this, cast<EnumType>(CT));
return;
@@ -6760,8 +6857,8 @@ void ASTContext::getObjCEncodingForTypeImpl(QualType T, std::string &S,
}
} else if (Options.IsOutermostType()) {
QualType P = PointeeTy;
- while (P->getAs<PointerType>())
- P = P->getAs<PointerType>()->getPointeeType();
+ while (auto PT = P->getAs<PointerType>())
+ 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<PointerType>()->getPointeeType());
+ return getObjCGCAttrKind(Ty->castAs<PointerType>()->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<VectorType>();
- const auto *Second = SecondVec->getAs<VectorType>();
+ const auto *First = FirstVec->castAs<VectorType>();
+ const auto *Second = SecondVec->castAs<VectorType>();
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<AttributedType>(Ty)) {
+ if (Attr->getAttrKind() == attr::ObjCOwnership)
+ return true;
+
+ Ty = Attr->getModifiedType();
+
+ // X *__strong (...)
+ } else if (const ParenType *Paren = dyn_cast<ParenType>(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<pr,...> and
/// Class<pr1, ...>.
-bool ASTContext::ObjCQualifiedClassTypesAreCompatible(QualType lhs,
- QualType rhs) {
- const auto *lhsQID = lhs->getAs<ObjCObjectPointerType>();
- const auto *rhsOPT = rhs->getAs<ObjCObjectPointerType>();
- 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<P..> 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<P..> and an 'id' in all cases.
+ if (lhs->isObjCIdType() || rhs->isObjCIdType())
return true;
- if (const ObjCObjectPointerType *lhsQID = lhs->getAsObjCQualifiedIdType()) {
- const auto *rhsOPT = rhs->getAs<ObjCObjectPointerType>();
-
- if (!rhsOPT) return false;
+ // Don't allow id<P..> to convert to Class or Class<P..> 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<P> 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<P> 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<P>*",
// 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<P> 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<x>");
+ assert(rhs->isObjCQualifiedIdType() && "One of the LHS/RHS should be id<x>");
- 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<P> 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<ObjCProtocolDecl *, 8> 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<P> 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<P1> to Class<P2>.
if (LHS->isObjCQualifiedClass() && RHS->isObjCQualifiedClass()) {
- return finish(ObjCQualifiedClassTypesAreCompatible(QualType(LHSOPT,0),
- QualType(RHSOPT,0)));
+ return finish(ObjCQualifiedClassTypesAreCompatible(LHSOPT, RHSOPT));
+ }
+
+ // Casts from Class to Class<Foo>, 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<PointerType>()->getPointeeType();
- QualType RHSPointee = RHS->getAs<PointerType>()->getPointeeType();
+ QualType LHSPointee = LHS->castAs<PointerType>()->getPointeeType();
+ QualType RHSPointee = RHS->castAs<PointerType>()->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<BlockPointerType>()->getPointeeType();
- QualType RHSPointee = RHS->getAs<BlockPointerType>()->getPointeeType();
+ QualType LHSPointee = LHS->castAs<BlockPointerType>()->getPointeeType();
+ QualType RHSPointee = RHS->castAs<BlockPointerType>()->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<AtomicType>()->getValueType();
- QualType RHSValue = RHS->getAs<AtomicType>()->getValueType();
+ QualType LHSValue = LHS->castAs<AtomicType>()->getValueType();
+ QualType RHSValue = RHS->castAs<AtomicType>()->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<VectorType>(),
- RHSCan->getAs<VectorType>()))
+ if (areCompatVectorTypes(LHSCan->castAs<VectorType>(),
+ RHSCan->castAs<VectorType>()))
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<ObjCObjectType>();
- const auto *RHSIface = RHS->getAs<ObjCObjectType>();
- if (canAssignObjCInterfaces(LHSIface, RHSIface))
+ if (canAssignObjCInterfaces(LHS->castAs<ObjCObjectType>(),
+ RHS->castAs<ObjCObjectType>()))
return LHS;
-
return {};
}
case Type::ObjCObjectPointer:
if (OfBlockPointer) {
if (canAssignObjCInterfacesInBlockPointer(
- LHS->getAs<ObjCObjectPointerType>(),
- RHS->getAs<ObjCObjectPointerType>(),
- BlockReturnType))
+ LHS->castAs<ObjCObjectPointerType>(),
+ RHS->castAs<ObjCObjectPointerType>(), BlockReturnType))
return LHS;
return {};
}
- if (canAssignObjCInterfaces(LHS->getAs<ObjCObjectPointerType>(),
- RHS->getAs<ObjCObjectPointerType>()))
+ if (canAssignObjCInterfaces(LHS->castAs<ObjCObjectPointerType>(),
+ RHS->castAs<ObjCObjectPointerType>()))
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<FunctionType>();
+ const auto *F = LHS->castAs<FunctionType>();
if (const auto *FPT = cast<FunctionProtoType>(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<ObjCObjectPointerType>()->getPointeeType();
- QualType RHSBaseQT = RHS->getAs<ObjCObjectPointerType>()->getPointeeType();
+ QualType LHSBaseQT = LHS->castAs<ObjCObjectPointerType>()->getPointeeType();
+ QualType RHSBaseQT = RHS->castAs<ObjCObjectPointerType>()->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<EnumType>())
T = ETy->getDecl()->getIntegerType();
- const auto *BTy = T->getAs<BuiltinType>();
- assert(BTy && "Unexpected signed integer or fixed point type");
- switch (BTy->getKind()) {
+ switch (T->castAs<BuiltinType>()->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<OMPAllocateDecl>(D))
return !D->getDeclContext()->isDependentContext();
- else if (isa<OMPDeclareReductionDecl>(D))
+ else if (isa<OMPDeclareReductionDecl>(D) || isa<OMPDeclareMapperDecl>(D))
return !D->getDeclContext()->isDependentContext();
else if (isa<ImportDecl>(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<MangleNumberingContext> &MCtx =
+ ExtraMangleNumberingContexts[D];
+ if (!MCtx)
+ MCtx = createMangleNumberingContext();
+ return *MCtx;
+}
+
std::unique_ptr<MangleNumberingContext>
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<ParentMap>(*this);
+ Parents = std::make_unique<ParentMap>(*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<BuiltinType>();
- switch (BT->getKind()) {
+ switch (Ty->castAs<BuiltinType>()->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<BuiltinType>();
const TargetInfo &Target = getTargetInfo();
- switch (BT->getKind()) {
+ switch (Ty->castAs<BuiltinType>()->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<BuiltinType>();
const TargetInfo &Target = getTargetInfo();
- switch (BT->getKind()) {
+ switch (Ty->castAs<BuiltinType>()->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<BuiltinType>();
- switch (BTy->getKind()) {
+ switch (Ty->castAs<BuiltinType>()->getKind()) {
case BuiltinType::UShortAccum:
return ShortAccumTy;
case BuiltinType::UAccum:
diff --git a/lib/AST/ASTDiagnostic.cpp b/lib/AST/ASTDiagnostic.cpp
index 15df86585294..30985441031d 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 9d5dd84161de..54acca7dc62c 100644
--- a/lib/AST/ASTImporter.cpp
+++ b/lib/AST/ASTImporter.cpp
@@ -80,6 +80,7 @@ namespace clang {
using ExpectedExpr = llvm::Expected<Expr *>;
using ExpectedDecl = llvm::Expected<Decl *>;
using ExpectedSLoc = llvm::Expected<SourceLocation>;
+ using ExpectedName = llvm::Expected<DeclarationName>;
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 <typename T>
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<FunctionDecl *> FindFunctionTemplateSpecialization(
FunctionDecl *FromFD);
@@ -927,6 +932,27 @@ Expected<LambdaCapture> ASTNodeImporter::import(const LambdaCapture &From) {
EllipsisLoc);
}
+template <typename T>
+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<TagDecl>(FromDC);
Error ChildErrors = Error::success();
- llvm::SmallVector<Decl *, 8> 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<RecordDecl>(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<FieldDecl>(D) || isa<IndirectFieldDecl>(D) || isa<FriendDecl>(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>(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<NamedDecl *, 4> 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<TypedefNameDecl>(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>(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>(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>(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<NamedDecl *, 4> 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>(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<EnumConstantDecl>(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>(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 <typename T>
-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<Decl *, 2> 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>(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<CXXMethodDecl>(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<CXXDestructorDecl>(
ToFunction, D, Importer.getToContext(), cast<CXXRecordDecl>(DC),
ToInnerLocStart, NameInfo, T, TInfo, D->isInlineSpecified(),
- D->isImplicit()))
+ D->isImplicit(), D->getConstexprKind()))
return ToFunction;
CXXDestructorDecl *ToDtor = cast<CXXDestructorDecl>(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<CXXConstructorDecl>(D)) {
- if (unsigned NumInitializers = FromConstructor->getNumCtorInitializers()) {
- SmallVector<CXXCtorInitializer *, 4> 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<CXXConstructorDecl>(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<CXXConstructorDecl>(D)) {
+ if (unsigned NumInitializers = FromConstructor->getNumCtorInitializers()) {
+ SmallVector<CXXCtorInitializer *, 4> 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<CXXConstructorDecl>(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<CXXMethodDecl>(D))
- ImportOverrides(cast<CXXMethodDecl>(ToFunction), FromCXXMethod);
+ if (Error Err = ImportOverriddenMethods(cast<CXXMethodDecl>(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>(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>(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>(ImportError::NameConflict);
-
VarDecl *DTemplated = D->getTemplatedDecl();
// Import the type.
@@ -5533,17 +5580,16 @@ ASTNodeImporter::VisitFunctionTemplateDecl(FunctionTemplateDecl *D) {
continue;
if (auto *FoundTemplate = dyn_cast<FunctionTemplateDecl>(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<ParmVarDecl *> 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<CXXMethodDecl>(
(*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<ASTImporterSharedState> 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<FileID> 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<FileID> 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>(ImportError::Unknown);
@@ -8421,6 +8486,10 @@ Expected<FileID> 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<Selector> 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<DeclarationName> 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>(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 912db3c130c5..db48405055cd 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<Decl *, Decl *> 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<Decl *, Decl *> 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 ba1581bd3f6b..6b7f6ec51086 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<NestedNameSpecifier>())
NNS->print(OS, PP);
- else if (const NestedNameSpecifierLoc *NNSL = get<NestedNameSpecifierLoc>())
- NNSL->getNestedNameSpecifier()->print(OS, PP);
- else if (const QualType *QT = get<QualType>())
+ else if (const NestedNameSpecifierLoc *NNSL = get<NestedNameSpecifierLoc>()) {
+ if (const NestedNameSpecifier *NNS = NNSL->getNestedNameSpecifier())
+ NNS->print(OS, PP);
+ else
+ OS << "(empty NestedNameSpecifierLoc)";
+ } else if (const QualType *QT = get<QualType>())
QT->print(OS, PP);
else if (const TypeLoc *TL = get<TypeLoc>())
TL->getType().print(OS, PP);
diff --git a/lib/AST/CXXInheritance.cpp b/lib/AST/CXXInheritance.cpp
index ecf451b175af..a3a3794b2edd 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<NamedDecl *[]>(NumDeclsFound);
+ DeclsFound = std::make_unique<NamedDecl *[]>(NumDeclsFound);
std::copy(Decls.begin(), Decls.end(), DeclsFound.get());
}
diff --git a/lib/AST/Comment.cpp b/lib/AST/Comment.cpp
index 25339c7901e3..23dc7ba93591 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 <type_traits>
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<CLASS>::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<DeclInfo>::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 19485f6018c0..c1ea3eab075e 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<FileID, unsigned> 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 c7f8aa7e16a0..29983b0a16c3 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 067b3ae4222e..69d61dc55162 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<TypedefType>())
return false;
+ if (const auto *P = QT->getAs<PointerType>())
+ if (P->getPointeeType()->getAs<TypedefType>())
+ return false;
+ if (const auto *P = QT->getAs<BlockPointerType>())
+ if (P->getPointeeType()->getAs<TypedefType>())
+ return false;
return QT->isFunctionPointerType() || QT->isBlockPointerType();
}
diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp
index 21cf9da18a8b..80235d8496d2 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<CXXRecordDecl>(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<DecompositionDecl>(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<const DeclContext *, 8>;
ContextsTy Contexts;
@@ -1644,11 +1662,6 @@ void NamedDecl::printQualifiedName(raw_ostream &OS,
}
OS << "::";
}
-
- if (getDeclName() || isa<DecompositionDecl>(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<AlwaysDestroyAttr>()));
}
+QualType::DestructionKind
+VarDecl::needsDestruction(const ASTContext &Ctx) const {
+ if (EvaluatedStmt *Eval = Init.dyn_cast<EvaluatedStmt *>())
+ 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<GNUInlineAttr>()) {
// 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<AliasAttr>()) &&
"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 fd80e1532eb5..77a3a4c679a1 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<char *>(
::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<PointerType>()->getPointeeType();
+ Ty = Ty->castAs<PointerType>()->getPointeeType();
else if (Ty->isFunctionReferenceType())
- Ty = Ty->getAs<ReferenceType>()->getPointeeType();
+ Ty = Ty->castAs<ReferenceType>()->getPointeeType();
else if (BlocksToo && Ty->isBlockPointerType())
- Ty = Ty->getAs<BlockPointerType>()->getPointeeType();
+ Ty = Ty->castAs<BlockPointerType>()->getPointeeType();
return Ty->getAs<FunctionType>();
}
@@ -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 59710a55498f..12ec44fa0279 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<CXXRecordDecl>(BaseType->getAs<RecordType>()->getDecl());
+ cast<CXXRecordDecl>(BaseType->castAs<RecordType>()->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<FunctionTemplateDecl>(CallOp);
+}
+
+CXXMethodDecl *CXXRecordDecl::getLambdaCallOperator() const {
+ NamedDecl *CallOp = getLambdaCallOperatorHelper(*this);
+
+ if (CallOp == nullptr)
+ return nullptr;
- NamedDecl *CallOp = Calls.front();
if (const auto *CallOpTmpl = dyn_cast<FunctionTemplateDecl>(CallOp))
return cast<CXXMethodDecl>(CallOpTmpl->getTemplatedDecl());
@@ -1880,7 +1910,7 @@ bool CXXRecordDecl::mayBeAbstract() const {
for (const auto &B : bases()) {
const auto *BaseDecl =
- cast<CXXRecordDecl>(B.getType()->getAs<RecordType>()->getDecl());
+ cast<CXXRecordDecl>(B.getType()->castAs<RecordType>()->getDecl());
if (BaseDecl->isAbstract())
return true;
}
@@ -2067,10 +2097,15 @@ CXXMethodDecl *CXXMethodDecl::getDevirtualizedMethod(const Expr *Base,
if (DevirtualizedMethod->hasAttr<FinalAttr>())
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<FinalAttr>())
return DevirtualizedMethod;
+ if (const auto *dtor = BestDynamicDecl->getDestructor()) {
+ if (dtor->hasAttr<FinalAttr>())
+ return DevirtualizedMethod;
+ }
if (const auto *DRE = dyn_cast<DeclRefExpr>(Base)) {
if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl()))
@@ -2532,7 +2567,7 @@ bool CXXConstructorDecl::isConvertingConstructor(bool AllowExplicit) const {
return false;
return (getNumParams() == 0 &&
- getType()->getAs<FunctionProtoType>()->isVariadic()) ||
+ getType()->castAs<FunctionProtoType>()->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 f5c69944034a..608b0b44072b 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 40c39c845db6..7e013c6c54d8 100644
--- a/lib/AST/DeclTemplate.cpp
+++ b/lib/AST/DeclTemplate.cpp
@@ -70,6 +70,8 @@ TemplateParameterList::TemplateParameterList(SourceLocation TemplateLoc,
}
if (RequiresClause) {
*getTrailingObjects<Expr *>() = RequiresClause;
+ if (RequiresClause->containsUnexpandedParameterPack())
+ ContainsUnexpandedParameterPack = true;
}
}
@@ -136,6 +138,18 @@ static void AdoptTemplateParameterList(TemplateParameterList *Params,
}
}
+void TemplateParameterList::
+getAssociatedConstraints(llvm::SmallVectorImpl<const Expr *> &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) {
@@ -145,6 +159,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<const Expr *> &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<DeclContext>(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<TemplateTypeParmType>()->getDepth();
+ return getTypeForDecl()->castAs<TemplateTypeParmType>()->getDepth();
}
unsigned TemplateTypeParmDecl::getIndex() const {
- return getTypeForDecl()->getAs<TemplateTypeParmType>()->getIndex();
+ return getTypeForDecl()->castAs<TemplateTypeParmType>()->getIndex();
}
bool TemplateTypeParmDecl::isParameterPack() const {
- return getTypeForDecl()->getAs<TemplateTypeParmType>()->isParameterPack();
+ return getTypeForDecl()->castAs<TemplateTypeParmType>()->isParameterPack();
}
//===----------------------------------------------------------------------===//
@@ -704,12 +735,6 @@ FunctionTemplateSpecializationInfo *FunctionTemplateSpecializationInfo::Create(
}
//===----------------------------------------------------------------------===//
-// TemplateDecl Implementation
-//===----------------------------------------------------------------------===//
-
-void TemplateDecl::anchor() {}
-
-//===----------------------------------------------------------------------===//
// ClassTemplateSpecializationDecl Implementation
//===----------------------------------------------------------------------===//
diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp
index 6ef77b8aee68..3438c3aadc6b 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<CXXRecordDecl>(E->getType()->getAs<RecordType>()->getDecl());
+ auto *Derived =
+ cast<CXXRecordDecl>(E->getType()->castAs<RecordType>()->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<ObjCBoolLiteralExpr>(E))
+ return true;
+
+ if (const auto *OVE = dyn_cast<OpaqueValueExpr>(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<WarnUnusedAttr>()) {
+ const auto *WarnURAttr = Type->getAttr<WarnUnusedResultAttr>();
+ if (Type->hasAttr<WarnUnusedAttr>() ||
+ (WarnURAttr && WarnURAttr->IsCXX11NoDiscard())) {
WarnE = this;
Loc = getBeginLoc();
R1 = getSourceRange();
return true;
}
}
+
+ const auto *CE = cast<CXXConstructExpr>(this);
+ if (const CXXConstructorDecl *Ctor = CE->getConstructor()) {
+ const auto *WarnURAttr = Ctor->getAttr<WarnUnusedResultAttr>();
+ 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<RecordType>()->getDecl();
+ RecordDecl *RD = ILE->getType()->castAs<RecordType>()->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<DeclRefExpr>(E1);
+ const auto *DRE2 = cast<DeclRefExpr>(E2);
+ return DRE1->isRValue() && DRE2->isRValue() &&
+ DRE1->getDecl() == DRE2->getDecl();
+ }
+ case ImplicitCastExprClass: {
+ // Peel off implicit casts.
+ while (true) {
+ const auto *ICE1 = dyn_cast<ImplicitCastExpr>(E1);
+ const auto *ICE2 = dyn_cast<ImplicitCastExpr>(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<DeclRefExpr>(E1);
+ const auto *DRE2 = dyn_cast<DeclRefExpr>(E2);
+ if (DRE1 && DRE2)
+ return declaresSameEntity(DRE1->getDecl(), DRE2->getDecl());
+
+ const auto *Ivar1 = dyn_cast<ObjCIvarRefExpr>(E1);
+ const auto *Ivar2 = dyn_cast<ObjCIvarRefExpr>(E2);
+ if (Ivar1 && Ivar2) {
+ return Ivar1->isFreeIvar() && Ivar2->isFreeIvar() &&
+ declaresSameEntity(Ivar1->getDecl(), Ivar2->getDecl());
+ }
+
+ const auto *Array1 = dyn_cast<ArraySubscriptExpr>(E1);
+ const auto *Array2 = dyn_cast<ArraySubscriptExpr>(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<IntegerLiteral>(Idx1);
+ const auto Integer2 = dyn_cast<IntegerLiteral>(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<MemberExpr>(E1) && isa<MemberExpr>(E2)) {
+ const auto *ME1 = cast<MemberExpr>(E1);
+ const auto *ME2 = cast<MemberExpr>(E2);
+ if (!declaresSameEntity(ME1->getMemberDecl(), ME2->getMemberDecl()))
+ return false;
+ if (const auto *D = dyn_cast<VarDecl>(ME1->getMemberDecl()))
+ if (D->isStaticDataMember())
+ return true;
+ E1 = ME1->getBase()->IgnoreParenImpCasts();
+ E2 = ME2->getBase()->IgnoreParenImpCasts();
+ }
+
+ if (isa<CXXThisExpr>(E1) && isa<CXXThisExpr>(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<DeclRefExpr>(E))
+ return DRE->getDecl();
+ if (const auto *ME = dyn_cast<MemberExpr>(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 b30f785ba8f5..904928bdf286 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<UnaryOperator>(E)) {
+ assert(NotEq->getOpcode() == UO_LNot);
+ E = NotEq->getSubExpr()->IgnoreImplicit();
+ SkippedNot = true;
+ }
+
+ // Decompose the outer binary operator.
+ if (auto *BO = dyn_cast<BinaryOperator>(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<CXXOperatorCallExpr>(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<BinaryOperator>(E)) {
+ assert(BO->getOpcode() == BO_Cmp);
+ Result.LHS = BO->getLHS();
+ Result.RHS = BO->getRHS();
+ Result.InnerBinOp = BO;
+ } else if (auto *BO = dyn_cast<CXXOperatorCallExpr>(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<PointerType>()->getPointeeType();
+ return ArgType->castAs<PointerType>()->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<MemberExpr>(getCallee()->IgnoreParens()))
return cast<CXXMethodDecl>(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<PointerType>();
- assert(PT && "base of arrow member access is not pointer");
- BaseType = PT->getPointeeType();
- }
+ if (isArrow())
+ BaseType = BaseType->castAs<PointerType>()->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<TemplateArgument> ConvertedArgs, Optional<bool> 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<TemplateArgument> 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<TemplateArgument>());
+ 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<TemplateArgument> ConvertedArgs,
+ Optional<bool> IsSatisfied) {
+ void *Buffer = C.Allocate(totalSizeToAlloc<TemplateArgument>(
+ 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<TemplateArgument>(
+ NumTemplateArgs));
+ return new (Buffer) ConceptSpecializationExpr(Empty, NumTemplateArgs);
+}
diff --git a/lib/AST/ExprClassification.cpp b/lib/AST/ExprClassification.cpp
index c61ee703aca8..9dbf6fe9e0f0 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<CallExpr>(E)->getCallReturnType(Ctx));
+ case Expr::CXXRewrittenBinaryOperatorClass:
+ return ClassifyInternal(
+ Ctx, cast<CXXRewrittenBinaryOperator>(E)->getSemanticForm());
+
// __builtin_choose_expr is equivalent to the chosen expression.
case Expr::ChooseExprClass:
return ClassifyInternal(Ctx, cast<ChooseExpr>(E)->getChosenSubExpr());
diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp
index f01b42e7ff76..42c746e60285 100644
--- a/lib/AST/ExprConstant.cpp
+++ b/lib/AST/ExprConstant.cpp
@@ -32,15 +32,21 @@
//
//===----------------------------------------------------------------------===//
+#include <cstring>
+#include <functional>
+#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 <cstring>
-#include <functional>
#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<TypeInfoLValue>())
return B.getTypeInfoType();
+ if (B.is<DynamicAllocLValue>())
+ return B.getDynamicAllocType();
+
const Expr *Base = B.get<const Expr*>();
// 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<typename KeyT>
+ 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<typename T>
- OptionalDiagnostic &operator<<(const T &v) {
- if (Diag)
- *Diag << v;
- return *this;
- }
-
- OptionalDiagnostic &operator<<(const APSInt &I) {
- if (Diag) {
- SmallVector<char, 32> 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<char, 32> Buffer;
- F.toString(Buffer, precision);
- *Diag << StringRef(Buffer.data(), Buffer.size());
- }
- return *this;
- }
-
- OptionalDiagnostic &operator<<(const APFixedPoint &FX) {
- if (Diag) {
- SmallVector<char, 32> 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<APValue*, 1, bool> 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<const ValueDecl*>())
+ Loc = VD->getLocation();
+ else if (const Expr *E = Base.dyn_cast<const Expr*>())
+ 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<ObjectUnderConstruction> {
}
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<T>::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<CXXNewExpr>(AllocExpr))
+ return NE->isArray() ? ArrayNew : New;
+ assert(isa<CallExpr>(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<ObjectUnderConstruction, ConstructionPhase>
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<DynamicAllocLValue, DynAlloc, DynAllocOrder> 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<APValue::LValuePathEntry> Path) {
+ isEvaluatingCtorDtor(APValue::LValueBase Base,
+ ArrayRef<APValue::LValuePathEntry> 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<ASTContext &>(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<ASTContext &>(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<DynAlloc*> lookupDynamicAlloc(DynamicAllocLValue DA) {
+ Optional<DynAlloc*> 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<T>::[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<CXXMethodDecl>(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<ClassTemplateSpecializationDecl>(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<PartialDiagnosticAt> 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<false> 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 bool isRead(AccessKinds AK) {
+ return AK == AK_Read || AK == AK_ReadObjectRepresentation;
}
-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<CXXConstructorDecl>(Frame->Callee)) {
- if (CD->isInheritingConstructor()) {
- addDiag(Frame->CallLoc, diag::note_constexpr_inherited_ctor_call_here)
- << CD->getParent();
- continue;
- }
- }
-
- SmallVector<char, 128> Buffer;
- llvm::raw_svector_ostream Out(Buffer);
- describeCall(Frame, Out);
- addDiag(Frame->CallLoc, diag::note_constexpr_call_here) << Out.str();
- }
-}
-
-/// 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 <class KeyTy>
-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<typename KeyT>
+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<CXXMethodDecl>(Frame->Callee) &&
- !isa<CXXConstructorDecl>(Frame->Callee) &&
- cast<CXXMethodDecl>(Frame->Callee)->isInstance();
+ bool IsMemberCall = isa<CXXMethodDecl>(Callee) &&
+ !isa<CXXConstructorDecl>(Callee) &&
+ cast<CXXMethodDecl>(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<FunctionDecl>(D);
}
- if (B.is<TypeInfoLValue>())
+ if (B.is<TypeInfoLValue>() || B.is<DynamicAllocLValue>())
return true;
const Expr *E = B.get<const Expr*>();
@@ -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<const Expr*>())
Info.Note(E->getExprLoc(), diag::note_constexpr_temporary_here);
+ else if (DynamicAllocLValue DA = Base.dyn_cast<DynamicAllocLValue>()) {
+ // FIXME: Produce a note for dangling pointers too.
+ if (Optional<DynAlloc*> 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<const MaterializeTemporaryExpr *, 8>;
+
+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<DynamicAllocLValue>()) {
+ Info.FFDiag(Loc, diag::note_constexpr_dynamic_alloc)
+ << IsReferenceType << !Designator.Entries.empty();
+ NoteLValueLocation(Info, Base);
+ return false;
+ }
+
if (const ValueDecl *VD = Base.dyn_cast<const ValueDecl*>()) {
if (const VarDecl *Var = dyn_cast<const VarDecl>(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<DLLImportAttr>())
+ // FIXME: Diagnostic!
return false;
}
if (const auto *FD = dyn_cast<const FunctionDecl>(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<DLLImportAttr>())
+ // FIXME: Diagnostic!
+ return false;
+ }
+ } else if (const auto *MTE = dyn_cast_or_null<MaterializeTemporaryExpr>(
+ Base.dyn_cast<const Expr *>())) {
+ 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<RecordType>()->getDecl();
if (const CXXRecordDecl *CD = dyn_cast<CXXRecordDecl>(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<const ValueDecl*>())
- if (declaresSameEntity(Evaluating, BaseD))
- return true;
+ auto *BaseD = Base.dyn_cast<const ValueDecl*>();
- // A temporary lifetime-extended by the variable whose initializer we're
- // evaluating.
- if (auto *BaseE = Base.dyn_cast<const Expr *>())
- if (auto *BaseMTE = dyn_cast<MaterializeTemporaryExpr>(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<const Expr *>())
+ if (auto *BaseMTE = dyn_cast<MaterializeTemporaryExpr>(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<const ValueDecl *>())) {
+ 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<DynamicAllocLValue>()) {
+ Optional<DynAlloc*> 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<const Expr*>();
@@ -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*>();
- 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<const Expr*>();
+ AccessKinds AK =
+ WantObjectRepresentation ? AK_ReadObjectRepresentation : AK_Read;
+
if (Base && !LVal.getLValueCallIndex() && !Type.isVolatileQualified()) {
if (const CompoundLiteralExpr *CLE = dyn_cast<CompoundLiteralExpr>(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<StringLiteral>(Base) || isa<PredefinedExpr>(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<ConstantArrayType>(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;
- return EvaluateStmt(Result, Info, IS->getElse(), Case);
+ 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;
+ 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<ForStmt>(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<DeclStmt>(S);
+ for (const auto *D : DS->decls()) {
+ if (const auto *VD = dyn_cast<VarDecl>(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<Expr>(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<DeclStmt>(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<ForStmt>(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<CXXDestructorDecl>(NamedMember) ? AK_Destroy : AK_MemberCall, false);
}
struct DynamicType {
@@ -4699,16 +5051,19 @@ static Optional<DynamicType> ComputeDynamicType(EvalInfo &Info, const Expr *E,
ArrayRef<APValue::LValuePathEntry> 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<DynamicType> ComputeDynamicType(EvalInfo &Info, const Expr *E,
static const CXXMethodDecl *HandleVirtualDispatch(
EvalInfo &Info, const Expr *E, LValue &This, const CXXMethodDecl *Found,
llvm::SmallVectorImpl<QualType> &CovariantAdjustmentPath) {
- Optional<DynamicType> DynType =
- ComputeDynamicType(Info, E, This, AK_MemberCall);
+ Optional<DynamicType> DynType = ComputeDynamicType(
+ Info, E, This,
+ isa<CXXDestructorDecl>(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<ConstantArrayType>(
- 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<MemberExpr>(E)) {
auto *FD = dyn_cast<FieldDecl>(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<const Expr *> Args, ArgVector &ArgValues,
}
}
}
- for (ArrayRef<const Expr*>::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,8 +5676,11 @@ 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");
@@ -5358,8 +5709,18 @@ static bool HandleConstructorCall(const Expr *E, const LValue &This,
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;
+ EvaluateStmt(Ret, Info, Definition->getBody()) != ESR_Failed &&
+ LifetimeExtendedScope.destroy();
}
static bool HandleConstructorCall(const Expr *E, const LValue &This,
@@ -5374,6 +5735,381 @@ static bool HandleConstructorCall(const Expr *E, const LValue &This,
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<FieldDecl*, 16> 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<T>::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;
+ }
+
+ Info.FFDiag(E, diag::note_constexpr_new_too_large) << APSInt(Size, true);
+ return false;
+ }
+
+ 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<ConstantArrayType>(AllocType));
+ return true;
+}
+
+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<DynAlloc *> 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<DynamicAllocLValue>();
+ if (!DA) {
+ Info.FFDiag(E, diag::note_constexpr_delete_not_heap_alloc)
+ << PointerAsString();
+ if (Pointer.Base)
+ NoteLValueLocation(Info, Pointer.Base);
+ return None;
+ }
+
+ Optional<DynAlloc *> 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;
+
+ // This is permitted only within a call to std::allocator<T>::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<DynamicAllocLValue>());
+ return true;
+}
+
//===----------------------------------------------------------------------===//
// Generic Evaluation
//===----------------------------------------------------------------------===//
@@ -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<BuiltinType>();
- assert(AsBuiltin && "non-integral enum underlying type?");
+ const auto *AsBuiltin =
+ RepresentationType.getCanonicalType()->castAs<BuiltinType>();
// 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<BinaryOperator>(Callee)) {
// Indirect bound member calls ('.*' or '->*').
- Member = dyn_cast_or_null<CXXMethodDecl>(
- HandleMemberPointerAccess(Info, BE, ThisVal, false));
+ const ValueDecl *D =
+ HandleMemberPointerAccess(Info, BE, ThisVal, false);
+ if (!D)
+ return false;
+ Member = dyn_cast<CXXMethodDecl>(D);
if (!Member)
return Error(Callee);
This = &ThisVal;
+ } else if (const auto *PDE = dyn_cast<CXXPseudoDestructorExpr>(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<CXXMethodDecl>(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<CXXDestructorDecl>(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<FieldDecl>(E->getMemberDecl())) {
- assert(BaseTy->getAs<RecordType>()->getDecl()->getCanonicalDecl() ==
+ assert(BaseTy->castAs<RecordType>()->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<CXXMethodDecl>(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<const Expr*> ArraySize = E->getArraySize()) {
+ const Expr *Stripped = *ArraySize;
+ for (; auto *ICE = dyn_cast<ImplicitCastExpr>(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<InitListExpr>(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<ConstantArrayType>(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<VectorType>();
+ const auto *VT = E->getType()->castAs<VectorType>();
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<StringLiteral>(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<TypeInfoLValue>()) {
return B.getTypeInfoType();
+ } else if (B.is<DynamicAllocLValue>()) {
+ 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<PointerType>()->getPointeeType();
+ QualType ElementType = Type->castAs<PointerType>()->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<ComplexType>()->getElementType();
+ QualType To = E->getType()->castAs<ComplexType>()->getElementType();
QualType From
- = E->getSubExpr()->getType()->getAs<ComplexType>()->getElementType();
+ = E->getSubExpr()->getType()->castAs<ComplexType>()->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<ComplexType>()->getElementType();
+ QualType To = E->getType()->castAs<ComplexType>()->getElementType();
QualType From
- = E->getSubExpr()->getType()->getAs<ComplexType>()->getElementType();
+ = E->getSubExpr()->getType()->castAs<ComplexType>()->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<ComplexType>()->getElementType();
+ QualType To = E->getType()->castAs<ComplexType>()->getElementType();
QualType From
- = E->getSubExpr()->getType()->getAs<ComplexType>()->getElementType();
+ = E->getSubExpr()->getType()->castAs<ComplexType>()->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<CXXMethodDecl>(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<DynAlloc *> 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<CXXMethodDecl>(VirtualDelete) << VirtualDelete;
+ return false;
+ }
+ }
+
+ if (!HandleDestruction(Info, E->getExprLoc(), Pointer.getLValueBase(),
+ (*Alloc)->Value, AllocType))
+ return false;
+
+ if (!Info.HeapAllocs.erase(Pointer.Base.dyn_cast<DynamicAllocLValue>())) {
+ // 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<ASTContext &>(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<PartialDiagnosticAt> &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<CXXRewrittenBinaryOperator>(E)->getSemanticForm(),
+ Ctx);
case Expr::DeclRefExprClass: {
if (isa<EnumConstantDecl>(cast<DeclRefExpr>(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<CXXMethodDecl>(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<const Expr*> Args;
diff --git a/lib/AST/ExternalASTMerger.cpp b/lib/AST/ExternalASTMerger.cpp
index 61e657da7c91..f678c2dd3b59 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<Decl *, Decl *> 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<ASTImporterSharedState> 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<Decl *> 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<DeclContext>(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<ImporterSource> Sources) : LogStream(&llvm::nulls()), Target(Target) {
+ SharedState = std::make_shared<ASTImporterSharedState>(
+ *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<ImporterSource> Sources) {
for (const ImporterSource &S : Sources) {
- assert(&S.AST != &Target.AST);
- Importers.push_back(llvm::make_unique<LazyASTImporter>(
- *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<LazyASTImporter>(
+ *this, Target.AST, Target.FM, S, SharedState));
}
}
void ExternalASTMerger::RemoveSources(llvm::ArrayRef<ImporterSource> 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<ASTImporter> &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<ImporterSource> Sources) {
std::pair<const DeclContext *, DCOrigin> 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 578d5bc56733..fcc0b3b11e25 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 9da829adcb49..764e5d46394d 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 4b3d5bee5631..2ed0ce1c79c5 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<const CXXRecordDecl *>(Type->getAs<RecordType>()->getDecl());
+ const auto *Decl =
+ static_cast<const CXXRecordDecl *>(Type->castAs<RecordType>()->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 000000000000..5fc93eb39f4e
--- /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<DeadBlock *>(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 000000000000..97fb9a3ca096
--- /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<unsigned> &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<unsigned> getDeclID() const { return DeclID; }
+
+ /// Returns a pointer to the stored data.
+ char *data() { return reinterpret_cast<char *>(this + 1); }
+
+ /// Returns a view over the data.
+ template <typename T>
+ T &deref() { return *reinterpret_cast<T *>(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<unsigned> 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 000000000000..3e6c8b5da9f0
--- /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 <cstddef>
+#include <cstdint>
+#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<unsigned>(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<uint64_t>(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 <typename T>
+ static typename std::enable_if<std::is_integral<T>::value, Boolean>::type
+ from(T Value) {
+ return Boolean(Value != 0);
+ }
+
+ template <unsigned SrcBits, bool SrcSign>
+ static typename std::enable_if<SrcBits != 0, Boolean>::type from(
+ Integral<SrcBits, SrcSign> Value) {
+ return Boolean(!Value.isZero());
+ }
+
+ template <bool SrcSign>
+ static Boolean from(Integral<0, SrcSign> Value) {
+ return Boolean(!Value.isZero());
+ }
+
+ static Boolean zero() { return from(false); }
+
+ template <typename T>
+ 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 000000000000..7a4569820a1d
--- /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<Function *> 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<PrimType, 8> ParamTypes;
+ llvm::DenseMap<unsigned, Function::ParamDescriptor> 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<PrimType> 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<ByteCodeGenError>(*BailLocation);
+ else
+ return Func;
+ } else {
+ // Create scopes from descriptors.
+ llvm::SmallVector<Scope, 2> 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<int64_t>(Reloc);
+ endian::write<int32_t, endianness::native, 1>(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 <typename... Tys>
+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<unsigned>::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<const char *>(&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<int>{
+ (emit(reinterpret_cast<const char *>(&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 000000000000..03452a350c96
--- /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<Function *> 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<const ParmVarDecl *, unsigned> Params;
+ /// Local descriptors.
+ llvm::SmallVector<SmallVector<Local, 8>, 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<SourceLocation> BailLocation;
+ /// Label information for linker.
+ llvm::DenseMap<LabelTy, unsigned> LabelOffsets;
+ /// Location of label relocations.
+ llvm::DenseMap<LabelTy, llvm::SmallVector<unsigned, 5>> LabelRelocs;
+ /// Program code.
+ std::vector<char> 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 <typename... Tys>
+ 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 000000000000..5c8cb4274260
--- /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 <typename T> using Expected = llvm::Expected<T>;
+template <typename T> using Optional = llvm::Optional<T>;
+
+namespace clang {
+namespace interp {
+
+/// Scope used to handle temporaries in toplevel variable declarations.
+template <class Emitter> class DeclScope final : public LocalScope<Emitter> {
+public:
+ DeclScope(ByteCodeExprGen<Emitter> *Ctx, const VarDecl *VD)
+ : LocalScope<Emitter>(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 Emitter> class OptionScope {
+public:
+ using InitFnRef = typename ByteCodeExprGen<Emitter>::InitFnRef;
+ using ChainedInitFnRef = std::function<bool(InitFnRef)>;
+
+ /// Root constructor, compiling or discarding primitives.
+ OptionScope(ByteCodeExprGen<Emitter> *Ctx, bool NewDiscardResult)
+ : Ctx(Ctx), OldDiscardResult(Ctx->DiscardResult),
+ OldInitFn(std::move(Ctx->InitFn)) {
+ Ctx->DiscardResult = NewDiscardResult;
+ Ctx->InitFn = llvm::Optional<InitFnRef>{};
+ }
+
+ /// Root constructor, setting up compilation state.
+ OptionScope(ByteCodeExprGen<Emitter> *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<Emitter> *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<Emitter> *Ctx;
+ /// Old discard flag to restore.
+ bool OldDiscardResult;
+ /// Old pointer emitter to restore.
+ llvm::Optional<InitFnRef> OldInitFn;
+};
+
+} // namespace interp
+} // namespace clang
+
+template <class Emitter>
+bool ByteCodeExprGen<Emitter>::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 <class Emitter>
+bool ByteCodeExprGen<Emitter>::VisitIntegerLiteral(const IntegerLiteral *LE) {
+ if (DiscardResult)
+ return true;
+
+ auto Val = LE->getValue();
+ QualType LitTy = LE->getType();
+ if (Optional<PrimType> T = classify(LitTy))
+ return emitConst(*T, getIntWidth(LitTy), LE->getValue(), LE);
+ return this->bail(LE);
+}
+
+template <class Emitter>
+bool ByteCodeExprGen<Emitter>::VisitParenExpr(const ParenExpr *PE) {
+ return this->Visit(PE->getSubExpr());
+}
+
+template <class Emitter>
+bool ByteCodeExprGen<Emitter>::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<PrimType> LT = classify(LHS->getType());
+ Optional<PrimType> RT = classify(RHS->getType());
+ if (!LT || !RT) {
+ return this->bail(BO);
+ }
+
+ if (Optional<PrimType> 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 <class Emitter>
+bool ByteCodeExprGen<Emitter>::discard(const Expr *E) {
+ OptionScope<Emitter> Scope(this, /*discardResult=*/true);
+ return this->Visit(E);
+}
+
+template <class Emitter>
+bool ByteCodeExprGen<Emitter>::visit(const Expr *E) {
+ OptionScope<Emitter> Scope(this, /*discardResult=*/false);
+ return this->Visit(E);
+}
+
+template <class Emitter>
+bool ByteCodeExprGen<Emitter>::visitBool(const Expr *E) {
+ if (Optional<PrimType> T = classify(E->getType())) {
+ return visit(E);
+ } else {
+ return this->bail(E);
+ }
+}
+
+template <class Emitter>
+bool ByteCodeExprGen<Emitter>::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 <class Emitter>
+bool ByteCodeExprGen<Emitter>::dereference(
+ const Expr *LV, DerefKind AK, llvm::function_ref<bool(PrimType)> Direct,
+ llvm::function_ref<bool(PrimType)> Indirect) {
+ if (Optional<PrimType> T = classify(LV->getType())) {
+ if (!LV->refersToBitField()) {
+ // Only primitive, non bit-field types can be dereferenced directly.
+ if (auto *DE = dyn_cast<DeclRefExpr>(LV)) {
+ if (!DE->getDecl()->getType()->isReferenceType()) {
+ if (auto *PD = dyn_cast<ParmVarDecl>(DE->getDecl()))
+ return dereferenceParam(LV, *T, PD, AK, Direct, Indirect);
+ if (auto *VD = dyn_cast<VarDecl>(DE->getDecl()))
+ return dereferenceVar(LV, *T, VD, AK, Direct, Indirect);
+ }
+ }
+ }
+
+ if (!visit(LV))
+ return false;
+ return Indirect(*T);
+ }
+
+ return false;
+}
+
+template <class Emitter>
+bool ByteCodeExprGen<Emitter>::dereferenceParam(
+ const Expr *LV, PrimType T, const ParmVarDecl *PD, DerefKind AK,
+ llvm::function_ref<bool(PrimType)> Direct,
+ llvm::function_ref<bool(PrimType)> 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 <class Emitter>
+bool ByteCodeExprGen<Emitter>::dereferenceVar(
+ const Expr *LV, PrimType T, const VarDecl *VD, DerefKind AK,
+ llvm::function_ref<bool(PrimType)> Direct,
+ llvm::function_ref<bool(PrimType)> 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 <class Emitter>
+bool ByteCodeExprGen<Emitter>::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 <class Emitter>
+unsigned ByteCodeExprGen<Emitter>::allocateLocalPrimitive(DeclTy &&Src,
+ PrimType Ty,
+ bool IsConst,
+ bool IsExtended) {
+ Descriptor *D = P.createDescriptor(Src, Ty, IsConst, Src.is<const Expr *>());
+ Scope::Local Local = this->createLocal(D);
+ if (auto *VD = dyn_cast_or_null<ValueDecl>(Src.dyn_cast<const Decl *>()))
+ Locals.insert({VD, Local});
+ VarScope->add(Local, IsExtended);
+ return Local.Offset;
+}
+
+template <class Emitter>
+llvm::Optional<unsigned>
+ByteCodeExprGen<Emitter>::allocateLocal(DeclTy &&Src, bool IsExtended) {
+ QualType Ty;
+
+ const ValueDecl *Key = nullptr;
+ bool IsTemporary = false;
+ if (auto *VD = dyn_cast_or_null<ValueDecl>(Src.dyn_cast<const Decl *>())) {
+ Key = VD;
+ Ty = VD->getType();
+ }
+ if (auto *E = Src.dyn_cast<const Expr *>()) {
+ 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 <class Emitter>
+bool ByteCodeExprGen<Emitter>::visitInitializer(
+ const Expr *Init, InitFnRef InitFn) {
+ OptionScope<Emitter> Scope(this, InitFn);
+ return this->Visit(Init);
+}
+
+template <class Emitter>
+bool ByteCodeExprGen<Emitter>::getPtrVarDecl(const VarDecl *VD, const Expr *E) {
+ // Generate a pointer to the local, loading refs.
+ if (Optional<unsigned> Idx = getGlobalIdx(VD)) {
+ if (VD->getType()->isReferenceType())
+ return this->emitGetGlobalPtr(*Idx, E);
+ else
+ return this->emitGetPtrGlobal(*Idx, E);
+ }
+ return this->bail(VD);
+}
+
+template <class Emitter>
+llvm::Optional<unsigned>
+ByteCodeExprGen<Emitter>::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 <class Emitter>
+const RecordType *ByteCodeExprGen<Emitter>::getRecordTy(QualType Ty) {
+ if (auto *PT = dyn_cast<PointerType>(Ty))
+ return PT->getPointeeType()->getAs<RecordType>();
+ else
+ return Ty->getAs<RecordType>();
+}
+
+template <class Emitter>
+Record *ByteCodeExprGen<Emitter>::getRecord(QualType Ty) {
+ if (auto *RecordTy = getRecordTy(Ty)) {
+ return getRecord(RecordTy->getDecl());
+ }
+ return nullptr;
+}
+
+template <class Emitter>
+Record *ByteCodeExprGen<Emitter>::getRecord(const RecordDecl *RD) {
+ return P.getOrCreateRecord(RD);
+}
+
+template <class Emitter>
+bool ByteCodeExprGen<Emitter>::visitExpr(const Expr *Exp) {
+ ExprScope<Emitter> RootScope(this);
+ if (!visit(Exp))
+ return false;
+
+ if (Optional<PrimType> T = classify(Exp))
+ return this->emitRet(*T, Exp);
+ else
+ return this->emitRetValue(Exp);
+}
+
+template <class Emitter>
+bool ByteCodeExprGen<Emitter>::visitDecl(const VarDecl *VD) {
+ const Expr *Init = VD->getInit();
+
+ if (Optional<unsigned> I = P.createGlobal(VD)) {
+ if (Optional<PrimType> T = classify(VD->getType())) {
+ {
+ // Primitive declarations - compute the value and set it.
+ DeclScope<Emitter> 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<Emitter> 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 <class Emitter>
+void ByteCodeExprGen<Emitter>::emitCleanup() {
+ for (VariableScope<Emitter> *C = VarScope; C; C = C->getParent())
+ C->emitDestruction();
+}
+
+namespace clang {
+namespace interp {
+
+template class ByteCodeExprGen<ByteCodeEmitter>;
+template class ByteCodeExprGen<EvalEmitter>;
+
+} // namespace interp
+} // namespace clang
diff --git a/lib/AST/Interp/ByteCodeExprGen.h b/lib/AST/Interp/ByteCodeExprGen.h
new file mode 100644
index 000000000000..1d0e34fc991f
--- /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 Emitter> class LocalScope;
+template <class Emitter> class RecordScope;
+template <class Emitter> class VariableScope;
+template <class Emitter> class DeclScope;
+template <class Emitter> class OptionScope;
+
+/// Compilation context for expressions.
+template <class Emitter>
+class ByteCodeExprGen : public ConstStmtVisitor<ByteCodeExprGen<Emitter>, 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<bool()>;
+
+ /// Current compilation context.
+ Context &Ctx;
+ /// Program to link to.
+ Program &P;
+
+public:
+ /// Initializes the compiler and the backend emitter.
+ template <typename... Tys>
+ 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<PrimType> classify(const Expr *E) const {
+ return E->isGLValue() ? PT_Ptr : classify(E->getType());
+ }
+ llvm::Optional<PrimType> 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<unsigned> allocateLocal(DeclTy &&Decl,
+ bool IsExtended = false);
+
+private:
+ friend class VariableScope<Emitter>;
+ friend class LocalScope<Emitter>;
+ friend class RecordScope<Emitter>;
+ friend class DeclScope<Emitter>;
+ friend class OptionScope<Emitter>;
+
+ /// 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<bool(PrimType)> Direct,
+ llvm::function_ref<bool(PrimType)> Indirect);
+ bool dereferenceParam(const Expr *LV, PrimType T, const ParmVarDecl *PD,
+ DerefKind AK,
+ llvm::function_ref<bool(PrimType)> Direct,
+ llvm::function_ref<bool(PrimType)> Indirect);
+ bool dereferenceVar(const Expr *LV, PrimType T, const VarDecl *PD,
+ DerefKind AK, llvm::function_ref<bool(PrimType)> Direct,
+ llvm::function_ref<bool(PrimType)> Indirect);
+
+ /// Emits an APInt constant.
+ bool emitConst(PrimType T, unsigned NumBits, const llvm::APInt &Value,
+ const Expr *E);
+
+ /// Emits an integer constant.
+ template <typename T> bool emitConst(const Expr *E, T Value) {
+ QualType Ty = E->getType();
+ unsigned NumBits = getIntWidth(Ty);
+ APInt WrappedValue(NumBits, Value, std::is_signed<T>::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<unsigned> getGlobalIdx(const VarDecl *VD);
+
+ /// Emits the initialized pointer.
+ bool emitInitFn() {
+ assert(InitFn && "missing initializer");
+ return (*InitFn)();
+ }
+
+protected:
+ /// Variable to storage mapping.
+ llvm::DenseMap<const ValueDecl *, Scope::Local> Locals;
+
+ /// OpaqueValueExpr to location mapping.
+ llvm::DenseMap<const OpaqueValueExpr *, unsigned> OpaqueExprs;
+
+ /// Current scope.
+ VariableScope<Emitter> *VarScope = nullptr;
+
+ /// Current argument index.
+ llvm::Optional<uint64_t> ArrayIndex;
+
+ /// Flag indicating if return value is to be discarded.
+ bool DiscardResult = false;
+
+ /// Expression being initialized.
+ llvm::Optional<InitFnRef> InitFn = {};
+};
+
+extern template class ByteCodeExprGen<ByteCodeEmitter>;
+extern template class ByteCodeExprGen<EvalEmitter>;
+
+/// Scope chain managing the variable lifetimes.
+template <class Emitter> 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<Emitter> *Ctx)
+ : Ctx(Ctx), Parent(Ctx->VarScope) {
+ Ctx->VarScope = this;
+ }
+
+ /// ByteCodeExprGen instance.
+ ByteCodeExprGen<Emitter> *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 Emitter> class LocalScope : public VariableScope<Emitter> {
+public:
+ LocalScope(ByteCodeExprGen<Emitter> *Ctx) : VariableScope<Emitter>(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<unsigned> Idx;
+};
+
+/// Scope for storage declared in a compound statement.
+template <class Emitter> class BlockScope final : public LocalScope<Emitter> {
+public:
+ BlockScope(ByteCodeExprGen<Emitter> *Ctx) : LocalScope<Emitter>(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 Emitter> class ExprScope final : public LocalScope<Emitter> {
+public:
+ ExprScope(ByteCodeExprGen<Emitter> *Ctx) : LocalScope<Emitter>(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 000000000000..5fd3d77c3842
--- /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 000000000000..a4fa4917705d
--- /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<ByteCodeGenError> {
+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 000000000000..c71301598bde
--- /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 <typename T> using Expected = llvm::Expected<T>;
+template <typename T> using Optional = llvm::Optional<T>;
+
+namespace clang {
+namespace interp {
+
+/// Scope managing label targets.
+template <class Emitter> class LabelScope {
+public:
+ virtual ~LabelScope() { }
+
+protected:
+ LabelScope(ByteCodeStmtGen<Emitter> *Ctx) : Ctx(Ctx) {}
+ /// ByteCodeStmtGen instance.
+ ByteCodeStmtGen<Emitter> *Ctx;
+};
+
+/// Sets the context for break/continue statements.
+template <class Emitter> class LoopScope final : public LabelScope<Emitter> {
+public:
+ using LabelTy = typename ByteCodeStmtGen<Emitter>::LabelTy;
+ using OptLabelTy = typename ByteCodeStmtGen<Emitter>::OptLabelTy;
+
+ LoopScope(ByteCodeStmtGen<Emitter> *Ctx, LabelTy BreakLabel,
+ LabelTy ContinueLabel)
+ : LabelScope<Emitter>(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 Emitter> class SwitchScope final : public LabelScope<Emitter> {
+public:
+ using LabelTy = typename ByteCodeStmtGen<Emitter>::LabelTy;
+ using OptLabelTy = typename ByteCodeStmtGen<Emitter>::OptLabelTy;
+ using CaseMap = typename ByteCodeStmtGen<Emitter>::CaseMap;
+
+ SwitchScope(ByteCodeStmtGen<Emitter> *Ctx, CaseMap &&CaseLabels,
+ LabelTy BreakLabel, OptLabelTy DefaultLabel)
+ : LabelScope<Emitter>(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 <class Emitter>
+bool ByteCodeStmtGen<Emitter>::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<CXXMethodDecl>(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 <class Emitter>
+bool ByteCodeStmtGen<Emitter>::visitStmt(const Stmt *S) {
+ switch (S->getStmtClass()) {
+ case Stmt::CompoundStmtClass:
+ return visitCompoundStmt(cast<CompoundStmt>(S));
+ case Stmt::DeclStmtClass:
+ return visitDeclStmt(cast<DeclStmt>(S));
+ case Stmt::ReturnStmtClass:
+ return visitReturnStmt(cast<ReturnStmt>(S));
+ case Stmt::IfStmtClass:
+ return visitIfStmt(cast<IfStmt>(S));
+ case Stmt::NullStmtClass:
+ return true;
+ default: {
+ if (auto *Exp = dyn_cast<Expr>(S))
+ return this->discard(Exp);
+ return this->bail(S);
+ }
+ }
+}
+
+template <class Emitter>
+bool ByteCodeStmtGen<Emitter>::visitCompoundStmt(
+ const CompoundStmt *CompoundStmt) {
+ BlockScope<Emitter> Scope(this);
+ for (auto *InnerStmt : CompoundStmt->body())
+ if (!visitStmt(InnerStmt))
+ return false;
+ return true;
+}
+
+template <class Emitter>
+bool ByteCodeStmtGen<Emitter>::visitDeclStmt(const DeclStmt *DS) {
+ for (auto *D : DS->decls()) {
+ // Variable declarator.
+ if (auto *VD = dyn_cast<VarDecl>(D)) {
+ if (!visitVarDecl(VD))
+ return false;
+ continue;
+ }
+
+ // Decomposition declarator.
+ if (auto *DD = dyn_cast<DecompositionDecl>(D)) {
+ return this->bail(DD);
+ }
+ }
+
+ return true;
+}
+
+template <class Emitter>
+bool ByteCodeStmtGen<Emitter>::visitReturnStmt(const ReturnStmt *RS) {
+ if (const Expr *RE = RS->getRetValue()) {
+ ExprScope<Emitter> 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 <class Emitter>
+bool ByteCodeStmtGen<Emitter>::visitIfStmt(const IfStmt *IS) {
+ BlockScope<Emitter> 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 <class Emitter>
+bool ByteCodeStmtGen<Emitter>::visitVarDecl(const VarDecl *VD) {
+ auto DT = VD->getType();
+
+ if (!VD->hasLocalStorage()) {
+ // No code generation required.
+ return true;
+ }
+
+ // Integers, pointers, primitives.
+ if (Optional<PrimType> T = this->classify(DT)) {
+ auto Off = this->allocateLocalPrimitive(VD, *T, DT.isConstQualified());
+ // Compile the initialiser in its own scope.
+ {
+ ExprScope<Emitter> 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<ByteCodeEmitter>;
+
+} // namespace interp
+} // namespace clang
diff --git a/lib/AST/Interp/ByteCodeStmtGen.h b/lib/AST/Interp/ByteCodeStmtGen.h
new file mode 100644
index 000000000000..d9c0b64ed4b8
--- /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 Emitter> class LoopScope;
+template <class Emitter> class SwitchScope;
+template <class Emitter> class LabelScope;
+
+/// Compilation context for statements.
+template <class Emitter>
+class ByteCodeStmtGen : public ByteCodeExprGen<Emitter> {
+ using LabelTy = typename Emitter::LabelTy;
+ using AddrTy = typename Emitter::AddrTy;
+ using OptLabelTy = llvm::Optional<LabelTy>;
+ using CaseMap = llvm::DenseMap<const SwitchCase *, LabelTy>;
+
+public:
+ template<typename... Tys>
+ ByteCodeStmtGen(Tys&&... Args)
+ : ByteCodeExprGen<Emitter>(std::forward<Tys>(Args)...) {}
+
+protected:
+ bool visitFunc(const FunctionDecl *F) override;
+
+private:
+ friend class LabelScope<Emitter>;
+ friend class LoopScope<Emitter>;
+ friend class SwitchScope<Emitter>;
+
+ // 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<PrimType> 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<EvalEmitter>;
+
+} // namespace interp
+} // namespace clang
+
+#endif
diff --git a/lib/AST/Interp/Context.cpp b/lib/AST/Interp/Context.cpp
new file mode 100644
index 000000000000..4f8f7b96e7c3
--- /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<ByteCodeEmitter>(*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<EvalEmitter> C(*this, *P, Parent, Stk, Result);
+ return Check(Parent, C.interpretExpr(E));
+}
+
+InterpResult Context::evaluateAsInitializer(State &Parent, const VarDecl *VD,
+ APValue &Result) {
+ ByteCodeExprGen<EvalEmitter> C(*this, *P, Parent, Stk, Result);
+ return Check(Parent, C.interpretDecl(VD));
+}
+
+const LangOptions &Context::getLangOpts() const { return Ctx.getLangOpts(); }
+
+llvm::Optional<PrimType> 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<AtomicType>(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<bool> &&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 000000000000..96368b6e5f02
--- /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<PrimType> 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<bool> &&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<Program> 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 000000000000..5c1a8a9cf306
--- /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 <typename T>
+static void ctorTy(Block *, char *Ptr, bool, bool, bool, Descriptor *) {
+ new (Ptr) T();
+}
+
+template <typename T> static void dtorTy(Block *, char *Ptr, Descriptor *) {
+ reinterpret_cast<T *>(Ptr)->~T();
+}
+
+template <typename T>
+static void moveTy(Block *, char *Src, char *Dst, Descriptor *) {
+ auto *SrcPtr = reinterpret_cast<T *>(Src);
+ auto *DstPtr = reinterpret_cast<T *>(Dst);
+ new (DstPtr) T(std::move(*SrcPtr));
+}
+
+template <typename T>
+static void ctorArrayTy(Block *, char *Ptr, bool, bool, bool, Descriptor *D) {
+ for (unsigned I = 0, NE = D->getNumElems(); I < NE; ++I) {
+ new (&reinterpret_cast<T *>(Ptr)[I]) T();
+ }
+}
+
+template <typename T>
+static void dtorArrayTy(Block *, char *Ptr, Descriptor *D) {
+ for (unsigned I = 0, NE = D->getNumElems(); I < NE; ++I) {
+ reinterpret_cast<T *>(Ptr)[I].~T();
+ }
+}
+
+template <typename T>
+static void moveArrayTy(Block *, char *Src, char *Dst, Descriptor *D) {
+ for (unsigned I = 0, NE = D->getNumElems(); I < NE; ++I) {
+ auto *SrcPtr = &reinterpret_cast<T *>(Src)[I];
+ auto *DstPtr = &reinterpret_cast<T *>(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<InlineDescriptor *>(ElemPtr);
+ auto *ElemLoc = reinterpret_cast<char *>(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<InlineDescriptor *>(ElemPtr);
+ auto *ElemLoc = reinterpret_cast<char *>(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<InlineDescriptor *>(SrcPtr);
+ auto *SrcElemLoc = reinterpret_cast<char *>(SrcDesc + 1);
+ auto *DstDesc = reinterpret_cast<InlineDescriptor *>(DstPtr);
+ auto *DstElemLoc = reinterpret_cast<char *>(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<InlineDescriptor *>(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<Descriptor **>(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<T>, return nullptr);
+}
+
+static BlockDtorFn getDtorPrim(PrimType Type) {
+ COMPOSITE_TYPE_SWITCH(Type, return dtorTy<T>, return nullptr);
+}
+
+static BlockMoveFn getMovePrim(PrimType Type) {
+ COMPOSITE_TYPE_SWITCH(Type, return moveTy<T>, return nullptr);
+}
+
+static BlockCtorFn getCtorArrayPrim(PrimType Type) {
+ COMPOSITE_TYPE_SWITCH(Type, return ctorArrayTy<T>, return nullptr);
+}
+
+static BlockDtorFn getDtorArrayPrim(PrimType Type) {
+ COMPOSITE_TYPE_SWITCH(Type, return dtorArrayTy<T>, return nullptr);
+}
+
+static BlockMoveFn getMoveArrayPrim(PrimType Type) {
+ COMPOSITE_TYPE_SWITCH(Type, return moveArrayTy<T>, 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<size_t>(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<size_t>(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<const Decl *>())
+ return D->getLocation();
+ if (auto *E = Source.dyn_cast<const Expr *>())
+ 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<char *>(this) + align(sizeof(InitMap));
+ return reinterpret_cast<T *>(Start);
+}
+
+bool InitMap::initialize(unsigned I) {
+ unsigned Bucket = I / PER_FIELD;
+ unsigned Mask = 1ull << static_cast<uint64_t>(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<uint64_t>(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 000000000000..b260b7600974
--- /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<const Decl *, const Expr *>;
+
+/// 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 Decl *>(); }
+ const Expr *asExpr() const { return Source.dyn_cast<const Expr *>(); }
+
+ const ValueDecl *asValueDecl() const {
+ return dyn_cast_or_null<ValueDecl>(asDecl());
+ }
+
+ const FieldDecl *asFieldDecl() const {
+ return dyn_cast_or_null<FieldDecl>(asDecl());
+ }
+
+ const RecordDecl *asRecordDecl() const {
+ return dyn_cast_or_null<RecordDecl>(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 000000000000..e77a825eb1f2
--- /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<CXXConstructorDecl>(F)) {
+ const std::string &Name = Cons->getParent()->getNameAsString();
+ OS << Name << "::" << Name << ":\n";
+ } else {
+ OS << F->getNameAsString() << ":\n";
+ }
+ } else {
+ OS << "<<expr>>\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<Opcode>();
+ 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 000000000000..22e8695b9211
--- /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 <typename T> using Expected = llvm::Expected<T>;
+
+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<bool> EvalEmitter::interpretExpr(const Expr *E) {
+ if (this->visitExpr(E))
+ return true;
+ if (BailLocation)
+ return llvm::make_error<ByteCodeGenError>(*BailLocation);
+ return false;
+}
+
+llvm::Expected<bool> EvalEmitter::interpretDecl(const VarDecl *VD) {
+ if (this->visitDecl(VD))
+ return true;
+ if (BailLocation)
+ return llvm::make_error<ByteCodeGenError>(*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<char[]>(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<bool>())
+ ActiveLabel = Label;
+ }
+ return true;
+}
+
+bool EvalEmitter::jumpFalse(const LabelTy &Label) {
+ if (isActive()) {
+ if (!S.Stk.pop<bool>())
+ 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 <PrimType OpType> bool EvalEmitter::emitRet(const SourceInfo &Info) {
+ if (!isActive())
+ return true;
+ using T = typename PrimConv<OpType>::T;
+ return ReturnValue<T>(S.Stk.pop<T>(), Result);
+}
+
+bool EvalEmitter::emitRetVoid(const SourceInfo &Info) { return true; }
+
+bool EvalEmitter::emitRetValue(const SourceInfo &Info) {
+ // Method to recursively traverse composites.
+ std::function<bool(QualType, const Pointer &, APValue &)> Composite;
+ Composite = [this, &Composite](QualType Ty, const Pointer &Ptr, APValue &R) {
+ if (auto *AT = Ty->getAs<AtomicType>())
+ Ty = AT->getValueType();
+
+ if (auto *RT = Ty->getAs<RecordType>()) {
+ 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<PrimType> T = Ctx.classify(FieldTy)) {
+ TYPE_SWITCH(*T, Ok &= ReturnValue<T>(FP.deref<T>(), 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<PrimType> T = Ctx.classify(FieldTy)) {
+ TYPE_SWITCH(*T, Ok &= ReturnValue<T>(FP.deref<T>(), 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<PrimType> T = Ctx.classify(ElemTy)) {
+ TYPE_SWITCH(*T, Ok &= ReturnValue<T>(EP.deref<T>(), 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<Pointer>();
+ 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<Pointer>(reinterpret_cast<Block *>(It->second.get()));
+ return true;
+}
+
+template <PrimType OpType>
+bool EvalEmitter::emitGetLocal(uint32_t I, const SourceInfo &Info) {
+ if (!isActive())
+ return true;
+
+ using T = typename PrimConv<OpType>::T;
+
+ auto It = Locals.find(I);
+ assert(It != Locals.end() && "Missing local variable");
+ auto *B = reinterpret_cast<Block *>(It->second.get());
+ S.Stk.push<T>(*reinterpret_cast<T *>(B + 1));
+ return true;
+}
+
+template <PrimType OpType>
+bool EvalEmitter::emitSetLocal(uint32_t I, const SourceInfo &Info) {
+ if (!isActive())
+ return true;
+
+ using T = typename PrimConv<OpType>::T;
+
+ auto It = Locals.find(I);
+ assert(It != Locals.end() && "Missing local variable");
+ auto *B = reinterpret_cast<Block *>(It->second.get());
+ *reinterpret_cast<T *>(B + 1) = S.Stk.pop<T>();
+ 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<Block *>(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 000000000000..eec2ff8ee753
--- /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<bool> interpretExpr(const Expr *E);
+ llvm::Expected<bool> 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<const ParmVarDecl *, unsigned> Params;
+ /// Local descriptors.
+ llvm::SmallVector<SmallVector<Local, 8>, 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<unsigned, std::unique_ptr<char[]>> 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<SourceLocation> 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 000000000000..16134aa1db36
--- /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 000000000000..b9a0ea9412f8
--- /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 000000000000..0ed13a92aa38
--- /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<PrimType, 8> &&ParamTypes,
+ llvm::DenseMap<unsigned, ParamDescriptor> &&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<unsigned, SourceInfo>;
+ 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<CXXMethodDecl>(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 000000000000..28531f04b6e9
--- /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<Local, 8>;
+
+ Scope(LocalVectorTy &&Descriptors) : Descriptors(std::move(Descriptors)) {}
+
+ llvm::iterator_range<LocalVectorTy::iterator> 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<PrimType, Descriptor *>;
+
+ /// 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<llvm::SmallVector<Scope, 2>::iterator> scopes() {
+ return llvm::make_range(Scopes.begin(), Scopes.end());
+ }
+
+ /// Range over argument types.
+ using arg_reverse_iterator = SmallVectorImpl<PrimType>::reverse_iterator;
+ llvm::iterator_range<arg_reverse_iterator> 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<CXXConstructorDecl>(F); }
+
+private:
+ /// Construct a function representing an actual function.
+ Function(Program &P, const FunctionDecl *F, unsigned ArgSize,
+ llvm::SmallVector<PrimType, 8> &&ParamTypes,
+ llvm::DenseMap<unsigned, ParamDescriptor> &&Params);
+
+ /// Sets the code of a function.
+ void setCode(unsigned NewFrameSize, std::vector<char> &&NewCode, SourceMap &&NewSrcMap,
+ llvm::SmallVector<Scope, 2> &&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<char> Code;
+ /// Opcode-to-expression mapping.
+ SourceMap SrcMap;
+ /// List of block descriptors.
+ llvm::SmallVector<Scope, 2> Scopes;
+ /// List of argument types.
+ llvm::SmallVector<PrimType, 8> ParamTypes;
+ /// Map from byte offset to parameter descriptor.
+ llvm::DenseMap<unsigned, ParamDescriptor> 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 000000000000..7cc788070de8
--- /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 <cstddef>
+#include <cstdint>
+
+namespace clang {
+namespace interp {
+
+using APInt = llvm::APInt;
+using APSInt = llvm::APSInt;
+
+/// Helper to compare two comparable types.
+template <typename T>
+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 <unsigned Bits, bool Signed> 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 <unsigned Bits, bool Signed> class Integral {
+private:
+ template <unsigned OtherBits, bool OtherSigned> friend class Integral;
+
+ // The primitive representing the integral.
+ using T = typename Repr<Bits, Signed>::Type;
+ T V;
+
+ /// Primitive representing limits.
+ static const auto Min = std::numeric_limits<T>::min();
+ static const auto Max = std::numeric_limits<T>::max();
+
+ /// Construct an integral from anything that is convertible to storage.
+ template <typename T> explicit Integral(T V) : V(V) {}
+
+public:
+ /// Zero-initializes an integral.
+ Integral() : V(0) {}
+
+ /// Constructs an integral from another integral.
+ template <unsigned SrcBits, bool SrcSign>
+ explicit Integral(Integral<SrcBits, SrcSign> 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<unsigned>(V) > RHS;
+ }
+
+ Integral operator-() const { return Integral(-V); }
+ Integral operator~() const { return Integral(~V); }
+
+ template <unsigned DstBits, bool DstSign>
+ explicit operator Integral<DstBits, DstSign>() const {
+ return Integral<DstBits, DstSign>(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<uint64_t>(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<Bits, false> toUnsigned() const {
+ return Integral<Bits, false>(*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<T>(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 <typename T>
+ static typename std::enable_if<std::is_integral<T>::value, Integral>::type
+ from(T Value) {
+ return Integral(Value);
+ }
+
+ template <unsigned SrcBits, bool SrcSign>
+ static typename std::enable_if<SrcBits != 0, Integral>::type
+ from(Integral<SrcBits, SrcSign> Value) {
+ return Integral(Value.V);
+ }
+
+ template <bool SrcSign> 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 <typename T> static Integral from(T Value, unsigned NumBits) {
+ return Integral(Value);
+ }
+
+ static bool inRange(int64_t Value, unsigned NumBits) {
+ return CheckRange<T, Min, Max>(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 <typename T>
+ static typename std::enable_if<std::is_signed<T>::value, bool>::type
+ CheckAddUB(T A, T B, T &R) {
+ return llvm::AddOverflow<T>(A, B, R);
+ }
+
+ template <typename T>
+ static typename std::enable_if<std::is_unsigned<T>::value, bool>::type
+ CheckAddUB(T A, T B, T &R) {
+ R = A + B;
+ return false;
+ }
+
+ template <typename T>
+ static typename std::enable_if<std::is_signed<T>::value, bool>::type
+ CheckSubUB(T A, T B, T &R) {
+ return llvm::SubOverflow<T>(A, B, R);
+ }
+
+ template <typename T>
+ static typename std::enable_if<std::is_unsigned<T>::value, bool>::type
+ CheckSubUB(T A, T B, T &R) {
+ R = A - B;
+ return false;
+ }
+
+ template <typename T>
+ static typename std::enable_if<std::is_signed<T>::value, bool>::type
+ CheckMulUB(T A, T B, T &R) {
+ return llvm::MulOverflow<T>(A, B, R);
+ }
+
+ template <typename T>
+ static typename std::enable_if<std::is_unsigned<T>::value, bool>::type
+ CheckMulUB(T A, T B, T &R) {
+ R = A * B;
+ return false;
+ }
+
+ template <typename T, T Min, T Max>
+ static typename std::enable_if<std::is_signed<T>::value, bool>::type
+ CheckRange(int64_t V) {
+ return Min <= V && V <= Max;
+ }
+
+ template <typename T, T Min, T Max>
+ static typename std::enable_if<std::is_unsigned<T>::value, bool>::type
+ CheckRange(int64_t V) {
+ return V >= 0 && static_cast<uint64_t>(V) <= Max;
+ }
+};
+
+template <unsigned Bits, bool Signed>
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, Integral<Bits, Signed> 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 000000000000..1a8109cedf76
--- /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 <limits>
+#include <vector>
+#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 <PrimType Name, class T = typename PrimConv<Name>::T>
+static bool Ret(InterpState &S, CodePtr &PC, APValue &Result) {
+ S.CallStackDepth--;
+ const T &Ret = S.Stk.pop<T>();
+
+ 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<T>(Ret);
+ } else {
+ delete S.Current;
+ S.Current = nullptr;
+ if (!ReturnValue<T>(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<bool>()) {
+ PC += Offset;
+ }
+ return true;
+}
+
+static bool Jf(InterpState &S, CodePtr &PC, int32_t Offset) {
+ if (!S.Stk.pop<bool>()) {
+ 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<CXXConstructorDecl>(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<CXXThisExpr>(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<Opcode>();
+ 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 000000000000..8934efa13b9c
--- /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 <limits>
+#include <vector>
+#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 <typename T> 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 <typename T> inline bool IsTrue(const T &V) { return !V.isZero(); }
+
+//===----------------------------------------------------------------------===//
+// Add, Sub, Mul
+//===----------------------------------------------------------------------===//
+
+template <typename T, bool (*OpFW)(T, T, unsigned, T *),
+ template <typename U> 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<T>(Result);
+ return true;
+ }
+
+ // If for some reason evaluation continues, use the truncated results.
+ S.Stk.push<T>(Result);
+
+ // Slow path - compute the result using another bit of precision.
+ APSInt Value = OpAP<APSInt>()(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 <PrimType Name, class T = typename PrimConv<Name>::T>
+bool Add(InterpState &S, CodePtr OpPC) {
+ const T &RHS = S.Stk.pop<T>();
+ const T &LHS = S.Stk.pop<T>();
+ const unsigned Bits = RHS.bitWidth() + 1;
+ return AddSubMulHelper<T, T::add, std::plus>(S, OpPC, Bits, LHS, RHS);
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool Sub(InterpState &S, CodePtr OpPC) {
+ const T &RHS = S.Stk.pop<T>();
+ const T &LHS = S.Stk.pop<T>();
+ const unsigned Bits = RHS.bitWidth() + 1;
+ return AddSubMulHelper<T, T::sub, std::minus>(S, OpPC, Bits, LHS, RHS);
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool Mul(InterpState &S, CodePtr OpPC) {
+ const T &RHS = S.Stk.pop<T>();
+ const T &LHS = S.Stk.pop<T>();
+ const unsigned Bits = RHS.bitWidth() * 2;
+ return AddSubMulHelper<T, T::mul, std::multiplies>(S, OpPC, Bits, LHS, RHS);
+}
+
+//===----------------------------------------------------------------------===//
+// EQ, NE, GT, GE, LT, LE
+//===----------------------------------------------------------------------===//
+
+using CompareFn = llvm::function_ref<bool(ComparisonCategoryResult)>;
+
+template <typename T>
+bool CmpHelper(InterpState &S, CodePtr OpPC, CompareFn Fn) {
+ using BoolT = PrimConv<PT_Bool>::T;
+ const T &RHS = S.Stk.pop<T>();
+ const T &LHS = S.Stk.pop<T>();
+ S.Stk.push<BoolT>(BoolT::from(Fn(LHS.compare(RHS))));
+ return true;
+}
+
+template <typename T>
+bool CmpHelperEQ(InterpState &S, CodePtr OpPC, CompareFn Fn) {
+ return CmpHelper<T>(S, OpPC, Fn);
+}
+
+template <>
+inline bool CmpHelper<Pointer>(InterpState &S, CodePtr OpPC, CompareFn Fn) {
+ using BoolT = PrimConv<PT_Bool>::T;
+ const Pointer &RHS = S.Stk.pop<Pointer>();
+ const Pointer &LHS = S.Stk.pop<Pointer>();
+
+ 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>(BoolT::from(Fn(Compa