diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index 739fd38a9a68d..ea84e31a6fa98 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -73,3 +73,8 @@ f6d557ee34b6bbdb1dc32f29e34b4a4a8ad35e81 # [libc++] Format the code base (#74334) 9783f28cbb155e4a8d49c12e1c60ce14dcfaf0c7 + +# [RFC] compiler-rt builtins cleanup and refactoring +082b89b25faae3e45a023caf51b65ca0f02f377f +0ba22f51d128bee9d69756c56c4678097270e10b +84da0e1bb75f8666cf222d2f600f37bebb9ea389 diff --git a/.github/new-prs-labeler.yml b/.github/new-prs-labeler.yml index b42bcb1887fb6..75105b877d23d 100644 --- a/.github/new-prs-labeler.yml +++ b/.github/new-prs-labeler.yml @@ -75,12 +75,49 @@ mc: clang:driver: - clang/*/Driver/** +compiler-rt:asan: + - compiler-rt/lib/asan/** + - compiler-rt/include/sanitizer/asan_interface.h + - compiler-rt/test/asan/** + - compiler-rt/lib/asan_abi/** + - compiler-rt/test/asan_abi/** + +compiler-rt:builtins: + - compiler-rt/lib/builtins/** + - compiler-rt/test/builtins/** + +compiler-rt:cfi: + - compiler-rt/lib/cfi/** + - compiler-rt/test/cfi/** + +compiler-rt:fuzzer: + - compiler-rt/lib/fuzzer/** + - compiler-rt/include/fuzzer/** + - compiler-rt/test/fuzzer/** + +compiler-rt:hwasan: + - compiler-rt/lib/hwasan/** + - compiler-rt/include/sanitizer/hwasan_interface.h + - compiler-rt/test/hwasan/** + +compiler-rt:lsan: + - compiler-rt/lib/lsan/** + - compiler-rt/include/sanitizer/lsan_interface.h + - compiler-rt/test/lsan/** + +compiler-rt:msan: + - compiler-rt/lib/msan/** + - compiler-rt/include/sanitizer/msan_interface.h + - compiler-rt/test/msan/** + compiler-rt:sanitizer: - llvm/lib/Transforms/Instrumentation/*Sanitizer* - compiler-rt/lib/interception/** - compiler-rt/lib/*san*/** + - compiler-rt/include/sanitizer/** - compiler-rt/test/*san*/** - compiler-rt/lib/fuzzer/** + - compiler-rt/include/fuzzer/** - compiler-rt/test/fuzzer/** - compiler-rt/lib/scudo/** - compiler-rt/test/scudo/** @@ -89,6 +126,19 @@ compiler-rt:scudo: - compiler-rt/lib/scudo/** - compiler-rt/test/scudo/** +compiler-rt:tsan: + - compiler-rt/lib/tsan/** + - compiler-rt/include/sanitizer/tsan_interface.h + - compiler-rt/include/sanitizer/tsan_interface_atomic.h + - compiler-rt/test/tsan/** + +compiler-rt:ubsan: + - compiler-rt/lib/ubsan/** + - compiler-rt/include/sanitizer/ubsan_interface.h + - compiler-rt/test/ubsan/** + - compiler-rt/lib/ubsan_minimal/** + - compiler-rt/test/ubsan_minimal/** + xray: - llvm/tools/llvm-xray/** - compiler-rt/*/xray/** diff --git a/.github/workflows/containers/github-action-ci/Dockerfile b/.github/workflows/containers/github-action-ci/Dockerfile index d91a7ad3a9d06..2f86b9ba1ef44 100644 --- a/.github/workflows/containers/github-action-ci/Dockerfile +++ b/.github/workflows/containers/github-action-ci/Dockerfile @@ -2,29 +2,35 @@ FROM docker.io/library/ubuntu:22.04 as base ENV LLVM_SYSROOT=/opt/llvm/ FROM base as toolchain -ENV LLVM_MAJOR=17 -ENV LLVM_VERSION=${LLVM_MAJOR}.0.6 -ENV LLVM_DIRNAME=clang+llvm-${LLVM_VERSION}-x86_64-linux-gnu-ubuntu-22.04 -ENV LLVM_FILENAME=${LLVM_DIRNAME}.tar.xz +ENV LLVM_VERSION=17.0.6 RUN apt-get update && \ apt-get install -y \ - curl \ - xz-utils + wget \ + gcc \ + g++ \ + cmake \ + ninja-build \ + python3 + +RUN wget https://github.com/llvm/llvm-project/archive/refs/tags/llvmorg-$LLVM_VERSION.tar.gz && tar -xf llvmorg-$LLVM_VERSION.tar.gz -RUN mkdir -p $LLVM_SYSROOT/bin/ $LLVM_SYSROOT/lib/ +WORKDIR /llvm-project-llvmorg-$LLVM_VERSION -RUN curl -O -L https://github.com/llvm/llvm-project/releases/download/llvmorg-$LLVM_VERSION/$LLVM_FILENAME +RUN mkdir build -RUN tar -C $LLVM_SYSROOT --strip-components=1 -xJf $LLVM_FILENAME \ - $LLVM_DIRNAME/bin/clang \ - $LLVM_DIRNAME/bin/clang++ \ - $LLVM_DIRNAME/bin/clang-cl \ - $LLVM_DIRNAME/bin/clang-$LLVM_MAJOR \ - $LLVM_DIRNAME/bin/lld \ - $LLVM_DIRNAME/bin/ld.lld \ - $LLVM_DIRNAME/lib/clang/ +RUN cmake -B ./build -G Ninja ./llvm \ + -C ./clang/cmake/caches/BOLT-PGO.cmake \ + -DBOOTSTRAP_LLVM_ENABLE_LLD=ON \ + -DBOOTSTRAP_BOOTSTRAP_LLVM_ENABLE_LLD=ON \ + -DPGO_INSTRUMENT_LTO=Thin \ + -DLLVM_ENABLE_RUNTIMES="compiler-rt" \ + -DCMAKE_INSTALL_PREFIX="$LLVM_SYSROOT" \ + -DLLVM_ENABLE_PROJECTS="bolt;clang;lld;clang-tools-extra" \ + -DLLVM_DISTRIBUTION_COMPONENTS="lld;compiler-rt;clang-format" \ + -DCLANG_DEFAULT_LINKER="lld" +RUN ninja -C ./build stage2-clang-bolt stage2-install-distribution && ninja -C ./build install-distribution && rm -rf ./build FROM base diff --git a/.github/workflows/libcxx-build-and-test.yaml b/.github/workflows/libcxx-build-and-test.yaml index 9579c05f1c5b1..4a881ef5ff56a 100644 --- a/.github/workflows/libcxx-build-and-test.yaml +++ b/.github/workflows/libcxx-build-and-test.yaml @@ -75,7 +75,7 @@ jobs: CC: ${{ matrix.cc }} CXX: ${{ matrix.cxx }} ENABLE_CLANG_TIDY: ${{ matrix.clang_tidy }} - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 # v4.3.0 if: always() with: name: ${{ matrix.config }}-${{ matrix.cxx }}-results @@ -124,10 +124,10 @@ jobs: CC: ${{ matrix.cc }} CXX: ${{ matrix.cxx }} ENABLE_CLANG_TIDY: ${{ matrix.clang_tidy }} - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 # v4.3.0 if: always() # Upload artifacts even if the build or test suite fails with: - name: ${{ matrix.config }}-results + name: ${{ matrix.config }}-${{ matrix.cxx }}-results path: | **/test-results.xml **/*.abilist @@ -189,7 +189,7 @@ jobs: CC: clang-18 CXX: clang++-18 ENABLE_CLANG_TIDY: "OFF" - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 # v4.3.0 if: always() with: name: ${{ matrix.config }}-results diff --git a/.github/workflows/merged-prs.yml b/.github/workflows/merged-prs.yml new file mode 100644 index 0000000000000..37fc6c67f000b --- /dev/null +++ b/.github/workflows/merged-prs.yml @@ -0,0 +1,41 @@ +name: "Add buildbot information to first PRs from new contributors" + +permissions: + contents: read + +on: + # It's safe to use pull_request_target here, because we aren't checking out + # code from the pull request branch. + # See https://securitylab.github.com/research/github-actions-preventing-pwn-requests/ + pull_request_target: + types: + - closed + +jobs: + buildbot_comment: + runs-on: ubuntu-latest + permissions: + pull-requests: write + if: >- + (github.repository == 'llvm/llvm-project') && + (github.event.pull_request.merged == true) + steps: + - name: Checkout Automation Script + uses: actions/checkout@v4 + with: + sparse-checkout: llvm/utils/git/ + ref: main + + - name: Setup Automation Script + working-directory: ./llvm/utils/git/ + run: | + pip install -r requirements.txt + + - name: Add Buildbot information comment + working-directory: ./llvm/utils/git/ + run: | + python3 ./github-automation.py \ + --token '${{ secrets.GITHUB_TOKEN }}' \ + pr-buildbot-information \ + --issue-number "${{ github.event.pull_request.number }}" \ + --author "${{ github.event.pull_request.user.login }}" diff --git a/bolt/include/bolt/Core/BinaryContext.h b/bolt/include/bolt/Core/BinaryContext.h index f0e7a8272ad0e..f1db1fbded6a4 100644 --- a/bolt/include/bolt/Core/BinaryContext.h +++ b/bolt/include/bolt/Core/BinaryContext.h @@ -554,6 +554,9 @@ class BinaryContext { /// Huge page size to use. static constexpr unsigned HugePageSize = 0x200000; + /// Addresses reserved for kernel on x86_64 start at this location. + static constexpr uint64_t KernelStartX86_64 = 0xFFFF'FFFF'8000'0000; + /// Map address to a constant island owner (constant data in code section) std::map AddressToConstantIslandMap; @@ -602,6 +605,9 @@ class BinaryContext { std::unique_ptr MAB; + /// Indicates if the binary is Linux kernel. + bool IsLinuxKernel{false}; + /// Indicates if relocations are available for usage. bool HasRelocations{false}; @@ -665,6 +671,11 @@ class BinaryContext { uint64_t StaleSampleCount{0}; /// the count of matched samples uint64_t MatchedSampleCount{0}; + /// the number of stale functions that have matching number of blocks in + /// the profile + uint64_t NumStaleFuncsWithEqualBlockCount{0}; + /// the number of blocks that have matching size but a differing hash + uint64_t NumStaleBlocksWithEqualIcount{0}; } Stats; // Address of the first allocated segment. diff --git a/bolt/include/bolt/Rewrite/MetadataManager.h b/bolt/include/bolt/Rewrite/MetadataManager.h index 1a4c394ff33bb..efbc74b4daba9 100644 --- a/bolt/include/bolt/Rewrite/MetadataManager.h +++ b/bolt/include/bolt/Rewrite/MetadataManager.h @@ -36,6 +36,9 @@ class MetadataManager { /// Execute metadata initializers after CFG was constructed for functions. void runInitializersPostCFG(); + /// Run finalization step of rewriters before the binary is emitted. + void runFinalizersPreEmit(); + /// Run finalization step of rewriters after code has been emitted. void runFinalizersAfterEmit(); }; diff --git a/bolt/include/bolt/Rewrite/MetadataRewriter.h b/bolt/include/bolt/Rewrite/MetadataRewriter.h index 12908089db428..1e7e0381c1e98 100644 --- a/bolt/include/bolt/Rewrite/MetadataRewriter.h +++ b/bolt/include/bolt/Rewrite/MetadataRewriter.h @@ -52,6 +52,9 @@ class MetadataRewriter { /// Run the rewriter once the functions are in CFG state. virtual Error postCFGInitializer() { return Error::success(); } + /// Run the pass before the binary is emitted. + virtual Error preEmitFinalizer() { return Error::success(); } + /// Finalize section contents based on the new context after the new code is /// emitted. virtual Error postEmitFinalizer() { return Error::success(); } diff --git a/bolt/include/bolt/Rewrite/RewriteInstance.h b/bolt/include/bolt/Rewrite/RewriteInstance.h index 495a3f1d7b7b4..170da78846b8f 100644 --- a/bolt/include/bolt/Rewrite/RewriteInstance.h +++ b/bolt/include/bolt/Rewrite/RewriteInstance.h @@ -183,6 +183,9 @@ class RewriteInstance { /// Process metadata in special sections after CFG is built for functions. void processMetadataPostCFG(); + /// Make changes to metadata before the binary is emitted. + void finalizeMetadataPreEmit(); + /// Update debug and other auxiliary information in the file. void updateMetadata(); @@ -261,6 +264,11 @@ class RewriteInstance { void createPLTBinaryFunction(uint64_t TargetAddress, uint64_t EntryAddress, uint64_t EntrySize); + /// Disassemble PLT instruction. + void disassemblePLTInstruction(const BinarySection &Section, + uint64_t InstrOffset, MCInst &Instruction, + uint64_t &InstrSize); + /// Disassemble aarch64-specific .plt \p Section auxiliary function void disassemblePLTSectionAArch64(BinarySection &Section); diff --git a/bolt/include/bolt/Utils/CommandLineOpts.h b/bolt/include/bolt/Utils/CommandLineOpts.h index 7b654f19f6d45..30e8bd777b3ca 100644 --- a/bolt/include/bolt/Utils/CommandLineOpts.h +++ b/bolt/include/bolt/Utils/CommandLineOpts.h @@ -18,7 +18,6 @@ namespace opts { extern bool HeatmapMode; -extern bool LinuxKernelMode; extern llvm::cl::OptionCategory BoltCategory; extern llvm::cl::OptionCategory BoltDiffCategory; diff --git a/bolt/lib/Passes/BinaryPasses.cpp b/bolt/lib/Passes/BinaryPasses.cpp index 955cd3726ad41..8505d37491415 100644 --- a/bolt/lib/Passes/BinaryPasses.cpp +++ b/bolt/lib/Passes/BinaryPasses.cpp @@ -1420,6 +1420,12 @@ void PrintProgramStats::runOnFunctions(BinaryContext &BC) { if (NumAllStaleFunctions) { const float PctStale = NumAllStaleFunctions / (float)NumAllProfiledFunctions * 100.0f; + const float PctStaleFuncsWithEqualBlockCount = + (float)BC.Stats.NumStaleFuncsWithEqualBlockCount / + NumAllStaleFunctions * 100.0f; + const float PctStaleBlocksWithEqualIcount = + (float)BC.Stats.NumStaleBlocksWithEqualIcount / + BC.Stats.NumStaleBlocks * 100.0f; auto printErrorOrWarning = [&]() { if (PctStale > opts::StaleThreshold) errs() << "BOLT-ERROR: "; @@ -1442,6 +1448,16 @@ void PrintProgramStats::runOnFunctions(BinaryContext &BC) { << "%) belong to functions with invalid" " (possibly stale) profile.\n"; } + outs() << "BOLT-INFO: " << BC.Stats.NumStaleFuncsWithEqualBlockCount + << " stale function" + << (BC.Stats.NumStaleFuncsWithEqualBlockCount == 1 ? "" : "s") + << format(" (%.1f%% of all stale)", PctStaleFuncsWithEqualBlockCount) + << " have matching block count.\n"; + outs() << "BOLT-INFO: " << BC.Stats.NumStaleBlocksWithEqualIcount + << " stale block" + << (BC.Stats.NumStaleBlocksWithEqualIcount == 1 ? "" : "s") + << format(" (%.1f%% of all stale)", PctStaleBlocksWithEqualIcount) + << " have matching icount.\n"; if (PctStale > opts::StaleThreshold) { errs() << "BOLT-ERROR: stale functions exceed specified threshold of " << opts::StaleThreshold << "%. Exiting.\n"; diff --git a/bolt/lib/Profile/DataAggregator.cpp b/bolt/lib/Profile/DataAggregator.cpp index be1e348b338f0..6a64bcde911e6 100644 --- a/bolt/lib/Profile/DataAggregator.cpp +++ b/bolt/lib/Profile/DataAggregator.cpp @@ -524,7 +524,7 @@ Error DataAggregator::preprocessProfile(BinaryContext &BC) { ErrorCallback(ReturnCode, ErrBuf); }; - if (opts::LinuxKernelMode) { + if (BC.IsLinuxKernel) { // Current MMap parsing logic does not work with linux kernel. // MMap entries for linux kernel uses PERF_RECORD_MMAP // format instead of typical PERF_RECORD_MMAP2 format. @@ -1056,7 +1056,7 @@ ErrorOr DataAggregator::parseBranchSample() { if (std::error_code EC = PIDRes.getError()) return EC; auto MMapInfoIter = BinaryMMapInfo.find(*PIDRes); - if (!opts::LinuxKernelMode && MMapInfoIter == BinaryMMapInfo.end()) { + if (!BC->IsLinuxKernel && MMapInfoIter == BinaryMMapInfo.end()) { consumeRestOfLine(); return make_error_code(errc::no_such_process); } @@ -1277,7 +1277,7 @@ std::error_code DataAggregator::printLBRHeatMap() { NamedRegionTimer T("parseBranch", "Parsing branch events", TimerGroupName, TimerGroupDesc, opts::TimeAggregator); - if (opts::LinuxKernelMode) { + if (BC->IsLinuxKernel) { opts::HeatmapMaxAddress = 0xffffffffffffffff; opts::HeatmapMinAddress = KernelBaseAddr; } diff --git a/bolt/lib/Profile/StaleProfileMatching.cpp b/bolt/lib/Profile/StaleProfileMatching.cpp index 26180f1321477..631ccaec6ae61 100644 --- a/bolt/lib/Profile/StaleProfileMatching.cpp +++ b/bolt/lib/Profile/StaleProfileMatching.cpp @@ -418,6 +418,7 @@ void matchWeightsByHashes(BinaryContext &BC, if (MatchedBlock == nullptr && YamlBB.Index == 0) MatchedBlock = Blocks[0]; if (MatchedBlock != nullptr) { + const BinaryBasicBlock *BB = BlockOrder[MatchedBlock->Index - 1]; MatchedBlocks[YamlBB.Index] = MatchedBlock; BlendedBlockHash BinHash = BlendedHashes[MatchedBlock->Index - 1]; LLVM_DEBUG(dbgs() << "Matched yaml block (bid = " << YamlBB.Index << ")" @@ -433,6 +434,8 @@ void matchWeightsByHashes(BinaryContext &BC, } else { LLVM_DEBUG(dbgs() << " loose match\n"); } + if (YamlBB.NumInstructions == BB->size()) + ++BC.Stats.NumStaleBlocksWithEqualIcount; } else { LLVM_DEBUG( dbgs() << "Couldn't match yaml block (bid = " << YamlBB.Index << ")" diff --git a/bolt/lib/Profile/YAMLProfileReader.cpp b/bolt/lib/Profile/YAMLProfileReader.cpp index a4a401fd3cabf..e4673f6e3c301 100644 --- a/bolt/lib/Profile/YAMLProfileReader.cpp +++ b/bolt/lib/Profile/YAMLProfileReader.cpp @@ -246,20 +246,20 @@ bool YAMLProfileReader::parseFunctionProfile( ProfileMatched &= !MismatchedBlocks && !MismatchedCalls && !MismatchedEdges; - if (ProfileMatched) - BF.markProfiled(YamlBP.Header.Flags); + if (!ProfileMatched) { + if (opts::Verbosity >= 1) + errs() << "BOLT-WARNING: " << MismatchedBlocks << " blocks, " + << MismatchedCalls << " calls, and " << MismatchedEdges + << " edges in profile did not match function " << BF << '\n'; - if (!ProfileMatched && opts::Verbosity >= 1) - errs() << "BOLT-WARNING: " << MismatchedBlocks << " blocks, " - << MismatchedCalls << " calls, and " << MismatchedEdges - << " edges in profile did not match function " << BF << '\n'; + if (YamlBF.NumBasicBlocks != BF.size()) + ++BC.Stats.NumStaleFuncsWithEqualBlockCount; - if (!ProfileMatched && opts::InferStaleProfile) { - if (inferStaleProfile(BF, YamlBF)) { + if (opts::InferStaleProfile && inferStaleProfile(BF, YamlBF)) ProfileMatched = true; - BF.markProfiled(YamlBP.Header.Flags); - } } + if (ProfileMatched) + BF.markProfiled(YamlBP.Header.Flags); return ProfileMatched; } diff --git a/bolt/lib/Rewrite/LinuxKernelRewriter.cpp b/bolt/lib/Rewrite/LinuxKernelRewriter.cpp index 2f449da6206f2..c8674d6b837ad 100644 --- a/bolt/lib/Rewrite/LinuxKernelRewriter.cpp +++ b/bolt/lib/Rewrite/LinuxKernelRewriter.cpp @@ -95,7 +95,7 @@ class LinuxKernelRewriter final : public MetadataRewriter { return 1; if (IP > Other.IP) return 0; - return ORC == NullORC; + return ORC == NullORC && Other.ORC != NullORC; } }; @@ -163,14 +163,18 @@ class LinuxKernelRewriter final : public MetadataRewriter { return Error::success(); } - Error postEmitFinalizer() override { - updateLKMarkers(); - + Error preEmitFinalizer() override { if (Error E = rewriteORCTables()) return E; return Error::success(); } + + Error postEmitFinalizer() override { + updateLKMarkers(); + + return Error::success(); + } }; Error LinuxKernelRewriter::markInstructions() { @@ -203,9 +207,7 @@ void LinuxKernelRewriter::insertLKMarker(uint64_t PC, uint64_t SectionOffset, } void LinuxKernelRewriter::processLKSections() { - assert(opts::LinuxKernelMode && - "process Linux Kernel special sections and their relocations only in " - "linux kernel mode.\n"); + assert(BC.IsLinuxKernel && "Linux kernel binary expected."); processLKExTable(); processLKPCIFixup(); @@ -286,8 +288,9 @@ void LinuxKernelRewriter::processLKExTable() { void LinuxKernelRewriter::processLKPCIFixup() { ErrorOr SectionOrError = BC.getUniqueSectionByName(".pci_fixup"); - assert(SectionOrError && - ".pci_fixup section not found in Linux Kernel binary"); + if (!SectionOrError) + return; + const uint64_t SectionSize = SectionOrError->getSize(); const uint64_t SectionAddress = SectionOrError->getAddress(); assert((SectionSize % 16) == 0 && ".pci_fixup size is not a multiple of 16"); diff --git a/bolt/lib/Rewrite/MetadataManager.cpp b/bolt/lib/Rewrite/MetadataManager.cpp index 059b43f83328a..4ce44820d9eca 100644 --- a/bolt/lib/Rewrite/MetadataManager.cpp +++ b/bolt/lib/Rewrite/MetadataManager.cpp @@ -44,6 +44,18 @@ void MetadataManager::runInitializersPostCFG() { } } +void MetadataManager::runFinalizersPreEmit() { + for (auto &Rewriter : Rewriters) { + LLVM_DEBUG(dbgs() << "BOLT-DEBUG: invoking " << Rewriter->getName() + << " before emitting binary context\n"); + if (Error E = Rewriter->preEmitFinalizer()) { + errs() << "BOLT-ERROR: while running " << Rewriter->getName() + << " before emit: " << toString(std::move(E)) << '\n'; + exit(1); + } + } +} + void MetadataManager::runFinalizersAfterEmit() { for (auto &Rewriter : Rewriters) { LLVM_DEBUG(dbgs() << "BOLT-DEBUG: invoking " << Rewriter->getName() diff --git a/bolt/lib/Rewrite/RewriteInstance.cpp b/bolt/lib/Rewrite/RewriteInstance.cpp index f5a8a5b716874..0fa5dcee64a32 100644 --- a/bolt/lib/Rewrite/RewriteInstance.cpp +++ b/bolt/lib/Rewrite/RewriteInstance.cpp @@ -512,6 +512,9 @@ Error RewriteInstance::discoverStorage() { Phdr.p_offset, Phdr.p_filesz, Phdr.p_align}; + if (BC->TheTriple->getArch() == llvm::Triple::x86_64 && + Phdr.p_vaddr >= BinaryContext::KernelStartX86_64) + BC->IsLinuxKernel = true; break; case ELF::PT_INTERP: BC->HasInterpHeader = true; @@ -519,6 +522,9 @@ Error RewriteInstance::discoverStorage() { } } + if (BC->IsLinuxKernel) + outs() << "BOLT-INFO: Linux kernel binary detected\n"; + for (const SectionRef &Section : InputFile->sections()) { Expected SectionNameOrErr = Section.getName(); if (Error E = SectionNameOrErr.takeError()) @@ -562,7 +568,7 @@ Error RewriteInstance::discoverStorage() { if (opts::Hugify && !BC->HasFixedLoadAddress) NextAvailableAddress += BC->PageAlign; - if (!opts::UseGnuStack) { + if (!opts::UseGnuStack && !BC->IsLinuxKernel) { // This is where the black magic happens. Creating PHDR table in a segment // other than that containing ELF header is tricky. Some loaders and/or // parts of loaders will apply e_phoff from ELF header assuming both are in @@ -742,6 +748,8 @@ Error RewriteInstance::run() { runOptimizationPasses(); + finalizeMetadataPreEmit(); + emitAndLink(); updateMetadata(); @@ -749,12 +757,11 @@ Error RewriteInstance::run() { if (opts::Instrument && !BC->IsStaticExecutable) updateRtFiniReloc(); - if (opts::LinuxKernelMode) { - errs() << "BOLT-WARNING: not writing the output file for Linux Kernel\n"; - return Error::success(); - } else if (opts::OutputFilename == "/dev/null") { + if (opts::OutputFilename == "/dev/null") { outs() << "BOLT-INFO: skipping writing final binary to disk\n"; return Error::success(); + } else if (BC->IsLinuxKernel) { + errs() << "BOLT-WARNING: Linux kernel support is experimental\n"; } // Rewrite allocatable contents and copy non-allocatable parts with mods. @@ -1282,7 +1289,7 @@ void RewriteInstance::discoverFileObjects() { } } - if (!opts::LinuxKernelMode) { + if (!BC->IsLinuxKernel) { // Read all relocations now that we have binary functions mapped. processRelocations(); } @@ -1462,25 +1469,29 @@ void RewriteInstance::createPLTBinaryFunction(uint64_t TargetAddress, setPLTSymbol(BF, Symbol->getName()); } -void RewriteInstance::disassemblePLTSectionAArch64(BinarySection &Section) { +void RewriteInstance::disassemblePLTInstruction(const BinarySection &Section, + uint64_t InstrOffset, + MCInst &Instruction, + uint64_t &InstrSize) { const uint64_t SectionAddress = Section.getAddress(); const uint64_t SectionSize = Section.getSize(); StringRef PLTContents = Section.getContents(); ArrayRef PLTData( reinterpret_cast(PLTContents.data()), SectionSize); - auto disassembleInstruction = [&](uint64_t InstrOffset, MCInst &Instruction, - uint64_t &InstrSize) { - const uint64_t InstrAddr = SectionAddress + InstrOffset; - if (!BC->DisAsm->getInstruction(Instruction, InstrSize, - PLTData.slice(InstrOffset), InstrAddr, - nulls())) { - errs() << "BOLT-ERROR: unable to disassemble instruction in PLT section " - << Section.getName() << " at offset 0x" - << Twine::utohexstr(InstrOffset) << '\n'; - exit(1); - } - }; + const uint64_t InstrAddr = SectionAddress + InstrOffset; + if (!BC->DisAsm->getInstruction(Instruction, InstrSize, + PLTData.slice(InstrOffset), InstrAddr, + nulls())) { + errs() << "BOLT-ERROR: unable to disassemble instruction in PLT section " + << Section.getName() << formatv(" at offset {0:x}\n", InstrOffset); + exit(1); + } +} + +void RewriteInstance::disassemblePLTSectionAArch64(BinarySection &Section) { + const uint64_t SectionAddress = Section.getAddress(); + const uint64_t SectionSize = Section.getSize(); uint64_t InstrOffset = 0; // Locate new plt entry @@ -1492,7 +1503,7 @@ void RewriteInstance::disassemblePLTSectionAArch64(BinarySection &Section) { uint64_t InstrSize; // Loop through entry instructions while (InstrOffset < SectionSize) { - disassembleInstruction(InstrOffset, Instruction, InstrSize); + disassemblePLTInstruction(Section, InstrOffset, Instruction, InstrSize); EntrySize += InstrSize; if (!BC->MIB->isIndirectBranch(Instruction)) { Instructions.emplace_back(Instruction); @@ -1513,7 +1524,7 @@ void RewriteInstance::disassemblePLTSectionAArch64(BinarySection &Section) { // Skip nops if any while (InstrOffset < SectionSize) { - disassembleInstruction(InstrOffset, Instruction, InstrSize); + disassemblePLTInstruction(Section, InstrOffset, Instruction, InstrSize); if (!BC->MIB->isNoop(Instruction)) break; @@ -1570,29 +1581,13 @@ void RewriteInstance::disassemblePLTSectionX86(BinarySection &Section, uint64_t EntrySize) { const uint64_t SectionAddress = Section.getAddress(); const uint64_t SectionSize = Section.getSize(); - StringRef PLTContents = Section.getContents(); - ArrayRef PLTData( - reinterpret_cast(PLTContents.data()), SectionSize); - - auto disassembleInstruction = [&](uint64_t InstrOffset, MCInst &Instruction, - uint64_t &InstrSize) { - const uint64_t InstrAddr = SectionAddress + InstrOffset; - if (!BC->DisAsm->getInstruction(Instruction, InstrSize, - PLTData.slice(InstrOffset), InstrAddr, - nulls())) { - errs() << "BOLT-ERROR: unable to disassemble instruction in PLT section " - << Section.getName() << " at offset 0x" - << Twine::utohexstr(InstrOffset) << '\n'; - exit(1); - } - }; for (uint64_t EntryOffset = 0; EntryOffset + EntrySize <= SectionSize; EntryOffset += EntrySize) { MCInst Instruction; uint64_t InstrSize, InstrOffset = EntryOffset; while (InstrOffset < EntryOffset + EntrySize) { - disassembleInstruction(InstrOffset, Instruction, InstrSize); + disassemblePLTInstruction(Section, InstrOffset, Instruction, InstrSize); // Check if the entry size needs adjustment. if (EntryOffset == 0 && BC->MIB->isTerminateBranch(Instruction) && EntrySize == 8) @@ -1811,8 +1806,6 @@ Error RewriteInstance::readSpecialSections() { << "\n"); if (isDebugSection(SectionName)) HasDebugInfo = true; - if (isKSymtabSection(SectionName)) - opts::LinuxKernelMode = true; } // Set IsRelro section attribute based on PT_GNU_RELRO segment. @@ -1854,6 +1847,11 @@ Error RewriteInstance::readSpecialSections() { BC->HasRelocations = HasTextRelocations && (opts::RelocationMode != cl::BOU_FALSE); + if (BC->IsLinuxKernel && BC->HasRelocations) { + outs() << "BOLT-INFO: disabling relocation mode for Linux kernel\n"; + BC->HasRelocations = false; + } + BC->IsStripped = !HasSymbolTable; if (BC->IsStripped && !opts::AllowStripped) { @@ -3035,7 +3033,7 @@ void RewriteInstance::preprocessProfileData() { } void RewriteInstance::initializeMetadataManager() { - if (opts::LinuxKernelMode) + if (BC->IsLinuxKernel) MetadataManager.registerRewriter(createLinuxKernelRewriter(*BC)); MetadataManager.registerRewriter(createPseudoProbeRewriter(*BC)); @@ -3416,6 +3414,10 @@ void RewriteInstance::emitAndLink() { } } +void RewriteInstance::finalizeMetadataPreEmit() { + MetadataManager.runFinalizersPreEmit(); +} + void RewriteInstance::updateMetadata() { MetadataManager.runFinalizersAfterEmit(); @@ -4143,10 +4145,10 @@ RewriteInstance::getOutputSections(ELFObjectFile *File, // Keep track of section header entries attached to the corresponding section. std::vector> OutputSections; - auto addSection = [&](const ELFShdrTy &Section, BinarySection *BinSec) { + auto addSection = [&](const ELFShdrTy &Section, BinarySection &BinSec) { ELFShdrTy NewSection = Section; - NewSection.sh_name = SHStrTab.getOffset(BinSec->getOutputName()); - OutputSections.emplace_back(BinSec, std::move(NewSection)); + NewSection.sh_name = SHStrTab.getOffset(BinSec.getOutputName()); + OutputSections.emplace_back(&BinSec, std::move(NewSection)); }; // Copy over entries for original allocatable sections using modified name. @@ -4164,7 +4166,7 @@ RewriteInstance::getOutputSections(ELFObjectFile *File, BinarySection *BinSec = BC->getSectionForSectionRef(SecRef); assert(BinSec && "Matching BinarySection should exist."); - addSection(Section, BinSec); + addSection(Section, *BinSec); } for (BinarySection &Section : BC->allocatableSections()) { @@ -4191,7 +4193,7 @@ RewriteInstance::getOutputSections(ELFObjectFile *File, NewSection.sh_link = 0; NewSection.sh_info = 0; NewSection.sh_addralign = Section.getAlignment(); - addSection(NewSection, &Section); + addSection(NewSection, Section); } // Sort all allocatable sections by their offset. @@ -4205,19 +4207,19 @@ RewriteInstance::getOutputSections(ELFObjectFile *File, for (auto &SectionKV : OutputSections) { ELFShdrTy &Section = SectionKV.second; - // TBSS section does not take file or memory space. Ignore it for layout - // purposes. - if (Section.sh_type == ELF::SHT_NOBITS && (Section.sh_flags & ELF::SHF_TLS)) + // Ignore TLS sections as they don't take any space in the file. + if (Section.sh_type == ELF::SHT_NOBITS) continue; + // Note that address continuity is not guaranteed as sections could be + // placed in different loadable segments. if (PrevSection && - PrevSection->sh_addr + PrevSection->sh_size > Section.sh_addr) { - if (opts::Verbosity > 1) + PrevSection->sh_offset + PrevSection->sh_size > Section.sh_offset) { + if (opts::Verbosity > 1) { outs() << "BOLT-INFO: adjusting size for section " << PrevBinSec->getOutputName() << '\n'; - PrevSection->sh_size = Section.sh_addr > PrevSection->sh_addr - ? Section.sh_addr - PrevSection->sh_addr - : 0; + } + PrevSection->sh_size = Section.sh_offset - PrevSection->sh_offset; } PrevSection = &Section; @@ -4251,7 +4253,7 @@ RewriteInstance::getOutputSections(ELFObjectFile *File, if (NewSection.sh_type == ELF::SHT_SYMTAB) NewSection.sh_info = NumLocalSymbols; - addSection(NewSection, BinSec); + addSection(NewSection, *BinSec); LastFileOffset = BinSec->getOutputFileOffset(); } @@ -4276,7 +4278,7 @@ RewriteInstance::getOutputSections(ELFObjectFile *File, NewSection.sh_info = 0; NewSection.sh_addralign = Section.getAlignment(); - addSection(NewSection, &Section); + addSection(NewSection, Section); } // Assign indices to sections. @@ -4359,8 +4361,10 @@ void RewriteInstance::patchELFSectionHeaderTable(ELFObjectFile *File) { assert((NewEhdr.e_entry || !Obj.getHeader().e_entry) && "cannot find new address for entry point"); } - NewEhdr.e_phoff = PHDRTableOffset; - NewEhdr.e_phnum = Phnum; + if (PHDRTableOffset) { + NewEhdr.e_phoff = PHDRTableOffset; + NewEhdr.e_phnum = Phnum; + } NewEhdr.e_shoff = SHTOffset; NewEhdr.e_shnum = OutputSections.size(); NewEhdr.e_shstrndx = NewSectionIndex[NewEhdr.e_shstrndx]; @@ -5495,7 +5499,8 @@ void RewriteInstance::rewriteFile() { addBATSection(); // Patch program header table. - patchELFPHDRTable(); + if (!BC->IsLinuxKernel) + patchELFPHDRTable(); // Finalize memory image of section string table. finalizeSectionStringTable(); diff --git a/bolt/lib/Utils/CommandLineOpts.cpp b/bolt/lib/Utils/CommandLineOpts.cpp index a1df5de262340..e910fa4f86722 100644 --- a/bolt/lib/Utils/CommandLineOpts.cpp +++ b/bolt/lib/Utils/CommandLineOpts.cpp @@ -29,7 +29,6 @@ const char *BoltRevision = namespace opts { bool HeatmapMode = false; -bool LinuxKernelMode = false; cl::OptionCategory BoltCategory("BOLT generic options"); cl::OptionCategory BoltDiffCategory("BOLTDIFF generic options"); diff --git a/bolt/test/X86/orc_unwind.s b/bolt/test/X86/linux-orc.s similarity index 76% rename from bolt/test/X86/orc_unwind.s rename to bolt/test/X86/linux-orc.s index 4f8b9332dfef1..bd652eafa23dd 100644 --- a/bolt/test/X86/orc_unwind.s +++ b/bolt/test/X86/linux-orc.s @@ -1,13 +1,14 @@ # REQUIRES: system-linux -## Check that BOLT correctly reads ORC unwind information used by Linux Kernel. +## Check that BOLT correctly reads ORC unwind information used by Linux kernel. # RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown %s -o %t.o -# RUN: %clang %cflags %t.o -o %t.exe +# RUN: %clang %cflags -nostdlib %t.o -o %t.exe -Wl,--image-base=0xffffffff80000000,--no-dynamic-linker,--no-eh-frame-hdr # RUN: llvm-bolt %t.exe --print-normalized --dump-orc --print-orc -o %t.out \ # RUN: |& FileCheck %s +# CHECK: BOLT-INFO: Linux kernel binary detected # CHECK: BOLT-INFO: ORC unwind information: # CHECK-NEXT: {sp: 8, bp: 0, info: 0x5}: _start # CHECK-NEXT: {sp: 0, bp: 0, info: 0x0}: _start @@ -15,24 +16,22 @@ # CHECK-NEXT: {sp: 16, bp: -16, info: 0x15}: foo # CHECK-NEXT: {sp: 16, bp: -16, info: 0x14}: foo # CHECK-NEXT: {sp: 8, bp: 0, info: 0x5}: foo +# CHECK-NEXT: {sp: 0, bp: 0, info: 0x0}: bar # CHECK-NEXT: {sp: 0, bp: 0, info: 0x0}: bar .text .globl _start .type _start, %function _start: - .cfi_startproc call foo # CHECK: callq foo # ORC: {sp: 8, bp: 0, info: 0x5} ret - .cfi_endproc .size _start, .-_start .globl foo .type foo, %function foo: - .cfi_startproc push %rbp # CHECK: pushq %rbp # ORC: {sp: 8, bp: 0, info: 0x5} .L1: @@ -44,24 +43,23 @@ foo: .L3: ret # CHECK: retq # ORC: {sp: 8, bp: 0, info: 0x5} - .cfi_endproc .size foo, .-foo bar: - .cfi_startproc ret -# Same ORC info propagated from foo above. +## Same ORC info propagated from foo above. # CHECK: retq # ORC: {sp: 8, bp: 0, info: 0x5} .L4: - .cfi_endproc .size bar, .-bar +# CHECK: BOLT-WARNING: Linux kernel support is experimental + .section .orc_unwind,"a",@progbits .align 4 .section .orc_unwind_ip,"a",@progbits .align 4 -# ORC for _start +## ORC for _start. .section .orc_unwind .2byte 8 .2byte 0 @@ -76,7 +74,7 @@ bar: .section .orc_unwind_ip .long foo - . -# ORC for foo +## ORC for foo. .section .orc_unwind .2byte 8 .2byte 0 @@ -112,7 +110,21 @@ bar: .section .orc_unwind_ip .long .L4 - . -# Fake Linux Kernel sections +## Duplicate terminator entries to test ORC reader. + .section .orc_unwind + .2byte 0 + .2byte 0 + .2byte 0 + .section .orc_unwind_ip + .long .L4 - . + + .section .orc_unwind + .2byte 0 + .2byte 0 + .2byte 0 + .section .orc_unwind_ip + .long .L4 - . + +## Fake Linux Kernel sections. .section __ksymtab,"a",@progbits .section __ksymtab_gpl,"a",@progbits - .section .pci_fixup,"a",@progbits diff --git a/bolt/test/X86/phdr-out-of-order.test b/bolt/test/X86/phdr-out-of-order.test new file mode 100644 index 0000000000000..1e2fc2a5a4cd8 --- /dev/null +++ b/bolt/test/X86/phdr-out-of-order.test @@ -0,0 +1,52 @@ +## Check that llvm-bolt correctly processes a binary with program headers and +## corresponding sections specified in non-ascending address order. + +RUN: split-file %s %t +RUN: yaml2obj %t/yaml -o %t.exe --max-size=0 +RUN: llvm-bolt %t.exe -o %t.bolt --allow-stripped +RUN: llvm-readelf -WS %t.bolt | FileCheck %s + +CHECK: .a PROGBITS 0000000000400000 [[#%.6x, OFFSET:]] 000001 +CHECK-NEXT: .b PROGBITS 0000000000000000 [[#%.6x, OFFSET+1]] 000001 +CHECK-NEXT: .c PROGBITS 0000000000600000 [[#%.6x, OFFSET+2]] 000001 + +#--- yaml +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +ProgramHeaders: + - Type: PT_LOAD + FirstSec: .a + LastSec: .a + VAddr: 0x400000 + - Type: PT_LOAD + FirstSec: .b + LastSec: .b + VAddr: 0x0 + - Type: PT_LOAD + FirstSec: .c + LastSec: .c + VAddr: 0x600000 +Sections: + - Name: .a + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + Content: 00 + AddressAlign: 0x1 + Address: 0x400000 + - Name: .b + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + Content: 00 + AddressAlign: 0x1 + Address: 0x0 + - Name: .c + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + Content: 00 + AddressAlign: 0x1 + Address: 0x600000 +... diff --git a/clang-tools-extra/clang-tidy/bugprone/ReservedIdentifierCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/ReservedIdentifierCheck.cpp index 7a06df454be99..f6714d056518d 100644 --- a/clang-tools-extra/clang-tidy/bugprone/ReservedIdentifierCheck.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/ReservedIdentifierCheck.cpp @@ -104,7 +104,7 @@ static std::optional getUnderscoreCapitalFixup(StringRef Name) { static bool startsWithUnderscoreInGlobalNamespace(StringRef Name, bool IsInGlobalNamespace, bool IsMacro) { - return !IsMacro && IsInGlobalNamespace && !Name.empty() && Name[0] == '_'; + return !IsMacro && IsInGlobalNamespace && Name.starts_with("_"); } static std::optional diff --git a/clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp b/clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp index d334ab8c437d3..b06a903f92b3e 100644 --- a/clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp @@ -33,7 +33,6 @@ #include "LimitedRandomnessCheck.h" #include "MutatingCopyCheck.h" #include "NonTrivialTypesLibcMemoryCallsCheck.h" -#include "PostfixOperatorCheck.h" #include "ProperlySeededRandomGeneratorCheck.h" #include "SetLongJmpCheck.h" #include "StaticObjectExceptionCheck.h" @@ -239,8 +238,6 @@ class CERTModule : public ClangTidyModule { CheckFactories.registerCheck( "cert-con54-cpp"); // DCL - CheckFactories.registerCheck( - "cert-dcl21-cpp"); CheckFactories.registerCheck("cert-dcl50-cpp"); CheckFactories.registerCheck( "cert-dcl51-cpp"); diff --git a/clang-tools-extra/clang-tidy/cert/CMakeLists.txt b/clang-tools-extra/clang-tidy/cert/CMakeLists.txt index 889180f62fde9..882735c9d1e0d 100644 --- a/clang-tools-extra/clang-tidy/cert/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/cert/CMakeLists.txt @@ -12,7 +12,6 @@ add_clang_library(clangTidyCERTModule LimitedRandomnessCheck.cpp MutatingCopyCheck.cpp NonTrivialTypesLibcMemoryCallsCheck.cpp - PostfixOperatorCheck.cpp ProperlySeededRandomGeneratorCheck.cpp SetLongJmpCheck.cpp StaticObjectExceptionCheck.cpp diff --git a/clang-tools-extra/clang-tidy/cert/PostfixOperatorCheck.cpp b/clang-tools-extra/clang-tidy/cert/PostfixOperatorCheck.cpp deleted file mode 100644 index 8ff63ade7315d..0000000000000 --- a/clang-tools-extra/clang-tidy/cert/PostfixOperatorCheck.cpp +++ /dev/null @@ -1,80 +0,0 @@ -//===--- PostfixOperatorCheck.cpp - clang-tidy-----------------------------===// -// -// 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 "PostfixOperatorCheck.h" -#include "clang/AST/ASTContext.h" -#include "clang/ASTMatchers/ASTMatchFinder.h" -#include "clang/Lex/Lexer.h" - -using namespace clang::ast_matchers; - -namespace clang::tidy::cert { - -void PostfixOperatorCheck::registerMatchers(MatchFinder *Finder) { - Finder->addMatcher(functionDecl(hasAnyOverloadedOperatorName("++", "--"), - unless(isInstantiated())) - .bind("decl"), - this); -} - -void PostfixOperatorCheck::check(const MatchFinder::MatchResult &Result) { - const auto *FuncDecl = Result.Nodes.getNodeAs("decl"); - - bool HasThis = false; - if (const auto *MethodDecl = dyn_cast(FuncDecl)) - HasThis = MethodDecl->isInstance(); - - // Check if the operator is a postfix one. - if (FuncDecl->getNumParams() != (HasThis ? 1 : 2)) - return; - - SourceRange ReturnRange = FuncDecl->getReturnTypeSourceRange(); - SourceLocation Location = ReturnRange.getBegin(); - if (!Location.isValid()) - return; - - QualType ReturnType = FuncDecl->getReturnType(); - - // Warn when the operators return a reference. - if (const auto *RefType = ReturnType->getAs()) { - auto Diag = diag(Location, "overloaded %0 returns a reference instead of a " - "constant object type") - << FuncDecl; - - if (Location.isMacroID() || ReturnType->getAs() || - RefType->getPointeeTypeAsWritten()->getAs()) - return; - - QualType ReplaceType = - ReturnType.getNonReferenceType().getLocalUnqualifiedType(); - // The getReturnTypeSourceRange omits the qualifiers. We do not want to - // duplicate the const. - if (!ReturnType->getPointeeType().isConstQualified()) - ReplaceType.addConst(); - - Diag << FixItHint::CreateReplacement( - ReturnRange, - ReplaceType.getAsString(Result.Context->getPrintingPolicy()) + " "); - - return; - } - - if (ReturnType.isConstQualified() || ReturnType->isBuiltinType() || - ReturnType->isPointerType()) - return; - - auto Diag = - diag(Location, "overloaded %0 returns a non-constant object instead of a " - "constant object type") - << FuncDecl; - - if (!Location.isMacroID() && !ReturnType->getAs()) - Diag << FixItHint::CreateInsertion(Location, "const "); -} - -} // namespace clang::tidy::cert diff --git a/clang-tools-extra/clang-tidy/cert/PostfixOperatorCheck.h b/clang-tools-extra/clang-tidy/cert/PostfixOperatorCheck.h deleted file mode 100644 index 782b9a99ba413..0000000000000 --- a/clang-tools-extra/clang-tidy/cert/PostfixOperatorCheck.h +++ /dev/null @@ -1,34 +0,0 @@ -//===--- PostfixOperatorCheck.h - clang-tidy---------------------*- 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_TOOLS_EXTRA_CLANG_TIDY_CERT_POSTFIX_OPERATOR_H -#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_POSTFIX_OPERATOR_H - -#include "../ClangTidyCheck.h" - -namespace clang::tidy::cert { - -/// Checks if the overloaded postfix ++ and -- operator return a constant -/// object. -/// -/// For the user-facing documentation see: -/// https://clang.llvm.org/extra/clang-tidy/checks/cert/dcl21-cpp.html -class PostfixOperatorCheck : public ClangTidyCheck { -public: - PostfixOperatorCheck(StringRef Name, ClangTidyContext *Context) - : ClangTidyCheck(Name, Context) {} - bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { - return LangOpts.CPlusPlus; - } - void registerMatchers(ast_matchers::MatchFinder *Finder) override; - void check(const ast_matchers::MatchFinder::MatchResult &Result) override; -}; - -} // namespace clang::tidy::cert - -#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_POSTFIX_OPERATOR_H diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/PreferMemberInitializerCheck.cpp b/clang-tools-extra/clang-tidy/cppcoreguidelines/PreferMemberInitializerCheck.cpp index f79a67819bb85..de96c3dc4efef 100644 --- a/clang-tools-extra/clang-tidy/cppcoreguidelines/PreferMemberInitializerCheck.cpp +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/PreferMemberInitializerCheck.cpp @@ -34,27 +34,6 @@ static bool isNoReturnCallStatement(const Stmt *S) { return Func->isNoReturn(); } -static bool isLiteral(const Expr *E) { - return isa(E); -} - -static bool isUnaryExprOfLiteral(const Expr *E) { - if (const auto *UnOp = dyn_cast(E)) - return isLiteral(UnOp->getSubExpr()); - return false; -} - -static bool shouldBeDefaultMemberInitializer(const Expr *Value) { - if (isLiteral(Value) || isUnaryExprOfLiteral(Value)) - return true; - - if (const auto *DRE = dyn_cast(Value)) - return isa(DRE->getDecl()); - - return false; -} - namespace { AST_MATCHER_P(FieldDecl, indexNotLessThan, unsigned, Index) { @@ -166,19 +145,7 @@ isAssignmentToMemberOf(const CXXRecordDecl *Rec, const Stmt *S, PreferMemberInitializerCheck::PreferMemberInitializerCheck( StringRef Name, ClangTidyContext *Context) - : ClangTidyCheck(Name, Context), - IsUseDefaultMemberInitEnabled( - Context->isCheckEnabled("modernize-use-default-member-init")), - UseAssignment( - Options.get("UseAssignment", - OptionsView("modernize-use-default-member-init", - Context->getOptions().CheckOptions, Context) - .get("UseAssignment", false))) {} - -void PreferMemberInitializerCheck::storeOptions( - ClangTidyOptions::OptionMap &Opts) { - Options.store(Opts, "UseAssignment", UseAssignment); -} + : ClangTidyCheck(Name, Context) {} void PreferMemberInitializerCheck::registerMatchers(MatchFinder *Finder) { Finder->addMatcher(cxxConstructorDecl(hasBody(compoundStmt()), @@ -230,139 +197,99 @@ void PreferMemberInitializerCheck::check( updateAssignmentLevel(Field, InitValue, Ctor, AssignedFields); if (!canAdvanceAssignment(AssignedFields[Field])) continue; - const bool IsInDefaultMemberInitializer = - IsUseDefaultMemberInitEnabled && getLangOpts().CPlusPlus11 && - Ctor->isDefaultConstructor() && - (getLangOpts().CPlusPlus20 || !Field->isBitField()) && - !Field->hasInClassInitializer() && - (!isa(Class->getDeclContext()) || - !cast(Class->getDeclContext())->isUnion()) && - shouldBeDefaultMemberInitializer(InitValue); - if (IsInDefaultMemberInitializer) { - bool InvalidFix = false; - SourceLocation FieldEnd = - Lexer::getLocForEndOfToken(Field->getSourceRange().getEnd(), 0, - *Result.SourceManager, getLangOpts()); - InvalidFix |= FieldEnd.isInvalid() || FieldEnd.isMacroID(); - SourceLocation SemiColonEnd; - if (auto NextToken = Lexer::findNextToken( - S->getEndLoc(), *Result.SourceManager, getLangOpts())) - SemiColonEnd = NextToken->getEndLoc(); - else - InvalidFix = true; - auto Diag = - diag(S->getBeginLoc(), "%0 should be initialized in an in-class" - " default member initializer") - << Field; - if (InvalidFix) - continue; - CharSourceRange StmtRange = - CharSourceRange::getCharRange(S->getBeginLoc(), SemiColonEnd); - SmallString<128> Insertion( - {UseAssignment ? " = " : "{", - Lexer::getSourceText(Result.SourceManager->getExpansionRange( - InitValue->getSourceRange()), - *Result.SourceManager, getLangOpts()), - UseAssignment ? "" : "}"}); - - Diag << FixItHint::CreateInsertion(FieldEnd, Insertion) - << FixItHint::CreateRemoval(StmtRange); - - } else { - StringRef InsertPrefix = ""; - bool HasInitAlready = false; - SourceLocation InsertPos; - SourceRange ReplaceRange; - bool AddComma = false; - bool InvalidFix = false; - unsigned Index = Field->getFieldIndex(); - const CXXCtorInitializer *LastInListInit = nullptr; - for (const CXXCtorInitializer *Init : Ctor->inits()) { - if (!Init->isWritten() || Init->isInClassMemberInitializer()) - continue; - if (Init->getMember() == Field) { - HasInitAlready = true; - if (isa(Init->getInit())) - InsertPos = Init->getRParenLoc(); - else { - ReplaceRange = Init->getInit()->getSourceRange(); - } - break; - } - if (Init->isMemberInitializer() && - Index < Init->getMember()->getFieldIndex()) { - InsertPos = Init->getSourceLocation(); - // There are initializers after the one we are inserting, so add a - // comma after this insertion in order to not break anything. - AddComma = true; - break; + StringRef InsertPrefix = ""; + bool HasInitAlready = false; + SourceLocation InsertPos; + SourceRange ReplaceRange; + bool AddComma = false; + bool InvalidFix = false; + unsigned Index = Field->getFieldIndex(); + const CXXCtorInitializer *LastInListInit = nullptr; + for (const CXXCtorInitializer *Init : Ctor->inits()) { + if (!Init->isWritten() || Init->isInClassMemberInitializer()) + continue; + if (Init->getMember() == Field) { + HasInitAlready = true; + if (isa(Init->getInit())) + InsertPos = Init->getRParenLoc(); + else { + ReplaceRange = Init->getInit()->getSourceRange(); } - LastInListInit = Init; + break; } - if (HasInitAlready) { - if (InsertPos.isValid()) - InvalidFix |= InsertPos.isMacroID(); - else - InvalidFix |= ReplaceRange.getBegin().isMacroID() || - ReplaceRange.getEnd().isMacroID(); - } else { - if (InsertPos.isInvalid()) { - if (LastInListInit) { - InsertPos = Lexer::getLocForEndOfToken( - LastInListInit->getRParenLoc(), 0, *Result.SourceManager, - getLangOpts()); - // Inserting after the last constructor initializer, so we need a - // comma. - InsertPrefix = ", "; - } else { - InsertPos = Lexer::getLocForEndOfToken( - Ctor->getTypeSourceInfo() - ->getTypeLoc() - .getAs() - .getLocalRangeEnd(), - 0, *Result.SourceManager, getLangOpts()); - - // If this is first time in the loop, there are no initializers so - // `:` declares member initialization list. If this is a - // subsequent pass then we have already inserted a `:` so continue - // with a comma. - InsertPrefix = FirstToCtorInits ? " : " : ", "; - } - } + if (Init->isMemberInitializer() && + Index < Init->getMember()->getFieldIndex()) { + InsertPos = Init->getSourceLocation(); + // There are initializers after the one we are inserting, so add a + // comma after this insertion in order to not break anything. + AddComma = true; + break; + } + LastInListInit = Init; + } + if (HasInitAlready) { + if (InsertPos.isValid()) InvalidFix |= InsertPos.isMacroID(); + else + InvalidFix |= ReplaceRange.getBegin().isMacroID() || + ReplaceRange.getEnd().isMacroID(); + } else { + if (InsertPos.isInvalid()) { + if (LastInListInit) { + InsertPos = + Lexer::getLocForEndOfToken(LastInListInit->getRParenLoc(), 0, + *Result.SourceManager, getLangOpts()); + // Inserting after the last constructor initializer, so we need a + // comma. + InsertPrefix = ", "; + } else { + InsertPos = Lexer::getLocForEndOfToken( + Ctor->getTypeSourceInfo() + ->getTypeLoc() + .getAs() + .getLocalRangeEnd(), + 0, *Result.SourceManager, getLangOpts()); + + // If this is first time in the loop, there are no initializers so + // `:` declares member initialization list. If this is a + // subsequent pass then we have already inserted a `:` so continue + // with a comma. + InsertPrefix = FirstToCtorInits ? " : " : ", "; + } } + InvalidFix |= InsertPos.isMacroID(); + } - SourceLocation SemiColonEnd; - if (auto NextToken = Lexer::findNextToken( - S->getEndLoc(), *Result.SourceManager, getLangOpts())) - SemiColonEnd = NextToken->getEndLoc(); + SourceLocation SemiColonEnd; + if (auto NextToken = Lexer::findNextToken( + S->getEndLoc(), *Result.SourceManager, getLangOpts())) + SemiColonEnd = NextToken->getEndLoc(); + else + InvalidFix = true; + + auto Diag = diag(S->getBeginLoc(), "%0 should be initialized in a member" + " initializer of the constructor") + << Field; + if (InvalidFix) + continue; + StringRef NewInit = Lexer::getSourceText( + Result.SourceManager->getExpansionRange(InitValue->getSourceRange()), + *Result.SourceManager, getLangOpts()); + if (HasInitAlready) { + if (InsertPos.isValid()) + Diag << FixItHint::CreateInsertion(InsertPos, NewInit); else - InvalidFix = true; - - auto Diag = diag(S->getBeginLoc(), "%0 should be initialized in a member" - " initializer of the constructor") - << Field; - if (InvalidFix) - continue; - StringRef NewInit = Lexer::getSourceText( - Result.SourceManager->getExpansionRange(InitValue->getSourceRange()), - *Result.SourceManager, getLangOpts()); - if (HasInitAlready) { - if (InsertPos.isValid()) - Diag << FixItHint::CreateInsertion(InsertPos, NewInit); - else - Diag << FixItHint::CreateReplacement(ReplaceRange, NewInit); - } else { - SmallString<128> Insertion({InsertPrefix, Field->getName(), "(", - NewInit, AddComma ? "), " : ")"}); - Diag << FixItHint::CreateInsertion(InsertPos, Insertion, - FirstToCtorInits); - FirstToCtorInits = areDiagsSelfContained(); - } - Diag << FixItHint::CreateRemoval( - CharSourceRange::getCharRange(S->getBeginLoc(), SemiColonEnd)); + Diag << FixItHint::CreateReplacement(ReplaceRange, NewInit); + } else { + SmallString<128> Insertion({InsertPrefix, Field->getName(), "(", NewInit, + AddComma ? "), " : ")"}); + Diag << FixItHint::CreateInsertion(InsertPos, Insertion, + FirstToCtorInits); + FirstToCtorInits = areDiagsSelfContained(); } + Diag << FixItHint::CreateRemoval( + CharSourceRange::getCharRange(S->getBeginLoc(), SemiColonEnd)); } } diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/PreferMemberInitializerCheck.h b/clang-tools-extra/clang-tidy/cppcoreguidelines/PreferMemberInitializerCheck.h index 7b39da1fd3c54..b3f8284b435af 100644 --- a/clang-tools-extra/clang-tidy/cppcoreguidelines/PreferMemberInitializerCheck.h +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/PreferMemberInitializerCheck.h @@ -24,12 +24,8 @@ class PreferMemberInitializerCheck : public ClangTidyCheck { bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { return LangOpts.CPlusPlus; } - void storeOptions(ClangTidyOptions::OptionMap &Opts) override; void registerMatchers(ast_matchers::MatchFinder *Finder) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override; - - const bool IsUseDefaultMemberInitEnabled; - const bool UseAssignment; }; } // namespace clang::tidy::cppcoreguidelines diff --git a/clang-tools-extra/clang-tidy/modernize/AvoidCArraysCheck.cpp b/clang-tools-extra/clang-tidy/modernize/AvoidCArraysCheck.cpp index d1b15479ffe7a..89790ea70cf22 100644 --- a/clang-tools-extra/clang-tidy/modernize/AvoidCArraysCheck.cpp +++ b/clang-tools-extra/clang-tidy/modernize/AvoidCArraysCheck.cpp @@ -12,6 +12,8 @@ using namespace clang::ast_matchers; +namespace clang::tidy::modernize { + namespace { AST_MATCHER(clang::TypeLoc, hasValidBeginLoc) { @@ -38,16 +40,32 @@ AST_MATCHER(clang::ParmVarDecl, isArgvOfMain) { } // namespace -namespace clang::tidy::modernize { +AvoidCArraysCheck::AvoidCArraysCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context), + AllowStringArrays(Options.get("AllowStringArrays", false)) {} + +void AvoidCArraysCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { + Options.store(Opts, "AllowStringArrays", AllowStringArrays); +} void AvoidCArraysCheck::registerMatchers(MatchFinder *Finder) { + ast_matchers::internal::Matcher IgnoreStringArrayIfNeededMatcher = + anything(); + if (AllowStringArrays) + IgnoreStringArrayIfNeededMatcher = + unless(typeLoc(loc(hasCanonicalType(incompleteArrayType( + hasElementType(isAnyCharacter())))), + hasParent(varDecl(hasInitializer(stringLiteral()), + unless(parmVarDecl()))))); + Finder->addMatcher( typeLoc(hasValidBeginLoc(), hasType(arrayType()), unless(anyOf(hasParent(parmVarDecl(isArgvOfMain())), hasParent(varDecl(isExternC())), hasParent(fieldDecl( hasParent(recordDecl(isExternCContext())))), - hasAncestor(functionDecl(isExternC()))))) + hasAncestor(functionDecl(isExternC())))), + std::move(IgnoreStringArrayIfNeededMatcher)) .bind("typeloc"), this); } diff --git a/clang-tools-extra/clang-tidy/modernize/AvoidCArraysCheck.h b/clang-tools-extra/clang-tidy/modernize/AvoidCArraysCheck.h index 7099f99c86949..719e88e4b3166 100644 --- a/clang-tools-extra/clang-tidy/modernize/AvoidCArraysCheck.h +++ b/clang-tools-extra/clang-tidy/modernize/AvoidCArraysCheck.h @@ -19,13 +19,19 @@ namespace clang::tidy::modernize { /// http://clang.llvm.org/extra/clang-tidy/checks/modernize/avoid-c-arrays.html class AvoidCArraysCheck : public ClangTidyCheck { public: - AvoidCArraysCheck(StringRef Name, ClangTidyContext *Context) - : ClangTidyCheck(Name, Context) {} + AvoidCArraysCheck(StringRef Name, ClangTidyContext *Context); + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + void storeOptions(ClangTidyOptions::OptionMap &Opts) override; bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { return LangOpts.CPlusPlus11; } - void registerMatchers(ast_matchers::MatchFinder *Finder) override; - void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + std::optional getCheckTraversalKind() const override { + return TK_IgnoreUnlessSpelledInSource; + } + +private: + const bool AllowStringArrays; }; } // namespace clang::tidy::modernize diff --git a/clang-tools-extra/clangd/InlayHints.cpp b/clang-tools-extra/clangd/InlayHints.cpp index c7dce041474a1..671a9c30ffa95 100644 --- a/clang-tools-extra/clangd/InlayHints.cpp +++ b/clang-tools-extra/clangd/InlayHints.cpp @@ -651,16 +651,12 @@ class InlayHintVisitor : public RecursiveASTVisitor { // implied object argument ([over.call.func]), the list of provided // arguments is preceded by the implied object argument for the purposes of // this correspondence... - // - // However, we don't have the implied object argument - // for static operator() per clang::Sema::BuildCallToObjectOfClassType. llvm::ArrayRef Args = {E->getArgs(), E->getNumArgs()}; // We don't have the implied object argument through a function pointer // either. if (const CXXMethodDecl *Method = dyn_cast_or_null(Callee.Decl)) - if (Method->isInstance() && - (IsFunctor || Method->hasCXXExplicitFunctionObjectParameter())) + if (IsFunctor || Method->hasCXXExplicitFunctionObjectParameter()) Args = Args.drop_front(1); processCall(Callee, Args); return true; diff --git a/clang-tools-extra/clangd/TidyFastChecks.inc b/clang-tools-extra/clangd/TidyFastChecks.inc index 9596ba4d725b6..9050ce16127ff 100644 --- a/clang-tools-extra/clangd/TidyFastChecks.inc +++ b/clang-tools-extra/clangd/TidyFastChecks.inc @@ -116,7 +116,6 @@ FAST(cert-con36-c, 2.0) FAST(cert-con54-cpp, 3.0) FAST(cert-dcl03-c, 2.0) FAST(cert-dcl16-c, -1.0) -FAST(cert-dcl21-cpp, 0.0) FAST(cert-dcl37-c, 3.0) FAST(cert-dcl50-cpp, -1.0) FAST(cert-dcl51-cpp, 1.0) diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index fd2cba4e4f463..d654e6c2b60b0 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -106,9 +106,25 @@ New check aliases Changes in existing checks ^^^^^^^^^^^^^^^^^^^^^^^^^^ +- Cleaned up :doc:`cppcoreguidelines-prefer-member-initializer + ` + by removing enforcement of rule `C.48 + `_, + which was deprecated since :program:`clang-tidy` 17. This rule is now covered + by :doc:`cppcoreguidelines-use-default-member-init + `. + +- Improved :doc:`modernize-avoid-c-arrays + ` check by introducing the new + `AllowStringArrays` option, enabling the exclusion of array types with deduced + length initialized from string literals. + Removed checks ^^^^^^^^^^^^^^ +- Removed `cert-dcl21-cpp`, which was deprecated since :program:`clang-tidy` 17, + since the rule DCL21-CPP has been removed from the CERT guidelines. + Improvements to include-fixer ----------------------------- diff --git a/clang-tools-extra/docs/clang-tidy/checks/cert/dcl21-cpp.rst b/clang-tools-extra/docs/clang-tidy/checks/cert/dcl21-cpp.rst deleted file mode 100644 index 9845c2776e11d..0000000000000 --- a/clang-tools-extra/docs/clang-tidy/checks/cert/dcl21-cpp.rst +++ /dev/null @@ -1,28 +0,0 @@ -.. title:: clang-tidy - cert-dcl21-cpp - -cert-dcl21-cpp -============== - -.. note:: - This check is deprecated since it's no longer part of the CERT standard. - It will be removed in :program:`clang-tidy` version 19. - -This check flags postfix ``operator++`` and ``operator--`` declarations -if the return type is not a const object. This also warns if the return type -is a reference type. - -The object returned by a postfix increment or decrement operator is supposed -to be a snapshot of the object's value prior to modification. With such an -implementation, any modifications made to the resulting object from calling -operator++(int) would be modifying a temporary object. Thus, such an -implementation of a postfix increment or decrement operator should instead -return a const object, prohibiting accidental mutation of a temporary object. -Similarly, it is unexpected for the postfix operator to return a reference to -its previous state, and any subsequent modifications would be operating on a -stale object. - -This check corresponds to the CERT C++ Coding Standard recommendation -DCL21-CPP. Overloaded postfix increment and decrement operators should return a -const object. However, all of the CERT recommendations have been removed from -public view, and so their justification for the behavior of this check requires -an account on their wiki to view. diff --git a/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/prefer-member-initializer.rst b/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/prefer-member-initializer.rst index 81aa662043bc3..6d1bdb93cc7b0 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/prefer-member-initializer.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/prefer-member-initializer.rst @@ -13,27 +13,11 @@ This check implements `C.49 `_ from the C++ Core Guidelines. -If the language version is `C++ 11` or above, the constructor is the default -constructor of the class, the field is not a bitfield (only in case of earlier -language version than `C++ 20`), furthermore the assigned value is a literal, -negated literal or ``enum`` constant then the preferred place of the -initialization is at the class member declaration. - -This latter rule is `C.48 +Please note, that this check does not enforce rule `C.48 `_ -from the C++ Core Guidelines. - -Please note, that this check does not enforce this latter rule for -initializations already implemented as member initializers. For that purpose +from the C++ Core Guidelines. For that purpose see check :doc:`modernize-use-default-member-init <../modernize/use-default-member-init>`. -.. note:: - - Enforcement of rule C.48 in this check is deprecated, to be removed in - :program:`clang-tidy` version 19 (only C.49 will be enforced by this check then). - Please use :doc:`cppcoreguidelines-use-default-member-init <../cppcoreguidelines/use-default-member-init>` - to enforce rule C.48. - Example 1 --------- @@ -51,16 +35,16 @@ Example 1 } }; -Here ``n`` can be initialized using a default member initializer, unlike +Here ``n`` can be initialized in the constructor initializer list, unlike ``m``, as ``m``'s initialization follows a control statement (``if``): .. code-block:: c++ class C { - int n{1}; + int n; int m; public: - C() { + C(): n(1) { if (dice()) return; m = 1; @@ -84,7 +68,7 @@ Example 2 } }; -Here ``n`` can be initialized in the constructor initialization list, unlike +Here ``n`` can be initialized in the constructor initializer list, unlike ``m``, as ``m``'s initialization follows a control statement (``if``): .. code-block:: c++ @@ -94,29 +78,3 @@ Here ``n`` can be initialized in the constructor initialization list, unlike return; m = mm; } - -.. option:: UseAssignment - - Note: this option is deprecated, to be removed in :program:`clang-tidy` - version 19. Please use the `UseAssignment` option from - :doc:`cppcoreguidelines-use-default-member-init <../cppcoreguidelines/use-default-member-init>` - instead. - - If this option is set to `true` (by default `UseAssignment` from - :doc:`modernize-use-default-member-init - <../modernize/use-default-member-init>` will be used), - the check will initialize members with an assignment. - In this case the fix of the first example looks like this: - -.. code-block:: c++ - - class C { - int n = 1; - int m; - public: - C() { - if (dice()) - return; - m = 1; - } - }; diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst index f773e80b562e4..f40192ed9dea2 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/list.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -155,7 +155,6 @@ Clang-Tidy Checks :doc:`bugprone-unused-return-value `, :doc:`bugprone-use-after-move `, :doc:`bugprone-virtual-near-miss `, "Yes" - :doc:`cert-dcl21-cpp `, "Yes" :doc:`cert-dcl50-cpp `, :doc:`cert-dcl58-cpp `, :doc:`cert-env33-c `, diff --git a/clang-tools-extra/docs/clang-tidy/checks/modernize/avoid-c-arrays.rst b/clang-tools-extra/docs/clang-tidy/checks/modernize/avoid-c-arrays.rst index bc61033ff1fa1..8f13ca4466a31 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/modernize/avoid-c-arrays.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/modernize/avoid-c-arrays.rst @@ -58,3 +58,14 @@ such headers between C code, and C++ code. Similarly, the ``main()`` function is ignored. Its second and third parameters can be either ``char* argv[]`` or ``char** argv``, but cannot be ``std::array<>``. + +.. option:: AllowStringArrays + + When set to `true` (default is `false`), variables of character array type + with deduced length, initialized directly from string literals, will be ignored. + This option doesn't affect cases where length can't be deduced, resembling + pointers, as seen in class members and parameters. Example: + + .. code:: c++ + + const char name[] = "Some name"; diff --git a/clang-tools-extra/test/clang-tidy/checkers/cert/dcl21-cpp.cpp b/clang-tools-extra/test/clang-tidy/checkers/cert/dcl21-cpp.cpp deleted file mode 100644 index c975f70fbb02c..0000000000000 --- a/clang-tools-extra/test/clang-tidy/checkers/cert/dcl21-cpp.cpp +++ /dev/null @@ -1,134 +0,0 @@ -// RUN: %check_clang_tidy %s cert-dcl21-cpp %t - -class A {}; - -A operator++(A &, int); -// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: overloaded 'operator++' returns a non-constant object instead of a constant object type [cert-dcl21-cpp] -// CHECK-FIXES: {{^}}const A operator++(A &, int); - -A operator--(A &, int); -// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: overloaded 'operator--' returns a no -// CHECK-FIXES: {{^}}const A operator--(A &, int); - -class B {}; - -B &operator++(B &); -const B operator++(B &, int); - -B &operator--(B &); -const B operator--(B &, int); - - -class D { -D &operator++(); -const D operator++(int); - -D &operator--(); -const D operator--(int); -}; - -class C { -C operator++(int); -// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: overloaded 'operator++' returns a no -// CHECK-FIXES: {{^}}const C operator++(int); - -C operator--(int); -// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: overloaded 'operator--' returns a no -// CHECK-FIXES: {{^}}const C operator--(int); -}; - -class E {}; - -E &operator++(E &, int); -// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: overloaded 'operator++' returns a reference instead of a constant object type [cert-dcl21-cpp] -// CHECK-FIXES: {{^}}const E operator++(E &, int); - -E &operator--(E &, int); -// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: overloaded 'operator--' returns a re -// CHECK-FIXES: {{^}}const E operator--(E &, int); - -class G { -G &operator++(int); -// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: overloaded 'operator++' returns a re -// CHECK-FIXES: {{^}}const G operator++(int); - -G &operator--(int); -// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: overloaded 'operator--' returns a re -// CHECK-FIXES: {{^}}const G operator--(int); -}; - -class F {}; - -const F &operator++(F &, int); -// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: overloaded 'operator++' returns a re -// CHECK-FIXES: {{^}}const F operator++(F &, int); - -const F &operator--(F &, int); -// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: overloaded 'operator--' returns a re -// CHECK-FIXES: {{^}}const F operator--(F &, int); - -class H { -const H &operator++(int); -// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: overloaded 'operator++' returns a re -// CHECK-FIXES: {{^}}const H operator++(int); - -const H &operator--(int); -// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: overloaded 'operator--' returns a re -// CHECK-FIXES: {{^}}const H operator--(int); -}; - - -#define FROM_MACRO P& -class P { -const FROM_MACRO operator++(int); -// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: overloaded 'operator++' returns a re -// CHECK-FIXES: {{^}}const FROM_MACRO operator++(int); -}; - - -template -class Q { -const Q &operator++(int); -// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: overloaded 'operator++' returns a re -// CHECK-FIXES: {{^}}const Q operator++(int); - -const Q &operator--(int); -// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: overloaded 'operator--' returns a re -// CHECK-FIXES: {{^}}const Q operator--(int); -}; - -void foobar() { - Q a; - Q b; - (void)a; - (void)b; -} - -struct S {}; -typedef S& SRef; - -SRef operator++(SRef, int); -// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: overloaded 'operator++' returns a re -// CHECK-FIXES: {{^}}SRef operator++(SRef, int); - -struct T { - typedef T& TRef; - - TRef operator++(int); -// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: overloaded 'operator++' returns a re -// CHECK-FIXES: {{^}} TRef operator++(int); -}; - -struct U { - typedef const U& ConstURef; - - ConstURef& operator++(int); -// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: overloaded 'operator++' returns a re -// CHECK-FIXES: {{^}} ConstURef& operator++(int); -}; - -struct V { - V *operator++(int); - V *const operator--(int); -}; - diff --git a/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/prefer-member-initializer-modernize-use-default-member-init-assignment.cpp b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/prefer-member-initializer-modernize-use-default-member-init-assignment.cpp deleted file mode 100644 index 70ef19181f7ad..0000000000000 --- a/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/prefer-member-initializer-modernize-use-default-member-init-assignment.cpp +++ /dev/null @@ -1,33 +0,0 @@ -// RUN: %check_clang_tidy %s cppcoreguidelines-prefer-member-initializer,modernize-use-default-member-init %t -- \ -// RUN: -config="{CheckOptions: {modernize-use-default-member-init.UseAssignment: true}}" -// RUN: %check_clang_tidy %s cppcoreguidelines-prefer-member-initializer,modernize-use-default-member-init %t -- \ -// RUN: -config="{CheckOptions: {modernize-use-default-member-init.UseAssignment: false, cppcoreguidelines-prefer-member-initializer.UseAssignment: true}}" - -class Simple1 { - int n; - // CHECK-FIXES: int n = 0; - double x; - // CHECK-FIXES: double x = 0.0; - -public: - Simple1() { - n = 0; - // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: 'n' should be initialized in an in-class default member initializer [cppcoreguidelines-prefer-member-initializer] - // CHECK-FIXES: {{^\ *$}} - x = 0.0; - // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: 'x' should be initialized in an in-class default member initializer [cppcoreguidelines-prefer-member-initializer] - // CHECK-FIXES: {{^\ *$}} - } - - Simple1(int nn, double xx) { - // CHECK-FIXES: Simple1(int nn, double xx) : n(nn), x(xx) { - n = nn; - // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: 'n' should be initialized in a member initializer of the constructor [cppcoreguidelines-prefer-member-initializer] - // CHECK-FIXES: {{^\ *$}} - x = xx; - // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: 'x' should be initialized in a member initializer of the constructor [cppcoreguidelines-prefer-member-initializer] - // CHECK-FIXES: {{^\ *$}} - } - - ~Simple1() = default; -}; diff --git a/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/prefer-member-initializer-modernize-use-default-member-init.cpp b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/prefer-member-initializer-modernize-use-default-member-init.cpp deleted file mode 100644 index 281a817a42b30..0000000000000 --- a/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/prefer-member-initializer-modernize-use-default-member-init.cpp +++ /dev/null @@ -1,33 +0,0 @@ -// RUN: %check_clang_tidy %s cppcoreguidelines-prefer-member-initializer,modernize-use-default-member-init %t -// RUN: %check_clang_tidy %s cppcoreguidelines-prefer-member-initializer,modernize-use-default-member-init %t -- \ -// RUN: -config="{CheckOptions: {modernize-use-default-member-init.UseAssignment: true, \ -// RUN: cppcoreguidelines-prefer-member-initializer.UseAssignment: false}}" - -class Simple1 { - int n; - // CHECK-FIXES: int n{0}; - double x; - // CHECK-FIXES: double x{0.0}; - -public: - Simple1() { - n = 0; - // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: 'n' should be initialized in an in-class default member initializer [cppcoreguidelines-prefer-member-initializer] - // CHECK-FIXES: {{^\ *$}} - x = 0.0; - // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: 'x' should be initialized in an in-class default member initializer [cppcoreguidelines-prefer-member-initializer] - // CHECK-FIXES: {{^\ *$}} - } - - Simple1(int nn, double xx) { - // CHECK-FIXES: Simple1(int nn, double xx) : n(nn), x(xx) { - n = nn; - // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: 'n' should be initialized in a member initializer of the constructor [cppcoreguidelines-prefer-member-initializer] - // CHECK-FIXES: {{^\ *$}} - x = xx; - // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: 'x' should be initialized in a member initializer of the constructor [cppcoreguidelines-prefer-member-initializer] - // CHECK-FIXES: {{^\ *$}} - } - - ~Simple1() = default; -}; diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/avoid-c-arrays-ignores-strings.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/avoid-c-arrays-ignores-strings.cpp new file mode 100644 index 0000000000000..f6d64848f9e3a --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/avoid-c-arrays-ignores-strings.cpp @@ -0,0 +1,9 @@ +// RUN: %check_clang_tidy %s modernize-avoid-c-arrays %t -- \ +// RUN: -config='{CheckOptions: { modernize-avoid-c-arrays.AllowStringArrays: true }}' + +const char name[] = "name"; +const char array[] = {'n', 'a', 'm', 'e', '\0'}; +// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: do not declare C-style arrays, use std::array<> instead [modernize-avoid-c-arrays] + +void takeCharArray(const char name[]); +// CHECK-MESSAGES: :[[@LINE-1]]:26: warning: do not declare C-style arrays, use std::array<> instead [modernize-avoid-c-arrays] diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/avoid-c-arrays.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/avoid-c-arrays.cpp index dd3078010eb38..ce99f0821b223 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/modernize/avoid-c-arrays.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/avoid-c-arrays.cpp @@ -86,3 +86,9 @@ struct Bar { int j[1]; }; } + +const char name[] = "Some string"; +// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: do not declare C-style arrays, use std::array<> instead [modernize-avoid-c-arrays] + +void takeCharArray(const char name[]); +// CHECK-MESSAGES: :[[@LINE-1]]:26: warning: do not declare C-style arrays, use std::array<> instead [modernize-avoid-c-arrays] diff --git a/clang/bindings/python/clang/cindex.py b/clang/bindings/python/clang/cindex.py index 754f03d718e88..44a34ca196274 100644 --- a/clang/bindings/python/clang/cindex.py +++ b/clang/bindings/python/clang/cindex.py @@ -3506,6 +3506,65 @@ def cursor(self): return cursor +class Rewriter(ClangObject): + """ + The Rewriter is a wrapper class around clang::Rewriter + + It enables rewriting buffers. + """ + + @staticmethod + def create(tu): + """ + Creates a new Rewriter + Parameters: + tu -- The translation unit for the target AST. + """ + return Rewriter(conf.lib.clang_CXRewriter_create(tu)) + + def __init__(self, ptr): + ClangObject.__init__(self, ptr) + + def __del__(self): + conf.lib.clang_CXRewriter_dispose(self) + + def insert_text_before(self, loc, insert): + """ + Insert the specified string at the specified location in + the original buffer. + """ + conf.lib.clang_CXRewriter_insertTextBefore(self, loc, insert) + + def replace_text(self, extent, replacement): + """ + This method replaces a range of characters in the input buffer with + a new string. + """ + conf.lib.clang_CXRewriter_replaceText(self, extent, replacement) + + def remove_text(self, extent): + """ + Remove the specified text region. + """ + conf.lib.clang_CXRewriter_removeText(self, extent) + + def overwrite_changed_files(self): + """ + Save all changed files to disk. + + Returns 1 if any files were not saved successfully, + returns 0 otherwise. + """ + return conf.lib.clang_CXRewriter_overwriteChangedFiles(self) + + def write_main_file_to_stdout(self): + """ + Writes the main file to stdout. + """ + sys.stdout.flush() + conf.lib.clang_CXRewriter_writeMainFileToStdOut(self) + + # Now comes the plumbing to hook up the C library. # Register callback types in common container. @@ -3571,6 +3630,13 @@ def cursor(self): ("clang_codeCompleteGetNumDiagnostics", [CodeCompletionResults], c_int), ("clang_createIndex", [c_int, c_int], c_object_p), ("clang_createTranslationUnit", [Index, c_interop_string], c_object_p), + ("clang_CXRewriter_create", [TranslationUnit], c_object_p), + ("clang_CXRewriter_dispose", [Rewriter]), + ("clang_CXRewriter_insertTextBefore", [Rewriter, SourceLocation, c_interop_string]), + ("clang_CXRewriter_overwriteChangedFiles", [Rewriter], c_int), + ("clang_CXRewriter_removeText", [Rewriter, SourceRange]), + ("clang_CXRewriter_replaceText", [Rewriter, SourceRange, c_interop_string]), + ("clang_CXRewriter_writeMainFileToStdOut", [Rewriter]), ("clang_CXXConstructor_isConvertingConstructor", [Cursor], bool), ("clang_CXXConstructor_isCopyConstructor", [Cursor], bool), ("clang_CXXConstructor_isDefaultConstructor", [Cursor], bool), diff --git a/clang/bindings/python/tests/cindex/test_rewrite.py b/clang/bindings/python/tests/cindex/test_rewrite.py new file mode 100644 index 0000000000000..42006f57554e2 --- /dev/null +++ b/clang/bindings/python/tests/cindex/test_rewrite.py @@ -0,0 +1,71 @@ +import unittest +import tempfile + +from clang.cindex import ( + Rewriter, + TranslationUnit, + File, + SourceLocation, + SourceRange, +) + + +class TestRewrite(unittest.TestCase): + code = """int main() { return 0; }""" + + def setUp(self): + self.tmp = tempfile.NamedTemporaryFile(suffix=".cpp", buffering=0) + self.tmp.write(TestRewrite.code.encode("utf-8")) + self.tmp.flush() + self.tu = TranslationUnit.from_source(self.tmp.name) + self.rew = Rewriter.create(self.tu) + self.file = File.from_name(self.tu, self.tmp.name) + + def tearDown(self): + self.tmp.close() + + def get_content(self) -> str: + with open(self.tmp.name, "r", encoding="utf-8") as f: + return f.read() + + def test_replace(self): + rng = SourceRange.from_locations( + SourceLocation.from_position(self.tu, self.file, 1, 5), + SourceLocation.from_position(self.tu, self.file, 1, 9), + ) + self.rew.replace_text(rng, "MAIN") + self.rew.overwrite_changed_files() + self.assertEqual(self.get_content(), "int MAIN() { return 0; }") + + def test_replace_shorter(self): + rng = SourceRange.from_locations( + SourceLocation.from_position(self.tu, self.file, 1, 5), + SourceLocation.from_position(self.tu, self.file, 1, 9), + ) + self.rew.replace_text(rng, "foo") + self.rew.overwrite_changed_files() + self.assertEqual(self.get_content(), "int foo() { return 0; }") + + def test_replace_longer(self): + rng = SourceRange.from_locations( + SourceLocation.from_position(self.tu, self.file, 1, 5), + SourceLocation.from_position(self.tu, self.file, 1, 9), + ) + self.rew.replace_text(rng, "patatino") + self.rew.overwrite_changed_files() + self.assertEqual(self.get_content(), "int patatino() { return 0; }") + + def test_insert(self): + pos = SourceLocation.from_position(self.tu, self.file, 1, 5) + self.rew.insert_text_before(pos, "ro") + self.rew.overwrite_changed_files() + self.assertEqual(self.get_content(), "int romain() { return 0; }") + + def test_remove(self): + rng = SourceRange.from_locations( + SourceLocation.from_position(self.tu, self.file, 1, 5), + SourceLocation.from_position(self.tu, self.file, 1, 9), + ) + self.rew.remove_text(rng) + self.rew.overwrite_changed_files() + self.assertEqual(self.get_content(), "int () { return 0; }") diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst index 4dc0de3a90f26..0b887288fe2cb 100644 --- a/clang/docs/ClangFormatStyleOptions.rst +++ b/clang/docs/ClangFormatStyleOptions.rst @@ -5277,15 +5277,9 @@ the configuration (without a prefix: ``Auto``). Possible values: * ``SBPO_Never`` (in configuration: ``Never``) - Never put a space before opening parentheses. - - .. code-block:: c++ - - void f() { - if(true) { - f(); - } - } + This is **deprecated** and replaced by ``Custom`` below, with all + ``SpaceBeforeParensOptions`` but ``AfterPlacementOperator`` set to + ``false``. * ``SBPO_ControlStatements`` (in configuration: ``ControlStatements``) Put a space before opening parentheses only after control statement @@ -5425,32 +5419,14 @@ the configuration (without a prefix: ``Auto``). void operator++ (int a); vs. void operator++(int a); object.operator++ (10); object.operator++(10); - * ``AfterPlacementOperatorStyle AfterPlacementOperator`` :versionbadge:`clang-format 18` - - Defines in which cases to put a space between ``new/delete`` operators - and opening parentheses. - - Possible values: - - * ``APO_Never`` (in configuration: ``Never``) - Remove space after ``new/delete`` operators and before ``(``. - - .. code-block:: c++ - - new(buf) T; - delete(buf) T; - - * ``APO_Always`` (in configuration: ``Always``) - Always add space after ``new/delete`` operators and before ``(``. + * ``bool AfterPlacementOperator`` If ``true``, put a space between operator ``new``/``delete`` and opening + parenthesis. - .. code-block:: c++ - - new (buf) T; - delete (buf) T; - - * ``APO_Leave`` (in configuration: ``Leave``) - Leave placement ``new/delete`` expressions as they are. + .. code-block:: c++ + true: false: + new (buf) T; vs. new(buf) T; + delete (buf) T; delete(buf) T; * ``bool AfterRequiresInClause`` If ``true``, put space between requires keyword in a requires clause and opening parentheses, if there is one. diff --git a/clang/docs/HIPSupport.rst b/clang/docs/HIPSupport.rst index 84cee45e83ba3..543c82cf90244 100644 --- a/clang/docs/HIPSupport.rst +++ b/clang/docs/HIPSupport.rst @@ -176,6 +176,10 @@ Predefined Macros * - ``HIP_API_PER_THREAD_DEFAULT_STREAM`` - Alias to ``__HIP_API_PER_THREAD_DEFAULT_STREAM__``. Deprecated. +Note that some architecture specific AMDGPU macros will have default values when +used from the HIP host compilation. Other :doc:`AMDGPU macros ` +like ``__AMDGCN_WAVEFRONT_SIZE__`` will default to 64 for example. + Compilation Modes ================= diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 9d68be469dac3..53040aa0f9074 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -49,6 +49,28 @@ ABI Changes in This Version AST Dumping Potentially Breaking Changes ---------------------------------------- +Clang Frontend Potentially Breaking Changes +------------------------------------------- +- Removed support for constructing on-stack ``TemplateArgumentList``s; interfaces should instead + use ``ArrayRef`` to pass template arguments. Transitioning internal uses to + ``ArrayRef`` reduces AST memory usage by 0.4% when compiling clang, and is + expected to show similar improvements on other workloads. + +Target OS macros extension +^^^^^^^^^^^^^^^^^^^^^^^^^^ +A new Clang extension (see :ref:`here `) is enabled for +Darwin (Apple platform) targets. Clang now defines ``TARGET_OS_*`` macros for +these targets, which could break existing code bases with improper checks for +the ``TARGET_OS_`` macros. For example, existing checks might fail to include +the ``TargetConditionals.h`` header from Apple SDKs and therefore leaving the +macros undefined and guarded code unexercised. + +Affected code should be checked to see if it's still intended for the specific +target and fixed accordingly. + +The extension can be turned off by the option ``-fno-define-target-os-macros`` +as a workaround. + What's New in Clang |release|? ============================== Some of the major new features and improvements to Clang are listed @@ -64,12 +86,16 @@ C++20 Feature Support - Clang won't perform ODR checks for decls in the global module fragment any more to ease the implementation and improve the user's using experience. - This follows the MSVC's behavior. + This follows the MSVC's behavior. Users interested in testing the more strict + behavior can use the flag '-Xclang -fno-skip-odr-check-in-gmf'. (`#79240 `_). C++23 Feature Support ^^^^^^^^^^^^^^^^^^^^^ +- Implemented `P2718R0: Lifetime extension in range-based for loops `_. Also + materialize temporary object which is a prvalue in discarded-value expression. + C++2c Feature Support ^^^^^^^^^^^^^^^^^^^^^ @@ -94,6 +120,17 @@ Non-comprehensive list of changes in this release New Compiler Flags ------------------ +.. _target_os_detail: + +Target OS macros extension +^^^^^^^^^^^^^^^^^^^^^^^^^^ +A pair of new flags ``-fdefine-target-os-macros`` and +``-fno-define-target-os-macros`` has been added to Clang to enable/disable the +extension to provide built-in definitions of a list of ``TARGET_OS_*`` macros +based on the target triple. + +The extension is enabled by default for Darwin (Apple platform) targets. + Deprecated Compiler Flags ------------------------- @@ -111,11 +148,17 @@ Improvements to Clang's diagnostics - Clang now applies syntax highlighting to the code snippets it prints. +- Clang now diagnoses member template declarations with multiple declarators. + Improvements to Clang's time-trace ---------------------------------- Bug Fixes in This Version ------------------------- +- Clang now accepts elaborated-type-specifiers that explicitly specialize + a member class template for an implicit instantiation of a class template. + +- Fixed missing warnings when doing bool-like conversions in C23 (`#79435 `_). Bug Fixes to Compiler Builtins ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -128,16 +171,23 @@ Bug Fixes to C++ Support - Fix crash when using lifetimebound attribute in function with trailing return. Fixes (`#73619 `_) -- Fix a crash when specializing an out-of-line member function with a default - parameter where we did an incorrect specialization of the initialization of - the default parameter. - Fixes (`#68490 `_) +- Addressed an issue where constraints involving injected class types are perceived + distinct from its specialization types. + (`#56482 `_) - Fixed a bug where variables referenced by requires-clauses inside nested generic lambdas were not properly injected into the constraint scope. (`#73418 `_) +- Fixed a crash where substituting into a requires-expression that refers to function + parameters during the equivalence determination of two constraint expressions. + (`#74447 `_) - Fixed deducing auto& from const int in template parameters of partial specializations. (`#77189 `_) +- Fix for crash when using a erroneous type in a return statement. + Fixes (`#63244 `_) + and (`#79745 `_) +- Fix incorrect code generation caused by the object argument of ``static operator()`` and ``static operator[]`` calls not being evaluated. + Fixes (`#67976 `_) Bug Fixes to AST Handling ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -221,6 +271,9 @@ Improvements - Support importing C++20 modules in clang-repl. +- Added support for ``TypeLoc::dump()`` for easier debugging, and improved + textual and JSON dumping for various ``TypeLoc``-related nodes. + Moved checkers ^^^^^^^^^^^^^^ @@ -232,6 +285,8 @@ Sanitizers Python Binding Changes ---------------------- +- Exposed `CXRewriter` API as `class Rewriter`. + Additional Information ====================== diff --git a/clang/docs/StandardCPlusPlusModules.rst b/clang/docs/StandardCPlusPlusModules.rst index 4e853990a7338..c322805d8db5b 100644 --- a/clang/docs/StandardCPlusPlusModules.rst +++ b/clang/docs/StandardCPlusPlusModules.rst @@ -457,6 +457,29 @@ Note that **currently** the compiler doesn't consider inconsistent macro definit Currently Clang would accept the above example. But it may produce surprising results if the debugging code depends on consistent use of ``NDEBUG`` also in other translation units. +Definitions consistency +^^^^^^^^^^^^^^^^^^^^^^^ + +The C++ language defines that same declarations in different translation units should have +the same definition, as known as ODR (One Definition Rule). Prior to modules, the translation +units don't dependent on each other and the compiler itself can't perform a strong +ODR violation check. With the introduction of modules, now the compiler have +the chance to perform ODR violations with language semantics across translation units. + +However, in the practice, we found the existing ODR checking mechanism is not stable +enough. Many people suffers from the false positive ODR violation diagnostics, AKA, +the compiler are complaining two identical declarations have different definitions +incorrectly. Also the true positive ODR violations are rarely reported. +Also we learned that MSVC don't perform ODR check for declarations in the global module +fragment. + +So in order to get better user experience, save the time checking ODR and keep consistent +behavior with MSVC, we disabled the ODR check for the declarations in the global module +fragment by default. Users who want more strict check can still use the +``-Xclang -fno-skip-odr-check-in-gmf`` flag to get the ODR check enabled. It is also +encouraged to report issues if users find false positive ODR violations or false negative ODR +violations with the flag enabled. + ABI Impacts ----------- diff --git a/clang/docs/tools/clang-formatted-files.txt b/clang/docs/tools/clang-formatted-files.txt index 18512b1a7bf6b..40ab76fa26a9e 100644 --- a/clang/docs/tools/clang-formatted-files.txt +++ b/clang/docs/tools/clang-formatted-files.txt @@ -985,8 +985,6 @@ clang-tools-extra/clang-tidy/cert/MutatingCopyCheck.cpp clang-tools-extra/clang-tidy/cert/MutatingCopyCheck.h clang-tools-extra/clang-tidy/cert/NonTrivialTypesLibcMemoryCallsCheck.cpp clang-tools-extra/clang-tidy/cert/NonTrivialTypesLibcMemoryCallsCheck.h -clang-tools-extra/clang-tidy/cert/PostfixOperatorCheck.cpp -clang-tools-extra/clang-tidy/cert/PostfixOperatorCheck.h clang-tools-extra/clang-tidy/cert/ProperlySeededRandomGeneratorCheck.cpp clang-tools-extra/clang-tidy/cert/ProperlySeededRandomGeneratorCheck.h clang-tools-extra/clang-tidy/cert/SetLongJmpCheck.cpp diff --git a/clang/docs/tools/dump_format_style.py b/clang/docs/tools/dump_format_style.py index 75d4a044ef19f..e41891f07de2e 100755 --- a/clang/docs/tools/dump_format_style.py +++ b/clang/docs/tools/dump_format_style.py @@ -474,7 +474,7 @@ class State: opts = sorted(opts, key=lambda x: x.name) options_text = "\n\n".join(map(str, opts)) -with open(DOC_FILE) as f: +with open(DOC_FILE, encoding="utf-8") as f: contents = f.read() contents = substitute(contents, "FORMAT_STYLE_OPTIONS", options_text) diff --git a/clang/include/clang/AST/ASTNodeTraverser.h b/clang/include/clang/AST/ASTNodeTraverser.h index 950da2b8d2c45..06d67e9cba953 100644 --- a/clang/include/clang/AST/ASTNodeTraverser.h +++ b/clang/include/clang/AST/ASTNodeTraverser.h @@ -23,7 +23,9 @@ #include "clang/AST/StmtVisitor.h" #include "clang/AST/TemplateArgumentVisitor.h" #include "clang/AST/Type.h" +#include "clang/AST/TypeLocVisitor.h" #include "clang/AST/TypeVisitor.h" +#include "llvm/Support/SaveAndRestore.h" namespace clang { @@ -48,6 +50,7 @@ struct { void Visit(const Stmt *Node); void Visit(const Type *T); void Visit(QualType T); + void Visit(TypeLoc); void Visit(const Decl *D); void Visit(const CXXCtorInitializer *Init); void Visit(const OMPClause *C); @@ -64,6 +67,7 @@ class ASTNodeTraverser public comments::ConstCommentVisitor, public TypeVisitor, + public TypeLocVisitor, public ConstAttrVisitor, public ConstTemplateArgumentVisitor { @@ -71,6 +75,14 @@ class ASTNodeTraverser /// not already been loaded. bool Deserialize = false; + /// Tracks whether we should dump TypeLocs etc. + /// + /// Detailed location information such as TypeLoc nodes is not usually + /// included in the dump (too verbose). + /// But when explicitly asked to dump a Loc node, we do so recursively, + /// including e.g. FunctionTypeLoc => ParmVarDecl => TypeLoc. + bool VisitLocs = false; + TraversalKind Traversal = TraversalKind::TK_AsIs; NodeDelegateType &getNodeDelegate() { @@ -85,7 +97,7 @@ class ASTNodeTraverser void SetTraversalKind(TraversalKind TK) { Traversal = TK; } TraversalKind GetTraversalKind() const { return Traversal; } - void Visit(const Decl *D) { + void Visit(const Decl *D, bool VisitLocs = false) { if (Traversal == TK_IgnoreUnlessSpelledInSource && D->isImplicit()) return; @@ -94,7 +106,10 @@ class ASTNodeTraverser if (!D) return; - ConstDeclVisitor::Visit(D); + { + llvm::SaveAndRestore RestoreVisitLocs(this->VisitLocs, VisitLocs); + ConstDeclVisitor::Visit(D); + } for (const auto &A : D->attrs()) Visit(A); @@ -181,6 +196,17 @@ class ASTNodeTraverser }); } + void Visit(TypeLoc T) { + getNodeDelegate().AddChild([=] { + getNodeDelegate().Visit(T); + if (T.isNull()) + return; + TypeLocVisitor::Visit(T); + if (auto Inner = T.getNextTypeLoc()) + Visit(Inner); + }); + } + void Visit(const Attr *A) { getNodeDelegate().AddChild([=] { getNodeDelegate().Visit(A); @@ -286,6 +312,8 @@ class ASTNodeTraverser Visit(*QT); else if (const auto *T = N.get()) Visit(T); + else if (const auto *TL = N.get()) + Visit(*TL); else if (const auto *C = N.get()) Visit(C); else if (const auto *C = N.get()) @@ -346,7 +374,7 @@ class ASTNodeTraverser void VisitComplexType(const ComplexType *T) { Visit(T->getElementType()); } void VisitLocInfoType(const LocInfoType *T) { - Visit(T->getTypeSourceInfo()->getType()); + Visit(T->getTypeSourceInfo()->getTypeLoc()); } void VisitPointerType(const PointerType *T) { Visit(T->getPointeeType()); } void VisitBlockPointerType(const BlockPointerType *T) { @@ -421,9 +449,55 @@ class ASTNodeTraverser if (!T->isSugared()) Visit(T->getPattern()); } + void VisitAutoType(const AutoType *T) { + for (const auto &Arg : T->getTypeConstraintArguments()) + Visit(Arg); + } // FIXME: ElaboratedType, DependentNameType, // DependentTemplateSpecializationType, ObjCObjectType + // For TypeLocs, we automatically visit the inner type loc (pointee type etc). + // We must explicitly visit other lexically-nested nodes. + void VisitFunctionProtoTypeLoc(FunctionProtoTypeLoc TL) { + TypeLocVisitor::VisitFunctionTypeLoc(TL); + for (const auto *Param : TL.getParams()) + Visit(Param, /*VisitTypeLocs=*/true); + } + void VisitAutoTypeLoc(AutoTypeLoc TL) { + if (const auto *CR = TL.getConceptReference()) { + if (auto *Args = CR->getTemplateArgsAsWritten()) + for (const auto &Arg : Args->arguments()) + dumpTemplateArgumentLoc(Arg); + } + } + void VisitMemberPointerTypeLoc(MemberPointerTypeLoc TL) { + Visit(TL.getClassTInfo()->getTypeLoc()); + } + void VisitVariableArrayTypeLoc(VariableArrayTypeLoc TL) { + Visit(TL.getSizeExpr()); + } + void VisitDependentSizedArrayTypeLoc(DependentSizedArrayTypeLoc TL) { + Visit(TL.getSizeExpr()); + } + void VisitDependentSizedExtVectorTypeLoc(DependentSizedExtVectorTypeLoc TL) { + Visit(cast(TL.getType())->getSizeExpr()); + } + void VisitTypeOfExprTypeLoc(TypeOfExprTypeLoc TL) { + Visit(TL.getUnderlyingExpr()); + } + void VisitDecltypeType(DecltypeType TL) { + Visit(TL.getUnderlyingExpr()); + } + void VisitTemplateSpecializationTypeLoc(TemplateSpecializationTypeLoc TL) { + for (unsigned I=0, N=TL.getNumArgs(); I < N; ++I) + dumpTemplateArgumentLoc(TL.getArgLoc(I)); + } + void VisitDependentTemplateSpecializationTypeLoc( + DependentTemplateSpecializationTypeLoc TL) { + for (unsigned I=0, N=TL.getNumArgs(); I < N; ++I) + dumpTemplateArgumentLoc(TL.getArgLoc(I)); + } + void VisitTypedefDecl(const TypedefDecl *D) { Visit(D->getUnderlyingType()); } void VisitEnumConstantDecl(const EnumConstantDecl *D) { @@ -468,6 +542,8 @@ class ASTNodeTraverser if (Traversal == TK_IgnoreUnlessSpelledInSource && D->isCXXForRangeDecl()) return; + if (const auto *TSI = D->getTypeSourceInfo(); VisitLocs && TSI) + Visit(TSI->getTypeLoc()); if (D->hasInit()) Visit(D->getInit()); } diff --git a/clang/include/clang/AST/ComparisonCategories.h b/clang/include/clang/AST/ComparisonCategories.h index b4ad37e394ce2..5a6aef80843cf 100644 --- a/clang/include/clang/AST/ComparisonCategories.h +++ b/clang/include/clang/AST/ComparisonCategories.h @@ -78,7 +78,7 @@ class ComparisonCategoryInfo { friend class Sema; public: - ComparisonCategoryInfo(const ASTContext &Ctx, CXXRecordDecl *RD, + ComparisonCategoryInfo(const ASTContext &Ctx, const CXXRecordDecl *RD, ComparisonCategoryType Kind) : Ctx(Ctx), Record(RD), Kind(Kind) {} diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h index 832ad2de6b08a..baf71145d99dc 100644 --- a/clang/include/clang/AST/DeclTemplate.h +++ b/clang/include/clang/AST/DeclTemplate.h @@ -241,9 +241,6 @@ class FixedSizeTemplateParameterListStorage /// A template argument list. class TemplateArgumentList final : private llvm::TrailingObjects { - /// The template argument list. - const TemplateArgument *Arguments; - /// The number of template arguments in this template /// argument list. unsigned NumArguments; @@ -258,30 +255,11 @@ class TemplateArgumentList final TemplateArgumentList(const TemplateArgumentList &) = delete; TemplateArgumentList &operator=(const TemplateArgumentList &) = delete; - /// Type used to indicate that the template argument list itself is a - /// stack object. It does not own its template arguments. - enum OnStackType { OnStack }; - /// Create a new template argument list that copies the given set of /// template arguments. static TemplateArgumentList *CreateCopy(ASTContext &Context, ArrayRef Args); - /// Construct a new, temporary template argument list on the stack. - /// - /// The template argument list does not own the template arguments - /// provided. - explicit TemplateArgumentList(OnStackType, ArrayRef Args) - : Arguments(Args.data()), NumArguments(Args.size()) {} - - /// Produces a shallow copy of the given template argument list. - /// - /// This operation assumes that the input argument list outlives it. - /// This takes the list as a pointer to avoid looking like a copy - /// constructor, since this really isn't safe to use that way. - explicit TemplateArgumentList(const TemplateArgumentList *Other) - : Arguments(Other->data()), NumArguments(Other->size()) {} - /// Retrieve the template argument at a given index. const TemplateArgument &get(unsigned Idx) const { assert(Idx < NumArguments && "Invalid template argument index"); @@ -301,7 +279,9 @@ class TemplateArgumentList final unsigned size() const { return NumArguments; } /// Retrieve a pointer to the template argument list. - const TemplateArgument *data() const { return Arguments; } + const TemplateArgument *data() const { + return getTrailingObjects(); + } }; void *allocateDefaultArgStorageChain(const ASTContext &C); diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h index 59f0aee2c0ced..3fc481a62a78a 100644 --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -82,7 +82,7 @@ struct SubobjectAdjustment { union { struct DTB DerivedToBase; - FieldDecl *Field; + const FieldDecl *Field; struct P Ptr; }; @@ -93,8 +93,7 @@ struct SubobjectAdjustment { DerivedToBase.DerivedClass = DerivedClass; } - SubobjectAdjustment(FieldDecl *Field) - : Kind(FieldAdjustment) { + SubobjectAdjustment(const FieldDecl *Field) : Kind(FieldAdjustment) { this->Field = Field; } diff --git a/clang/include/clang/AST/JSONNodeDumper.h b/clang/include/clang/AST/JSONNodeDumper.h index 4def5389137fa..dde70dde2fa2b 100644 --- a/clang/include/clang/AST/JSONNodeDumper.h +++ b/clang/include/clang/AST/JSONNodeDumper.h @@ -197,6 +197,7 @@ class JSONNodeDumper void Visit(const Type *T); void Visit(QualType T); void Visit(const Decl *D); + void Visit(TypeLoc TL); void Visit(const comments::Comment *C, const comments::FullComment *FC); void Visit(const TemplateArgument &TA, SourceRange R = {}, @@ -207,6 +208,7 @@ class JSONNodeDumper void Visit(const GenericSelectionExpr::ConstAssociation &A); void Visit(const concepts::Requirement *R); void Visit(const APValue &Value, QualType Ty); + void Visit(const ConceptReference *); void VisitAliasAttr(const AliasAttr *AA); void VisitCleanupAttr(const CleanupAttr *CA); diff --git a/clang/include/clang/AST/OpenMPClause.h b/clang/include/clang/AST/OpenMPClause.h index 924ca189381ba..325a1baa44614 100644 --- a/clang/include/clang/AST/OpenMPClause.h +++ b/clang/include/clang/AST/OpenMPClause.h @@ -2513,6 +2513,46 @@ class OMPRelaxedClause final : public OMPClause { } }; +/// This represents 'weak' clause in the '#pragma omp atomic' +/// directives. +/// +/// \code +/// #pragma omp atomic compare weak +/// \endcode +/// In this example directive '#pragma omp atomic' has 'weak' clause. +class OMPWeakClause final : public OMPClause { +public: + /// Build 'weak' clause. + /// + /// \param StartLoc Starting location of the clause. + /// \param EndLoc Ending location of the clause. + OMPWeakClause(SourceLocation StartLoc, SourceLocation EndLoc) + : OMPClause(llvm::omp::OMPC_weak, StartLoc, EndLoc) {} + + /// Build an empty clause. + OMPWeakClause() + : OMPClause(llvm::omp::OMPC_weak, SourceLocation(), SourceLocation()) {} + + child_range children() { + return child_range(child_iterator(), child_iterator()); + } + + const_child_range children() const { + return const_child_range(const_child_iterator(), const_child_iterator()); + } + + child_range used_children() { + return child_range(child_iterator(), child_iterator()); + } + const_child_range used_children() const { + return const_child_range(const_child_iterator(), const_child_iterator()); + } + + static bool classof(const OMPClause *T) { + return T->getClauseKind() == llvm::omp::OMPC_weak; + } +}; + /// This represents 'fail' clause in the '#pragma omp atomic' /// directive. /// diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h index 16b98b364eaca..9da5206a21c34 100644 --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -3436,6 +3436,11 @@ bool RecursiveASTVisitor::VisitOMPRelaxedClause(OMPRelaxedClause *) { return true; } +template +bool RecursiveASTVisitor::VisitOMPWeakClause(OMPWeakClause *) { + return true; +} + template bool RecursiveASTVisitor::VisitOMPThreadsClause(OMPThreadsClause *) { return true; diff --git a/clang/include/clang/AST/TextNodeDumper.h b/clang/include/clang/AST/TextNodeDumper.h index 732749ad305e1..3c4283f657efa 100644 --- a/clang/include/clang/AST/TextNodeDumper.h +++ b/clang/include/clang/AST/TextNodeDumper.h @@ -24,6 +24,7 @@ #include "clang/AST/StmtVisitor.h" #include "clang/AST/TemplateArgumentVisitor.h" #include "clang/AST/Type.h" +#include "clang/AST/TypeLocVisitor.h" #include "clang/AST/TypeVisitor.h" namespace clang { @@ -132,6 +133,7 @@ class TextNodeDumper public ConstTemplateArgumentVisitor, public ConstStmtVisitor, public TypeVisitor, + public TypeLocVisitor, public ConstDeclVisitor { raw_ostream &OS; const bool ShowColors; @@ -179,6 +181,8 @@ class TextNodeDumper void Visit(QualType T); + void Visit(TypeLoc); + void Visit(const Decl *D); void Visit(const CXXCtorInitializer *Init); @@ -291,6 +295,8 @@ class TextNodeDumper void VisitTypeTraitExpr(const TypeTraitExpr *Node); void VisitArrayTypeTraitExpr(const ArrayTypeTraitExpr *Node); void VisitExpressionTraitExpr(const ExpressionTraitExpr *Node); + void VisitCXXDefaultArgExpr(const CXXDefaultArgExpr *Node); + void VisitCXXDefaultInitExpr(const CXXDefaultInitExpr *Node); void VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *Node); void VisitExprWithCleanups(const ExprWithCleanups *Node); void VisitUnresolvedLookupExpr(const UnresolvedLookupExpr *Node); @@ -337,6 +343,8 @@ class TextNodeDumper void VisitObjCInterfaceType(const ObjCInterfaceType *T); void VisitPackExpansionType(const PackExpansionType *T); + void VisitTypeLoc(TypeLoc TL); + void VisitLabelDecl(const LabelDecl *D); void VisitTypedefDecl(const TypedefDecl *D); void VisitEnumDecl(const EnumDecl *D); diff --git a/clang/include/clang/AST/TypeLoc.h b/clang/include/clang/AST/TypeLoc.h index a17b4af13d03f..b1b38f882d19f 100644 --- a/clang/include/clang/AST/TypeLoc.h +++ b/clang/include/clang/AST/TypeLoc.h @@ -229,6 +229,9 @@ class TypeLoc { /// __nullable, or __null_unspecifier), if there is one. SourceLocation findNullabilityLoc() const; + void dump() const; + void dump(llvm::raw_ostream &, const ASTContext &) const; + private: static bool isKind(const TypeLoc&) { return true; diff --git a/clang/include/clang/Analysis/Analyses/PostOrderCFGView.h b/clang/include/clang/Analysis/Analyses/PostOrderCFGView.h index 4356834adf76f..c4998bb2285f7 100644 --- a/clang/include/clang/Analysis/Analyses/PostOrderCFGView.h +++ b/clang/include/clang/Analysis/Analyses/PostOrderCFGView.h @@ -70,7 +70,41 @@ class PostOrderCFGView : public ManagedAnalysis { }; private: - using po_iterator = llvm::po_iterator; + // The CFG orders the blocks of loop bodies before those of loop successors + // (both numerically, and in the successor order of the loop condition + // block). So, RPO necessarily reverses that order, placing the loop successor + // *before* the loop body. For many analyses, particularly those that converge + // to a fixpoint, this results in potentially significant extra work because + // loop successors will necessarily need to be reconsidered once the algorithm + // has reached a fixpoint on the loop body. + // + // This definition of CFG graph traits reverses the order of children, so that + // loop bodies will come first in an RPO. + struct CFGLoopBodyFirstTraits { + using NodeRef = const ::clang::CFGBlock *; + using ChildIteratorType = ::clang::CFGBlock::const_succ_reverse_iterator; + + static ChildIteratorType child_begin(NodeRef N) { return N->succ_rbegin(); } + static ChildIteratorType child_end(NodeRef N) { return N->succ_rend(); } + + using nodes_iterator = ::clang::CFG::const_iterator; + + static NodeRef getEntryNode(const ::clang::CFG *F) { + return &F->getEntry(); + } + + static nodes_iterator nodes_begin(const ::clang::CFG *F) { + return F->nodes_begin(); + } + + static nodes_iterator nodes_end(const ::clang::CFG *F) { + return F->nodes_end(); + } + + static unsigned size(const ::clang::CFG *F) { return F->size(); } + }; + using po_iterator = + llvm::po_iterator; std::vector Blocks; using BlockOrderTy = llvm::DenseMap; diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td index 1af01fe0d700c..31a2bdeb2d3e5 100644 --- a/clang/include/clang/Basic/Builtins.td +++ b/clang/include/clang/Basic/Builtins.td @@ -1065,7 +1065,7 @@ def FPrintfChk : Builtin { def PrintfChk : Builtin { let Spellings = ["__builtin___printf_chk"]; let Attributes = [FunctionWithBuiltinPrefix, PrintfFormat<1>]; - let Prototype = "int(int, int, char const* restrict, ...)"; + let Prototype = "int(int, char const* restrict, ...)"; } def VFPrintfChk : Builtin { diff --git a/clang/include/clang/Basic/BuiltinsAMDGPU.def b/clang/include/clang/Basic/BuiltinsAMDGPU.def index 74dfd1d214e84..e9dd8dcd0b60e 100644 --- a/clang/include/clang/Basic/BuiltinsAMDGPU.def +++ b/clang/include/clang/Basic/BuiltinsAMDGPU.def @@ -292,23 +292,23 @@ TARGET_BUILTIN(__builtin_amdgcn_s_wait_event_export_ready, "v", "n", "gfx11-inst // Postfix w32 indicates the builtin requires wavefront size of 32. // Postfix w64 indicates the builtin requires wavefront size of 64. //===----------------------------------------------------------------------===// -TARGET_BUILTIN(__builtin_amdgcn_wmma_f32_16x16x16_f16_w32, "V8fV16hV16hV8f", "nc", "gfx11-insts") -TARGET_BUILTIN(__builtin_amdgcn_wmma_f32_16x16x16_bf16_w32, "V8fV16sV16sV8f", "nc", "gfx11-insts") -TARGET_BUILTIN(__builtin_amdgcn_wmma_f16_16x16x16_f16_w32, "V16hV16hV16hV16hIb", "nc", "gfx11-insts") -TARGET_BUILTIN(__builtin_amdgcn_wmma_bf16_16x16x16_bf16_w32, "V16sV16sV16sV16sIb", "nc", "gfx11-insts") -TARGET_BUILTIN(__builtin_amdgcn_wmma_f16_16x16x16_f16_tied_w32, "V16hV16hV16hV16hIb", "nc", "gfx11-insts") -TARGET_BUILTIN(__builtin_amdgcn_wmma_bf16_16x16x16_bf16_tied_w32, "V16sV16sV16sV16sIb", "nc", "gfx11-insts") -TARGET_BUILTIN(__builtin_amdgcn_wmma_i32_16x16x16_iu8_w32, "V8iIbV4iIbV4iV8iIb", "nc", "gfx11-insts") -TARGET_BUILTIN(__builtin_amdgcn_wmma_i32_16x16x16_iu4_w32, "V8iIbV2iIbV2iV8iIb", "nc", "gfx11-insts") - -TARGET_BUILTIN(__builtin_amdgcn_wmma_f32_16x16x16_f16_w64, "V4fV16hV16hV4f", "nc", "gfx11-insts") -TARGET_BUILTIN(__builtin_amdgcn_wmma_f32_16x16x16_bf16_w64, "V4fV16sV16sV4f", "nc", "gfx11-insts") -TARGET_BUILTIN(__builtin_amdgcn_wmma_f16_16x16x16_f16_w64, "V8hV16hV16hV8hIb", "nc", "gfx11-insts") -TARGET_BUILTIN(__builtin_amdgcn_wmma_bf16_16x16x16_bf16_w64, "V8sV16sV16sV8sIb", "nc", "gfx11-insts") -TARGET_BUILTIN(__builtin_amdgcn_wmma_f16_16x16x16_f16_tied_w64, "V8hV16hV16hV8hIb", "nc", "gfx11-insts") -TARGET_BUILTIN(__builtin_amdgcn_wmma_bf16_16x16x16_bf16_tied_w64, "V8sV16sV16sV8sIb", "nc", "gfx11-insts") -TARGET_BUILTIN(__builtin_amdgcn_wmma_i32_16x16x16_iu8_w64, "V4iIbV4iIbV4iV4iIb", "nc", "gfx11-insts") -TARGET_BUILTIN(__builtin_amdgcn_wmma_i32_16x16x16_iu4_w64, "V4iIbV2iIbV2iV4iIb", "nc", "gfx11-insts") +TARGET_BUILTIN(__builtin_amdgcn_wmma_f32_16x16x16_f16_w32, "V8fV16hV16hV8f", "nc", "gfx11-insts,wavefrontsize32") +TARGET_BUILTIN(__builtin_amdgcn_wmma_f32_16x16x16_bf16_w32, "V8fV16sV16sV8f", "nc", "gfx11-insts,wavefrontsize32") +TARGET_BUILTIN(__builtin_amdgcn_wmma_f16_16x16x16_f16_w32, "V16hV16hV16hV16hIb", "nc", "gfx11-insts,wavefrontsize32") +TARGET_BUILTIN(__builtin_amdgcn_wmma_bf16_16x16x16_bf16_w32, "V16sV16sV16sV16sIb", "nc", "gfx11-insts,wavefrontsize32") +TARGET_BUILTIN(__builtin_amdgcn_wmma_f16_16x16x16_f16_tied_w32, "V16hV16hV16hV16hIb", "nc", "gfx11-insts,wavefrontsize32") +TARGET_BUILTIN(__builtin_amdgcn_wmma_bf16_16x16x16_bf16_tied_w32, "V16sV16sV16sV16sIb", "nc", "gfx11-insts,wavefrontsize32") +TARGET_BUILTIN(__builtin_amdgcn_wmma_i32_16x16x16_iu8_w32, "V8iIbV4iIbV4iV8iIb", "nc", "gfx11-insts,wavefrontsize32") +TARGET_BUILTIN(__builtin_amdgcn_wmma_i32_16x16x16_iu4_w32, "V8iIbV2iIbV2iV8iIb", "nc", "gfx11-insts,wavefrontsize32") + +TARGET_BUILTIN(__builtin_amdgcn_wmma_f32_16x16x16_f16_w64, "V4fV16hV16hV4f", "nc", "gfx11-insts,wavefrontsize64") +TARGET_BUILTIN(__builtin_amdgcn_wmma_f32_16x16x16_bf16_w64, "V4fV16sV16sV4f", "nc", "gfx11-insts,wavefrontsize64") +TARGET_BUILTIN(__builtin_amdgcn_wmma_f16_16x16x16_f16_w64, "V8hV16hV16hV8hIb", "nc", "gfx11-insts,wavefrontsize64") +TARGET_BUILTIN(__builtin_amdgcn_wmma_bf16_16x16x16_bf16_w64, "V8sV16sV16sV8sIb", "nc", "gfx11-insts,wavefrontsize64") +TARGET_BUILTIN(__builtin_amdgcn_wmma_f16_16x16x16_f16_tied_w64, "V8hV16hV16hV8hIb", "nc", "gfx11-insts,wavefrontsize64") +TARGET_BUILTIN(__builtin_amdgcn_wmma_bf16_16x16x16_bf16_tied_w64, "V8sV16sV16sV8sIb", "nc", "gfx11-insts,wavefrontsize64") +TARGET_BUILTIN(__builtin_amdgcn_wmma_i32_16x16x16_iu8_w64, "V4iIbV4iIbV4iV4iIb", "nc", "gfx11-insts,wavefrontsize64") +TARGET_BUILTIN(__builtin_amdgcn_wmma_i32_16x16x16_iu4_w64, "V4iIbV2iIbV2iV4iIb", "nc", "gfx11-insts,wavefrontsize64") TARGET_BUILTIN(__builtin_amdgcn_s_sendmsg_rtn, "UiUIi", "n", "gfx11-insts") TARGET_BUILTIN(__builtin_amdgcn_s_sendmsg_rtnl, "UWiUIi", "n", "gfx11-insts") diff --git a/clang/include/clang/Basic/BuiltinsNVPTX.def b/clang/include/clang/Basic/BuiltinsNVPTX.def index 0f2e8260143be..7819e71d7fe2a 100644 --- a/clang/include/clang/Basic/BuiltinsNVPTX.def +++ b/clang/include/clang/Basic/BuiltinsNVPTX.def @@ -44,6 +44,7 @@ #pragma push_macro("PTX42") #pragma push_macro("PTX60") #pragma push_macro("PTX61") +#pragma push_macro("PTX62") #pragma push_macro("PTX63") #pragma push_macro("PTX64") #pragma push_macro("PTX65") @@ -76,7 +77,8 @@ #define PTX65 "ptx65|" PTX70 #define PTX64 "ptx64|" PTX65 #define PTX63 "ptx63|" PTX64 -#define PTX61 "ptx61|" PTX63 +#define PTX62 "ptx62|" PTX63 +#define PTX61 "ptx61|" PTX62 #define PTX60 "ptx60|" PTX61 #define PTX42 "ptx42|" PTX60 @@ -146,6 +148,7 @@ BUILTIN(__nvvm_read_ptx_sreg_lanemask_gt, "i", "nc") BUILTIN(__nvvm_read_ptx_sreg_clock, "i", "n") BUILTIN(__nvvm_read_ptx_sreg_clock64, "LLi", "n") +BUILTIN(__nvvm_read_ptx_sreg_globaltimer, "LLi", "n") BUILTIN(__nvvm_read_ptx_sreg_pm0, "i", "n") BUILTIN(__nvvm_read_ptx_sreg_pm1, "i", "n") @@ -155,6 +158,8 @@ BUILTIN(__nvvm_read_ptx_sreg_pm3, "i", "n") // MISC BUILTIN(__nvvm_prmt, "UiUiUiUi", "") +BUILTIN(__nvvm_exit, "v", "r") +TARGET_BUILTIN(__nvvm_nanosleep, "vUi", "n", AND(SM_70, PTX63)) // Min Max @@ -632,6 +637,9 @@ TARGET_BUILTIN(__nvvm_vote_any_sync, "bUib", "", PTX60) TARGET_BUILTIN(__nvvm_vote_uni_sync, "bUib", "", PTX60) TARGET_BUILTIN(__nvvm_vote_ballot_sync, "UiUib", "", PTX60) +// Mask +TARGET_BUILTIN(__nvvm_activemask, "Ui", "n", PTX62) + // Match TARGET_BUILTIN(__nvvm_match_any_sync_i32, "UiUiUi", "", AND(SM_70,PTX60)) TARGET_BUILTIN(__nvvm_match_any_sync_i64, "UiUiWi", "", AND(SM_70,PTX60)) @@ -1065,6 +1073,7 @@ TARGET_BUILTIN(__nvvm_getctarank_shared_cluster, "iv*3", "", AND(SM_90,PTX78)) #pragma pop_macro("PTX42") #pragma pop_macro("PTX60") #pragma pop_macro("PTX61") +#pragma pop_macro("PTX62") #pragma pop_macro("PTX63") #pragma pop_macro("PTX64") #pragma pop_macro("PTX65") diff --git a/clang/include/clang/Basic/DiagnosticDriverKinds.td b/clang/include/clang/Basic/DiagnosticDriverKinds.td index 094fe19509412..476528375fb88 100644 --- a/clang/include/clang/Basic/DiagnosticDriverKinds.td +++ b/clang/include/clang/Basic/DiagnosticDriverKinds.td @@ -57,6 +57,8 @@ def warn_drv_avr_stdlib_not_linked: Warning< InGroup; def err_drv_cuda_bad_gpu_arch : Error<"unsupported CUDA gpu architecture: %0">; def err_drv_offload_bad_gpu_arch : Error<"unsupported %0 gpu architecture: %1">; +def err_drv_offload_missing_gpu_arch : Error< + "Must pass in an explicit %0 gpu architecture to '%1'">; def err_drv_no_cuda_installation : Error< "cannot find CUDA installation; provide its path via '--cuda-path', or pass " "'-nocudainc' to build without CUDA includes">; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 24d32cb87c89e..7638a7e84c3c0 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -7148,8 +7148,7 @@ def warn_standalone_specifier : Warning<"'%0' ignored on this declaration">, def ext_standalone_specifier : ExtWarn<"'%0' is not permitted on a declaration " "of a type">, InGroup; def err_standalone_class_nested_name_specifier : Error< - "forward declaration of %select{class|struct|interface|union|enum|enum class|enum struct}0 cannot " - "have a nested name specifier">; + "forward declaration of %0 cannot have a nested name specifier">; def err_typecheck_sclass_func : Error<"illegal storage class on function">; def err_static_block_func : Error< "function declared in block scope cannot have 'static' storage class">; @@ -11043,7 +11042,8 @@ def note_omp_atomic_compare: Note< "expect lvalue for result value|expect scalar value|expect integer value|unexpected 'else' statement|expect '==' operator|expect an assignment statement 'v = x'|" "expect a 'if' statement|expect no more than two statements|expect a compound statement|expect 'else' statement|expect a form 'r = x == e; if (r) ...'}0">; def err_omp_atomic_fail_wrong_or_no_clauses : Error<"expected a memory order clause">; -def err_omp_atomic_fail_no_compare : Error<"expected 'compare' clause with the 'fail' modifier">; +def err_omp_atomic_no_compare : Error<"expected 'compare' clause with the '%0' modifier">; +def err_omp_atomic_weak_no_equality : Error<"expected '==' operator for 'weak' clause">; def err_omp_atomic_several_clauses : Error< "directive '#pragma omp atomic' cannot contain more than one 'read', 'write', 'update', 'capture', or 'compare' clause">; def err_omp_several_mem_order_clauses : Error< diff --git a/clang/include/clang/Basic/DiagnosticSerializationKinds.td b/clang/include/clang/Basic/DiagnosticSerializationKinds.td index 11c706ebf84b5..4c4659ed517e0 100644 --- a/clang/include/clang/Basic/DiagnosticSerializationKinds.td +++ b/clang/include/clang/Basic/DiagnosticSerializationKinds.td @@ -44,6 +44,9 @@ def err_pch_diagopt_mismatch : Error<"%0 is currently enabled, but was not in " "the PCH file">; def err_pch_modulecache_mismatch : Error<"PCH was compiled with module cache " "path '%0', but the path is currently '%1'">; +def err_pch_vfsoverlay_mismatch : Error<"PCH was compiled with different VFS overlay files than are currently in use">; +def note_pch_vfsoverlay_files : Note<"%select{PCH|current translation unit}0 has the following VFS overlays:\n%1">; +def note_pch_vfsoverlay_empty : Note<"%select{PCH|current translation unit}0 has no VFS overlays">; def err_pch_version_too_old : Error< "PCH file uses an older PCH format that is no longer supported">; diff --git a/clang/include/clang/Basic/FileManager.h b/clang/include/clang/Basic/FileManager.h index 56cb093dd8c37..997c17a0ffcfc 100644 --- a/clang/include/clang/Basic/FileManager.h +++ b/clang/include/clang/Basic/FileManager.h @@ -248,6 +248,10 @@ class FileManager : public RefCountedBase { return FS; } + /// Enable or disable tracking of VFS usage. Used to not track full header + /// search and implicit modulemap lookup. + void trackVFSUsage(bool Active); + void setVirtualFileSystem(IntrusiveRefCntPtr FS) { this->FS = std::move(FS); } diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def index 1e671a7c46016..2b42b521a3036 100644 --- a/clang/include/clang/Basic/LangOptions.def +++ b/clang/include/clang/Basic/LangOptions.def @@ -174,6 +174,7 @@ LANGOPT(MathErrno , 1, 1, "errno in math functions") BENIGN_LANGOPT(HeinousExtensions , 1, 0, "extensions that we really don't like and may be ripped out at any time") LANGOPT(Modules , 1, 0, "modules semantics") COMPATIBLE_LANGOPT(CPlusPlusModules, 1, 0, "C++ modules syntax") +LANGOPT(SkipODRCheckInGMF, 1, 0, "Skip ODR checks for decls in the global module fragment") LANGOPT(BuiltinHeadersInSystemModules, 1, 0, "builtin headers belong to system modules, and _Builtin_ modules are ignored for cstdlib headers") BENIGN_ENUM_LANGOPT(CompilingModule, CompilingModuleKind, 3, CMK_None, "compiling a module interface") diff --git a/clang/include/clang/Basic/OpenACCKinds.h b/clang/include/clang/Basic/OpenACCKinds.h index 3662798fa5aba..afdd0e8983c9e 100644 --- a/clang/include/clang/Basic/OpenACCKinds.h +++ b/clang/include/clang/Basic/OpenACCKinds.h @@ -243,6 +243,11 @@ enum class OpenACCClauseKind { Async, /// 'tile' clause, allowed on 'loop' and Combined constructs. Tile, + /// 'gang' clause, allowed on 'loop' and Combined constructs. + Gang, + /// 'wait' clause, allowed on Compute, Data, 'update', and Combined + /// constructs. + Wait, /// Represents an invalid clause, for the purposes of parsing. Invalid, @@ -371,6 +376,12 @@ inline const StreamingDiagnostic &operator<<(const StreamingDiagnostic &Out, case OpenACCClauseKind::Tile: return Out << "tile"; + case OpenACCClauseKind::Gang: + return Out << "gang"; + + case OpenACCClauseKind::Wait: + return Out << "wait"; + case OpenACCClauseKind::Invalid: return Out << ""; } diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index d940665969339..a4b35e370999e 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -2985,6 +2985,14 @@ def fmodule_output : Flag<["-"], "fmodule-output">, Flags<[NoXarchOption]>, Visibility<[ClangOption, CC1Option]>, HelpText<"Save intermediate module file results when compiling a standard C++ module unit.">; +defm skip_odr_check_in_gmf : BoolOption<"f", "skip-odr-check-in-gmf", + LangOpts<"SkipODRCheckInGMF">, DefaultFalse, + PosFlag, + NegFlag>, + Group; + def fmodules_prune_interval : Joined<["-"], "fmodules-prune-interval=">, Group, Visibility<[ClangOption, CC1Option]>, MetaVarName<"">, HelpText<"Specify the interval (in seconds) between attempts to prune the module cache">, diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h index bc9eecd42f9eb..efcb4e1d87ea4 100644 --- a/clang/include/clang/Format/Format.h +++ b/clang/include/clang/Format/Format.h @@ -4157,14 +4157,9 @@ struct FormatStyle { /// Different ways to put a space before opening parentheses. enum SpaceBeforeParensStyle : int8_t { - /// Never put a space before opening parentheses. - /// \code - /// void f() { - /// if(true) { - /// f(); - /// } - /// } - /// \endcode + /// This is **deprecated** and replaced by ``Custom`` below, with all + /// ``SpaceBeforeParensOptions`` but ``AfterPlacementOperator`` set to + /// ``false``. SBPO_Never, /// Put a space before opening parentheses only after control statement /// keywords (``for/if/while...``). @@ -4273,28 +4268,14 @@ struct FormatStyle { /// object.operator++ (10); object.operator++(10); /// \endcode bool AfterOverloadedOperator; - /// Styles for adding spacing between ``new/delete`` operators and opening - /// parentheses. - enum AfterPlacementOperatorStyle : int8_t { - /// Remove space after ``new/delete`` operators and before ``(``. - /// \code - /// new(buf) T; - /// delete(buf) T; - /// \endcode - APO_Never, - /// Always add space after ``new/delete`` operators and before ``(``. - /// \code - /// new (buf) T; - /// delete (buf) T; - /// \endcode - APO_Always, - /// Leave placement ``new/delete`` expressions as they are. - APO_Leave, - }; - /// Defines in which cases to put a space between ``new/delete`` operators - /// and opening parentheses. - /// \version 18 - AfterPlacementOperatorStyle AfterPlacementOperator; + /// If ``true``, put a space between operator ``new``/``delete`` and opening + /// parenthesis. + /// \code + /// true: false: + /// new (buf) T; vs. new(buf) T; + /// delete (buf) T; delete(buf) T; + /// \endcode + bool AfterPlacementOperator; /// If ``true``, put space between requires keyword in a requires clause and /// opening parentheses, if there is one. /// \code @@ -4327,7 +4308,7 @@ struct FormatStyle { : AfterControlStatements(false), AfterForeachMacros(false), AfterFunctionDeclarationName(false), AfterFunctionDefinitionName(false), AfterIfMacros(false), - AfterOverloadedOperator(false), AfterPlacementOperator(APO_Leave), + AfterOverloadedOperator(false), AfterPlacementOperator(true), AfterRequiresInClause(false), AfterRequiresInExpression(false), BeforeNonEmptyParentheses(false) {} diff --git a/clang/include/clang/Lex/HeaderSearch.h b/clang/include/clang/Lex/HeaderSearch.h index a2c33842924b1..705dcfa8aacc3 100644 --- a/clang/include/clang/Lex/HeaderSearch.h +++ b/clang/include/clang/Lex/HeaderSearch.h @@ -576,6 +576,13 @@ class HeaderSearch { /// Note: implicit module maps don't contribute to entry usage. std::vector computeUserEntryUsage() const; + /// Collect which HeaderSearchOptions::VFSOverlayFiles have been meaningfully + /// used so far and mark their index with 'true' in the resulting bit vector. + /// + /// Note: this ignores VFSs that redirect non-affecting files such as unused + /// modulemaps. + std::vector collectVFSUsageAndClear() const; + /// This method returns a HeaderMap for the specified /// FileEntry, uniquing them through the 'HeaderMaps' datastructure. const HeaderMap *CreateHeaderMap(FileEntryRef FE); diff --git a/clang/include/clang/Lex/HeaderSearchOptions.h b/clang/include/clang/Lex/HeaderSearchOptions.h index fa2d0b502d72c..637dc77e5d957 100644 --- a/clang/include/clang/Lex/HeaderSearchOptions.h +++ b/clang/include/clang/Lex/HeaderSearchOptions.h @@ -263,6 +263,10 @@ class HeaderSearchOptions { LLVM_PREFERRED_TYPE(bool) unsigned ModulesStrictContextHash : 1; + /// Whether to include ivfsoverlay usage information in written AST files. + LLVM_PREFERRED_TYPE(bool) + unsigned ModulesIncludeVFSUsage : 1; + HeaderSearchOptions(StringRef _Sysroot = "/") : Sysroot(_Sysroot), ModuleFormat("raw"), DisableModuleHash(false), ImplicitModuleMaps(false), ModuleMapFileHomeIsCwd(false), @@ -277,7 +281,7 @@ class HeaderSearchOptions { ModulesSkipDiagnosticOptions(false), ModulesSkipHeaderSearchPaths(false), ModulesSkipPragmaDiagnosticMappings(false), ModulesHashContent(false), - ModulesStrictContextHash(false) {} + ModulesStrictContextHash(false), ModulesIncludeVFSUsage(false) {} /// AddPath - Add the \p Path path to the specified \p Group list. void AddPath(StringRef Path, frontend::IncludeDirGroup Group, diff --git a/clang/include/clang/Lex/Token.h b/clang/include/clang/Lex/Token.h index 1409e2c58b550..36ec5ddaa29ad 100644 --- a/clang/include/clang/Lex/Token.h +++ b/clang/include/clang/Lex/Token.h @@ -22,6 +22,7 @@ namespace clang { class IdentifierInfo; +class LangOptions; /// Token - This structure provides full information about a lexed token. /// It is not intended to be space efficient, it is intended to return as much @@ -288,6 +289,8 @@ class Token { /// Return the ObjC keyword kind. tok::ObjCKeywordKind getObjCKeywordID() const; + bool isSimpleTypeSpecifier(const LangOptions &LangOpts) const; + /// Return true if this token has trigraphs or escaped newlines in it. bool needsCleaning() const { return getFlag(NeedsCleaning); } diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index baad6155b7c63..da18cf88edcc9 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -2402,7 +2402,7 @@ class Parser : public CodeCompletionHandler { struct ForRangeInit { SourceLocation ColonLoc; ExprResult RangeExpr; - + SmallVector LifetimeExtendTemps; bool ParsedForRangeDecl() { return !ColonLoc.isInvalid(); } }; struct ForRangeInfo : ForRangeInit { @@ -2423,6 +2423,7 @@ class Parser : public CodeCompletionHandler { bool MightBeDeclarator(DeclaratorContext Context); DeclGroupPtrTy ParseDeclGroup(ParsingDeclSpec &DS, DeclaratorContext Context, ParsedAttributes &Attrs, + ParsedTemplateInfo &TemplateInfo, SourceLocation *DeclEnd = nullptr, ForRangeInit *FRI = nullptr); Decl *ParseDeclarationAfterDeclarator(Declarator &D, @@ -3605,22 +3606,25 @@ class Parser : public CodeCompletionHandler { bool ParseOpenACCSizeExpr(); /// Parses a comma delimited list of 'size-expr's. bool ParseOpenACCSizeExprList(); + /// Parses a 'gang-arg-list', used for the 'gang' clause. + bool ParseOpenACCGangArgList(); + /// Parses a 'gang-arg', used for the 'gang' clause. + bool ParseOpenACCGangArg(); private: //===--------------------------------------------------------------------===// // C++ 14: Templates [temp] // C++ 14.1: Template Parameters [temp.param] - Decl *ParseDeclarationStartingWithTemplate(DeclaratorContext Context, - SourceLocation &DeclEnd, - ParsedAttributes &AccessAttrs, - AccessSpecifier AS = AS_none); - Decl *ParseTemplateDeclarationOrSpecialization(DeclaratorContext Context, - SourceLocation &DeclEnd, - ParsedAttributes &AccessAttrs, - AccessSpecifier AS); - Decl *ParseSingleDeclarationAfterTemplate( - DeclaratorContext Context, const ParsedTemplateInfo &TemplateInfo, + DeclGroupPtrTy + ParseDeclarationStartingWithTemplate(DeclaratorContext Context, + SourceLocation &DeclEnd, + ParsedAttributes &AccessAttrs); + DeclGroupPtrTy ParseTemplateDeclarationOrSpecialization( + DeclaratorContext Context, SourceLocation &DeclEnd, + ParsedAttributes &AccessAttrs, AccessSpecifier AS); + DeclGroupPtrTy ParseDeclarationAfterTemplate( + DeclaratorContext Context, ParsedTemplateInfo &TemplateInfo, ParsingDeclRAIIObject &DiagsFromParams, SourceLocation &DeclEnd, ParsedAttributes &AccessAttrs, AccessSpecifier AS = AS_none); bool ParseTemplateParameters(MultiParseScope &TemplateScopes, unsigned Depth, @@ -3669,12 +3673,12 @@ class Parser : public CodeCompletionHandler { TemplateTy Template, SourceLocation OpenLoc); ParsedTemplateArgument ParseTemplateTemplateArgument(); ParsedTemplateArgument ParseTemplateArgument(); - Decl *ParseExplicitInstantiation(DeclaratorContext Context, - SourceLocation ExternLoc, - SourceLocation TemplateLoc, - SourceLocation &DeclEnd, - ParsedAttributes &AccessAttrs, - AccessSpecifier AS = AS_none); + DeclGroupPtrTy ParseExplicitInstantiation(DeclaratorContext Context, + SourceLocation ExternLoc, + SourceLocation TemplateLoc, + SourceLocation &DeclEnd, + ParsedAttributes &AccessAttrs, + AccessSpecifier AS = AS_none); // C++2a: Template, concept definition [temp] Decl * ParseConceptDefinition(const ParsedTemplateInfo &TemplateInfo, diff --git a/clang/include/clang/Rewrite/Core/HTMLRewrite.h b/clang/include/clang/Rewrite/Core/HTMLRewrite.h index 340411e553479..eecf589632746 100644 --- a/clang/include/clang/Rewrite/Core/HTMLRewrite.h +++ b/clang/include/clang/Rewrite/Core/HTMLRewrite.h @@ -24,6 +24,15 @@ class RewriteBuffer; class Preprocessor; namespace html { + struct RelexRewriteCache; + using RelexRewriteCacheRef = std::shared_ptr; + + /// If you need to rewrite the same file multiple times, you can instantiate + /// a RelexRewriteCache and refer functions such as SyntaxHighlight() + /// and HighlightMacros() to it so that to avoid re-lexing the file each time. + /// The cache may outlive the rewriter as long as cached FileIDs and source + /// locations continue to make sense for the translation unit as a whole. + RelexRewriteCacheRef instantiateRelexRewriteCache(); /// HighlightRange - Highlight a range in the source code with the specified /// start/end tags. B/E must be in the same file. This ensures that @@ -67,13 +76,15 @@ namespace html { /// SyntaxHighlight - Relex the specified FileID and annotate the HTML with /// information about keywords, comments, etc. - void SyntaxHighlight(Rewriter &R, FileID FID, const Preprocessor &PP); + void SyntaxHighlight(Rewriter &R, FileID FID, const Preprocessor &PP, + RelexRewriteCacheRef Cache = nullptr); /// HighlightMacros - This uses the macro table state from the end of the /// file, to reexpand macros and insert (into the HTML) information about the /// macro expansions. This won't be perfectly perfect, but it will be /// reasonably close. - void HighlightMacros(Rewriter &R, FileID FID, const Preprocessor &PP); + void HighlightMacros(Rewriter &R, FileID FID, const Preprocessor &PP, + RelexRewriteCacheRef Cache = nullptr); } // end html namespace } // end clang namespace diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index b5946e3f3178f..780a2f2d8ce27 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -1342,6 +1342,12 @@ class Sema final { /// context not already known to be immediately invoked. llvm::SmallPtrSet ReferenceToConsteval; + /// P2718R0 - Lifetime extension in range-based for loops. + /// MaterializeTemporaryExprs in for-range-init expressions which need to + /// extend lifetime. Add MaterializeTemporaryExpr* if the value of + /// InLifetimeExtendingContext is true. + SmallVector ForRangeLifetimeExtendTemps; + /// \brief Describes whether we are in an expression constext which we have /// to handle differently. enum ExpressionKind { @@ -1361,6 +1367,39 @@ class Sema final { // VLAs). bool InConditionallyConstantEvaluateContext = false; + /// Whether we are currently in a context in which all temporaries must be + /// lifetime-extended, even if they're not bound to a reference (for + /// example, in a for-range initializer). + bool InLifetimeExtendingContext = false; + + /// Whether we are currently in a context in which all temporaries must be + /// materialized. + /// + /// [class.temporary]/p2: + /// The materialization of a temporary object is generally delayed as long + /// as possible in order to avoid creating unnecessary temporary objects. + /// + /// Temporary objects are materialized: + /// (2.1) when binding a reference to a prvalue ([dcl.init.ref], + /// [expr.type.conv], [expr.dynamic.cast], [expr.static.cast], + /// [expr.const.cast], [expr.cast]), + /// + /// (2.2) when performing member access on a class prvalue ([expr.ref], + /// [expr.mptr.oper]), + /// + /// (2.3) when performing an array-to-pointer conversion or subscripting + /// on an array prvalue ([conv.array], [expr.sub]), + /// + /// (2.4) when initializing an object of type + /// std​::​initializer_list from a braced-init-list + /// ([dcl.init.list]), + /// + /// (2.5) for certain unevaluated operands ([expr.typeid], [expr.sizeof]) + /// + /// (2.6) when a prvalue that has type other than cv void appears as a + /// discarded-value expression ([expr.context]). + bool InMaterializeTemporaryObjectContext = false; + // When evaluating immediate functions in the initializer of a default // argument or default member initializer, this is the declaration whose // default initializer is being evaluated and the location of the call @@ -2644,8 +2683,6 @@ class Sema final { void DiagnoseUseOfUnimplementedSelectors(); - bool isSimpleTypeSpecifier(tok::TokenKind Kind) const; - ParsedType getTypeName(const IdentifierInfo &II, SourceLocation NameLoc, Scope *S, CXXScopeSpec *SS = nullptr, bool isClassName = false, bool HasTrailingDot = false, @@ -5253,22 +5290,17 @@ class Sema final { BFRK_Check }; - StmtResult ActOnCXXForRangeStmt(Scope *S, SourceLocation ForLoc, - SourceLocation CoawaitLoc, - Stmt *InitStmt, - Stmt *LoopVar, - SourceLocation ColonLoc, Expr *Collection, - SourceLocation RParenLoc, - BuildForRangeKind Kind); - StmtResult BuildCXXForRangeStmt(SourceLocation ForLoc, - SourceLocation CoawaitLoc, - Stmt *InitStmt, - SourceLocation ColonLoc, - Stmt *RangeDecl, Stmt *Begin, Stmt *End, - Expr *Cond, Expr *Inc, - Stmt *LoopVarDecl, - SourceLocation RParenLoc, - BuildForRangeKind Kind); + StmtResult ActOnCXXForRangeStmt( + Scope *S, SourceLocation ForLoc, SourceLocation CoawaitLoc, + Stmt *InitStmt, Stmt *LoopVar, SourceLocation ColonLoc, Expr *Collection, + SourceLocation RParenLoc, BuildForRangeKind Kind, + ArrayRef LifetimeExtendTemps = {}); + StmtResult BuildCXXForRangeStmt( + SourceLocation ForLoc, SourceLocation CoawaitLoc, Stmt *InitStmt, + SourceLocation ColonLoc, Stmt *RangeDecl, Stmt *Begin, Stmt *End, + Expr *Cond, Expr *Inc, Stmt *LoopVarDecl, SourceLocation RParenLoc, + BuildForRangeKind Kind, + ArrayRef LifetimeExtendTemps = {}); StmtResult FinishCXXForRangeStmt(Stmt *ForRange, Stmt *Body); StmtResult ActOnGotoStmt(SourceLocation GotoLoc, @@ -9297,12 +9329,12 @@ class Sema final { TemplateDeductionResult DeduceTemplateArguments(ClassTemplatePartialSpecializationDecl *Partial, - const TemplateArgumentList &TemplateArgs, + ArrayRef TemplateArgs, sema::TemplateDeductionInfo &Info); TemplateDeductionResult DeduceTemplateArguments(VarTemplatePartialSpecializationDecl *Partial, - const TemplateArgumentList &TemplateArgs, + ArrayRef TemplateArgs, sema::TemplateDeductionInfo &Info); TemplateDeductionResult SubstituteExplicitTemplateArguments( @@ -9475,7 +9507,7 @@ class Sema final { MultiLevelTemplateArgumentList getTemplateInstantiationArgs( const NamedDecl *D, const DeclContext *DC = nullptr, bool Final = false, - const TemplateArgumentList *Innermost = nullptr, + std::optional> Innermost = std::nullopt, bool RelativeToPrimary = false, const FunctionDecl *Pattern = nullptr, bool ForConstraintInstantiation = false, bool SkipForSpecialization = false); @@ -10010,6 +10042,18 @@ class Sema final { return currentEvaluationContext().isImmediateFunctionContext(); } + bool isInLifetimeExtendingContext() const { + assert(!ExprEvalContexts.empty() && + "Must be in an expression evaluation context"); + return ExprEvalContexts.back().InLifetimeExtendingContext; + } + + bool isInMaterializeTemporaryObjectContext() const { + assert(!ExprEvalContexts.empty() && + "Must be in an expression evaluation context"); + return ExprEvalContexts.back().InMaterializeTemporaryObjectContext; + } + bool isCheckingDefaultArgumentOrInitializer() const { const ExpressionEvaluationContextRecord &Ctx = currentEvaluationContext(); return (Ctx.Context == @@ -10049,6 +10093,32 @@ class Sema final { return Res; } + /// keepInLifetimeExtendingContext - Pull down InLifetimeExtendingContext + /// flag from previous context. + void keepInLifetimeExtendingContext() { + if (ExprEvalContexts.size() > 2 && + ExprEvalContexts[ExprEvalContexts.size() - 2] + .InLifetimeExtendingContext) { + auto &LastRecord = ExprEvalContexts.back(); + auto &PrevRecord = ExprEvalContexts[ExprEvalContexts.size() - 2]; + LastRecord.InLifetimeExtendingContext = + PrevRecord.InLifetimeExtendingContext; + } + } + + /// keepInMaterializeTemporaryObjectContext - Pull down + /// InMaterializeTemporaryObjectContext flag from previous context. + void keepInMaterializeTemporaryObjectContext() { + if (ExprEvalContexts.size() > 2 && + ExprEvalContexts[ExprEvalContexts.size() - 2] + .InMaterializeTemporaryObjectContext) { + auto &LastRecord = ExprEvalContexts.back(); + auto &PrevRecord = ExprEvalContexts[ExprEvalContexts.size() - 2]; + LastRecord.InMaterializeTemporaryObjectContext = + PrevRecord.InMaterializeTemporaryObjectContext; + } + } + /// RAII class used to determine whether SFINAE has /// trapped any errors that occur during template argument /// deduction. @@ -10467,7 +10537,7 @@ class Sema final { bool AtEndOfTU = false); VarTemplateSpecializationDecl *BuildVarTemplateInstantiation( VarTemplateDecl *VarTemplate, VarDecl *FromVar, - const TemplateArgumentList &TemplateArgList, + const TemplateArgumentList *PartialSpecArgs, const TemplateArgumentListInfo &TemplateArgsInfo, SmallVectorImpl &Converted, SourceLocation PointOfInstantiation, @@ -12305,6 +12375,9 @@ class Sema final { /// Called on well-formed 'relaxed' clause. OMPClause *ActOnOpenMPRelaxedClause(SourceLocation StartLoc, SourceLocation EndLoc); + /// Called on well-formed 'weak' clause. + OMPClause *ActOnOpenMPWeakClause(SourceLocation StartLoc, + SourceLocation EndLoc); /// Called on well-formed 'init' clause. OMPClause * diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h index ea084b328d612..9de925163599d 100644 --- a/clang/include/clang/Serialization/ASTBitCodes.h +++ b/clang/include/clang/Serialization/ASTBitCodes.h @@ -405,6 +405,9 @@ enum UnhashedControlBlockRecordTypes { /// Record code for the indices of used header search entries. HEADER_SEARCH_ENTRY_USAGE, + + /// Record code for the indices of used VFSs. + VFS_USAGE, }; /// Record code for extension blocks. diff --git a/clang/include/clang/Serialization/ASTReader.h b/clang/include/clang/Serialization/ASTReader.h index ba06ab0cd4509..a4c7f54ab9e8b 100644 --- a/clang/include/clang/Serialization/ASTReader.h +++ b/clang/include/clang/Serialization/ASTReader.h @@ -1780,12 +1780,13 @@ class ASTReader /// Read the control block for the named AST file. /// /// \returns true if an error occurred, false otherwise. - static bool readASTFileControlBlock(StringRef Filename, FileManager &FileMgr, - const InMemoryModuleCache &ModuleCache, - const PCHContainerReader &PCHContainerRdr, - bool FindModuleFileExtensions, - ASTReaderListener &Listener, - bool ValidateDiagnosticOptions); + static bool readASTFileControlBlock( + StringRef Filename, FileManager &FileMgr, + const InMemoryModuleCache &ModuleCache, + const PCHContainerReader &PCHContainerRdr, bool FindModuleFileExtensions, + ASTReaderListener &Listener, bool ValidateDiagnosticOptions, + unsigned ClientLoadCapabilities = ARR_ConfigurationMismatch | + ARR_OutOfDate); /// Determine whether the given AST file is acceptable to load into a /// translation unit with the given language and target options. @@ -2270,6 +2271,9 @@ class ASTReader SourceRange ReadSourceRange(ModuleFile &F, const RecordData &Record, unsigned &Idx, LocSeq *Seq = nullptr); + static llvm::BitVector ReadBitVector(const RecordData &Record, + const StringRef Blob); + // Read a string static std::string ReadString(const RecordDataImpl &Record, unsigned &Idx); @@ -2452,8 +2456,10 @@ class BitsUnpacker { uint32_t CurrentBitsIndex = ~0; }; -inline bool isFromExplicitGMF(const Decl *D) { - return D->getOwningModule() && D->getOwningModule()->isExplicitGlobalModule(); +inline bool shouldSkipCheckingODR(const Decl *D) { + return D->getOwningModule() && + D->getASTContext().getLangOpts().SkipODRCheckInGMF && + D->getOwningModule()->isExplicitGlobalModule(); } } // namespace clang diff --git a/clang/include/clang/Serialization/ASTWriter.h b/clang/include/clang/Serialization/ASTWriter.h index de69f99003d82..5e2f305b294ca 100644 --- a/clang/include/clang/Serialization/ASTWriter.h +++ b/clang/include/clang/Serialization/ASTWriter.h @@ -467,10 +467,10 @@ class ASTWriter : public ASTDeserializationListener, std::vector NonAffectingRanges; std::vector NonAffectingOffsetAdjustments; - /// Collects input files that didn't affect compilation of the current module, + /// Computes input files that didn't affect compilation of the current module, /// and initializes data structures necessary for leaving those files out /// during \c SourceManager serialization. - void collectNonAffectingInputFiles(); + void computeNonAffectingInputFiles(); /// Returns an adjusted \c FileID, accounting for any non-affecting input /// files. diff --git a/clang/include/clang/Serialization/ModuleFile.h b/clang/include/clang/Serialization/ModuleFile.h index 9a14129d72ff3..bc0aa89966c2b 100644 --- a/clang/include/clang/Serialization/ModuleFile.h +++ b/clang/include/clang/Serialization/ModuleFile.h @@ -189,6 +189,9 @@ class ModuleFile { /// The bit vector denoting usage of each header search entry (true = used). llvm::BitVector SearchPathUsage; + /// The bit vector denoting usage of each VFS entry (true = used). + llvm::BitVector VFSUsage; + /// Whether this module has been directly imported by the /// user. bool DirectlyImported = false; diff --git a/clang/include/clang/StaticAnalyzer/Core/BugReporter/BugSuppression.h b/clang/include/clang/StaticAnalyzer/Core/BugReporter/BugSuppression.h index 4fd81b6275197..35ae06575eaa0 100644 --- a/clang/include/clang/StaticAnalyzer/Core/BugReporter/BugSuppression.h +++ b/clang/include/clang/StaticAnalyzer/Core/BugReporter/BugSuppression.h @@ -19,6 +19,7 @@ #include "llvm/ADT/SmallVector.h" namespace clang { +class ASTContext; class Decl; namespace ento { @@ -27,6 +28,8 @@ class PathDiagnosticLocation; class BugSuppression { public: + explicit BugSuppression(const ASTContext &ACtx) : ACtx(ACtx) {} + using DiagnosticIdentifierList = llvm::ArrayRef; /// Return true if the given bug report was explicitly suppressed by the user. @@ -45,6 +48,8 @@ class BugSuppression { llvm::SmallVector; llvm::DenseMap CachedSuppressionLocations; + + const ASTContext &ACtx; }; } // end namespace ento diff --git a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningFilesystem.h b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningFilesystem.h index 9a2aea5d6efa1..846fdc7253977 100644 --- a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningFilesystem.h +++ b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningFilesystem.h @@ -280,8 +280,12 @@ class EntryRef { /// This is not a thread safe VFS. A single instance is meant to be used only in /// one thread. Multiple instances are allowed to service multiple threads /// running in parallel. -class DependencyScanningWorkerFilesystem : public llvm::vfs::ProxyFileSystem { +class DependencyScanningWorkerFilesystem + : public llvm::RTTIExtends { public: + static const char ID; + DependencyScanningWorkerFilesystem( DependencyScanningFilesystemSharedCache &SharedCache, IntrusiveRefCntPtr FS); diff --git a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningService.h b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningService.h index dcdf1c171f6d7..4f9867262a275 100644 --- a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningService.h +++ b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningService.h @@ -45,6 +45,9 @@ enum class ScanningOutputFormat { P1689, }; +#define DSS_LAST_BITMASK_ENUM(Id) \ + LLVM_MARK_AS_BITMASK_ENUM(Id), All = llvm::NextPowerOf2(Id) - 1 + enum class ScanningOptimizations { None = 0, @@ -54,11 +57,15 @@ enum class ScanningOptimizations { /// Remove warnings from system modules. SystemWarnings = 2, - LLVM_MARK_AS_BITMASK_ENUM(SystemWarnings), - All = HeaderSearch | SystemWarnings, + /// Remove unused -ivfsoverlay arguments. + VFS = 4, + + DSS_LAST_BITMASK_ENUM(VFS), Default = All }; +#undef DSS_LAST_BITMASK_ENUM + /// The dependency scanning service contains shared configuration and state that /// is used by the individual dependency scanning workers. class DependencyScanningService { diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 71c9c0003d18c..78a04b4c69426 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -1749,7 +1749,8 @@ TypeInfoChars ASTContext::getTypeInfoDataSizeInChars(QualType T) const { // of a base-class subobject. We decide whether that's possible // during class layout, so here we can just trust the layout results. if (getLangOpts().CPlusPlus) { - if (const auto *RT = T->getAs()) { + if (const auto *RT = T->getAs(); + RT && !RT->getDecl()->isInvalidDecl()) { const ASTRecordLayout &layout = getASTRecordLayout(RT->getDecl()); Info.Width = layout.getDataSize(); } diff --git a/clang/lib/AST/ASTDumper.cpp b/clang/lib/AST/ASTDumper.cpp index cc9a84eecaadb..6efc5bb92e28d 100644 --- a/clang/lib/AST/ASTDumper.cpp +++ b/clang/lib/AST/ASTDumper.cpp @@ -200,6 +200,19 @@ LLVM_DUMP_METHOD void Type::dump(llvm::raw_ostream &OS, QualType(this, 0).dump(OS, Context); } +//===----------------------------------------------------------------------===// +// TypeLoc method implementations +//===----------------------------------------------------------------------===// + +LLVM_DUMP_METHOD void TypeLoc::dump() const { + ASTDumper(llvm::errs(), /*ShowColors=*/false).Visit(*this); +} + +LLVM_DUMP_METHOD void TypeLoc::dump(llvm::raw_ostream &OS, + const ASTContext &Context) const { + ASTDumper(OS, Context, Context.getDiagnostics().getShowColors()).Visit(*this); +} + //===----------------------------------------------------------------------===// // Decl method implementations //===----------------------------------------------------------------------===// diff --git a/clang/lib/AST/ASTTypeTraits.cpp b/clang/lib/AST/ASTTypeTraits.cpp index 4c7496c699bef..99916f523aa95 100644 --- a/clang/lib/AST/ASTTypeTraits.cpp +++ b/clang/lib/AST/ASTTypeTraits.cpp @@ -228,6 +228,8 @@ void DynTypedNode::dump(llvm::raw_ostream &OS, T->dump(OS, Context); else if (const ConceptReference *C = get()) C->dump(OS); + else if (const TypeLoc *TL = get()) + TL->dump(OS, Context); else OS << "Unable to dump values of type " << NodeKind.asStringRef() << "\n"; } diff --git a/clang/lib/AST/ComparisonCategories.cpp b/clang/lib/AST/ComparisonCategories.cpp index 58411201c3b09..28244104d6636 100644 --- a/clang/lib/AST/ComparisonCategories.cpp +++ b/clang/lib/AST/ComparisonCategories.cpp @@ -48,7 +48,7 @@ bool ComparisonCategoryInfo::ValueInfo::hasValidIntValue() const { // Before we attempt to get the value of the first field, ensure that we // actually have one (and only one) field. - auto *Record = VD->getType()->getAsCXXRecordDecl(); + const auto *Record = VD->getType()->getAsCXXRecordDecl(); if (std::distance(Record->field_begin(), Record->field_end()) != 1 || !Record->field_begin()->getType()->isIntegralOrEnumerationType()) return false; @@ -98,13 +98,13 @@ static const NamespaceDecl *lookupStdNamespace(const ASTContext &Ctx, return StdNS; } -static CXXRecordDecl *lookupCXXRecordDecl(const ASTContext &Ctx, - const NamespaceDecl *StdNS, - ComparisonCategoryType Kind) { +static const CXXRecordDecl *lookupCXXRecordDecl(const ASTContext &Ctx, + const NamespaceDecl *StdNS, + ComparisonCategoryType Kind) { StringRef Name = ComparisonCategories::getCategoryString(Kind); DeclContextLookupResult Lookup = StdNS->lookup(&Ctx.Idents.get(Name)); if (!Lookup.empty()) - if (CXXRecordDecl *RD = dyn_cast(Lookup.front())) + if (const CXXRecordDecl *RD = dyn_cast(Lookup.front())) return RD; return nullptr; } @@ -116,7 +116,7 @@ ComparisonCategories::lookupInfo(ComparisonCategoryType Kind) const { return &It->second; if (const NamespaceDecl *NS = lookupStdNamespace(Ctx, StdNS)) - if (CXXRecordDecl *RD = lookupCXXRecordDecl(Ctx, NS, Kind)) + if (const CXXRecordDecl *RD = lookupCXXRecordDecl(Ctx, NS, Kind)) return &Data.try_emplace((char)Kind, Ctx, RD, Kind).first->second; return nullptr; @@ -126,13 +126,13 @@ const ComparisonCategoryInfo * ComparisonCategories::lookupInfoForType(QualType Ty) const { assert(!Ty.isNull() && "type must be non-null"); using CCT = ComparisonCategoryType; - auto *RD = Ty->getAsCXXRecordDecl(); + const auto *RD = Ty->getAsCXXRecordDecl(); if (!RD) return nullptr; // Check to see if we have information for the specified type cached. const auto *CanonRD = RD->getCanonicalDecl(); - for (auto &KV : Data) { + for (const auto &KV : Data) { const ComparisonCategoryInfo &Info = KV.second; if (CanonRD == Info.Record->getCanonicalDecl()) return &Info; diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp index 7d7556e670f95..3c217d6a6a5ae 100644 --- a/clang/lib/AST/DeclTemplate.cpp +++ b/clang/lib/AST/DeclTemplate.cpp @@ -871,8 +871,7 @@ void TemplateTemplateParmDecl::setDefaultArgument( // TemplateArgumentList Implementation //===----------------------------------------------------------------------===// TemplateArgumentList::TemplateArgumentList(ArrayRef Args) - : Arguments(getTrailingObjects()), - NumArguments(Args.size()) { + : NumArguments(Args.size()) { std::uninitialized_copy(Args.begin(), Args.end(), getTrailingObjects()); } @@ -1583,6 +1582,10 @@ void TemplateParamObjectDecl::printAsInit(llvm::raw_ostream &OS, TemplateParameterList *clang::getReplacedTemplateParameterList(Decl *D) { switch (D->getKind()) { + case Decl::Kind::CXXRecord: + return cast(D) + ->getDescribedTemplate() + ->getTemplateParameters(); case Decl::Kind::ClassTemplate: return cast(D)->getTemplateParameters(); case Decl::Kind::ClassTemplateSpecialization: { diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index c2cd46dfbafcd..d665a08deb47e 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -86,12 +86,12 @@ const Expr *Expr::skipRValueSubobjectAdjustments( while (true) { E = E->IgnoreParens(); - if (const CastExpr *CE = dyn_cast(E)) { + if (const auto *CE = dyn_cast(E)) { if ((CE->getCastKind() == CK_DerivedToBase || CE->getCastKind() == CK_UncheckedDerivedToBase) && E->getType()->isRecordType()) { E = CE->getSubExpr(); - auto *Derived = + const auto *Derived = cast(E->getType()->castAs()->getDecl()); Adjustments.push_back(SubobjectAdjustment(CE, Derived)); continue; @@ -101,10 +101,10 @@ const Expr *Expr::skipRValueSubobjectAdjustments( E = CE->getSubExpr(); continue; } - } else if (const MemberExpr *ME = dyn_cast(E)) { + } else if (const auto *ME = dyn_cast(E)) { if (!ME->isArrow()) { assert(ME->getBase()->getType()->isRecordType()); - if (FieldDecl *Field = dyn_cast(ME->getMemberDecl())) { + if (const auto *Field = dyn_cast(ME->getMemberDecl())) { if (!Field->isBitField() && !Field->getType()->isReferenceType()) { E = ME->getBase(); Adjustments.push_back(SubobjectAdjustment(Field)); @@ -112,12 +112,11 @@ const Expr *Expr::skipRValueSubobjectAdjustments( } } } - } else if (const BinaryOperator *BO = dyn_cast(E)) { + } else if (const auto *BO = dyn_cast(E)) { if (BO->getOpcode() == BO_PtrMemD) { assert(BO->getRHS()->isPRValue()); E = BO->getLHS(); - const MemberPointerType *MPT = - BO->getRHS()->getType()->getAs(); + const auto *MPT = BO->getRHS()->getType()->getAs(); Adjustments.push_back(SubobjectAdjustment(MPT, BO->getRHS())); continue; } diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index a41bdac46c814..63453890d9879 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -7951,7 +7951,8 @@ class ExprEvaluatorBase // Overloaded operator calls to member functions are represented as normal // calls with '*this' as the first argument. const CXXMethodDecl *MD = dyn_cast(FD); - if (MD && MD->isImplicitObjectMemberFunction()) { + if (MD && + (MD->isImplicitObjectMemberFunction() || (OCE && MD->isStatic()))) { // FIXME: When selecting an implicit conversion for an overloaded // operator delete, we sometimes try to evaluate calls to conversion // operators without a 'this' parameter! @@ -7960,7 +7961,11 @@ class ExprEvaluatorBase if (!EvaluateObjectArgument(Info, Args[0], ThisVal)) return false; - This = &ThisVal; + + // If we are calling a static operator, the 'this' argument needs to be + // ignored after being evaluated. + if (MD->isInstance()) + This = &ThisVal; // If this is syntactically a simple assignment using a trivial // assignment operator, start the lifetimes of union members as needed, diff --git a/clang/lib/AST/Interp/ByteCodeEmitter.cpp b/clang/lib/AST/Interp/ByteCodeEmitter.cpp index fd2a92d9d3f91..409ce21506caa 100644 --- a/clang/lib/AST/Interp/ByteCodeEmitter.cpp +++ b/clang/lib/AST/Interp/ByteCodeEmitter.cpp @@ -10,6 +10,7 @@ #include "ByteCodeGenError.h" #include "Context.h" #include "Floating.h" +#include "IntegralAP.h" #include "Opcode.h" #include "Program.h" #include "clang/AST/ASTLambda.h" @@ -209,9 +210,11 @@ static void emit(Program &P, std::vector &Code, const T &Val, } } -template <> -void emit(Program &P, std::vector &Code, const Floating &Val, - bool &Success) { +/// Emits a serializable value. These usually (potentially) contain +/// heap-allocated memory and aren't trivially copyable. +template +static void emitSerialized(std::vector &Code, const T &Val, + bool &Success) { size_t Size = Val.bytesToSerialize(); if (Code.size() + Size > std::numeric_limits::max()) { @@ -228,6 +231,24 @@ void emit(Program &P, std::vector &Code, const Floating &Val, Val.serialize(Code.data() + ValPos); } +template <> +void emit(Program &P, std::vector &Code, const Floating &Val, + bool &Success) { + emitSerialized(Code, Val, Success); +} + +template <> +void emit(Program &P, std::vector &Code, + const IntegralAP &Val, bool &Success) { + emitSerialized(Code, Val, Success); +} + +template <> +void emit(Program &P, std::vector &Code, const IntegralAP &Val, + bool &Success) { + emitSerialized(Code, Val, Success); +} + template bool ByteCodeEmitter::emitOp(Opcode Op, const Tys &... Args, const SourceInfo &SI) { bool Success = true; diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp index d4501cefb2131..a93635a9f6888 100644 --- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp +++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp @@ -311,6 +311,63 @@ bool ByteCodeExprGen::VisitCastExpr(const CastExpr *CE) { return this->emitInitElem(T, 1, SubExpr); } + case CK_IntegralComplexCast: + case CK_FloatingComplexCast: + case CK_IntegralComplexToFloatingComplex: + case CK_FloatingComplexToIntegralComplex: { + assert(CE->getType()->isAnyComplexType()); + assert(SubExpr->getType()->isAnyComplexType()); + if (DiscardResult) + return this->discard(SubExpr); + + if (!Initializing) { + std::optional LocalIndex = + allocateLocal(CE, /*IsExtended=*/true); + if (!LocalIndex) + return false; + if (!this->emitGetPtrLocal(*LocalIndex, CE)) + return false; + } + + // Location for the SubExpr. + // Since SubExpr is of complex type, visiting it results in a pointer + // anyway, so we just create a temporary pointer variable. + std::optional SubExprOffset = allocateLocalPrimitive( + SubExpr, PT_Ptr, /*IsConst=*/true, /*IsExtended=*/false); + if (!SubExprOffset) + return false; + + if (!this->visit(SubExpr)) + return false; + if (!this->emitSetLocal(PT_Ptr, *SubExprOffset, CE)) + return false; + + PrimType SourceElemT = classifyComplexElementType(SubExpr->getType()); + QualType DestElemType = + CE->getType()->getAs()->getElementType(); + PrimType DestElemT = classifyPrim(DestElemType); + // Cast both elements individually. + for (unsigned I = 0; I != 2; ++I) { + if (!this->emitGetLocal(PT_Ptr, *SubExprOffset, CE)) + return false; + if (!this->emitConstUint8(I, CE)) + return false; + if (!this->emitArrayElemPtrPopUint8(CE)) + return false; + if (!this->emitLoadPop(SourceElemT, CE)) + return false; + + // Do the cast. + if (!this->emitPrimCast(SourceElemT, DestElemT, DestElemType, CE)) + return false; + + // Save the value. + if (!this->emitInitElem(DestElemT, I, CE)) + return false; + } + return true; + } + case CK_ToVoid: return discard(SubExpr); @@ -336,6 +393,31 @@ bool ByteCodeExprGen::VisitFloatingLiteral(const FloatingLiteral *E) { return this->emitConstFloat(E->getValue(), E); } +template +bool ByteCodeExprGen::VisitImaginaryLiteral( + const ImaginaryLiteral *E) { + assert(E->getType()->isAnyComplexType()); + if (DiscardResult) + return true; + + if (!Initializing) { + std::optional LocalIndex = allocateLocal(E, /*IsExtended=*/false); + if (!LocalIndex) + return false; + if (!this->emitGetPtrLocal(*LocalIndex, E)) + return false; + } + + const Expr *SubExpr = E->getSubExpr(); + PrimType SubExprT = classifyPrim(SubExpr->getType()); + + if (!this->visitZeroInitializer(SubExprT, SubExpr->getType(), SubExpr)) + return false; + if (!this->emitInitElem(SubExprT, 0, SubExpr)) + return false; + return this->visitArrayElemInit(1, SubExpr); +} + template bool ByteCodeExprGen::VisitParenExpr(const ParenExpr *E) { return this->delegate(E->getSubExpr()); @@ -594,7 +676,14 @@ bool ByteCodeExprGen::VisitLogicalBinOp(const BinaryOperator *E) { template bool ByteCodeExprGen::VisitComplexBinOp(const BinaryOperator *E) { - assert(Initializing); + // Prepare storage for result. + if (!Initializing) { + std::optional LocalIndex = allocateLocal(E, /*IsExtended=*/false); + if (!LocalIndex) + return false; + if (!this->emitGetPtrLocal(*LocalIndex, E)) + return false; + } const Expr *LHS = E->getLHS(); const Expr *RHS = E->getRHS(); @@ -826,7 +915,7 @@ bool ByteCodeExprGen::visitArrayElemInit(unsigned ElemIndex, return false; if (!this->visitInitializer(Init)) return false; - return this->emitPopPtr(Init); + return this->emitInitPtrPop(Init); } template @@ -854,13 +943,26 @@ bool ByteCodeExprGen::VisitInitListExpr(const InitListExpr *E) { return this->visitInitList(E->inits(), E); if (T->isArrayType()) { - // FIXME: Array fillers. unsigned ElementIndex = 0; for (const Expr *Init : E->inits()) { if (!this->visitArrayElemInit(ElementIndex, Init)) return false; ++ElementIndex; } + + // Expand the filler expression. + // FIXME: This should go away. + if (const Expr *Filler = E->getArrayFiller()) { + const ConstantArrayType *CAT = + Ctx.getASTContext().getAsConstantArrayType(E->getType()); + uint64_t NumElems = CAT->getSize().getZExtValue(); + + for (; ElementIndex != NumElems; ++ElementIndex) { + if (!this->visitArrayElemInit(ElementIndex, Filler)) + return false; + } + } + return true; } @@ -1838,6 +1940,12 @@ bool ByteCodeExprGen::VisitSizeOfPackExpr(const SizeOfPackExpr *E) { return this->emitConst(E->getPackLength(), E); } +template +bool ByteCodeExprGen::VisitGenericSelectionExpr( + const GenericSelectionExpr *E) { + return this->delegate(E->getResultExpr()); +} + template bool ByteCodeExprGen::discard(const Expr *E) { if (E->containsErrors()) return false; @@ -2191,15 +2299,13 @@ bool ByteCodeExprGen::emitConst(T Value, PrimType Ty, const Expr *E) { return this->emitConstSint64(Value, E); case PT_Uint64: return this->emitConstUint64(Value, E); - case PT_IntAP: - case PT_IntAPS: - assert(false); - return false; case PT_Bool: return this->emitConstBool(Value, E); case PT_Ptr: case PT_FnPtr: case PT_Float: + case PT_IntAP: + case PT_IntAPS: llvm_unreachable("Invalid integral type"); break; } @@ -2215,6 +2321,11 @@ bool ByteCodeExprGen::emitConst(T Value, const Expr *E) { template bool ByteCodeExprGen::emitConst(const APSInt &Value, PrimType Ty, const Expr *E) { + if (Ty == PT_IntAPS) + return this->emitConstIntAPS(Value, E); + if (Ty == PT_IntAP) + return this->emitConstIntAP(Value, E); + if (Value.isSigned()) return this->emitConst(Value.getSExtValue(), Ty, E); return this->emitConst(Value.getZExtValue(), Ty, E); @@ -2803,6 +2914,7 @@ bool ByteCodeExprGen::VisitUnaryOperator(const UnaryOperator *E) { return false; return this->visitZeroInitializer(*T, SubExpr->getType(), SubExpr); } + if (!this->visit(SubExpr)) return false; if (!this->emitConstUint8(1, E)) diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.h b/clang/lib/AST/Interp/ByteCodeExprGen.h index 1d5ae46e56346..1710b4446432b 100644 --- a/clang/lib/AST/Interp/ByteCodeExprGen.h +++ b/clang/lib/AST/Interp/ByteCodeExprGen.h @@ -30,7 +30,6 @@ namespace interp { template class LocalScope; template class DestructorScope; -template class RecordScope; template class VariableScope; template class DeclScope; template class OptionScope; @@ -61,6 +60,7 @@ class ByteCodeExprGen : public ConstStmtVisitor, bool>, bool VisitCastExpr(const CastExpr *E); bool VisitIntegerLiteral(const IntegerLiteral *E); bool VisitFloatingLiteral(const FloatingLiteral *E); + bool VisitImaginaryLiteral(const ImaginaryLiteral *E); bool VisitParenExpr(const ParenExpr *E); bool VisitBinaryOperator(const BinaryOperator *E); bool VisitLogicalBinOp(const BinaryOperator *E); @@ -108,6 +108,7 @@ class ByteCodeExprGen : public ConstStmtVisitor, bool>, bool VisitOffsetOfExpr(const OffsetOfExpr *E); bool VisitCXXScalarValueInitExpr(const CXXScalarValueInitExpr *E); bool VisitSizeOfPackExpr(const SizeOfPackExpr *E); + bool VisitGenericSelectionExpr(const GenericSelectionExpr *E); protected: bool visitExpr(const Expr *E) override; @@ -128,15 +129,8 @@ class ByteCodeExprGen : public ConstStmtVisitor, bool>, // If the function does not exist yet, it is compiled. const Function *getFunction(const FunctionDecl *FD); - /// Classifies a type. std::optional classify(const Expr *E) const { - if (E->isGLValue()) { - if (E->getType()->isFunctionType()) - return PT_FnPtr; - return PT_Ptr; - } - - return classify(E->getType()); + return Ctx.classify(E); } std::optional classify(QualType Ty) const { return Ctx.classify(Ty); @@ -180,6 +174,9 @@ class ByteCodeExprGen : public ConstStmtVisitor, bool>, if (!visitInitializer(Init)) return false; + if (!this->emitInitPtr(Init)) + return false; + return this->emitPopPtr(Init); } @@ -191,6 +188,9 @@ class ByteCodeExprGen : public ConstStmtVisitor, bool>, if (!visitInitializer(Init)) return false; + if (!this->emitInitPtr(Init)) + return false; + return this->emitPopPtr(Init); } @@ -202,7 +202,7 @@ class ByteCodeExprGen : public ConstStmtVisitor, bool>, if (!visitInitializer(I)) return false; - return this->emitPopPtr(I); + return this->emitInitPtrPop(I); } bool visitInitList(ArrayRef Inits, const Expr *E); @@ -219,7 +219,6 @@ class ByteCodeExprGen : public ConstStmtVisitor, bool>, friend class VariableScope; friend class LocalScope; friend class DestructorScope; - friend class RecordScope; friend class DeclScope; friend class OptionScope; friend class ArrayIndexScope; diff --git a/clang/lib/AST/Interp/Context.cpp b/clang/lib/AST/Interp/Context.cpp index ba80a015965b3..5f5a6622f10f3 100644 --- a/clang/lib/AST/Interp/Context.cpp +++ b/clang/lib/AST/Interp/Context.cpp @@ -111,11 +111,13 @@ bool Context::evaluateAsInitializer(State &Parent, const VarDecl *VD, #endif // Ensure global variables are fully initialized. - if (shouldBeGloballyIndexed(VD) && !Res.isInvalid() && - (VD->getType()->isRecordType() || VD->getType()->isArrayType())) { + if (shouldBeGloballyIndexed(VD) && + (VD->getType()->isRecordType() || VD->getType()->isArrayType() || + VD->getType()->isAnyComplexType())) { assert(Res.isLValue()); - if (!Res.checkFullyInitialized(C.getState())) + if (!VD->getType()->isAnyComplexType() && + !Res.checkFullyInitialized(C.getState())) return false; // lvalue-to-rvalue conversion. diff --git a/clang/lib/AST/Interp/Context.h b/clang/lib/AST/Interp/Context.h index ab83a8d132246..c7620921e467e 100644 --- a/clang/lib/AST/Interp/Context.h +++ b/clang/lib/AST/Interp/Context.h @@ -70,9 +70,20 @@ class Context final { /// Return the size of T in bits. uint32_t getBitWidth(QualType T) const { return Ctx.getIntWidth(T); } - /// Classifies an expression. + /// Classifies a type. std::optional classify(QualType T) const; + /// Classifies an expression. + std::optional classify(const Expr *E) const { + if (E->isGLValue()) { + if (E->getType()->isFunctionType()) + return PT_FnPtr; + return PT_Ptr; + } + + return classify(E->getType()); + } + const CXXMethodDecl * getOverridingFunction(const CXXRecordDecl *DynamicDecl, const CXXRecordDecl *StaticDecl, diff --git a/clang/lib/AST/Interp/Descriptor.cpp b/clang/lib/AST/Interp/Descriptor.cpp index b330e54baf335..5701cf0acf915 100644 --- a/clang/lib/AST/Interp/Descriptor.cpp +++ b/clang/lib/AST/Interp/Descriptor.cpp @@ -243,18 +243,19 @@ Descriptor::Descriptor(const DeclTy &D, PrimType Type, MetadataSize MD, bool IsMutable) : Source(D), ElemSize(primSize(Type)), Size(ElemSize * NumElems), MDSize(MD.value_or(0)), - AllocSize(align(Size) + sizeof(InitMapPtr) + MDSize), IsConst(IsConst), - IsMutable(IsMutable), IsTemporary(IsTemporary), IsArray(true), - CtorFn(getCtorArrayPrim(Type)), DtorFn(getDtorArrayPrim(Type)), - MoveFn(getMoveArrayPrim(Type)) { + AllocSize(align(MDSize) + align(Size) + sizeof(InitMapPtr)), + IsConst(IsConst), IsMutable(IsMutable), IsTemporary(IsTemporary), + IsArray(true), CtorFn(getCtorArrayPrim(Type)), + DtorFn(getDtorArrayPrim(Type)), MoveFn(getMoveArrayPrim(Type)) { assert(Source && "Missing source"); } /// Primitive unknown-size arrays. -Descriptor::Descriptor(const DeclTy &D, PrimType Type, bool IsTemporary, - UnknownSize) - : Source(D), ElemSize(primSize(Type)), Size(UnknownSizeMark), MDSize(0), - AllocSize(alignof(void *) + sizeof(InitMapPtr)), IsConst(true), +Descriptor::Descriptor(const DeclTy &D, PrimType Type, MetadataSize MD, + bool IsTemporary, UnknownSize) + : Source(D), ElemSize(primSize(Type)), Size(UnknownSizeMark), + MDSize(MD.value_or(0)), + AllocSize(MDSize + sizeof(InitMapPtr) + alignof(void *)), IsConst(true), IsMutable(false), IsTemporary(IsTemporary), IsArray(true), CtorFn(getCtorArrayPrim(Type)), DtorFn(getDtorArrayPrim(Type)), MoveFn(getMoveArrayPrim(Type)) { @@ -275,12 +276,12 @@ Descriptor::Descriptor(const DeclTy &D, const Descriptor *Elem, MetadataSize MD, } /// Unknown-size arrays of composite elements. -Descriptor::Descriptor(const DeclTy &D, const Descriptor *Elem, +Descriptor::Descriptor(const DeclTy &D, const Descriptor *Elem, MetadataSize MD, bool IsTemporary, UnknownSize) : Source(D), ElemSize(Elem->getAllocSize() + sizeof(InlineDescriptor)), - Size(UnknownSizeMark), MDSize(0), - AllocSize(alignof(void *) + sizeof(InitMapPtr)), ElemDesc(Elem), - IsConst(true), IsMutable(false), IsTemporary(IsTemporary), IsArray(true), + Size(UnknownSizeMark), MDSize(MD.value_or(0)), + AllocSize(MDSize + alignof(void *)), ElemDesc(Elem), IsConst(true), + IsMutable(false), IsTemporary(IsTemporary), IsArray(true), CtorFn(ctorArrayDesc), DtorFn(dtorArrayDesc), MoveFn(moveArrayDesc) { assert(Source && "Missing source"); } diff --git a/clang/lib/AST/Interp/Descriptor.h b/clang/lib/AST/Interp/Descriptor.h index 580c200f90952..6cca9d5feeded 100644 --- a/clang/lib/AST/Interp/Descriptor.h +++ b/clang/lib/AST/Interp/Descriptor.h @@ -73,6 +73,10 @@ struct InlineDescriptor { unsigned IsFieldMutable : 1; const Descriptor *Desc; + + InlineDescriptor(const Descriptor *D) + : Offset(sizeof(InlineDescriptor)), IsConst(false), IsInitialized(false), + IsBase(false), IsActive(false), IsFieldMutable(false), Desc(D) {} }; /// Describes a memory block created by an allocation site. @@ -128,15 +132,16 @@ struct Descriptor final { 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); + Descriptor(const DeclTy &D, PrimType Type, MetadataSize MDSize, + bool IsTemporary, UnknownSize); /// Allocates a descriptor for an array of composites. Descriptor(const DeclTy &D, const Descriptor *Elem, MetadataSize MD, unsigned NumElems, bool IsConst, bool IsTemporary, bool IsMutable); /// Allocates a descriptor for an array of composites of unknown size. - Descriptor(const DeclTy &D, const Descriptor *Elem, bool IsTemporary, - UnknownSize); + Descriptor(const DeclTy &D, const Descriptor *Elem, MetadataSize MD, + bool IsTemporary, UnknownSize); /// Allocates a descriptor for a record. Descriptor(const DeclTy &D, const Record *R, MetadataSize MD, bool IsConst, diff --git a/clang/lib/AST/Interp/Disasm.cpp b/clang/lib/AST/Interp/Disasm.cpp index d276df8f29262..eba437e05f59d 100644 --- a/clang/lib/AST/Interp/Disasm.cpp +++ b/clang/lib/AST/Interp/Disasm.cpp @@ -12,6 +12,7 @@ #include "Floating.h" #include "Function.h" +#include "IntegralAP.h" #include "Opcode.h" #include "PrimType.h" #include "Program.h" @@ -37,6 +38,20 @@ template <> inline Floating ReadArg(Program &P, CodePtr &OpPC) { return F; } +template <> +inline IntegralAP ReadArg>(Program &P, CodePtr &OpPC) { + IntegralAP I = IntegralAP::deserialize(*OpPC); + OpPC += align(I.bytesToSerialize()); + return I; +} + +template <> +inline IntegralAP ReadArg>(Program &P, CodePtr &OpPC) { + IntegralAP I = IntegralAP::deserialize(*OpPC); + OpPC += align(I.bytesToSerialize()); + return I; +} + LLVM_DUMP_METHOD void Function::dump() const { dump(llvm::errs()); } LLVM_DUMP_METHOD void Function::dump(llvm::raw_ostream &OS) const { diff --git a/clang/lib/AST/Interp/IntegralAP.h b/clang/lib/AST/Interp/IntegralAP.h index 55e29caa1cd74..bab9774288bfa 100644 --- a/clang/lib/AST/Interp/IntegralAP.h +++ b/clang/lib/AST/Interp/IntegralAP.h @@ -263,6 +263,31 @@ template class IntegralAP final { *R = IntegralAP(A.V.lshr(ShiftAmount)); } + // === Serialization support === + size_t bytesToSerialize() const { + // 4 bytes for the BitWidth followed by N bytes for the actual APInt. + return sizeof(uint32_t) + (V.getBitWidth() / CHAR_BIT); + } + + void serialize(std::byte *Buff) const { + assert(V.getBitWidth() < std::numeric_limits::max()); + uint32_t BitWidth = V.getBitWidth(); + + std::memcpy(Buff, &BitWidth, sizeof(uint32_t)); + llvm::StoreIntToMemory(V, (uint8_t *)(Buff + sizeof(uint32_t)), + BitWidth / CHAR_BIT); + } + + static IntegralAP deserialize(const std::byte *Buff) { + uint32_t BitWidth; + std::memcpy(&BitWidth, Buff, sizeof(uint32_t)); + IntegralAP Val(APInt(BitWidth, 0ull, !Signed)); + + llvm::LoadIntFromMemory(Val.V, (const uint8_t *)Buff + sizeof(uint32_t), + BitWidth / CHAR_BIT); + return Val; + } + private: template