diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 02303c5e6cc57..ff56ace71e0ed 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,152 +1,2 @@ -# This file lists reviewers that are auto-assigned when a pull request modifies -# certain files or directories. If you add yourself to this file, you commit to -# reviewing a large fraction of pull requests in the relevant area. -# -# The GitHub "code owners" mechanism is used exclusively to auto-assign -# reviewers and does not carry significance beyond that. It is not necessary -# to receive an approval from a "code owner" in particular -- any LLVM project -# member can approve pull requests. -# -# Note that GitHub's concept of "code owner" is independent from LLVM's own -# "code owner" concept, they merely happen to share terminology. See -# https://llvm.org/docs/DeveloperPolicy.html#code-owners, as well as the -# CODE_OWNERS.txt files in the respective subproject directories. - -/libcxx/ @llvm/reviewers-libcxx -/libcxxabi/ @llvm/reviewers-libcxxabi -/libunwind/ @llvm/reviewers-libunwind -/runtimes/ @llvm/reviewers-libcxx - -/llvm/lib/Analysis/BasicAliasAnalysis.cpp @nikic -/llvm/lib/Analysis/InstructionSimplify.cpp @nikic -/llvm/lib/Analysis/LazyValueInfo.cpp @nikic -/llvm/lib/Analysis/ScalarEvolution.cpp @nikic -/llvm/lib/Analysis/ValueTracking.cpp @nikic -/llvm/lib/IR/ConstantRange.cpp @nikic -/llvm/lib/IR/Core.cpp @nikic -/llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp @nikic -/llvm/lib/Transforms/Scalar/MemCpyOptimizer.cpp @nikic -/llvm/lib/Transforms/InstCombine/ @nikic - -/clang/include/clang/Sema/Sema.h @Endilll -/clang/test/CXX/drs/ @Endilll -/clang/www/cxx_dr_status.html @Endilll -/clang/www/make_cxx_dr_status @Endilll - -clang/lib/AST/Interp/ @tbaederr -clang/test/AST/Interp/ @tbaederr - -/clang/include/clang/CIR @lanza @bcardosolopes -/clang/lib/CIR @lanza @bcardosolopes -/clang/tools/cir-* @lanza @bcardosolopes - -/lldb/ @JDevlieghere - -# MLIR Interfaces. -/mlir/include/mlir/Interfaces/TilingInterface.* @MaheshRavishankar @nicolasvasilache -/mlir/lib/Interfaces/TilingInterface.* @MaheshRavishankar @nicolasvasilache -/mlir/include/mlir/Interfaces/ValueBoundsOpInterface.* @matthias-springer -/mlir/lib/Interfaces/ValueBoundsOpInterface.* @matthias-springer -/mlir/**/ValueBoundsOpInterfaceImpl.* @matthias-springer -/mlir/include/mlir/Interfaces/RuntimeVerifiableOpInterface.* @matthias-springer -/mlir/lib/Interfaces/RuntimeVerifiableOpInterface.* @matthias-springer -/mlir/**/RuntimeVerifiableOpInterfaceImpl.* @matthias-springer -/mlir/include/mlir/Interfaces/SubsetOpInterface.* @matthias-springer -/mlir/lib/Interfaces/SubsetOpInterface.* @matthias-springer -/mlir/**/SubsetOpInterfaceImpl.* @matthias-springer -/mlir/include/mlir/Interfaces/DestinationStyleOpInterface.* @matthias-springer -/mlir/lib/Interfaces/DestinationStyleOpInterface.* @matthias-springer - -# Bufferization Dialect in MLIR. -/mlir/include/mlir/Dialect/Bufferization @matthias-springer -/mlir/lib/Dialect/Bufferization @matthias-springer -/mlir/**/BufferizableOpInterfaceImpl.* @matthias-springer -/mlir/Dialect/*/Transforms/Bufferize.cpp @matthias-springer - -# Linalg Dialect in MLIR. -/mlir/include/mlir/Dialect/Linalg @dcaballe @nicolasvasilache @rengolin -/mlir/lib/Dialect/Linalg @dcaballe @nicolasvasilache @rengolin -/mlir/lib/Dialect/Linalg/Transforms/DecomposeLinalgOps.cpp @MaheshRavishankar @nicolasvasilache -/mlir/lib/Dialect/Linalg/Transforms/DropUnitDims.cpp @dcaballe @MaheshRavishankar @nicolasvasilache -/mlir/lib/Dialect/Linalg/Transforms/ElementwiseOpFusion.cpp @MaheshRavishankar @nicolasvasilache -/mlir/lib/Dialect/Linalg/Transforms/DataLayoutPropagation.cpp @hanhanW @nicolasvasilache -/mlir/lib/Dialect/Linalg/Transforms/Transforms.cpp @dcaballe @hanhanW @nicolasvasilache -/mlir/lib/Dialect/Linalg/Transforms/Vectorization.cpp @banach-space @dcaballe @hanhanW @nicolasvasilache - -# MemRef Dialect in MLIR. -/mlir/lib/Dialect/MemRef/Transforms/EmulateNarrowType.cpp @MaheshRavishankar @nicolasvasilache - -# Vector Dialect in MLIR. -/mlir/**/*AMX* @aartbik @dcaballe -/mlir/**/*Neon* @banach-space @dcaballe @nicolasvasilache -/mlir/**/*SME* @banach-space @dcaballe @nicolasvasilache -/mlir/**/*SVE* @banach-space @dcaballe @nicolasvasilache -/mlir/**/*VectorInterfaces* @dcaballe @nicolasvasilache -/mlir/**/*VectorToSCF* @banach-space @dcaballe @matthias-springer @nicolasvasilache -/mlir/**/*VectorToLLVM* @banach-space @dcaballe @nicolasvasilache -/mlir/**/*X86Vector* @aartbik @dcaballe @nicolasvasilache -/mlir/include/mlir/Dialect/Vector @banach-space @dcaballe @nicolasvasilache -/mlir/include/mlir/Dialect/Vector/IR @kuhar -/mlir/lib/Dialect/Vector @banach-space @dcaballe @nicolasvasilache -/mlir/lib/Dialect/Vector/Transforms/* @banach-space @dcaballe @hanhanW @nicolasvasilache -/mlir/lib/Dialect/Vector/Transforms/VectorEmulateNarrowType.cpp @banach-space @dcaballe @MaheshRavishankar @nicolasvasilache -/mlir/**/*EmulateNarrowType* @dcaballe @hanhanW - -# Presburger library in MLIR -/mlir/**/*Presburger* @Groverkss @Superty - -# Tensor Dialect in MLIR. -/mlir/lib/Dialect/Tensor/IR/TensorTilingInterfaceImpl.cpp @hanhanW @nicolasvasilache -/mlir/lib/Dialect/Tensor/Transforms/* @hanhanW @nicolasvasilache - -# Transform Dialect in MLIR. -/mlir/include/mlir/Dialect/Transform/* @ftynse @nicolasvasilache -/mlir/lib/Dialect/Transform/* @ftynse @nicolasvasilache -/mlir/**/*TransformOps* @ftynse @nicolasvasilache - -# SPIR-V Dialect in MLIR. -/mlir/**/SPIRV/ @antiagainst @kuhar -/mlir/**/SPIRVTo*/ @antiagainst @kuhar -/mlir/**/*ToSPIRV/ @antiagainst @kuhar -/mlir/tools/mlir-tblgen/SPIRVUtilsGen.cpp @antiagainst @kuhar - -# MLIR Sparsifier. -/mlir/**/*SparseTensor*/ @aartbik @PeimingLiu @yinying-lisa-li @matthias-springer - -# MLIR NVGPU Dialect -/mlir/**/NVGPU*/ @grypp -/mlir/test/**/CUDA/ @grypp - -# MLIR NVVM Dialect in MLIR -/mlir/**/LLVMIR/**/BasicPtxBuilderInterface* @grypp -/mlir/**/NVVM* @grypp - -# MLIR Index Dialect -/mlir/**/Index* @mogball - -# MLIR Python Bindings -/mlir/test/python/ @ftynse @makslevental @stellaraccident -/mlir/python/ @ftynse @makslevental @stellaraccident - -# MLIR Mem2Reg/SROA -/mlir/**/Transforms/Mem2Reg.* @moxinilian -/mlir/**/Transforms/SROA.* @moxinilian - -# BOLT -/bolt/ @aaupov @maksfb @rafaelauler @ayermolo @dcci - -# Bazel build system. -/utils/bazel/ @rupprecht @keith - -# InstallAPI and TextAPI -/llvm/**/TextAPI/ @cyndyishida -/clang/**/InstallAPI/ @cyndyishida -/clang/tools/clang-installapi/ @cyndyishida - -# ExtractAPI -/clang/**/ExtractAPI @daniel-grumberg - -# DWARFLinker, dwarfutil, dsymutil -/llvm/**/DWARFLinker/ @JDevlieghere -/llvm/**/dsymutil/ @JDevlieghere -/llvm/**/llvm-dwarfutil/ @JDevlieghere +# For the release branch @swiftlang/llvm-project-branch-managers needs to approve the changes +* @swiftlang/llvm-project-branch-managers diff --git a/.github/workflows/get-llvm-version/action.yml b/.github/workflows/get-llvm-version/action.yml new file mode 100644 index 0000000000000..2218d926fc13d --- /dev/null +++ b/.github/workflows/get-llvm-version/action.yml @@ -0,0 +1,26 @@ +name: Get LLVM Version +description: >- + Get the LLVM version from the llvm-project source tree. This action assumes + the llvm-project sources have already been checked out into GITHUB_WORKSPACE. + +outputs: + major: + description: LLVM major version + value: ${{ steps.version.outputs.major }} + minor: + description: LLVM minor version + value: ${{ steps.version.outputs.minor }} + patch: + description: LLVM patch version + value: ${{ steps.version.outputs.patch }} + +runs: + using: "composite" + steps: + - name: Get Version + shell: bash + id: version + run: | + for v in major minor patch; do + echo "$v=`llvm/utils/release/get-llvm-version.sh --$v`" >> $GITHUB_OUTPUT + done diff --git a/.github/workflows/libclang-abi-tests.yml b/.github/workflows/libclang-abi-tests.yml index 972d21c3bcedf..9e839ff49e283 100644 --- a/.github/workflows/libclang-abi-tests.yml +++ b/.github/workflows/libclang-abi-tests.yml @@ -33,9 +33,9 @@ jobs: ABI_HEADERS: ${{ steps.vars.outputs.ABI_HEADERS }} ABI_LIBS: ${{ steps.vars.outputs.ABI_LIBS }} BASELINE_VERSION_MAJOR: ${{ steps.vars.outputs.BASELINE_VERSION_MAJOR }} - LLVM_VERSION_MAJOR: ${{ steps.version.outputs.LLVM_VERSION_MAJOR }} - LLVM_VERSION_MINOR: ${{ steps.version.outputs.LLVM_VERSION_MINOR }} - LLVM_VERSION_PATCH: ${{ steps.version.outputs.LLVM_VERSION_PATCH }} + LLVM_VERSION_MAJOR: ${{ steps.version.outputs.major }} + LLVM_VERSION_MINOR: ${{ steps.version.outputs.minor }} + LLVM_VERSION_PATCH: ${{ steps.version.outputs.patch }} steps: - name: Checkout source uses: actions/checkout@v4 @@ -44,14 +44,14 @@ jobs: - name: Get LLVM version id: version - uses: llvm/actions/get-llvm-version@main + uses: ./.github/workflows/get-llvm-version - name: Setup Variables id: vars run: | remote_repo='https://github.com/llvm/llvm-project' - if [ ${{ steps.version.outputs.LLVM_VERSION_PATCH }} -eq 0 ]; then - major_version=$(( ${{ steps.version.outputs.LLVM_VERSION_MAJOR }} - 1)) + if [ ${{ steps.version.outputs.patch }} -eq 0 ]; then + major_version=$(( ${{ steps.version.outputs.major }} - 1)) baseline_ref="llvmorg-$major_version.1.0" # If there is a minor release, we want to use that as the base line. @@ -73,8 +73,8 @@ jobs: } >> "$GITHUB_OUTPUT" else { - echo "BASELINE_VERSION_MAJOR=${{ steps.version.outputs.LLVM_VERSION_MAJOR }}" - echo "BASELINE_REF=llvmorg-${{ steps.version.outputs.LLVM_VERSION_MAJOR }}.1.0" + echo "BASELINE_VERSION_MAJOR=${{ steps.version.outputs.major }}" + echo "BASELINE_REF=llvmorg-${{ steps.version.outputs.major }}.1.0" echo "ABI_HEADERS=." echo "ABI_LIBS=libclang.so libclang-cpp.so" } >> "$GITHUB_OUTPUT" diff --git a/.github/workflows/llvm-project-tests.yml b/.github/workflows/llvm-project-tests.yml index 0a228c41f354e..17a54be16badc 100644 --- a/.github/workflows/llvm-project-tests.yml +++ b/.github/workflows/llvm-project-tests.yml @@ -131,6 +131,7 @@ jobs: -DCMAKE_BUILD_TYPE=Release \ -DLLVM_ENABLE_ASSERTIONS=ON \ -DLLDB_INCLUDE_TESTS=OFF \ + -DLIBCLC_TARGETS_TO_BUILD="amdgcn--;amdgcn--amdhsa;r600--;nvptx--;nvptx64--;nvptx--nvidiacl;nvptx64--nvidiacl" \ -DCMAKE_C_COMPILER_LAUNCHER=sccache \ -DCMAKE_CXX_COMPILER_LAUNCHER=sccache \ $extra_cmake_args \ @@ -142,8 +143,6 @@ jobs: env: LLVM_BUILDDIR: ${{ steps.build-llvm.outputs.llvm-builddir }} run: | - # Make sure all of LLVM libraries that llvm-config needs are built. + # The libclc tests don't have a generated check target so all we can + # do is build it. ninja -C "$LLVM_BUILDDIR" - cmake -G Ninja -S libclc -B libclc-build -DLLVM_DIR="$LLVM_BUILDDIR"/lib/cmake/llvm -DLIBCLC_TARGETS_TO_BUILD="amdgcn--;amdgcn--amdhsa;r600--;nvptx--;nvptx64--;nvptx--nvidiacl;nvptx64--nvidiacl" - ninja -C libclc-build - ninja -C libclc-build test diff --git a/.github/workflows/llvm-tests.yml b/.github/workflows/llvm-tests.yml index 64d60bc3da45e..26e644229aaa2 100644 --- a/.github/workflows/llvm-tests.yml +++ b/.github/workflows/llvm-tests.yml @@ -43,9 +43,9 @@ jobs: ABI_HEADERS: ${{ steps.vars.outputs.ABI_HEADERS }} BASELINE_VERSION_MAJOR: ${{ steps.vars.outputs.BASELINE_VERSION_MAJOR }} BASELINE_VERSION_MINOR: ${{ steps.vars.outputs.BASELINE_VERSION_MINOR }} - LLVM_VERSION_MAJOR: ${{ steps.version.outputs.LLVM_VERSION_MAJOR }} - LLVM_VERSION_MINOR: ${{ steps.version.outputs.LLVM_VERSION_MINOR }} - LLVM_VERSION_PATCH: ${{ steps.version.outputs.LLVM_VERSION_PATCH }} + LLVM_VERSION_MAJOR: ${{ steps.version.outputs.major }} + LLVM_VERSION_MINOR: ${{ steps.version.outputs.minor }} + LLVM_VERSION_PATCH: ${{ steps.version.outputs.patch }} steps: - name: Checkout source uses: actions/checkout@v4 @@ -54,7 +54,7 @@ jobs: - name: Get LLVM version id: version - uses: llvm/actions/get-llvm-version@main + uses: ./.github/workflows/get-llvm-version - name: Setup Variables id: vars @@ -66,14 +66,14 @@ jobs: # 18.1.0 We want to check 17.0.x # 18.1.1 We want to check 18.1.0 echo "BASELINE_VERSION_MINOR=1" >> "$GITHUB_OUTPUT" - if [ ${{ steps.version.outputs.LLVM_VERSION_PATCH }} -eq 0 ]; then + if [ ${{ steps.version.outputs.patch }} -eq 0 ]; then { - echo "BASELINE_VERSION_MAJOR=$(( ${{ steps.version.outputs.LLVM_VERSION_MAJOR }} - 1))" + echo "BASELINE_VERSION_MAJOR=$(( ${{ steps.version.outputs.major }} - 1))" echo "ABI_HEADERS=llvm-c" } >> "$GITHUB_OUTPUT" else { - echo "BASELINE_VERSION_MAJOR=${{ steps.version.outputs.LLVM_VERSION_MAJOR }}" + echo "BASELINE_VERSION_MAJOR=${{ steps.version.outputs.major }}" echo "ABI_HEADERS=." } >> "$GITHUB_OUTPUT" fi diff --git a/.github/workflows/release-binaries-all.yml b/.github/workflows/release-binaries-all.yml new file mode 100644 index 0000000000000..394b0c74d24ed --- /dev/null +++ b/.github/workflows/release-binaries-all.yml @@ -0,0 +1,98 @@ +name: Release Binaries All + +permissions: + contents: read # Default everything to read-only + +on: + workflow_dispatch: + inputs: + release-version: + description: 'Release Version' + required: true + type: string + upload: + description: 'Upload binaries to the release page' + required: true + default: false + type: boolean + + workflow_call: + inputs: + release-version: + description: 'Release Version' + required: true + type: string + upload: + description: 'Upload binaries to the release page' + required: true + default: false + type: boolean + + pull_request: + types: + - opened + - synchronize + - reopened + # When a PR is closed, we still start this workflow, but then skip + # all the jobs, which makes it effectively a no-op. The reason to + # do this is that it allows us to take advantage of concurrency groups + # to cancel in progress CI jobs whenever the PR is closed. + - closed + paths: + - '.github/workflows/release-binaries-all.yml' + - '.github/workflows/release-binaries.yml' + - '.github/workflows/release-binaries-setup-stage/*' + - '.github/workflows/release-binaries-save-stage/*' + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || 'dispatch' }} + cancel-in-progress: True + +jobs: + setup-variables: + if: >- + (github.event_name != 'pull_request' || github.event.action != 'closed') + runs-on: ubuntu-22.04 + outputs: + release-version: ${{ steps.vars.outputs.release-version }} + upload: ${{ steps.vars.outputs.upload }} + steps: + - shell: bash + id: vars + run: | + upload="${{ inputs.upload }}" + release_version="${{ inputs.release-version }}" + if [ "${{ github.event_name }}" = "pull_request" ]; then + upload="false" + release_version="" + fi + echo "release-version=$release_version" >> "$GITHUB_OUTPUT" + echo "upload=$upload" >> "$GITHUB_OUTPUT" + + release-binaries-all: + name: Build Release Binaries + needs: + - setup-variables + permissions: + contents: write # For release uploads + id-token: write # For artifact attestations + attestations: write # For artifact attestations + strategy: + fail-fast: false + matrix: + runs-on: + - ubuntu-22.04 + - windows-2022 + - macos-13 + - macos-14 + + uses: ./.github/workflows/release-binaries.yml + with: + release-version: "${{ needs.setup-variables.outputs.release-version }}" + upload: ${{ needs.setup-variables.outputs.upload == 'true'}} + runs-on: "${{ matrix.runs-on }}" + secrets: + # This will be empty for pull_request events, but that's fine, because + # the release-binaries workflow does not use this secret for the + # pull_request event. + RELEASE_TASKS_USER_TOKEN: ${{ secrets.RELEASE_TASKS_USER_TOKEN }} diff --git a/.github/workflows/release-binaries-save-stage/action.yml b/.github/workflows/release-binaries-save-stage/action.yml new file mode 100644 index 0000000000000..f08088c7bc56f --- /dev/null +++ b/.github/workflows/release-binaries-save-stage/action.yml @@ -0,0 +1,44 @@ +name: Save Stage +description: >- + Upload the source and binary directories from a build stage so that they + can be re-used in the next stage. This action is used to the release + binaries workflow into multiple stages to avoid the 6 hour timeout on + the GitHub hosted runners. +inputs: + build-prefix: + description: "Directory containing the build directory." + required: true + type: 'string' + +permissions: + contents: read + +runs: + using: "composite" + steps: + # We need to create an archive of the build directory, because it has too + # many files to upload. + - name: Package Build and Source Directories + shell: bash + run: | + # Remove .git/config to avoid leaking GITHUB_TOKEN stored there. + # See https://unit42.paloaltonetworks.com/github-repo-artifacts-leak-tokens/ + rm -Rf .git/config + # Windows does not support symlinks, so we need to dereference them. + tar --exclude build/ ${{ (runner.os == 'Windows' && '-h') || '' }} -c . | zstd -T0 -c > ../llvm-project.tar.zst + mv ../llvm-project.tar.zst . + tar -C ${{ inputs.build-prefix }} -c build/ | zstd -T0 -c > build.tar.zst + + - name: Upload Stage 1 Source + uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 #v4.3.0 + with: + name: ${{ runner.os }}-${{ runner.arch }}-${{ github.job }}-source + path: llvm-project.tar.zst + retention-days: 2 + + - name: Upload Stage 1 Build Dir + uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 #v4.3.0 + with: + name: ${{ runner.os}}-${{ runner.arch }}-${{ github.job }}-build + path: build.tar.zst + retention-days: 2 diff --git a/.github/workflows/release-binaries-setup-stage/action.yml b/.github/workflows/release-binaries-setup-stage/action.yml new file mode 100644 index 0000000000000..f5e5db27e6595 --- /dev/null +++ b/.github/workflows/release-binaries-setup-stage/action.yml @@ -0,0 +1,59 @@ +name: Setup Stage +description: >- + Setup the next stage of the release binaries workflow. This sets up the + environment correctly for a new stage of the release binaries workflow + and also restores the source and build directory from the previous stage. + +inputs: + previous-artifact: + description: >- + A unique descriptor for the artifact from the previous stage. This will + be used to construct the final artifact pattern, which is: + $RUNNER_OS-$RUNNER_ARCH-$PREVIOUS_ARTIFACT-* + required: false + type: 'string' + +outputs: + build-prefix: + description: "Directory containing the build directory." + value: ${{ steps.build-prefix.outputs.build-prefix }} + +runs: + using: "composite" + steps: + - name: Install Ninja + uses: llvm/actions/install-ninja@22e9f909d35b50bd1181709564bfe816eaeaae81 # main + + - name: Setup Windows + if: startsWith(runner.os, 'Windows') + uses: llvm/actions/setup-windows@main + with: + arch: amd64 + + - name: Set Build Prefix + id: build-prefix + shell: bash + run: | + build_prefix=`pwd` + if [ "${{ runner.os }}" = "Linux" ]; then + sudo chown $USER:$USER /mnt/ + build_prefix=/mnt/ + fi + echo "build-prefix=$build_prefix" >> $GITHUB_OUTPUT + + - name: Download Previous Stage Artifact + if: ${{ inputs.previous-artifact }} + id: download + uses: actions/download-artifact@6b208ae046db98c579e8a3aa621ab581ff575935 # v4.1.1 + with: + pattern: ${{ runner.os }}-${{ runner.arch }}-${{ inputs.previous-artifact }}-* + merge-multiple: true + + - name: Unpack Artifact + if: ${{ steps.download.outputs.download-path }} + shell: bash + run: | + tar --zstd -xf llvm-project.tar.zst + rm llvm-project.tar.zst + tar --zstd -C ${{ steps.build-prefix.outputs.build-prefix}} -xf build.tar.zst + rm build.tar.zst diff --git a/.github/workflows/release-binaries.yml b/.github/workflows/release-binaries.yml index 7de4d00334d14..f24e25879b96b 100644 --- a/.github/workflows/release-binaries.yml +++ b/.github/workflows/release-binaries.yml @@ -5,28 +5,43 @@ on: inputs: release-version: description: 'Release Version' - required: true + required: false type: string upload: description: 'Upload binaries to the release page' required: true default: false type: boolean + runs-on: + description: "Runner to use for the build" + required: true + type: choice + options: + - ubuntu-22.04 + - windows-2022 + - macos-13 + - macos-14 workflow_call: inputs: release-version: description: 'Release Version' - required: true + required: false type: string upload: description: 'Upload binaries to the release page' required: true default: false type: boolean - schedule: - # * is a special character in YAML so you have to quote this string - - cron: '0 8 1 * *' + runs-on: + description: "Runner to use for the build" + required: true + type: string + secrets: + RELEASE_TASKS_USER_TOKEN: + description: "Secret used to check user permissions." + required: false + permissions: contents: read # Default everything to read-only @@ -34,30 +49,45 @@ permissions: jobs: prepare: name: Prepare to build binaries - runs-on: ubuntu-22.04 + runs-on: ${{ inputs.runs-on }} if: github.repository == 'llvm/llvm-project' outputs: release-version: ${{ steps.vars.outputs.release-version }} ref: ${{ steps.vars.outputs.ref }} upload: ${{ steps.vars.outputs.upload }} + target-cmake-flags: ${{ steps.vars.outputs.target-cmake-flags }} + build-flang: ${{ steps.vars.outputs.build-flang }} + enable-pgo: ${{ steps.vars.outputs.enable-pgo }} + release-binary-basename: ${{ steps.vars.outputs.release-binary-basename }} + release-binary-filename: ${{ steps.vars.outputs.release-binary-filename }} steps: + # It's good practice to use setup-python, but this is also required on macos-14 + # due to https://github.com/actions/runner-images/issues/10385 + - uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f + with: + python-version: '3.12' + - name: Checkout LLVM uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Install Dependencies + shell: bash run: | pip install --require-hashes -r ./llvm/utils/git/requirements.txt - name: Check Permissions + if: github.event_name != 'pull_request' env: GITHUB_TOKEN: ${{ github.token }} USER_TOKEN: ${{ secrets.RELEASE_TASKS_USER_TOKEN }} + shell: bash run: | ./llvm/utils/release/./github-upload-release.py --token "$GITHUB_TOKEN" --user ${{ github.actor }} --user-token "$USER_TOKEN" check-permissions - name: Collect Variables id: vars + shell: bash # In order for the test-release.sh script to run correctly, the LLVM # source needs to be at the following location relative to the build dir: # | X.Y.Z-rcN | ./rcN/llvm-project @@ -67,242 +97,396 @@ jobs: # | X.Y.Z-rcN | -rc N -test-asserts # | X.Y.Z | -final run: | - tag="${{ github.ref_name }}" trimmed=$(echo ${{ inputs.release-version }} | xargs) - [[ "$trimmed" != "" ]] && tag="llvmorg-$trimmed" - if [ "$tag" = "main" ]; then - # If tag is main, then we've been triggered by a scheduled so pass so - # use the head commit as the tag. - tag=`git rev-parse HEAD` + if [ -n "$trimmed" ]; then + release_version="$trimmed" + ref="llvmorg-$release_version" + else + release_version="${{ (github.event_name == 'pull_request' && format('PR{0}', github.event.pull_request.number)) || 'CI'}}-${{ github.sha }}" + ref=${{ github.sha }} fi if [ -n "${{ inputs.upload }}" ]; then upload="${{ inputs.upload }}" else upload="false" fi - bash .github/workflows/set-release-binary-outputs.sh "$tag" "$upload" + echo "release-version=$release_version">> $GITHUB_OUTPUT + echo "ref=$ref" >> $GITHUB_OUTPUT + echo "upload=$upload" >> $GITHUB_OUTPUT + + release_binary_basename="LLVM-$release_version-${{ runner.os }}-${{ runner.arch }}" + echo "release-binary-basename=$release_binary_basename" >> $GITHUB_OUTPUT + echo "release-binary-filename=$release_binary_basename.tar.xz" >> $GITHUB_OUTPUT + + # Detect necessary CMake flags + target="${{ runner.os }}-${{ runner.arch }}" + echo "enable-pgo=false" >> $GITHUB_OUTPUT + target_cmake_flags="-DLLVM_RELEASE_ENABLE_PGO=OFF" + # The macOS builds try to cross compile some libraries so we need to + # add extra CMake args to disable them. + # See https://github.com/llvm/llvm-project/issues/99767 + if [ "${{ runner.os }}" = "macOS" ]; then + target_cmake_flags="$target_cmake_flags -DBOOTSTRAP_COMPILER_RT_ENABLE_IOS=OFF" + if [ "${{ runner.arch }}" = "ARM64" ]; then + arches=arm64 + else + arches=x86_64 + fi + target_cmake_flags="$target_cmake_flags -DBOOTSTRAP_DARWIN_osx_ARCHS=$arches -DBOOTSTRAP_DARWIN_osx_BUILTIN_ARCHS=$arches" + fi - build-stage1-linux: - name: "Build Stage 1 Linux" + build_flang="true" + + if [ "${{ runner.os }}" = "Windows" ]; then + # The build times out on Windows, so we need to disable LTO. + target_cmake_flags="$target_cmake_flags -DLLVM_RELEASE_ENABLE_LTO=OFF" + fi + + echo "target-cmake-flags=$target_cmake_flags" >> $GITHUB_OUTPUT + echo "build-flang=$build_flang" >> $GITHUB_OUTPUT + + build-stage1: + name: "Build Stage 1" needs: prepare - runs-on: ubuntu-22.04 if: github.repository == 'llvm/llvm-project' + runs-on: ${{ inputs.runs-on }} steps: + + - name: Checkout Actions + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + ref: ${{ (github.event_name == 'pull_request' && github.sha) || 'main' }} + sparse-checkout: | + .github/workflows/ + sparse-checkout-cone-mode: false + # Check out outside of working directory so the source checkout doesn't + # remove it. + path: workflows + + # actions/checkout does not support paths outside of the GITHUB_WORKSPACE. + # Also, anything that we put inside of GITHUB_WORKSPACE will be overwritten + # by future actions/checkout steps. Therefore, in order to checkout the + # latest actions from main, we need to first checkout out the actions inside of + # GITHUB_WORKSPACE (see previous step), then use actions/checkout to checkout + # the code being built and the move the actions from main back into GITHUB_WORKSPACE, + # becasue the uses on composite actions only reads workflows from inside GITHUB_WORKSPACE. + - shell: bash + run: mv workflows ../workflows-main + - name: Checkout LLVM uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: ref: ${{ needs.prepare.outputs.ref }} - - name: Install Ninja - uses: llvm/actions/install-ninja@22e9f909d35b50bd1181709564bfe816eaeaae81 # main + - name: Copy main workflows + shell: bash + run: | + mv ../workflows-main . + + - name: Setup Stage + id: setup-stage + uses: ./workflows-main/.github/workflows/release-binaries-setup-stage - name: Setup sccache uses: hendrikmuhs/ccache-action@ca3acd2731eef11f1572ccb126356c2f9298d35e # v1.2.9 with: - max-size: 250M - key: sccache-${{ runner.os }}-release + # Default to 2G to workaround: https://github.com/hendrikmuhs/ccache-action/issues/174 + max-size: 2G + key: sccache-${{ runner.os }}-${{ runner.arch }}-release variant: sccache - name: Build Stage 1 Clang + id: build + shell: bash run: | - sudo chown $USER:$USER /mnt/ - cmake -G Ninja -C clang/cmake/caches/Release.cmake -DCMAKE_C_COMPILER_LAUNCHER=sccache -DCMAKE_CXX_COMPILER_LAUNCHER=sccache -S llvm -B /mnt/build - ninja -v -C /mnt/build - - # We need to create an archive of the build directory, because it has too - # many files to upload. - - name: Package Build and Source Directories - run: | - tar -c . | zstd -T0 -c > llvm-project.tar.zst - tar -C /mnt/ -c build/ | zstd -T0 -c > build.tar.zst - - - name: Upload Stage 1 Source - uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 #v4.3.0 + # There were some issues on the ARM64 MacOS runners with trying to build x86 object, + # so we need to set some extra cmake flags to disable this. + cmake -G Ninja -S llvm -B ${{ steps.setup-stage.outputs.build-prefix }}/build \ + ${{ needs.prepare.outputs.target-cmake-flags }} \ + -C clang/cmake/caches/Release.cmake \ + -DBOOTSTRAP_LLVM_PARALLEL_LINK_JOBS=1 \ + -DBOOTSTRAP_CPACK_PACKAGE_FILE_NAME="${{ needs.prepare.outputs.release-binary-basename }}" \ + -DCMAKE_C_COMPILER_LAUNCHER=sccache \ + -DCMAKE_CXX_COMPILER_LAUNCHER=sccache + ninja -v -C ${{ steps.setup-stage.outputs.build-prefix }}/build + # There is a race condition on the MacOS builders and this command is here + # to help debug that when it happens. + ls -ltr ${{ steps.setup-stage.outputs.build-prefix }}/build + + - name: Save Stage + uses: ./workflows-main/.github/workflows/release-binaries-save-stage with: - name: stage1-source - path: llvm-project.tar.zst - retention-days: 2 + build-prefix: ${{ steps.setup-stage.outputs.build-prefix }} - - name: Upload Stage 1 Build Dir - uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 #v4.3.0 - with: - name: stage1-build - path: build.tar.zst - retention-days: 2 - - build-stage2-linux: - name: "Build Stage 2 Linux" + build-stage2: + name: "Build Stage 2" needs: - prepare - - build-stage1-linux - runs-on: ubuntu-22.04 + - build-stage1 if: github.repository == 'llvm/llvm-project' + runs-on: ${{ inputs.runs-on }} steps: - - name: Install Ninja - uses: llvm/actions/install-ninja@22e9f909d35b50bd1181709564bfe816eaeaae81 # main - - - name: Download Stage 1 Artifacts - uses: actions/download-artifact@6b208ae046db98c579e8a3aa621ab581ff575935 # v4.1.1 + - name: Checkout Actions + uses: actions/checkout@v4 with: - pattern: stage1-* - merge-multiple: true - - - name: Unpack Artifacts - run: | - tar --zstd -xf llvm-project.tar.zst - rm llvm-project.tar.zst - sudo chown $USER:$USER /mnt/ - tar --zstd -C /mnt -xf build.tar.zst - rm build.tar.zst + ref: ${{ (github.event_name == 'pull_request' && github.sha) || 'main' }} + sparse-checkout: | + .github/workflows/ + sparse-checkout-cone-mode: false + path: workflows + - name: Setup Stage + id: setup-stage + uses: ./workflows/.github/workflows/release-binaries-setup-stage + with: + previous-artifact: build-stage1 - name: Build Stage 2 # Re-enable once PGO builds are supported. - if: false - run: | - ninja -C /mnt/build stage2-instrumented - - # We need to create an archive of the build directory, because it has too - # many files to upload. - - name: Save Build and Source Directories + if: needs.prepare.outputs.enable-pgo == 'true' + shell: bash run: | - tar -c . | zstd -T0 -c > llvm-project.tar.zst - tar -C /mnt/ -c build/ | zstd -T0 -c > build.tar.zst + ninja -C ${{ steps.setup-stage.outputs.build-prefix}}/build stage2-instrumented - - name: Upload Stage 2 Source - uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 #v4.3.0 + - name: Save Stage + uses: ./workflows/.github/workflows/release-binaries-save-stage with: - name: stage2-source - path: ${{ github.workspace }}/llvm-project.tar.zst - retention-days: 2 + build-prefix: ${{ steps.setup-stage.outputs.build-prefix }} - - name: Upload Stage 2 Build Dir - uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 #v4.3.0 + build-stage3-clang: + name: "Build Stage 3 LLVM/Clang" + needs: + - prepare + - build-stage2 + if: github.repository == 'llvm/llvm-project' + runs-on: ${{ inputs.runs-on }} + steps: + - name: Checkout Actions + uses: actions/checkout@v4 + with: + ref: ${{ (github.event_name == 'pull_request' && github.sha) || 'main' }} + sparse-checkout: | + .github/workflows/ + sparse-checkout-cone-mode: false + path: workflows + - name: Setup Stage + id: setup-stage + uses: ./workflows/.github/workflows/release-binaries-setup-stage with: - name: stage2-build - path: ${{ github.workspace }}/build.tar.zst - retention-days: 2 + previous-artifact: build-stage2 + - name: Build LLVM/Clang + shell: bash + run: | + # There is a race condition on the MacOS builders and this command is here + # to help debug that when it happens. + ls -ltr ${{ steps.setup-stage.outputs.build-prefix }}/build + ninja -C ${{ steps.setup-stage.outputs.build-prefix }}/build stage2-clang + # Build some of the larger binaries here too. + ninja -C ${{ steps.setup-stage.outputs.build-prefix }}/build/tools/clang/stage2-bins/ \ + clang-scan-deps \ + modularize clangd \ + clangd-indexer \ + clang-check \ + ${{ (runner.os == 'Linux' && 'clangd-fuzzer') || '' }} \ + clang-tidy \ + llc \ + lli \ + llvm-exegesis \ + llvm-opt-fuzzer \ + llvm-reduce \ + llvm-lto \ + dsymutil + + - name: Save Stage + uses: ./workflows/.github/workflows/release-binaries-save-stage + with: + build-prefix: ${{ steps.setup-stage.outputs.build-prefix }} - build-stage3-linux: - name: "Build Stage 3 Linux" + build-stage3-flang: + name: "Build Stage 3 Flang/MLIR/Bolt" needs: - prepare - - build-stage2-linux - outputs: - filename: ${{ steps.package-info.outputs.release-filename }} - runs-on: ubuntu-22.04-16x64 - if: github.repository == 'llvm/llvm-project' + - build-stage3-clang + runs-on: ${{ inputs.runs-on }} steps: - - name: Install Ninja - uses: llvm/actions/install-ninja@22e9f909d35b50bd1181709564bfe816eaeaae81 # main - - - name: 'Download artifact' - uses: actions/download-artifact@6b208ae046db98c579e8a3aa621ab581ff575935 # v4.1.1 + - name: Checkout Actions + uses: actions/checkout@v4 with: - pattern: stage2-* - merge-multiple: true + ref: ${{ (github.event_name == 'pull_request' && github.sha) || 'main' }} + sparse-checkout: | + .github/workflows/ + sparse-checkout-cone-mode: false + path: workflows + - name: Setup Stage + id: setup-stage + uses: ./workflows/.github/workflows/release-binaries-setup-stage + with: + previous-artifact: build-stage3-clang - - name: Unpack Artifact + - name: Build Flang / MLIR / Bolt + shell: bash run: | - tar --zstd -xf llvm-project.tar.zst - rm llvm-project.tar.zst - sudo chown $USER:$USER /mnt/ - tar --zstd -C /mnt -xf build.tar.zst - rm build.tar.zst + # Build some of the mlir tools that take a long time to link + if [ "${{ needs.prepare.outputs.build-flang }}" = "true" ]; then + ninja -C ${{ steps.setup-stage.outputs.build-prefix }}/build/tools/clang/stage2-bins/ -j2 flang-new bbc + fi + ninja -C ${{ steps.setup-stage.outputs.build-prefix }}/build/tools/clang/stage2-bins/ \ + mlir-bytecode-parser-fuzzer \ + mlir-cpu-runner \ + mlir-lsp-server \ + mlir-opt \ + mlir-query \ + mlir-reduce \ + mlir-text-parser-fuzzer \ + mlir-translate \ + mlir-transform-opt \ + mlir-cat \ + mlir-minimal-opt \ + mlir-minimal-opt-canonicalize \ + mlir-pdll-lsp-server \ + llvm-bolt \ + llvm-bolt-heatmap + + - name: Save Stage + uses: ./workflows/.github/workflows/release-binaries-save-stage + with: + build-prefix: ${{ steps.setup-stage.outputs.build-prefix }} - - name: Build Release Package - run: | - ninja -C /mnt/build stage2-package + build-stage3-all: + name: "Build Stage 3" + needs: + - prepare + - build-stage3-flang + runs-on: ${{ inputs.runs-on }} + steps: + - name: Checkout Actions + uses: actions/checkout@v4 + with: + ref: ${{ (github.event_name == 'pull_request' && github.sha) || 'main' }} + sparse-checkout: | + .github/workflows/ + sparse-checkout-cone-mode: false + path: workflows + - name: Setup Stage + id: setup-stage + uses: ./workflows/.github/workflows/release-binaries-setup-stage + with: + previous-artifact: build-stage3-flang - - id: package-info + - name: Build Release Package + shell: bash run: | - filename="LLVM-${{ needs.prepare.outputs.release-version }}-Linux.tar.xz" - echo "filename=$filename" >> $GITHUB_OUTPUT - echo "path=/mnt/build/tools/clang/stage2-bins/$filename" >> $GITHUB_OUTPUT + which cmake + ninja -C ${{ steps.setup-stage.outputs.build-prefix }}/build stage2-package + # Copy Release artifact to the workspace so it is easier to upload. + # This is necessary, because on Windows, the build-prefix path can + # only be used on bash steps, because it uses the form of /d/files/ + # and other steps expect D:\files. + mv ${{ steps.setup-stage.outputs.build-prefix }}/build/tools/clang/stage2-bins/${{ needs.prepare.outputs.release-binary-filename }} . - uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 #v4.3.0 - if: always() with: - name: release-binary - path: ${{ steps.package-info.outputs.path }} + name: ${{ runner.os }}-${{ runner.arch }}-release-binary + # Due to path differences on Windows when running in bash vs running on node, + # we need to search for files in the current workspace. + path: | + ${{ needs.prepare.outputs.release-binary-filename }} # Clean up some build files to reduce size of artifact. - name: Clean Up Build Directory + shell: bash run: | - find /mnt/build -iname ${{ steps.package-info.outputs.filename }} -delete + find ${{ steps.setup-stage.outputs.build-prefix }}/build -iname ${{ needs.prepare.outputs.release-binary-filename }} -delete + rm -Rf ${{ steps.setup-stage.outputs.build-prefix }}/build/tools/clang/stage2-bins/_CPack_Packages - # We need to create an archive of the build directory, because it has too - # many files to upload. - - name: Save Build and Source Directories - run: | - tar -c . | zstd -T0 -c > llvm-project.tar.zst - tar -C /mnt/ -c build/ | zstd -T0 -c > build.tar.zst - - - name: Upload Stage 3 Source - uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 #v4.3.0 + - name: Save Stage + uses: ./workflows/.github/workflows/release-binaries-save-stage with: - name: stage3-source - path: llvm-project.tar.zst - retention-days: 2 + build-prefix: ${{ steps.setup-stage.outputs.build-prefix }} - - name: Upload Stage 3 Build Dir - uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 #v4.3.0 - with: - name: stage3-build - path: build.tar.zst - retention-days: 2 - - upload-release-binaries-linux: - name: "Upload Linux Release Binaries" + upload-release-binaries: + name: "Upload Release Binaries" needs: - prepare - - build-stage3-linux - if : ${{ needs.prepare.outputs.upload == 'true' }} + - build-stage3-all + if: >- + always() && + github.event_name != 'pull_request' && + needs.prepare.outputs.upload == 'true' runs-on: ubuntu-22.04 permissions: contents: write # For release uploads + id-token: write # For artifact attestations + attestations: write # For artifact attestations steps: + - name: Checkout Release Scripts + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + sparse-checkout: | + llvm/utils/release/github-upload-release.py + llvm/utils/git/requirements.txt + sparse-checkout-cone-mode: false + - name: 'Download artifact' uses: actions/download-artifact@6b208ae046db98c579e8a3aa621ab581ff575935 # v4.1.1 with: - name: release-binary + pattern: '*-release-binary' + merge-multiple: true + + - name: Attest Build Provenance + id: provenance + uses: actions/attest-build-provenance@897ed5eab6ed058a474202017ada7f40bfa52940 # v1.0.0 + with: + subject-path: ${{ needs.prepare.outputs.release-binary-filename }} + + - name: Rename attestation file + run: + mv ${{ steps.provenance.outputs.bundle-path }} ${{ needs.prepare.outputs.release-binary-filename }}.jsonl + + - name: Upload Build Provenance + uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 #v4.3.3 + with: + name: ${{ needs.prepare.outputs.release-binary-filename }}-attestation + path: ${{ needs.prepare.outputs.release-binary-filename }}.jsonl + + - name: Install Python Requirements + run: | + pip install --require-hashes -r ./llvm/utils/git/requirements.txt - name: Upload Release + shell: bash run: | - sudo apt install python3-github - ./llvm-project/llvm/utils/release/github-upload-release.py \ + ./llvm/utils/release/github-upload-release.py \ --token ${{ github.token }} \ --release ${{ needs.prepare.outputs.release-version }} \ upload \ - --files ${{ needs.build-stage3-linux.outputs.release-filename }} + --files ${{ needs.prepare.outputs.release-binary-filename }}* - - test-stage3-linux: - name: "Test Stage 3 Linux" + test-stage3: + name: "Test Stage 3" needs: - prepare - - build-stage3-linux - runs-on: ubuntu-22.04 - if: github.repository == 'llvm/llvm-project' + - build-stage3-all + if: >- + github.repository == 'llvm/llvm-project' + runs-on: ${{ inputs.runs-on }} steps: - - name: Install Ninja - uses: llvm/actions/install-ninja@22e9f909d35b50bd1181709564bfe816eaeaae81 # main - - - name: 'Download artifact' - uses: actions/download-artifact@6b208ae046db98c579e8a3aa621ab581ff575935 # v4.1.1 + - name: Checkout Actions + uses: actions/checkout@v4 with: - pattern: stage3-* - merge-multiple: true - - - name: Unpack Artifact - run: | - tar --zstd -xf llvm-project.tar.zst - rm llvm-project.tar.zst - sudo chown $USER:$USER /mnt/ - tar --zstd -C /mnt -xf build.tar.zst - rm build.tar.zst + ref: ${{ (github.event_name == 'pull_request' && github.sha) || 'main' }} + sparse-checkout: | + .github/workflows/ + sparse-checkout-cone-mode: false + path: workflows + - name: Setup Stage + id: setup-stage + uses: ./workflows/.github/workflows/release-binaries-setup-stage + with: + previous-artifact: build-stage3-all - name: Run Tests + shell: bash run: | - ninja -C /mnt/build stage2-check-all + ninja -C ${{ steps.setup-stage.outputs.build-prefix }}/build stage2-check-all diff --git a/.github/workflows/release-documentation.yml b/.github/workflows/release-documentation.yml index 70e5f08b6f72e..922c5093f1357 100644 --- a/.github/workflows/release-documentation.yml +++ b/.github/workflows/release-documentation.yml @@ -72,17 +72,20 @@ jobs: ref: main fetch-depth: 0 path: www-releases + persist-credentials: false - name: Upload Release Notes if: env.upload env: - WWW_RELEASES_TOKEN: ${{ secrets.WWW_RELEASES_TOKEN }} + GH_TOKEN: ${{ secrets.WWW_RELEASES_TOKEN }} run: | - mkdir -p ../www-releases/${{ inputs.release-version }} - mv ./docs-build/html-export/* ../www-releases/${{ inputs.release-version }} - cd ../www-releases + mkdir -p www-releases/${{ inputs.release-version }} + mv ./docs-build/html-export/* www-releases/${{ inputs.release-version }} + cd www-releases + git checkout -b ${{ inputs.release-version }} git add ${{ inputs.release-version }} git config user.email "llvmbot@llvm.org" git config user.name "llvmbot" git commit -a -m "Add ${{ inputs.release-version }} documentation" - git push "https://$WWW_RELEASES_TOKEN@github.com/${{ github.repository_owner }}/www-releases" main:main + git push --force "https://$GH_TOKEN@github.com/llvmbot/www-releases.git" HEAD:refs/heads/${{ inputs.release-version }} + gh pr create -f -B main -H ${{ inputs.release-version }} -R llvmbot/www-releases diff --git a/.github/workflows/release-doxygen.yml b/.github/workflows/release-doxygen.yml index ef00a438ce7ac..ea95e5bb12b2b 100644 --- a/.github/workflows/release-doxygen.yml +++ b/.github/workflows/release-doxygen.yml @@ -25,6 +25,10 @@ on: description: 'Upload documentation' required: false type: boolean + secrets: + RELEASE_TASKS_USER_TOKEN: + description: "Secret used to check user permissions." + required: false jobs: release-doxygen: @@ -63,5 +67,6 @@ jobs: if: env.upload env: GITHUB_TOKEN: ${{ github.token }} + USER_TOKEN: ${{ secrets.RELEASE_TASKS_USER_TOKEN }} run: | - ./llvm/utils/release/github-upload-release.py --token "$GITHUB_TOKEN" --release "${{ inputs.release-version }}" --user "${{ github.actor }}" upload --files ./*doxygen*.tar.xz + ./llvm/utils/release/github-upload-release.py --token "$GITHUB_TOKEN" --release "${{ inputs.release-version }}" --user "${{ github.actor }}" --user-token "$USER_TOKEN" upload --files ./*doxygen*.tar.xz diff --git a/.github/workflows/release-lit.yml b/.github/workflows/release-lit.yml index 0316ba406041d..9d6f3140e6883 100644 --- a/.github/workflows/release-lit.yml +++ b/.github/workflows/release-lit.yml @@ -17,6 +17,10 @@ on: description: 'Release Version' required: true type: string + secrets: + RELEASE_TASKS_USER_TOKEN: + description: "Secret used to check user permissions." + required: false jobs: release-lit: @@ -36,8 +40,9 @@ jobs: - name: Check Permissions env: GITHUB_TOKEN: ${{ github.token }} + USER_TOKEN: ${{ secrets.RELEASE_TASKS_USER_TOKEN }} run: | - ./llvm/utils/release/./github-upload-release.py --token "$GITHUB_TOKEN" --user ${{ github.actor }} check-permissions + ./llvm/utils/release/./github-upload-release.py --token "$GITHUB_TOKEN" --user ${{ github.actor }} --user-token "$USER_TOKEN" check-permissions - name: Setup Cpp uses: aminya/setup-cpp@v1 diff --git a/.github/workflows/release-sources.yml b/.github/workflows/release-sources.yml index 9c5b1a9f01709..a6c86823f99df 100644 --- a/.github/workflows/release-sources.yml +++ b/.github/workflows/release-sources.yml @@ -16,6 +16,10 @@ on: description: Release Version required: true type: string + secrets: + RELEASE_TASKS_USER_TOKEN: + description: "Secret used to check user permissions." + required: false # Run on pull_requests for testing purposes. pull_request: paths: @@ -47,7 +51,7 @@ jobs: steps: - id: inputs run: | - ref=${{ inputs.release-version || github.sha }} + ref=${{ (inputs.release-version && format('llvmorg-{0}', inputs.release-version)) || github.sha }} if [ -n "${{ inputs.release-version }}" ]; then export_args="-release ${{ inputs.release-version }} -final" else diff --git a/.github/workflows/release-tasks.yml b/.github/workflows/release-tasks.yml index 2ed56dace1d4c..780dd0ff6325c 100644 --- a/.github/workflows/release-tasks.yml +++ b/.github/workflows/release-tasks.yml @@ -66,6 +66,9 @@ jobs: with: release-version: ${{ needs.validate-tag.outputs.release-version }} upload: true + # Called workflows don't have access to secrets by default, so we need to explicitly pass secrets that we use. + secrets: + RELEASE_TASKS_USER_TOKEN: ${{ secrets.RELEASE_TASKS_USER_TOKEN }} release-lit: name: Release Lit @@ -73,22 +76,41 @@ jobs: uses: ./.github/workflows/release-lit.yml with: release-version: ${{ needs.validate-tag.outputs.release-version }} + # Called workflows don't have access to secrets by default, so we need to explicitly pass secrets that we use. + secrets: + RELEASE_TASKS_USER_TOKEN: ${{ secrets.RELEASE_TASKS_USER_TOKEN }} release-binaries: name: Build Release Binaries permissions: contents: write + id-token: write + attestations: write needs: - validate-tag - release-create + strategy: + fail-fast: false + matrix: + runs-on: + - ubuntu-22.04 + - windows-2022 + - macos-13 + - macos-14 + uses: ./.github/workflows/release-binaries.yml with: release-version: ${{ needs.validate-tag.outputs.release-version }} upload: true + runs-on: ${{ matrix.runs-on }} + # Called workflows don't have access to secrets by default, so we need to explicitly pass secrets that we use. + secrets: + RELEASE_TASKS_USER_TOKEN: ${{ secrets.RELEASE_TASKS_USER_TOKEN }} release-sources: name: Package Release Sources permissions: + contents: read id-token: write attestations: write needs: @@ -96,3 +118,6 @@ jobs: uses: ./.github/workflows/release-sources.yml with: release-version: ${{ needs.validate-tag.outputs.release-version }} + # Called workflows don't have access to secrets by default, so we need to explicitly pass secrets that we use. + secrets: + RELEASE_TASKS_USER_TOKEN: ${{ secrets.RELEASE_TASKS_USER_TOKEN }} diff --git a/.github/workflows/version-check.yml b/.github/workflows/version-check.yml index 4ce6119a407f5..894e07d323ca9 100644 --- a/.github/workflows/version-check.yml +++ b/.github/workflows/version-check.yml @@ -27,5 +27,5 @@ jobs: - name: Version Check run: | - version=$(grep -o 'LLVM_VERSION_\(MAJOR\|MINOR\|PATCH\) [0-9]\+' llvm/CMakeLists.txt | cut -d ' ' -f 2 | tr "\n" "." | sed 's/.$//g') + version=$(grep -o 'LLVM_VERSION_\(MAJOR\|MINOR\|PATCH\) [0-9]\+' cmake/Modules/LLVMVersion.cmake | cut -d ' ' -f 2 | tr "\n" "." | sed 's/.$//g') .github/workflows/version-check.py "$version" diff --git a/.gitignore b/.gitignore index 20c4f52cd3786..ced8f777c7775 100644 --- a/.gitignore +++ b/.gitignore @@ -23,6 +23,9 @@ #OS X specific files. .DS_store +# Temporary file created by the automerger tool. +.am.txt + # Ignore the user specified CMake presets in subproject directories. /*/CMakeUserPresets.json diff --git a/README.md b/README.md index a9b29ecbc1a3a..9de8064f8812a 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,11 @@ +# Apple's fork of llvm-project + +This is Apple's fork of llvm-project. For more information on Apple's +branching scheme, please see +[apple-docs/AppleBranchingScheme.md](https://github.com/apple/llvm-project/tree/apple/main/apple-docs/AppleBranchingScheme.md). + +The LLVM project's main README follows. + # The LLVM Compiler Infrastructure [](https://securityscorecards.dev/viewer/?uri=github.com/llvm/llvm-project) diff --git a/apple-ci/amtool b/apple-ci/amtool new file mode 100755 index 0000000000000..19d300385c01d --- /dev/null +++ b/apple-ci/amtool @@ -0,0 +1,345 @@ +#!/usr/bin/env python3 +""" +Tool to reproduce and resolve the issues reported by the automerger. +""" + +import argparse +import json +import logging +import shlex +import subprocess +import sys +from typing import List, Optional + +log = logging.getLogger() + +REMOTE = 'git@github.com:swiftlang/llvm-project.git' + +class GitError(Exception): + """ + An exception thrown if the git command failed. + + Attributes + ---------- + args : List[str] + The list of arguments passed to `git`. + returncode : int + The exit code of the `git` process. + stdout : str + The output of `git`. + stderr : str + The error output of `git`. + """ + + def __init__(self, args, returncode: int, stdout: str, stderr: str): + self.args = args + self.returncode = returncode + self.stdout = stdout + self.stderr = stderr + + def __repr__(self): + return f'GitError({self.args}, {self.returncode}, "{self.stdout}", "{self.stderr}")' + + +def _git_to_str(args: List[str]): + return 'git ' + ' '.join(map(lambda arg: shlex.quote(arg), args)) + + +def invoke(*cmd, git_dir: Optional[str] = None, + stdin: Optional[str] = None, + stdout=None, + stderr=subprocess.PIPE, + strip: bool = True, ignore_error: bool = False, + timeout: Optional[int] = None): + """ Invokes a git subprocess with the passed string arguments and return + the stdout of the git command as a string if text otherwise a file + handle. + """ + if git_dir is not None: + all_args = ['-C', git_dir] + list(cmd) + else: + all_args = list(cmd) + log.debug('$ %s', _git_to_str(all_args)) + p = subprocess.Popen(['git'] + all_args, + stdout=stdout, + stderr=stderr, + stdin=subprocess.PIPE if stdin else None, + universal_newlines=True) + out, err = p.communicate(input=stdin, timeout=timeout) + if p.returncode == 0: + if out: + if strip: + out = out.rstrip() + for line in out.splitlines(): + log.debug('STDOUT: %s', line) + if err: + for line in err.rstrip().splitlines(): + log.debug('STDERR: %s', line) + return out + log.debug('EXIT STATUS: %d', p.returncode) + if err: + for line in err.rstrip().splitlines(): + log.debug('STDERR: %s', line) + if ignore_error: + return None + raise GitError(all_args, p.returncode, out, err) + + +def git(*cmd, **kwargs): + """ Invokes a git subprocess with the passed string arguments and return + the stdout of the git command. + """ + return invoke(*cmd, **kwargs, stdout=subprocess.PIPE) + + +class Commit: + """ Represents the commit being merged.""" + def __init__(self, sha: str): + self.sha = sha + + def short_sha(self): + return self.sha[0:12] + + def get_previous_commit(self): + return git('rev-parse', self.sha + '^') + + +class MergeId: + """ Encapsulates the merge ID constructed by the automerger and the + corresponding git operations. + """ + prefix = 'refs/am' + + def __init__(self, merge_id: str): + self.merge_id = merge_id + parts = merge_id.split('_') + try: + self.commit = Commit(parts[0]) + self.target_branch = '/'.join(parts[1:]) + except IndexError: + log.error("Merge Id not correctly formed.") + + @property + def ref_name(self): + return self.prefix + "/changes/" + self.merge_id + + @property + def merge_candidate_ref_name(self): + return self.prefix + "/merge-candidate/" + self.merge_id + + def get_previous_merge_id(self): + previous_commit = self.commit.get_previous_commit() + return MergeId(self.merge_id.replace(self.commit.sha, previous_commit)) + + @staticmethod + def fetch(*args): + """Helper function for the "git fetch" command.""" + try: + git('fetch', *args) + return True + except GitError as e: + if e.returncode == 128: + return False + raise e + + def fetch_ref_name(self): + refspec = self.ref_name + ":" + self.ref_name + return self.fetch(REMOTE, self.target_branch, refspec) + + def fetch_merge_candidate_ref_name(self): + refspec = "+" + self.merge_candidate_ref_name + ":" + self.merge_candidate_ref_name + return self.fetch(REMOTE, refspec) + + @staticmethod + def checkout(*args): + """Helper function for the "git checkout" command.""" + try: + git('checkout', *args) + return (True, '') + except GitError as e: + return (False, e.stderr) + + def checkout_merge_candidate(self): + """Checkout the merge candidate for this merge ID.""" + return self.checkout(self.merge_candidate_ref_name) + + def checkout_target_branch(self): + """Checkout the target branch for this merge ID.""" + if self.fetch(REMOTE, self.target_branch): + return self.checkout('FETCH_HEAD') + return (False, '') + + def get_source_branch_name(self): + """Get the source branch name (upstream) for this target branch.""" + content = None + if self.fetch(REMOTE, 'repo/apple-llvm-config/am'): + content = git('cat-file', '-p', + 'FETCH_HEAD:apple-llvm-config/am/am-config.json') + if not content: + return None + config = json.loads(content) + if not config: + return None + for json_dict in config: + if json_dict['target'] == self.target_branch: + return json_dict['upstream'] + return None + + def merge(self): + source_branch = self.get_source_branch_name() + if not source_branch: + log.error(f"Could not figure out the source branch for {self.target_branch}.") + try: + git('merge', '--no-edit', "-X", "diff-algorithm=histogram", + "--summary", self.ref_name, '-m', + f"Merge commit '{self.commit.short_sha()}' from {source_branch} into {self.target_branch}") + return True + except GitError as e: + if 'CONFLICT' in e.stdout: + return False + raise e + + def push(self): + try: + git('push', REMOTE, f'HEAD:{self.ref_name}') + return (True, '') + except GitError as e: + return (False, e.stdout) + + +def parse_args(): + """Parse the command line arguments.""" + + parser = argparse.ArgumentParser(description="Automerger Tool") + parser.add_argument('-v', '--verbose', action='store_true', required=False, + help='enable verbose outout and show commands being run') + + subparsers = parser.add_subparsers(dest='command', required=True, + help='the command to run') + # Reproduce + parser_reproduce = subparsers.add_parser('reproduce', + help='Reproduce the issue observed when performing merge') + parser_reproduce.add_argument('id', help='the merge ID to reproduce') + # Push + parser_push = subparsers.add_parser('push', + help='push the resolution, so that the automerger can pick it up') + + args = parser.parse_args() + return args + + +def main(): + args = parse_args() + + # Default to INFO level. Increase to DEBUG level if verbose flag passed. + log_level = logging.INFO + if args.verbose: + log_level = logging.DEBUG + + log.setLevel(log_level) + # create console handler with a higher log level + ch = logging.StreamHandler() + ch.setLevel(log_level) + # create formatter and add it to the handlers + ch_fomatter = logging.Formatter('%(levelname)s: %(message)s') + ch.setFormatter(ch_fomatter) + # add the handlers to the logger + log.addHandler(ch) + + # File to record the merge ID locally so we can use it in the `push` + # command without having the user enter it again. + record = '.am.txt' + + # Reproduce mode. + if args.command == "reproduce": + log.info('Attempting to reproduce the issue.') + merge_id = MergeId(args.id) + + # Record the ref locally so we can use it in the `push` command + # without having the user enter it again. + with open(record, 'w') as f: + f.write(args.id) + + # Fetch the ref. If we failed to fetch then just return because it is + # likely that the commit has already been merged and the ref deleted. + log.info('Fetching the ref and the target branch ...') + status = merge_id.fetch_ref_name() + if not status: + log.error('Unable to fetch the ref. Are you in the right repo? Or, is it already merged?') + return 1 + log.info('Successfully fetched.') + + # Fetch the merge candidate ref for the previous commit and check it + # out in order to apply this commit on top of it. This allows us to + # reproduce just this issue and not any other issues in the prior + # commits which have not been merged yet. + # If we failed to fetch then it is likely that the previous commit has + # already been merged. Checkout the target branch in that case. + previous_merge_id = merge_id.get_previous_merge_id() + log.info('Fetching the previous commit ...') + status = previous_merge_id.fetch_merge_candidate_ref_name() + if not status: + log.info('Previous commit already merged. Checking out the target branch instead.') + status, msg = merge_id.checkout_target_branch() + if not status: + log.error('Failed to checkout.') + log.error(msg) + return 1 + log.info('Successfully checked out the target branch.') + else: + log.info('Successfully fetched.') + log.info('Now checking out the previous commit.') + status, msg = previous_merge_id.checkout_merge_candidate() + if not status: + log.error('Failed to checkout.') + log.error(msg) + return 1 + log.info('Successfully checked out the previous commit.') + + # Perform the merge. + log.info('Performing the merge ...') + rc = merge_id.merge() + if not rc: + log.info('Please resolve the conflicts and push the merge commit.') + return 0 + log.info('No merge conflict seen. Is this a build/test failure?') + log.info('Please resolve the issue and push the commit.') + return 0 + + # Push mode. + elif args.command == "push": + # Read the ref saved locally by the `reproduce` command. + try: + with open(record, 'r') as f: + content = f.read() + except FileNotFoundError: + log.error('Did you run the `reproduce` command before?') + return 1 + log.debug(f'Content : {content}') + + # Check if we happen to be still in the middle of the merge. + # Proceed to push if otherwise the merge has been concluded. + try: + git('rev-parse', '--verify', '--quiet', 'MERGE_HEAD') + log.error('Looks like you are in the middle of the merge.') + log.error('Please conclude the merge before pushing.') + return 1 + except GitError: + pass + + # Save the commit sha so that we can include it in the output message. + merge_commit = git('rev-parse', 'HEAD') + + # Perform the push. + merge_id = MergeId(content) + log.info("Pushing ...") + status, msg = merge_id.push() + if not status: + log.error('Failed to push.') + log.error(msg) + return 1 + log.info(f'Successfully pushed `{merge_commit}`.') + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/apple-ci/clang/am/build.sh b/apple-ci/clang/am/build.sh new file mode 100644 index 0000000000000..b18334fa389a2 --- /dev/null +++ b/apple-ci/clang/am/build.sh @@ -0,0 +1,37 @@ +#!/usr/bin/env bash +set -eu + +SRC_DIR=$PWD/llvm-project +BUILD_DIR=$PWD/build + +for arg; do + case $arg in + --src=*) SRC_DIR="${arg##*=}"; shift ;; + --build=*) BUILD_DIR="${arg##*=}"; shift ;; + *) echo "Incorrect usage." >&2; exit 1 ;; + esac +done + +echo +echo "SRC_DIR . . . . = $SRC_DIR" +echo "BUILD_DIR . . . = $BUILD_DIR" +echo + +NINJA=$(xcrun -f ninja) + +HOST_COMPILER_PATH=$(dirname $(xcrun -f clang)) + +mkdir -p $BUILD_DIR && cd $_ +set -x +xcrun cmake -G Ninja \ + -DCMAKE_MAKE_PROGRAM=$NINJA \ + -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_ASSERTIONS=ON \ + -DCMAKE_C_COMPILER=$HOST_COMPILER_PATH/clang \ + -DCMAKE_CXX_COMPILER=$HOST_COMPILER_PATH/clang++ \ + -DCMAKE_C_COMPILER_LAUNCHER=$HOST_COMPILER_PATH/clang-cache \ + -DCMAKE_CXX_COMPILER_LAUNCHER=$HOST_COMPILER_PATH/clang-cache \ + -DLLVM_TARGETS_TO_BUILD="X86;ARM;AArch64" \ + -DLLVM_ENABLE_PROJECTS="clang;clang-tools-extra;compiler-rt;lldb" \ + -DLLDB_ENABLE_SWIFT_SUPPORT=OFF \ + -DLLDB_INCLUDE_TESTS=OFF \ + $SRC_DIR/llvm && $NINJA diff --git a/apple-ci/pr.sh b/apple-ci/pr.sh new file mode 100755 index 0000000000000..f21587f77896b --- /dev/null +++ b/apple-ci/pr.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +# Build script for ci.swift.org PR testing. +# Tools like cmake/ninja needs to be in $PATH +# and run the script in build directory. + +LLVM_PROJECT_SRC=$1 +LLVM_ENABLE_PROJECTS=${2:-"clang;clang-tools-extra"} + +echo '--- CMake Config ---' +cmake -G Ninja \ + -DCMAKE_BUILD_TYPE=Release \ + -DLLVM_ENABLE_ASSERTIONS=On \ + -DLLVM_ENABLE_PROJECTS=${LLVM_ENABLE_PROJECTS} \ + '-DLLVM_TARGETS_TO_BUILD=X86;ARM;AArch64' \ + '-DLLVM_LIT_ARGS=-v' \ + ${LLVM_PROJECT_SRC}/llvm + +echo '--- Ninja Build ---' +ninja -v +echo '--- Ninja Test ---' +ninja -v -k 0 check-all diff --git a/apple-docs/AppleBranchingScheme.md b/apple-docs/AppleBranchingScheme.md new file mode 100644 index 0000000000000..694d2166bd372 --- /dev/null +++ b/apple-docs/AppleBranchingScheme.md @@ -0,0 +1,102 @@ +# Apple's branching scheme for llvm-project + +This document explains the various important branches in +[apple/llvm-project](https://github.com/apple/llvm-project), +how they relate to one another, and how they correspond to branches in the +[apple/swift](https://github.com/apple/swift) repository. + +These are the most important branches to know about: + +- [next](https://github.com/apple/llvm-project/tree/next) + is aligned with Swift's + [next](https://github.com/apple/swift/tree/next) branch. +- The current `stable` branch (check + [update-checkout-config.json](https://github.com/apple/swift/blob/main/utils/update_checkout/update-checkout-config.json) + for up-to-date info) is aligned with Swift's + [main](https://github.com/apple/swift/tree/main) branch. +- `swift/*` branches are aligned with the corresponding Swift branch without + the `swift/` prefix. + +## Upstream branches + +The `llvm.org/*` branches are forwarded, unchanged, from +[github.com/llvm/llvm-project](https://github.com/llvm/llvm-project). These +are read-only, exact copies of the upstream LLVM project's branches. They are +forwarded here as a convenience for easy reference, to avoid the need for extra +remotes. + +- [llvm.org/main](https://github.com/apple/llvm-project/tree/llvm.org/main) + is the most important branch here, matching the LLVM project's + [main](https://github.com/llvm/llvm-project/tree/main) branch. + +## Downstream branches + +Downstream branches contain changes on top of what is in the LLVM project. +This includes some patches that have not yet been upstreamed to the LLVM +project, including some special support for Swift. + +We are actively working on either upstreaming or reverting many of those +differences. The goal is to fully eliminate all *non-Swift* differences between +`next` and `llvm.org/main`. + +Any LLVM development that does not depend on the Swift repository should happen +upstream. The only changes that are allowed to be submitted without going +through upstream LLVM are those that are either directly related to upstreaming +content or that are needed because of the existing differences (e.g., resolving +merge conflicts or fixing build errors). + +- [next](https://github.com/apple/llvm-project/tree/next) is + downstream of + [llvm.org/main](https://github.com/apple/llvm-project/tree/llvm.org/main). + There is a gated automerger that does testing before merging in. Most + changes to this branch should be redirected to <https://reviews.llvm.org/> + (see also <http://llvm.org/docs/Contributing.html>). Changes made to a + stabilization branch are cherry-picked here. +- `stable/*`: These branches are periodic stabilization branches, where + fixes are made or manually cherry-picked from `llvm.org/main`. +- `swift/release/*`: These are Swift release branches, where fixed are cherry- + picked from a stabilization branch during release convergence. + +## Historical trivia + +### Mappings to branches from before the monorepo transition + +Before the LLVM project's monorepo transition, Apple maintained downstream +forks of various split repositories. Here is a mapping from a few of the new +branches in the llvm-project monorepo to their original split repositories. + +- [apple/main](https://github.com/apple/llvm-project/tree/apple/main) was + generated from the `upstream-with-swift` branches in + [swift-clang](https://github.com/apple/swift-clang/), + [swift-llvm](https://github.com/apple/swift-llvm/), + [swift-compiler-rt](https://github.com/apple/swift-compiler-rt/), + [swift-clang-tools-extra](https://github.com/apple/swift-clang-tools-extra/), + and [swift-libcxx](https://github.com/apple/swift-libcxx/), with the notable + **exclusion** of [swift-lldb](https://github.com/apple/swift-lldb/), +- [swift/next](https://github.com/apple/llvm-project/tree/swift/next) + was generated from the `upstream-with-swift` branch in + [swift-lldb](https://github.com/apple/swift-lldb/), interleaved with merges + from [apple/main](https://github.com/apple/llvm-project/tree/apple/main). +- [apple/stable/20190104](https://github.com/apple/llvm-project/tree/apple/stable/20190104) + was generated from the `swift-5.1-branch` branches in + [swift-clang](https://github.com/apple/swift-clang/), + [swift-llvm](https://github.com/apple/swift-llvm/), + [swift-compiler-rt](https://github.com/apple/swift-compiler-rt/), + [swift-clang-tools-extra](https://github.com/apple/swift-clang-tools-extra/), + and [swift-libcxx](https://github.com/apple/swift-libcxx/), with the notable + **exclusion** of [swift-lldb](https://github.com/apple/swift-lldb/), +- [swift/swift-5.1-branch](https://github.com/apple/llvm-project/tree/swift/swift-5.1-branch) + was generated from the `swift-5.1-branch` branch in + [swift-lldb](https://github.com/apple/swift-lldb/), interleaved with merges + from + [apple/stable/20190104](https://github.com/apple/llvm-project/tree/apple/stable/20190104). +- [swift/main](https://github.com/apple/llvm-project/tree/swift/main) was + generated from the `stable` branch from all six split repos. + +### Branching scheme prior to July 2021 change + +Prior to July 2021, the `apple:llvm-project` repository maintained two sets +of downstream branches: one set that did not include any content that _depended +on_ Swift, and another set that included all downstream changes. See +[this forum thread](https://forums.swift.org/t/simplifying-the-apple-llvm-project-branches/50287) +explaining the change. diff --git a/apple-llvm-config/am/apple-master.json b/apple-llvm-config/am/apple-master.json new file mode 100644 index 0000000000000..427b8b4bbf218 --- /dev/null +++ b/apple-llvm-config/am/apple-master.json @@ -0,0 +1,3 @@ +{ + "upstream": "llvm.org/master" +} diff --git a/apple-llvm-config/am/swift-master-next.json b/apple-llvm-config/am/swift-master-next.json new file mode 100644 index 0000000000000..6e0e6b553f348 --- /dev/null +++ b/apple-llvm-config/am/swift-master-next.json @@ -0,0 +1,3 @@ +{ + "upstream": "apple/master" +} diff --git a/apple-llvm-config/pr.json b/apple-llvm-config/pr.json new file mode 100644 index 0000000000000..9de9ce6265850 --- /dev/null +++ b/apple-llvm-config/pr.json @@ -0,0 +1,9 @@ +{ +"type": "github", +"domain": "github.com", +"user": "apple", +"repo": "llvm-project", +"test": { + "type":"swift-ci" +} +} diff --git a/bolt/lib/Rewrite/RewriteInstance.cpp b/bolt/lib/Rewrite/RewriteInstance.cpp index 33ebae3b6e6de..3d24936271bf8 100644 --- a/bolt/lib/Rewrite/RewriteInstance.cpp +++ b/bolt/lib/Rewrite/RewriteInstance.cpp @@ -2143,6 +2143,14 @@ bool RewriteInstance::analyzeRelocation( if (!Relocation::isSupported(RType)) return false; + auto IsWeakReference = [](const SymbolRef &Symbol) { + Expected<uint32_t> SymFlagsOrErr = Symbol.getFlags(); + if (!SymFlagsOrErr) + return false; + return (*SymFlagsOrErr & SymbolRef::SF_Undefined) && + (*SymFlagsOrErr & SymbolRef::SF_Weak); + }; + const bool IsAArch64 = BC->isAArch64(); const size_t RelSize = Relocation::getSizeForType(RType); @@ -2174,7 +2182,8 @@ bool RewriteInstance::analyzeRelocation( // Section symbols are marked as ST_Debug. IsSectionRelocation = (cantFail(Symbol.getType()) == SymbolRef::ST_Debug); // Check for PLT entry registered with symbol name - if (!SymbolAddress && (IsAArch64 || BC->isRISCV())) { + if (!SymbolAddress && !IsWeakReference(Symbol) && + (IsAArch64 || BC->isRISCV())) { const BinaryData *BD = BC->getPLTBinaryDataByName(SymbolName); SymbolAddress = BD ? BD->getAddress() : 0; } @@ -2603,7 +2612,7 @@ void RewriteInstance::handleRelocation(const SectionRef &RelocatedSection, Expected<StringRef> SectionName = Section->getName(); if (SectionName && !SectionName->empty()) ReferencedSection = BC->getUniqueSectionByName(*SectionName); - } else if (ReferencedSymbol && ContainingBF && + } else if (BC->isRISCV() && ReferencedSymbol && ContainingBF && (cantFail(Symbol.getFlags()) & SymbolRef::SF_Absolute)) { // This might be a relocation for an ABS symbols like __global_pointer$ on // RISC-V @@ -5498,6 +5507,14 @@ uint64_t RewriteInstance::getNewFunctionOrDataAddress(uint64_t OldAddress) { if (const BinaryFunction *BF = BC->getBinaryFunctionContainingAddress(OldAddress)) { if (BF->isEmitted()) { + // If OldAddress is the another entry point of + // the function, then BOLT could get the new address. + if (BF->isMultiEntry()) { + for (const BinaryBasicBlock &BB : *BF) + if (BB.isEntryPoint() && + (BF->getAddress() + BB.getOffset()) == OldAddress) + return BF->getOutputAddress() + BB.getOffset(); + } BC->errs() << "BOLT-ERROR: unable to get new address corresponding to " "input address 0x" << Twine::utohexstr(OldAddress) << " in function " << *BF diff --git a/bolt/test/AArch64/Inputs/build_id.ldscript b/bolt/test/AArch64/Inputs/build_id.ldscript new file mode 100644 index 0000000000000..0af8e960f491b --- /dev/null +++ b/bolt/test/AArch64/Inputs/build_id.ldscript @@ -0,0 +1,9 @@ +SECTIONS +{ + PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x400000)); . = SEGMENT_START("text-segment", 0x400000) + SIZEOF_HEADERS; + .note.gnu.build-id (0x400400): + { + build_id_note = ABSOLUTE(.); + *(.note.gnu.build-id) + } +} diff --git a/bolt/test/AArch64/build_id.c b/bolt/test/AArch64/build_id.c new file mode 100644 index 0000000000000..01e433c7ca8fd --- /dev/null +++ b/bolt/test/AArch64/build_id.c @@ -0,0 +1,25 @@ +// This test checks that referencing build_id through GOT table +// would result in GOT access after disassembly, not directly +// to build_id address. + +// RUN: %clang %cflags -fuse-ld=lld -Wl,-T,%S/Inputs/build_id.ldscript -Wl,-q \ +// RUN: -Wl,--no-relax -Wl,--build-id=sha1 %s -o %t.exe +// RUN: llvm-bolt -print-disasm --print-only=get_build_id %t.exe -o %t.bolt | \ +// RUN: FileCheck %s + +// CHECK: adrp [[REG:x[0-28]+]], __BOLT_got_zero +// CHECK: ldr x{{.*}}, [[[REG]], :lo12:__BOLT_got_zero{{.*}}] + +struct build_id_note { + char pad[16]; + char hash[20]; +}; + +extern const struct build_id_note build_id_note; + +__attribute__((noinline)) char get_build_id() { return build_id_note.hash[0]; } + +int main() { + get_build_id(); + return 0; +} diff --git a/bolt/test/AArch64/update-weak-reference-symbol.s b/bolt/test/AArch64/update-weak-reference-symbol.s new file mode 100644 index 0000000000000..600a06b8b6d8f --- /dev/null +++ b/bolt/test/AArch64/update-weak-reference-symbol.s @@ -0,0 +1,34 @@ +// This test checks whether BOLT can correctly handle relocations against weak symbols. + +// RUN: %clang %cflags -Wl,-z,notext -shared -Wl,-q %s -o %t.so +// RUN: llvm-bolt %t.so -o %t.so.bolt +// RUN: llvm-nm -n %t.so.bolt > %t.out.txt +// RUN: llvm-objdump -dj .rodata %t.so.bolt >> %t.out.txt +// RUN: FileCheck %s --input-file=%t.out.txt + +# CHECK: w func_1 +# CHECK: {{0+}}[[#%x,ADDR:]] W func_2 + +# CHECK: {{.*}} <.rodata>: +# CHECK-NEXT: {{.*}} .word 0x00000000 +# CHECK-NEXT: {{.*}} .word 0x00000000 +# CHECK-NEXT: {{.*}} .word 0x{{[0]+}}[[#ADDR]] +# CHECK-NEXT: {{.*}} .word 0x00000000 + + .text + .weak func_2 + .weak func_1 + .global wow + .type wow, %function +wow: + bl func_1 + bl func_2 + ret + .type func_2, %function +func_2: + ret + .section .rodata +.LC0: + .xword func_1 +.LC1: + .xword func_2 diff --git a/bolt/test/X86/Inputs/build_id.yaml b/bolt/test/X86/Inputs/build_id.yaml new file mode 100644 index 0000000000000..af012904ff950 --- /dev/null +++ b/bolt/test/X86/Inputs/build_id.yaml @@ -0,0 +1,326 @@ +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 + Entry: 0x4010A0 +ProgramHeaders: + - Type: PT_PHDR + Flags: [ PF_R ] + VAddr: 0x400040 + Align: 0x8 + Offset: 0x40 + - Type: PT_INTERP + Flags: [ PF_R ] + FirstSec: .interp + LastSec: .interp + VAddr: 0x400444 + Offset: 0x444 + - Type: PT_LOAD + Flags: [ PF_X, PF_R ] + FirstSec: .init + LastSec: .fini + VAddr: 0x401000 + Align: 0x1000 + Offset: 0x1000 + - Type: PT_LOAD + Flags: [ PF_R ] + FirstSec: .rodata + LastSec: .rodata + VAddr: 0x402000 + Align: 0x1000 + Offset: 0x2000 + - Type: PT_LOAD + Flags: [ PF_W, PF_R ] + FirstSec: .init_array + LastSec: .bss + VAddr: 0x403DD8 + Align: 0x1000 + Offset: 0x2DD8 + - Type: PT_DYNAMIC + Flags: [ PF_W, PF_R ] + FirstSec: .dynamic + LastSec: .dynamic + VAddr: 0x403DE8 + Align: 0x8 + Offset: 0x2DE8 + - Type: PT_NOTE + Flags: [ PF_R ] + FirstSec: .note.gnu.build-id + LastSec: .note.ABI-tag + VAddr: 0x400400 + Align: 0x4 + Offset: 0x400 +Sections: + - Name: .note.gnu.build-id + Type: SHT_NOTE + Flags: [ SHF_ALLOC ] + Address: 0x400400 + AddressAlign: 0x4 + Offset: 0x400 + Notes: + - Name: GNU + Desc: 3C34F7D1612996940C48F98DC272543BC3C9C956 + Type: NT_PRPSINFO + - Name: .note.ABI-tag + Type: SHT_NOTE + Flags: [ SHF_ALLOC ] + Address: 0x400424 + AddressAlign: 0x4 + Notes: + - Name: GNU + Desc: '00000000030000000200000000000000' + Type: NT_VERSION + - Name: .interp + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + Address: 0x400444 + AddressAlign: 0x1 + Content: 2F6C696236342F6C642D6C696E75782D7838362D36342E736F2E3200 + - Name: .gnu.hash + Type: SHT_GNU_HASH + Flags: [ SHF_ALLOC ] + Address: 0x400460 + Link: .dynsym + AddressAlign: 0x8 + Header: + SymNdx: 0x7 + Shift2: 0x6 + BloomFilter: [ 0x810000 ] + HashBuckets: [ 0x7, 0x0 ] + HashValues: [ 0x6DCE65D1 ] + - Name: .dynsym + Type: SHT_DYNSYM + Flags: [ SHF_ALLOC ] + Address: 0x400488 + Link: .dynstr + AddressAlign: 0x8 + - Name: .dynstr + Type: SHT_STRTAB + Flags: [ SHF_ALLOC ] + Address: 0x400548 + AddressAlign: 0x1 + - Name: .gnu.version + Type: SHT_GNU_versym + Flags: [ SHF_ALLOC ] + Address: 0x4005F2 + Link: .dynsym + AddressAlign: 0x2 + Entries: [ 0, 2, 3, 1, 1, 4, 1, 2 ] + - Name: .gnu.version_r + Type: SHT_GNU_verneed + Flags: [ SHF_ALLOC ] + Address: 0x400608 + Link: .dynstr + AddressAlign: 0x8 + Dependencies: + - Version: 1 + File: libc.so.6 + Entries: + - Name: GLIBC_2.3.4 + Hash: 157882740 + Flags: 0 + Other: 4 + - Name: GLIBC_2.34 + Hash: 110530996 + Flags: 0 + Other: 3 + - Name: GLIBC_2.2.5 + Hash: 157882997 + Flags: 0 + Other: 2 + - Name: .init + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Address: 0x401000 + AddressAlign: 0x4 + Offset: 0x1000 + Content: F30F1EFA4883EC08488B05D92F00004885C07402FFD04883C408C3 + - Name: .plt.sec + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Address: 0x401060 + AddressAlign: 0x10 + EntSize: 0x10 + Content: F30F1EFAF2FF25AD2F00000F1F440000F30F1EFAF2FF25A52F00000F1F440000 + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Address: 0x401080 + AddressAlign: 0x10 + Content: F30F1EFA4883EC0831C0E80101000031C04883C408C3662E0F1F840000000000F30F1EFA31ED4989D15E4889E24883E4F050544531C031C9488D3DC1FFFFFFFF15132F0000F4662E0F1F840000000000488D3D612F0000488D055A2F00004839F87415488B05F62E00004885C07409FFE00F1F8000000000C30F1F8000000000488D3D312F0000488D352A2F00004829FE4889F048C1EE3F48C1F8034801C648D1FE7414488B05C52E00004885C07408FFE0660F1F440000C30F1F8000000000F30F1EFA803DED2E000000752B5548833DA22E0000004889E5740C488B3DCE2E0000E8E9FEFFFFE864FFFFFFC605C52E0000015DC30F1F00C30F1F8000000000F30F1EFAE977FFFFFF0F1F8000000000F30F1EFA415455488D2D660E000053488D1D6AF2FFFF4C8D6314660F1F4400000FB6134889EEBF0100000031C04883C301E8AAFEFFFF4C39E375E55BBF0A0000005D415CE987FEFFFF + - Name: .fini + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Address: 0x4011DC + AddressAlign: 0x4 + Content: F30F1EFA4883EC084883C408C3 + - Name: .rodata + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + Address: 0x402000 + AddressAlign: 0x4 + Offset: 0x2000 + Content: '0100020025303268687800' + - Name: .init_array + Type: SHT_INIT_ARRAY + Flags: [ SHF_WRITE, SHF_ALLOC ] + Address: 0x403DD8 + AddressAlign: 0x8 + EntSize: 0x8 + Offset: 0x2DD8 + Content: '8011400000000000' + - Name: .fini_array + Type: SHT_FINI_ARRAY + Flags: [ SHF_WRITE, SHF_ALLOC ] + Address: 0x403DE0 + AddressAlign: 0x8 + EntSize: 0x8 + Content: '4011400000000000' + - Name: .dynamic + Type: SHT_DYNAMIC + Flags: [ SHF_WRITE, SHF_ALLOC ] + Address: 0x403DE8 + Link: .dynstr + AddressAlign: 0x8 + Entries: + - Tag: DT_NEEDED + Value: 0x37 + - Tag: DT_INIT + Value: 0x401000 + - Tag: DT_FINI + Value: 0x4011DC + - Tag: DT_INIT_ARRAY + Value: 0x403DD8 + - Tag: DT_INIT_ARRAYSZ + Value: 0x8 + - Tag: DT_FINI_ARRAY + Value: 0x403DE0 + - Tag: DT_FINI_ARRAYSZ + Value: 0x8 + - Tag: DT_GNU_HASH + Value: 0x400460 + - Tag: DT_STRTAB + Value: 0x400548 + - Tag: DT_SYMTAB + Value: 0x400488 + - Tag: DT_STRSZ + Value: 0xA9 + - Tag: DT_SYMENT + Value: 0x18 + - Tag: DT_DEBUG + Value: 0x0 + - Tag: DT_PLTGOT + Value: 0x404000 + - Tag: DT_PLTRELSZ + Value: 0x30 + - Tag: DT_PLTREL + Value: 0x7 + - Tag: DT_FLAGS + Value: 0x8 + - Tag: DT_FLAGS_1 + Value: 0x8000001 + - Tag: DT_VERNEED + Value: 0x400608 + - Tag: DT_VERNEEDNUM + Value: 0x1 + - Tag: DT_VERSYM + Value: 0x4005F2 + - Tag: DT_RELACOUNT + Value: 0x3 + - Tag: DT_NULL + Value: 0x0 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + Address: 0x404028 + AddressAlign: 0x8 + Content: '00000000000000003040400000000000' + - Name: .tm_clone_table + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + Address: 0x404038 + AddressAlign: 0x8 + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + Address: 0x404038 + AddressAlign: 0x1 + Size: 0x8 + - Name: .rela.text + Type: SHT_RELA + Flags: [ SHF_INFO_LINK ] + Link: .symtab + AddressAlign: 0x8 + Info: .text + Relocations: + - Offset: 0x40108B + Symbol: print_build_id + Type: R_X86_64_PLT32 + Addend: -4 + - Offset: 0x4010BB + Symbol: main + Type: R_X86_64_PC32 + Addend: -4 + - Offset: 0x4011A2 + Symbol: build_id_note + Type: R_X86_64_PC32 + Addend: 12 + - Type: SectionHeaderTable + Sections: + - Name: .note.gnu.build-id + - Name: .note.ABI-tag + - Name: .interp + - Name: .gnu.hash + - Name: .dynsym + - Name: .dynstr + - Name: .gnu.version + - Name: .gnu.version_r + - Name: .init + - Name: .plt.sec + - Name: .text + - Name: .rela.text + - Name: .fini + - Name: .rodata + - Name: .init_array + - Name: .fini_array + - Name: .dynamic + - Name: .data + - Name: .tm_clone_table + - Name: .bss + - Name: .symtab + - Name: .strtab + - Name: .shstrtab +Symbols: + - Name: print_build_id + Type: STT_FUNC + Section: .text + Binding: STB_GLOBAL + Value: 0x401190 + Size: 0x49 + - Name: _end + Section: .bss + Binding: STB_GLOBAL + Value: 0x404040 + - Name: _start + Type: STT_FUNC + Section: .text + Binding: STB_GLOBAL + Value: 0x4010A0 + Size: 0x26 + - Name: __bss_start + Section: .bss + Binding: STB_GLOBAL + Value: 0x404038 + - Name: main + Type: STT_FUNC + Section: .text + Binding: STB_GLOBAL + Value: 0x401080 + Size: 0x16 + - Name: build_id_note + Index: SHN_ABS + Binding: STB_GLOBAL + Value: 0x400400 +... diff --git a/bolt/test/X86/build_id.test b/bolt/test/X86/build_id.test new file mode 100644 index 0000000000000..8d28e12dbf94f --- /dev/null +++ b/bolt/test/X86/build_id.test @@ -0,0 +1,8 @@ +// This test checks that relocation addend used to address build_id fields +// is properly disassembled by BOLT. + +RUN: yaml2obj %p/Inputs/build_id.yaml &> %t.exe +RUN: llvm-bolt -print-disasm --print-only=print_build_id %t.exe -o %t.bolt | \ +RUN: FileCheck %s + +CHECK: leaq build_id_note+16(%rip), %rbx diff --git a/bolt/test/X86/dynamic-relocs-on-entry.s b/bolt/test/X86/dynamic-relocs-on-entry.s new file mode 100644 index 0000000000000..2a29a43c4939a --- /dev/null +++ b/bolt/test/X86/dynamic-relocs-on-entry.s @@ -0,0 +1,32 @@ +// This test examines whether BOLT can correctly process when +// dynamic relocation points to other entry points of the +// function. + +# RUN: %clang %cflags -fPIC -pie %s -o %t.exe -nostdlib -Wl,-q +# RUN: llvm-bolt %t.exe -o %t.bolt > %t.out.txt +# RUN: readelf -r %t.bolt >> %t.out.txt +# RUN: llvm-objdump --disassemble-symbols=chain %t.bolt >> %t.out.txt +# RUN: FileCheck %s --input-file=%t.out.txt + +## Check if the new address in `chain` is correctly updated by BOLT +# CHECK: Relocation section '.rela.dyn' at offset 0x{{.*}} contains 1 entry: +# CHECK: {{.*}} R_X86_64_RELATIVE [[#%x,ADDR:]] +# CHECK: [[#ADDR]]: c3 retq + .text + .type chain, @function +chain: + movq $1, %rax +Label: + ret + .size chain, .-chain + + .type _start, @function + .global _start +_start: + jmpq *.Lfoo(%rip) + ret + .size _start, .-_start + + .data +.Lfoo: + .quad Label \ No newline at end of file diff --git a/bolt/test/perf2bolt/lit.local.cfg b/bolt/test/perf2bolt/lit.local.cfg index 4ee9ad08cc78a..0fecf913aa98b 100644 --- a/bolt/test/perf2bolt/lit.local.cfg +++ b/bolt/test/perf2bolt/lit.local.cfg @@ -1,4 +1,5 @@ import shutil +import subprocess -if shutil.which("perf") is not None: - config.available_features.add("perf") \ No newline at end of file +if shutil.which("perf") is not None and subprocess.run(["perf", "record", "-e", "cycles:u", "-o", "/dev/null", "--", "perf", "--version"], capture_output=True).returncode == 0: + config.available_features.add("perf") diff --git a/bolt/test/runtime/AArch64/r_aarch64_prelxx.s b/bolt/test/runtime/AArch64/r_aarch64_prelxx.s new file mode 100644 index 0000000000000..89bc6ccf667fb --- /dev/null +++ b/bolt/test/runtime/AArch64/r_aarch64_prelxx.s @@ -0,0 +1,37 @@ +// This test checks processing of R_AARCH64_PREL64/32/16 relocations + +// RUN: %clang %cflags -nostartfiles -nostdlib %s -o %t.exe -Wl,-q \ +// RUN: -Wl,-z,max-page-size=4 +// RUN: llvm-readelf -Wa %t.exe | FileCheck %s -check-prefix=CHECKPREL + +// CHECKPREL: R_AARCH64_PREL16 {{.*}} .dummy + 0 +// CHECKPREL-NEXT: R_AARCH64_PREL32 {{.*}} _start + 4 +// CHECKPREL-NEXT: R_AARCH64_PREL64 {{.*}} _start + 8 + +// RUN: llvm-bolt %t.exe -o %t.bolt +// RUN: llvm-readobj -S --section-data %t.bolt | FileCheck %s + +// CHECK: Name: .data +// CHECK: SectionData ( +// CHECK: 0000: FCFF0000 44FF3F00 44FF3F00 00000000 +// CHECK: ) + + .text + .align 4 + .globl _start + .type _start, %function +_start: + adr x0, datatable + mov x0, #0 + ret + +.section .dummy, "da" +dummy: + .word 0 + +.section .data +datatable: + .hword dummy - datatable + .align 2 + .word _start - datatable + .xword _start - datatable diff --git a/clang-tools-extra/clang-tidy/ExpandModularHeadersPPCallbacks.cpp b/clang-tools-extra/clang-tidy/ExpandModularHeadersPPCallbacks.cpp index 5e2cc207560d3..45274ac61f8b3 100644 --- a/clang-tools-extra/clang-tidy/ExpandModularHeadersPPCallbacks.cpp +++ b/clang-tools-extra/clang-tidy/ExpandModularHeadersPPCallbacks.cpp @@ -257,6 +257,13 @@ void ExpandModularHeadersPPCallbacks::PragmaAssumeNonNullEnd( SourceLocation Loc) { parseToLocation(Loc); } +/*TO_UPSTREAM(BoundsSafety) ON*/ +void ExpandModularHeadersPPCallbacks:: +PragmaAbiPointerAttributesSet(SourceLocation Loc, + ArrayRef<const IdentifierInfo *> Spec) { + parseToLocation(Loc); +} +/*TO_UPSTREAM(BoundsSafety) OFF*/ void ExpandModularHeadersPPCallbacks::MacroExpands(const Token &MacroNameTok, const MacroDefinition &, SourceRange Range, diff --git a/clang-tools-extra/clang-tidy/ExpandModularHeadersPPCallbacks.h b/clang-tools-extra/clang-tidy/ExpandModularHeadersPPCallbacks.h index 0742c21bc4372..fbc0c17cdd24a 100644 --- a/clang-tools-extra/clang-tidy/ExpandModularHeadersPPCallbacks.h +++ b/clang-tools-extra/clang-tidy/ExpandModularHeadersPPCallbacks.h @@ -99,6 +99,10 @@ class ExpandModularHeadersPPCallbacks : public PPCallbacks { void PragmaWarningPop(SourceLocation Loc) override; void PragmaAssumeNonNullBegin(SourceLocation Loc) override; void PragmaAssumeNonNullEnd(SourceLocation Loc) override; + /*TO_UPSTREAM(BoundsSafety) ON*/ + void PragmaAbiPointerAttributesSet(SourceLocation Loc, + ArrayRef<const IdentifierInfo *>) override; + /*TO_UPSTREAM(BoundsSafety) OFF*/ void MacroExpands(const Token &MacroNameTok, const MacroDefinition &, SourceRange Range, const MacroArgs *) override; void MacroDefined(const Token &MacroNameTok, diff --git a/clang-tools-extra/clang-tidy/boost/UseRangesCheck.cpp b/clang-tools-extra/clang-tidy/boost/UseRangesCheck.cpp index 4022ea0cdaf5e..e45687fde6d9f 100644 --- a/clang-tools-extra/clang-tidy/boost/UseRangesCheck.cpp +++ b/clang-tools-extra/clang-tidy/boost/UseRangesCheck.cpp @@ -204,7 +204,7 @@ utils::UseRangesCheck::ReplacerMap UseRangesCheck::getReplacerMap() const { ReplacerMap Results; static const Signature SingleSig = {{0}}; static const Signature TwoSig = {{0}, {2}}; - static const auto AddFrom = + const auto AddFrom = [&Results](llvm::IntrusiveRefCntPtr<UseRangesCheck::Replacer> Replacer, std::initializer_list<StringRef> Names, StringRef Prefix) { llvm::SmallString<64> Buffer; @@ -214,17 +214,17 @@ utils::UseRangesCheck::ReplacerMap UseRangesCheck::getReplacerMap() const { } }; - static const auto AddFromStd = - [](llvm::IntrusiveRefCntPtr<UseRangesCheck::Replacer> Replacer, - std::initializer_list<StringRef> Names) { + const auto AddFromStd = + [&](llvm::IntrusiveRefCntPtr<UseRangesCheck::Replacer> Replacer, + std::initializer_list<StringRef> Names) { AddFrom(Replacer, Names, "std"); }; - static const auto AddFromBoost = - [](llvm::IntrusiveRefCntPtr<UseRangesCheck::Replacer> Replacer, - std::initializer_list< - std::pair<StringRef, std::initializer_list<StringRef>>> - NamespaceAndNames) { + const auto AddFromBoost = + [&](llvm::IntrusiveRefCntPtr<UseRangesCheck::Replacer> Replacer, + std::initializer_list< + std::pair<StringRef, std::initializer_list<StringRef>>> + NamespaceAndNames) { for (auto [Namespace, Names] : NamespaceAndNames) AddFrom(Replacer, Names, SmallString<64>{"boost", (Namespace.empty() ? "" : "::"), diff --git a/clang-tools-extra/clang-tidy/readability/NonConstParameterCheck.cpp b/clang-tools-extra/clang-tidy/readability/NonConstParameterCheck.cpp index 95a3a5165e2e8..43b69a24bdb16 100644 --- a/clang-tools-extra/clang-tidy/readability/NonConstParameterCheck.cpp +++ b/clang-tools-extra/clang-tidy/readability/NonConstParameterCheck.cpp @@ -157,9 +157,12 @@ void NonConstParameterCheck::diagnoseNonConstParameters() { if (!Function) continue; unsigned Index = Par->getFunctionScopeIndex(); - for (FunctionDecl *FnDecl : Function->redecls()) + for (FunctionDecl *FnDecl : Function->redecls()) { + if (FnDecl->getNumParams() <= Index) + continue; Fixes.push_back(FixItHint::CreateInsertion( FnDecl->getParamDecl(Index)->getBeginLoc(), "const ")); + } diag(Par->getLocation(), "pointer parameter '%0' can be pointer to const") << Par->getName() << Fixes; diff --git a/clang-tools-extra/clang-tidy/utils/UseRangesCheck.cpp b/clang-tools-extra/clang-tidy/utils/UseRangesCheck.cpp index e2daa5010e2ae..aba4d17ccd035 100644 --- a/clang-tools-extra/clang-tidy/utils/UseRangesCheck.cpp +++ b/clang-tools-extra/clang-tidy/utils/UseRangesCheck.cpp @@ -39,12 +39,6 @@ static constexpr const char ArgName[] = "ArgName"; namespace clang::tidy::utils { -static bool operator==(const UseRangesCheck::Indexes &L, - const UseRangesCheck::Indexes &R) { - return std::tie(L.BeginArg, L.EndArg, L.ReplaceArg) == - std::tie(R.BeginArg, R.EndArg, R.ReplaceArg); -} - static std::string getFullPrefix(ArrayRef<UseRangesCheck::Indexes> Signature) { std::string Output; llvm::raw_string_ostream OS(Output); @@ -54,15 +48,6 @@ static std::string getFullPrefix(ArrayRef<UseRangesCheck::Indexes> Signature) { return Output; } -static llvm::hash_code hash_value(const UseRangesCheck::Indexes &Indexes) { - return llvm::hash_combine(Indexes.BeginArg, Indexes.EndArg, - Indexes.ReplaceArg); -} - -static llvm::hash_code hash_value(const UseRangesCheck::Signature &Sig) { - return llvm::hash_combine_range(Sig.begin(), Sig.end()); -} - namespace { AST_MATCHER(Expr, hasSideEffects) { @@ -123,24 +108,26 @@ makeMatcherPair(StringRef State, const UseRangesCheck::Indexes &Indexes, } void UseRangesCheck::registerMatchers(MatchFinder *Finder) { - Replaces = getReplacerMap(); + auto Replaces = getReplacerMap(); ReverseDescriptor = getReverseDescriptor(); auto BeginEndNames = getFreeBeginEndMethods(); llvm::SmallVector<StringRef, 4> BeginNames{ llvm::make_first_range(BeginEndNames)}; llvm::SmallVector<StringRef, 4> EndNames{ llvm::make_second_range(BeginEndNames)}; - llvm::DenseSet<ArrayRef<Signature>> Seen; + Replacers.clear(); + llvm::DenseSet<Replacer *> SeenRepl; for (auto I = Replaces.begin(), E = Replaces.end(); I != E; ++I) { - const ArrayRef<Signature> &Signatures = - I->getValue()->getReplacementSignatures(); - if (!Seen.insert(Signatures).second) + auto Replacer = I->getValue(); + if (!SeenRepl.insert(Replacer.get()).second) continue; - assert(!Signatures.empty() && - llvm::all_of(Signatures, [](auto Index) { return !Index.empty(); })); + Replacers.push_back(Replacer); + assert(!Replacer->getReplacementSignatures().empty() && + llvm::all_of(Replacer->getReplacementSignatures(), + [](auto Index) { return !Index.empty(); })); std::vector<StringRef> Names(1, I->getKey()); for (auto J = std::next(I); J != E; ++J) - if (J->getValue()->getReplacementSignatures() == Signatures) + if (J->getValue() == Replacer) Names.push_back(J->getKey()); std::vector<ast_matchers::internal::DynTypedMatcher> TotalMatchers; @@ -148,7 +135,7 @@ void UseRangesCheck::registerMatchers(MatchFinder *Finder) { // signatures in order of length(longest to shortest). This way any // signature that is a subset of another signature will be matched after the // other. - SmallVector<Signature> SigVec(Signatures); + SmallVector<Signature> SigVec(Replacer->getReplacementSignatures()); llvm::sort(SigVec, [](auto &L, auto &R) { return R.size() < L.size(); }); for (const auto &Signature : SigVec) { std::vector<ast_matchers::internal::DynTypedMatcher> Matchers; @@ -163,7 +150,8 @@ void UseRangesCheck::registerMatchers(MatchFinder *Finder) { } Finder->addMatcher( callExpr( - callee(functionDecl(hasAnyName(std::move(Names))).bind(FuncDecl)), + callee(functionDecl(hasAnyName(std::move(Names))) + .bind((FuncDecl + Twine(Replacers.size() - 1).str()))), ast_matchers::internal::DynTypedMatcher::constructVariadic( ast_matchers::internal::DynTypedMatcher::VO_AnyOf, ASTNodeKind::getFromNodeKind<CallExpr>(), @@ -205,21 +193,33 @@ static void removeFunctionArgs(DiagnosticBuilder &Diag, const CallExpr &Call, } void UseRangesCheck::check(const MatchFinder::MatchResult &Result) { - const auto *Function = Result.Nodes.getNodeAs<FunctionDecl>(FuncDecl); - std::string Qualified = "::" + Function->getQualifiedNameAsString(); - auto Iter = Replaces.find(Qualified); - assert(Iter != Replaces.end()); + Replacer *Replacer = nullptr; + const FunctionDecl *Function = nullptr; + for (auto [Node, Value] : Result.Nodes.getMap()) { + StringRef NodeStr(Node); + if (!NodeStr.consume_front(FuncDecl)) + continue; + Function = Value.get<FunctionDecl>(); + size_t Index; + if (NodeStr.getAsInteger(10, Index)) { + llvm_unreachable("Unable to extract replacer index"); + } + assert(Index < Replacers.size()); + Replacer = Replacers[Index].get(); + break; + } + assert(Replacer && Function); SmallString<64> Buffer; - for (const Signature &Sig : Iter->getValue()->getReplacementSignatures()) { + for (const Signature &Sig : Replacer->getReplacementSignatures()) { Buffer.assign({BoundCall, getFullPrefix(Sig)}); const auto *Call = Result.Nodes.getNodeAs<CallExpr>(Buffer); if (!Call) continue; auto Diag = createDiag(*Call); - if (auto ReplaceName = Iter->getValue()->getReplaceName(*Function)) + if (auto ReplaceName = Replacer->getReplaceName(*Function)) Diag << FixItHint::CreateReplacement(Call->getCallee()->getSourceRange(), *ReplaceName); - if (auto Include = Iter->getValue()->getHeaderInclusion(*Function)) + if (auto Include = Replacer->getHeaderInclusion(*Function)) Diag << Inserter.createIncludeInsertion( Result.SourceManager->getFileID(Call->getBeginLoc()), *Include); llvm::SmallVector<unsigned, 3> ToRemove; diff --git a/clang-tools-extra/clang-tidy/utils/UseRangesCheck.h b/clang-tools-extra/clang-tidy/utils/UseRangesCheck.h index 927e9694b0ec7..3a454bcf0cf07 100644 --- a/clang-tools-extra/clang-tidy/utils/UseRangesCheck.h +++ b/clang-tools-extra/clang-tidy/utils/UseRangesCheck.h @@ -85,7 +85,7 @@ class UseRangesCheck : public ClangTidyCheck { std::optional<TraversalKind> getCheckTraversalKind() const override; private: - ReplacerMap Replaces; + std::vector<llvm::IntrusiveRefCntPtr<Replacer>> Replacers; std::optional<ReverseIteratorDescriptor> ReverseDescriptor; IncludeInserter Inserter; }; diff --git a/clang-tools-extra/clangd/ClangdLSPServer.cpp b/clang-tools-extra/clangd/ClangdLSPServer.cpp index 06573a5755424..efb024d05870d 100644 --- a/clang-tools-extra/clangd/ClangdLSPServer.cpp +++ b/clang-tools-extra/clangd/ClangdLSPServer.cpp @@ -893,30 +893,53 @@ void ClangdLSPServer::onPrepareRename(const TextDocumentPositionParams &Params, }); } +/// Validate that `Edits` are valid and form a `WorkspaceEdit` that contains +/// the edits as its `changes`. +static llvm::Expected<WorkspaceEdit> +formWorkspaceEdit(const FileEdits &Edits, const ClangdServer &Server) { + if (auto Err = validateEdits(Server, Edits)) + return std::move(Err); + WorkspaceEdit Result; + // FIXME: use documentChanges if SupportDocumentChanges is true. + Result.changes.emplace(); + for (const auto &Rep : Edits) { + (*Result.changes)[URI::createFile(Rep.first()).toString()] = + Rep.second.asTextEdits(); + } + return Result; +} + void ClangdLSPServer::onRename(const RenameParams &Params, Callback<WorkspaceEdit> Reply) { Path File = std::string(Params.textDocument.uri.file()); if (!Server->getDraft(File)) return Reply(llvm::make_error<LSPError>( "onRename called for non-added file", ErrorCode::InvalidParams)); + auto Callback = [Reply = std::move(Reply), + this](llvm::Expected<RenameResult> R) mutable { + if (!R) + return Reply(R.takeError()); + llvm::Expected<WorkspaceEdit> WorkspaceEdit = + formWorkspaceEdit(R->GlobalChanges, *Server); + Reply(std::move(WorkspaceEdit)); + }; Server->rename(File, Params.position, Params.newName, Opts.Rename, - [File, Params, Reply = std::move(Reply), - this](llvm::Expected<RenameResult> R) mutable { - if (!R) - return Reply(R.takeError()); - if (auto Err = validateEdits(*Server, R->GlobalChanges)) - return Reply(std::move(Err)); - WorkspaceEdit Result; - // FIXME: use documentChanges if SupportDocumentChanges is - // true. - Result.changes.emplace(); - for (const auto &Rep : R->GlobalChanges) { - (*Result - .changes)[URI::createFile(Rep.first()).toString()] = - Rep.second.asTextEdits(); - } - Reply(Result); - }); + std::move(Callback)); +} + +void ClangdLSPServer::onIndexedRename(const IndexedRenameParams &Params, + Callback<WorkspaceEdit> Reply) { + auto Callback = [Reply = std::move(Reply), + this](llvm::Expected<FileEdits> Edits) mutable { + if (!Edits) { + return Reply(Edits.takeError()); + } + llvm::Expected<WorkspaceEdit> WorkspaceEdit = + formWorkspaceEdit(*Edits, *Server); + Reply(std::move(WorkspaceEdit)); + }; + Server->indexedRename(Params.positions, Params.textDocument.uri.file(), + Params.oldName, Params.newName, std::move(Callback)); } void ClangdLSPServer::onDocumentDidClose( @@ -1676,6 +1699,7 @@ void ClangdLSPServer::bindMethods(LSPBinder &Bind, Bind.method("textDocument/switchSourceHeader", this, &ClangdLSPServer::onSwitchSourceHeader); Bind.method("textDocument/prepareRename", this, &ClangdLSPServer::onPrepareRename); Bind.method("textDocument/rename", this, &ClangdLSPServer::onRename); + Bind.method("workspace/indexedRename", this, &ClangdLSPServer::onIndexedRename); Bind.method("textDocument/hover", this, &ClangdLSPServer::onHover); Bind.method("textDocument/documentSymbol", this, &ClangdLSPServer::onDocumentSymbol); Bind.method("workspace/executeCommand", this, &ClangdLSPServer::onCommand); diff --git a/clang-tools-extra/clangd/ClangdLSPServer.h b/clang-tools-extra/clangd/ClangdLSPServer.h index 0b8e4720f5323..0fa72fa463fc6 100644 --- a/clang-tools-extra/clangd/ClangdLSPServer.h +++ b/clang-tools-extra/clangd/ClangdLSPServer.h @@ -139,6 +139,7 @@ class ClangdLSPServer : private ClangdServer::Callbacks, void onPrepareRename(const TextDocumentPositionParams &, Callback<PrepareRenameResult>); void onRename(const RenameParams &, Callback<WorkspaceEdit>); + void onIndexedRename(const IndexedRenameParams &, Callback<WorkspaceEdit>); void onHover(const TextDocumentPositionParams &, Callback<std::optional<Hover>>); void onPrepareTypeHierarchy(const TypeHierarchyPrepareParams &, diff --git a/clang-tools-extra/clangd/ClangdServer.cpp b/clang-tools-extra/clangd/ClangdServer.cpp index e910a80ba0bae..2fb620643c232 100644 --- a/clang-tools-extra/clangd/ClangdServer.cpp +++ b/clang-tools-extra/clangd/ClangdServer.cpp @@ -628,6 +628,50 @@ void ClangdServer::rename(PathRef File, Position Pos, llvm::StringRef NewName, WorkScheduler->runWithAST("Rename", File, std::move(Action)); } +void ClangdServer::indexedRename( + const std::map<URIForFile, std::vector<Position>> &Positions, + PathRef PrimaryFile, llvm::StringRef OldName, llvm::StringRef NewName, + Callback<FileEdits> CB) { + ParseInputs Inputs; + Inputs.TFS = &TFS; + Inputs.CompileCommand = CDB.getCompileCommand(PrimaryFile) + .value_or(CDB.getFallbackCommand(PrimaryFile)); + IgnoreDiagnostics IgnoreDiags; + std::unique_ptr<CompilerInvocation> CI = + buildCompilerInvocation(Inputs, IgnoreDiags); + if (!CI) { + return CB(llvm::make_error<llvm::StringError>( + "Unable to get compiler arguments for primary file", + llvm::inconvertibleErrorCode())); + } + const LangOptions &LangOpts = CI->getLangOpts(); + + tooling::SymbolName OldSymbolName(OldName, LangOpts); + tooling::SymbolName NewSymbolName(NewName, LangOpts); + + llvm::StringMap<std::vector<Range>> FilesToRanges; + for (auto Entry : Positions) { + std::vector<Range> &Ranges = FilesToRanges[Entry.first.file()]; + for (Position Pos : Entry.second) { + // Compute the range for the given position: + // - If the old name is a simple identifier, we can add its length to the + // start position's column because identifiers can't contain newlines + // - If we have a multi-piece symbol name, them `editsForLocations` will + // only look at the start of the range to call + // `findObjCSymbolSelectorPieces`. It is thus fine to use an empty + // range that points to the symbol's start. + Position End = Pos; + if (std::optional<std::string> Identifier = + OldSymbolName.getSinglePiece()) { + End.line += Identifier->size(); + } + Ranges.push_back({Pos, End}); + } + } + CB(editsForLocations(FilesToRanges, OldSymbolName, NewSymbolName, + *getHeaderFS().view(std::nullopt), LangOpts)); +} + namespace { // May generate several candidate selections, due to SelectionTree ambiguity. // vector of pointers because GCC doesn't like non-copyable Selection. diff --git a/clang-tools-extra/clangd/ClangdServer.h b/clang-tools-extra/clangd/ClangdServer.h index a653cdb56b751..1257bdab07fa0 100644 --- a/clang-tools-extra/clangd/ClangdServer.h +++ b/clang-tools-extra/clangd/ClangdServer.h @@ -345,6 +345,17 @@ class ClangdServer { void rename(PathRef File, Position Pos, llvm::StringRef NewName, const RenameOptions &Opts, Callback<RenameResult> CB); + /// Rename all occurrences of a symbol named `OldName` to `NewName` at the + /// given `Positions`. + /// + /// `PrimaryFile` is used to determine the language options for the symbol to + /// rename, eg. to decide whether `OldName` and `NewName` are Objective-C + /// selectors or normal identifiers. + void + indexedRename(const std::map<URIForFile, std::vector<Position>> &Positions, + PathRef PrimaryFile, llvm::StringRef OldName, + llvm::StringRef NewName, Callback<FileEdits> CB); + struct TweakRef { std::string ID; /// ID to pass for applyTweak. std::string Title; /// A single-line message to show in the UI. diff --git a/clang-tools-extra/clangd/CodeComplete.cpp b/clang-tools-extra/clangd/CodeComplete.cpp index 89eee392837af..1a081b9c28273 100644 --- a/clang-tools-extra/clangd/CodeComplete.cpp +++ b/clang-tools-extra/clangd/CodeComplete.cpp @@ -97,6 +97,7 @@ toCompletionItemKind(index::SymbolKind Kind, using SK = index::SymbolKind; switch (Kind) { case SK::Unknown: + case SK::CommentTag: return CompletionItemKind::Missing; case SK::Module: case SK::Namespace: diff --git a/clang-tools-extra/clangd/Protocol.cpp b/clang-tools-extra/clangd/Protocol.cpp index c08f80442eaa0..d3b8585d5743c 100644 --- a/clang-tools-extra/clangd/Protocol.cpp +++ b/clang-tools-extra/clangd/Protocol.cpp @@ -298,6 +298,7 @@ SymbolKind adjustKindToCapability(SymbolKind Kind, SymbolKind indexSymbolKindToSymbolKind(index::SymbolKind Kind) { switch (Kind) { case index::SymbolKind::Unknown: + case index::SymbolKind::CommentTag: return SymbolKind::Variable; case index::SymbolKind::Module: return SymbolKind::Module; @@ -1204,6 +1205,15 @@ llvm::json::Value toJSON(const PrepareRenameResult &PRR) { }; } +bool fromJSON(const llvm::json::Value &Params, + IndexedRenameParams &IndexedRename, llvm::json::Path P) { + llvm::json::ObjectMapper O(Params, P); + return O && O.map("textDocument", IndexedRename.textDocument) && + O.map("oldName", IndexedRename.oldName) && + O.map("newName", IndexedRename.newName) && + O.map("positions", IndexedRename.positions); +} + llvm::json::Value toJSON(const DocumentHighlight &DH) { return llvm::json::Object{ {"range", toJSON(DH.range)}, diff --git a/clang-tools-extra/clangd/Protocol.h b/clang-tools-extra/clangd/Protocol.h index a0f8b04bc4ffd..2c901c72b0129 100644 --- a/clang-tools-extra/clangd/Protocol.h +++ b/clang-tools-extra/clangd/Protocol.h @@ -128,6 +128,23 @@ struct URIForFile { llvm::json::Value toJSON(const URIForFile &U); bool fromJSON(const llvm::json::Value &, URIForFile &, llvm::json::Path); +template <typename T> +bool fromJSON(const llvm::json::Value &E, std::map<URIForFile, T> &Out, + llvm::json::Path P) { + if (auto *O = E.getAsObject()) { + Out.clear(); + for (const auto &KV : *O) { + URIForFile URI; + fromJSON(llvm::json::Value(KV.first), URI, P); + if (!fromJSON(KV.second, Out[URI], P.field(KV.first))) + return false; + } + return true; + } + P.report("expected object"); + return false; +} + struct TextDocumentIdentifier { /// The text document's URI. URIForFile uri; @@ -1445,6 +1462,37 @@ struct PrepareRenameResult { }; llvm::json::Value toJSON(const PrepareRenameResult &PRR); +/// Rename all occurrences of a symbol named `oldName` to `newName` at the +/// given `positions`. +/// +/// The use case of this method is for when the positions to rename are already +/// known, eg. from an index lookup outside of clangd's built-in index. In +/// particular, it determines the edits necessary to rename multi-piece +/// Objective-C selector names. +/// +/// `textDocument` is used to determine the language options for the symbol to +/// rename, eg. to decide whether `oldName` and `newName` are Objective-C +/// selectors or normal identifiers. +/// +/// This is a clangd extension. +struct IndexedRenameParams { + /// The document in which the declaration to rename is declared. Its compiler + /// arguments are used to infer language settings for the rename. + TextDocumentIdentifier textDocument; + + /// The old name of the symbol. + std::string oldName; + + /// The new name of the symbol. + std::string newName; + + /// The positions at which the symbol is known to appear and that should be + /// renamed. + std::map<URIForFile, std::vector<Position>> positions; +}; +bool fromJSON(const llvm::json::Value &, IndexedRenameParams &, + llvm::json::Path); + enum class DocumentHighlightKind { Text = 1, Read = 2, Write = 3 }; /// A document highlight is a range inside a text document which deserves diff --git a/clang-tools-extra/clangd/Quality.cpp b/clang-tools-extra/clangd/Quality.cpp index 7371d95fbf275..3ec572404009b 100644 --- a/clang-tools-extra/clangd/Quality.cpp +++ b/clang-tools-extra/clangd/Quality.cpp @@ -146,6 +146,7 @@ categorize(const index::SymbolInfo &D) { case index::SymbolKind::Using: case index::SymbolKind::Module: case index::SymbolKind::Unknown: + case index::SymbolKind::CommentTag: return SymbolQualitySignals::Unknown; } llvm_unreachable("Unknown index::SymbolKind"); diff --git a/clang-tools-extra/clangd/ScanningProjectModules.cpp b/clang-tools-extra/clangd/ScanningProjectModules.cpp index 92f75ef7d5c25..39f57f03f6dfe 100644 --- a/clang-tools-extra/clangd/ScanningProjectModules.cpp +++ b/clang-tools-extra/clangd/ScanningProjectModules.cpp @@ -37,7 +37,8 @@ class ModuleDependencyScanner { const ThreadsafeFS &TFS) : CDB(CDB), TFS(TFS), Service(tooling::dependencies::ScanningMode::CanonicalPreprocessing, - tooling::dependencies::ScanningOutputFormat::P1689) {} + tooling::dependencies::ScanningOutputFormat::P1689, + CASOptions(), nullptr, nullptr, nullptr) {} /// The scanned modules dependency information for a specific source file. struct ModuleDependencyInfo { diff --git a/clang-tools-extra/clangd/TidyFastChecks.inc b/clang-tools-extra/clangd/TidyFastChecks.inc index 9050ce16127ff..de1a025602fa9 100644 --- a/clang-tools-extra/clangd/TidyFastChecks.inc +++ b/clang-tools-extra/clangd/TidyFastChecks.inc @@ -7,370 +7,435 @@ #define SLOW(CHECK, DELTA) #endif -FAST(abseil-cleanup-ctad, -1.0) +FAST(abseil-cleanup-ctad, -2.0) FAST(abseil-duration-addition, 0.0) -FAST(abseil-duration-comparison, 1.0) -FAST(abseil-duration-conversion-cast, 3.0) -FAST(abseil-duration-division, -0.0) -FAST(abseil-duration-factory-float, 1.0) -FAST(abseil-duration-factory-scale, -0.0) -FAST(abseil-duration-subtraction, 1.0) -FAST(abseil-duration-unnecessary-conversion, 4.0) -FAST(abseil-faster-strsplit-delimiter, 2.0) -FAST(abseil-no-internal-dependencies, -1.0) -FAST(abseil-no-namespace, -1.0) -FAST(abseil-redundant-strcat-calls, 2.0) -FAST(abseil-str-cat-append, 1.0) -FAST(abseil-string-find-startswith, 1.0) -FAST(abseil-string-find-str-contains, 1.0) -FAST(abseil-time-comparison, -0.0) -FAST(abseil-time-subtraction, 0.0) +FAST(abseil-duration-comparison, -1.0) +FAST(abseil-duration-conversion-cast, -1.0) +FAST(abseil-duration-division, 0.0) +FAST(abseil-duration-factory-float, 2.0) +FAST(abseil-duration-factory-scale, 1.0) +FAST(abseil-duration-subtraction, -1.0) +FAST(abseil-duration-unnecessary-conversion, -0.0) +FAST(abseil-faster-strsplit-delimiter, 3.0) +FAST(abseil-no-internal-dependencies, 1.0) +FAST(abseil-no-namespace, -0.0) +FAST(abseil-redundant-strcat-calls, 1.0) +FAST(abseil-str-cat-append, -0.0) +FAST(abseil-string-find-startswith, -1.0) +FAST(abseil-string-find-str-contains, 4.0) +FAST(abseil-time-comparison, -1.0) +FAST(abseil-time-subtraction, 1.0) FAST(abseil-upgrade-duration-conversions, 2.0) SLOW(altera-id-dependent-backward-branch, 13.0) -FAST(altera-kernel-name-restriction, -1.0) -FAST(altera-single-work-item-barrier, -1.0) -FAST(altera-struct-pack-align, -1.0) +FAST(altera-kernel-name-restriction, 4.0) +FAST(altera-single-work-item-barrier, 1.0) +FAST(altera-struct-pack-align, -0.0) FAST(altera-unroll-loops, 2.0) -FAST(android-cloexec-accept, -1.0) -FAST(android-cloexec-accept4, 3.0) -FAST(android-cloexec-creat, 0.0) -FAST(android-cloexec-dup, 3.0) -FAST(android-cloexec-epoll-create, -2.0) -FAST(android-cloexec-epoll-create1, -1.0) -FAST(android-cloexec-fopen, -0.0) -FAST(android-cloexec-inotify-init, 1.0) -FAST(android-cloexec-inotify-init1, 2.0) -FAST(android-cloexec-memfd-create, 2.0) -FAST(android-cloexec-open, -1.0) -FAST(android-cloexec-pipe, -1.0) +FAST(android-cloexec-accept, 0.0) +FAST(android-cloexec-accept4, 1.0) +FAST(android-cloexec-creat, 1.0) +FAST(android-cloexec-dup, 0.0) +FAST(android-cloexec-epoll-create, 2.0) +FAST(android-cloexec-epoll-create1, 0.0) +FAST(android-cloexec-fopen, -1.0) +FAST(android-cloexec-inotify-init, 2.0) +FAST(android-cloexec-inotify-init1, -0.0) +FAST(android-cloexec-memfd-create, -1.0) +FAST(android-cloexec-open, 1.0) +FAST(android-cloexec-pipe, -0.0) FAST(android-cloexec-pipe2, 0.0) FAST(android-cloexec-socket, 1.0) -FAST(android-comparison-in-temp-failure-retry, 0.0) -FAST(boost-use-to-string, 1.0) -FAST(bugprone-argument-comment, 2.0) +FAST(android-comparison-in-temp-failure-retry, 1.0) +FAST(boost-use-ranges, 2.0) +FAST(boost-use-to-string, 2.0) +FAST(bugprone-argument-comment, 4.0) FAST(bugprone-assert-side-effect, 1.0) -FAST(bugprone-assignment-in-if-condition, -0.0) -FAST(bugprone-bad-signal-to-kill-thread, -1.0) +FAST(bugprone-assignment-in-if-condition, 2.0) +FAST(bugprone-bad-signal-to-kill-thread, 1.0) FAST(bugprone-bool-pointer-implicit-conversion, 0.0) -FAST(bugprone-branch-clone, -0.0) +FAST(bugprone-branch-clone, 1.0) +FAST(bugprone-casting-through-void, 1.0) +FAST(bugprone-chained-comparison, 1.0) +FAST(bugprone-compare-pointer-to-member-virtual-function, -0.0) FAST(bugprone-copy-constructor-init, 1.0) -FAST(bugprone-dangling-handle, 0.0) -FAST(bugprone-dynamic-static-initializers, 1.0) +FAST(bugprone-crtp-constructor-accessibility, 0.0) +FAST(bugprone-dangling-handle, -0.0) +FAST(bugprone-dynamic-static-initializers, 0.0) FAST(bugprone-easily-swappable-parameters, 2.0) -FAST(bugprone-exception-escape, 1.0) -FAST(bugprone-fold-init-type, 2.0) +FAST(bugprone-empty-catch, 1.0) +FAST(bugprone-exception-escape, 0.0) +FAST(bugprone-fold-init-type, 1.0) FAST(bugprone-forward-declaration-namespace, 0.0) -FAST(bugprone-forwarding-reference-overload, -0.0) -FAST(bugprone-implicit-widening-of-multiplication-result, 3.0) +FAST(bugprone-forwarding-reference-overload, -1.0) +FAST(bugprone-implicit-widening-of-multiplication-result, 2.0) FAST(bugprone-inaccurate-erase, -0.0) +FAST(bugprone-inc-dec-in-conditions, 3.0) +FAST(bugprone-incorrect-enable-if, -1.0) FAST(bugprone-incorrect-roundings, 1.0) -FAST(bugprone-infinite-loop, 4.0) -FAST(bugprone-integer-division, -3.0) -FAST(bugprone-lambda-function-name, 1.0) -FAST(bugprone-macro-parentheses, 8.0) +FAST(bugprone-infinite-loop, 1.0) +FAST(bugprone-integer-division, -0.0) +FAST(bugprone-lambda-function-name, 0.0) +FAST(bugprone-macro-parentheses, 1.0) FAST(bugprone-macro-repeated-side-effects, 1.0) -FAST(bugprone-misplaced-operator-in-strlen-in-alloc, -0.0) -FAST(bugprone-misplaced-pointer-arithmetic-in-alloc, 2.0) -FAST(bugprone-misplaced-widening-cast, -2.0) -FAST(bugprone-move-forwarding-reference, -2.0) -FAST(bugprone-multiple-statement-macro, 1.0) -FAST(bugprone-narrowing-conversions, -1.0) -FAST(bugprone-no-escape, 3.0) -FAST(bugprone-not-null-terminated-result, 4.0) -FAST(bugprone-parent-virtual-call, 2.0) -FAST(bugprone-posix-return, 0.0) -FAST(bugprone-redundant-branch-condition, 0.0) -FAST(bugprone-reserved-identifier, 2.0) +FAST(bugprone-misplaced-operator-in-strlen-in-alloc, 0.0) +FAST(bugprone-misplaced-pointer-arithmetic-in-alloc, -0.0) +FAST(bugprone-misplaced-widening-cast, -1.0) +FAST(bugprone-move-forwarding-reference, -1.0) +FAST(bugprone-multi-level-implicit-pointer-conversion, -1.0) +FAST(bugprone-multiple-new-in-one-expression, 0.0) +FAST(bugprone-multiple-statement-macro, 2.0) +FAST(bugprone-narrowing-conversions, 2.0) +FAST(bugprone-no-escape, 1.0) +FAST(bugprone-non-zero-enum-to-bool-conversion, 0.0) +FAST(bugprone-not-null-terminated-result, 0.0) +FAST(bugprone-optional-value-conversion, 1.0) +FAST(bugprone-parent-virtual-call, 1.0) +FAST(bugprone-pointer-arithmetic-on-polymorphic-object, 0.0) +FAST(bugprone-posix-return, -0.0) +FAST(bugprone-redundant-branch-condition, -0.0) +FAST(bugprone-reserved-identifier, -1.0) +FAST(bugprone-return-const-ref-from-parameter, -2.0) FAST(bugprone-shared-ptr-array-mismatch, 0.0) -FAST(bugprone-signal-handler, -0.0) -FAST(bugprone-signed-char-misuse, 1.0) -FAST(bugprone-sizeof-container, -0.0) -FAST(bugprone-sizeof-expression, 0.0) -FAST(bugprone-spuriously-wake-up-functions, 3.0) -FAST(bugprone-string-constructor, 1.0) -FAST(bugprone-string-integer-assignment, 1.0) -FAST(bugprone-string-literal-with-embedded-nul, 0.0) -FAST(bugprone-stringview-nullptr, 2.0) -FAST(bugprone-suspicious-enum-usage, -0.0) -FAST(bugprone-suspicious-include, 1.0) +FAST(bugprone-signal-handler, -1.0) +FAST(bugprone-signed-char-misuse, -2.0) +FAST(bugprone-sizeof-container, -1.0) +FAST(bugprone-sizeof-expression, 1.0) +FAST(bugprone-spuriously-wake-up-functions, 1.0) +FAST(bugprone-standalone-empty, 7.0) +FAST(bugprone-string-constructor, 3.0) +FAST(bugprone-string-integer-assignment, -0.0) +FAST(bugprone-string-literal-with-embedded-nul, 1.0) +FAST(bugprone-stringview-nullptr, 4.0) +FAST(bugprone-suspicious-enum-usage, 2.0) +FAST(bugprone-suspicious-include, 0.0) FAST(bugprone-suspicious-memory-comparison, 0.0) -FAST(bugprone-suspicious-memset-usage, 2.0) -FAST(bugprone-suspicious-missing-comma, 0.0) -FAST(bugprone-suspicious-realloc-usage, 1.0) -FAST(bugprone-suspicious-semicolon, 0.0) -FAST(bugprone-suspicious-string-compare, 2.0) -FAST(bugprone-swapped-arguments, 0.0) -FAST(bugprone-terminating-continue, 2.0) -FAST(bugprone-throw-keyword-missing, -1.0) +FAST(bugprone-suspicious-memset-usage, 0.0) +FAST(bugprone-suspicious-missing-comma, -2.0) +FAST(bugprone-suspicious-realloc-usage, -0.0) +FAST(bugprone-suspicious-semicolon, 6.0) +FAST(bugprone-suspicious-string-compare, 1.0) +FAST(bugprone-suspicious-stringview-data-usage, 1.0) +FAST(bugprone-swapped-arguments, 1.0) +FAST(bugprone-switch-missing-default-case, 2.0) +FAST(bugprone-terminating-continue, -1.0) +FAST(bugprone-throw-keyword-missing, 0.0) FAST(bugprone-too-small-loop-variable, 0.0) -FAST(bugprone-unchecked-optional-access, 1.0) +FAST(bugprone-unchecked-optional-access, 2.0) FAST(bugprone-undefined-memory-manipulation, 1.0) -FAST(bugprone-undelegated-constructor, 0.0) -FAST(bugprone-unhandled-exception-at-new, 0.0) +FAST(bugprone-undelegated-constructor, 1.0) +FAST(bugprone-unhandled-exception-at-new, -1.0) FAST(bugprone-unhandled-self-assignment, 0.0) -FAST(bugprone-unused-raii, 2.0) -FAST(bugprone-unused-return-value, 0.0) -FAST(bugprone-use-after-move, 5.0) +FAST(bugprone-unique-ptr-array-mismatch, 0.0) +FAST(bugprone-unsafe-functions, 1.0) +FAST(bugprone-unused-local-non-trivial-variable, -1.0) +FAST(bugprone-unused-raii, 1.0) +FAST(bugprone-unused-return-value, 4.0) +FAST(bugprone-use-after-move, 4.0) FAST(bugprone-virtual-near-miss, 0.0) -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-dcl37-c, 3.0) +FAST(cert-con36-c, 1.0) +FAST(cert-con54-cpp, 2.0) +FAST(cert-ctr56-cpp, 0.0) +FAST(cert-dcl03-c, 0.0) +FAST(cert-dcl16-c, 1.0) +FAST(cert-dcl37-c, 1.0) FAST(cert-dcl50-cpp, -1.0) -FAST(cert-dcl51-cpp, 1.0) +FAST(cert-dcl51-cpp, -1.0) FAST(cert-dcl54-cpp, 0.0) -FAST(cert-dcl58-cpp, 1.0) -FAST(cert-dcl59-cpp, 0.0) -FAST(cert-env33-c, -1.0) -FAST(cert-err09-cpp, 1.0) +FAST(cert-dcl58-cpp, -0.0) +FAST(cert-dcl59-cpp, 1.0) +FAST(cert-env33-c, 1.0) +FAST(cert-err09-cpp, -0.0) FAST(cert-err33-c, 4.0) -FAST(cert-err34-c, 0.0) -FAST(cert-err52-cpp, 1.0) -FAST(cert-err58-cpp, 0.0) -FAST(cert-err60-cpp, 0.0) -FAST(cert-err61-cpp, -0.0) -FAST(cert-exp42-c, 0.0) +FAST(cert-err34-c, -1.0) +FAST(cert-err52-cpp, -1.0) +FAST(cert-err58-cpp, -0.0) +FAST(cert-err60-cpp, -0.0) +FAST(cert-err61-cpp, 2.0) +FAST(cert-exp42-c, 1.0) FAST(cert-fio38-c, 1.0) -FAST(cert-flp30-c, 0.0) +FAST(cert-flp30-c, 3.0) FAST(cert-flp37-c, 1.0) +FAST(cert-int09-c, -1.0) FAST(cert-mem57-cpp, 0.0) -FAST(cert-msc30-c, -0.0) -FAST(cert-msc32-c, 1.0) -FAST(cert-msc50-cpp, -1.0) -FAST(cert-msc51-cpp, 1.0) -FAST(cert-msc54-cpp, -1.0) -FAST(cert-oop11-cpp, 1.0) -FAST(cert-oop54-cpp, -0.0) -FAST(cert-oop57-cpp, 1.0) +FAST(cert-msc24-c, 0.0) +FAST(cert-msc30-c, 0.0) +FAST(cert-msc32-c, -0.0) +FAST(cert-msc33-c, 2.0) +FAST(cert-msc50-cpp, -0.0) +FAST(cert-msc51-cpp, 2.0) +FAST(cert-msc54-cpp, -0.0) +FAST(cert-oop11-cpp, -0.0) +FAST(cert-oop54-cpp, 2.0) +FAST(cert-oop57-cpp, -0.0) FAST(cert-oop58-cpp, 0.0) -FAST(cert-pos44-c, 0.0) -FAST(cert-pos47-c, -0.0) -FAST(cert-sig30-c, 2.0) -FAST(cert-str34-c, 0.0) +FAST(cert-pos44-c, 2.0) +FAST(cert-pos47-c, 0.0) +FAST(cert-sig30-c, 1.0) +FAST(cert-str34-c, 2.0) FAST(concurrency-mt-unsafe, 3.0) FAST(concurrency-thread-canceltype-asynchronous, 1.0) -FAST(cppcoreguidelines-avoid-c-arrays, 2.0) -FAST(cppcoreguidelines-avoid-const-or-ref-data-members, 1.0) -FAST(cppcoreguidelines-avoid-do-while, -2.0) -FAST(cppcoreguidelines-avoid-goto, 2.0) -FAST(cppcoreguidelines-avoid-magic-numbers, 1.0) +FAST(cppcoreguidelines-avoid-c-arrays, 0.0) +FAST(cppcoreguidelines-avoid-capturing-lambda-coroutines, -1.0) +FAST(cppcoreguidelines-avoid-const-or-ref-data-members, -2.0) +FAST(cppcoreguidelines-avoid-do-while, -1.0) +FAST(cppcoreguidelines-avoid-goto, -1.0) +FAST(cppcoreguidelines-avoid-magic-numbers, -2.0) FAST(cppcoreguidelines-avoid-non-const-global-variables, -0.0) +FAST(cppcoreguidelines-avoid-reference-coroutine-parameters, -0.0) FAST(cppcoreguidelines-c-copy-assignment-signature, 1.0) -FAST(cppcoreguidelines-explicit-virtual-functions, -1.0) -FAST(cppcoreguidelines-init-variables, 2.0) -FAST(cppcoreguidelines-interfaces-global-init, -1.0) -FAST(cppcoreguidelines-macro-usage, 2.0) -FAST(cppcoreguidelines-narrowing-conversions, -2.0) +FAST(cppcoreguidelines-explicit-virtual-functions, 0.0) +FAST(cppcoreguidelines-init-variables, 1.0) +FAST(cppcoreguidelines-interfaces-global-init, 1.0) +FAST(cppcoreguidelines-macro-to-enum, 0.0) +FAST(cppcoreguidelines-macro-usage, -0.0) +FAST(cppcoreguidelines-misleading-capture-default-by-value, -1.0) +FAST(cppcoreguidelines-missing-std-forward, 0.0) +FAST(cppcoreguidelines-narrowing-conversions, 2.0) FAST(cppcoreguidelines-no-malloc, -1.0) -FAST(cppcoreguidelines-non-private-member-variables-in-classes, -0.0) +FAST(cppcoreguidelines-no-suspend-with-lock, 1.0) +FAST(cppcoreguidelines-noexcept-destructor, -0.0) +FAST(cppcoreguidelines-noexcept-move-operations, 2.0) +FAST(cppcoreguidelines-noexcept-swap, -2.0) +FAST(cppcoreguidelines-non-private-member-variables-in-classes, 1.0) FAST(cppcoreguidelines-owning-memory, 3.0) -FAST(cppcoreguidelines-prefer-member-initializer, 1.0) -FAST(cppcoreguidelines-pro-bounds-array-to-pointer-decay, 3.0) -FAST(cppcoreguidelines-pro-bounds-constant-array-index, 3.0) +FAST(cppcoreguidelines-prefer-member-initializer, 2.0) +FAST(cppcoreguidelines-pro-bounds-array-to-pointer-decay, 2.0) +FAST(cppcoreguidelines-pro-bounds-constant-array-index, 1.0) FAST(cppcoreguidelines-pro-bounds-pointer-arithmetic, 0.0) -FAST(cppcoreguidelines-pro-type-const-cast, 1.0) -FAST(cppcoreguidelines-pro-type-cstyle-cast, -1.0) +FAST(cppcoreguidelines-pro-type-const-cast, -1.0) +FAST(cppcoreguidelines-pro-type-cstyle-cast, 2.0) FAST(cppcoreguidelines-pro-type-member-init, 1.0) -FAST(cppcoreguidelines-pro-type-reinterpret-cast, 2.0) +FAST(cppcoreguidelines-pro-type-reinterpret-cast, -1.0) FAST(cppcoreguidelines-pro-type-static-cast-downcast, 0.0) -FAST(cppcoreguidelines-pro-type-union-access, 1.0) -FAST(cppcoreguidelines-pro-type-vararg, 1.0) -FAST(cppcoreguidelines-slicing, 3.0) +FAST(cppcoreguidelines-pro-type-union-access, 0.0) +FAST(cppcoreguidelines-pro-type-vararg, -1.0) +FAST(cppcoreguidelines-rvalue-reference-param-not-moved, 1.0) +FAST(cppcoreguidelines-slicing, 1.0) FAST(cppcoreguidelines-special-member-functions, -1.0) -FAST(cppcoreguidelines-virtual-class-destructor, 0.0) -FAST(darwin-avoid-spinlock, 1.0) -FAST(darwin-dispatch-once-nonstatic, -0.0) -FAST(fuchsia-default-arguments-calls, 2.0) +FAST(cppcoreguidelines-use-default-member-init, 0.0) +FAST(cppcoreguidelines-virtual-class-destructor, -0.0) +FAST(darwin-avoid-spinlock, 2.0) +FAST(darwin-dispatch-once-nonstatic, 0.0) +FAST(fuchsia-default-arguments-calls, 1.0) FAST(fuchsia-default-arguments-declarations, -0.0) FAST(fuchsia-header-anon-namespaces, -0.0) -FAST(fuchsia-multiple-inheritance, -1.0) -FAST(fuchsia-overloaded-operator, -0.0) +FAST(fuchsia-multiple-inheritance, 0.0) +FAST(fuchsia-overloaded-operator, 4.0) FAST(fuchsia-statically-constructed-objects, -0.0) -FAST(fuchsia-trailing-return, 2.0) +FAST(fuchsia-trailing-return, 1.0) FAST(fuchsia-virtual-inheritance, 1.0) -FAST(google-build-explicit-make-pair, 2.0) +FAST(google-build-explicit-make-pair, 3.0) FAST(google-build-namespaces, -1.0) -FAST(google-build-using-namespace, 0.0) -FAST(google-default-arguments, 1.0) +FAST(google-build-using-namespace, -0.0) +FAST(google-default-arguments, 0.0) FAST(google-explicit-constructor, 2.0) -FAST(google-global-names-in-headers, -1.0) +FAST(google-global-names-in-headers, 0.0) FAST(google-objc-avoid-nsobject-new, 1.0) -FAST(google-objc-avoid-throwing-exception, 1.0) -FAST(google-objc-function-naming, 1.0) -FAST(google-objc-global-variable-declaration, -0.0) -FAST(google-readability-avoid-underscore-in-googletest-name, -0.0) -FAST(google-readability-braces-around-statements, 1.0) -FAST(google-readability-casting, 1.0) -FAST(google-readability-function-size, 0.0) -FAST(google-readability-namespace-comments, 0.0) +FAST(google-objc-avoid-throwing-exception, -0.0) +FAST(google-objc-function-naming, -1.0) +FAST(google-objc-global-variable-declaration, 0.0) +FAST(google-readability-avoid-underscore-in-googletest-name, 1.0) +FAST(google-readability-braces-around-statements, 0.0) +FAST(google-readability-casting, -0.0) +FAST(google-readability-function-size, 3.0) +FAST(google-readability-namespace-comments, -0.0) FAST(google-readability-todo, 1.0) -FAST(google-runtime-int, -1.0) -FAST(google-runtime-operator, -0.0) -FAST(google-upgrade-googletest-case, 0.0) +FAST(google-runtime-int, 0.0) +FAST(google-runtime-operator, 0.0) +FAST(google-upgrade-googletest-case, 1.0) FAST(hicpp-avoid-c-arrays, 1.0) -FAST(hicpp-avoid-goto, 0.0) -FAST(hicpp-braces-around-statements, 1.0) -FAST(hicpp-deprecated-headers, -1.0) -FAST(hicpp-exception-baseclass, 0.0) -FAST(hicpp-explicit-conversions, -1.0) +FAST(hicpp-avoid-goto, -0.0) +FAST(hicpp-braces-around-statements, 0.0) +FAST(hicpp-deprecated-headers, 1.0) +FAST(hicpp-exception-baseclass, -1.0) +FAST(hicpp-explicit-conversions, 1.0) FAST(hicpp-function-size, 1.0) -FAST(hicpp-invalid-access-moved, 6.0) +FAST(hicpp-ignored-remove-result, 3.0) +FAST(hicpp-invalid-access-moved, 4.0) FAST(hicpp-member-init, 2.0) -FAST(hicpp-move-const-arg, 1.0) -FAST(hicpp-multiway-paths-covered, 3.0) -FAST(hicpp-named-parameter, 1.0) -FAST(hicpp-new-delete-operators, 0.0) -FAST(hicpp-no-array-decay, 3.0) -FAST(hicpp-no-assembler, -0.0) -FAST(hicpp-no-malloc, 1.0) +FAST(hicpp-move-const-arg, 3.0) +FAST(hicpp-multiway-paths-covered, 0.0) +FAST(hicpp-named-parameter, 2.0) +FAST(hicpp-new-delete-operators, -0.0) +FAST(hicpp-no-array-decay, 4.0) +FAST(hicpp-no-assembler, 1.0) +FAST(hicpp-no-malloc, 2.0) FAST(hicpp-noexcept-move, -0.0) -FAST(hicpp-signed-bitwise, 0.0) -FAST(hicpp-special-member-functions, 0.0) -FAST(hicpp-static-assert, 1.0) -FAST(hicpp-undelegated-constructor, 3.0) -FAST(hicpp-uppercase-literal-suffix, 3.0) -FAST(hicpp-use-auto, 3.0) -FAST(hicpp-use-emplace, 1.0) -FAST(hicpp-use-equals-default, 1.0) -FAST(hicpp-use-equals-delete, 0.0) -FAST(hicpp-use-noexcept, -1.0) -FAST(hicpp-use-nullptr, 2.0) -FAST(hicpp-use-override, -0.0) -FAST(hicpp-vararg, -2.0) -FAST(linuxkernel-must-check-errs, 1.0) -FAST(llvm-else-after-return, 0.0) -FAST(llvm-header-guard, 2.0) -FAST(llvm-include-order, 3.0) -FAST(llvm-namespace-comment, 1.0) -FAST(llvm-prefer-isa-or-dyn-cast-in-conditionals, 0.0) -FAST(llvm-prefer-register-over-unsigned, 1.0) -FAST(llvm-qualified-auto, 0.0) -FAST(llvm-twine-local, -2.0) -FAST(llvmlibc-callee-namespace, 2.0) -FAST(llvmlibc-implementation-in-namespace, 2.0) -FAST(llvmlibc-restrict-system-libc-headers, -0.0) -FAST(misc-confusable-identifiers, 1.0) -SLOW(misc-const-correctness, 261.0) +FAST(hicpp-signed-bitwise, -1.0) +FAST(hicpp-special-member-functions, -2.0) +FAST(hicpp-static-assert, 4.0) +FAST(hicpp-undelegated-constructor, 6.0) +FAST(hicpp-uppercase-literal-suffix, 5.0) +FAST(hicpp-use-auto, 0.0) +FAST(hicpp-use-emplace, 3.0) +FAST(hicpp-use-equals-default, 2.0) +FAST(hicpp-use-equals-delete, 1.0) +FAST(hicpp-use-noexcept, -0.0) +FAST(hicpp-use-nullptr, 1.0) +FAST(hicpp-use-override, -1.0) +FAST(hicpp-vararg, 0.0) +FAST(linuxkernel-must-check-errs, -0.0) +FAST(llvm-else-after-return, -1.0) +FAST(llvm-header-guard, 3.0) +FAST(llvm-include-order, 0.0) +FAST(llvm-namespace-comment, 0.0) +FAST(llvm-prefer-isa-or-dyn-cast-in-conditionals, 3.0) +FAST(llvm-prefer-register-over-unsigned, -0.0) +FAST(llvm-qualified-auto, 4.0) +FAST(llvm-twine-local, -0.0) +FAST(llvmlibc-callee-namespace, -0.0) +FAST(llvmlibc-implementation-in-namespace, 1.0) +FAST(llvmlibc-inline-function-decl, 3.0) +FAST(llvmlibc-restrict-system-libc-headers, 0.0) +FAST(misc-confusable-identifiers, -1.0) +SLOW(misc-const-correctness, 67.0) +FAST(misc-coroutine-hostile-raii, 1.0) FAST(misc-definitions-in-headers, -1.0) -FAST(misc-misleading-bidirectional, -0.0) -FAST(misc-misleading-identifier, -1.0) -FAST(misc-misplaced-const, 1.0) -FAST(misc-new-delete-overloads, -1.0) -FAST(misc-no-recursion, -2.0) -FAST(misc-non-copyable-objects, 1.0) -FAST(misc-non-private-member-variables-in-classes, 2.0) -FAST(misc-redundant-expression, 0.0) -FAST(misc-static-assert, 1.0) -FAST(misc-throw-by-value-catch-by-reference, -1.0) +SLOW(misc-header-include-cycle, 10.0) +FAST(misc-include-cleaner, 5.0) +FAST(misc-misleading-bidirectional, 1.0) +FAST(misc-misleading-identifier, 3.0) +FAST(misc-misplaced-const, -2.0) +FAST(misc-new-delete-overloads, 1.0) +FAST(misc-no-recursion, 0.0) +FAST(misc-non-copyable-objects, 0.0) +FAST(misc-non-private-member-variables-in-classes, -1.0) +FAST(misc-redundant-expression, 1.0) +FAST(misc-static-assert, 3.0) +FAST(misc-throw-by-value-catch-by-reference, -0.0) FAST(misc-unconventional-assign-operator, 1.0) -FAST(misc-uniqueptr-reset-release, 0.0) -FAST(misc-unused-alias-decls, 1.0) -FAST(misc-unused-parameters, 0.0) -FAST(misc-unused-using-decls, 4.0) +FAST(misc-uniqueptr-reset-release, 2.0) +FAST(misc-unused-alias-decls, 2.0) +FAST(misc-unused-parameters, 3.0) +FAST(misc-unused-using-decls, 1.0) +FAST(misc-use-anonymous-namespace, 1.0) +FAST(misc-use-internal-linkage, 1.0) FAST(modernize-avoid-bind, 1.0) FAST(modernize-avoid-c-arrays, 2.0) -FAST(modernize-concat-nested-namespaces, -1.0) -FAST(modernize-deprecated-headers, -0.0) -FAST(modernize-deprecated-ios-base-aliases, 1.0) +FAST(modernize-concat-nested-namespaces, -0.0) +FAST(modernize-deprecated-headers, 0.0) +FAST(modernize-deprecated-ios-base-aliases, 0.0) FAST(modernize-loop-convert, 2.0) -FAST(modernize-macro-to-enum, 1.0) -FAST(modernize-make-shared, -0.0) -FAST(modernize-make-unique, 0.0) -FAST(modernize-pass-by-value, 1.0) -FAST(modernize-raw-string-literal, 1.0) -FAST(modernize-redundant-void-arg, -1.0) -FAST(modernize-replace-auto-ptr, -1.0) -FAST(modernize-replace-disallow-copy-and-assign-macro, -2.0) -FAST(modernize-replace-random-shuffle, 0.0) -FAST(modernize-return-braced-init-list, -1.0) -FAST(modernize-shrink-to-fit, 2.0) -FAST(modernize-unary-static-assert, -1.0) -FAST(modernize-use-auto, 2.0) +FAST(modernize-macro-to-enum, 0.0) +FAST(modernize-make-shared, 2.0) +FAST(modernize-make-unique, 1.0) +FAST(modernize-min-max-use-initializer-list, 1.0) +FAST(modernize-pass-by-value, 0.0) +FAST(modernize-raw-string-literal, 2.0) +FAST(modernize-redundant-void-arg, 1.0) +FAST(modernize-replace-auto-ptr, 0.0) +FAST(modernize-replace-disallow-copy-and-assign-macro, -0.0) +FAST(modernize-replace-random-shuffle, 1.0) +FAST(modernize-return-braced-init-list, 1.0) +FAST(modernize-shrink-to-fit, 1.0) +FAST(modernize-type-traits, 1.0) +FAST(modernize-unary-static-assert, 1.0) +FAST(modernize-use-auto, 0.0) FAST(modernize-use-bool-literals, 1.0) -FAST(modernize-use-default-member-init, 2.0) -FAST(modernize-use-emplace, 1.0) -FAST(modernize-use-equals-default, 2.0) -FAST(modernize-use-equals-delete, 0.0) -FAST(modernize-use-nodiscard, 1.0) -FAST(modernize-use-noexcept, 1.0) -FAST(modernize-use-nullptr, 2.0) -FAST(modernize-use-override, 1.0) -FAST(modernize-use-trailing-return-type, -0.0) -FAST(modernize-use-transparent-functors, -1.0) -FAST(modernize-use-uncaught-exceptions, 1.0) +FAST(modernize-use-constraints, 1.0) +FAST(modernize-use-default-member-init, -0.0) +FAST(modernize-use-designated-initializers, 1.0) +FAST(modernize-use-emplace, 2.0) +FAST(modernize-use-equals-default, 1.0) +FAST(modernize-use-equals-delete, 2.0) +FAST(modernize-use-nodiscard, -2.0) +FAST(modernize-use-noexcept, -2.0) +FAST(modernize-use-nullptr, 1.0) +FAST(modernize-use-override, 0.0) +FAST(modernize-use-ranges, 0.0) +FAST(modernize-use-starts-ends-with, 0.0) +FAST(modernize-use-std-format, -1.0) +FAST(modernize-use-std-numbers, 0.0) +FAST(modernize-use-std-print, -0.0) +FAST(modernize-use-trailing-return-type, 3.0) +FAST(modernize-use-transparent-functors, 0.0) +FAST(modernize-use-uncaught-exceptions, 0.0) FAST(modernize-use-using, 1.0) -FAST(objc-assert-equals, -1.0) -FAST(objc-avoid-nserror-init, -2.0) -FAST(objc-dealloc-in-category, -0.0) -FAST(objc-forbidden-subclassing, 0.0) -FAST(objc-missing-hash, 1.0) -FAST(objc-nsdate-formatter, 1.0) -FAST(objc-nsinvocation-argument-lifetime, -2.0) -FAST(objc-property-declaration, -1.0) -FAST(objc-super-self, -0.0) -FAST(openmp-exception-escape, 1.0) -FAST(openmp-use-default-none, 1.0) -FAST(performance-faster-string-find, 2.0) -FAST(performance-for-range-copy, 2.0) -FAST(performance-implicit-conversion-in-loop, 1.0) -FAST(performance-inefficient-algorithm, 0.0) -FAST(performance-inefficient-string-concatenation, -0.0) -FAST(performance-inefficient-vector-operation, 1.0) -FAST(performance-move-const-arg, 3.0) -FAST(performance-move-constructor-init, 1.0) -FAST(performance-no-automatic-move, -1.0) -FAST(performance-no-int-to-ptr, 2.0) +FAST(objc-assert-equals, 1.0) +FAST(objc-avoid-nserror-init, -0.0) +FAST(objc-dealloc-in-category, 0.0) +FAST(objc-forbidden-subclassing, 2.0) +FAST(objc-missing-hash, -0.0) +FAST(objc-nsdate-formatter, 0.0) +FAST(objc-nsinvocation-argument-lifetime, 0.0) +FAST(objc-property-declaration, -0.0) +FAST(objc-super-self, -2.0) +FAST(openmp-exception-escape, -1.0) +FAST(openmp-use-default-none, 2.0) +FAST(performance-avoid-endl, 2.0) +FAST(performance-enum-size, -1.0) +FAST(performance-faster-string-find, 1.0) +FAST(performance-for-range-copy, 1.0) +FAST(performance-implicit-conversion-in-loop, 0.0) +FAST(performance-inefficient-algorithm, 1.0) +FAST(performance-inefficient-string-concatenation, 1.0) +FAST(performance-inefficient-vector-operation, -0.0) +FAST(performance-move-const-arg, 2.0) +FAST(performance-move-constructor-init, 2.0) +FAST(performance-no-automatic-move, 2.0) +FAST(performance-no-int-to-ptr, 0.0) +FAST(performance-noexcept-destructor, -2.0) FAST(performance-noexcept-move-constructor, 1.0) -FAST(performance-trivially-destructible, -1.0) -FAST(performance-type-promotion-in-math-fn, 4.0) -FAST(performance-unnecessary-copy-initialization, 4.0) +FAST(performance-noexcept-swap, -2.0) +FAST(performance-trivially-destructible, 3.0) +FAST(performance-type-promotion-in-math-fn, 2.0) +FAST(performance-unnecessary-copy-initialization, 2.0) FAST(performance-unnecessary-value-param, 2.0) -FAST(portability-restrict-system-includes, 2.0) -FAST(portability-simd-intrinsics, 2.0) -FAST(portability-std-allocator-const, 2.0) +FAST(portability-restrict-system-includes, 1.0) +FAST(portability-simd-intrinsics, 1.0) +FAST(portability-std-allocator-const, 3.0) FAST(readability-avoid-const-params-in-decls, -0.0) -FAST(readability-braces-around-statements, 2.0) -FAST(readability-const-return-type, -0.0) -FAST(readability-container-contains, -0.0) -FAST(readability-container-data-pointer, 0.0) -SLOW(readability-container-size-empty, 16.0) -FAST(readability-convert-member-functions-to-static, 0.0) -FAST(readability-delete-null-pointer, 0.0) -FAST(readability-duplicate-include, -0.0) -FAST(readability-else-after-return, 1.0) +FAST(readability-avoid-nested-conditional-operator, -1.0) +FAST(readability-avoid-return-with-void-value, 0.0) +FAST(readability-avoid-unconditional-preprocessor-if, -1.0) +FAST(readability-braces-around-statements, 1.0) +FAST(readability-const-return-type, -1.0) +FAST(readability-container-contains, 3.0) +FAST(readability-container-data-pointer, -1.0) +SLOW(readability-container-size-empty, 13.0) +FAST(readability-convert-member-functions-to-static, 4.0) +FAST(readability-delete-null-pointer, -1.0) +FAST(readability-duplicate-include, 2.0) +FAST(readability-else-after-return, 0.0) +FAST(readability-enum-initial-value, 0.0) FAST(readability-function-cognitive-complexity, 0.0) -FAST(readability-function-size, 3.0) -FAST(readability-identifier-length, -1.0) -FAST(readability-identifier-naming, 5.0) -FAST(readability-implicit-bool-conversion, 2.0) -FAST(readability-inconsistent-declaration-parameter-name, 1.0) -FAST(readability-isolate-declaration, 1.0) -FAST(readability-magic-numbers, -1.0) -FAST(readability-make-member-function-const, 2.0) -FAST(readability-misleading-indentation, 0.0) -FAST(readability-misplaced-array-index, -0.0) +FAST(readability-function-size, 0.0) +FAST(readability-identifier-length, 2.0) +FAST(readability-identifier-naming, 1.0) +FAST(readability-implicit-bool-conversion, 3.0) +FAST(readability-inconsistent-declaration-parameter-name, -0.0) +FAST(readability-isolate-declaration, 0.0) +FAST(readability-magic-numbers, 4.0) +FAST(readability-make-member-function-const, 1.0) +FAST(readability-math-missing-parentheses, 1.0) +FAST(readability-misleading-indentation, 1.0) +FAST(readability-misplaced-array-index, 0.0) FAST(readability-named-parameter, -0.0) -FAST(readability-non-const-parameter, 1.0) -FAST(readability-qualified-auto, -0.0) -FAST(readability-redundant-access-specifiers, -1.0) -FAST(readability-redundant-control-flow, -1.0) -FAST(readability-redundant-declaration, -0.0) -FAST(readability-redundant-function-ptr-dereference, -1.0) -FAST(readability-redundant-member-init, 0.0) -FAST(readability-redundant-preprocessor, 0.0) -FAST(readability-redundant-smartptr-get, 6.0) -FAST(readability-redundant-string-cstr, 0.0) -FAST(readability-redundant-string-init, 1.0) -FAST(readability-simplify-boolean-expr, 1.0) -FAST(readability-simplify-subscript-expr, -0.0) -FAST(readability-static-accessed-through-instance, 1.0) -FAST(readability-static-definition-in-anonymous-namespace, -0.0) +FAST(readability-non-const-parameter, 2.0) +FAST(readability-operators-representation, 0.0) +FAST(readability-qualified-auto, 0.0) +FAST(readability-redundant-access-specifiers, 1.0) +FAST(readability-redundant-casting, -0.0) +FAST(readability-redundant-control-flow, 1.0) +FAST(readability-redundant-declaration, 1.0) +FAST(readability-redundant-function-ptr-dereference, 0.0) +FAST(readability-redundant-inline-specifier, 0.0) +FAST(readability-redundant-member-init, 1.0) +FAST(readability-redundant-preprocessor, -0.0) +FAST(readability-redundant-smartptr-get, 4.0) +FAST(readability-redundant-string-cstr, 1.0) +FAST(readability-redundant-string-init, -0.0) +FAST(readability-reference-to-constructed-temporary, 3.0) +FAST(readability-simplify-boolean-expr, -0.0) +FAST(readability-simplify-subscript-expr, 1.0) +FAST(readability-static-accessed-through-instance, 0.0) +FAST(readability-static-definition-in-anonymous-namespace, 0.0) FAST(readability-string-compare, -0.0) -FAST(readability-suspicious-call-argument, 0.0) -FAST(readability-uniqueptr-delete-release, 1.0) -FAST(readability-uppercase-literal-suffix, 3.0) -FAST(readability-use-anyofallof, 1.0) +FAST(readability-suspicious-call-argument, -1.0) +FAST(readability-uniqueptr-delete-release, 3.0) +FAST(readability-uppercase-literal-suffix, 0.0) +FAST(readability-use-anyofallof, 2.0) +FAST(readability-use-std-min-max, -1.0) FAST(zircon-temporary-objects, 1.0) #undef FAST diff --git a/clang-tools-extra/clangd/index/SymbolCollector.cpp b/clang-tools-extra/clangd/index/SymbolCollector.cpp index 5c4e2150cf312..acabf51df69e4 100644 --- a/clang-tools-extra/clangd/index/SymbolCollector.cpp +++ b/clang-tools-extra/clangd/index/SymbolCollector.cpp @@ -290,9 +290,10 @@ class SymbolCollector::HeaderFileURICache { } struct FrameworkHeaderPath { - // Path to the framework directory containing the Headers/PrivateHeaders - // directories e.g. /Frameworks/Foundation.framework/ - llvm::StringRef HeadersParentDir; + // Path to the frameworks directory containing the .framework directory. + llvm::StringRef FrameworkParentDir; + // Name of the framework. + llvm::StringRef FrameworkName; // Subpath relative to the Headers or PrivateHeaders dir, e.g. NSObject.h // Note: This is NOT relative to the `HeadersParentDir`. llvm::StringRef HeaderSubpath; @@ -306,19 +307,17 @@ class SymbolCollector::HeaderFileURICache { path::reverse_iterator I = path::rbegin(Path); path::reverse_iterator Prev = I; path::reverse_iterator E = path::rend(Path); + FrameworkHeaderPath HeaderPath; while (I != E) { - if (*I == "Headers") { - FrameworkHeaderPath HeaderPath; - HeaderPath.HeadersParentDir = Path.substr(0, I - E); + if (*I == "Headers" || *I == "PrivateHeaders") { HeaderPath.HeaderSubpath = Path.substr(Prev - E); - HeaderPath.IsPrivateHeader = false; - return HeaderPath; - } - if (*I == "PrivateHeaders") { - FrameworkHeaderPath HeaderPath; - HeaderPath.HeadersParentDir = Path.substr(0, I - E); - HeaderPath.HeaderSubpath = Path.substr(Prev - E); - HeaderPath.IsPrivateHeader = true; + HeaderPath.IsPrivateHeader = *I == "PrivateHeaders"; + if (++I == E) + break; + HeaderPath.FrameworkName = *I; + if (!HeaderPath.FrameworkName.consume_back(".framework")) + break; + HeaderPath.FrameworkParentDir = Path.substr(0, I - E); return HeaderPath; } Prev = I; @@ -334,26 +333,27 @@ class SymbolCollector::HeaderFileURICache { // <Foundation/NSObject_Private.h> which should be used instead of directly // importing the header. std::optional<std::string> - getFrameworkUmbrellaSpelling(llvm::StringRef Framework, - const HeaderSearch &HS, + getFrameworkUmbrellaSpelling(const HeaderSearch &HS, FrameworkHeaderPath &HeaderPath) { + StringRef Framework = HeaderPath.FrameworkName; auto Res = CacheFrameworkToUmbrellaHeaderSpelling.try_emplace(Framework); auto *CachedSpelling = &Res.first->second; if (!Res.second) { return HeaderPath.IsPrivateHeader ? CachedSpelling->PrivateHeader : CachedSpelling->PublicHeader; } - SmallString<256> UmbrellaPath(HeaderPath.HeadersParentDir); - llvm::sys::path::append(UmbrellaPath, "Headers", Framework + ".h"); + SmallString<256> UmbrellaPath(HeaderPath.FrameworkParentDir); + llvm::sys::path::append(UmbrellaPath, Framework + ".framework", "Headers", + Framework + ".h"); llvm::vfs::Status Status; auto StatErr = HS.getFileMgr().getNoncachedStatValue(UmbrellaPath, Status); if (!StatErr) CachedSpelling->PublicHeader = llvm::formatv("<{0}/{0}.h>", Framework); - UmbrellaPath = HeaderPath.HeadersParentDir; - llvm::sys::path::append(UmbrellaPath, "PrivateHeaders", - Framework + "_Private.h"); + UmbrellaPath = HeaderPath.FrameworkParentDir; + llvm::sys::path::append(UmbrellaPath, Framework + ".framework", + "PrivateHeaders", Framework + "_Private.h"); StatErr = HS.getFileMgr().getNoncachedStatValue(UmbrellaPath, Status); if (!StatErr) @@ -369,8 +369,7 @@ class SymbolCollector::HeaderFileURICache { // give <Foundation/Foundation.h> if the umbrella header exists, otherwise // <Foundation/NSObject.h>. std::optional<llvm::StringRef> - getFrameworkHeaderIncludeSpelling(FileEntryRef FE, llvm::StringRef Framework, - HeaderSearch &HS) { + getFrameworkHeaderIncludeSpelling(FileEntryRef FE, HeaderSearch &HS) { auto Res = CachePathToFrameworkSpelling.try_emplace(FE.getName()); auto *CachedHeaderSpelling = &Res.first->second; if (!Res.second) @@ -384,13 +383,15 @@ class SymbolCollector::HeaderFileURICache { return std::nullopt; } if (auto UmbrellaSpelling = - getFrameworkUmbrellaSpelling(Framework, HS, *HeaderPath)) { + getFrameworkUmbrellaSpelling(HS, *HeaderPath)) { *CachedHeaderSpelling = *UmbrellaSpelling; return llvm::StringRef(*CachedHeaderSpelling); } *CachedHeaderSpelling = - llvm::formatv("<{0}/{1}>", Framework, HeaderPath->HeaderSubpath).str(); + llvm::formatv("<{0}/{1}>", HeaderPath->FrameworkName, + HeaderPath->HeaderSubpath) + .str(); return llvm::StringRef(*CachedHeaderSpelling); } @@ -409,11 +410,8 @@ class SymbolCollector::HeaderFileURICache { // Framework headers are spelled as <FrameworkName/Foo.h>, not // "path/FrameworkName.framework/Headers/Foo.h". auto &HS = PP->getHeaderSearchInfo(); - if (const auto *HFI = HS.getExistingFileInfo(*FE)) - if (!HFI->Framework.empty()) - if (auto Spelling = - getFrameworkHeaderIncludeSpelling(*FE, HFI->Framework, HS)) - return *Spelling; + if (auto Spelling = getFrameworkHeaderIncludeSpelling(*FE, HS)) + return *Spelling; if (!tooling::isSelfContainedHeader(*FE, PP->getSourceManager(), PP->getHeaderSearchInfo())) { diff --git a/clang-tools-extra/clangd/refactor/Rename.cpp b/clang-tools-extra/clangd/refactor/Rename.cpp index c85e13dbdfe97..2e4d5de6eff77 100644 --- a/clang-tools-extra/clangd/refactor/Rename.cpp +++ b/clang-tools-extra/clangd/refactor/Rename.cpp @@ -40,6 +40,8 @@ namespace clang { namespace clangd { namespace { +using tooling::SymbolName; + std::optional<std::string> filePath(const SymbolLocation &Loc, llvm::StringRef HintFilePath) { if (!Loc) @@ -591,11 +593,11 @@ bool isMatchingSelectorName(const syntax::Token &Cur, const syntax::Token &Next, // The search will terminate upon seeing Terminator or a ; at the top level. std::optional<SymbolRange> findAllSelectorPieces(llvm::ArrayRef<syntax::Token> Tokens, - const SourceManager &SM, Selector Sel, + const SourceManager &SM, const SymbolName &Name, tok::TokenKind Terminator) { assert(!Tokens.empty()); - unsigned NumArgs = Sel.getNumArgs(); + unsigned NumArgs = Name.getNamePieces().size(); llvm::SmallVector<tok::TokenKind, 8> Closes; std::vector<Range> SelectorPieces; for (unsigned Index = 0, Last = Tokens.size(); Index < Last - 1; ++Index) { @@ -605,12 +607,12 @@ findAllSelectorPieces(llvm::ArrayRef<syntax::Token> Tokens, auto PieceCount = SelectorPieces.size(); if (PieceCount < NumArgs && isMatchingSelectorName(Tok, Tokens[Index + 1], SM, - Sel.getNameForSlot(PieceCount))) { + Name.getNamePieces()[PieceCount])) { // If 'foo:' instead of ':' (empty selector), we need to skip the ':' // token after the name. We don't currently properly support empty // selectors since we may lex them improperly due to ternary statements // as well as don't properly support storing their ranges for edits. - if (!Sel.getNameForSlot(PieceCount).empty()) + if (!Name.getNamePieces()[PieceCount].empty()) ++Index; SelectorPieces.push_back( halfOpenToRange(SM, Tok.range(SM).toCharRange(SM))); @@ -662,16 +664,17 @@ findAllSelectorPieces(llvm::ArrayRef<syntax::Token> Tokens, /// Collects all ranges of the given identifier/selector in the source code. /// -/// If a selector is given, this does a full lex of the given source code in -/// order to identify all selector fragments (e.g. in method exprs/decls) since -/// they are non-contiguous. -std::vector<SymbolRange> collectRenameIdentifierRanges( - llvm::StringRef Identifier, llvm::StringRef Content, - const LangOptions &LangOpts, std::optional<Selector> Selector) { +/// If `Name` is an Objective-C symbol name, this does a full lex of the given +/// source code in order to identify all selector fragments (e.g. in method +/// exprs/decls) since they are non-contiguous. +std::vector<SymbolRange> +collectRenameIdentifierRanges(const tooling::SymbolName &Name, + llvm::StringRef Content, + const LangOptions &LangOpts) { std::vector<SymbolRange> Ranges; - if (!Selector) { + if (auto SinglePiece = Name.getSinglePiece()) { auto IdentifierRanges = - collectIdentifierRanges(Identifier, Content, LangOpts); + collectIdentifierRanges(*SinglePiece, Content, LangOpts); for (const auto &R : IdentifierRanges) Ranges.emplace_back(R); return Ranges; @@ -685,7 +688,7 @@ std::vector<SymbolRange> collectRenameIdentifierRanges( // parsing a method declaration or definition which isn't at the top level or // similar looking expressions (e.g. an @selector() expression). llvm::SmallVector<tok::TokenKind, 8> Closes; - llvm::StringRef FirstSelPiece = Selector->getNameForSlot(0); + llvm::StringRef FirstSelPiece = Name.getNamePieces()[0]; auto Tokens = syntax::tokenize(SM.getMainFileID(), SM, LangOpts); unsigned Last = Tokens.size() - 1; @@ -717,7 +720,7 @@ std::vector<SymbolRange> collectRenameIdentifierRanges( // Check if we can find all the relevant selector peices starting from // this token auto SelectorRanges = - findAllSelectorPieces(ArrayRef(Tokens).slice(Index), SM, *Selector, + findAllSelectorPieces(ArrayRef(Tokens).slice(Index), SM, Name, Closes.empty() ? tok::l_brace : Closes.back()); if (SelectorRanges) Ranges.emplace_back(std::move(*SelectorRanges)); @@ -764,7 +767,6 @@ renameObjCMethodWithinFile(ParsedAST &AST, const ObjCMethodDecl *MD, std::vector<SourceLocation> SelectorOccurences) { const SourceManager &SM = AST.getSourceManager(); auto Code = SM.getBufferData(SM.getMainFileID()); - auto RenameIdentifier = MD->getSelector().getNameForSlot(0).str(); llvm::SmallVector<llvm::StringRef, 8> NewNames; NewName.split(NewNames, ":"); @@ -774,7 +776,7 @@ renameObjCMethodWithinFile(ParsedAST &AST, const ObjCMethodDecl *MD, Ranges.push_back(tokenRangeForLoc(AST, Loc, SM, LangOpts)); auto FilePath = AST.tuPath(); auto RenameRanges = collectRenameIdentifierRanges( - RenameIdentifier, Code, LangOpts, MD->getSelector()); + SymbolName(MD->getDeclName()), Code, LangOpts); auto RenameEdit = buildRenameEdit(FilePath, Code, RenameRanges, NewNames); if (!RenameEdit) return error("failed to rename in file {0}: {1}", FilePath, @@ -936,21 +938,14 @@ renameOutsideFile(const NamedDecl &RenameDecl, llvm::StringRef MainFilePath, ExpBuffer.getError().message()); continue; } - std::string RenameIdentifier = RenameDecl.getNameAsString(); - std::optional<Selector> Selector = std::nullopt; + SymbolName RenameName(RenameDecl.getDeclName()); llvm::SmallVector<llvm::StringRef, 8> NewNames; - if (const auto *MD = dyn_cast<ObjCMethodDecl>(&RenameDecl)) { - RenameIdentifier = MD->getSelector().getNameForSlot(0).str(); - if (MD->getSelector().getNumArgs() > 1) - Selector = MD->getSelector(); - } NewName.split(NewNames, ":"); auto AffectedFileCode = (*ExpBuffer)->getBuffer(); - auto RenameRanges = - adjustRenameRanges(AffectedFileCode, RenameIdentifier, - std::move(FileAndOccurrences.second), - RenameDecl.getASTContext().getLangOpts(), Selector); + auto RenameRanges = adjustRenameRanges( + AffectedFileCode, RenameName, std::move(FileAndOccurrences.second), + RenameDecl.getASTContext().getLangOpts()); if (!RenameRanges) { // Our heuristics fails to adjust rename ranges to the current state of // the file, it is most likely the index is stale, so we give up the @@ -1028,6 +1023,46 @@ bool operator<(const SymbolRange &LHS, const SymbolRange &RHS) { return LHS.range() < RHS.range(); } +llvm::Expected<FileEdits> +editsForLocations(const llvm::StringMap<std::vector<Range>> &Ranges, + const SymbolName &OldName, const SymbolName &NewName, + llvm::vfs::FileSystem &FS, const LangOptions &LangOpts) { + FileEdits Results; + for (auto &FileAndOccurrences : Ranges) { + llvm::StringRef FilePath = FileAndOccurrences.first(); + + auto ExpBuffer = FS.getBufferForFile(FilePath); + if (!ExpBuffer) { + elog("Fail to read file content: Fail to open file {0}: {1}", FilePath, + ExpBuffer.getError().message()); + continue; + } + std::string RenameIdentifier = OldName.getNamePieces()[0]; + llvm::SmallVector<llvm::StringRef, 8> NewNames(NewName.getNamePieces()); + + auto AffectedFileCode = (*ExpBuffer)->getBuffer(); + auto RenameRanges = + adjustRenameRanges(AffectedFileCode, OldName, + std::move(FileAndOccurrences.second), LangOpts); + if (!RenameRanges) { + // Our heuristics fails to adjust rename ranges to the current state of + // the file, it is most likely the index is stale, so we give up the + // entire rename. + return error("Index results don't match the content of file {0} " + "(the index may be stale)", + FilePath); + } + auto RenameEdit = + buildRenameEdit(FilePath, AffectedFileCode, *RenameRanges, NewNames); + if (!RenameEdit) + return error("failed to rename in file {0}: {1}", FilePath, + RenameEdit.takeError()); + if (!RenameEdit->Replacements.empty()) + Results.insert({FilePath, std::move(*RenameEdit)}); + } + return Results; +} + llvm::Expected<RenameResult> rename(const RenameInputs &RInputs) { assert(!RInputs.Index == !RInputs.FS && "Index and FS must either both be specified or both null."); @@ -1238,14 +1273,13 @@ llvm::Expected<Edit> buildRenameEdit(llvm::StringRef AbsFilePath, // were inserted). If such a "near miss" is found, the rename is still // possible std::optional<std::vector<SymbolRange>> -adjustRenameRanges(llvm::StringRef DraftCode, llvm::StringRef Identifier, - std::vector<Range> Indexed, const LangOptions &LangOpts, - std::optional<Selector> Selector) { +adjustRenameRanges(llvm::StringRef DraftCode, const tooling::SymbolName &Name, + std::vector<Range> Indexed, const LangOptions &LangOpts) { trace::Span Tracer("AdjustRenameRanges"); assert(!Indexed.empty()); assert(llvm::is_sorted(Indexed)); std::vector<SymbolRange> Lexed = - collectRenameIdentifierRanges(Identifier, DraftCode, LangOpts, Selector); + collectRenameIdentifierRanges(Name, DraftCode, LangOpts); llvm::sort(Lexed); return getMappedRanges(Indexed, Lexed); } diff --git a/clang-tools-extra/clangd/refactor/Rename.h b/clang-tools-extra/clangd/refactor/Rename.h index be5c346ae150f..ab2b4dded2a6e 100644 --- a/clang-tools-extra/clangd/refactor/Rename.h +++ b/clang-tools-extra/clangd/refactor/Rename.h @@ -13,6 +13,7 @@ #include "SourceCode.h" #include "clang/Basic/IdentifierTable.h" #include "clang/Basic/LangOptions.h" +#include "clang/Tooling/Refactoring/Rename/SymbolName.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Support/Error.h" #include <optional> @@ -52,6 +53,22 @@ struct RenameInputs { RenameOptions Opts = {}; }; +/// Compute the edits that need to be applied to rename symbols in `Ranges` from +/// `OldName` to `NewName`. The key of `Ranges` is the file path of the file in +/// which the range resides. +/// +/// If `OldName` and `NewName` are single-piece identifiers, this just creates +/// edits to change the ranges to `NewName`. +/// +/// If `OldName` and `NewName` are multi-piece Objective-C selectors, only the +/// start of the ranges is considered and the file is lexed to find the argument +/// labels of the selector to rename. +llvm::Expected<FileEdits> +editsForLocations(const llvm::StringMap<std::vector<Range>> &Ranges, + const tooling::SymbolName &OldName, + const tooling::SymbolName &NewName, llvm::vfs::FileSystem &FS, + const LangOptions &LangOpts); + struct RenameResult { // The range of the symbol that the user can attempt to rename. Range Target; @@ -108,9 +125,8 @@ llvm::Expected<Edit> buildRenameEdit(llvm::StringRef AbsFilePath, /// occurrence has the same length). /// REQUIRED: Indexed is sorted. std::optional<std::vector<SymbolRange>> -adjustRenameRanges(llvm::StringRef DraftCode, llvm::StringRef Identifier, - std::vector<Range> Indexed, const LangOptions &LangOpts, - std::optional<Selector> Selector); +adjustRenameRanges(llvm::StringRef DraftCode, const tooling::SymbolName &Name, + std::vector<Range> Indexed, const LangOptions &LangOpts); /// Calculates the lexed occurrences that the given indexed occurrences map to. /// Returns std::nullopt if we don't find a mapping. diff --git a/clang-tools-extra/clangd/test/input-mirror.test b/clang-tools-extra/clangd/test/input-mirror.test index a34a4a08cf60c..cd0253fa8c5d5 100644 --- a/clang-tools-extra/clangd/test/input-mirror.test +++ b/clang-tools-extra/clangd/test/input-mirror.test @@ -1,3 +1,5 @@ +# REQUIRES: rdar107853608 + # RUN: clangd -pretty -sync -input-mirror-file %t < %s # Note that we have to use '-b' as -input-mirror-file does not have a newline at the end of file. # RUN: diff -b %t %s diff --git a/clang-tools-extra/clangd/unittests/ClangdLSPServerTests.cpp b/clang-tools-extra/clangd/unittests/ClangdLSPServerTests.cpp index 75a140767035b..49a94045ea487 100644 --- a/clang-tools-extra/clangd/unittests/ClangdLSPServerTests.cpp +++ b/clang-tools-extra/clangd/unittests/ClangdLSPServerTests.cpp @@ -262,6 +262,22 @@ TEST_F(LSPTest, ClangTidyRename) { EXPECT_EQ(Params, std::vector{llvm::json::Value(std::move(ExpectedEdit))}); } +TEST_F(LSPTest, ClangTidyCrash_Issue109367) { + // This test requires clang-tidy checks to be linked in. + if (!CLANGD_TIDY_CHECKS) + return; + Opts.ClangTidyProvider = [](tidy::ClangTidyOptions &ClangTidyOpts, + llvm::StringRef) { + ClangTidyOpts.Checks = {"-*,boost-use-ranges"}; + }; + // Check that registering the boost-use-ranges checker's matchers + // on two different threads does not cause a crash. + auto &Client = start(); + Client.didOpen("a.cpp", ""); + Client.didOpen("b.cpp", ""); + Client.sync(); +} + TEST_F(LSPTest, IncomingCalls) { Annotations Code(R"cpp( void calle^e(int); diff --git a/clang-tools-extra/clangd/unittests/RenameTests.cpp b/clang-tools-extra/clangd/unittests/RenameTests.cpp index 7d9252110b27d..6b0f1813d7ab5 100644 --- a/clang-tools-extra/clangd/unittests/RenameTests.cpp +++ b/clang-tools-extra/clangd/unittests/RenameTests.cpp @@ -1509,6 +1509,7 @@ TEST(RenameTest, PrepareRename) { /*NewName=*/std::nullopt, {}); // Verify that for multi-file rename, we only return main-file occurrences. ASSERT_TRUE(bool(Results)) << Results.takeError(); + ASSERT_EQ(Results->Placeholder, "func"); // We don't know the result is complete in prepareRename (passing a nullptr // index internally), so GlobalChanges should be empty. EXPECT_TRUE(Results->GlobalChanges.empty()); @@ -1540,6 +1541,38 @@ TEST(RenameTest, PrepareRename) { } } +TEST(RenameTest, PrepareRenameObjC) { + Annotations Input(R"cpp( + @interface Foo + - (int)performA^ction:(int)action w^ith:(int)value; + @end + @implementation Foo + - (int)performA^ction:(int)action w^ith:(int)value { + return [self ^performAction^:action ^w^ith^:value]; + } + @end + )cpp"); + std::string Path = testPath("foo.m"); + MockFS FS; + FS.Files[Path] = std::string(Input.code()); + + auto ServerOpts = ClangdServer::optsForTest(); + ServerOpts.BuildDynamicSymbolIndex = true; + + trace::TestTracer Tracer; + MockCompilationDatabase CDB; + CDB.ExtraClangFlags = {"-xobjective-c"}; + ClangdServer Server(CDB, FS, ServerOpts); + runAddDocument(Server, Path, Input.code()); + + for (Position Point : Input.points()) { + auto Results = runPrepareRename(Server, Path, Point, + /*NewName=*/std::nullopt, {}); + ASSERT_TRUE(bool(Results)) << Results.takeError(); + ASSERT_EQ(Results->Placeholder, "performAction:with:"); + } +} + TEST(CrossFileRenameTests, DirtyBuffer) { Annotations FooCode("class [[Foo]] {};"); std::string FooPath = testPath("foo.cc"); @@ -2046,8 +2079,9 @@ TEST(CrossFileRenameTests, ObjC) { } @end )cpp", - }}; - + } + }; + trace::TestTracer Tracer; for (const auto &T : Cases) { SCOPED_TRACE(T.FooH); @@ -2178,9 +2212,9 @@ TEST(CrossFileRenameTests, adjustRenameRanges) { for (const auto &T : Tests) { SCOPED_TRACE(T.DraftCode); Annotations Draft(T.DraftCode); - auto ActualRanges = adjustRenameRanges(Draft.code(), "x", - Annotations(T.IndexedCode).ranges(), - LangOpts, std::nullopt); + auto ActualRanges = adjustRenameRanges( + Draft.code(), tooling::SymbolName("x", /*IsObjectiveCSelector=*/false), + Annotations(T.IndexedCode).ranges(), LangOpts); if (!ActualRanges) EXPECT_THAT(Draft.ranges(), testing::IsEmpty()); else @@ -2423,6 +2457,130 @@ TEST(CrossFileRenameTests, adjustmentCost) { } } +static URIForFile uriForPath(StringRef Path) { + URI Uri = llvm::cantFail(URI::parse(("file://" + Path).str())); + return llvm::cantFail(URIForFile::fromURI(Uri, /*HintPath=*/"")); +} + +TEST(IndexedRename, IndexedRename) { + MockCompilationDatabase CDB; + CDB.ExtraClangFlags = {"-xobjective-c"}; + // rename is runnning on all "^" points in FooH, and "[[]]" ranges are the + // expected rename occurrences. + struct Case { + llvm::StringRef FooH; + llvm::StringRef FooM; + llvm::StringRef OldName; + llvm::StringRef NewName; + llvm::StringRef ExpectedFooH; + llvm::StringRef ExpectedFooM; + }; + Case Cases[] = { + { + // Input + R"cpp( + void ^foo(); + )cpp", + R"cpp( + void ^foo() { + return ^foo(); + } + )cpp", + // Old name + "foo", + // New name + "bar", + // Expected + R"cpp( + void bar(); + )cpp", + R"cpp( + void bar() { + return bar(); + } + )cpp", + }, + { + // Input + R"cpp( + @interface Foo + - (int)^performAction:(int)action with:(int)value; + @end + )cpp", + R"cpp( + @implementation Foo + - (int)^performAction:(int)action with:(int)value { + [self ^performAction:action with:value]; + } + @end + )cpp", + // Old name + "performAction:with:", + // New name + "performNewAction:by:", + // Expected + R"cpp( + @interface Foo + - (int)performNewAction:(int)action by:(int)value; + @end + )cpp", + R"cpp( + @implementation Foo + - (int)performNewAction:(int)action by:(int)value { + [self performNewAction:action by:value]; + } + @end + )cpp", + } + }; + trace::TestTracer Tracer; + for (const auto &T : Cases) { + SCOPED_TRACE(T.FooH); + Annotations FooH(T.FooH); + Annotations FooM(T.FooM); + std::string FooHPath = testPath("foo.h"); + std::string FooMPath = testPath("foo.m"); + + MockFS FS; + FS.Files[FooHPath] = std::string(FooH.code()); + FS.Files[FooMPath] = std::string(FooM.code()); + + auto ServerOpts = ClangdServer::optsForTest(); + ClangdServer Server(CDB, FS, ServerOpts); + + std::map<URIForFile, std::vector<Position>> Positions; + Positions[uriForPath(FooHPath)] = FooH.points(); + Positions[uriForPath(FooMPath)] = FooM.points(); + FileEdits Edits = llvm::cantFail( + runIndexedRename(Server, Positions, FooHPath, T.OldName, T.NewName)); + + EXPECT_THAT(applyEdits(std::move(Edits)), + UnorderedElementsAre(Pair(Eq(FooHPath), Eq(T.ExpectedFooH)), + Pair(Eq(FooMPath), Eq(T.ExpectedFooM)))); + } +} + +TEST(IndexedRename, IndexedRenameDoesntCrashIfNoCompilerCommandsExistForFile) { + Annotations FooM(R"cpp( + void ^foo(); + )cpp"); + std::string Path = testPath("foo.swift"); + + MockFS FS; + FS.Files[Path] = std::string(FooM.code()); + + auto ServerOpts = ClangdServer::optsForTest(); + MockCompilationDatabase CDB; + ClangdServer Server(CDB, FS, ServerOpts); + + std::map<URIForFile, std::vector<Position>> Positions; + Positions[uriForPath(Path)] = FooM.points(); + llvm::Expected<FileEdits> Edits = + runIndexedRename(Server, Positions, Path, "cFunc", "dFunc"); + EXPECT_FALSE((bool)Edits); + consumeError(Edits.takeError()); +} + } // namespace } // namespace clangd } // namespace clang diff --git a/clang-tools-extra/clangd/unittests/SyncAPI.cpp b/clang-tools-extra/clangd/unittests/SyncAPI.cpp index d48622eba5378..fe92958780b30 100644 --- a/clang-tools-extra/clangd/unittests/SyncAPI.cpp +++ b/clang-tools-extra/clangd/unittests/SyncAPI.cpp @@ -106,6 +106,15 @@ llvm::Expected<RenameResult> runRename(ClangdServer &Server, PathRef File, return std::move(*Result); } +llvm::Expected<FileEdits> runIndexedRename( + ClangdServer &Server, std::map<URIForFile, std::vector<Position>> Positions, + PathRef PrimaryFile, llvm::StringRef OldName, llvm::StringRef NewName) { + std::optional<llvm::Expected<FileEdits>> Result; + Server.indexedRename(Positions, PrimaryFile, OldName, NewName, + capture(Result)); + return std::move(*Result); +} + llvm::Expected<RenameResult> runPrepareRename(ClangdServer &Server, PathRef File, Position Pos, std::optional<std::string> NewName, diff --git a/clang-tools-extra/clangd/unittests/SyncAPI.h b/clang-tools-extra/clangd/unittests/SyncAPI.h index cf3de4f742e84..7a48ad06f511a 100644 --- a/clang-tools-extra/clangd/unittests/SyncAPI.h +++ b/clang-tools-extra/clangd/unittests/SyncAPI.h @@ -47,6 +47,10 @@ llvm::Expected<RenameResult> runRename(ClangdServer &Server, PathRef File, Position Pos, StringRef NewName, const clangd::RenameOptions &RenameOpts); +llvm::Expected<FileEdits> runIndexedRename( + ClangdServer &Server, std::map<URIForFile, std::vector<Position>> Positions, + PathRef PrimaryFile, llvm::StringRef OldName, llvm::StringRef NewName); + llvm::Expected<RenameResult> runPrepareRename(ClangdServer &Server, PathRef File, Position Pos, std::optional<std::string> NewName, diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index 083b098d05d4a..ebcdeca8c2ee5 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -61,6 +61,8 @@ Diagnostics Semantic Highlighting ^^^^^^^^^^^^^^^^^^^^^ +- Improved semantic token coverage in some edge cases, e.g. IndirectFieldDecl + Compile flags ^^^^^^^^^^^^^ @@ -70,24 +72,57 @@ Hover Code completion ^^^^^^^^^^^^^^^ +- ``--function-arg-placeholders=0`` is now respected for variable template argument lists + as well +- Macro proposals now use the completion item kind ``Constant`` (for object-like macros) + or ``Function`` (for function-style macros) even for proposals coming from the index + Code actions ^^^^^^^^^^^^ +- The "extract variable" tweak is no longer offered for the initializer expression of a + declaration - The tweak for turning unscoped into scoped enums now removes redundant prefixes from the enum values. +- Support "move function body out-of-line" in non-header files as well Signature help ^^^^^^^^^^^^^^ +- Signature help now shows function argument names for calls through pointers to + functions in struct fields + Cross-references ^^^^^^^^^^^^^^^^ +- Improve go-to-definition for some concept references + +Document outline +^^^^^^^^^^^^^^^^ + +- Improved precision of document outline information for symbols whose definitions + involve macro expansions + +Clang-tidy integration +^^^^^^^^^^^^^^^^^^^^^^ + +- The quick fix for clang-tidy's ``readability-identifier-naming`` diagnostic is now + hooked to invoke ``textDocument/rename``, renaming the identifier across the whole + project rather than just the translation unit of the diagnostic +- ``misc-const-correctness`` can now be enabled with ``FastCheckFilter: None`` + (previously clangd would force it off unconditionally due to its run time) + Objective-C ^^^^^^^^^^^ +- Added support for renaming Objective-C methods + Miscellaneous ^^^^^^^^^^^^^ +- Worked around a clang-format bug that caused memory exhaustion when opening some large + ``.h`` files due to the formatter's language guessing heuristic (#GH85703) +- Various other stability improvements, e.g. crash fixes - Added a boolean option `AnalyzeAngledIncludes` to `Includes` config section, which allows to enable unused includes detection for all angled ("system") headers. At this moment umbrella headers are not supported, so enabling this option @@ -496,6 +531,10 @@ Changes in existing checks ``static_cast``. Fixed false positives in C++20 spaceship operator by ignoring casts in implicit and defaulted functions. +- Improved :doc:`readability-non-const-parameter + <clang-tidy/checks/readability/non-const-parameter>` check to not crash when + redeclaration have fewer parameters than expected. + - Improved :doc:`readability-redundant-inline-specifier <clang-tidy/checks/readability/redundant-inline-specifier>` check to properly emit warnings for static data member with an in-class initializer. diff --git a/clang-tools-extra/modularize/CoverageChecker.cpp b/clang-tools-extra/modularize/CoverageChecker.cpp index 0e76c539aa3c8..b536ee00497c0 100644 --- a/clang-tools-extra/modularize/CoverageChecker.cpp +++ b/clang-tools-extra/modularize/CoverageChecker.cpp @@ -223,10 +223,9 @@ bool CoverageChecker::collectModuleHeaders(const Module &Mod) { return false; } - for (auto &HeaderKind : Mod.Headers) - for (auto &Header : HeaderKind) - ModuleMapHeadersSet.insert( - ModularizeUtilities::getCanonicalPath(Header.Entry.getName())); + for (const auto &Header : Mod.getAllHeaders()) + ModuleMapHeadersSet.insert( + ModularizeUtilities::getCanonicalPath(Header.Entry.getName())); for (auto *Submodule : Mod.submodules()) collectModuleHeaders(*Submodule); diff --git a/clang-tools-extra/modularize/ModularizeUtilities.cpp b/clang-tools-extra/modularize/ModularizeUtilities.cpp index b202b3aae8f8a..476e13770a94f 100644 --- a/clang-tools-extra/modularize/ModularizeUtilities.cpp +++ b/clang-tools-extra/modularize/ModularizeUtilities.cpp @@ -358,7 +358,7 @@ bool ModularizeUtilities::collectModuleHeaders(const clang::Module &Mod) { } else if (std::optional<clang::Module::DirectoryName> UmbrellaDir = Mod.getUmbrellaDirAsWritten()) { // If there normal headers, assume these are umbrellas and skip collection. - if (Mod.Headers->size() == 0) { + if (Mod.getHeaders(Module::HK_Normal).empty()) { // Collect headers in umbrella directory. if (!collectUmbrellaHeaders(UmbrellaDir->Entry.getName(), UmbrellaDependents)) @@ -371,16 +371,8 @@ bool ModularizeUtilities::collectModuleHeaders(const clang::Module &Mod) { // modules or because they are meant to be included by another header, // and thus should be ignored by modularize. - int NormalHeaderCount = Mod.Headers[clang::Module::HK_Normal].size(); - - for (int Index = 0; Index < NormalHeaderCount; ++Index) { - DependentsVector NormalDependents; - // Collect normal header. - const clang::Module::Header &Header( - Mod.Headers[clang::Module::HK_Normal][Index]); - std::string HeaderPath = getCanonicalPath(Header.Entry.getName()); - HeaderFileNames.push_back(HeaderPath); - } + for (const auto &Header : Mod.getHeaders(clang::Module::HK_Normal)) + HeaderFileNames.push_back(getCanonicalPath(Header.Entry.getName())); int MissingCountThisModule = Mod.MissingHeaders.size(); diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/Inputs/use-ranges/fake_std.h b/clang-tools-extra/test/clang-tidy/checkers/modernize/Inputs/use-ranges/fake_std.h index 6596511c7a38b..69ac9954f4afa 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/modernize/Inputs/use-ranges/fake_std.h +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/Inputs/use-ranges/fake_std.h @@ -7,8 +7,8 @@ template <typename T> class vector { public: using iterator = T *; using const_iterator = const T *; - using reverse_iterator = T*; - using reverse_const_iterator = const T*; + using reverse_iterator = T *; + using reverse_const_iterator = const T *; constexpr const_iterator begin() const; constexpr const_iterator end() const; @@ -72,8 +72,8 @@ template <typename Container> constexpr auto crend(const Container &Cont) { return Cont.crend(); } // Find -template< class InputIt, class T > -InputIt find( InputIt first, InputIt last, const T& value ); +template <class InputIt, class T> +InputIt find(InputIt first, InputIt last, const T &value); // Reverse template <typename Iter> void reverse(Iter begin, Iter end); @@ -82,6 +82,7 @@ template <typename Iter> void reverse(Iter begin, Iter end); template <class InputIt1, class InputIt2> bool includes(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2); +inline namespace _V1 { // IsPermutation template <class ForwardIt1, class ForwardIt2> bool is_permutation(ForwardIt1 first1, ForwardIt1 last1, ForwardIt2 first2); @@ -97,9 +98,10 @@ template <class InputIt1, class InputIt2> bool equal(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2); template <class InputIt1, class InputIt2, class BinaryPred> -bool equal(InputIt1 first1, InputIt1 last1, - InputIt2 first2, InputIt2 last2, BinaryPred p) { - // Need a definition to suppress undefined_internal_type when invoked with lambda +bool equal(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2, + BinaryPred p) { + // Need a definition to suppress undefined_internal_type when invoked with + // lambda return true; } @@ -108,6 +110,7 @@ void iota(ForwardIt first, ForwardIt last, T value); template <class ForwardIt> ForwardIt rotate(ForwardIt first, ForwardIt middle, ForwardIt last); +} // namespace _V1 } // namespace std diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/non-const-parameter.c b/clang-tools-extra/test/clang-tidy/checkers/readability/non-const-parameter.c new file mode 100644 index 0000000000000..db50467f3dd94 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/readability/non-const-parameter.c @@ -0,0 +1,11 @@ +// RUN: %check_clang_tidy %s readability-non-const-parameter %t + +static int f(); + +int f(p) + int *p; +// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: pointer parameter 'p' can be pointer to const [readability-non-const-parameter] +// CHECK-FIXES: {{^}} const int *p;{{$}} +{ + return *p; +} diff --git a/clang/CONTRIBUTING.md b/clang/CONTRIBUTING.md new file mode 100644 index 0000000000000..a0c1644fc66a2 --- /dev/null +++ b/clang/CONTRIBUTING.md @@ -0,0 +1,14 @@ +By submitting a pull request, you represent that you have the right to license +your contribution to Apple and the community, and agree by submitting the patch +that your contributions are licensed under the [Swift +license](https://swift.org/LICENSE.txt). + +--- + +Changes to this repository follow special considerations as described on +Swift.org under "[LLVM and Swift](https://swift.org/contributing/#llvm-and-swift)". +Please make sure your change is appropriate for this repository. + +Before submitting a pull request, please make sure you have tested your +changes and that they follow the Swift project [guidelines for contributing +code](https://swift.org/contributing/#contributing-code). diff --git a/clang/cmake/caches/Apple-stage2.cmake b/clang/cmake/caches/Apple-stage2.cmake index e919c56739679..2e1ccec785aa2 100644 --- a/clang/cmake/caches/Apple-stage2.cmake +++ b/clang/cmake/caches/Apple-stage2.cmake @@ -56,6 +56,8 @@ set(LLVM_TOOLCHAIN_TOOLS llvm-size llvm-cxxfilt llvm-config + llvm-cas + llvm-cas-dump CACHE STRING "") set(LLVM_BUILD_UTILS ON CACHE BOOL "") @@ -73,6 +75,7 @@ set(LLVM_DISTRIBUTION_COMPONENTS clang-format clang-resource-headers Remarks + clang-features-file ${LLVM_TOOLCHAIN_TOOLS} ${LLVM_TOOLCHAIN_UTILITIES} CACHE STRING "") diff --git a/clang/cmake/caches/Release.cmake b/clang/cmake/caches/Release.cmake index 9e6feb479d45f..c4947bf453872 100644 --- a/clang/cmake/caches/Release.cmake +++ b/clang/cmake/caches/Release.cmake @@ -29,9 +29,13 @@ endfunction() # cache file to CMake via -C. e.g. # # cmake -D LLVM_RELEASE_ENABLE_PGO=ON -C Release.cmake +set (DEFAULT_RUNTIMES "compiler-rt;libcxx") +if (NOT WIN32) + list(APPEND DEFAULT_RUNTIMES "libcxxabi" "libunwind") +endif() set(LLVM_RELEASE_ENABLE_LTO THIN CACHE STRING "") set(LLVM_RELEASE_ENABLE_PGO ON CACHE BOOL "") -set(LLVM_RELEASE_ENABLE_RUNTIMES "compiler-rt;libcxx;libcxxabi;libunwind" CACHE STRING "") +set(LLVM_RELEASE_ENABLE_RUNTIMES ${DEFAULT_RUNTIMES} CACHE STRING "") set(LLVM_RELEASE_ENABLE_PROJECTS "clang;lld;lldb;clang-tools-extra;bolt;polly;mlir;flang" CACHE STRING "") # Note we don't need to add install here, since it is one of the pre-defined # steps. @@ -43,11 +47,14 @@ set(LLVM_TARGETS_TO_BUILD Native CACHE STRING "") set(CLANG_ENABLE_BOOTSTRAP ON CACHE BOOL "") set(STAGE1_PROJECTS "clang") -set(STAGE1_RUNTIMES "") + +# Building Flang on Windows requires compiler-rt, so we need to build it in +# stage1. compiler-rt is also required for building the Flang tests on +# macOS. +set(STAGE1_RUNTIMES "compiler-rt") if (LLVM_RELEASE_ENABLE_PGO) list(APPEND STAGE1_PROJECTS "lld") - list(APPEND STAGE1_RUNTIMES "compiler-rt") set(CLANG_BOOTSTRAP_TARGETS generate-profdata stage2-package @@ -94,3 +101,6 @@ set_final_stage_var(LLVM_ENABLE_PROJECTS "${LLVM_RELEASE_ENABLE_PROJECTS}" STRIN set_final_stage_var(CPACK_GENERATOR "TXZ" STRING) set_final_stage_var(CPACK_ARCHIVE_THREADS "0" STRING) +if(${CMAKE_HOST_SYSTEM_NAME} MATCHES "Darwin") + set_final_stage_var(LLVM_USE_STATIC_ZSTD "ON" BOOL) +endif() diff --git a/clang/docs/APINotes.rst b/clang/docs/APINotes.rst index bc09b16bab5d2..dcefa6810dac6 100644 --- a/clang/docs/APINotes.rst +++ b/clang/docs/APINotes.rst @@ -188,6 +188,18 @@ declaration kind), all of which are optional: - Name: tzdb SwiftCopyable: false +:SwiftConformsTo: + + Allows annotating a C++ class as conforming to a Swift protocol. Equivalent + to ``SWIFT_CONFORMS_TO_PROTOCOL``. The value is a module-qualified name of a + Swift protocol. + + :: + + Tags: + - Name: vector + SwiftConformsTo: Cxx.CxxSequence + :Availability, AvailabilityMsg: A value of "nonswift" is equivalent to ``NS_SWIFT_UNAVAILABLE``. A value of diff --git a/clang/docs/CommandGuide/clang.rst b/clang/docs/CommandGuide/clang.rst index 663aca1f6ddcb..a0c2594d06c61 100644 --- a/clang/docs/CommandGuide/clang.rst +++ b/clang/docs/CommandGuide/clang.rst @@ -429,8 +429,12 @@ Code Generation Options :option:`-Ofast` Enables all the optimizations from :option:`-O3` along with other aggressive optimizations that may violate strict compliance with - language standards. This is deprecated in favor of :option:`-O3` - in combination with :option:`-ffast-math`. + language standards. This is deprecated in Clang 19 and a warning is emitted + that :option:`-O3` in combination with :option:`-ffast-math` should be used + instead if the request for non-standard math behavior is intended. There + is no timeline yet for removal; the aim is to discourage use of + :option:`-Ofast` due to the surprising behavior of an optimization flag + changing the observable behavior of correct code. :option:`-Os` Like :option:`-O2` with extra optimizations to reduce code size. diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst index 81784c75081ba..1c4a6ecca2142 100644 --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -1503,6 +1503,7 @@ Conditional ``explicit`` __cpp_conditional_explicit C+ ``static operator()`` __cpp_static_call_operator C++23 C++03 Attributes on Lambda-Expressions C++23 C++11 Attributes on Structured Bindings __cpp_structured_bindings C++26 C++03 +Pack Indexing __cpp_pack_indexing C++26 C++03 ``= delete ("should have a reason");`` __cpp_deleted_function C++26 C++03 -------------------------------------------- -------------------------------- ------------- ------------- Designated initializers (N494) C99 C89 diff --git a/clang/docs/PointerAuthentication.rst b/clang/docs/PointerAuthentication.rst index 68674f318c84f..6e381c0dc820e 100644 --- a/clang/docs/PointerAuthentication.rst +++ b/clang/docs/PointerAuthentication.rst @@ -47,14 +47,13 @@ This document serves four purposes: - It documents several language extensions that are useful on targets using pointer authentication. -- It will eventually present a theory of operation for the security mitigation, - describing the basic requirements for correctness, various weaknesses in the - mechanism, and ways in which programmers can strengthen its protections - (including recommendations for language implementors). +- It presents a theory of operation for the security mitigation, describing the + basic requirements for correctness, various weaknesses in the mechanism, and + ways in which programmers can strengthen its protections (including + recommendations for language implementors). -- It will eventually document the language ABIs currently used for C, C++, - Objective-C, and Swift on arm64e, although these are not yet stable on any - target. +- It documents the language ABIs currently used for C, C++, Objective-C, and + Swift on arm64e, although these are not yet stable on any target. Basic Concepts -------------- @@ -125,7 +124,7 @@ independently for I and D keys.) interfaces or as primitives in a compiler IR because they expose raw pointers. Raw pointers require special attention in the language implementation to avoid the accidental creation of exploitable code - sequences. + sequences; see the section on `Attackable code sequences`_. The following details are all implementation-defined: @@ -255,19 +254,133 @@ signing schema breaks down even more simply: It is important that the signing schema be independently derived at all signing and authentication sites. Preferably, the schema should be hard-coded everywhere it is needed, but at the very least, it must not be derived by -inspecting information stored along with the pointer. +inspecting information stored along with the pointer. See the section on +`Attacks on pointer authentication`_ for more information. + + + + Language Features ----------------- -There is currently one main pointer authentication language feature: +There are three levels of the pointer authentication language feature: + +- The language implementation automatically signs and authenticates function + pointers (and certain data pointers) across a variety of standard situations, + including return addresses, function pointers, and C++ virtual functions. The + intent is for all pointers to code in program memory to be signed in some way + and for all branches to code in program text to authenticate those + signatures. -- The language provides the ``<ptrauth.h>`` intrinsic interface for manually - signing and authenticating pointers in code. These can be used in +- The language also provides extensions to override the default rules used by + the language implementation. For example, the ``__ptrauth`` type qualifier + can be used to change how pointers are signed when they are stored in + a particular variable or field; this provides much stronger protection than + is guaranteed by the default rules for C function and data pointers. + +- Finally, the language provides the ``<ptrauth.h>`` intrinsic interface for + manually signing and authenticating pointers in code. These can be used in circumstances where very specific behavior is required. +Language implementation +~~~~~~~~~~~~~~~~~~~~~~~ + +For the most part, pointer authentication is an unobserved detail of the +implementation of the programming language. Any element of the language +implementation that would perform an indirect branch to a pointer is implicitly +altered so that the pointer is signed when first constructed and authenticated +when the branch is performed. This includes: + +- indirect-call features in the programming language, such as C function + pointers, C++ virtual functions, C++ member function pointers, the "blocks" + C extension, and so on; + +- returning from a function, no matter how it is called; and + +- indirect calls introduced by the implementation, such as branches through the + global offset table (GOT) used to implement direct calls to functions defined + outside of the current shared object. + +For more information about this, see the `Language ABI`_ section. + +However, some aspects of the implementation are observable by the programmer or +otherwise require special notice. + +C data pointers +^^^^^^^^^^^^^^^ + +The current implementation in Clang does not sign pointers to ordinary data by +default. For a partial explanation of the reasoning behind this, see the +`Theory of Operation`_ section. + +A specific data pointer which is more security-sensitive than most can be +signed using the `__ptrauth qualifier`_ or using the ``<ptrauth.h>`` +intrinsics. + +C function pointers +^^^^^^^^^^^^^^^^^^^ + +The C standard imposes restrictions on the representation and semantics of +function pointer types which make it difficult to achieve satisfactory +signature diversity in the default language rules. See `Attacks on pointer +authentication`_ for more information about signature diversity. Programmers +should strongly consider using the ``__ptrauth`` qualifier to improve the +protections for important function pointers, such as the components of of +a hand-rolled "v-table"; see the section on the `__ptrauth qualifier`_ for +details. + +The value of a pointer to a C function includes a signature, even when the +value is cast to a non-function-pointer type like ``void*`` or ``intptr_t``. On +implementations that use high bits to store the signature, this means that +relational comparisons and hashes will vary according to the exact signature +value, which is likely to change between executions of a program. In some +implementations, it may also vary based on the exact function pointer type. + +Null pointers +^^^^^^^^^^^^^ + +In principle, an implementation could derive the signed null pointer value +simply by applying the standard signing algorithm to the raw null pointer +value. However, for likely signing algorithms, this would mean that the signed +null pointer value would no longer be statically known, which would have many +negative consequences. For one, it would become substantially more expensive +to emit null pointer values or to perform null-pointer checks. For another, +the pervasive (even if technically unportable) assumption that null pointers +are bitwise zero would be invalidated, making it substantially more difficult +to adopt pointer authentication, as well as weakening common optimizations for +zero-initialized memory such as the use of ``.bzz`` sections. Therefore it is +beneficial to treat null pointers specially by giving them their usual +representation. On AArch64, this requires additional code when working with +possibly-null pointers, such as when copying a pointer field that has been +signed with address diversity. + +Return addresses and frame pointers +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The current implementation in Clang implicitly signs both return addresses and +frame pointers. While these values are technically implementation details of +a function, there are some important libraries and development tools which rely +on manually walking the chain of stack frames. These tools must be updated to +correctly account for pointer authentication, either by stripping signatures +(if security is not important for the tool, e.g. if it is capturing a stack +trace during a crash) or properly authenticating them. More information about +how these values are signed is available in the `Language ABI`_ section. + +C++ virtual functions +^^^^^^^^^^^^^^^^^^^^^ + +The current implementation in Clang signs virtual function pointers with +a discriminator derived from the full signature of the overridden method, +including the method name and parameter types. It is possible to write C++ +code that relies on v-table layout remaining constant despite changes to +a method signature; for example, a parameter might be a ``typedef`` that +resolves to a different type based on a build setting. Such code violates +C++'s One Definition Rule (ODR), but that violation is not normally detected; +however, pointer authentication will detect it. + -Language Extensions +Language extensions ~~~~~~~~~~~~~~~~~~~ Feature Testing @@ -280,6 +393,115 @@ a number of different tests. normal interface. This may be true even on targets where pointer authentication is not enabled by default. +- ``__has_feature(ptrauth_returns)`` is true if the target uses pointer + authentication to protect return addresses. + +- ``__has_feature(ptrauth_calls)`` is true if the target uses pointer + authentication to protect indirect branches. This implies + ``__has_feature(ptrauth_returns)`` and ``__has_feature(ptrauth_intrinsics)``. + +Clang provides several other tests only for historical purposes; for current +purposes they are all equivalent to ``ptrauth_calls``. + +__ptrauth qualifier +^^^^^^^^^^^^^^^^^^^ + +``__ptrauth(key, address, discriminator)`` is an extended type qualifier which +causes so-qualified objects to hold pointers signed using the specified schema +rather than the default schema for such types. + +In the current implementation in Clang, the qualified type must be a C pointer +type, either to a function or to an object. It currently cannot be an +Objective-C pointer type, a C++ reference type, or a block pointer type; these +restrictions may be lifted in the future. + +The current implementation in Clang is known to not provide adequate safety +guarantees against the creation of `signing oracles`_ when assigning data +pointers to ``__ptrauth``-qualified gl-values. See the section on `safe +derivation`_ for more information. + +The qualifier's operands are as follows: + +- ``key`` - an expression evaluating to a key value from ``<ptrauth.h>``; must + be a constant expression + +- ``address`` - whether to use address diversity (1) or not (0); must be + a constant expression with one of these two values + +- ``discriminator`` - a constant discriminator; must be a constant expression + +See `Discriminators`_ for more information about discriminators. + +Currently the operands must be constant-evaluable even within templates. In the +future this restriction may be lifted to allow value-dependent expressions as +long as they instantiate to a constant expression. + +Consistent with the ordinary C/C++ rule for parameters, top-level ``__ptrauth`` +qualifiers on a parameter (after parameter type adjustment) are ignored when +deriving the type of the function. The parameter will be passed using the +default ABI for the unqualified pointer type. + +If ``x`` is an object of type ``__ptrauth(key, address, discriminator) T``, +then the signing schema of the value stored in ``x`` is a key of ``key`` and +a discriminator determined as follows: + +- if ``address`` is 0, then the discriminator is ``discriminator``; + +- if ``address`` is 1 and ``discriminator`` is 0, then the discriminator is + ``&x``; otherwise + +- if ``address`` is 1 and ``discriminator`` is non-zero, then the discriminator + is ``ptrauth_blend_discriminator(&x, discriminator)``; see + `ptrauth_blend_discriminator`_. + +Non-triviality from address diversity ++++++++++++++++++++++++++++++++++++++ + +Address diversity must impose additional restrictions in order to allow the +implementation to correctly copy values. In C++, a type qualified with address +diversity is treated like a class type with non-trivial copy/move constructors +and assignment operators, with the usual effect on containing classes and +unions. C does not have a standard concept of non-triviality, and so we must +describe the basic rules here, with the intention of imitating the emergent +rules of C++: + +- A type may be **non-trivial to copy**. + +- A type may also be **illegal to copy**. Types that are illegal to copy are + always non-trivial to copy. + +- A type may also be **address-sensitive**. + +- A type qualified with a ``ptrauth`` qualifier that requires address diversity + is non-trivial to copy and address-sensitive. + +- An array type is illegal to copy, non-trivial to copy, or address-sensitive + if its element type is illegal to copy, non-trivial to copy, or + address-sensitive, respectively. + +- A struct type is illegal to copy, non-trivial to copy, or address-sensitive + if it has a field whose type is illegal to copy, non-trivial to copy, or + address-sensitive, respectively. + +- A union type is both illegal and non-trivial to copy if it has a field whose + type is non-trivial or illegal to copy. + +- A union type is address-sensitive if it has a field whose type is + address-sensitive. + +- A program is ill-formed if it uses a type that is illegal to copy as + a function parameter, argument, or return type. + +- A program is ill-formed if an expression requires a type to be copied that is + illegal to copy. + +- Otherwise, copying a type that is non-trivial to copy correctly copies its + subobjects. + +- Types that are address-sensitive must always be passed and returned + indirectly. Thus, changing the address-sensitivity of a type may be + ABI-breaking even if its size and alignment do not change. + ``<ptrauth.h>`` ~~~~~~~~~~~~~~~ @@ -386,7 +608,7 @@ Produce a signed pointer for the given raw pointer without applying any authentication or extra treatment. This operation is not required to have the same behavior on a null pointer that the language implementation would. -This is a treacherous operation that can easily result in signing oracles. +This is a treacherous operation that can easily result in `signing oracles`_. Programs should use it seldom and carefully. ``ptrauth_auth_and_resign`` @@ -407,7 +629,29 @@ a null pointer that the language implementation would. The code sequence produced for this operation must not be directly attackable. However, if the discriminator values are not constant integers, their computations may still be attackable. In the future, Clang should be enhanced -to guaranteed non-attackability if these expressions are safely-derived. +to guaranteed non-attackability if these expressions are +:ref:`safely-derived<Safe derivation>`. + +``ptrauth_auth_function`` +^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: c + + ptrauth_auth_function(pointer, key, discriminator) + +Authenticate that ``pointer`` is signed with ``key`` and ``discriminator`` and +re-sign it to the standard schema for a function pointer of its type. + +``pointer`` must have function pointer type. The result will have the same +type as ``pointer``. This operation is not required to have the same behavior +on a null pointer that the language implementation would. + +This operation makes the same attackability guarantees as +``ptrauth_auth_and_resign``. + +If this operation appears syntactically as the function operand of a call, +Clang guarantees that the call will directly authenticate the function value +using the given schema rather than re-signing to the standard schema. ``ptrauth_auth_data`` ^^^^^^^^^^^^^^^^^^^^^ @@ -423,7 +667,7 @@ remove the signature. as ``pointer``. This operation is not required to have the same behavior on a null pointer that the language implementation would. -In the future when Clang makes safe derivation guarantees, the result of +In the future when Clang makes `safe derivation`_ guarantees, the result of this operation should be considered safely-derived. ``ptrauth_sign_generic_data`` @@ -453,6 +697,466 @@ type. Implementations are not required to make all bits of the result equally significant; in particular, some implementations are known to not leave meaningful data in the low bits. +Standard ``__ptrauth`` qualifiers +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +``<ptrauth.h>`` additionally provides several macros which expand to +``__ptrauth`` qualifiers for common ABI situations. + +For convenience, these macros expand to nothing when pointer authentication is +disabled. + +These macros can be found in the header; some details of these macros may be +unstable or implementation-specific. + + + + + +Theory of Operation +------------------- + +The threat model of pointer authentication is as follows: + +- The attacker has the ability to read and write to a certain range of addresses, possibly the entire address space. However, they are constrained by the normal rules of the process: for example, they cannot write to memory that is mapped read-only, and if they access unmapped memory it will trigger a trap. + +- The attacker has no ability to add arbitrary executable code to the program. For example, the program does not include malicious code to begin with, and the attacker cannot alter existing instructions, load a malicious shared library, or remap writable pages as executable. If the attacker wants to get the process to perform a specific sequence of actions, they must somehow subvert the normal control flow of the process. + +In both of the above paragraphs, it is merely assumed that the attacker's *current* capabilities are restricted; that is, their current exploit does not directly give them the power to do these things. The attacker's immediate goal may well be to leverage their exploit to gain these capabilities, e.g. to load a malicious dynamic library into the process, even though the process does not directly contain code to do so. + +Note that any bug that fits the above threat model can be immediately exploited as a denial-of-service attack by simply performing an illegal access and crashing the program. Pointer authentication cannot protect against this. While denial-of-service attacks are unfortunate, they are also unquestionably the best possible result of a bug this severe. Therefore, pointer authentication enthusiastically embraces the idea of halting the program on a pointer authentication failure rather than continuing in a possibly-compromised state. + +Pointer authentication is a form of control-flow integrity (CFI) enforcement. The basic security hypothesis behind CFI enforcement is that many bugs can only be usefully exploited (other than as a denial-of-service) by leveraging them to subvert the control flow of the program. If this is true, then by inhibiting or limiting that subversion, it may be possible to largely mitigate the security consequences of those bugs by rendering them impractical (or, ideally, impossible) to exploit. + +Every indirect branch in a program has a purpose. Using human intelligence, a programmer can describe where a particular branch *should* go according to this purpose: a ``return`` in ``printf`` should return to the call site, a particular call in ``qsort`` should call the comparator that was passed in as an argument, and so on. But for CFI to enforce that every branch in a program goes where it *should* in this sense would require CFI to perfectly enforce every semantic rule of the program's abstract machine; that is, it would require making the programming environment perfectly sound. That is out of scope. Instead, the goal of CFI is merely to catch attempts to make a branch go somewhere that its obviously *shouldn't* for its purpose: for example, to stop a call from branching into the middle of a function rather than its beginning. As the information available to CFI gets better about the purpose of the branch, CFI can enforce tighter and tighter restrictions on where the branch is permitted to go. Still, ultimately CFI cannot make the program sound. This may help explain why pointer authentication makes some of the choices it does: for example, to sign and authenticate mostly code pointers rather than every pointer in the program. Preventing attackers from redirecting branches is both particularly important and particularly approachable as a goal. Detecting corruption more broadly is infeasible with these techniques, and the attempt would have far higher cost. + +Attacks on pointer authentication +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Pointer authentication works as follows. Every indirect branch in a program has a purpose. For every purpose, the implementation chooses a :ref:`signing schema<Signing schemas>`. At some place where a pointer is known to be correct for its purpose, it is signed according to the purpose's schema. At every place where the pointer is needed for its purpose, it is authenticated according to the purpose's schema. If that authentication fails, the program is halted. + +There are a variety of ways to attack this. + +Attacks of interest to programmers +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +These attacks arise from weaknesses in the default protections offered by pointer authentication. They can be addressed by using attributes or intrinsics to opt in to stronger protection. + +Substitution attacks +++++++++++++++++++++ + +An attacker can simply overwrite a pointer intended for one purpose with a pointer intended for another purpose if both purposes use the same signing schema and that schema does not use address diversity. + +The most common source of this weakness is when code relies on using the default language rules for C function pointers. The current implementation uses the exact same signing schema for all C function pointers, even for functions of substantially different type. While efforts are ongoing to improve constant diversity for C function pointers of different type, there are necessary limits to this. The C standard requires function pointers to be copyable with ``memcpy``, which means that function pointers can never use address diversity. Furthermore, even if a function pointer can only be replaced with another function of the exact same type, that can still be useful to an attacker, as in the following example of a hand-rolled "v-table": + +.. code-block:: c + + struct ObjectOperations { + void (*retain)(Object *); + void (*release)(Object *); + void (*deallocate)(Object *); + void (*logStatus)(Object *); + }; + +This weakness can be mitigated by using a more specific signing schema for each purpose. For example, in this example, the ``__ptrauth`` qualifier can be used with a different constant discriminator for each field. Since there's no particular reason it's important for this v-table to be copyable with ``memcpy``, the functions can also be signed with address diversity: + +.. code-block:: c + + #if __has_feature(ptrauth_calls) + #define objectOperation(discriminator) \ + __ptrauth(ptrauth_key_function_pointer, 1, discriminator) + #else + #define objectOperation(discriminator) + #endif + + struct ObjectOperations { + void (*objectOperation(0xf017) retain)(Object *); + void (*objectOperation(0x2639) release)(Object *); + void (*objectOperation(0x8bb0) deallocate)(Object *); + void (*objectOperation(0xc5d4) logStatus)(Object *); + }; + +This weakness can also sometimes be mitigated by simply keeping the signed pointer in constant memory, but this is less effective than using better signing diversity. + +.. _Access path attacks: + +Access path attacks ++++++++++++++++++++ + +If a signed pointer is often accessed indirectly (that is, by first loading the address of the object where the signed pointer is stored), an attacker can affect uses of it by overwriting the intermediate pointer in the access path. + +The most common scenario exhibiting this weakness is an object with a pointer to a "v-table" (a structure holding many function pointers). An attacker does not need to replace a signed function pointer in the v-table if they can instead simply replace the v-table pointer in the object with their own pointer --- perhaps to memory where they've constructed their own v-table, or to existing memory that coincidentally happens to contain a signed pointer at the right offset that's been signed with the right signing schema. + +This attack arises because data pointers are not signed by default. It works even if the signed pointer uses address diversity: address diversity merely means that each pointer is signed with its own storage address, which (by design) is invariant to changes in the accessing pointer. + +Using sufficiently diverse signing schemas within the v-table can provide reasonably strong mitigation against this weakness. Always use address diversity in v-tables to prevent attackers from assembling their own v-table. Avoid re-using constant discriminators to prevent attackers from replacing a v-table pointer with a pointer to totally unrelated memory that just happens to contain an similarly-signed pointer. + +Further mitigation can be attained by signing pointers to v-tables. Any signature at all should prevent attackers from forging v-table pointers; they will need to somehow harvest an existing signed pointer from elsewhere in memory. Using a meaningful constant discriminator will force this to be harvested from an object with similar structure (e.g. a different implementation of the same interface). Using address diversity will prevent such harvesting entirely. However, care must be taken when sourcing the v-table pointer originally; do not blindly sign a pointer that is not :ref:`safely derived<Safe derivation>`. + +.. _Signing oracles: + +Signing oracles ++++++++++++++++ + +A signing oracle is a bit of code which can be exploited by an attacker to sign an arbitrary pointer in a way that can later be recovered. Such oracles can be used by attackers to forge signatures matching the oracle's signing schema, which is likely to cause a total compromise of pointer authentication's effectiveness. + +This attack only affects ordinary programmers if they are using certain treacherous patterns of code. Currently this includes: + +- all uses of the ``__ptrauth_sign_unauthenticated`` intrinsic and +- assigning data pointers to ``__ptrauth``-qualified l-values. + +Care must be taken in these situations to ensure that the pointer being signed has been :ref:`safely derived<Safe derivation>` or is otherwise not possible to attack. (In some cases, this may be challenging without compiler support.) + +A diagnostic will be added in the future for implicitly dangerous patterns of code, such as assigning a non-safely-derived data pointer to a ``__ptrauth``-qualified l-value. + +.. _Authentication oracles: + +Authentication oracles +++++++++++++++++++++++ + +An authentication oracle is a bit of code which can be exploited by an attacker to leak whether a signed pointer is validly signed without halting the program if it isn't. Such oracles can be used to forge signatures matching the oracle's signing schema if the attacker can repeatedly invoke the oracle for different candidate signed pointers. This is likely to cause a total compromise of pointer authentication's effectiveness. + +There should be no way for an ordinary programmer to create an authentication oracle using the current set of operations. However, implementation flaws in the past have occasionally given rise to authentication oracles due to a failure to immediately trap on authentication failure. + +The likelihood of creating an authentication oracle is why there is currently no intrinsic which queries whether a signed pointer is validly signed. + + +Attacks of interest to implementors +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +These attacks are not inherent to the model; they arise from mistakes in either implementing or using the `sign` and `auth` operations. Avoiding these mistakes requires careful work throughout the system. + +Failure to trap on authentication failure ++++++++++++++++++++++++++++++++++++++++++ + +Any failure to halt the program on an authentication failure is likely to be exploitable by attackers to create an :ref:`authentication oracle<Authentication oracles>`. + +There are several different ways to introduce this problem: + +- The implementation might try to halt the program in some way that can be intercepted. + + For example, the ``auth`` instruction in ARMv8.3 does not directly trap; instead it corrupts its result so that it is always an invalid pointer. If the program subsequently attempts to use that pointer, that will be a bad memory access, and it will trap into the kernel. However, kernels do not usually immediately halt programs that trigger traps due to bad memory accesses; instead they notify the process to give it an opportunity to recover. If this happens with an ``auth`` failure, the attacker may be able to exploit the recovery path in a way that creates an oracle. Kernels should ensure that these sorts of traps are not recoverable. + +- A compiler might use an intermediate representation (IR) for ``sign`` and ``auth`` operations that cannot make adequate correctness guarantees. + + For example, suppose that an IR uses ARMv8.3-like semantics for ``auth``: the operation merely corrupts its result on failure instead of promising the trap. A frontend might emit patterns of IR that always follow an ``auth`` with a memory access, thinking that this ensures correctness. But if the IR can be transformed to insert code between the ``auth`` and the access, or if the ``auth`` can be speculated, then this potentially creates an oracle. It is better for ``auth`` to semantically guarantee to trap, potentially requiring an explicit check in the generated code. An ARMv8.3-like target can avoid this explicit check in the common case by recognizing the pattern of an ``auth`` followed immediately by an access. + +Attackable code sequences ++++++++++++++++++++++++++ + +If code that is part of a pointer authentication operation is interleaved with code that may itself be vulnerable to attacks, an attacker may be able to use this to create a :ref:`signing<Signing oracles>` or :ref:`authentication<Authentication oracles>` oracle. + +For example, suppose that the compiler is generating a call to a function and passing two arguments: a signed constant pointer and a value derived from a call. In ARMv8.3, this code might look like so: + +.. code-block:: asm + + adr x19, _callback. ; compute &_callback + paciza x19 ; sign it with a constant discriminator of 0 + blr _argGenerator ; call _argGenerator() (returns in x0) + mov x1, x0 ; move call result to second arg register + mov x0, x19 ; move signed &_callback to first arg register + blr _function ; call _function + +This code is correct, as would be a sequencing that does *both* the ``adr`` and the ``paciza`` after the call to ``_argGenerator``. But a sequence that computes the address of ``_callback`` but leaves it as a raw pointer in a register during the call to ``_argGenerator`` would be vulnerable: + +.. code-block:: asm + + adr x19, _callback. ; compute &_callback + blr _argGenerator ; call _argGenerator() (returns in x0) + mov x1, x0 ; move call result to second arg register + paciza x19 ; sign &_callback + mov x0, x19 ; move signed &_callback to first arg register + blr _function ; call _function + +If ``_argGenerator`` spills ``x19`` (a callee-save register), and if the attacker can perform a write during this call, then the attacker can overwrite the spill slot with an arbitrary pointer that will eventually be unconditionally signed after the function returns. This would be a signing oracle. + +The implementation can avoid this by obeying two basic rules: + +- The compiler's intermediate representations (IR) should not provide operations that expose intermediate raw pointers. This may require providing extra operations that perform useful combinations of operations. + + For example, there should be an "atomic" auth-and-resign operation that should be used instead of emitting an ``auth`` operation whose result is fed into a ``sign``. + + Similarly, if a pointer should be authenticated as part of doing a memory access or a call, then the access or call should be decorated with enough information to perform the authentication; there should not be a separate ``auth`` whose result is used as the pointer operand for the access or call. (In LLVM IR, we do this for calls, but not yet for loads or stores.) + + "Operations" includes things like materializing a signed pointer to a known function or global variable. The compiler must be able to recognize and emit this as a unified operation, rather than potentially splitting it up as in the example above. + +- The compiler backend should not be too aggressive about scheduling instructions that are part of a pointer authentication operation. This may require custom code-generation of these operations in some cases. + +Register clobbering ++++++++++++++++++++ + +As a refinement of the section on `Attackable code sequences`_, if the attacker has the ability to modify arbitrary *register* state at arbitrary points in the program, then special care must be taken. + +For example, ARMv8.3 might materialize a signed function pointer like so: + +.. code-block:: asm + + adr x0, _callback. ; compute &_callback + paciza x0 ; sign it with a constant discriminator of 0 + +If an attacker has the ability to overwrite ``x0`` between these two instructions, this code sequence is vulnerable to becoming a signing oracle. + +For the most part, this sort of attack is not possible: it is a basic element of the design of modern computation that register state is private and inviolable. However, in systems that support asynchronous interrupts, this property requires the cooperation of the interrupt-handling code. If that code saves register state to memory, and that memory can be overwritten by an attacker, then essentially the attack can overwrite arbitrary register state at an arbitrary point. This could be a concern if the threat model includes attacks on the kernel or if the program uses user-space preemptive multitasking. + +(Readers might object that an attacker cannot rely on asynchronous interrupts triggering at an exact instruction boundary. In fact, researchers have had some success in doing exactly that. Even ignoring that, though, we should aim to protect against lucky attackers just as much as good ones.) + +To protect against this, saved register state must be at least partially signed (using something like `ptrauth_sign_generic_data`_). This is required for correctness anyway because saved thread states include security-critical registers such as SP, FP, PC, and LR (where applicable). Ideally, this signature would cover all the registers, but since saving and restoring registers can be very performance-sensitive, that may not be acceptable. It is sufficient to set aside a small number of scratch registers that will be guaranteed to be preserved correctly; the compiler can then be careful to only store critical values like intermediate raw pointers in those registers. + +``setjmp`` and ``longjmp`` should sign and authenticate the core registers (SP, FP, PC, and LR), but they do not need to worry about intermediate values because ``setjmp`` can only be called synchronously, and the compiler should never schedule pointer-authentication operations interleaved with arbitrary calls. + +.. _Relative addresses: + +Attacks on relative addressing +++++++++++++++++++++++++++++++ + +Relative addressing is a technique used to compress and reduce the load-time cost of infrequently-used global data. The pointer authentication system is unlikely to support signing or authenticating a relative address, and in most cases it would defeat the point to do so: it would take additional storage space, and applying the signature would take extra work at load time. + +Relative addressing is not precluded by the use of pointer authentication, but it does take extra considerations to make it secure: + +- Relative addresses must only be stored in read-only memory. A writable relative address can be overwritten to point nearly anywhere, making it inherently insecure; this danger can only be compensated for with techniques for protecting arbitrary data like `ptrauth_sign_generic_data`_. + +- Relative addresses must only be accessed through signed pointers with adequate diversity. If an attacker can perform an `access path attack` to replace the pointer through which the relative address is accessed, they can easily cause the relative address to point wherever they want. + +Signature forging ++++++++++++++++++ + +If an attacker can exactly reproduce the behavior of the signing algorithm, and they know all the correct inputs to it, then they can perfectly forge a signature on an arbitrary pointer. + +There are three components to avoiding this mistake: + +- The abstract signing algorithm should be good: it should not have glaring flaws which would allow attackers to predict its result with better than random accuracy without knowing all the inputs (like the key values). + +- The key values should be kept secret. If at all possible, they should never be stored in accessible memory, or perhaps only stored encrypted. + +- Contexts that are meant to be independently protected should use different key values. For example, the kernel should not use the same keys as user processes. Different user processes should also use different keys from each other as much as possible, although this may pose its own technical challenges. + +Remapping ++++++++++ + +If an attacker can change the memory protections on certain pages of the program's memory, that can substantially weaken the protections afforded by pointer authentication. + +- If an attacker can inject their own executable code, they can also certainly inject code that can be used as a :ref:`signing oracle<Signing Oracles>`. The same is true if they can write to the instruction stream. + +- If an attacker can remap read-only program sections to be writable, then any use of :ref:`relative addresses` in global data becomes insecure. + +- If an attacker can remap read-only program sections to be writable, then it is unsafe to use unsigned pointers in `global offset tables`_. + +Remapping memory in this way often requires the attacker to have already substantively subverted the control flow of the process. Nonetheless, if the operating system has a mechanism for mapping pages in a way that cannot be remapped, this should be used wherever possible. + + + +.. _Safe Derivation: + +Safe derivation +~~~~~~~~~~~~~~~ + +Whether a data pointer is stored, even briefly, as a raw pointer can affect the security-correctness of a program. (Function pointers are never implicitly stored as raw pointers; raw pointers to functions can only be produced with the ``<ptrauth.h>`` intrinsics.) Repeated re-signing can also impact performance. Clang makes a modest set of guarantees in this area: + +- An expression of pointer type is said to be **safely derived** if: + + - it takes the address of a global variable or function, or + + - it is a load from a gl-value of ``__ptrauth``-qualified type. + +- If a value that is safely derived is assigned to a ``__ptrauth``-qualified object, including by initialization, then the value will be directly signed as appropriate for the target qualifier and will not be stored as a raw pointer. + +- If the function expression of a call is a gl-value of ``__ptrauth``-qualified type, then the call will be authenticated directly according to the source qualifier and will not be resigned to the default rule for a function pointer of its type. + +These guarantees are known to be inadequate for data pointer security. In particular, Clang should be enhanced to make the following guarantees: + +- A pointer should additionally be considered safely derived if it is: + + - the address of a gl-value that is safely derived, + + - the result of pointer arithmetic on a pointer that is safely derived (with some restrictions on the integer operand), + + - the result of a comma operator where the second operand is safely derived, + + - the result of a conditional operator where the selected operand is safely derived, or + + - the result of loading from a safely derived gl-value. + +- A gl-value should be considered safely derived if it is: + + - a dereference of a safely derived pointer, + + - a member access into a safely derived gl-value, or + + - a reference to a variable. + +- An access to a safely derived gl-value should be guaranteed to not allow replacement of any of the safely-derived component values at any point in the access. "Access" should include loading a function pointer. + +- Assignments should include pointer-arithmetic operators like ``+=``. + +Making these guarantees will require further work, including significant new support in LLVM IR. + +Furthermore, Clang should implement a warning when assigning a data pointer that is not safely derived to a ``__ptrauth``-qualified gl-value. + + + +Language ABI +------------ + +This section describes the pointer-authentication ABI currently implemented in Clang for the Apple arm64e target. As other targets adopt pointer authentication, this section should be generalized to express their ABIs as well. + +Key assignments +~~~~~~~~~~~~~~~ + +ARMv8.3 provides four abstract signing keys: ``IA``, ``IB``, ``DA``, and ``DB``. The architecture designates ``IA`` and ``IB`` for signing code pointers and ``DA`` and ``DB`` for signing data pointers; this is reinforced by two properties: + +- The ISA provides instructions that perform combined auth+call and auth+load operations; these instructions can only use the ``I`` keys and ``D`` keys, respectively. + +- AArch64's TBI feature can be separately enabled for code pointers (controlling whether indirect-branch instructions ignore those bits) and data pointers (controlling whether memory-access instructions) ignore those bits. If TBI is enabled for a kind of pointer, the sign and auth operations preserve the TBI bits when signing with an associated keys (at the cost of shrinking the number of available signing bits by 8). + +arm64e then further subdivides the keys as follows: + +- The ``A`` keys are used for primarily "global" purposes like signing v-tables and function pointers. These keys are sometimes called *process-independent* or *cross-process* because on existing OSes they are not changed when changing processes, although this is not a platform guarantee. + +- The ``B`` keys are used for primarily "local" purposes like signing return addresses and frame pointers. These keys are sometimes called *process-specific* because they are typically different between processes. However, they are in fact shared across processes in one situation: systems which provide ``fork`` cannot change these keys in the child process; they can only be changed during ``exec``. + +Implementation-defined algorithms and quantities +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The cryptographic hash algorithm used to compute signatures in ARMv8.3 is a private detail of the hardware implementation. + +arm64e restricts constant discriminators (used in ``__ptrauth`` and ``ptrauth_blend_discriminator``) to the range from 0 to 65535, inclusive. A 0 discriminator generally signifies that no blending is required; see the documentation for ``ptrauth_blend_discriminator``. This range is somewhat narrow but has two advantages: + +- The AArch64 ISA allows an arbitrary 16-bit immediate to be written over the top 16 bits of a register in a single instruction: + + .. code-block:: asm + + movk xN, #0x4849, LSL 48 + + This is ideal for the discriminator blending operation because it adds minimal code-size overhead and avoids overwriting any interesting bits from the pointer. Blending in a wider constant discriminator would either clobber interesting bits (e.g. if it was loaded with ``movk xN, #0x4c4f, LSL 32``) or require significantly more code (e.g. if the discriminator was loaded with a ``mov+bfi`` sequence). + +- It is possible to pack a 16-bit discriminator into loader metadata with minimal compromises, whereas a wider discriminator would require extra metadata storage and therefore significantly impact load times. + +The string hash used by ``ptrauth_string_discriminator`` is a 64-bit SipHash-2-4 using the constant seed ``b5d4c9eb79104a796fec8b1b428781d4`` (big-endian), with the result reduced by modulo to the range of non-zero discriminators (i.e. ``(rawHash % 65535) + 1``). + +Return addresses +~~~~~~~~~~~~~~~~ + +The kernel must ensure that attackers cannot replace LR due to an asynchronous exception; see `Register clobbering`_. If this is done by generally protecting LR, then functions which don't spill LR to the stack can avoid signing it entirely. Otherwise, the return address must be signed; on arm64e it is signed with the ``IB`` key using the stack pointer on entry as the discriminator. + +Protecting return addresses is of such particular importance that the ``IB`` key is almost entirely reserved for this purpose. + +Global offset tables +~~~~~~~~~~~~~~~~~~~~ + +The global offset table (GOT) is not ABI, but it is a common implementation technique for dynamic linking which deserves special discussion here. + +Whenever possible, signed pointers should be materialized directly in code rather than via the GOT, e.g. using an ``adrp+add+pac`` sequence on ARMv8.3. This decreases the amount of work necessary at load time to initialize the GOT, but more importantly, it defines away the potential for several attacks: + +- Attackers cannot change instructions, so there is no way to cause this code sequence to materialize a different pointer, whereas an access via the GOT always has *at minimum* a probabilistic chance to be the target of successful `substitution attacks`_. + +- The GOT is a dense pool of fixed pointers at a fixed offset relative to code; attackers can search this pool for useful pointers that can be used in `substitution attacks`_, whereas pointers that are only materialized directly are not so easily available. + +- Similarly, attackers can use `access path attacks`_ to replace a pointer to a signed pointer with a pointer to the GOT if the signing schema used within the GOT happens to be the same as the original pointer. This kind of collision becomes much less likely to be useful the fewer pointers are in the GOT in the first place. + +If this can be done for a symbol, then the compiler need only ensure that it materializes the signed pointer using registers that are safe against `register clobbering`_. + +However, many symbols can only be accessed via the GOT, e.g. because they resolve to definitions outside of the current image. In this case, care must be taken to ensure that using the GOT does not introduce weaknesses. + +- If the entire GOT can be mapped read-only after loading, then no signing is required within the GOT. In fact, not signing pointers in the GOT is preferable in this case because it makes the GOT useless for the harvesting and access-path attacks above. Storing raw pointers in this way is usually extremely unsafe, but for the special case of an immutable GOT entry it's fine because the GOT is always accessed via an address that is directly materialized in code and thus provably unattackable. (But see `Remapping`_.) + +- Otherwise, GOT entries which are used for producing a signed pointer constant must be signed. The signing schema used in the GOT need not match the target signing schema for the signed constant. To counteract the threats of substitution attacks, it's best if GOT entries can be signed with address diversity. Using a good constant discriminator as well (perhaps derived from the symbol name) can make it less useful to use a pointer to the GOT as the replacement in an :ref:`access path attack<Access path attacks>`. + +In either case, the compiler must ensure that materializing the address of a GOT entry as part of producing a signed pointer constant is not vulnerable to `register clobbering`_. If the linker also generates code for this, e.g. for call stubs, this generated code must take the same precautions. + +C function pointers +~~~~~~~~~~~~~~~~~~~ + +On arm64e, C function pointers are currently signed with the ``IA`` key without address diversity and with a constant discriminator of 0. + +The C and C++ standards do not permit C function pointers to be signed with address diversity by default: in C++ terms, function pointer types are required to be trivially copyable, which means they must be copyable with ``memcpy``. + +The use of a uniform constant discriminator is seen as a serious defect which should be remedied, and improving this is under investigation. + +C++ virtual tables +~~~~~~~~~~~~~~~~~~ + +The pointer to a C++ virtual table is currently signed with the ``DA`` key, no address diversity, and a constant discriminator of 0. The use of no address diversity, as well as the uniform constant discriminator, are seen as weaknesses. Not using address diversity allows attackers to simply copy valid v-table pointers from one object to another. However, using a uniform discriminator of 0 does have positive performance and code-size implications on ARMv8.3, and diversity for the most important v-table access pattern (virtual dispatch) is already better assured by the signing schemas used on the virtual functions. It is also known that some code in practice copies objects containing v-tables with ``memcpy``, and while this is not permitted formally, it is something that may be invasive to eliminate. + +Virtual functions in a C++ virtual table are signed with the ``IA`` key, address diversity, and a constant discriminator equal to the string hash (see `ptrauth_string_discriminator`_) of the mangled name of the function which originally gave rise to the v-table slot. + +C++ member function pointers +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +A member function pointer is signed with the ``IA`` key, no address diversity, and a constant discriminator equal to the string hash (see `ptrauth_string_discriminator`_) of the member pointer type. Address diversity is not permitted by C++ for member function pointers because they must be trivially-copyable types. + +The Itanium C++ ABI specifies that member function pointers to virtual functions simply store an offset to the correct v-table slot. This ABI cannot be used securely with pointer authentication because there is no safe place to store the constant discriminator for the target v-table slot: if it's stored with the offset, an attacker can simply overwrite it with the right discriminator for the offset. Even if the programmer never uses pointers to virtual functions, the existence of this code path makes all member function pointer dereferences insecure. + +arm64e changes this ABI so that virtual function pointers are stored using dispatch thunks with vague linkage. Because arm64e supports interoperation with ``arm64`` code when pointer authentication is disabled, an arm64e member function pointer dereference still recognizes the virtual-function representation but uses an bogus discriminator on that path that should always trap if pointer authentication is enabled dynamically. + +The use of dispatch thunks means that ``==`` on member function pointers is no longer reliable for virtual functions, but this is acceptable because the standard makes no guarantees about it in the first place. + +The use of dispatch thunks also potentially enables v-tables to be signed using a declaration-specific constant discriminator in the future; otherwise this discriminator would also need to be stored in the member pointer. + +Blocks +~~~~~~ + +Block pointers are data pointers which must interoperate with the ObjC `id` type and therefore cannot be signed themselves. + +The invocation pointer in a block is signed with the ``IA`` key using address diversity and a constant dicriminator of 0. Using a uniform discriminator is seen as a weakness to be potentially improved, but this is tricky due to the subtype polymorphism directly permitted for blocks. + +Block descriptors and ``__block`` variables can contain pointers to functions that can be used to copy or destroy the object. These functions are signed with the ``IA`` key, address diversity, and a constant discriminator of 0. The structure of block descriptors is under consideration for improvement. + +Objective-C methods +~~~~~~~~~~~~~~~~~~~ + +Objective-C method lists sign methods with the ``IA`` key using address diversity and a constant discriminator of 0. Using a uniform constant discriminator is believed to be acceptable because these tables are only accessed internally to the Objective-C runtime. + +The Objective-C runtime provides additional protection to methods that have been loaded into the Objective-C method cache; this protection is private to the runtime. + +Pointer authentication cannot protect against access-path atacks against the Objective-C ``isa`` pointer, through which all dispatch occurs, because of compatibility requirements and existing and important usage of high bits in the pointer. + +Swift class methods +~~~~~~~~~~~~~~~~~~~ + +Class methods in Swift are signed in the class object with the ``IA`` key using address diversity and a constant discriminator equal to the string hash (see `ptrauth_string_discriminator`_) of the mangling of the original overridable method. + +Resilient class-method lookup relies on passing a method descriptor; this method descriptor should be signed but currently isn't. The lookup function returns a function pointer that is signed using ``IA`` without address diversity and with the correct constant discriminator for the looked-up method. + +Swift's equivalent of a C++ v-table pointer is the ``isa`` pointer of an object. On arm64e, this is constrained by Objective-C compatibility and cannot be a signed pointer. + +Swift heap destructors +~~~~~~~~~~~~~~~~~~~~~~ + +Objects that are retained and released with Swift's native reference-counting system, including both native classes and temporary "box" allocations, must provide a destructor function in their metadata. This destructor function is signed with the ``IA`` key using address diversity and a constant discriminator of ``0xbbbf``. + +Swift protocol requirements +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Protocol function requirements are signed in the protocol witness table with the ``IA`` key using address diversity and a constant discriminator equal to the string hash (see `ptrauth_string_discriminator`_) of the mangling of the protocol requirement. + +Swift function types +~~~~~~~~~~~~~~~~~~~~ + +The invocation pointers of Swift function values are signed using the ``IA`` key without address diversity and with a constant discriminator derived loosely from the function type. + +Address diversity cannot be used by default for function values because function types are intended to be a "loadable" type which can be held and passed in registers. + +The constant discriminator currently accounts for potential abstraction in the function signature in ways that decrease the diversity of signatures; improving this is under investigation. + +Swift metadata +~~~~~~~~~~~~~~ + +Type metadata pointers in Swift are not signed. + +Type context descriptors must be signed because they frequently contain `relative addresses`_. Type context descriptors are signed with the ``DA`` key without address diversity (except when stored in type metadata) and with a constant discriminator of ``0xae86``. + +Swift value witnesses +~~~~~~~~~~~~~~~~~~~~~ + +Value witness functions in Swift are signed in the value witness table using the ``IA`` key with address diversity and an operation-specific constant discriminator which can be found in the Swift project headers. + +Swift coroutines +~~~~~~~~~~~~~~~~ + +Resumption functions for Swift coroutines are signed using the ``IA`` key without address diversity and with a constant discriminator derived from the yield type of the coroutine. Resumption functions cannot be signed with address diversity as they are returned directly in registers from the coroutine. + + + Alternative Implementations diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 24d88ed6edf00..5bc23e37ccc33 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -147,7 +147,7 @@ Clang Frontend Potentially Breaking Changes that ``none`` means that there is no operating system. As opposed to an unknown type of operating system. - This change my cause clang to not find libraries, or libraries to be built at + This change can cause clang to not find libraries, or libraries to be built at different file system locations. This can be fixed by changing your builds to use the new normalized triple. However, we recommend instead getting the normalized triple from clang itself, as this will make your builds more @@ -447,6 +447,10 @@ Non-comprehensive list of changes in this release type of the pointer was taken into account. This improves compatibility with GCC's libstdc++. +- The type traits builtin ``__is_nullptr`` is deprecated in CLang 19 and will be + removed in Clang 20. ``__is_same(__remove_cv(T), decltype(nullptr))`` can be + used instead to check whether a type ``T`` is a ``nullptr``. + New Compiler Flags ------------------ - ``-fsanitize=implicit-bitfield-conversion`` checks implicit truncation and @@ -557,6 +561,9 @@ Removed Compiler Flags Attribute Changes in Clang -------------------------- +- Adding [[clang::unsafe_buffer_usage]] attribute to a method definition now turns off all -Wunsafe-buffer-usage + related warnings within the method body. + - Introduced a new function attribute ``__attribute__((amdgpu_max_num_work_groups(x, y, z)))`` or ``[[clang::amdgpu_max_num_work_groups(x, y, z)]]`` for the AMDGPU target. This attribute can be attached to HIP or OpenCL kernel function definitions to provide an optimization hint. The parameters @@ -629,6 +636,56 @@ Attribute Changes in Clang The attributes declare constraints about a function's behavior pertaining to blocking and heap memory allocation. +- The ``hybrid_patchable`` attribute is now supported on ARM64EC targets. It can be used to specify + that a function requires an additional x86-64 thunk, which may be patched at runtime. + +- Clang now supports ``[[clang::lifetime_capture_by(X)]]``. Similar to lifetimebound, this can be + used to specify when a reference to a function parameter is captured by another capturing entity ``X``. + +- There is a new ``format_matches`` attribute to complement the existing + ``format`` attribute. ``format_matches`` allows the compiler to verify that + a format string argument is equivalent to a reference format string: it is + useful when a function accepts a format string without its accompanying + arguments to format. For instance: + + .. code-block:: c + + static int status_code; + static const char *status_string; + + void print_status(const char *fmt) { + fprintf(stderr, fmt, status_code, status_string); + // ^ warning: format string is not a string literal [-Wformat-nonliteral] + } + + void stuff(void) { + print_status("%s (%#08x)\n"); + // order of %s and %x is swapped but there is no diagnostic + } + + Before the introducion of ``format_matches``, this code cannot be verified + at compile-time. ``format_matches`` plugs that hole: + + .. code-block:: c + + __attribute__((format_matches(printf, 1, "%x %s"))) + void print_status(const char *fmt) { + fprintf(stderr, fmt, status_code, status_string); + // ^ `fmt` verified as if it was "%x %s" here; no longer triggers + // -Wformat-nonliteral, would warn if arguments did not match "%x %s" + } + + void stuff(void) { + print_status("%s (%#08x)\n"); + // warning: format specifier 's' is incompatible with 'x' + // warning: format specifier 'x' is incompatible with 's' + } + + Like with ``format``, the first argument is the format string flavor and the + second argument is the index of the format string parameter. + ``format_matches`` accepts an example valid format string as its third + argument. For more information, see the Clang attributes documentation. + Improvements to Clang's diagnostics ----------------------------------- - Clang now emits an error instead of a warning for ``-Wundefined-internal`` @@ -751,7 +808,7 @@ Improvements to Clang's diagnostics - Clang now diagnoses dangling assignments for pointer-like objects (annotated with `[[gsl::Pointer]]`) under `-Wdangling-assignment-gsl` (off by default) Fixes #GH63310. - + - Clang now diagnoses uses of alias templates with a deprecated attribute. (Fixes #GH18236). .. code-block:: c++ @@ -765,6 +822,14 @@ Improvements to Clang's diagnostics UsingWithAttr<int> objUsingWA; // warning: 'UsingWithAttr' is deprecated +- Clang now diagnoses undefined behavior in constant expressions more consistently. This includes invalid shifts, and signed overflow in arithmetic. + +- Clang now diagnoses dangling references to fields of temporary objects. Fixes #GH81589. + + +- Fixed a bug where Clang hung on an unsupported optional scope specifier ``::`` when parsing + Objective-C. Clang now emits a diagnostic message instead of hanging. + Improvements to Clang's time-trace ---------------------------------- @@ -891,6 +956,9 @@ Bug Fixes in This Version - Fixed an assertion failure when a template non-type parameter contains an invalid expression. +- Fixed the definition of ``ATOMIC_FLAG_INIT`` in ``<stdatomic.h>`` so it can + be used in C++. + Bug Fixes to Compiler Builtins ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -1106,6 +1174,10 @@ Bug Fixes to C++ Support Fixes (#GH85992). - Fixed a crash-on-invalid bug involving extraneous template parameter with concept substitution. (#GH73885) - Fixed assertion failure by skipping the analysis of an invalid field declaration. (#GH99868) +- Fix an issue with dependent source location expressions (#GH106428), (#GH81155), (#GH80210), (#GH85373) +- Fix handling of ``_`` as the name of a lambda's init capture variable. (#GH107024) +- Fixed a crash when an expression with a dependent ``__typeof__`` type is used as the operand of a unary operator. (#GH97646) + Bug Fixes to AST Handling ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -1176,11 +1248,13 @@ Arm and AArch64 Support improvements for most targets. We have not changed the default behavior for ARMv6, but may revisit that decision in the future. Users can restore the old behavior with -m[no-]unaligned-access. + - An alias identifier (rdma) has been added for targeting the AArch64 Architecture Extension which uses Rounding Doubling Multiply Accumulate instructions (rdm). The identifier is available on the command line as a feature modifier for -march and -mcpu as well as via target attributes like ``target_version`` or ``target_clones``. + - Support has been added for the following processors (-mcpu identifiers in parenthesis): * Arm Cortex-R52+ (cortex-r52plus). * Arm Cortex-R82AE (cortex-r82ae). @@ -1192,6 +1266,20 @@ Arm and AArch64 Support * Arm Neoverse-N3 (neoverse-n3). * Arm Neoverse-V3 (neoverse-v3). * Arm Neoverse-V3AE (neoverse-v3ae). +- ``-mbranch-protection=gcs`` has been added which enables support for the + Guarded Control Stack extension, and ``-mbranch-protection=standard`` also + enables this. Enabling GCS causes the GCS GNU property bit to be set on output + objects. It doesn't cause any code generation changes, as the code generated + by clang is already compatible with GCS. + + - Experimental support has been added for pointer authentication ABI for С/C++. + + - Pointer authentication ABI could be enabled for AArch64 Linux via + ``-mabi=pauthtest`` option or via specifying ``pauthtest`` environment part of + target triple. + + - The C23 ``_BitInt`` implementation has been brought into compliance + with AAPCS32 and AAPCS64. Android Support ^^^^^^^^^^^^^^^ @@ -1226,6 +1314,10 @@ Windows Support to ensure compatibility with msvc. Previously strict aliasing was only disabled if the driver mode was cl. +- Clang now can process the `i128` and `ui128` integeral suffixes when MSVC + extensions are enabled. This allows for properly processing ``intsafe.h`` in + the Windows SDK. + LoongArch Support ^^^^^^^^^^^^^^^^^ @@ -1248,6 +1340,14 @@ RISC-V Support accesses may be created. ``-m[no-]strict-align`` applies to both scalar and vector. +PowerPC Support +^^^^^^^^^^^^^^^ + +- Clang now emits errors for impossible ``__attribute__((musttail))``. +- Added support for ``-mcpu=[pwr11 | power11]`` and ``-mtune=[pwr11 | power11]``. +- Added support for ``builtin_cpu_supports`` on AIX, along with a subset of + features that can be queried. + CUDA/HIP Language Changes ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -1266,6 +1366,14 @@ AIX Support base is encoded as an immediate operand. This access sequence is not used for TLS variables larger than 32KB, and is currently only supported on 64-bit mode. +- Introduced the options ``-mtocdata/-mno-tocdata`` to enable/disable TOC data + transformations for the listed suitable variables. +- Introduced the ``-maix-shared-lib-tls-model-opt`` option to enable the tuning + of changing local-dynamic mode access(es) to initial-exec access(es) at the + function level on 64-bit mode. +- Clang now emits errors for ``-gdwarf-5``. +- Added the support of the OpenMP runtime libomp on AIX. OpenMP applications can be + compiled with ``-fopenmp`` and execute on AIX. NetBSD Support ^^^^^^^^^^^^^^ @@ -1310,6 +1418,10 @@ AST Matchers - Fixed captureVars assertion failure if not capturesVariables. (#GH76425) - ``forCallable`` now properly preserves binding on successful match. (#GH89657) +- Ensure ``hasType`` and ``hasDeclaration`` match Objective-C interface declarations. + +- Ensure ``pointee`` matches Objective-C pointer types. + clang-format ------------ @@ -1344,6 +1456,11 @@ New features - Support C++23 static operator calls. (#GH84972) +- Function effects, e.g. the ``nonblocking`` and ``nonallocating`` "performance constraint" + attributes, are now verified. For example, for functions declared with the ``nonblocking`` + attribute, the compiler can generate warnings about the use of any language features, or calls to + other functions, which may block. + Crash and bug fixes ^^^^^^^^^^^^^^^^^^^ @@ -1357,9 +1474,17 @@ Crash and bug fixes - Fixed a crash when storing through an address that refers to the address of a label. (#GH89185) +- Fixed a crash when using ``__builtin_bitcast(type, array)`` as an array + subscript. (#GH94496) + - Z3 crosschecking (aka. Z3 refutation) is now bounded, and can't consume more total time than the eymbolic execution itself. (#GH97298) +- In clang-18, we regressed in terms of analysis time for projects having many + nested loops with buffer indexing or shifting or other binary operations. + For example, functions computing different hash values. Some of this slowdown + was attributed to taint analysis, which is fixed now. (#GH105493) + - ``std::addressof``, ``std::as_const``, ``std::forward``, ``std::forward_like``, ``std::move``, ``std::move_if_noexcept``, are now modeled just like their builtin counterpart. (#GH94193) @@ -1420,6 +1545,7 @@ OpenMP Support -------------- - Added support for the `[[omp::assume]]` attribute. +- AIX added an include directory for ``omp.h`` at ``/opt/IBM/openxlCSDK/include/openmp``. Additional Information ====================== diff --git a/clang/docs/StandardCPlusPlusModules.rst b/clang/docs/StandardCPlusPlusModules.rst index b87491910e222..2478a77e7640c 100644 --- a/clang/docs/StandardCPlusPlusModules.rst +++ b/clang/docs/StandardCPlusPlusModules.rst @@ -398,6 +398,16 @@ BMIs cannot be shipped in an archive to create a module library. Instead, the BMIs(``*.pcm``) are compiled into object files(``*.o``) and those object files are added to the archive instead. +clang-cl +~~~~~~~~ + +``clang-cl`` supports the same options as ``clang++`` for modules as detailed above; +there is no need to prefix these options with ``/clang:``. Note that ``cl.exe`` +`options to emit/consume IFC files <https://devblogs.microsoft.com/cppblog/using-cpp-modules-in-msvc-from-the-command-line-part-1/>` are *not* supported. +The resultant precompiled modules are also not compatible for use with ``cl.exe``. + +We recommend that build system authors use the above-mentioned ``clang++`` options with ``clang-cl`` to build modules. + Consistency Requirements ~~~~~~~~~~~~~~~~~~~~~~~~ @@ -1387,13 +1397,6 @@ have ``.cppm`` (or ``.ccm``, ``.cxxm``, ``.c++m``) as the file extension. However, the behavior is inconsistent with other compilers. This is tracked by `#57416 <https://github.com/llvm/llvm-project/issues/57416>`_. -clang-cl is not compatible with standard C++ modules -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -``/clang:-fmodule-file`` and ``/clang:-fprebuilt-module-path`` cannot be used -to specify the BMI with ``clang-cl.exe``. This is tracked by -`#64118 <https://github.com/llvm/llvm-project/issues/64118>`_. - Incorrect ODR violation diagnostics ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/clang/docs/UsersManual.rst b/clang/docs/UsersManual.rst index e9b95739ea2ab..64e991451bf70 100644 --- a/clang/docs/UsersManual.rst +++ b/clang/docs/UsersManual.rst @@ -4745,6 +4745,12 @@ Execute ``clang-cl /?`` to see a list of supported options: -flto=<value> Set LTO mode to either 'full' or 'thin' -flto Enable LTO in 'full' mode -fmerge-all-constants Allow merging of constants + -fmodule-file=<module_name>=<module-file> + Use the specified module file that provides the module <module_name> + -fmodule-header=<header> + Build <header> as a C++20 header unit + -fmodule-output=<path> + Save intermediate module file results when compiling a standard C++ module unit. -fms-compatibility-version=<value> Dot-separated value representing the Microsoft compiler version number to report in _MSC_VER (0 = don't define it; default is same value as installed cl.exe, or 1933) diff --git a/clang/docs/analyzer/checkers.rst b/clang/docs/analyzer/checkers.rst index 76a9aae170893..459f4950d2a65 100644 --- a/clang/docs/analyzer/checkers.rst +++ b/clang/docs/analyzer/checkers.rst @@ -3433,6 +3433,52 @@ Check for non-determinism caused by sorting of pointers. alpha.WebKit ^^^^^^^^^^^^ +.. _alpha-webkit-NoUncheckedPtrMemberChecker: + +alpha.webkit.MemoryUnsafeCastChecker +"""""""""""""""""""""""""""""""""""""" +Check for all casts from a base type to its derived type as these might be memory-unsafe. + +Example: + +.. code-block:: cpp + + class Base { }; + class Derived : public Base { }; + + void f(Base* base) { + Derived* derived = static_cast<Derived*>(base); // ERROR + } + +For all cast operations (C-style casts, static_cast, reinterpret_cast, dynamic_cast), if the source type a `Base*` and the destination type is `Derived*`, where `Derived` inherits from `Base`, the static analyzer should signal an error. + +This applies to: + +- C structs, C++ structs and classes, and Objective-C classes and protocols. +- Pointers and references. +- Inside template instantiations and macro expansions that are visible to the compiler. + +For types like this, instead of using built in casts, the programmer will use helper functions that internally perform the appropriate type check and disable static analysis. + +alpha.webkit.NoUncheckedPtrMemberChecker +"""""""""""""""""""""""""""""""""""""""" +Raw pointers and references to an object which supports CheckedPtr or CheckedRef can't be used as class members. Only CheckedPtr, CheckedRef, RefPtr, or Ref are allowed. + +.. code-block:: cpp + + struct CheckableObj { + void incrementCheckedPtrCount() {} + void decrementCheckedPtrCount() {} + }; + + struct Foo { + CheckableObj* ptr; // warn + CheckableObj& ptr; // warn + // ... + }; + +See `WebKit Guidelines for Safer C++ Programming <https://github.com/WebKit/WebKit/wiki/Safer-CPP-Guidelines>`_ for details. + .. _alpha-webkit-UncountedCallArgsChecker: alpha.webkit.UncountedCallArgsChecker @@ -3522,6 +3568,12 @@ We also define a set of safe transformations which if passed a safe value as an - casts - unary operators like ``&`` or ``*`` +alpha.webkit.UncheckedCallArgsChecker +""""""""""""""""""""""""""""""""""""" +The goal of this rule is to make sure that lifetime of any dynamically allocated CheckedPtr capable object passed as a call argument keeps its memory region past the end of the call. This applies to call to any function, method, lambda, function pointer or functor. CheckedPtr capable objects aren't supposed to be allocated on stack so we check arguments for parameters of raw pointers and references to unchecked types. + +The rules of when to use and not to use CheckedPtr / CheckedRef are same as alpha.webkit.UncountedCallArgsChecker for ref-counted objects. + alpha.webkit.UncountedLocalVarsChecker """""""""""""""""""""""""""""""""""""" The goal of this rule is to make sure that any uncounted local variable is backed by a ref-counted object with lifetime that is strictly larger than the scope of the uncounted local variable. To be on the safe side we require the scope of an uncounted variable to be embedded in the scope of ref-counted object that backs it. @@ -3546,7 +3598,7 @@ These are examples of cases that we consider safe: RefCountable* uncounted = this; // ok } -Here are some examples of situations that we warn about as they *might* be potentially unsafe. The logic is that either we're able to guarantee that an argument is safe or it's considered if not a bug then bug-prone. +Here are some examples of situations that we warn about as they *might* be potentially unsafe. The logic is that either we're able to guarantee that a local variable is safe or it's considered unsafe. .. code-block:: cpp @@ -3565,11 +3617,48 @@ Here are some examples of situations that we warn about as they *might* be poten RefCountable* uncounted = counted.get(); // warn } -We don't warn about these cases - we don't consider them necessarily safe but since they are very common and usually safe we'd introduce a lot of false positives otherwise: -- variable defined in condition part of an ```if``` statement -- variable defined in init statement condition of a ```for``` statement +alpha.webkit.UncheckedLocalVarsChecker +"""""""""""""""""""""""""""""""""""""" +The goal of this rule is to make sure that any unchecked local variable is backed by a CheckedPtr or CheckedRef with lifetime that is strictly larger than the scope of the unchecked local variable. To be on the safe side we require the scope of an unchecked variable to be embedded in the scope of CheckedPtr/CheckRef object that backs it. + +These are examples of cases that we consider safe: + + .. code-block:: cpp + + void foo1() { + CheckedPtr<RefCountable> counted; + // The scope of uncounted is EMBEDDED in the scope of counted. + { + RefCountable* uncounted = counted.get(); // ok + } + } + + void foo2(CheckedPtr<RefCountable> counted_param) { + RefCountable* uncounted = counted_param.get(); // ok + } + + void FooClass::foo_method() { + RefCountable* uncounted = this; // ok + } + +Here are some examples of situations that we warn about as they *might* be potentially unsafe. The logic is that either we're able to guarantee that a local variable is safe or it's considered unsafe. -For the time being we also don't warn about uninitialized uncounted local variables. + .. code-block:: cpp + + void foo1() { + RefCountable* uncounted = new RefCountable; // warn + } + + RefCountable* global_uncounted; + void foo2() { + RefCountable* uncounted = global_uncounted; // warn + } + + void foo3() { + RefPtr<RefCountable> counted; + // The scope of uncounted is not EMBEDDED in the scope of counted. + RefCountable* uncounted = counted.get(); // warn + } Debug Checkers --------------- diff --git a/clang/include/clang-c/CAS.h b/clang/include/clang-c/CAS.h new file mode 100644 index 0000000000000..a9aecbc6c51f6 --- /dev/null +++ b/clang/include/clang-c/CAS.h @@ -0,0 +1,390 @@ +/*==-- clang-c/CAS.h - CAS Interface ------------------------------*- C -*-===*\ +|* *| +|* Part of the LLVM Project, under the Apache License v2.0 with LLVM *| +|* Exceptions. *| +|* See https://llvm.org/LICENSE.txt for license information. *| +|* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception *| +|* *| +|*===----------------------------------------------------------------------===*| +|* *| +|* This header provides interfaces for creating and working with CAS and *| +|* ActionCache interfaces. *| +|* *| +|* An example of its usage is available in c-index-test/core_main.cpp. *| +|* *| +|* EXPERIMENTAL: These interfaces are experimental and will change. If you *| +|* use these be prepared for them to change without notice on any commit. *| +|* *| +\*===----------------------------------------------------------------------===*/ + +#ifndef LLVM_CLANG_C_CAS_H +#define LLVM_CLANG_C_CAS_H + +#include "clang-c/CXErrorCode.h" +#include "clang-c/CXString.h" +#include "clang-c/Platform.h" +#include <stdbool.h> +#include <stdint.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \defgroup CAS CAS and ActionCache interface. + * @{ + */ + +/** + * Configuration options for ObjectStore and ActionCache. + */ +typedef struct CXOpaqueCASOptions *CXCASOptions; + +/** + * Encapsulates instances of ObjectStore and ActionCache, created from a + * particular configuration of \p CXCASOptions. + */ +typedef struct CXOpaqueCASDatabases *CXCASDatabases; + +/** + * Content-addressable storage for objects. + */ +typedef struct CXOpaqueCASObjectStore *CXCASObjectStore; + +/** + * A cache from a key describing an action to the result of doing it. + */ +typedef struct CXOpaqueCASActionCache *CXCASActionCache; + +typedef struct CXOpaqueCASObject *CXCASObject; + +/** + * Result of \c clang_experimental_cas_getCachedCompilation. + */ +typedef struct CXOpaqueCASCachedCompilation *CXCASCachedCompilation; + +/** + * Result of \c clang_experimental_cas_replayCompilation. + */ +typedef struct CXOpaqueCASReplayResult *CXCASReplayResult; + +/** + * Used for cancelling asynchronous actions. + */ +typedef struct CXOpaqueCASCancellationToken *CXCASCancellationToken; + +/** + * Create a \c CXCASOptions object. + */ +CINDEX_LINKAGE CXCASOptions clang_experimental_cas_Options_create(void); + +/** + * Dispose of a \c CXCASOptions object. + */ +CINDEX_LINKAGE void clang_experimental_cas_Options_dispose(CXCASOptions); + +/** + * Configure the file path to use for on-disk CAS/cache instances. + */ +CINDEX_LINKAGE void +clang_experimental_cas_Options_setOnDiskPath(CXCASOptions, const char *Path); + +/** + * Configure the path to a library that implements the LLVM CAS plugin API. + */ +CINDEX_LINKAGE void +clang_experimental_cas_Options_setPluginPath(CXCASOptions, const char *Path); + +/** + * Set a value for a named option that the CAS plugin supports. + */ +CINDEX_LINKAGE void +clang_experimental_cas_Options_setPluginOption(CXCASOptions, const char *Name, + const char *Value); + +/** + * Creates instances for a CAS object store and action cache based on the + * configuration of a \p CXCASOptions. + * + * \param Opts configuration options. + * \param[out] Error The error string to pass back to client (if any). + * + * \returns The resulting instances object, or null if there was an error. + */ +CINDEX_LINKAGE CXCASDatabases +clang_experimental_cas_Databases_create(CXCASOptions Opts, CXString *Error); + +/** + * Dispose of a \c CXCASDatabases object. + */ +CINDEX_LINKAGE void clang_experimental_cas_Databases_dispose(CXCASDatabases); + +/** + * Get the local storage size of the CAS/cache data in bytes. + * + * \param[out] OutError The error object to pass back to client (if any). + * If non-null the object must be disposed using \c clang_Error_dispose. + * \returns the local storage size of the CAS/cache data, or -1 if the + * implementation does not support reporting such size, or -2 if an error + * occurred. + */ +CINDEX_LINKAGE int64_t clang_experimental_cas_Databases_get_storage_size( + CXCASDatabases, CXError *OutError); + +/** + * Set the size for limiting disk storage growth. + * + * \param size_limit the maximum size limit in bytes. 0 means no limit. Negative + * values are invalid. + * \returns an error object if there was an error, NULL otherwise. + * If non-null the object must be disposed using \c clang_Error_dispose. + */ +CINDEX_LINKAGE CXError clang_experimental_cas_Databases_set_size_limit( + CXCASDatabases, int64_t size_limit); + +/** + * Prune local storage to reduce its size according to the desired size limit. + * Pruning can happen concurrently with other operations. + * + * \returns an error object if there was an error, NULL otherwise. + * If non-null the object must be disposed using \c clang_Error_dispose. + */ +CINDEX_LINKAGE +CXError clang_experimental_cas_Databases_prune_ondisk_data(CXCASDatabases); + +/** + * Checks whether an object is materialized in the database using its printed + * \p CASID. + * + * \param CASID The printed CASID string for the object. + * \param[out] OutError The error object to pass back to client (if any). + * If non-null the object must be disposed using \c clang_Error_dispose. + * + * \returns whether the object is materialized in the database. + */ +CINDEX_LINKAGE bool clang_experimental_cas_isMaterialized(CXCASDatabases, + const char *CASID, + CXError *OutError); + +/** + * Loads an object using its printed \p CASID. + * + * \param CASID The printed CASID string for the object. + * \param[out] OutError The error object to pass back to client (if any). + * If non-null the object must be disposed using \c clang_Error_dispose. + * + * \returns The resulting object, or null if the object was not found or an + * error occurred. The object should be disposed using + * \c clang_experimental_cas_CASObject_dispose. + */ +CINDEX_LINKAGE CXCASObject clang_experimental_cas_loadObjectByString( + CXCASDatabases, const char *CASID, CXError *OutError); + +/** + * Asynchronous version of \c clang_experimental_cas_loadObjectByString. + * + * \param CASID The printed CASID string for the object. + * \param Ctx opaque value to pass to the callback. + * \param Callback receives a \c CXCASObject, or \c CXError if an error occurred + * or both NULL if the object was not found or the call was cancelled. + * The objects should be disposed with + * \c clang_experimental_cas_CASObject_dispose or \c clang_Error_dispose. + * \param[out] OutToken if non-null receives a \c CXCASCancellationToken that + * can be used to cancel the call using + * \c clang_experimental_cas_CancellationToken_cancel. The object should be + * disposed using \c clang_experimental_cas_CancellationToken_dispose. + */ +CINDEX_LINKAGE void clang_experimental_cas_loadObjectByString_async( + CXCASDatabases, const char *CASID, void *Ctx, + void (*Callback)(void *Ctx, CXCASObject, CXError), + CXCASCancellationToken *OutToken); + +/** + * Dispose of a \c CXCASObject object. + */ +CINDEX_LINKAGE void clang_experimental_cas_CASObject_dispose(CXCASObject); + +/** + * Looks up a cache key and returns the associated set of compilation output IDs + * + * \param CacheKey The printed compilation cache key string. + * \param Globally if true it is a hint to the underlying CAS implementation + * that the lookup is profitable to be done on a distributed caching level, not + * just locally. + * \param[out] OutError The error object to pass back to client (if any). + * If non-null the object must be disposed using \c clang_Error_dispose. + * + * \returns The resulting object, or null if the cache key was not found or an + * error occurred. The object should be disposed using + * \c clang_experimental_cas_CachedCompilation_dispose. + */ +CINDEX_LINKAGE CXCASCachedCompilation +clang_experimental_cas_getCachedCompilation(CXCASDatabases, + const char *CacheKey, bool Globally, + CXError *OutError); + +/** + * Asynchronous version of \c clang_experimental_cas_getCachedCompilation. + * + * \param CacheKey The printed compilation cache key string. + * \param Globally if true it is a hint to the underlying CAS implementation + * that the lookup is profitable to be done on a distributed caching level, not + * just locally. + * \param Ctx opaque value to pass to the callback. + * \param Callback receives a \c CXCASCachedCompilation, or \c CXError if an + * error occurred or both NULL if the object was not found or the call was + * cancelled. The objects should be disposed with + * \c clang_experimental_cas_CachedCompilation_dispose or \c clang_Error_dispose + * \param[out] OutToken if non-null receives a \c CXCASCancellationToken that + * can be used to cancel the call using + * \c clang_experimental_cas_CancellationToken_cancel. The object should be + * disposed using \c clang_experimental_cas_CancellationToken_dispose. + */ +CINDEX_LINKAGE void clang_experimental_cas_getCachedCompilation_async( + CXCASDatabases, const char *CacheKey, bool Globally, void *Ctx, + void (*Callback)(void *Ctx, CXCASCachedCompilation, CXError), + CXCASCancellationToken *OutToken); + +/** + * Dispose of a \c CXCASCachedCompilation object. + */ +CINDEX_LINKAGE void + clang_experimental_cas_CachedCompilation_dispose(CXCASCachedCompilation); + +/** + * \returns number of compilation outputs. + */ +CINDEX_LINKAGE size_t clang_experimental_cas_CachedCompilation_getNumOutputs( + CXCASCachedCompilation); + +/** + * \returns the compilation output name given the index via \p OutputIdx. + */ +CINDEX_LINKAGE CXString clang_experimental_cas_CachedCompilation_getOutputName( + CXCASCachedCompilation, size_t OutputIdx); + +/** + * \returns the compilation output printed CASID given the index via + * \p OutputIdx. + */ +CINDEX_LINKAGE CXString +clang_experimental_cas_CachedCompilation_getOutputCASIDString( + CXCASCachedCompilation, size_t OutputIdx); + +/** + * \returns whether the compilation output data exist in the local CAS given the + * index via \p OutputIdx. + */ +CINDEX_LINKAGE bool +clang_experimental_cas_CachedCompilation_isOutputMaterialized( + CXCASCachedCompilation, size_t OutputIdx); + +/** + * If distributed caching is available it uploads the compilation outputs and + * the association of key <-> outputs to the distributed cache. + * This allows separating the task of computing the compilation outputs and + * storing them in the local cache, from the task of "uploading" them. + * + * \param Ctx opaque value to pass to the callback. + * \param Callback receives a \c CXError if an error occurred. The error will be + * NULL if the call was successful or cancelled. The error should be disposed + * via \c clang_Error_dispose. + * \param[out] OutToken if non-null receives a \c CXCASCancellationToken that + * can be used to cancel the call using + * \c clang_experimental_cas_CancellationToken_cancel. The object should be + * disposed using \c clang_experimental_cas_CancellationToken_dispose. + */ +CINDEX_LINKAGE void clang_experimental_cas_CachedCompilation_makeGlobal( + CXCASCachedCompilation, void *Ctx, void (*Callback)(void *Ctx, CXError), + CXCASCancellationToken *OutToken); + +/** + * Replays a cached compilation by writing the cached outputs to the filesystem + * and/or stderr based on the given compilation arguments. + * + * \param argc number of compilation arguments. + * \param argv array of compilation arguments. + * \param WorkingDirectory working directory to use, can be NULL. + * \param reserved for future use, caller must pass NULL. + * \param[out] OutError The error object to pass back to client (if any). + * If non-null the object must be disposed using \c clang_Error_dispose. + * + * \returns a \c CXCASReplayResult object or NULL if an error occurred or a + * compilation output was not found in the CAS. The object should be disposed + * via \c clang_experimental_cas_ReplayResult_dispose. + */ +CINDEX_LINKAGE CXCASReplayResult clang_experimental_cas_replayCompilation( + CXCASCachedCompilation, int argc, const char *const *argv, + const char *WorkingDirectory, void *reserved, CXError *OutError); + +/** + * Dispose of a \c CXCASReplayResult object. + */ +CINDEX_LINKAGE void + clang_experimental_cas_ReplayResult_dispose(CXCASReplayResult); + +/** + * Get the diagnostic text of a replayed cached compilation. + */ +CINDEX_LINKAGE +CXString clang_experimental_cas_ReplayResult_getStderr(CXCASReplayResult); + +/** + * Cancel an asynchronous CAS-related action. + */ +CINDEX_LINKAGE void + clang_experimental_cas_CancellationToken_cancel(CXCASCancellationToken); + +/** + * Dispose of a \c CXCASCancellationToken object. + */ +CINDEX_LINKAGE void + clang_experimental_cas_CancellationToken_dispose(CXCASCancellationToken); + +/** + * Dispose of a \c CXCASObjectStore object. + */ +CINDEX_LINKAGE void +clang_experimental_cas_ObjectStore_dispose(CXCASObjectStore CAS); + +/** + * Dispose of a \c CXCASActionCache object. + */ +CINDEX_LINKAGE void +clang_experimental_cas_ActionCache_dispose(CXCASActionCache Cache); + +/** + * Gets or creates a persistent on-disk CAS object store at \p Path. + * Deprecated, use \p clang_experimental_cas_Databases_create() instead. + * + * \param Path The path to locate the object store. + * \param[out] Error The error string to pass back to client (if any). + * + * \returns The resulting object store, or null if there was an error. + */ +CINDEX_DEPRECATED CINDEX_LINKAGE CXCASObjectStore +clang_experimental_cas_OnDiskObjectStore_create(const char *Path, + CXString *Error); + +/** + * Gets or creates a persistent on-disk action cache at \p Path. + * Deprecated, use \p clang_experimental_cas_Databases_create() instead. + * + * \param Path The path to locate the object store. + * \param[out] Error The error string to pass back to client (if any). + * + * \returns The resulting object store, or null if there was an error. + */ +CINDEX_DEPRECATED CINDEX_LINKAGE CXCASActionCache +clang_experimental_cas_OnDiskActionCache_create(const char *Path, + CXString *Error); + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif // LLVM_CLANG_C_CAS_H diff --git a/clang/include/clang-c/CXDiagnostic.h b/clang/include/clang-c/CXDiagnostic.h index 911d001f06697..2ebb7d138328d 100644 --- a/clang/include/clang-c/CXDiagnostic.h +++ b/clang/include/clang-c/CXDiagnostic.h @@ -148,6 +148,30 @@ CINDEX_LINKAGE void clang_disposeDiagnosticSet(CXDiagnosticSet Diags); */ CINDEX_LINKAGE CXDiagnosticSet clang_getChildDiagnostics(CXDiagnostic D); +/** + * Get the contents if the given file that was provided via diagnostics. + * + * \param diags the diagnostics set to query for the contents of the file. + * \param file the file to get the contents of. + * \param outFileSize if non-null, set to the file size on success. + * \returns on success, a pointer to the file contents. Otherwise, NULL. + */ +CINDEX_LINKAGE const char *clang_getDiagnosticFileContents( + CXDiagnosticSet diags, CXFile file, size_t *outFileSize); + +/** + * Retrieve the original source range if the given file was provided via + * diagnostics and is conceptually a replacement for the original source range. + * + * \param diags the diagnostics set to query for the contents of the file. + * \param file the file to get the contents of. + * \returns on success, the source range (into another file) that is + * conceptually replaced by the contents of the given file (available via + * \c clang_getDiagnosticFileContents). + */ +CINDEX_LINKAGE CXSourceRange clang_getDiagnosticFileOriginalSourceRange( + CXDiagnosticSet diags, CXFile file); + /** * Destroy a diagnostic. */ diff --git a/clang/include/clang-c/CXErrorCode.h b/clang/include/clang-c/CXErrorCode.h index b3a0b9d66d5f8..e284b2843a7ff 100644 --- a/clang/include/clang-c/CXErrorCode.h +++ b/clang/include/clang-c/CXErrorCode.h @@ -53,9 +53,47 @@ enum CXErrorCode { /** * An AST deserialization error has occurred. */ - CXError_ASTReadError = 4 + CXError_ASTReadError = 4, + + /** + * \brief A refactoring action is not available at the given location + * or in the given source range. + */ + CXError_RefactoringActionUnavailable = 5, + + /** + * \brief A refactoring action is not able to use the given name because + * it contains an unexpected number of strings. + */ + CXError_RefactoringNameSizeMismatch = 6, + + /** + * \brief A name of a symbol is invalid, i.e. it is reserved by the source + * language and can't be used as a name for this symbol. + */ + CXError_RefactoringNameInvalid = 7 }; +/** + * Represents an error with error code and description string. + */ +typedef struct CXOpaqueError *CXError; + +/** + * \returns the error code. + */ +CINDEX_LINKAGE enum CXErrorCode clang_Error_getCode(CXError); + +/** + * \returns the error description string. + */ +CINDEX_LINKAGE const char *clang_Error_getDescription(CXError); + +/** + * Dispose of a \c CXError object. + */ +CINDEX_LINKAGE void clang_Error_dispose(CXError); + LLVM_CLANG_C_EXTERN_C_END #endif diff --git a/clang/include/clang-c/CXString.h b/clang/include/clang-c/CXString.h index f117010c71a46..f132a2191a79e 100644 --- a/clang/include/clang-c/CXString.h +++ b/clang/include/clang-c/CXString.h @@ -16,6 +16,7 @@ #include "clang-c/ExternC.h" #include "clang-c/Platform.h" +#include <stddef.h> LLVM_CLANG_C_EXTERN_C_BEGIN @@ -44,6 +45,14 @@ typedef struct { unsigned Count; } CXStringSet; +/** + * An array of C strings. + */ +typedef struct { + const char **Strings; + size_t Count; +} CXCStringArray; + /** * Retrieve the character data associated with the given string. */ diff --git a/clang/include/clang-c/Dependencies.h b/clang/include/clang-c/Dependencies.h new file mode 100644 index 0000000000000..b90a123fd2e32 --- /dev/null +++ b/clang/include/clang-c/Dependencies.h @@ -0,0 +1,701 @@ +/*==-- clang-c/Dependencies.h - Dependency Discovery C Interface --*- C -*-===*\ +|* *| +|* Part of the LLVM Project, under the Apache License v2.0 with LLVM *| +|* Exceptions. *| +|* See https://llvm.org/LICENSE.txt for license information. *| +|* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception *| +|* *| +|*===----------------------------------------------------------------------===*| +|* *| +|* This header provides a dependency discovery interface similar to *| +|* clang-scan-deps. *| +|* *| +|* An example of its usage is available in c-index-test/core_main.cpp. *| +|* *| +|* EXPERIMENTAL: These interfaces are experimental and will change. If you *| +|* use these be prepared for them to change without notice on any commit. *| +|* *| +\*===----------------------------------------------------------------------===*/ + +#ifndef LLVM_CLANG_C_DEPENDENCIES_H +#define LLVM_CLANG_C_DEPENDENCIES_H + +#include "clang-c/BuildSystem.h" +#include "clang-c/CAS.h" +#include "clang-c/CXDiagnostic.h" +#include "clang-c/CXErrorCode.h" +#include "clang-c/CXString.h" +#include "clang-c/Platform.h" +#include <stddef.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \defgroup SCAN_DEPS Dependency scanning service. + * @{ + */ + +typedef struct { + CXString Name; + /** + * The context hash of a module represents the set of compiler options that + * may make one version of a module incompatible from another. This includes + * things like language mode, predefined macros, header search paths, etc... + * + * Modules with the same name but a different \c ContextHash should be treated + * as separate modules for the purpose of a build. + */ + CXString ContextHash; + + /** + * The path to the modulemap file which defines this module. + * + * This can be used to explicitly build this module. This file will + * additionally appear in \c FileDeps as a dependency. + */ + CXString ModuleMapPath; + + /** + * The list of files which this module directly depends on. + * + * If any of these change then the module needs to be rebuilt. + */ + CXStringSet *FileDeps; + + /** + * The list of modules which this module direct depends on. + * + * This does include the context hash. The format is + * `<module-name>:<context-hash>` + */ + CXStringSet *ModuleDeps; + + /** + * The canonical command line to build this module. + * + * If getFileDependencies_v3 or later was used to get this dependency, it is + * a complete command line. When using getFileDependencies_v2, it excludes + * arguments containing modules-related paths: + * "-fmodule-file=", "-o", "-fmodule-map-file=". + */ + CXStringSet *BuildArguments; +} CXModuleDependency; + +typedef struct { + int Count; + CXModuleDependency *Modules; +} CXModuleDependencySet; + +/** + * See \c CXModuleDependency for the meaning of these fields, with the addition + * that they represent only the direct dependencies for \c CXDependencyMode_Full + * mode. + */ +typedef struct { + CXString ContextHash; + CXStringSet *FileDeps; + CXStringSet *ModuleDeps; + CXStringSet *BuildArguments; +} CXFileDependencies; + +/** + * An individual command-line invocation that is part of an overall compilation + * \c CXFileDependenciesList. + * + * See \c CXModuleDependency for the meaning of these fields, with the addition + * that they represent only the direct dependencies for \c CXDependencyMode_Full + * mode. + */ +typedef struct { + CXString ContextHash; + CXStringSet *FileDeps; + CXStringSet *ModuleDeps; + CXString Executable; + CXStringSet *BuildArguments; +} CXTranslationUnitCommand; + +typedef struct { + size_t NumCommands; + CXTranslationUnitCommand *Commands; +} CXFileDependenciesList; + +/** + * An output file kind needed by module dependencies. + */ +typedef enum { + CXOutputKind_ModuleFile = 0, + CXOutputKind_Dependencies = 1, + CXOutputKind_DependenciesTarget = 2, + CXOutputKind_SerializedDiagnostics = 3, +} CXOutputKind; + +CINDEX_LINKAGE void +clang_experimental_ModuleDependencySet_dispose(CXModuleDependencySet *MD); + +CINDEX_LINKAGE void +clang_experimental_FileDependencies_dispose(CXFileDependencies *ID); + +CINDEX_LINKAGE void +clang_experimental_FileDependenciesList_dispose(CXFileDependenciesList *Deps); + +/** + * Object encapsulating instance of a dependency scanner service. + * + * The dependency scanner service is a global instance that owns the + * global cache and other global state that's shared between the dependency + * scanner workers. The service APIs are thread safe. + * + * The service aims to provide a consistent view of file content throughout + * its lifetime. A client that wants to see changes to file content should + * create a new service at the time. For example, a build system might use + * one service for each build. + * + * TODO: Consider using DirectoryWatcher to get notified about file changes + * and adding an API that allows clients to invalidate changed files. This + * could allow a build system to reuse a single service between builds. + */ +typedef struct CXOpaqueDependencyScannerService *CXDependencyScannerService; + +/** + * The mode to report module dependencies in. + */ +typedef enum { + /** + * Flatten all module dependencies. This reports the full transitive set of + * header and module map dependencies needed to do an implicit module build. + */ + CXDependencyMode_Flat, + + /** + * Report the full module graph. This reports only the direct dependencies of + * each file, and calls a callback for each module that is discovered. + */ + CXDependencyMode_Full, +} CXDependencyMode; + +/** + * Options used to construct a \c CXDependencyScannerService. + */ +typedef struct CXOpaqueDependencyScannerServiceOptions + *CXDependencyScannerServiceOptions; + +/** + * Creates a default set of service options. + * Must be disposed with \c + * clang_experimental_DependencyScannerServiceOptions_dispose. + */ +CINDEX_LINKAGE CXDependencyScannerServiceOptions +clang_experimental_DependencyScannerServiceOptions_create(void); + +/** + * Dispose of a \c CXDependencyScannerServiceOptions object. + */ +CINDEX_LINKAGE void clang_experimental_DependencyScannerServiceOptions_dispose( + CXDependencyScannerServiceOptions); + +/** + * Specify a \c CXDependencyMode in the given options. + */ +CINDEX_LINKAGE void +clang_experimental_DependencyScannerServiceOptions_setDependencyMode( + CXDependencyScannerServiceOptions Opts, CXDependencyMode Mode); + +/** + * Specify the object store and action cache databases in the given options. + * With this set, the scanner will produce cached commands. + */ +CINDEX_LINKAGE void +clang_experimental_DependencyScannerServiceOptions_setCASDatabases( + CXDependencyScannerServiceOptions Opts, CXCASDatabases); + +/** + * Specify the specific CAS options for the scanner to use for the produced + * compiler arguments. + */ +CINDEX_LINKAGE void +clang_experimental_DependencyScannerServiceOptions_setCASOptions( + CXDependencyScannerServiceOptions Opts, CXCASOptions); + +/** + * Set the working directory optimization option. + * The dependency scanner service option Opts will indicate to the scanner that + * the current working directory can or cannot be ignored when computing the + * pcms' context hashes. The scanner will then determine if it is safe to + * optimize each module and act accordingly. + * + * \param Value If it is non zero, the option is on. Otherwise the + * option is off. + */ +CINDEX_LINKAGE void +clang_experimental_DependencyScannerServiceOptions_setCWDOptimization( + CXDependencyScannerServiceOptions Opts, int Value); + +/** + * Specify a \c CXCASObjectStore in the given options. If an object store and + * action cache are available, the scanner will produce cached commands. + * Deprecated, use + * \p clang_experimental_DependencyScannerServiceOptions_setCASDatabases() + * instead. + */ +CINDEX_DEPRECATED CINDEX_LINKAGE void +clang_experimental_DependencyScannerServiceOptions_setObjectStore( + CXDependencyScannerServiceOptions Opts, CXCASObjectStore CAS); + +/** + * Specify a \c CXCASActionCache in the given options. If an object store and + * action cache are available, the scanner will produce cached commands. + * Deprecated, use + * \p clang_experimental_DependencyScannerServiceOptions_setCASDatabases() + * instead. + */ +CINDEX_DEPRECATED CINDEX_LINKAGE void +clang_experimental_DependencyScannerServiceOptions_setActionCache( + CXDependencyScannerServiceOptions Opts, CXCASActionCache Cache); + +/** + * See \c clang_experimental_DependencyScannerService_create_v1. + */ +CINDEX_LINKAGE CXDependencyScannerService +clang_experimental_DependencyScannerService_create_v0(CXDependencyMode Format); + +/** + * Create a \c CXDependencyScannerService object. + * Must be disposed with \c clang_DependencyScannerService_dispose(). + */ +CINDEX_LINKAGE CXDependencyScannerService +clang_experimental_DependencyScannerService_create_v1( + CXDependencyScannerServiceOptions Opts); + +/** + * Dispose of a \c CXDependencyScannerService object. + * + * The service object must be disposed of after the workers are disposed of. + */ +CINDEX_LINKAGE void clang_experimental_DependencyScannerService_dispose_v0( + CXDependencyScannerService); + +/** + * Object encapsulating instance of a dependency scanner worker. + * + * The dependency scanner workers are expected to be used in separate worker + * threads. An individual worker is not thread safe. + * + * Operations on a worker are not thread-safe and should only be used from a + * single thread at a time. They are intended to be used by a single dedicated + * thread in a thread pool, but they are not inherently pinned to a thread. + */ +typedef struct CXOpaqueDependencyScannerWorker *CXDependencyScannerWorker; + +/** + * Create a \c CXDependencyScannerWorker object. + * Must be disposed with + * \c clang_experimental_DependencyScannerWorker_dispose_v0(). + */ +CINDEX_LINKAGE CXDependencyScannerWorker + clang_experimental_DependencyScannerWorker_create_v0( + CXDependencyScannerService); + +CINDEX_LINKAGE void clang_experimental_DependencyScannerWorker_dispose_v0( + CXDependencyScannerWorker); + +/** + * A callback that is called whenever a module is discovered when in + * \c CXDependencyMode_Full mode. + * + * \param Context the context that was passed to + * \c clang_experimental_DependencyScannerWorker_getFileDependencies_vX. + * \param MDS the list of discovered modules. Must be freed by calling + * \c clang_experimental_ModuleDependencySet_dispose. + */ +typedef void CXModuleDiscoveredCallback(void *Context, + CXModuleDependencySet *MDS); + +/** + * A callback that is called to determine the paths of output files for each + * module dependency. The ModuleFile (pcm) path mapping is mandatory. + * + * \param Context the MLOContext that was passed to + * \c clang_experimental_DependencyScannerWorker_getFileDependencies_vX. + * \param ModuleName the name of the dependent module. + * \param ContextHash the context hash of the dependent module. + * See \c CXModuleDependency::ContextHash. + & \param OutputKind the kind of module output to lookup. + * \param[out] Output the output path(s) or name, whose total size must be <= + * \p MaxLen. In the case of multiple outputs of the same + * kind, this can be a null-separated list. + * \param MaxLen the maximum size of Output. + * + * \returns the actual length of Output. If the return value is > \p MaxLen, + * the callback will be repeated with a larger buffer. + */ +typedef size_t CXModuleLookupOutputCallback(void *Context, + const char *ModuleName, + const char *ContextHash, + CXOutputKind OutputKind, + char *Output, size_t MaxLen); + +/** + * Deprecated, use \c clang_experimental_DependencyScannerWorker_getDepGraph. + * + * See \c clang_experimental_DependencyScannerWorker_getFileDependencies_v4. + */ +CINDEX_LINKAGE CXFileDependencies * +clang_experimental_DependencyScannerWorker_getFileDependencies_v3( + CXDependencyScannerWorker Worker, int argc, const char *const *argv, + const char *ModuleName, const char *WorkingDirectory, void *MDCContext, + CXModuleDiscoveredCallback *MDC, void *MLOContext, + CXModuleLookupOutputCallback *MLO, unsigned Options, CXString *error); + +/** + * Deprecated, use \c clang_experimental_DependencyScannerWorker_getDepGraph. + * + * Calculates the list of file dependencies for a particular compiler + * invocation. + * + * \param argc the number of compiler invocation arguments (including argv[0]). + * \param argv the compiler driver invocation arguments (including argv[0]). + * \param ModuleName If non-null, the dependencies of the named module are + * returned. Otherwise, the dependencies of the whole + * translation unit are returned. + * \param WorkingDirectory the directory in which the invocation runs. + * \param MDCContext the context that will be passed to \c MDC each time it is + * called. + * \param MDC a callback that is called whenever a new module is discovered. + * This may receive the same module on different workers. This should + * be NULL if + * \c clang_experimental_DependencyScannerService_create_v0 was + * called with \c CXDependencyMode_Flat. This callback will be called + * on the same thread that called this function. + * \param MLOContext the context that will be passed to \c MLO each time it is + * called. + * \param MLO a callback that is called to determine the paths of output files + * for each module dependency. This may receive the same module on + * different workers. This should be NULL if + * \c clang_experimental_DependencyScannerService_create_v0 was + * called with \c CXDependencyMode_Flat. This callback will be called + * on the same thread that called this function. + * \param Options reserved for future use, always pass 0. + * \param [out] Out A non-NULL pointer to store the resulting dependencies. The + * output must be freed by calling + * \c clang_experimental_FileDependenciesList_dispose. + * \param [out] error the error string to pass back to client (if any). + * + * \returns \c CXError_Success on success; otherwise a non-zero \c CXErrorCode + * indicating the kind of error. + */ +CINDEX_LINKAGE enum CXErrorCode +clang_experimental_DependencyScannerWorker_getFileDependencies_v4( + CXDependencyScannerWorker Worker, int argc, const char *const *argv, + const char *ModuleName, const char *WorkingDirectory, void *MDCContext, + CXModuleDiscoveredCallback *MDC, void *MLOContext, + CXModuleLookupOutputCallback *MLO, unsigned Options, + CXFileDependenciesList **Out, CXString *error); + +/** + * Output of \c clang_experimental_DependencyScannerWorker_getDepGraph. + */ +typedef struct CXOpaqueDepGraph *CXDepGraph; + +/** + * An individual module dependency that is part of an overall compilation + * \c CXDepGraph. + */ +typedef struct CXOpaqueDepGraphModule *CXDepGraphModule; + +/** + * An individual command-line invocation that is part of an overall compilation + * \c CXDepGraph. + */ +typedef struct CXOpaqueDepGraphTUCommand *CXDepGraphTUCommand; + +/** + * Settings to use for the + * \c clang_experimental_DependencyScannerWorker_getDepGraph action. + */ +typedef struct CXOpaqueDependencyScannerWorkerScanSettings + *CXDependencyScannerWorkerScanSettings; + +/** + * Creates a set of settings for + * \c clang_experimental_DependencyScannerWorker_getDepGraph action. + * Must be disposed with + * \c clang_experimental_DependencyScannerWorkerScanSettings_dispose. + * Memory for settings is not copied. Any provided pointers must be valid until + * the call to \c clang_experimental_DependencyScannerWorker_getDepGraph. + * + * \param argc the number of compiler invocation arguments (including argv[0]). + * \param argv the compiler driver invocation arguments (including argv[0]). + * \param ModuleName If non-null, the dependencies of the named module are + * returned. Otherwise, the dependencies of the whole + * translation unit are returned. + * \param WorkingDirectory the directory in which the invocation runs. + * \param MLOContext the context that will be passed to \c MLO each time it is + * called. + * \param MLO a callback that is called to determine the paths of output files + * for each module dependency. This may receive the same module on + * different workers. This should be NULL if + * \c clang_experimental_DependencyScannerService_create_v1 was + * called with \c CXDependencyMode_Flat. This callback will be called + * on the same thread that called \c + * clang_experimental_DependencyScannerWorker_getDepGraph. + */ +CINDEX_LINKAGE CXDependencyScannerWorkerScanSettings +clang_experimental_DependencyScannerWorkerScanSettings_create( + int argc, const char *const *argv, const char *ModuleName, + const char *WorkingDirectory, void *MLOContext, + CXModuleLookupOutputCallback *MLO); + +/** + * Dispose of a \c CXDependencyScannerWorkerScanSettings object. + */ +CINDEX_LINKAGE void + clang_experimental_DependencyScannerWorkerScanSettings_dispose( + CXDependencyScannerWorkerScanSettings); + +/** + * Produces the dependency graph for a particular compiler invocation. + * + * \param Settings object created via + * \c clang_experimental_DependencyScannerWorkerScanSettings_create. + * \param [out] Out A non-NULL pointer to store the resulting dependencies. The + * output must be freed by calling + * \c clang_experimental_DepGraph_dispose. + * + * \returns \c CXError_Success on success; otherwise a non-zero \c CXErrorCode + * indicating the kind of error. When returning \c CXError_Failure there will + * be a \c CXDepGraph object on \p Out that can be used to get diagnostics via + * \c clang_experimental_DepGraph_getDiagnostics. + */ +CINDEX_LINKAGE enum CXErrorCode +clang_experimental_DependencyScannerWorker_getDepGraph( + CXDependencyScannerWorker, CXDependencyScannerWorkerScanSettings Settings, + CXDepGraph *Out); + +/** + * Dispose of a \c CXDepGraph object. + */ +CINDEX_LINKAGE void clang_experimental_DepGraph_dispose(CXDepGraph); + +/** + * \returns the number of \c CXDepGraphModule objects in the graph. + */ +CINDEX_LINKAGE size_t clang_experimental_DepGraph_getNumModules(CXDepGraph); + +/** + * \returns the \c CXDepGraphModule object at the given \p Index. + * + * The \c CXDepGraphModule object is only valid to use while \c CXDepGraph is + * valid. Must be disposed with \c clang_experimental_DepGraphModule_dispose. + */ +CINDEX_LINKAGE CXDepGraphModule +clang_experimental_DepGraph_getModule(CXDepGraph, size_t Index); + +/** + * \returns the \c CXDepGraphModule object at the given \p Index in + * a topologically sorted list. + * + * The \c CXDepGraphModule object is only valid to use while \c CXDepGraph is + * valid. Must be disposed with \c clang_experimental_DepGraphModule_dispose. + */ +CINDEX_LINKAGE CXDepGraphModule +clang_experimental_DepGraph_getModuleTopological(CXDepGraph, size_t Index); + +CINDEX_LINKAGE void clang_experimental_DepGraphModule_dispose(CXDepGraphModule); + +/** + * \returns the name of the module. This may include `:` for C++20 module + * partitions, or a header-name for C++20 header units. + * + * The string is only valid to use while the \c CXDepGraphModule object is + * valid. + */ +CINDEX_LINKAGE +const char *clang_experimental_DepGraphModule_getName(CXDepGraphModule); + +/** + * \returns the context hash of a module represents the set of compiler options + * that may make one version of a module incompatible from another. This + * includes things like language mode, predefined macros, header search paths, + * etc... + * + * Modules with the same name but a different \c ContextHash should be treated + * as separate modules for the purpose of a build. + * + * The string is only valid to use while the \c CXDepGraphModule object is + * valid. + */ +CINDEX_LINKAGE +const char *clang_experimental_DepGraphModule_getContextHash(CXDepGraphModule); + +/** + * \returns the path to the modulemap file which defines this module. If there's + * no modulemap (e.g. for a C++ module) returns \c NULL. + * + * This can be used to explicitly build this module. This file will + * additionally appear in \c FileDeps as a dependency. + * + * The string is only valid to use while the \c CXDepGraphModule object is + * valid. + */ +CINDEX_LINKAGE const char * + clang_experimental_DepGraphModule_getModuleMapPath(CXDepGraphModule); + +/** + * \returns the list of files which this module directly depends on. + * + * If any of these change then the module needs to be rebuilt. + * + * The strings are only valid to use while the \c CXDepGraphModule object is + * valid. + */ +CINDEX_LINKAGE CXCStringArray + clang_experimental_DepGraphModule_getFileDeps(CXDepGraphModule); + +/** + * \returns the list of modules which this module direct depends on. + * + * This does include the context hash. The format is + * `<module-name>:<context-hash>` + * + * The strings are only valid to use while the \c CXDepGraphModule object is + * valid. + */ +CINDEX_LINKAGE CXCStringArray + clang_experimental_DepGraphModule_getModuleDeps(CXDepGraphModule); + +/** + * \returns the canonical command line to build this module. + * + * The strings are only valid to use while the \c CXDepGraphModule object is + * valid. + */ +CINDEX_LINKAGE CXCStringArray + clang_experimental_DepGraphModule_getBuildArguments(CXDepGraphModule); + +/** + * @returns the CASID of the include-tree for this module, if any. + * + * The string is only valid to use while the \c CXDepGraphModule object is + * valid. + */ +CINDEX_LINKAGE const char * + clang_experimental_DepGraphModule_getIncludeTreeID(CXDepGraphModule); + +/** + * \returns the \c ActionCache key for this module, if any. + * + * The string is only valid to use while the \c CXDepGraphModule object is + * valid. + */ +CINDEX_LINKAGE +const char *clang_experimental_DepGraphModule_getCacheKey(CXDepGraphModule); + +/** + * \returns 1 if the scanner ignores the current working directory when + * computing the module's context hash. Otherwise returns 0. + */ +CINDEX_LINKAGE +int clang_experimental_DepGraphModule_isCWDIgnored(CXDepGraphModule); + +/** + * \returns the number \c CXDepGraphTUCommand objects in the graph. + */ +CINDEX_LINKAGE size_t clang_experimental_DepGraph_getNumTUCommands(CXDepGraph); + +/** + * \returns the \c CXDepGraphTUCommand object at the given \p Index. + * + * The \c CXDepGraphTUCommand object is only valid to use while \c CXDepGraph is + * valid. Must be disposed with \c clang_experimental_DepGraphTUCommand_dispose. + */ +CINDEX_LINKAGE CXDepGraphTUCommand +clang_experimental_DepGraph_getTUCommand(CXDepGraph, size_t Index); + +/** + * Dispose of a \c CXDepGraphTUCommand object. + */ +CINDEX_LINKAGE void + clang_experimental_DepGraphTUCommand_dispose(CXDepGraphTUCommand); + +/** + * \returns the executable name for the command. + * + * The string is only valid to use while the \c CXDepGraphTUCommand object is + * valid. + */ +CINDEX_LINKAGE const char * + clang_experimental_DepGraphTUCommand_getExecutable(CXDepGraphTUCommand); + +/** + * \returns the canonical command line to build this translation unit. + * + * The strings are only valid to use while the \c CXDepGraphTUCommand object is + * valid. + */ +CINDEX_LINKAGE CXCStringArray + clang_experimental_DepGraphTUCommand_getBuildArguments(CXDepGraphTUCommand); + +/** + * \returns the \c ActionCache key for this translation unit, if any. + * + * The string is only valid to use while the \c CXDepGraphTUCommand object is + * valid. + */ +CINDEX_LINKAGE const char * + clang_experimental_DepGraphTUCommand_getCacheKey(CXDepGraphTUCommand); + +/** + * \returns the list of files which this translation unit directly depends on. + * + * The strings are only valid to use while the \c CXDepGraph object is valid. + */ +CINDEX_LINKAGE +CXCStringArray clang_experimental_DepGraph_getTUFileDeps(CXDepGraph); + +/** + * \returns the list of modules which this translation unit direct depends on. + * + * This does include the context hash. The format is + * `<module-name>:<context-hash>` + * + * The strings are only valid to use while the \c CXDepGraph object is valid. + */ +CINDEX_LINKAGE +CXCStringArray clang_experimental_DepGraph_getTUModuleDeps(CXDepGraph); + +/** + * @returns the CASID of the include-tree for this TU, if any. + * + * The string is only valid to use while the \c CXDepGraph object is valid. + */ +CINDEX_LINKAGE +const char *clang_experimental_DepGraph_getTUIncludeTreeID(CXDepGraph); + +/** + * \returns the context hash of the C++20 module this translation unit exports. + * + * If the translation unit is not a module then this is empty. + * + * The string is only valid to use while the \c CXDepGraph object is valid. + */ +CINDEX_LINKAGE +const char *clang_experimental_DepGraph_getTUContextHash(CXDepGraph); + +/** + * \returns The diagnostics emitted during scanning. These must be always freed + * by calling \c clang_disposeDiagnosticSet. + */ +CINDEX_LINKAGE +CXDiagnosticSet clang_experimental_DepGraph_getDiagnostics(CXDepGraph); + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif // LLVM_CLANG_C_DEPENDENCIES_H diff --git a/clang/include/clang-c/Driver.h b/clang/include/clang-c/Driver.h new file mode 100644 index 0000000000000..54dd1b0b043a3 --- /dev/null +++ b/clang/include/clang-c/Driver.h @@ -0,0 +1,78 @@ +/*==-- clang-c/Driver.h - A C Interface for the Clang Driver ------*- C -*-===*\ +|* *| +|* Part of the LLVM Project, under the Apache License v2.0 with LLVM *| +|* Exceptions. *| +|* See https://llvm.org/LICENSE.txt for license information. *| +|* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception *| +|* *| +|*===----------------------------------------------------------------------===*| +|* *| +|* This header provides a C API for extracting information from the clang *| +|* driver. *| +|* *| +\*===----------------------------------------------------------------------===*/ + + +#ifndef CLANG_CLANG_C_DRIVER +#define CLANG_CLANG_C_DRIVER + +#include "clang-c/Index.h" +#include "clang-c/Platform.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Contains the command line arguments for an external action. Same format as + * provided to main. + */ +typedef struct { + /* Number of arguments in ArgV */ + int ArgC; + /* Null terminated array of pointers to null terminated argument strings */ + const char **ArgV; +} CXExternalAction; + +/** + * Contains the list of external actions clang would invoke. + */ +typedef struct { + int Count; + CXExternalAction **Actions; +} CXExternalActionList; + +/** + * Get the external actions that the clang driver will invoke for the given + * command line. + * + * \param ArgC number of arguments in \p ArgV. + * \param ArgV array of null terminated arguments. Doesn't need to be null + * terminated. + * \param Environment must be null. + * \param WorkingDirectory a null terminated path to the working directory to + * use for this invocation. `nullptr` to use the current working directory of + * the process. + * \param OutDiags will be set to a \c CXDiagnosticSet if there's an error. + * Must be freed by calling \c clang_disposeDiagnosticSet . + * \returns A pointer to a \c CXExternalActionList on success, null on failure. + * The returned \c CXExternalActionList must be freed by calling + * \c clang_Driver_ExternalActionList_dispose . + */ +CINDEX_LINKAGE CXExternalActionList * +clang_Driver_getExternalActionsForCommand_v0(int ArgC, const char **ArgV, + const char **Environment, + const char *WorkingDirectory, + CXDiagnosticSet *OutDiags); + +/** + * Deallocate a \c CXExternalActionList + */ +CINDEX_LINKAGE void +clang_Driver_ExternalActionList_dispose(CXExternalActionList *EAL); + +#ifdef __cplusplus +} +#endif + +#endif // CLANG_CLANG_C_DRIVER diff --git a/clang/include/clang-c/Index.h b/clang/include/clang-c/Index.h index 115f5ab090f96..4e52d5886bf3c 100644 --- a/clang/include/clang-c/Index.h +++ b/clang/include/clang-c/Index.h @@ -34,7 +34,7 @@ * compatible, thus CINDEX_VERSION_MAJOR is expected to remain stable. */ #define CINDEX_VERSION_MAJOR 0 -#define CINDEX_VERSION_MINOR 64 +#define CINDEX_VERSION_MINOR 65 #define CINDEX_VERSION_ENCODE(major, minor) (((major)*10000) + ((minor)*1)) @@ -1691,7 +1691,17 @@ enum CXCursorKind { */ CXCursor_PackIndexingExpr = 156, - CXCursor_LastExpr = CXCursor_PackIndexingExpr, + /* TO_UPSTREAM(BoundsSafety) ON */ + /** + * BoundsSafety '__builtin_unsafe_forge_bidi_indexable(addr, size)' or + * '__builtin_forge_single(addr) + */ + CXCursor_ForgePtrExpr = 198, + + CXCursor_GetBoundExpr = 199, + + CXCursor_LastExpr = CXCursor_GetBoundExpr, + /* TO_UPSTREAM(BoundsSafety) OFF */ /* Statements */ CXCursor_FirstStmt = 200, diff --git a/clang/include/clang-c/Refactor.h b/clang/include/clang-c/Refactor.h new file mode 100644 index 0000000000000..983a652481d9e --- /dev/null +++ b/clang/include/clang-c/Refactor.h @@ -0,0 +1,1313 @@ +/*==-- clang-c/Refactor.h - Refactoring Public C Interface --------*- C -*-===*\ +|* *| +|* Part of the LLVM Project, under the Apache License v2.0 with LLVM *| +|* Exceptions. *| +|* See https://llvm.org/LICENSE.txt for license information. *| +|* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception *| +|* *| +|*===----------------------------------------------------------------------===*| +|* *| +|* This header provides a public inferface to a Clang library for performing *| +|* refactoring actions on projects without exposing the full Clang C++ API. *| +|* *| +\*===----------------------------------------------------------------------===*/ + +#ifndef LLVM_CLANG_C_REFACTOR_H +#define LLVM_CLANG_C_REFACTOR_H + +#include "clang-c/Index.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \defgroup CINDEX_REFACTOR Refactoring options. + * + * @{ + */ + +/** + * \brief The refactoring options that can be specified for each refactoring + * action. + */ +enum CXRefactoringOption { + /** + * \brief The refactoring actions like 'rename' will avoid looking for + * occurrences of the renamed symbol in comments if this option is enabled. + */ + CXRefactorOption_AvoidTextualMatches = 1 +}; + +/** + * \brief Opaque pointer representing a set of options that can be given to + * a refactoring action. + */ +typedef void *CXRefactoringOptionSet; + +/** + * \brief Returns a new option set. + */ +CINDEX_LINKAGE +CXRefactoringOptionSet clang_RefactoringOptionSet_create(void); + +/** + * \brief Parses and returns a new option set or NULL if the given string is + * invalid. + */ +CINDEX_LINKAGE +CXRefactoringOptionSet +clang_RefactoringOptionSet_createFromString(const char *String); + +/** + * \brief Adds a new option to the given refactoring option set. + */ +CINDEX_LINKAGE +void clang_RefactoringOptionSet_add(CXRefactoringOptionSet Set, + enum CXRefactoringOption Option); + +/** + * \brief Converts the given refactoring option set to a string value. + */ +CINDEX_LINKAGE +CXString clang_RefactoringOptionSet_toString(CXRefactoringOptionSet Set); + +/** + * \brief Free the given option set. + * + * Option sets should be freed by this function only when they were created + * using the \c clang_RefactoringOptionSet_create* methods. + */ +CINDEX_LINKAGE +void clang_RefactoringOptionSet_dispose(CXRefactoringOptionSet Set); + +/** + * @} + */ + +/** + * \defgroup CINDEX_REFACTOR Refactoring actions. + * + * @{ + */ + +/** + * \brief The refactoring actions that can be performed by libclang. + */ +enum CXRefactoringActionType { + /** + * \brief The 'rename' refactoring action. + */ + CXRefactor_Rename = 0, + + /** + * \brief The local 'rename' refactoring action. + */ + CXRefactor_Rename_Local = 1, + + /** + * \brief The 'extract' refactoring action extracts source code into a + * new function. + */ + CXRefactor_Extract = 2, + + /** + * \brief The sub-action of 'extract' that extracts source code into a new + * method. + */ + CXRefactor_Extract_Method = 3, + + /** + * \brief The action that converts an if/else constructs to a switch block. + */ + CXRefactor_IfSwitchConversion = 4, + + /** + * \brief The action that wraps an Objective-C string literal in an + * NSLocalizedString macro. + */ + CXRefactor_LocalizeObjCStringLiteral = 5, + + /** + * \brief The action that adds missing switch cases to an switch over an enum. + */ + CXRefactor_FillInEnumSwitchCases = 6, + + /** + * \brief The action that adds missing protocol methods to an Objective-C + * class. + */ + CXRefactor_FillInMissingProtocolStubs = 7, + + /** + * \brief The action that extracts an expression that's repeated in a function + * into a new variable. + */ + CXRefactor_ExtractRepeatedExpressionIntoVariable = 8, + + /** + * \brief The action that adds missing abstract class method overrides to a + * class. + */ + CXRefactor_FillInMissingMethodStubsFromAbstractClasses = 9, + + /** + * \brief The action that generates dummy method definitions for method + * declarations without respective definitions. + */ + CXRefactor_ImplementDeclaredMethods = 10, + + /** + * \brief The sub-action of 'extract' that extracts source expression into a + * new variable. + */ + CXRefactor_Extract_Expression = 11, +}; + +/** + * \brief Return the name of the given refactoring action. + */ +CINDEX_LINKAGE +CXString +clang_RefactoringActionType_getName(enum CXRefactoringActionType Action); + +/** + * \brief A set of refactoring actions that can be performed at some specific + * location in a source file. + * + * The actions in the action set are ordered by their priority: most important + * actions are placed before the less important ones. + */ +typedef struct { + const enum CXRefactoringActionType *Actions; + unsigned NumActions; +} CXRefactoringActionSet; + +/** + * \brief Free the given refactoring action set. + */ +CINDEX_LINKAGE void +clang_RefactoringActionSet_dispose(CXRefactoringActionSet *Set); + +typedef struct { + enum CXRefactoringActionType Action; + /** + * \brief The set of diagnostics that describes the reason why this action + * couldn't be initiated. This set of diagnostics is managed by the + * \c CXRefactoringActionSetWithDiagnostics and shouldn't be freed manually. + */ + CXDiagnosticSet Diagnostics; +} CXRefactoringActionWithDiagnostics; + +/** + * \brief A set of refactoring actions that couldn't be initiated at some + * location and their respective diagnostics that describe the reason why + * the initiation failed. + */ +typedef struct { + CXRefactoringActionWithDiagnostics *Actions; + unsigned NumActions; +} CXRefactoringActionSetWithDiagnostics; + +/** + * \brief Free the given refactoring action set with diagnostics. + */ +CINDEX_LINKAGE void clang_RefactoringActionSetWithDiagnostics_dispose( + CXRefactoringActionSetWithDiagnostics *Set); + +/** + * \brief Find the set of refactoring actions that can be performed at the given + * location. + * + * This function examines the AST around the given source range and creates a + * \c CXRefactoringActionSet that contains all of the actions that can be + * performed in the given source range. + * + * \param TU The translation unit which contains the given source range. + * + * \param Location The location at which the refactoring action will be + * performed. + * + * \param SelectionRange The range in which the AST should be checked. Usually + * corresponds to the selection range or location of the cursor in the editor. + * Can be a null range. + * + * \param Options The optional refactoring options that might influence the way + * the search is performed. + * + * \param[out] OutSet A non-NULL pointer to store the created + * \c CXRefactoringActionSet. + * + * \returns Zero on success, CXError_RefactoringActionUnavailable when + * there are no actions available in the given range, or an error code + * otherwise. + */ +CINDEX_LINKAGE +enum CXErrorCode +clang_Refactoring_findActionsAt(CXTranslationUnit TU, CXSourceLocation Location, + CXSourceRange SelectionRange, + CXRefactoringOptionSet Options, + CXRefactoringActionSet *OutSet); + +/** + * \brief Find the set of refactoring actions that can be performed at the given + * location. + * + * This function examines the AST around the given source range and creates a + * \c CXRefactoringActionSet that contains all of the actions that can be + * performed in the given source range. It also creates a + * \c CXRefactoringActionSetWithDiagnostics that might describe the reason why + * some refactoring actions are not be available. + * + * \param TU The translation unit which contains the given source range. + * + * \param Location The location at which the refactoring action will be + * performed. + * + * \param SelectionRange The range in which the AST should be checked. Usually + * corresponds to the selection range or location of the cursor in the editor. + * Can be a null range. + * + * \param Options The optional refactoring options that might influence the way + * the search is performed. + * + * \param[out] OutSet A non-NULL pointer to store the created + * \c CXRefactoringActionSet. + * + * \param[out] OutFailureSet An optional pointer to store the created + * \c CXRefactoringActionSetWithDiagnostics that describes the failures reasons + * for some of the refactoring actions. + * + * \returns Zero on success, CXError_RefactoringActionUnavailable when + * there are no actions available in the given range, or an error code + * otherwise. + */ +CINDEX_LINKAGE +enum CXErrorCode clang_Refactoring_findActionsWithInitiationFailureDiagnosicsAt( + CXTranslationUnit TU, CXSourceLocation Location, + CXSourceRange SelectionRange, CXRefactoringOptionSet Options, + CXRefactoringActionSet *OutSet, + CXRefactoringActionSetWithDiagnostics *OutFailureSet); + +/** + * @} + */ + +/** + * \defgroup CINDEX_REFACTOR_INITIATE Refactoring initiation + * + * @{ + */ + +/** + * \brief Opaque pointer representing the initiated refactoring action. + */ +typedef void *CXRefactoringAction; + +/** + * \brief Free the given refactoring action. + * + * The refactoring action should be freed before the initiation and/or + * implementation translation units. + */ +CINDEX_LINKAGE void clang_RefactoringAction_dispose(CXRefactoringAction Action); + +/** + * \brief Return the source range that's associated with the initiated + * refactoring action. + * + * The returned source range covers the source that will be modified by the + * given refactoring action. If the action has no associated source range, + * then this function will return a null \c CXSourceRange. + */ +CINDEX_LINKAGE CXSourceRange +clang_RefactoringAction_getSourceRangeOfInterest(CXRefactoringAction Action); + +/** + * \brief Return the type of the initiated action, which might be different + * to the type of the requested action. For an operation 'rename', the action + * could actually initiate the local 'rename' operation. + */ +CINDEX_LINKAGE +enum CXRefactoringActionType +clang_RefactoringAction_getInitiatedActionType(CXRefactoringAction Action); + +/** + * \brief Return a non-zero value when the refactoring action requires access + * to an additional translation unit that contains an implementation of some + * declaration. + */ +// TODO: Remove (this is no longer needed due to refactoring continuations). +CINDEX_LINKAGE +int clang_RefactoringAction_requiresImplementationTU( + CXRefactoringAction Action); + +/** + * \brief Return a USR that corresponds to the declaration whose implementation + * is required in order for the given refactoring action to work correctly. + */ +// TODO: Remove (this is no longer needed due to refactoring continuations). +CINDEX_LINKAGE +CXString clang_RefactoringAction_getUSRThatRequiresImplementationTU( + CXRefactoringAction Action); + +/** + * \brief Set the translation unit that contains the declaration whose + * implementation is required for the given refactoring action to work + * correctly. + */ +// TODO: Remove (this is no longer needed due to refactoring continuations). +CINDEX_LINKAGE +enum CXErrorCode +clang_RefactoringAction_addImplementationTU(CXRefactoringAction Action, + CXTranslationUnit TU); + +/** + * \brief A refactoring candidate determines on which piece of source code the + * action should be applied. + * + * Most refactoring actions have just one candidate, but some actions, like + * 'Extract' can produce multiple candidates. + * + * The candidates are managed by the refactoring action, and their description + * string doesn't need to be freed manually. + */ +typedef struct { CXString Description; } CXRefactoringCandidate; + +/** + * \brief A set of refactoring candidates on which the previously initiatied + * refactoring action can be performed. + * + * The candidates in the candidate set are ordered by their priority: the + * ones that are more likely to be selected are placed before the other ones. + * + * A non-empty refactoring candidate set always has more than one refactoring + * candidate, because when a refactoring action has just one candidate, + * \c clang_RefactoringAction_getRefactoringCandidates will return an empty + * candidate set. + */ +typedef struct { + const CXRefactoringCandidate *Candidates; + unsigned NumCandidates; +} CXRefactoringCandidateSet; + +/** + * \brief Returns the given action's refactoring candidates. + * + * The resulting refactoring candidate set will be empty when the given \c + * CXRefactoringAction has just one refactoring candidate. + * + * \param Action A previously initiated \c CXRefactoringAction. + * + * \param[out] OutRefactoringCandidateSet An pointer to store the action's + * refactoring candidate set. + * + * \returns Zero on success, or an error code otherwise. + */ +CINDEX_LINKAGE +enum CXErrorCode clang_RefactoringAction_getRefactoringCandidates( + CXRefactoringAction Action, + CXRefactoringCandidateSet *OutRefactoringCandidateSet); + +/** + * \brief Tells the given refactoring action that it has to perform the + * operation on the refactoring candidate that's located at \p Index in the \c + * CXRefactoringCandidateSet. + */ +CINDEX_LINKAGE +enum CXErrorCode +clang_RefactoringAction_selectRefactoringCandidate(CXRefactoringAction Action, + unsigned Index); + +// TODO: Remove. +CINDEX_LINKAGE +enum CXErrorCode clang_Refactoring_initiateActionAt( + CXTranslationUnit TU, CXSourceLocation Location, + CXSourceRange SelectionRange, enum CXRefactoringActionType ActionType, + CXRefactoringOptionSet Options, CXRefactoringAction *OutAction, + CXString *OutFailureReason); + +/** + * \brief Initiate a specific refactoring action at the given location. + * + * This function initiates an \p ActionType refactoring action when it can + * be initiated at the given location and creates a \c CXRefactoringAction + * action that will allow the control. + * + * \param TU The translation unit in which the action should be initiated. + * + * \param Location The location at which the refactoring action will be + * performed. + * + * \param SelectionRange The range in which the AST should be checked. Usually + * corresponds to the selection range or location of the cursor in the editor. + * Can be a null range. + * + * \param ActionType The type of action that should be initiated. + * + * \param Options The optional refactoring options that might have an influence + * on the initiation process. + * + * \param[out] OutAction A non-NULL pointer to store the created + * \c CXRefactoringAction. + * + * \param[out] OutDiagnostics An optional pointer to store any diagnostics that + * describe why the action wasn't initiated. + * + * \returns Zero on success, CXError_RefactoringActionUnavailable when + * the given refactoring action can't be performed at the given location, or an + * error code otherwise. + */ +CINDEX_LINKAGE +enum CXErrorCode clang_Refactoring_initiateAction( + CXTranslationUnit TU, CXSourceLocation Location, + CXSourceRange SelectionRange, enum CXRefactoringActionType ActionType, + CXRefactoringOptionSet Options, CXRefactoringAction *OutAction, + CXDiagnosticSet *OutDiagnostics); + +/** + * \brief Initiate a specific refactoring action on a particular declaration. + * + * This function searches for the declaration that corresponds to \p DeclUSR + * and initiates an \p ActionType a refactoring action on that declaration + * if possible. + * + * \param TU The translation unit in which the declaration is defined. + * + * \param DeclUSR The USR that corresponds to the declaration of interest. + * + * \param ActionType The type of action that should be initiated. + * + * \param Options The optional refactoring options that might have an influence + * on the initiation process. + * + * \param[out] OutAction A non-NULL pointer to store the created + * \c CXRefactoringAction. + * + * \returns Zero on success, CXError_RefactoringActionUnavailable when + * the given refactoring action can't be performed on the found declaration, or + * an error code otherwise. + */ +// TODO: Remove (not needed). +CINDEX_LINKAGE +enum CXErrorCode clang_Refactoring_initiateActionOnDecl( + CXTranslationUnit TU, const char *DeclUSR, + enum CXRefactoringActionType ActionType, CXRefactoringOptionSet Options, + CXRefactoringAction *OutAction, CXString *OutFailureReason); + +/** + * @} + */ + +/** + * \defgroup CINDEX_REFACTOR_REPLACEMENT Refactoring replacement + * + * @{ + */ + +/** + * \brief A source location in a single file that is independent of \c + * CXTranslationUnit. + */ +typedef struct { unsigned Line, Column; } CXFileLocation; + +/** + * \brief A source range in a single file that is independent of \c + * CXTranslationUnit. + */ +typedef struct { CXFileLocation Begin, End; } CXFileRange; + +// TODO: Remove +typedef struct { + CXFileRange Range; + CXString ReplacementString; +} CXRefactoringReplacement_Old; + +// TODO: Remove +typedef struct { + CXString Filename; + const CXRefactoringReplacement_Old *Replacements; + unsigned NumReplacements; +} CXRefactoringFileReplacementSet_Old; + +// TODO: Remove +typedef struct { + const CXRefactoringFileReplacementSet_Old *FileReplacementSets; + unsigned NumFileReplacementSets; +} CXRefactoringReplacements_Old; + +/** + * \brief Identifies a character range in the source code of a single file that + * should be replaced with the replacement string. + * + * Replacements are managed by the result of a specific refactoring action, + * like \c CXRenamingResult, and are invalidated when the refactoring result is + * destroyed. + */ +typedef struct { + CXFileRange Range; + CXString ReplacementString; + void *AssociatedData; +} CXRefactoringReplacement; + +/** +* \brief A set of refactoring replacements that are applicable to a certain + * file. + */ +typedef struct { + CXString Filename; + const CXRefactoringReplacement *Replacements; + unsigned NumReplacements; +} CXRefactoringFileReplacementSet; + +/** + * \brief A set of refactoring replacements that have been produced by a + * refactoring operation. + * + * The refactoring replacements depend on \c CXRefactoringResult, and can't be + * used after the refactoring result is freed. + */ +typedef struct { + const CXRefactoringFileReplacementSet *FileReplacementSets; + unsigned NumFileReplacementSets; +} CXRefactoringReplacements; + +/** + * @} + */ + +/** + * \defgroup CINDEX_SYMBOL_OPERATION Symbol-based refactoring operation + * (e.g. Rename). + * + * @{ + */ + +/** + * \brief The type of a symbol occurrence. + * + * The occurrence kind determines if an occurrence can be renamed automatically + * or if the user has to make the decision whether or not this occurrence + * should be renamed. + */ +enum CXSymbolOccurrenceKind { + /** + * \brief This occurrence is an exact match and can be renamed automatically. + */ + CXSymbolOccurrence_MatchingSymbol = 0, + + /** + * \brief This is an occurrence of a matching selector. It can't be renamed + * automatically unless the indexer proves that this selector refers only + * to the declarations that correspond to the renamed symbol. + */ + CXSymbolOccurrence_MatchingSelector = 1, + + /** + * \brief This is an occurrence of an implicit property that uses the + * renamed method. + */ + CXSymbolOccurrence_MatchingImplicitProperty = 2, + + /** + * \brief This is an occurrence of an symbol name in a comment. + */ + CXSymbolOccurrence_MatchingCommentString = 3, + + /** + * \brief This is an occurrence of an symbol name in a documentation comment. + */ + CXSymbolOccurrence_MatchingDocCommentString = 4, + + /** + * \brief This is an occurrence of an symbol name in a filename in an inclusion + * directive. + */ + CXSymbolOccurrence_MatchingFilename = 5, + + /** + * \brief This is an occurrence of an symbol name in a string literal. + */ + CXSymbolOccurrence_MatchingStringLiteral = 6, + + /** + * \brief This is an occurrence of a symbol name that belongs to the extracted + * declaration. Note: this occurrence can be in two replacements as we might + * extract an out-of-line method that will be both declared and defined. + */ + CXSymbolOccurrence_ExtractedDeclaration = 100, + + /** + * \brief This is an occurrence of a symbol name that references the extracted + * declaration. + */ + CXSymbolOccurrence_ExtractedDeclaration_Reference = 101, +}; + +// TODO: Remove +typedef struct { + const CXRefactoringReplacement_Old *Replacements; + unsigned ReplacementCount; + enum CXSymbolOccurrenceKind Kind; + /** + * Whether or not this occurrence is inside a macro. When this is true, the + * replacements of the occurrence contain just a single empty replacement that + * points to the location of the macro expansion. + */ + int IsMacroExpansion; +} CXRenamedSymbolOccurrence; + +/** + * \brief An occurrence of a symbol. + * + * Contains the source ranges that represent the pieces of the name of the + * symbol. The occurrences are managed by \c CXRenamingResult, and are + * invalidated when \c CXRenamingResult is destroyed. + */ +typedef struct { + const CXFileRange *NamePieces; + unsigned NumNamePieces; + enum CXSymbolOccurrenceKind Kind; + /** + * Whether or not this occurrence is inside a macro. When this is true, the + * replacements of the occurrence contain just a single empty replacement that + * points to the location of the macro expansion. + */ + int IsMacroExpansion; + unsigned SymbolIndex; +} CXSymbolOccurrence; + +// TODO: Remove +typedef struct { + CXString Filename; + const CXRenamedSymbolOccurrence *Occurrences; + unsigned NumOccurrences; +} CXFileRenamingResult; // TODO: Remove + +/** +* \brief A set of symbol occurrences that occur in a single file. + */ +typedef struct { + CXString Filename; + /** + * The set of occurrences for each symbol of interest. + */ + const CXSymbolOccurrence *Occurrences; + unsigned NumOccurrences; +} CXSymbolOccurrencesInFile; + +/** + * \brief Opaque pointer representing all of the renames that should take place + * in a single translation unit. + * + * The result of a renaming action is indepedent from \c CXRenamingAction, and + * remains valid after \c CXRenamingAction is destroyed. + */ +typedef void *CXRenamingResult; + +/** + * \brief Opaque pointer representing all of the symbol occurrences from a + * single TU/file. + * + * The result of a symbol search occurrence search operation is indepedent from + * \c CXRefactoringAction, and remains valid after \c CXRefactoringAction is + * destroyed. + */ +typedef void *CXSymbolOccurrencesResult; + +/** + * \brief Find the cursor that's being renamed at the given location. + * + * \param TU The translation unit in which the cursor is present. + * + * \param Location The location at which the refactoring action will be + * performed. + * + * \param SelectionRange The range in which the AST should be checked. Usually + * corresponds to the selection range or location of the cursor in the editor. + * Can be a null range. + * + * \returns Zero on success, CXError_RefactoringActionUnavailable when + * there's no suitable cursor at the given location, or an error code otherwise. + */ +CINDEX_LINKAGE +enum CXErrorCode clang_Refactoring_findRenamedCursor( + CXTranslationUnit TU, CXSourceLocation Location, + CXSourceRange SelectionRange, CXCursor *OutCursor); + +/** + * \brief Initiates a renaming operation on a previously initiated refactoring + * action. + * + * The initiation process finds the symbols that have to be renamed for a + * previously initiated \c CXRefactor_Rename refactoring action. + * + * \returns Zero on success, or an error code otherwise. + */ +// TODO: Remove +CINDEX_LINKAGE +enum CXErrorCode +clang_Refactoring_initiateRenamingOperation(CXRefactoringAction Action); + +/** + * \brief Set the new name of the renamed symbol in the given \c + * RenamingAction. + * + * \returns Zero on success, CXError_RefactoringNameInvalid when the new name + * isn't a valid identifier, CXError_RefactoringNameSizeMismatch when the new + * name has an incorrect number of pieces or a different error code otherwise. + */ +// TODO: Remove +CINDEX_LINKAGE +enum CXErrorCode clang_RenamingOperation_setNewName(CXRefactoringAction Action, + const char *NewName); + +/** + * \brief Return the number of symbols that are renamed by the given renaming + * action. + * + * A renaming action typically works on just one symbol. However, there are + * certain language constructs that require work with more than one symbol in + * order for them to be renamed correctly. Property declarations in Objective-C + * are the perfect example: in addition to the actual property, the action has + * to rename the corresponding getters and setters, as well as the backing ivar. + */ +// TODO: Remove +CINDEX_LINKAGE +unsigned clang_RenamingOperation_getNumSymbols(CXRefactoringAction Action); + +/** + * \brief Return the USR of the declaration that was found for the symbol at the + * given \p Index in the given renaming action. + */ +// TODO: Remove +CINDEX_LINKAGE +CXString clang_RenamingOperation_getUSRForSymbol(CXRefactoringAction Action, + unsigned Index); + +// TODO: Remove +CINDEX_LINKAGE +CXRenamingResult clang_Refactoring_findRenamedOccurrencesInPrimaryTUs( + CXRefactoringAction Action, const char *const *CommandLineArgs, + int NumCommandLineArgs, struct CXUnsavedFile *UnsavedFiles, + unsigned NumUnsavedFiles); + +/** + * \brief Find all of the occurrences of the symbol that is being searched for + * by the given refactoring action in the translation unit that was used to + * initiate the refactoring action. + * + * This function searches for all of the \c CXSymbolOccurrence in the + * translation units that are referenced by the given \c CXRefactoringAction by + * iterating through the AST of the each translation unit. The occurrences that + * are found don't have to be from the main file in the translation unit, they + * can be from files included in that translation unit. + * + * \param Action The \c CXRefactoringAction operation that was inititated by + * \c clang_Refactoring_initiateActionAt(). + * + * \param CommandLineArgs The command-line arguments that would be + * passed to the \c clang executable if it were being invoked out-of-process. + * + * \param NumCommandLineArgs The number of command-line arguments in + * \c CommandLineArgs. + * + * \param UnsavedFiles the files that have not yet been saved to disk + * but may be required for parsing, including the contents of + * those files. The contents and name of these files (as specified by + * CXUnsavedFile) are copied when necessary, so the client only needs to + * guarantee their validity until the call to this function returns. + * + * \param NumUnsavedFiles the number of unsaved file entries in \p + * UnsavedFiles. + * + * \returns If successful, a new \c CXSymbolOccurrencesResult structure + * containing the occurrences of the symbol in the initiation translation unit, + * which should eventually be freed with \c clang_SymbolOccurrences_dispose(). + * If symbol search fails, returns NULL. + */ +CINDEX_LINKAGE +CXSymbolOccurrencesResult clang_Refactoring_findSymbolOccurrencesInInitiationTU( + CXRefactoringAction Action, const char *const *CommandLineArgs, + int NumCommandLineArgs, struct CXUnsavedFile *UnsavedFiles, + unsigned NumUnsavedFiles); + +// TODO: Remove +typedef struct { + CXFileLocation Location; + /** + * The kind of the declaration/expression that was indexed at this location. + * This is particularly important for Objective-C selectors. The refactoring + * engine requires the following cursor kinds for the following indexed + * occurrences: + * - ObjC method declaration: CXCursor_ObjC(Instance/Class)MethodDecl + * - ObjC method message send: CXCursor_ObjCMessageExpr + * Other occurrences can use any other cursor cursor kinds. + */ + enum CXCursorKind CursorKind; +} CXRenamedIndexedSymbolLocation; + +// TODO: Remove +typedef struct { + /** + * An array of occurrences that represent indexed occurrences of a symbol. + * It's valid to pass-in no indexed locations, the refactoring engine will + * just perform textual search in that case. + */ + const CXRenamedIndexedSymbolLocation *IndexedLocations; + unsigned IndexedLocationCount; + /** + * The kind of the declaration that is being renamed. + * This is particularly important for Objective-C selectors. The refactoring + * engine requires the following cursor kinds for the following renamed + * declaration: + * - ObjC methods: CXCursor_ObjC(Instance/Class)MethodDecl + * Other declarations can use any other cursor cursor kinds. + */ + enum CXCursorKind CursorKind; + const char *Name; + const char *NewName; +} CXRenamedIndexedSymbol; + +// TODO: Remove +CINDEX_LINKAGE +enum CXErrorCode clang_Refactoring_findRenamedOccurrencesInIndexedFile( + const CXRenamedIndexedSymbol *Symbols, unsigned NumSymbols, CXIndex CIdx, + const char *Filename, const char *const *CommandLineArgs, + int NumCommandLineArgs, struct CXUnsavedFile *UnsavedFiles, + unsigned NumUnsavedFiles, CXRefactoringOptionSet Options, + CXRenamingResult *OutResult); + +/** + * \brief A location of an already known occurrence of a symbol. + * + * Used for rename-indexed operation where the renaming is performed on an + * already indexed source file. + */ +typedef struct { + CXFileLocation Location; + /** + * The kind of the declaration/expression that was indexed at this location. + * This is particularly important for Objective-C selectors. The refactoring + * engine requires the following cursor kinds for the following indexed + * occurrences: + * - ObjC method declaration: CXCursor_ObjC(Instance/Class)MethodDecl + * - ObjC method message send: CXCursor_ObjCMessageExpr + * - filename in an #include: CXCursor_InclusionDirective + * Other occurrences can use any other cursor cursor kinds. + */ + enum CXCursorKind CursorKind; +} CXIndexedSymbolLocation; + +/** + * \brief A symbol that should be found the an indexer symbol search operation. + * + * Used for rename-indexed operation where the renaming is performed on an + * already indexed source file. + */ +typedef struct { + /** + * An array of occurrences that represent indexed occurrences of a symbol. + * It's valid to pass-in no indexed locations, the refactoring engine will + * just perform textual search in that case. + */ + const CXIndexedSymbolLocation *IndexedLocations; + unsigned IndexedLocationCount; + /** + * The kind of the declaration that is being renamed. + * This is particularly important for Objective-C selectors. The refactoring + * engine requires the following cursor kinds for the following renamed + * declaration: + * - ObjC methods: CXCursor_ObjC(Instance/Class)MethodDecl + * - ObjC class: CXCursor_ObjCInterfaceDecl + * Other declarations can use any other cursor cursor kinds. + */ + enum CXCursorKind CursorKind; + /** + * The name of the symbol. Objective-C selector names should be specified + * using the ':' separator for selector pieces. + */ + const char *Name; +} CXIndexedSymbol; + +/** + * \brief Find all of the occurrences of a symbol in an indexed file. + * + * This function searches for all of the \c CXIndexedSymbol in the + * given file by inspecting the source code at the given indexed locations. + * + * The indexed operations are thread-safe and can be performed concurrently. + * + * \param Symbols The information about the symbols that includes the locations + * for a symbol in the file as determined by the indexer. + * + * \param NumSymbols The number of symbols in \p Symbols. + * + * \param CIdx The index object with which the translation unit will be + * associated. + * + * \param Filename The name of the source file that contains the given + * \p Locations. + * + * \param CommandLineArgs The command-line arguments that would be + * passed to the \c clang executable if it were being invoked out-of-process. + * These command-line options will be parsed and will affect how the translation + * unit is parsed. + * + * \param NumCommandLineArgs The number of command-line arguments in + * \c CommandLineArgs. + * + * \param UnsavedFiles the files that have not yet been saved to disk + * but may be required for parsing, including the contents of + * those files. The contents and name of these files (as specified by + * CXUnsavedFile) are copied when necessary, so the client only needs to + * guarantee their validity until the call to this function returns. + * + * \param NumUnsavedFiles the number of unsaved file entries in \p + * UnsavedFiles. + * + * \param Options The optional refactoring options that might have an influence + * on the initiation process. + * + * \param[out] OutResult A non-NULL pointer to store the created + * \c CXSymbolOccurrencesResult. + * + * \returns Zero on success, or a different error code otherwise. + */ +CINDEX_LINKAGE +enum CXErrorCode clang_Refactoring_findSymbolOccurrencesInIndexedFile( + const CXIndexedSymbol *Symbols, unsigned NumSymbols, CXIndex CIdx, + const char *Filename, const char *const *CommandLineArgs, + int NumCommandLineArgs, struct CXUnsavedFile *UnsavedFiles, + unsigned NumUnsavedFiles, CXRefactoringOptionSet Options, + CXSymbolOccurrencesResult *OutResult); + +// TODO: Remove +CINDEX_LINKAGE +unsigned clang_RenamingResult_getNumModifiedFiles(CXRenamingResult Result); + +// TODO: Remove +CINDEX_LINKAGE +void clang_RenamingResult_getResultForFile(CXRenamingResult Result, + unsigned FileIndex, + CXFileRenamingResult *OutResult); + +// TODO: Remove +CINDEX_LINKAGE +void clang_RenamingResult_dispose(CXRenamingResult Result); + +/** + * \brief Return the number of files that have occurrences of the specific + * symbol. + */ +CINDEX_LINKAGE +unsigned clang_SymbolOccurrences_getNumFiles(CXSymbolOccurrencesResult Result); + +/** + * \brief Return the set of symbol occurrences in a single file. + * + * The resulting \c CXSymbolOccurrencesInFile is managed by the + * \c CXSymbolOccurrencesResult and doesn't have to be disposed of manually. + */ +CINDEX_LINKAGE +void clang_SymbolOccurrences_getOccurrencesForFile( + CXSymbolOccurrencesResult Result, unsigned FileIndex, + CXSymbolOccurrencesInFile *OutResult); + +// TODO: Support refactoring continuations for \c CXSymbolOccurrencesResult, +// e.g. for function parameter name rename. + +/** + * \brief Free the given symbol occurrences result. + */ +CINDEX_LINKAGE +void clang_SymbolOccurrences_dispose(CXSymbolOccurrencesResult Result); + +/** + * @} + */ + +/** + * \defgroup CINDEX_REFACTOR_PERFORM Performing refactoring operations. + * + * @{ + */ + +/** + * \brief Opaque pointer representing the results of the refactoring operation. + * + * The result of a refactoring action depends on the \c CXRefactoringAction, and + * is invalidated after \c CXRefactoringAction is destroyed. + */ +typedef void *CXRefactoringResult; + +/** + * \brief Opaque pointer representing a refactoring continuation. + * + * Refactoring continuations allow refactoring operations to run in external + * AST units with some results that were obtained after querying the indexer. + * + * The refactoring continuation is not dependent on the \c CXRefactoringAction + * or \c CXRefactoringResult. It does depend on the initiation + * \c CXTranslationUnit initially, but that dependency can be terminated. + */ +typedef void *CXRefactoringContinuation; + +/** + * \brief Opaque pointer representing a query to the indexer. + */ +typedef void *CXIndexerQuery; + +/** + * \brief Performs the previously initiated refactoring operation. + * + * This function executes the refactoring operation which produces a set of + * candidate source replacements that can be applied to the source files. + * + * \param Action The refactoring action. + * + * \param CommandLineArgs The command-line arguments that would be + * passed to the \c clang executable if it were being invoked out-of-process. + * These command-line options will be parsed and will affect how the translation + * unit is parsed. + * + * \param NumCommandLineArgs The number of command-line arguments in + * \c CommandLineArgs. + * + * \param UnsavedFiles the files that have not yet been saved to disk + * but may be required for parsing, including the contents of + * those files. The contents and name of these files (as specified by + * CXUnsavedFile) are copied when necessary, so the client only needs to + * guarantee their validity until the call to this function returns. + * + * \param NumUnsavedFiles the number of unsaved file entries in \p + * UnsavedFiles. + * + * \param Options The optional refactoring options that might have an influence + * on the way the particular action will be performed. + * + * \param[out] OutFailureReason An optional pointer to store a message that + * describes why the action wasn't performed. + * + * \returns If successful, a new \c CXRefactoringResult structure containing the + * source replacement candidates, which should eventually be freed with + * \c clang_RefactoringResult_dispose(). If the refactoring operation fails, + * returns NULL. + */ +CINDEX_LINKAGE +CXRefactoringResult clang_Refactoring_performOperation( + CXRefactoringAction Action, const char *const *CommandLineArgs, + int NumCommandLineArgs, struct CXUnsavedFile *UnsavedFiles, + unsigned NumUnsavedFiles, CXRefactoringOptionSet Options, + CXString *OutFailureReason); + +// TODO: Remove. This is the deprecated API. +CINDEX_LINKAGE +void clang_RefactoringResult_getReplacements( + CXRefactoringResult Result, CXRefactoringReplacements_Old *OutReplacements); + +/** + * \brief Return the set of refactoring source replacements. + * + * The resulting \c CXRefactoringReplacements are managed by the + * \c CXRefactoringResult and don't have to be disposed of manually. + */ +CINDEX_LINKAGE +CXRefactoringReplacements +clang_RefactoringResult_getSourceReplacements(CXRefactoringResult Result); + +/** + * \brief Represents a set of symbol occurrences that are associated with a + * single refactoring replacement. + * + * The symbol occurrences depend on \c CXRefactoringResult, and can't be + * used after the refactoring result is freed. + */ +typedef struct { + const CXSymbolOccurrence *AssociatedSymbolOccurrences; + unsigned NumAssociatedSymbolOccurrences; +} CXRefactoringReplacementAssociatedSymbolOccurrences; + +/** + * \brief Return the set of symbol occurrences that are associated with the + * given \p Replacement. + */ +CINDEX_LINKAGE +CXRefactoringReplacementAssociatedSymbolOccurrences +clang_RefactoringReplacement_getAssociatedSymbolOccurrences( + CXRefactoringReplacement Replacement); + +/** + * \brief Returns the refactoring continuation associated with this result, or + * NULL if this result has no refactoring continuation. + */ +CINDEX_LINKAGE +CXRefactoringContinuation +clang_RefactoringResult_getContinuation(CXRefactoringResult Result); + +/** + * \brief Free the given refactoring result. + */ +CINDEX_LINKAGE +void clang_RefactoringResult_dispose(CXRefactoringResult Result); + +/** + * \brief Load the indexer query results from a YAML string. + * + * Mainly used for testing. + */ +CINDEX_LINKAGE +enum CXErrorCode +clang_RefactoringContinuation_loadSerializedIndexerQueryResults( + CXRefactoringContinuation Continuation, const char *Source); + +/** + * \brief Return the number of indexer queries that a refactoring continuation + * has. + */ +CINDEX_LINKAGE +unsigned clang_RefactoringContinuation_getNumIndexerQueries( + CXRefactoringContinuation Continuation); + +/** + * \brief Return the indexer query at index \p Index. + */ +CINDEX_LINKAGE +CXIndexerQuery clang_RefactoringContinuation_getIndexerQuery( + CXRefactoringContinuation Continuation, unsigned Index); + +/** + * \brief Verify that the all of the indexer queries are satisfied by the + * continuation. + * + * \returns Null if all of the queries are satisfied an no errors have been + * reported, or a set of diagnostics that describes why the continuation should + * not be run. + */ +CINDEX_LINKAGE +CXDiagnosticSet clang_RefactoringContinuation_verifyBeforeFinalizing( + CXRefactoringContinuation Continuation); + +/** + * \brief Terminate the connection between the initiation TU and the refactoring + * continuation. + * + * The continuation converts all the TU-specific state to TU-independent state. + * The indexer queries that are associate with this continuation are also + * invalidated. + */ +CINDEX_LINKAGE +void clang_RefactoringContinuation_finalizeEvaluationInInitationTU( + CXRefactoringContinuation Continuation); + +/** + * \brief Continue performing the previously initiated and performed refactoring + * operation in the given translation unit \p TU. + */ +CINDEX_LINKAGE +CXRefactoringResult clang_RefactoringContinuation_continueOperationInTU( + CXRefactoringContinuation Continuation, CXTranslationUnit TU, + CXString *OutFailureReason); + +/** + * \brief Free the given refactoring continuation. + */ +CINDEX_LINKAGE +void clang_RefactoringContinuation_dispose( + CXRefactoringContinuation Continuation); + +/** + * @} + */ + +/** + * \defgroup CINDEX_REFACTOR_INDEXER_QUERY Indexer Queries. + * + * @{ + */ + +/** + * \brief The types of indexer queries. + */ +enum CXIndexerQueryKind { + CXIndexerQuery_Unknown = 0, + + /** + * \brief The indexer should find the file that contains/should contain the + * implementation of some declaration. + * A file result is expected. + */ + CXIndexerQuery_Decl_FileThatShouldImplement = 1, + + /** + * \brief The indexer should determine if the some declaration is defined. + * An integer result is expected. + */ + CXIndexerQuery_Decl_IsDefined = 2, +}; + +/** + * \brief Return the kind of the indexer query \p Query. + */ +CINDEX_LINKAGE +enum CXIndexerQueryKind clang_IndexerQuery_getKind(CXIndexerQuery Query); + +/** + * \brief Return the number of cursors that the \p Query has. + */ +CINDEX_LINKAGE +unsigned clang_IndexerQuery_getNumCursors(CXIndexerQuery Query); + +/** + * \brief Return the cursor at the given \p CursorIndex. + */ +CINDEX_LINKAGE +CXCursor clang_IndexerQuery_getCursor(CXIndexerQuery Query, + unsigned CursorIndex); + +/** + * \brief The action that the indexer should take after evaluating the query. + */ +enum CXIndexerQueryAction { + /** + * \brief This result requires no further action. + */ + CXIndexerQueryAction_None = 0, + + /** + * \brief The indexer should run the \c CXRefactoringContinuaton in a + * translation unit that contains this file. + */ + CXIndexerQueryAction_RunContinuationInTUThatHasThisFile = 1, +}; + +/** + * \brief Consumes an integer/boolean query result. + */ +CINDEX_LINKAGE +enum CXIndexerQueryAction +clang_IndexerQuery_consumeIntResult(CXIndexerQuery Query, unsigned CursorIndex, + int Value); + +/** + * \brief Consumes a filename query result. + * + * This function may return + * \c CXIndexerQueryAction_RunContinuationInTUThatHasThisFile which + * should tell the indexer that it has to run the refactoring continuation in + * the TU that contains this file. + */ +CINDEX_LINKAGE +enum CXIndexerQueryAction +clang_IndexerQuery_consumeFileResult(CXIndexerQuery Query, unsigned CursorIndex, + const char *Filename); + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* LLVM_CLANG_C_REFACTOR_H */ diff --git a/clang/include/clang/APINotes/APINotesReader.h b/clang/include/clang/APINotes/APINotesReader.h index 03657352c49a5..00513258c263c 100644 --- a/clang/include/clang/APINotes/APINotesReader.h +++ b/clang/include/clang/APINotes/APINotesReader.h @@ -46,6 +46,13 @@ class APINotesReader { APINotesReader(const APINotesReader &) = delete; APINotesReader &operator=(const APINotesReader &) = delete; + /// Retrieve the name of the module for which this reader is providing API + /// notes. + llvm::StringRef getModuleName() const; + + /// Retrieve the module options + ModuleOptions getModuleOptions() const; + /// Captures the completed versioned information for a particular part of /// API notes, including both unversioned API notes and each versioned API /// note for that particular entity. @@ -141,6 +148,13 @@ class APINotesReader { ObjCSelectorRef Selector, bool IsInstanceMethod); + /// Look for information regarding the given field of a C struct. + /// + /// \param Name The name of the field. + /// + /// \returns information about the field, if known. + VersionedInfo<FieldInfo> lookupField(ContextID CtxID, llvm::StringRef Name); + /// Look for information regarding the given C++ method in the given C++ tag /// context. /// diff --git a/clang/include/clang/APINotes/APINotesWriter.h b/clang/include/clang/APINotes/APINotesWriter.h index e0fe5eacef725..6a5e59f96869e 100644 --- a/clang/include/clang/APINotes/APINotesWriter.h +++ b/clang/include/clang/APINotes/APINotesWriter.h @@ -86,6 +86,14 @@ class APINotesWriter { void addCXXMethod(ContextID CtxID, llvm::StringRef Name, const CXXMethodInfo &Info, llvm::VersionTuple SwiftVersion); + /// Add information about a specific C record field. + /// + /// \param CtxID The context in which this field resides, i.e. a C/C++ tag. + /// \param Name The name of the field. + /// \param Info Information about this field. + void addField(ContextID CtxID, llvm::StringRef Name, const FieldInfo &Info, + llvm::VersionTuple SwiftVersion); + /// Add information about a global variable. /// /// \param Name The name of this global variable. @@ -122,6 +130,9 @@ class APINotesWriter { /// \param Info Information about this typedef. void addTypedef(std::optional<Context> Ctx, llvm::StringRef Name, const TypedefInfo &Info, llvm::VersionTuple SwiftVersion); + + /// Add module options + void addModuleOptions(ModuleOptions opts); }; } // namespace api_notes } // namespace clang diff --git a/clang/include/clang/APINotes/Types.h b/clang/include/clang/APINotes/Types.h index c8e5e4df25d17..8b38be6b3b932 100644 --- a/clang/include/clang/APINotes/Types.h +++ b/clang/include/clang/APINotes/Types.h @@ -300,6 +300,72 @@ inline bool operator!=(const ContextInfo &LHS, const ContextInfo &RHS) { return !(LHS == RHS); } +/* TO_UPSTREAM(BoundsSafety) ON */ +class BoundsSafetyInfo { +public: + enum class BoundsSafetyKind { + CountedBy = 0, + CountedByOrNull, + SizedBy, + SizedByOrNull, + EndedBy, + }; + +private: + /// Whether this property has been audited for nullability. + LLVM_PREFERRED_TYPE(bool) + unsigned KindAudited : 1; + + /// The kind of nullability for this property. Only valid if the nullability + /// has been audited. + LLVM_PREFERRED_TYPE(BoundsSafetyKind) + unsigned Kind : 3; + + LLVM_PREFERRED_TYPE(bool) + unsigned LevelAudited : 1; + + unsigned Level : 3; + +public: + std::string ExternalBounds; + + BoundsSafetyInfo() + : KindAudited(false), Kind(0), LevelAudited(false), Level(0), + ExternalBounds("") {} + + std::optional<BoundsSafetyKind> getKind() const { + return KindAudited ? std::optional<BoundsSafetyKind>( + static_cast<BoundsSafetyKind>(Kind)) + : std::nullopt; + } + + void setKindAudited(BoundsSafetyKind kind) { + KindAudited = true; + Kind = static_cast<unsigned>(kind); + } + + std::optional<unsigned> getLevel() const { + return LevelAudited ? std::optional<unsigned>(Level) : std::nullopt; + } + + void setLevelAudited(unsigned level) { + LevelAudited = true; + Level = level; + } + + friend bool operator==(const BoundsSafetyInfo &, const BoundsSafetyInfo &); + + LLVM_DUMP_METHOD void dump(llvm::raw_ostream &OS) const; +}; + +inline bool operator==(const BoundsSafetyInfo &LHS, + const BoundsSafetyInfo &RHS) { + return LHS.KindAudited == RHS.KindAudited && LHS.Kind == RHS.Kind && + LHS.LevelAudited == RHS.LevelAudited && LHS.Level == RHS.Level && + LHS.ExternalBounds == RHS.ExternalBounds; +} +/* TO_UPSTREAM(BoundsSafety) OFF */ + /// API notes for a variable/property. class VariableInfo : public CommonEntityInfo { /// Whether this property has been audited for nullability. @@ -425,25 +491,46 @@ class ParamInfo : public VariableInfo { LLVM_PREFERRED_TYPE(bool) unsigned NoEscape : 1; + /// Whether lifetimebound was specified. + LLVM_PREFERRED_TYPE(bool) + unsigned LifetimeboundSpecified : 1; + + /// Whether the this parameter has the 'lifetimebound' attribute. + LLVM_PREFERRED_TYPE(bool) + unsigned Lifetimebound : 1; + /// A biased RetainCountConventionKind, where 0 means "unspecified". /// /// Only relevant for out-parameters. unsigned RawRetainCountConvention : 3; public: + /* TO_UPSTREAM(BoundsSafety) ON */ + std::optional<BoundsSafetyInfo> BoundsSafety; + /* TO_UPSTREAM(BoundsSafety) OFF */ + ParamInfo() - : NoEscapeSpecified(false), NoEscape(false), RawRetainCountConvention() {} + : NoEscapeSpecified(false), NoEscape(false), + LifetimeboundSpecified(false), Lifetimebound(false), + RawRetainCountConvention(), BoundsSafety(std::nullopt) {} std::optional<bool> isNoEscape() const { - if (!NoEscapeSpecified) - return std::nullopt; - return NoEscape; + return NoEscapeSpecified ? std::optional<bool>(NoEscape) : std::nullopt; } void setNoEscape(std::optional<bool> Value) { NoEscapeSpecified = Value.has_value(); NoEscape = Value.value_or(false); } + std::optional<bool> isLifetimebound() const { + return LifetimeboundSpecified ? std::optional<bool>(Lifetimebound) + : std::nullopt; + } + void setLifetimebound(std::optional<bool> Value) { + LifetimeboundSpecified = Value.has_value(); + Lifetimebound = Value.value_or(false); + } + std::optional<RetainCountConventionKind> getRetainCountConvention() const { if (!RawRetainCountConvention) return std::nullopt; @@ -463,9 +550,19 @@ class ParamInfo : public VariableInfo { NoEscape = RHS.NoEscape; } + if (!LifetimeboundSpecified && RHS.LifetimeboundSpecified) { + LifetimeboundSpecified = true; + Lifetimebound = RHS.Lifetimebound; + } + if (!RawRetainCountConvention) RawRetainCountConvention = RHS.RawRetainCountConvention; + /* TO_UPSTREAM(BoundsSafety) ON */ + if (!BoundsSafety) + BoundsSafety = RHS.BoundsSafety; + /* TO_UPSTREAM(BoundsSafety) OFF */ + return *this; } @@ -478,7 +575,12 @@ inline bool operator==(const ParamInfo &LHS, const ParamInfo &RHS) { return static_cast<const VariableInfo &>(LHS) == RHS && LHS.NoEscapeSpecified == RHS.NoEscapeSpecified && LHS.NoEscape == RHS.NoEscape && - LHS.RawRetainCountConvention == RHS.RawRetainCountConvention; + LHS.LifetimeboundSpecified == RHS.LifetimeboundSpecified && + LHS.Lifetimebound == RHS.Lifetimebound && + LHS.RawRetainCountConvention == RHS.RawRetainCountConvention && + /* TO_UPSTREAM(BoundsSafety) ON */ + LHS.BoundsSafety == RHS.BoundsSafety; + /* TO_UPSTREAM(BoundsSafety) OFF */ } inline bool operator!=(const ParamInfo &LHS, const ParamInfo &RHS) { @@ -518,6 +620,9 @@ class FunctionInfo : public CommonEntityInfo { /// The result type of this function, as a C type. std::string ResultType; + /// Ownership convention for return value + std::string SwiftReturnOwnership; + /// The function parameters. std::vector<ParamInfo> Params; @@ -598,7 +703,8 @@ inline bool operator==(const FunctionInfo &LHS, const FunctionInfo &RHS) { LHS.NumAdjustedNullable == RHS.NumAdjustedNullable && LHS.NullabilityPayload == RHS.NullabilityPayload && LHS.ResultType == RHS.ResultType && LHS.Params == RHS.Params && - LHS.RawRetainCountConvention == RHS.RawRetainCountConvention; + LHS.RawRetainCountConvention == RHS.RawRetainCountConvention && + LHS.SwiftReturnOwnership == RHS.SwiftReturnOwnership; } inline bool operator!=(const FunctionInfo &LHS, const FunctionInfo &RHS) { @@ -616,6 +722,8 @@ class ObjCMethodInfo : public FunctionInfo { LLVM_PREFERRED_TYPE(bool) unsigned RequiredInit : 1; + std::optional<ParamInfo> Self; + ObjCMethodInfo() : DesignatedInit(false), RequiredInit(false) {} friend bool operator==(const ObjCMethodInfo &, const ObjCMethodInfo &); @@ -632,12 +740,15 @@ class ObjCMethodInfo : public FunctionInfo { } LLVM_DUMP_METHOD void dump(llvm::raw_ostream &OS); + + void mergePropInfoIntoGetter(const ObjCPropertyInfo &pInfo); + void mergePropInfoIntoSetter(const ObjCPropertyInfo &pInfo); }; inline bool operator==(const ObjCMethodInfo &LHS, const ObjCMethodInfo &RHS) { return static_cast<const FunctionInfo &>(LHS) == RHS && LHS.DesignatedInit == RHS.DesignatedInit && - LHS.RequiredInit == RHS.RequiredInit; + LHS.RequiredInit == RHS.RequiredInit && LHS.Self == RHS.Self; } inline bool operator!=(const ObjCMethodInfo &LHS, const ObjCMethodInfo &RHS) { @@ -656,12 +767,30 @@ class GlobalFunctionInfo : public FunctionInfo { GlobalFunctionInfo() {} }; +/// Describes API notes data for a C/C++ record field. +class FieldInfo : public VariableInfo { +public: + FieldInfo() {} +}; + /// Describes API notes data for a C++ method. class CXXMethodInfo : public FunctionInfo { public: CXXMethodInfo() {} + + std::optional<ParamInfo> This; + + LLVM_DUMP_METHOD void dump(llvm::raw_ostream &OS); }; +inline bool operator==(const CXXMethodInfo &LHS, const CXXMethodInfo &RHS) { + return static_cast<const FunctionInfo &>(LHS) == RHS && LHS.This == RHS.This; +} + +inline bool operator!=(const CXXMethodInfo &LHS, const CXXMethodInfo &RHS) { + return !(LHS == RHS); +} + /// Describes API notes data for an enumerator. class EnumConstantInfo : public CommonEntityInfo { public: @@ -680,16 +809,25 @@ class TagInfo : public CommonTypeInfo { LLVM_PREFERRED_TYPE(bool) unsigned SwiftCopyable : 1; + LLVM_PREFERRED_TYPE(bool) + unsigned SwiftEscapableSpecified : 1; + LLVM_PREFERRED_TYPE(bool) + unsigned SwiftEscapable : 1; + public: std::optional<std::string> SwiftImportAs; std::optional<std::string> SwiftRetainOp; std::optional<std::string> SwiftReleaseOp; + /// The Swift protocol that this type should be automatically conformed to. + std::optional<std::string> SwiftConformance; + std::optional<EnumExtensibilityKind> EnumExtensibility; TagInfo() : HasFlagEnum(0), IsFlagEnum(0), SwiftCopyableSpecified(false), - SwiftCopyable(false) {} + SwiftCopyable(false), SwiftEscapableSpecified(false), + SwiftEscapable(false) {} std::optional<bool> isFlagEnum() const { if (HasFlagEnum) @@ -710,6 +848,16 @@ class TagInfo : public CommonTypeInfo { SwiftCopyable = Value.value_or(false); } + std::optional<bool> isSwiftEscapable() const { + return SwiftEscapableSpecified ? std::optional<bool>(SwiftEscapable) + : std::nullopt; + } + + void setSwiftEscapable(std::optional<bool> Value) { + SwiftEscapableSpecified = Value.has_value(); + SwiftEscapable = Value.value_or(false); + } + TagInfo &operator|=(const TagInfo &RHS) { static_cast<CommonTypeInfo &>(*this) |= RHS; @@ -720,6 +868,9 @@ class TagInfo : public CommonTypeInfo { if (!SwiftReleaseOp) SwiftReleaseOp = RHS.SwiftReleaseOp; + if (!SwiftConformance) + SwiftConformance = RHS.SwiftConformance; + if (!HasFlagEnum) setFlagEnum(RHS.isFlagEnum()); @@ -729,6 +880,9 @@ class TagInfo : public CommonTypeInfo { if (!SwiftCopyableSpecified) setSwiftCopyable(RHS.isSwiftCopyable()); + if (!SwiftEscapableSpecified) + setSwiftEscapable(RHS.isSwiftEscapable()); + return *this; } @@ -742,8 +896,10 @@ inline bool operator==(const TagInfo &LHS, const TagInfo &RHS) { LHS.SwiftImportAs == RHS.SwiftImportAs && LHS.SwiftRetainOp == RHS.SwiftRetainOp && LHS.SwiftReleaseOp == RHS.SwiftReleaseOp && + LHS.SwiftConformance == RHS.SwiftConformance && LHS.isFlagEnum() == RHS.isFlagEnum() && LHS.isSwiftCopyable() == RHS.isSwiftCopyable() && + LHS.isSwiftEscapable() == RHS.isSwiftEscapable() && LHS.EnumExtensibility == RHS.EnumExtensibility; } @@ -815,6 +971,11 @@ struct ObjCSelectorRef { unsigned NumArgs; llvm::ArrayRef<llvm::StringRef> Identifiers; }; + +/// Descripts a series of options for a module +struct ModuleOptions { + bool SwiftInferImportAsMember = false; +}; } // namespace api_notes } // namespace clang diff --git a/clang/include/clang/AST/APValue.h b/clang/include/clang/AST/APValue.h index c4206b73b1156..94b5abee1d276 100644 --- a/clang/include/clang/AST/APValue.h +++ b/clang/include/clang/AST/APValue.h @@ -13,6 +13,7 @@ #ifndef LLVM_CLANG_AST_APVALUE_H #define LLVM_CLANG_AST_APVALUE_H +#include "clang/AST/CharUnits.h" #include "clang/Basic/LLVM.h" #include "llvm/ADT/APFixedPoint.h" #include "llvm/ADT/APFloat.h" @@ -29,7 +30,6 @@ template <typename T> class BasicReaderBase; class AddrLabelExpr; class ASTContext; - class CharUnits; class CXXRecordDecl; class Decl; class DiagnosticBuilder; @@ -88,6 +88,59 @@ class DynamicAllocLValue { static constexpr int NumLowBitsAvailable = 3; }; + +/* TO_UPSTREAM(BoundsSafety) ON*/ +/// Symbolic representation of a size forged pointer +class ForgedPtrLValue { + const ValueDecl *Base; + +public: + ForgedPtrLValue() : Base(nullptr) {} + explicit ForgedPtrLValue(const ValueDecl *Base) : Base(Base) {} + explicit operator bool() const { return Base; } + + const ValueDecl *getBaseValueDecl() const { return Base; } + + void *getOpaqueValue() { return const_cast<ValueDecl *>(Base); } + static ForgedPtrLValue getFromOpaqueValue(void *Value) { + ForgedPtrLValue V; + V.Base = reinterpret_cast<ValueDecl *>(Value); + return V; + } +}; + +/// This represents a pointer union that is either DynamicAllocLValue or +/// ForgedPtrLValue. +/// +/// This is a hack to be able to add ForgedPtrLValue as another pointer member +/// to the pointer union of APValue::LValueBase because it already has the +/// maximum number of members for the available low bits, i.e., 2. +/// DynamicAllocLValue and ValueDecl* have 3 low bits available and thus we can +/// use this one more remaining bit to encode ForgedPtrLValue. +class DynamicAllocOrForgedPtrLValue + : public llvm::PointerUnion<DynamicAllocLValue, ForgedPtrLValue> { + + using BaseTy = llvm::PointerUnion<DynamicAllocLValue, ForgedPtrLValue>; + +public: + DynamicAllocOrForgedPtrLValue() = default; + DynamicAllocOrForgedPtrLValue(DynamicAllocLValue LV) : BaseTy(LV) {} + DynamicAllocOrForgedPtrLValue(ForgedPtrLValue LV) : BaseTy(LV) {} + + explicit operator bool() const { + if (is<DynamicAllocLValue>()) { + return static_cast<bool>(get<DynamicAllocLValue>()); + } + return static_cast<bool>(get<ForgedPtrLValue>()); + } + + static DynamicAllocOrForgedPtrLValue getFromOpaqueValue(void *Value) { + DynamicAllocOrForgedPtrLValue V; + V.Val.setFromOpaqueValue(Value); + return V; + } +}; +/* TO_UPSTREAM(BoundsSafety) OFF*/ } namespace llvm { @@ -113,6 +166,28 @@ template<> struct PointerLikeTypeTraits<clang::DynamicAllocLValue> { static constexpr int NumLowBitsAvailable = clang::DynamicAllocLValue::NumLowBitsAvailable; }; + +/* TO_UPSTREAM(BoundsSafety) ON*/ +template <> struct PointerLikeTypeTraits<clang::ForgedPtrLValue> { + static void *getAsVoidPointer(clang::ForgedPtrLValue V) { + return V.getOpaqueValue(); + } + static clang::ForgedPtrLValue getFromVoidPointer(void *P) { + return clang::ForgedPtrLValue::getFromOpaqueValue(P); + } + static constexpr int NumLowBitsAvailable = 3; +}; + +template <> struct PointerLikeTypeTraits<clang::DynamicAllocOrForgedPtrLValue> { + static void *getAsVoidPointer(clang::DynamicAllocOrForgedPtrLValue V) { + return V.getOpaqueValue(); + } + static clang::DynamicAllocOrForgedPtrLValue getFromVoidPointer(void *P) { + return clang::DynamicAllocOrForgedPtrLValue::getFromOpaqueValue(P); + } + static constexpr int NumLowBitsAvailable = 2; +}; +/* TO_UPSTREAM(BoundsSafety) OFF*/ } namespace clang { @@ -145,28 +220,64 @@ class APValue { class LValueBase { typedef llvm::PointerUnion<const ValueDecl *, const Expr *, TypeInfoLValue, - DynamicAllocLValue> + DynamicAllocOrForgedPtrLValue> PtrTy; + template <class T> + static constexpr bool IsDAOrForgedV = + std::is_same<T, DynamicAllocLValue>::value || + std::is_same<T, ForgedPtrLValue>::value; + + template <class T, class U = T> + using EnableIfDAOrForged = + typename std::enable_if<IsDAOrForgedV<T>, U>::type; + + template <class T, class U = T> + using EnableIfNotDANorForged = + typename std::enable_if<!IsDAOrForgedV<T>, U>::type; + public: LValueBase() : Local{} {} LValueBase(const ValueDecl *P, unsigned I = 0, unsigned V = 0); LValueBase(const Expr *P, unsigned I = 0, unsigned V = 0); static LValueBase getDynamicAlloc(DynamicAllocLValue LV, QualType Type); static LValueBase getTypeInfo(TypeInfoLValue LV, QualType TypeInfo); + static LValueBase getForgedPtr(ForgedPtrLValue LV, QualType Type); void Profile(llvm::FoldingSetNodeID &ID) const; - template <class T> - bool is() const { return Ptr.is<T>(); } + template <class T> EnableIfNotDANorForged<T, bool> is() const { + return Ptr.is<T>(); + } - template <class T> - T get() const { return Ptr.get<T>(); } + template <class T> EnableIfDAOrForged<T, bool> is() const { + if (!Ptr.is<DynamicAllocOrForgedPtrLValue>()) + return false; + return Ptr.get<DynamicAllocOrForgedPtrLValue>().is<T>(); + } - template <class T> - T dyn_cast() const { return Ptr.dyn_cast<T>(); } + template <class T> EnableIfNotDANorForged<T> get() const { + return Ptr.get<T>(); + } + + template <class T> EnableIfDAOrForged<T> get() const { + assert(is<T>() && "Invalid accessor called"); + return Ptr.get<DynamicAllocOrForgedPtrLValue>().get<T>(); + } + + template <class T> EnableIfNotDANorForged<T> dyn_cast() const { + return Ptr.dyn_cast<T>(); + } + + template <class T> EnableIfDAOrForged<T> dyn_cast() const { + if (is<T>()) + return Ptr.get<DynamicAllocOrForgedPtrLValue>().dyn_cast<T>(); + return T(); + } void *getOpaqueValue() const; + // TO_UPSTREAM(BoundsSafety) + const ValueDecl *getValueDecl() const; bool isNull() const; @@ -176,6 +287,7 @@ class APValue { unsigned getVersion() const; QualType getTypeInfoType() const; QualType getDynamicAllocType() const; + QualType getForgedPtrAsArrayType() const; QualType getType() const; @@ -197,6 +309,8 @@ class APValue { void *TypeInfoType; /// The QualType, if this is a DynamicAllocLValue. void *DynamicAllocType; + /// The QualType, if this is a ForgedPtrLValue. + void *ForgedPtrAsArrayType; }; }; @@ -305,10 +419,45 @@ class APValue { }; struct MemberPointerData; + /* TO_UPSTREAM(BoundsSafety) ON */ + struct LVBase { + APValue::LValueBase Base; + CharUnits Offset; + // BoundsSafety : While the base also holds a corresponding constant array type + // for forged pointer, we still keep track of forged size because the array + // size will be different from the actual forged size if it is not a multiple + // of element type size after a bitcast. The codegen doesn't round up/down + // the bounds to be a type-size multiple, we should keep it the same for + // constant emission. Once __builtin_forge_* has a type as an argument, we + // may consider round down the size with the element type size. + CharUnits ForgedSize; + // While 'Offset' is the offset within the LValue, 'ForgedOffset' is the + // offset of the base pointer of __builtin_unsafe_forge*. For example, in + // the following, + // '__bidi_indexable_unsafe_forge_bidi_indexable(base + N) + M' + // 'N' should be 'ForgedOffset' and 'M' should be 'Offset'. This way, the + // forged pointer itself becomes an LValue starting at base + 'ForgedOffset'. + CharUnits ForgedOffset; + unsigned PathLength; + bool IsNullPtr : 1; + bool IsOnePastTheEnd : 1; + bool IsForgeBidi : 1; + bool IsForgeSingle : 1; + bool IsForgeTerminatedBy : 1; + }; + + struct LVPlaceHolder { + LVBase Base; + LValuePathEntry Path[1]; + }; + /* TO_UPSTREAM(BoundsSafety) OFF */ + // We ensure elsewhere that Data is big enough for LV and MemberPointerData. typedef llvm::AlignedCharArrayUnion<void *, APSInt, APFloat, ComplexAPSInt, ComplexAPFloat, Vec, Arr, StructData, - UnionData, AddrLabelDiffData> DataType; + UnionData, AddrLabelDiffData, + // TO_UPSTREAM(BoundsSafety) + LVPlaceHolder> DataType; static const size_t DataSize = sizeof(DataType); DataType Data; @@ -487,12 +636,33 @@ class APValue { const CharUnits &getLValueOffset() const { return const_cast<APValue*>(this)->getLValueOffset(); } + + /* TO_UPSTREAM(BoundsSafety) ON */ + CharUnits &getLValueForgedSize(); + const CharUnits &getLValueForgedSize() const { + return const_cast<APValue *>(this)->getLValueForgedSize(); + } + + CharUnits &getLValueForgedOffset(); + const CharUnits &getLValueForgedOffset() const { + return const_cast<APValue *>(this)->getLValueForgedOffset(); + } + + CharUnits getUnwrappedLValueOffset() const; + /* TO_UPSTREAM(BoundsSafety) OFF */ + bool isLValueOnePastTheEnd() const; bool hasLValuePath() const; ArrayRef<LValuePathEntry> getLValuePath() const; unsigned getLValueCallIndex() const; unsigned getLValueVersion() const; bool isNullPointer() const; + /* TO_UPSTREAM(BoundsSafety) ON */ + bool isLValueForgeBidi() const; + bool isLValueForgeSingle() const; + bool isLValueForgeTerminatedBy() const; + bool isLValueForge() const; + /* TO_UPSTREAM(BoundsSafety) OFF */ APValue &getVectorElt(unsigned I) { assert(isVector() && "Invalid accessor"); @@ -628,6 +798,12 @@ class APValue { ((AddrLabelDiffData *)(char *)&Data)->RHSExpr = RHSExpr; } + /* TO_UPSTREAM(BoundsSafety) ON */ + void setLValueForgedBidi(const CharUnits &Size, const CharUnits &Offset); + void setLValueForgedSingle(const CharUnits &Offset); + void setLValueForgedTerminatedBy(const CharUnits &Offset); + /* TO_UPSTREAM(BoundsSafety) OFF */ + private: void DestroyDataAndMakeUninit(); void MakeInt() { diff --git a/clang/include/clang/AST/ASTConsumer.h b/clang/include/clang/AST/ASTConsumer.h index 447f2592d2359..6cf4504dcfa60 100644 --- a/clang/include/clang/AST/ASTConsumer.h +++ b/clang/include/clang/AST/ASTConsumer.h @@ -27,6 +27,7 @@ namespace clang { class VarDecl; class FunctionDecl; class ImportDecl; + class TargetInfo; /// ASTConsumer - This is an abstract interface that should be implemented by /// clients that read ASTs. This abstraction layer allows the client to be @@ -47,6 +48,14 @@ class ASTConsumer { /// ASTContext. virtual void Initialize(ASTContext &Context) {} + /// Initialize - This is called to initialize the consumer, providing the + /// ASTContext. 'CodeGenTargetInfo' specifies the code-generation configuration + /// for this compilation instance, which may differ from the one carried + /// by the Context itself only in the OS Version number - + /// for example when type-checking must be performed against an epoch OS version + /// while code-generation must run according to the user-specified OS version. + virtual void Initialize(ASTContext &Context, const TargetInfo &CodeGenTargetInfo) {} + /// HandleTopLevelDecl - Handle the specified top-level declaration. This is /// called by the parser to process every top-level Decl*. /// diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index 6d1c8ca8a2f96..8914c6a491536 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -25,6 +25,7 @@ #include "clang/AST/RawCommentList.h" #include "clang/AST/TemplateName.h" #include "clang/Basic/LLVM.h" +#include "clang/Basic/LangOptions.h" #include "clang/Basic/PartialDiagnostic.h" #include "clang/Basic/SourceLocation.h" #include "llvm/ADT/DenseMap.h" @@ -256,6 +257,14 @@ class ASTContext : public RefCountedBase<ASTContext> { mutable llvm::FoldingSet<CountAttributedType> CountAttributedTypes; + /* TO_UPSTREAM(BoundsSafety) ON */ + mutable llvm::FoldingSet<DynamicRangePointerType> + DynamicRangePointerTypes; + + mutable llvm::ContextualFoldingSet<ValueTerminatedType, ASTContext &> + ValueTerminatedTypes; + /* TO_UPSTREAM(BoundsSafety) OFF */ + mutable llvm::FoldingSet<QualifiedTemplateName> QualifiedTemplateNames; mutable llvm::FoldingSet<DependentTemplateName> DependentTemplateNames; mutable llvm::FoldingSet<SubstTemplateTemplateParmStorage> @@ -482,6 +491,24 @@ class ASTContext : public RefCountedBase<ASTContext> { ASTContext &this_() { return *this; } + mutable std::optional<std::string> ObjCMsgSendUsageFile; + llvm::SmallVector<const ObjCMethodDecl *, 4> ObjCMsgSendUsage; + +public: + /// Check whether env variable CLANG_COMPILER_OBJC_MESSAGE_TRACE_PATH is set. + /// If it is set, assign the value to ObjCMsgSendUsageFile. + bool isObjCMsgSendUsageFileSpecified() const; + + /// Return the file name stored in ObjCMsgSendUsageFile if it has a value, + /// return an empty string otherwise. + std::string getObjCMsgSendUsageFilename() const; + + /// Record an ObjC method. + void recordObjCMsgSendUsage(const ObjCMethodDecl *Method); + + /// Write the collected ObjC method tracing information to a file. + void writeObjCMsgSendUsages(const std::string &Filename); + public: /// A type synonym for the TemplateOrInstantiation mapping. using TemplateOrSpecializationInfo = @@ -775,6 +802,35 @@ class ASTContext : public RefCountedBase<ASTContext> { return DiagAllocator; } + struct AvailabilityDomainInfo { + FeatureAvailKind Kind = FeatureAvailKind::None; + ImplicitCastExpr *Call = nullptr; + bool isInvalid() const { return Kind == FeatureAvailKind::None; } + }; + + std::map<StringRef, VarDecl *> AvailabilityDomainMap; + + void addAvailabilityDomainMap(StringRef Name, VarDecl *VD) { + AvailabilityDomainMap[Name] = VD; + } + + std::pair<DomainAvailabilityAttr *, bool> + checkNewFeatureAvailability(Decl *D, StringRef DomainName, bool Unavailable); + + bool hasFeatureAvailabilityAttr(const Decl *D) const; + + // Retrieve availability domain information for a feature. + AvailabilityDomainInfo getFeatureAvailInfo(StringRef FeatureName) const; + + // Retrieve feature name and availability domain information on a decl. If the + // decl doesn't have attribute availability_domain on it, the name will be + // empty and AvailabilityDomainInfo::Kind will be set to + // FeatureAvailKind::None. + std::pair<StringRef, AvailabilityDomainInfo> + getFeatureAvailInfo(Decl *D) const; + + bool hasUnavailableFeature(const Decl *D) const; + const TargetInfo &getTargetInfo() const { return *Target; } const TargetInfo *getAuxTargetInfo() const { return AuxTarget; } @@ -1386,9 +1442,12 @@ class ASTContext : public RefCountedBase<ASTContext> { /// Return the uniqued reference to the type for a pointer to /// the specified type. - QualType getPointerType(QualType T) const; - CanQualType getPointerType(CanQualType T) const { - return CanQualType::CreateUnsafe(getPointerType((QualType) T)); + QualType getPointerType(QualType T, BoundsSafetyPointerAttributes A = + BoundsSafetyPointerAttributes()) const; + CanQualType getPointerType( + CanQualType T, + BoundsSafetyPointerAttributes A = BoundsSafetyPointerAttributes()) const { + return CanQualType::CreateUnsafe(getPointerType((QualType)T, A)); } QualType @@ -1396,6 +1455,42 @@ class ASTContext : public RefCountedBase<ASTContext> { bool OrNull, ArrayRef<TypeCoupledDeclRefInfo> DependentDecls) const; + /* TO_UPSTREAM(BoundsSafety) ON */ + QualType getDynamicRangePointerType( + QualType T, Expr *StartPtr, Expr *EndPtr, + ArrayRef<TypeCoupledDeclRefInfo> StartPtrDecls, + ArrayRef<TypeCoupledDeclRefInfo> EndPtrDecls) const; + + QualType getValueTerminatedType(QualType T, Expr *TerminatorExpr) const; + + /// Return a new pointer type with the -fbounds-safety pointer attribute with + /// preserving existing AttributedTypes and qualifiers. + QualType getBoundsSafetyPointerType(QualType PointerTy, + BoundsSafetyPointerAttributes); + + /// Return a result type of merging -fbounds-safety pointer attributes of \p SrcTy + /// to \p DstTy, while preserving existing AttributedTypes and qualifiers of + /// \p DstTy. The type merging is performed recursively in nested pointers. + /// The caller should provide \p MergeFunctor to create a merged pointer type + /// using the recursively merged pointee type. + /// mergeBoundsSafetyPointerTypes removes any AttributedType(s) from \p + /// DstTy, calls \p MergeFunctor to merge the attributes at each level, and + /// then reapplies the AttributedType(s) to the merged type. \p OrigDstTy is + /// the same as \p DstTy but without dropping the AttributedType(s). This + /// allows us to check any AttributedType(s) in \p MergeFunctor in order to + /// make decision about the merged type. + QualType mergeBoundsSafetyPointerTypes( + QualType DstTy, QualType SrcTy, + std::function<QualType(QualType /* DstTy */, QualType /* SrcTy */, + QualType /* MergePointeeTy */, + QualType /* OrigDstTy */)> &MergeFunctor, + QualType OrigDstTy = QualType()); + + QualType getBoundsSafetyAutoPointerType(QualType T, + BoundsSafetyPointerAttributes AbiFAttr, + bool ShouldAutoBound); + /* TO_UPSTREAM(BoundsSafety) OFF */ + /// Return the uniqued reference to a type adjusted from the original /// type to a new type. QualType getAdjustedType(QualType Orig, QualType New) const; @@ -1663,8 +1758,15 @@ class ASTContext : public RefCountedBase<ASTContext> { QualType getInjectedClassNameType(CXXRecordDecl *Decl, QualType TST) const; QualType getAttributedType(attr::Kind attrKind, QualType modifiedType, + QualType equivalentType, + const Attr *attr = nullptr) const; + + QualType getAttributedType(const Attr *attr, QualType modifiedType, QualType equivalentType) const; + QualType getAttributedType(NullabilityKind nullability, QualType modifiedType, + QualType equivalentType); + QualType getBTFTagAttributedType(const BTFTypeTagAttr *BTFAttr, QualType Wrapped); @@ -1805,13 +1907,6 @@ class ASTContext : public RefCountedBase<ASTContext> { QualType DeducedType, bool IsDependent) const; -private: - QualType getDeducedTemplateSpecializationTypeInternal(TemplateName Template, - QualType DeducedType, - bool IsDependent, - QualType Canon) const; - -public: /// Return the unique reference to the type for the specified TagDecl /// (struct/union/class/enum) decl. QualType getTagDeclType(const TagDecl *Decl) const; @@ -2391,6 +2486,14 @@ class ASTContext : public RefCountedBase<ASTContext> { uint64_t getTypeSize(QualType T) const { return getTypeInfo(T).Width; } uint64_t getTypeSize(const Type *T) const { return getTypeInfo(T).Width; } + /*TO_UPSTREAM(BoundsSafety) ON*/ + uint64_t getTypeSizeOrNull(QualType T) const { + if (T->isIncompleteOrSizelessType()) + return 0; + return getTypeSize(T); + } + /*TO_UPSTREAM(BoundsSafety) OFF*/ + /// Return the size of the character type, in bits. uint64_t getCharWidth() const { return getTypeSize(CharTy); @@ -2645,6 +2748,37 @@ class ASTContext : public RefCountedBase<ASTContext> { return getCanonicalType(T1) == getCanonicalType(T2); } + /* TO_UPSTREAM(BoundsSafety) ON*/ + /// These enum values (other than CanMerge) are aligned with the options for + /// the third parameter in + /// diag::err_cond_expr_nested_bounds_safety_pointer_attribute_mismatch + enum BoundsSafePointerTypeMergeKind { + BSPTMK_NestedBoundsMismatch, + BSPTMK_FunctionTypeMismatch, + BSPTMK_TerminatedByMismatch, + BSPTMK_CanMerge, + }; + /// Given two pointer types that can be unified without losing type safety, + /// check whether they can be merged without losing bounds safety. + /// In practice this means checking whether their inner pointer type bounds + /// sugar nodes match (if any exist). Non-nested pointer types can always be + /// unified, potentially requiring dynamic checks. + BoundsSafePointerTypeMergeKind canMergeTypeBounds(QualType LHSTy, + QualType RHSTy) const; + + /// Given two pointer types, check whether either of them if is a + /// ValueTerminatedType, and if so, that the other is a ValueTerminatedType + /// with the same terminator. Does not check pointee type! + BoundsSafePointerTypeMergeKind + checkTerminatedByMismatch(QualType LHSTy, QualType RHSTy) const; + BoundsSafePointerTypeMergeKind canMergeInnerTypeBounds(QualType LHSTy, + QualType RHSTy) const; + BoundsSafePointerTypeMergeKind + canMergeFunctionTypeBounds(const FunctionProtoType *LHSTy, + const FunctionProtoType *RHSTy) const; + + /* TO_UPSTREAM(BoundsSafety) OFF*/ + /// Determine whether the given expressions \p X and \p Y are equivalent. bool hasSameExpr(const Expr *X, const Expr *Y) const; @@ -2722,6 +2856,16 @@ class ASTContext : public RefCountedBase<ASTContext> { /// Determine if two types are similar, ignoring only CVR qualifiers. bool hasCvrSimilarType(QualType T1, QualType T2); + /* TO_UPSTREAM(BoundsSafety) ON */ + /// Determine if two types have same -fbounds-safety pointer layouts, recursively. + bool hasSameBoundsSafetyPointerLayout(QualType T1, QualType T2); + + /// Determine if two types have compatible -fbounds-safety pointer layouts, + /// recursively. + bool hasCompatibleBoundsSafetyPointerLayout(QualType T1, QualType T2, + bool ExactCheck = false); + /* TO_UPSTREAM(BoundsSafety) OFF */ + /// Retrieves the "canonical" nested name specifier for a /// given nested name specifier. /// diff --git a/clang/include/clang/AST/ASTImporter.h b/clang/include/clang/AST/ASTImporter.h index 4ffd913846575..f851decd0965c 100644 --- a/clang/include/clang/AST/ASTImporter.h +++ b/clang/include/clang/AST/ASTImporter.h @@ -258,7 +258,6 @@ class TypeSourceInfo; FoundDeclsTy findDeclsInToCtx(DeclContext *DC, DeclarationName Name); void AddToLookupTable(Decl *ToD); - llvm::Error ImportAttrs(Decl *ToD, Decl *FromD); protected: /// Can be overwritten by subclasses to implement their own import logic. diff --git a/clang/include/clang/AST/ASTMutationListener.h b/clang/include/clang/AST/ASTMutationListener.h index 2c4ec2ce67f36..b8bd5403df477 100644 --- a/clang/include/clang/AST/ASTMutationListener.h +++ b/clang/include/clang/AST/ASTMutationListener.h @@ -149,6 +149,17 @@ class ASTMutationListener { virtual void AddedAttributeToRecord(const Attr *Attr, const RecordDecl *Record) {} + /* TO_UPSTREAM(BoundsSafety) ON*/ + /// An attribute to a Decl to write in a separate record because the attribute + /// and the Decl create a cycle during deserialization. + /// + /// \param Attr The attribute to the Decl + /// + /// \param Record The Decl owns the attribute + virtual void LazyAttributeToDecl(const Attr *Attr, + const Decl *D) {} + /* TO_UPSTREAM(BoundsSafety) OFF*/ + /// The parser find the named module declaration. virtual void EnteringModulePurview() {} diff --git a/clang/include/clang/AST/AbstractBasicReader.h b/clang/include/clang/AST/AbstractBasicReader.h index 4b627c65e276b..0cc78f56cc903 100644 --- a/clang/include/clang/AST/AbstractBasicReader.h +++ b/clang/include/clang/AST/AbstractBasicReader.h @@ -219,6 +219,13 @@ class DataStreamBasicReader : public BasicReaderBase<Impl> { return Qualifiers::fromOpaqueValue(value); } + /* TO_UPSTREAM(BoundsSafety) ON */ + BoundsSafetyPointerAttributes readBoundsSafetyPointerAttributes() { + uint32_t value = asImpl().readUInt32(); + return BoundsSafetyPointerAttributes::fromOpaqueValue(value); + } + /* TO_UPSTREAM(BoundsSafety) OFF */ + FunctionProtoType::ExceptionSpecInfo readExceptionSpecInfo(llvm::SmallVectorImpl<QualType> &buffer) { FunctionProtoType::ExceptionSpecInfo esi; diff --git a/clang/include/clang/AST/AbstractBasicWriter.h b/clang/include/clang/AST/AbstractBasicWriter.h index b941add8bde88..79c7fb9fa1a2d 100644 --- a/clang/include/clang/AST/AbstractBasicWriter.h +++ b/clang/include/clang/AST/AbstractBasicWriter.h @@ -201,6 +201,12 @@ class DataStreamBasicWriter : public BasicWriterBase<Impl> { asImpl().writeUInt64(value.getAsOpaqueValue()); } + /* TO_UPSTREAM(BoundsSafety) ON */ + void writeBoundsSafetyPointerAttributes(BoundsSafetyPointerAttributes value) { + asImpl().writeUInt32(value.getAsOpaqueValue()); + } + /* TO_UPSTREAM(BoundsSafety) OFF */ + void writeExceptionSpecInfo( const FunctionProtoType::ExceptionSpecInfo &esi) { asImpl().writeUInt32(uint32_t(esi.Type)); diff --git a/clang/include/clang/AST/Attr.h b/clang/include/clang/AST/Attr.h index 8e9b7ad8b4682..269c8a1174e72 100644 --- a/clang/include/clang/AST/Attr.h +++ b/clang/include/clang/AST/Attr.h @@ -24,6 +24,7 @@ #include "clang/Basic/OpenMPKinds.h" #include "clang/Basic/Sanitizers.h" #include "clang/Basic/SourceLocation.h" +#include "llvm/ADT/PointerEmbeddedInt.h" #include "llvm/Frontend/HLSL/HLSLResource.h" #include "llvm/Support/CodeGen.h" #include "llvm/Support/ErrorHandling.h" @@ -379,6 +380,13 @@ inline const StreamingDiagnostic &operator<<(const StreamingDiagnostic &DB, DB.AddTaggedVal(reinterpret_cast<uint64_t>(At), DiagnosticsEngine::ak_attr); return DB; } + +/// Determine if type T is a valid subject for a nonnull and similar +/// attributes. Dependent types are considered valid so they can be checked +/// during instantiation time. By default, we look through references (the +/// behavior used by nonnull), but if the second parameter is true, then we +/// treat a reference type as valid. +bool isValidPointerAttrType(QualType T, bool RefOkay = false); } // end namespace clang #endif diff --git a/clang/include/clang/AST/AttrIterator.h b/clang/include/clang/AST/AttrIterator.h index 66571e1cf0b8e..1a66130129f5a 100644 --- a/clang/include/clang/AST/AttrIterator.h +++ b/clang/include/clang/AST/AttrIterator.h @@ -94,6 +94,8 @@ class specific_attr_iterator { specific_attr_iterator Right) { return !(Left == Right); } + + Iterator getCurrent() const { return Current; } }; template <typename SpecificAttr, typename Container> diff --git a/clang/include/clang/AST/Availability.h b/clang/include/clang/AST/Availability.h index 26ae622e5b449..520a734ec4c28 100644 --- a/clang/include/clang/AST/Availability.h +++ b/clang/include/clang/AST/Availability.h @@ -37,6 +37,8 @@ class AvailabilitySpec { /// Name of the platform that Version corresponds to. StringRef Platform; + StringRef DomainName; + SourceLocation BeginLoc, EndLoc; public: @@ -45,6 +47,9 @@ class AvailabilitySpec { : Version(Version), Platform(Platform), BeginLoc(BeginLoc), EndLoc(EndLoc) {} + AvailabilitySpec(StringRef DomainName, SourceLocation Loc) + : DomainName(DomainName), BeginLoc(Loc), EndLoc(Loc) {} + /// This constructor is used when representing the '*' case. AvailabilitySpec(SourceLocation StarLoc) : BeginLoc(StarLoc), EndLoc(StarLoc) {} @@ -55,7 +60,12 @@ class AvailabilitySpec { SourceLocation getEndLoc() const { return EndLoc; } /// Returns true when this represents the '*' case. - bool isOtherPlatformSpec() const { return Version.empty(); } + bool isOtherPlatformSpec() const { + return Version.empty() && DomainName.empty(); + } + + bool isDomainName() const { return !DomainName.empty(); } + StringRef getDomainName() const { return DomainName; } }; class Decl; @@ -97,6 +107,10 @@ struct AvailabilityInfo { return UnconditionallyUnavailable; } + /// Augments the existing information with additional constraints provided by + /// \c Other. + void mergeWith(AvailabilityInfo Other); + AvailabilityInfo(StringRef Domain, VersionTuple I, VersionTuple D, VersionTuple O, bool U, bool UD, bool UU) : Domain(Domain), Introduced(I), Deprecated(D), Obsoleted(O), diff --git a/clang/include/clang/AST/ComputeDependence.h b/clang/include/clang/AST/ComputeDependence.h index 6d3a51c379f9d..2ae54f2c762c8 100644 --- a/clang/include/clang/AST/ComputeDependence.h +++ b/clang/include/clang/AST/ComputeDependence.h @@ -79,6 +79,15 @@ class LambdaExpr; class CXXUnresolvedConstructExpr; class CXXDependentScopeMemberExpr; class MaterializeTemporaryExpr; +/* TO_UPSTREAM(BoundsSafety) ON */ +class MaterializeSequenceExpr; +class PredefinedBoundsCheckExpr; +class BoundsCheckExpr; +class AssumptionExpr; +class BoundsSafetyPointerPromotionExpr; +class GetBoundExpr; +class ForgePtrExpr; +/* TO_UPSTREAM(BoundsSafety) OFF */ class CXXFoldExpr; class CXXParenListInitExpr; class TypeTraitExpr; @@ -176,6 +185,16 @@ ExprDependence computeDependence(TypeTraitExpr *E); ExprDependence computeDependence(ConceptSpecializationExpr *E, bool ValueDependent); +/* TO_UPSTREAM(BoundsSafety) ON */ +ExprDependence computeDependence(MaterializeSequenceExpr *E); +ExprDependence computeDependence(PredefinedBoundsCheckExpr *E); +ExprDependence computeDependence(BoundsCheckExpr *E); +ExprDependence computeDependence(AssumptionExpr *E); +ExprDependence computeDependence(BoundsSafetyPointerPromotionExpr *E); +ExprDependence computeDependence(GetBoundExpr *E); +ExprDependence computeDependence(ForgePtrExpr *E); +/* TO_UPSTREAM(BoundsSafety) OFF */ + ExprDependence computeDependence(SYCLUniqueStableNameExpr *E); ExprDependence computeDependence(PredefinedExpr *E); ExprDependence computeDependence(CallExpr *E, llvm::ArrayRef<Expr *> PreArgs); diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index 561a9d872acfb..50a712836e3dd 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -687,6 +687,14 @@ class ValueDecl : public NamedDecl { /// can be captured. bool isInitCapture() const; + /* TO_UPSTREAM(BoundsSafety) ON */ + /// Whether this decl is a dependent parameter referred to by the return type + /// that is a bounds-attributed type. + bool isDependentParamOfReturnType( + const BoundsAttributedType **RetType = nullptr, + const TypeCoupledDeclRefInfo **Info = nullptr) const; + /* TO_UPSTREAM(BoundsSafety) OFF */ + // If this is a VarDecl, or a BindindDecl with an // associated decomposed VarDecl, return that VarDecl. VarDecl *getPotentiallyDecomposedVarDecl(); @@ -4338,6 +4346,9 @@ class RecordDecl : public TagDecl { /// leaks. bool isOrContainsUnion() const; + // TO_UPSTREAM(BoundsSafety) + bool isParentStructOf(const Decl *D) const; + // Iterator access to field members. The field iterator only visits // the non-static data members of this class, ignoring any static // data members, functions, constructors, destructors, etc. @@ -5003,6 +5014,12 @@ void Redeclarable<decl_type>::setPreviousDecl(decl_type *PrevDecl) { cast<NamedDecl>(static_cast<decl_type*>(this))->isLinkageValid()); } +/* TO_UPSTREAM(BoundsSafety) ON */ +// A BoundsSafety helper function. +/// Return `true` if \p D is const qualified or attributed as immutable. +bool IsConstOrLateConst(const Decl *D); +/* TO_UPSTREAM(BoundsSafety) OFF */ + // Inline function definitions. /// Check if the given decl is complete. diff --git a/clang/include/clang/AST/DeclBase.h b/clang/include/clang/AST/DeclBase.h index 40f01abf384e9..04dbd1db6cba8 100644 --- a/clang/include/clang/AST/DeclBase.h +++ b/clang/include/clang/AST/DeclBase.h @@ -670,9 +670,19 @@ class alignas(8) Decl { /// Whether this declaration comes from another module unit. bool isInAnotherModuleUnit() const; + /// Whether this declaration comes from the same module unit being compiled. + bool isInCurrentModuleUnit() const; + + /// Whether the definition of the declaration should be emitted in external + /// sources. + bool shouldEmitInExternalSource() const; + /// Whether this declaration comes from explicit global module. bool isFromExplicitGlobalModule() const; + /// Whether this declaration comes from global module. + bool isFromGlobalModule() const; + /// Whether this declaration comes from a named module. bool isInNamedModule() const; diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h index fb52ac804849d..bf6a5ce92d438 100644 --- a/clang/include/clang/AST/DeclCXX.h +++ b/clang/include/clang/AST/DeclCXX.h @@ -1188,10 +1188,6 @@ class CXXRecordDecl : public RecordDecl { /// /// \note This does NOT include a check for union-ness. bool isEmpty() const { return data().Empty; } - /// Marks this record as empty. This is used by DWARFASTParserClang - /// when parsing records with empty fields having [[no_unique_address]] - /// attribute - void markEmpty() { data().Empty = true; } void setInitMethod(bool Val) { data().HasInitMethod = Val; } bool hasInitMethod() const { return data().HasInitMethod; } @@ -1210,6 +1206,13 @@ class CXXRecordDecl : public RecordDecl { return D.HasPublicFields || D.HasProtectedFields || D.HasPrivateFields; } + /// If this is a standard-layout class or union, any and all data members will + /// be declared in the same type. + /// + /// This retrieves the type where any fields are declared, + /// or the current class if there is no class with fields. + const CXXRecordDecl *getStandardLayoutBaseWithFields() const; + /// Whether this class is polymorphic (C++ [class.virtual]), /// which means that the class contains or inherits a virtual function. bool isPolymorphic() const { return data().Polymorphic; } diff --git a/clang/include/clang/AST/DeclContextInternals.h b/clang/include/clang/AST/DeclContextInternals.h index e169c48592192..0acd9df9e1572 100644 --- a/clang/include/clang/AST/DeclContextInternals.h +++ b/clang/include/clang/AST/DeclContextInternals.h @@ -244,7 +244,11 @@ class StoredDeclsList { // FIXME: Move the assert before the single decl case when we fix the // duplication coming from the ASTReader reading builtin types. - assert(!llvm::is_contained(getLookupResult(), D) && "Already exists!"); + + // SWIFT: FIXME^2: This assertion causes problems in Swift's ClangImporter. + // SWIFT: We should probably set its ASTContext to Objective-C++ mode to avoid it. + // SWIFT: assert(!llvm::is_contained(getLookupResult(), D) && "Already exists!"); + // Determine if this declaration is actually a redeclaration. for (DeclListNode *N = getAsList(); /*return in loop*/; N = N->Rest.dyn_cast<DeclListNode *>()) { diff --git a/clang/include/clang/AST/DeclObjC.h b/clang/include/clang/AST/DeclObjC.h index d2cc61ca19f8a..bf9c5de2c1160 100644 --- a/clang/include/clang/AST/DeclObjC.h +++ b/clang/include/clang/AST/DeclObjC.h @@ -881,6 +881,11 @@ class ObjCPropertyDecl : public NamedDecl { return Assign; } + /// Return true if this property has an explicitly specified getter name. + bool hasExplicitGetterName() const { + return (PropertyAttributes & ObjCPropertyAttribute::kind_getter); + } + Selector getGetterName() const { return GetterName; } SourceLocation getGetterNameLoc() const { return GetterNameLoc; } @@ -889,6 +894,11 @@ class ObjCPropertyDecl : public NamedDecl { GetterNameLoc = Loc; } + /// Return true if this property has an explicitly specified setter name. + bool hasExplicitSetterName() const { + return (PropertyAttributes & ObjCPropertyAttribute::kind_setter); + } + Selector getSetterName() const { return SetterName; } SourceLocation getSetterNameLoc() const { return SetterNameLoc; } @@ -1239,7 +1249,8 @@ class ObjCInterfaceDecl : public ObjCContainerDecl /// which will be NULL if this class has not yet been defined. /// /// The bit indicates when we don't need to check for out-of-date - /// declarations. It will be set unless modules are enabled. + /// declarations. It will be set unless there is an ExternalASTSource that + /// could provide a definition. llvm::PointerIntPair<DefinitionData *, 1, bool> Data; ObjCInterfaceDecl(const ASTContext &C, DeclContext *DC, SourceLocation AtLoc, @@ -1528,7 +1539,7 @@ class ObjCInterfaceDecl : public ObjCContainerDecl // If the name of this class is out-of-date, bring it up-to-date, which // might bring in a definition. // Note: a null value indicates that we don't have a definition and that - // modules are enabled. + // there is a ExternalASTSource that could provide a definition. if (!Data.getOpaqueValue()) getMostRecentDecl(); @@ -2099,7 +2110,8 @@ class ObjCProtocolDecl : public ObjCContainerDecl, /// which will be NULL if this class has not yet been defined. /// /// The bit indicates when we don't need to check for out-of-date - /// declarations. It will be set unless modules are enabled. + /// declarations. It will be set unless there is an ExternalASTSource that + /// could provide a definition. llvm::PointerIntPair<DefinitionData *, 1, bool> Data; ObjCProtocolDecl(ASTContext &C, DeclContext *DC, IdentifierInfo *Id, @@ -2236,7 +2248,7 @@ class ObjCProtocolDecl : public ObjCContainerDecl, // If the name of this protocol is out-of-date, bring it up-to-date, which // might bring in a definition. // Note: a null value indicates that we don't have a definition and that - // modules are enabled. + // there is a ExternalASTSource that could provide a definition. if (!Data.getOpaqueValue()) getMostRecentDecl(); @@ -2772,17 +2784,25 @@ raw_ostream &operator<<(raw_ostream &OS, const ObjCImplementationDecl &ID); class ObjCCompatibleAliasDecl : public NamedDecl { /// Class that this is an alias of. ObjCInterfaceDecl *AliasedClass; + /// The location of the name of the referenced class. + SourceLocation AliasedClassLoc; + /// The location of the '@'. + SourceLocation AtLoc; - ObjCCompatibleAliasDecl(DeclContext *DC, SourceLocation L, IdentifierInfo *Id, - ObjCInterfaceDecl* aliasedClass) - : NamedDecl(ObjCCompatibleAlias, DC, L, Id), AliasedClass(aliasedClass) {} + ObjCCompatibleAliasDecl(DeclContext *DC, SourceLocation NameLoc, + IdentifierInfo *Id, ObjCInterfaceDecl *AliasedClass, + SourceLocation AliasedClassLoc, SourceLocation AtLoc) + : NamedDecl(ObjCCompatibleAlias, DC, NameLoc, Id), + AliasedClass(AliasedClass), AliasedClassLoc(AliasedClassLoc), + AtLoc(AtLoc) {} void anchor() override; public: - static ObjCCompatibleAliasDecl *Create(ASTContext &C, DeclContext *DC, - SourceLocation L, IdentifierInfo *Id, - ObjCInterfaceDecl* aliasedClass); + static ObjCCompatibleAliasDecl * + Create(ASTContext &C, DeclContext *DC, SourceLocation NameLoc, + IdentifierInfo *Id, ObjCInterfaceDecl *AliasedClass, + SourceLocation AliasedClassLoc, SourceLocation AtLoc); static ObjCCompatibleAliasDecl *CreateDeserialized(ASTContext &C, GlobalDeclID ID); @@ -2791,6 +2811,17 @@ class ObjCCompatibleAliasDecl : public NamedDecl { ObjCInterfaceDecl *getClassInterface() { return AliasedClass; } void setClassInterface(ObjCInterfaceDecl *D) { AliasedClass = D; } + SourceLocation getClassInterfaceLoc() const { return AliasedClassLoc; } + + void setClassInterfaceLoc(SourceLocation Loc) { AliasedClassLoc = Loc; } + + SourceLocation getAtLoc() const { return AtLoc; } + void setAtLoc(SourceLocation Loc) { AtLoc = Loc; } + + SourceRange getSourceRange() const override LLVM_READONLY { + return SourceRange(AtLoc, AtLoc); + } + static bool classof(const Decl *D) { return classofKind(D->getKind()); } static bool classofKind(Kind K) { return K == ObjCCompatibleAlias; } }; diff --git a/clang/include/clang/AST/DependentASTVisitor.h b/clang/include/clang/AST/DependentASTVisitor.h new file mode 100644 index 0000000000000..b218ec4841f02 --- /dev/null +++ b/clang/include/clang/AST/DependentASTVisitor.h @@ -0,0 +1,90 @@ +//===--- DependentASTVisitor.h - Helper for dependent nodes -----*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines the DependentASTVisitor RecursiveASTVisitor layer, which +// is responsible for visiting unresolved symbol references. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_AST_DEPENDENT_AST_VISITOR_H +#define LLVM_CLANG_AST_DEPENDENT_AST_VISITOR_H + +#include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclTemplate.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/AST/Type.h" + +namespace clang { + +// TODO: Use in the indexer. +template <typename Derived> +class DependentASTVisitor : public RecursiveASTVisitor<Derived> { +private: + bool visitDependentReference( + const Type *T, const DeclarationName &Name, SourceLocation Loc, + llvm::function_ref<bool(const NamedDecl *ND)> Filter) { + if (!T) + return true; + const TemplateSpecializationType *TST = + T->getAs<TemplateSpecializationType>(); + if (!TST) + return true; + TemplateName TN = TST->getTemplateName(); + const ClassTemplateDecl *TD = + dyn_cast_or_null<ClassTemplateDecl>(TN.getAsTemplateDecl()); + if (!TD) + return true; + CXXRecordDecl *RD = TD->getTemplatedDecl(); + if (!RD->hasDefinition()) + return true; + RD = RD->getDefinition(); + std::vector<const NamedDecl *> Symbols = + RD->lookupDependentName(Name, Filter); + // FIXME: Improve overload handling. + if (Symbols.size() != 1) + return true; + if (Loc.isInvalid()) + return true; + return RecursiveASTVisitor<Derived>::getDerived() + .VisitDependentSymbolReference(Symbols[0], Loc); + } + +public: + bool VisitCXXDependentScopeMemberExpr(CXXDependentScopeMemberExpr *E) { + const DeclarationNameInfo &Info = E->getMemberNameInfo(); + return visitDependentReference( + E->getBaseType().getTypePtrOrNull(), Info.getName(), Info.getLoc(), + [](const NamedDecl *D) { return D->isCXXInstanceMember(); }); + } + + bool VisitDependentScopeDeclRefExpr(DependentScopeDeclRefExpr *E) { + const DeclarationNameInfo &Info = E->getNameInfo(); + const NestedNameSpecifier *NNS = E->getQualifier(); + return visitDependentReference( + NNS->getAsType(), Info.getName(), Info.getLoc(), + [](const NamedDecl *D) { return !D->isCXXInstanceMember(); }); + } + + bool VisitDependentNameTypeLoc(DependentNameTypeLoc TL) { + const DependentNameType *DNT = TL.getTypePtr(); + const NestedNameSpecifier *NNS = DNT->getQualifier(); + DeclarationName Name(DNT->getIdentifier()); + return visitDependentReference( + NNS->getAsType(), Name, TL.getNameLoc(), + [](const NamedDecl *ND) { return isa<TypeDecl>(ND); }); + } + + bool VisitDependentSymbolReference(const NamedDecl *Symbol, + SourceLocation SymbolNameLoc) { + return true; + } +}; + +} // end namespace clang + +#endif // LLVM_CLANG_AST_DEPENDENT_AST_VISITOR_H diff --git a/clang/include/clang/AST/DependentDiagnostic.h b/clang/include/clang/AST/DependentDiagnostic.h index cadf970620041..4196f6a5db731 100644 --- a/clang/include/clang/AST/DependentDiagnostic.h +++ b/clang/include/clang/AST/DependentDiagnostic.h @@ -149,9 +149,11 @@ class DeclContext::ddiag_iterator { return tmp; } +#ifndef __swift__ bool operator==(ddiag_iterator Other) const { return Ptr == Other.Ptr; } +#endif bool operator!=(ddiag_iterator Other) const { return Ptr != Other.Ptr; diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h index 5b813bfc2faf9..7857b2c37fd0a 100644 --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -31,6 +31,7 @@ #include "llvm/ADT/APFloat.h" #include "llvm/ADT/APSInt.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/iterator.h" #include "llvm/ADT/iterator_range.h" @@ -262,6 +263,12 @@ class Expr : public ValueStmt { bool isUnusedResultAWarning(const Expr *&WarnExpr, SourceLocation &Loc, SourceRange &R1, SourceRange &R2, ASTContext &Ctx) const; + /* TO_UPSTREAM(BoundsSafety) ON */ + bool isUnusedResultAWarning(const Expr *&WarnExpr, SourceLocation &Loc, + SourceRange &R1, SourceRange &R2, + ASTContext &Ctx, + llvm::SmallPtrSetImpl<const OpaqueValueExpr *> &B) const; + /* TO_UPSTREAM(BoundsSafety) OFF */ /// isLValue - True if this expression is an "l-value" according to /// the rules of the current language. C and C++ give somewhat @@ -782,6 +789,18 @@ class Expr : public ValueStmt { /// strlen, false otherwise. bool tryEvaluateStrLen(uint64_t &Result, ASTContext &Ctx) const; + /* TO_UPSTREAM(BoundsSafety) ON*/ + /// If the current Expr is an array or a pointer to an array element, this + /// will try to statically determine the value of the last element of that + /// array. + bool tryEvaluateTerminatorElement(EvalResult &Result, + const ASTContext &Ctx) const; + + bool EvaluateAsTerminatorValue( + llvm::APSInt &Result, const ASTContext &Ctx, + SideEffectsKind AllowSideEffects = SE_NoSideEffects) const; + /* TO_UPSTREAM(BoundsSafety) OFF*/ + bool EvaluateCharRangeAsString(std::string &Result, const Expr *SizeExpression, const Expr *PtrExpression, ASTContext &Ctx, @@ -837,6 +856,11 @@ class Expr : public ValueStmt { ASTContext &Ctx, NullPointerConstantValueDependence NPC) const; + /* TO_UPSTREAM(BoundsSafety) ON*/ + NullPointerConstantKind isNullPointerConstantIgnoreCastsAndOVEs( + ASTContext &Ctx, NullPointerConstantValueDependence NPC) const; + /* TO_UPSTREAM(BoundsSafety) OFF*/ + /// isOBJCGCCandidate - Return true if this expression may be used in a read/ /// write barrier. bool isOBJCGCCandidate(ASTContext &Ctx) const; @@ -919,6 +943,8 @@ class Expr : public ValueStmt { /// * What IgnoreImpCasts() skips /// * MaterializeTemporaryExpr /// * SubstNonTypeTemplateParmExpr + // TO_UPSTREAM(BoundsSafety) + Expr *IgnoreParenImpCasts(llvm::SmallPtrSetImpl<const OpaqueValueExpr *> &BoundValues) LLVM_READONLY; Expr *IgnoreParenImpCasts() LLVM_READONLY; const Expr *IgnoreParenImpCasts() const { return const_cast<Expr *>(this)->IgnoreParenImpCasts(); @@ -1175,6 +1201,13 @@ class OpaqueValueExpr : public Expr { Expr *SourceExpr; public: + /*TO_UPSTREAM(BoundsSafety) ON*/ + static OpaqueValueExpr *Wrap(const ASTContext &Context, Expr *E); + static OpaqueValueExpr *EnsureWrapped( + const ASTContext &Context, Expr *E, + SmallVectorImpl<OpaqueValueExpr *> &OVEs); + /*TO_UPSTREAM(BoundsSafety) OFF*/ + OpaqueValueExpr(SourceLocation Loc, QualType T, ExprValueKind VK, ExprObjectKind OK = OK_Ordinary, Expr *SourceExpr = nullptr) : Expr(OpaqueValueExprClass, T, VK, OK), SourceExpr(SourceExpr) { @@ -3839,6 +3872,832 @@ class CStyleCastExpr final friend class CastExpr; }; +/* TO_UPSTREAM(BoundsSafety) ON */ +// Attaches one or more assumptions to an expression. The assumptions are each +// codegen'd like they were the parameter of `__builtin_assume`. +class AssumptionExpr final + : public Expr, + private llvm::TrailingObjects<AssumptionExpr, Expr *> { + friend TrailingObjects; + friend class ASTStmtWriter; + + Expr **getTrailingExprs() { + return const_cast<Expr **>(getTrailingObjects<Expr *>()); + } + + Expr *const *getTrailingExprs() const { + return getTrailingObjects<Expr *>(); + } + + AssumptionExpr(EmptyShell Empty, unsigned NumExprs) + : Expr(AssumptionExprClass, Empty) { + AssumptionExprBits.NumExprs = NumExprs; + } + + AssumptionExpr(Expr *ResultExpr, llvm::ArrayRef<Expr *> Assumptions); + +public: + static bool classof(const Stmt *T) { + return T->getStmtClass() == AssumptionExprClass; + } + + static AssumptionExpr *Create(const ASTContext &Ctx, Expr *Result, + llvm::ArrayRef<Expr *> Assumptions); + + static AssumptionExpr *CreateEmpty(const ASTContext &Ctx, unsigned NumExprs); + + Expr *getWrappedExpr() { return getTrailingExprs()[0]; } + const Expr *getWrappedExpr() const { return getTrailingExprs()[0]; } + + void setWrappedExpr(Expr *E) { + if (E) { + getTrailingExprs()[0] = E; + setType(E->getType()); + setValueKind(E->getValueKind()); + setObjectKind(E->getObjectKind()); + } else { + setType(QualType()); + } + } + + unsigned getNumSubExprs() const { return AssumptionExprBits.NumExprs; } + Expr *getSubExpr(unsigned I) { return getTrailingExprs()[I]; } + const Expr *getSubExpr(unsigned I) const { return getTrailingExprs()[I]; } + void setSubExpr(unsigned I, Expr *E) { + if (I == 0) + setWrappedExpr(E); + else + getTrailingExprs()[I] = E; + } + + unsigned getNumAssumptions() const { return getNumSubExprs() - 1; } + Expr *getAssumption(unsigned I) { return getSubExpr(I + 1); } + const Expr *getAssumption(unsigned I) const { return getSubExpr(I + 1); } + void setAssumption(unsigned I, Expr *E) { setSubExpr(I + 1, E); } + + SourceLocation getBeginLoc() const LLVM_READONLY { + return getWrappedExpr()->getBeginLoc(); + } + SourceLocation getEndLoc() const LLVM_READONLY { + return getWrappedExpr()->getEndLoc(); + } + + typedef Expr * const * assumption_iterator; + typedef const Expr * const * const_assumption_iterator; + + assumption_iterator assumptions_begin() { + return reinterpret_cast<Expr * const *> (getTrailingExprs()) + 1; + } + const_assumption_iterator assumptions_begin() const { + return reinterpret_cast<const Expr * const *> (getTrailingExprs()) + 1; + } + assumption_iterator assumptions_end() { + return assumptions_begin() + getNumAssumptions(); + } + const_assumption_iterator assumptions_end() const { + return assumptions_begin() + getNumAssumptions(); + } + + llvm::iterator_range<assumption_iterator> assumptions() { + return llvm::make_range(assumptions_begin(), assumptions_end()); + } + llvm::iterator_range<const_assumption_iterator> assumptions() const { + return llvm::make_range(assumptions_begin(), assumptions_end()); + } + + child_range children() { + Stmt **begin = reinterpret_cast<Stmt **>(getTrailingExprs()); + return child_range(begin, begin + getNumSubExprs()); + } + + const_child_range children() const { + Stmt *const *begin = reinterpret_cast<Stmt *const *>(getTrailingExprs()); + return const_child_range(begin, begin + getNumSubExprs()); + } +}; + +// Implicitly promote a pointer with external bounds to a wide pointer. Although +// this expression doesn't belong to the CastExpr family, it should usually be +// treated as such. +class BoundsSafetyPointerPromotionExpr final + : public Expr, + private llvm::TrailingObjects<BoundsSafetyPointerPromotionExpr, Stmt *> { + friend TrailingObjects; + friend class ASTStmtWriter; + + BoundsSafetyPointerPromotionExpr(EmptyShell Empty) + : Expr(BoundsSafetyPointerPromotionExprClass, Empty) { + setPointer(nullptr); + setNullCheck(false); + } + + BoundsSafetyPointerPromotionExpr(QualType QT, Expr *Ptr, Expr *UpperBound, + Expr *LowerBound, bool NullCheck = false) + : Expr(BoundsSafetyPointerPromotionExprClass, QT, VK_PRValue, OK_Ordinary) { + setPointer(Ptr); + setNullCheck(NullCheck); + if (Stmt **lowerPtr = getLowerBoundPtr()) + *lowerPtr = LowerBound; + if (Stmt **upperPtr = getUpperBoundPtr()) + *upperPtr = UpperBound; + setDependence(computeDependence(this)); + } + + BoundsSafetyPointerPromotionExpr *unconst() const { + return const_cast<BoundsSafetyPointerPromotionExpr *>(this); + } + + Stmt **getPointerPtr(); + Stmt **getLowerBoundPtr(); + Stmt **getUpperBoundPtr(); + +public: + static BoundsSafetyPointerPromotionExpr * + Create(const ASTContext &Context, QualType QT, Expr *Ptr, Expr *UpperBound, + Expr *LowerBound = nullptr, bool NullCheck = false); + + static BoundsSafetyPointerPromotionExpr *CreateEmpty(const ASTContext &Context, + unsigned SubExprCount); + + SourceLocation getBeginLoc() const LLVM_READONLY { + return getPointer()->getBeginLoc(); + } + + SourceLocation getEndLoc() const LLVM_READONLY { + return getPointer()->getEndLoc(); + } + + Expr *getSubExprAsWritten(); + Expr *getPointer() { return cast_or_null<Expr>(*getPointerPtr()); } + Expr *getLowerBound() { return cast_or_null<Expr>(*getLowerBoundPtr()); } + Expr *getUpperBound() { return cast_or_null<Expr>(*getUpperBoundPtr()); } + const Expr *getSubExpr() const { return getPointer(); } + const Expr *getPointer() const { return unconst()->getPointer(); } + const Expr *getLowerBound() const { return unconst()->getLowerBound(); } + const Expr *getUpperBound() const { return unconst()->getUpperBound(); } + bool getNullCheck() const { + return BoundsSafetyPointerPromotionExprBits.NullCheck; + } + + Expr *getSubExpr() { return getPointer(); } + const Expr *getSubExprAsWritten() const { + return unconst()->getSubExprAsWritten(); + } + + void setPointer(Expr *newValue) { *getPointerPtr() = newValue; } + void setLowerBound(Expr *newValue) { *getLowerBoundPtr() = newValue; } + void setUpperBound(Expr *newValue) { *getUpperBoundPtr() = newValue; } + void setNullCheck(bool NullCheck) { + BoundsSafetyPointerPromotionExprBits.NullCheck = NullCheck; + } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == BoundsSafetyPointerPromotionExprClass; + } + + unsigned getNumChildren() const { + return unconst()->getLowerBoundPtr() ? 3 : 2; + } + + child_range children() { + return child_range(getPointerPtr(), getPointerPtr() + getNumChildren()); + } + + const_child_range children() const { + auto Range = unconst()->children(); + return const_child_range(Range.begin(), Range.end()); + } +}; + +/// __unsafe_forge_bidi_indexable(addr, size) +/// __unsafe_forge_single(addr) +/// __unsafe_forge_terminated_by(addr, terminator) +class ForgePtrExpr final : public Expr { + enum { ADDR, SIZE, TERMINATOR, NUM_SUBEXPRS }; + + Stmt *SubExprs[NUM_SUBEXPRS]; + + SourceLocation KWLoc; + SourceLocation RParenLoc; + +public: + ForgePtrExpr(QualType T, ExprValueKind VK, Expr *AddrExpr, Expr *SizeExpr, + Expr *TermExpr, SourceLocation KWLoc, SourceLocation RParenLoc) + : Expr(ForgePtrExprClass, T, VK, OK_Ordinary), KWLoc(KWLoc), + RParenLoc(RParenLoc) { + SubExprs[ADDR] = AddrExpr; + SubExprs[SIZE] = SizeExpr; + SubExprs[TERMINATOR] = TermExpr; + setDependence(computeDependence(this)); + } + + explicit ForgePtrExpr(EmptyShell Empty) + : Expr(ForgePtrExprClass, Empty) { + SubExprs[ADDR] = nullptr; + SubExprs[SIZE] = nullptr; + SubExprs[TERMINATOR] = nullptr; + } + + bool ForgesBidiIndexablePointer() const { + return !ForgesSinglePointer() && !ForgesTerminatedByPointer(); + } + bool ForgesSinglePointer() const { + auto BaseFA = getType()->getAs<PointerType>()->getPointerAttributes(); + return !BaseFA.hasUpperBound() && !ForgesTerminatedByPointer(); + } + bool ForgesTerminatedByPointer() const { + return getType()->isValueTerminatedType(); + } + + Expr *getAddr() const { return cast<Expr>(SubExprs[ADDR]); } + void setAddr(Expr *E) { SubExprs[ADDR] = E; } + Expr *getSize() const { return cast_or_null<Expr>(SubExprs[SIZE]); } + void setSize(Expr *E) { SubExprs[SIZE] = E; } + Expr *getTerminator() const { + return cast_or_null<Expr>(SubExprs[TERMINATOR]); + } + void setTerminator(Expr *E) { SubExprs[TERMINATOR] = E; } + + SourceLocation getBeginLoc() const LLVM_READONLY { return KWLoc; } + void setBeginLoc(SourceLocation Loc) { KWLoc = Loc; } + SourceLocation getEndLoc() const LLVM_READONLY { return RParenLoc; } + void setEndLoc(SourceLocation Loc) { RParenLoc = Loc; } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == ForgePtrExprClass; + } + + // Iterators + child_range children() { + return child_range(&SubExprs[0], &SubExprs[0] + NUM_SUBEXPRS); + } + const_child_range children() const { + return const_child_range(&SubExprs[0], &SubExprs[0] + NUM_SUBEXPRS); + } +}; + +/// GetBoundExpr - get the lower or upper bound of a pointer expression. +/// This would be a builtin if it wasn't easier to create an entirely new +/// expression subclass than to instantiate a call to a builtin from Sema. +/// Most GetBoundExpr are synthetic, and their source locations default to +class GetBoundExpr final : public Expr { +public: + enum BoundKind { BK_Lower, BK_Upper }; + +private: + Stmt *SubExpr; + BoundKind Kind; + SourceLocation BuiltinLoc, RParenLoc; + +public: + GetBoundExpr(SourceLocation BuiltinLoc, SourceLocation RParenLoc, + Expr *SubExpr, BoundKind Kind, QualType ResultType) + : Expr(GetBoundExprClass, ResultType, VK_PRValue, OK_Ordinary), + SubExpr(SubExpr), Kind(Kind), BuiltinLoc(BuiltinLoc), RParenLoc(RParenLoc) + { + setDependence(computeDependence(this)); + } + + GetBoundExpr(Expr *SubExpr, BoundKind Kind, QualType ResultType) + : GetBoundExpr(SourceLocation(), SourceLocation(), SubExpr, Kind, + ResultType) + { } + + explicit GetBoundExpr(EmptyShell Empty) + : Expr(GetBoundExprClass, Empty), SubExpr(nullptr), Kind(BK_Lower) { + } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == GetBoundExprClass; + } + + SourceLocation getBuiltinLoc() const LLVM_READONLY { return BuiltinLoc; } + void setBuiltinLoc(SourceLocation Loc) { BuiltinLoc = Loc; } + + SourceLocation getRParenLoc() const LLVM_READONLY { return RParenLoc; } + void setRParenLoc(SourceLocation Loc) { RParenLoc = Loc; } + + SourceLocation getBeginLoc() const LLVM_READONLY { + return BuiltinLoc.isInvalid() ? getSubExpr()->getBeginLoc() : BuiltinLoc; + } + SourceLocation getEndLoc() const LLVM_READONLY { + return RParenLoc.isInvalid() ? getSubExpr()->getEndLoc() : RParenLoc; + } + + child_range children() { + return child_range(&SubExpr, &SubExpr + 1); + } + + const_child_range children() const { + return const_child_range(&SubExpr, &SubExpr + 1); + } + + Expr *getSubExpr() { return cast<Expr>(SubExpr); } + const Expr *getSubExpr() const { return cast<Expr>(SubExpr); } + void setSubExpr(Expr *NewValue) { SubExpr = NewValue; } + + BoundKind getBoundKind() const { return Kind; } + void setBoundKind(BoundKind K) { Kind = K; } +}; + +enum class BoundsCheckKind : unsigned { + FlexibleArrayCountAssign, + FlexibleArrayCountCast, + FlexibleArrayCountDeref +}; + +/// PredefinedBoundsCheckExpr - AST representation of bounds checks that +/// BoundsSafety added. This is a wrapper expression to wrap the expression +/// (GuardedExpr) that will be executed if the bounds check succeeds. The +/// subexpressions except the first one (GuardedExpr) are used for bounds check +/// whose semantics is determined by the kind of bounds check (BoundsCheckKind). +/// Most subexpressions are likely to be OpaqueValueExpr to avoid re-evaluating +/// expressions. As bounds checks are necessarily implicit, the expression uses +/// the source location of the wrapped expression. +class PredefinedBoundsCheckExpr final + : public Expr, + public llvm::TrailingObjects<PredefinedBoundsCheckExpr, Stmt *> { + static constexpr unsigned CheckArgsOffset = 1; + + PredefinedBoundsCheckExpr(Expr *GuardedExpr, BoundsCheckKind Kind, + ArrayRef<Expr *> CheckArgs); + PredefinedBoundsCheckExpr(EmptyShell Empty, unsigned NumChildren) + : Expr(PredefinedBoundsCheckExprClass, Empty) { + PredefinedBoundsCheckExprBits.NumChildren = NumChildren; + } + + Stmt **getTrailingStmts() { + return const_cast<Stmt **>(getTrailingObjects<Stmt *>()); + } + + Stmt *const *getTrailingStmts() const { return getTrailingObjects<Stmt *>(); } + + Expr **getSubExprs() { return reinterpret_cast<Expr **>(getTrailingStmts()); } + + Expr *const *getSubExprs() const { + return reinterpret_cast<Expr *const *>(getTrailingStmts()); + } + +public: + static PredefinedBoundsCheckExpr *Create(ASTContext &Ctx, Expr *GuardedExpr, + BoundsCheckKind Kind, + ArrayRef<Expr *> CheckArgs); + + static PredefinedBoundsCheckExpr *CreateEmpty(ASTContext &Ctx, + unsigned NumChildren); + + static bool classof(const Stmt *T) { + return T->getStmtClass() == PredefinedBoundsCheckExprClass; + } + + SourceLocation getBeginLoc() const LLVM_READONLY { + return getGuardedExpr()->getBeginLoc(); + } + SourceLocation getEndLoc() const LLVM_READONLY { + return getGuardedExpr()->getEndLoc(); + } + + unsigned getNumSubExprs() const { + return PredefinedBoundsCheckExprBits.NumChildren; + } + + unsigned getNumCheckArgs() const { + assert(getNumSubExprs() >= CheckArgsOffset); + return getNumSubExprs() - CheckArgsOffset; + } + + static constexpr unsigned getCheckArgsOffset() { return CheckArgsOffset; } + + typedef Expr *const *checkargs_iterator; + typedef const Expr *const *const_checkargs_iterator; + + checkargs_iterator checkargs_begin() { + return reinterpret_cast<Expr *const *>(getSubExprs() + + getCheckArgsOffset()); + } + const_checkargs_iterator checkargs_begin() const { + return reinterpret_cast<const Expr *const *>(getSubExprs() + + getCheckArgsOffset()); + } + checkargs_iterator checkargs_end() { + return checkargs_begin() + getNumCheckArgs(); + } + const_checkargs_iterator checkargs_end() const { + return checkargs_begin() + getNumCheckArgs(); + } + + llvm::iterator_range<checkargs_iterator> checkargs() { + return llvm::make_range(checkargs_begin(), checkargs_end()); + } + llvm::iterator_range<const_checkargs_iterator> checkargs() const { + return llvm::make_range(checkargs_begin(), checkargs_end()); + } + + child_range children() { + Stmt **begin = getTrailingStmts(); + return child_range(begin, begin + getNumSubExprs()); + } + + const_child_range children() const { + Stmt *const *begin = getTrailingStmts(); + return const_child_range(begin, begin + getNumSubExprs()); + } + + BoundsCheckKind getKind() const { + return static_cast<BoundsCheckKind>(PredefinedBoundsCheckExprBits.Kind); + } + + StringRef getKindName() const; + + Expr *getGuardedExpr() { return getSubExpr(0); } + const Expr *getGuardedExpr() const { return getSubExpr(0); } + void setGuardedExpr(Expr *NewValue) { setSubExpr(0, NewValue); } + + Expr *getSubExpr(unsigned i) { + assert(i < getNumSubExprs()); + return cast<Expr>(getTrailingStmts()[i]); + } + + const Expr *getSubExpr(unsigned i) const { + assert(i < getNumSubExprs()); + return cast<Expr>(getTrailingStmts()[i]); + } + + // This returns the pointer to the base struct with flexible array member. + const Expr *getFamBasePtr() const { + switch (getKind()) { + case BoundsCheckKind::FlexibleArrayCountAssign: + case BoundsCheckKind::FlexibleArrayCountCast: + case BoundsCheckKind::FlexibleArrayCountDeref: + return getSubExpr(CheckArgsOffset); + } + llvm_unreachable("Unsupported BoundsCheckKind"); + } + + const Expr *getFamPtr() const { + switch (getKind()) { + case BoundsCheckKind::FlexibleArrayCountAssign: + case BoundsCheckKind::FlexibleArrayCountCast: + case BoundsCheckKind::FlexibleArrayCountDeref: + return getSubExpr(CheckArgsOffset + 1); + } + llvm_unreachable("Unsupported BoundsCheckKind"); + } + + const Expr *getFamCount() const { + switch (getKind()) { + case BoundsCheckKind::FlexibleArrayCountAssign: + case BoundsCheckKind::FlexibleArrayCountCast: + case BoundsCheckKind::FlexibleArrayCountDeref: + return getSubExpr(CheckArgsOffset + 2); + } + llvm_unreachable("Unsupported BoundsCheckKind"); + } + + void setSubExpr(unsigned i, Expr *E) { + assert(i < getNumSubExprs()); + getTrailingStmts()[i] = E; + } +}; + +/// BoundsCheckExpr - AST representation of a bounds check that BoundsSafety added. +/// This wraps the expression that will be evaluated if the bounds check +/// succeeds or evaluated before the bounds checks if PostGuard is true. The object +/// also holds all the bounds to check. Many operands are +/// likely to be OpaqueValueExpr to avoid re-evaluating expressions. As bounds +/// checks are necessarily implicit, they never have a source location. +class BoundsCheckExpr final : + public Expr, public llvm::TrailingObjects<BoundsCheckExpr, Stmt *> { + BoundsCheckExpr(Expr *GuardedExpr, Expr *Cond, + ArrayRef<OpaqueValueExpr *> CommonExprs); + BoundsCheckExpr(EmptyShell Empty, unsigned NumChildren) + : Expr(BoundsCheckExprClass, Empty) { + BoundsCheckExprBits.NumChildren = NumChildren; + } + + Stmt **getTrailingStmts() { + return const_cast<Stmt **>(getTrailingObjects<Stmt *>()); + } + + Stmt *const *getTrailingStmts() const { + return getTrailingObjects<Stmt *>(); + } + + Expr **getSubExprs() { + return reinterpret_cast<Expr **>(getTrailingStmts()); + } + + Expr *const *getSubExprs() const { + return reinterpret_cast<Expr *const *>(getTrailingStmts()); + } + +public: + static BoundsCheckExpr *Create(ASTContext &Ctx, Expr *GuardedExpr, + Expr *Cond, ArrayRef<OpaqueValueExpr *> CommonExprs); + + static BoundsCheckExpr *CreateEmpty(ASTContext &Ctx, unsigned NumChildren); + + static bool classof(const Stmt *T) { + return T->getStmtClass() == BoundsCheckExprClass; + } + + SourceLocation getBeginLoc() const LLVM_READONLY { + return getGuardedExpr()->getBeginLoc(); + } + SourceLocation getEndLoc() const LLVM_READONLY { + return getGuardedExpr()->getEndLoc(); + } + + unsigned getNumSubExprs() const { return BoundsCheckExprBits.NumChildren; } + unsigned getNumCommonExprs() const { + assert(getNumSubExprs() >= 2); + return getNumSubExprs() - 2; + } + + typedef OpaqueValueExpr * const * opaquevalues_iterator; + typedef const OpaqueValueExpr * const * const_opaquevalues_iterator; + + opaquevalues_iterator opaquevalues_begin() { + return reinterpret_cast<OpaqueValueExpr * const *> (getSubExprs() + 2); + } + const_opaquevalues_iterator opaquevalues_begin() const { + return reinterpret_cast<const OpaqueValueExpr * const *> (getSubExprs() + 2); + } + opaquevalues_iterator opaquevalues_end() { + return opaquevalues_begin() + getNumCommonExprs(); + } + const_opaquevalues_iterator opaquevalues_end() const { + return opaquevalues_begin() + getNumCommonExprs(); + } + + llvm::iterator_range<opaquevalues_iterator> opaquevalues() { + return llvm::make_range(opaquevalues_begin(), opaquevalues_end()); + } + llvm::iterator_range<const_opaquevalues_iterator> opaquevalues() const { + return llvm::make_range(opaquevalues_begin(), opaquevalues_end()); + } + + child_range children() { + Stmt **begin = getTrailingStmts(); + return child_range(begin, begin + getNumSubExprs()); + } + + const_child_range children() const { + Stmt *const *begin = getTrailingStmts(); + return const_child_range(begin, begin + getNumSubExprs()); + } + + Expr *getGuardedExpr() { return getSubExpr(0); } + const Expr *getGuardedExpr() const { return getSubExpr(0); } + void setGuardedExpr(Expr *NewValue) { setSubExpr(0, NewValue); } + + Expr *getCond() { return getSubExpr(1); } + const Expr *getCond() const { return getSubExpr(1); } + void setCond(Expr *Cond) { setSubExpr(1, Cond); } + + Expr *getSubExpr(unsigned i) { + assert(i < getNumSubExprs()); + return cast<Expr>(getTrailingStmts()[i]); + } + + const Expr *getSubExpr(unsigned i) const { + assert(i < getNumSubExprs()); + return cast<Expr>(getTrailingStmts()[i]); + } + + void setSubExpr(unsigned i, Expr *E) { + assert(i < getNumSubExprs()); + getTrailingStmts()[i] = E; + } +}; + +class MaterializeSequenceExpr final : + public Expr, public llvm::TrailingObjects<MaterializeSequenceExpr, Expr *> { + + MaterializeSequenceExpr(Expr *WrappedExpr, ArrayRef<OpaqueValueExpr *> Values, bool Unbind) + : Expr(MaterializeSequenceExprClass, WrappedExpr->getType(), + WrappedExpr->getValueKind(), + WrappedExpr->getObjectKind()) { + MaterializeSequenceExprBits.NumExprs = Values.size() + 1; + MaterializeSequenceExprBits.Unbind = Unbind; + getSubExprs()[0] = WrappedExpr; + std::copy(Values.begin(), Values.end(), getSubExprs() + 1); + setDependence(computeDependence(this)); + } + + MaterializeSequenceExpr(EmptyShell Empty, unsigned NumExprs) + : Expr(MaterializeSequenceExprClass, Empty) { + MaterializeSequenceExprBits.NumExprs = NumExprs; + } + + Expr **getSubExprs() { + return getTrailingObjects<Expr *>(); + } + + Expr *const *getSubExprs() const { + return getTrailingObjects<Expr *>(); + } + + friend class ASTStmtReader; +public: + static MaterializeSequenceExpr *Create(const ASTContext &Ctx, Expr *WrappedExpr, + ArrayRef<OpaqueValueExpr *> Values, + bool Unbind = false); + + static MaterializeSequenceExpr *CreateEmpty(const ASTContext &Ctx, unsigned NumExprs); + + static bool classof(const Stmt *T) { + return T->getStmtClass() == MaterializeSequenceExprClass; + } + + unsigned getNumSubExprs() const { + return MaterializeSequenceExprBits.NumExprs; + } + + bool isBinding() const { + return !isUnbinding(); + } + + bool isUnbinding() const { + return MaterializeSequenceExprBits.Unbind; + } + + unsigned getNumOpaqueValueExprs() const { + assert(getNumSubExprs() > 1); + return getNumSubExprs() - 1; + } + + Expr *getWrappedExpr() const { + assert(getNumSubExprs() > 0); + return getSubExprs()[0]; + } + + typedef OpaqueValueExpr * const * opaquevalues_iterator; + typedef const OpaqueValueExpr * const * const_opaquevalues_iterator; + + opaquevalues_iterator opaquevalues_begin() { + return reinterpret_cast<OpaqueValueExpr * const *> (getSubExprs() + 1); + } + const_opaquevalues_iterator opaquevalues_begin() const { + return reinterpret_cast<const OpaqueValueExpr * const *> (getSubExprs() + 1); + } + opaquevalues_iterator opaquevalues_end() { + return opaquevalues_begin() + getNumOpaqueValueExprs(); + } + const_opaquevalues_iterator opaquevalues_end() const { + return opaquevalues_begin() + getNumOpaqueValueExprs(); + } + + llvm::iterator_range<opaquevalues_iterator> opaquevalues() { + return llvm::make_range(opaquevalues_begin(), opaquevalues_end()); + } + llvm::iterator_range<const_opaquevalues_iterator> opaquevalues() const { + return llvm::make_range(opaquevalues_begin(), opaquevalues_end()); + } + + SourceLocation getBeginLoc() const { + return getWrappedExpr()->getBeginLoc(); + } + + SourceLocation getEndLoc() const { + return getWrappedExpr()->getEndLoc(); + } + + child_range children() { + Stmt **begin = reinterpret_cast<Stmt **>(getSubExprs()); + return child_range(begin, + begin + getNumSubExprs()); + } + + const_child_range children() const { + Stmt *const *begin = reinterpret_cast<Stmt *const *>(getSubExprs()); + return const_child_range(begin, + begin + getNumSubExprs()); + } +}; + +/// TerminatedByToIndexableExpr - The AST representation of +/// __builtin_terminated_by_to_indexable() and +/// __builtin_unsafe_terminated_by_to_indexable(). +class TerminatedByToIndexableExpr final : public Expr { +private: + enum { POINTER, TERMINATOR, END_EXPR }; + Stmt *SubExprs[END_EXPR]; + bool IncludeTerminator; + SourceLocation BuiltinLoc, RParenLoc; + +public: + TerminatedByToIndexableExpr(SourceLocation BuiltinLoc, + SourceLocation RParenLoc, Expr *Pointer, + Expr *Terminator, bool IncludeTerminator, + QualType ResultType) + : Expr(TerminatedByToIndexableExprClass, ResultType, VK_PRValue, + OK_Ordinary), + SubExprs{Pointer, Terminator}, IncludeTerminator(IncludeTerminator), + BuiltinLoc(BuiltinLoc), RParenLoc(RParenLoc) {} + + TerminatedByToIndexableExpr(Expr *Pointer, Expr *Terminator, + QualType ResultType) + : TerminatedByToIndexableExpr(SourceLocation(), SourceLocation(), Pointer, + Terminator, false, ResultType) {} + + explicit TerminatedByToIndexableExpr(EmptyShell Empty) + : Expr(TerminatedByToIndexableExprClass, Empty), SubExprs{}, + IncludeTerminator(false) {} + + static bool classof(const Stmt *T) { + return T->getStmtClass() == TerminatedByToIndexableExprClass; + } + + SourceLocation getBuiltinLoc() const LLVM_READONLY { return BuiltinLoc; } + void setBuiltinLoc(SourceLocation Loc) { BuiltinLoc = Loc; } + + SourceLocation getRParenLoc() const LLVM_READONLY { return RParenLoc; } + void setRParenLoc(SourceLocation Loc) { RParenLoc = Loc; } + + SourceLocation getBeginLoc() const LLVM_READONLY { + return BuiltinLoc.isInvalid() ? getPointer()->getBeginLoc() : BuiltinLoc; + } + SourceLocation getEndLoc() const LLVM_READONLY { + return RParenLoc.isInvalid() ? getPointer()->getEndLoc() : RParenLoc; + } + + child_range children() { return child_range(SubExprs, SubExprs + END_EXPR); } + + const_child_range children() const { + return const_child_range(SubExprs, SubExprs + END_EXPR); + } + + Expr *getPointer() const { return cast<Expr>(SubExprs[POINTER]); } + void setPointer(Expr *E) { SubExprs[POINTER] = E; } + Expr *getTerminator() const { return cast<Expr>(SubExprs[TERMINATOR]); } + void setTerminator(Expr *E) { SubExprs[TERMINATOR] = E; } + + bool includesTerminator() const { return IncludeTerminator; } + void setIncludeTerminator(bool Include) { IncludeTerminator = Include; } +}; + +/// TerminatedByFromIndexableExpr - The AST representation of +/// __builtin_unsafe_terminated_by_from_indexable(). +class TerminatedByFromIndexableExpr final : public Expr { +private: + enum { POINTER, POINTER_TO_TERMINATOR, END_EXPR }; + Stmt *SubExprs[END_EXPR]; + SourceLocation BuiltinLoc, RParenLoc; + +public: + TerminatedByFromIndexableExpr(SourceLocation BuiltinLoc, + SourceLocation RParenLoc, Expr *Pointer, + Expr *PointerToTerminator, QualType ResultType) + : Expr(TerminatedByFromIndexableExprClass, ResultType, VK_PRValue, + OK_Ordinary), + SubExprs{Pointer, PointerToTerminator}, BuiltinLoc(BuiltinLoc), + RParenLoc(RParenLoc) {} + + TerminatedByFromIndexableExpr(Expr *Pointer, Expr *PointerToTerminator, + QualType ResultType) + : TerminatedByFromIndexableExpr(SourceLocation(), SourceLocation(), + Pointer, PointerToTerminator, + ResultType) {} + + explicit TerminatedByFromIndexableExpr(EmptyShell Empty) + : Expr(TerminatedByFromIndexableExprClass, Empty), SubExprs{} {} + + static bool classof(const Stmt *T) { + return T->getStmtClass() == TerminatedByFromIndexableExprClass; + } + + SourceLocation getBuiltinLoc() const LLVM_READONLY { return BuiltinLoc; } + void setBuiltinLoc(SourceLocation Loc) { BuiltinLoc = Loc; } + + SourceLocation getRParenLoc() const LLVM_READONLY { return RParenLoc; } + void setRParenLoc(SourceLocation Loc) { RParenLoc = Loc; } + + SourceLocation getBeginLoc() const LLVM_READONLY { + return BuiltinLoc.isInvalid() ? getPointer()->getBeginLoc() : BuiltinLoc; + } + SourceLocation getEndLoc() const LLVM_READONLY { + if (RParenLoc.isValid()) + return RParenLoc; + return getPointerToTerminator() ? getPointerToTerminator()->getEndLoc() + : SourceLocation(); + } + + child_range children() { return child_range(SubExprs, SubExprs + END_EXPR); } + + const_child_range children() const { + return const_child_range(SubExprs, SubExprs + END_EXPR); + } + + Expr *getPointer() const { return cast<Expr>(SubExprs[POINTER]); } + void setPointer(Expr *E) { SubExprs[POINTER] = E; } + Expr *getPointerToTerminator() const { + return cast_or_null<Expr>(SubExprs[POINTER_TO_TERMINATOR]); + } + void setPointerToTerminator(Expr *E) { SubExprs[POINTER_TO_TERMINATOR] = E; } +}; +/* TO_UPSTREAM(BoundsSafety) OFF */ + /// A builtin binary operation expression such as "x + y" or "x <= y". /// /// This expression node kind describes a builtin binary operation, @@ -5082,6 +5941,28 @@ class InitListExpr : public Expr { return cast_or_null<Expr>(InitExprs[Init]); } + /* TO_UPSTREAM(BoundsSafety) ON*/ + Expr *getInitForField(const ValueDecl *VD) { + assert(isSemanticForm()); + if (auto FD = dyn_cast<FieldDecl>(VD)) { + unsigned FieldIdx = 0; + for (FieldDecl *Sibling : FD->getParent()->fields()) { + if (Sibling->isUnnamedBitField()) + continue; + if (Sibling == FD) + break; + ++FieldIdx; + } + if (FieldIdx >= getNumInits()) + return nullptr; + return getInit(FieldIdx); + } + + auto IFD = cast<IndirectFieldDecl>(VD); + return getInitForField(IFD->getAnonField()); + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + void setInit(unsigned Init, Expr *expr) { assert(Init < getNumInits() && "Initializer access out of range!"); InitExprs[Init] = expr; diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h index c2feac525c1ea..45cfd7bfb7f92 100644 --- a/clang/include/clang/AST/ExprCXX.h +++ b/clang/include/clang/AST/ExprCXX.h @@ -3229,7 +3229,7 @@ class UnresolvedLookupExpr final const DeclarationNameInfo &NameInfo, bool RequiresADL, const TemplateArgumentListInfo *TemplateArgs, UnresolvedSetIterator Begin, UnresolvedSetIterator End, - bool KnownDependent); + bool KnownDependent, bool KnownInstantiationDependent); UnresolvedLookupExpr(EmptyShell Empty, unsigned NumResults, bool HasTemplateKWAndArgsInfo); @@ -3248,7 +3248,7 @@ class UnresolvedLookupExpr final NestedNameSpecifierLoc QualifierLoc, const DeclarationNameInfo &NameInfo, bool RequiresADL, UnresolvedSetIterator Begin, UnresolvedSetIterator End, - bool KnownDependent); + bool KnownDependent, bool KnownInstantiationDependent); // After canonicalization, there may be dependent template arguments in // CanonicalConverted But none of Args is dependent. When any of @@ -3258,7 +3258,8 @@ class UnresolvedLookupExpr final NestedNameSpecifierLoc QualifierLoc, SourceLocation TemplateKWLoc, const DeclarationNameInfo &NameInfo, bool RequiresADL, const TemplateArgumentListInfo *Args, UnresolvedSetIterator Begin, - UnresolvedSetIterator End, bool KnownDependent); + UnresolvedSetIterator End, bool KnownDependent, + bool KnownInstantiationDependent); static UnresolvedLookupExpr *CreateEmpty(const ASTContext &Context, unsigned NumResults, diff --git a/clang/include/clang/AST/ExprObjC.h b/clang/include/clang/AST/ExprObjC.h index f833916c91aa5..552a0768e2201 100644 --- a/clang/include/clang/AST/ExprObjC.h +++ b/clang/include/clang/AST/ExprObjC.h @@ -1693,30 +1693,78 @@ class ObjCBridgedCastExpr final /// be used in the condition of an \c if, but it is also usable as top level /// expressions. /// -class ObjCAvailabilityCheckExpr : public Expr { +class ObjCAvailabilityCheckExpr final + : public Expr, + private llvm::TrailingObjects<ObjCAvailabilityCheckExpr, char> { +public: + struct VersionAsWritten { + /// Platform version canonicalized for use with availability checks. + VersionTuple Version; + /// Platform version as written in the source. + VersionTuple SourceVersion; + }; + +private: friend class ASTStmtReader; + friend llvm::TrailingObjects<ObjCAvailabilityCheckExpr, char>; - VersionTuple VersionToCheck; + VersionAsWritten VersionToCheck; SourceLocation AtLoc, RParen; + void setHasDomainName(bool V) { + ObjCAvailabilityCheckExprBits.HasDomainName = V; + } + + ObjCAvailabilityCheckExpr(SourceLocation AtLoc, SourceLocation RParen, + QualType Ty, StringRef DomainName) + : Expr(ObjCAvailabilityCheckExprClass, Ty, VK_PRValue, OK_Ordinary), + VersionToCheck(), AtLoc(AtLoc), RParen(RParen) { + setDependence(ExprDependence::None); + setHasDomainName(true); + strcpy(getTrailingObjects<char>(), DomainName.data()); + } + public: - ObjCAvailabilityCheckExpr(VersionTuple VersionToCheck, SourceLocation AtLoc, + ObjCAvailabilityCheckExpr(VersionAsWritten VersionToCheck, + SourceLocation AtLoc, SourceLocation RParen, QualType Ty) : Expr(ObjCAvailabilityCheckExprClass, Ty, VK_PRValue, OK_Ordinary), VersionToCheck(VersionToCheck), AtLoc(AtLoc), RParen(RParen) { setDependence(ExprDependence::None); + setHasDomainName(false); } + static ObjCAvailabilityCheckExpr * + CreateAvailabilityFeatureCheck(SourceLocation AtLoc, SourceLocation RParen, + QualType Ty, StringRef DomainName, + const ASTContext &C); + explicit ObjCAvailabilityCheckExpr(EmptyShell Shell) - : Expr(ObjCAvailabilityCheckExprClass, Shell) {} + : Expr(ObjCAvailabilityCheckExprClass, Shell) { + setHasDomainName(false); + } + + static ObjCAvailabilityCheckExpr * + CreateEmpty(const ASTContext &C, Stmt::EmptyShell Empty, size_t FeaturesLen); SourceLocation getBeginLoc() const { return AtLoc; } SourceLocation getEndLoc() const { return RParen; } SourceRange getSourceRange() const { return {AtLoc, RParen}; } /// This may be '*', in which case this should fold to true. - bool hasVersion() const { return !VersionToCheck.empty(); } - VersionTuple getVersion() const { return VersionToCheck; } + bool hasVersion() const { return !VersionToCheck.Version.empty(); } + VersionTuple getVersion() const { return VersionToCheck.Version; } + VersionTuple getVersionAsWritten() const { + return VersionToCheck.SourceVersion; + } + + bool hasDomainName() const { + return ObjCAvailabilityCheckExprBits.HasDomainName; + } + StringRef getDomainName() const { + assert(hasDomainName()); + return getTrailingObjects<char>(); + } child_range children() { return child_range(child_iterator(), child_iterator()); diff --git a/clang/include/clang/AST/ExternalASTSource.h b/clang/include/clang/AST/ExternalASTSource.h index 385c32edbae0f..582ed7c65f58c 100644 --- a/clang/include/clang/AST/ExternalASTSource.h +++ b/clang/include/clang/AST/ExternalASTSource.h @@ -25,10 +25,12 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/iterator.h" #include "llvm/Support/PointerLikeTypeTraits.h" +#include <algorithm> #include <cassert> #include <cstddef> #include <cstdint> #include <iterator> +#include <new> #include <optional> #include <utility> @@ -326,29 +328,49 @@ struct LazyOffsetPtr { /// /// If the low bit is clear, a pointer to the AST node. If the low /// bit is set, the upper 63 bits are the offset. - mutable uint64_t Ptr = 0; + static constexpr size_t DataSize = std::max(sizeof(uint64_t), sizeof(T *)); + alignas(uint64_t) alignas(T *) mutable unsigned char Data[DataSize] = {}; + + unsigned char GetLSB() const { + return Data[llvm::sys::IsBigEndianHost ? DataSize - 1 : 0]; + } + + template <typename U> U &As(bool New) const { + unsigned char *Obj = + Data + (llvm::sys::IsBigEndianHost ? DataSize - sizeof(U) : 0); + if (New) + return *new (Obj) U; + return *std::launder(reinterpret_cast<U *>(Obj)); + } + + T *&GetPtr() const { return As<T *>(false); } + uint64_t &GetU64() const { return As<uint64_t>(false); } + void SetPtr(T *Ptr) const { As<T *>(true) = Ptr; } + void SetU64(uint64_t U64) const { As<uint64_t>(true) = U64; } public: LazyOffsetPtr() = default; - explicit LazyOffsetPtr(T *Ptr) : Ptr(reinterpret_cast<uint64_t>(Ptr)) {} + explicit LazyOffsetPtr(T *Ptr) : Data() { SetPtr(Ptr); } - explicit LazyOffsetPtr(uint64_t Offset) : Ptr((Offset << 1) | 0x01) { + explicit LazyOffsetPtr(uint64_t Offset) : Data() { assert((Offset << 1 >> 1) == Offset && "Offsets must require < 63 bits"); if (Offset == 0) - Ptr = 0; + SetPtr(nullptr); + else + SetU64((Offset << 1) | 0x01); } LazyOffsetPtr &operator=(T *Ptr) { - this->Ptr = reinterpret_cast<uint64_t>(Ptr); + SetPtr(Ptr); return *this; } LazyOffsetPtr &operator=(uint64_t Offset) { assert((Offset << 1 >> 1) == Offset && "Offsets must require < 63 bits"); if (Offset == 0) - Ptr = 0; + SetPtr(nullptr); else - Ptr = (Offset << 1) | 0x01; + SetU64((Offset << 1) | 0x01); return *this; } @@ -356,15 +378,15 @@ struct LazyOffsetPtr { /// Whether this pointer is non-NULL. /// /// This operation does not require the AST node to be deserialized. - explicit operator bool() const { return Ptr != 0; } + explicit operator bool() const { return isOffset() || GetPtr() != nullptr; } /// Whether this pointer is non-NULL. /// /// This operation does not require the AST node to be deserialized. - bool isValid() const { return Ptr != 0; } + bool isValid() const { return isOffset() || GetPtr() != nullptr; } /// Whether this pointer is currently stored as an offset. - bool isOffset() const { return Ptr & 0x01; } + bool isOffset() const { return GetLSB() & 0x01; } /// Retrieve the pointer to the AST node that this lazy pointer points to. /// @@ -375,9 +397,9 @@ struct LazyOffsetPtr { if (isOffset()) { assert(Source && "Cannot deserialize a lazy pointer without an AST source"); - Ptr = reinterpret_cast<uint64_t>((Source->*Get)(OffsT(Ptr >> 1))); + SetPtr((Source->*Get)(OffsT(GetU64() >> 1))); } - return reinterpret_cast<T*>(Ptr); + return GetPtr(); } /// Retrieve the address of the AST node pointer. Deserializes the pointee if @@ -385,7 +407,7 @@ struct LazyOffsetPtr { T **getAddressOfPointer(ExternalASTSource *Source) const { // Ensure the integer is in pointer form. (void)get(Source); - return reinterpret_cast<T**>(&Ptr); + return &GetPtr(); } }; diff --git a/clang/include/clang/AST/FormatString.h b/clang/include/clang/AST/FormatString.h index a074dd23e2ad4..3560766433fe2 100644 --- a/clang/include/clang/AST/FormatString.h +++ b/clang/include/clang/AST/FormatString.h @@ -292,7 +292,7 @@ class ArgType { }; private: - const Kind K; + Kind K; QualType T; const char *Name = nullptr; bool Ptr = false; @@ -338,6 +338,7 @@ class ArgType { } MatchKind matchesType(ASTContext &C, QualType argTy) const; + MatchKind matchesArgType(ASTContext &C, const ArgType &other) const; QualType getRepresentativeType(ASTContext &C) const; diff --git a/clang/include/clang/AST/IgnoreExpr.h b/clang/include/clang/AST/IgnoreExpr.h index 917bada61fa6f..e7a2c49fde863 100644 --- a/clang/include/clang/AST/IgnoreExpr.h +++ b/clang/include/clang/AST/IgnoreExpr.h @@ -45,6 +45,35 @@ const Expr *IgnoreExprNodes(const Expr *E, FnTys &&...Fns) { return IgnoreExprNodes(const_cast<Expr *>(E), std::forward<FnTys>(Fns)...); } +/* TO_UPSTREAM(BoundsSafety) ON */ +inline Expr *IgnoreBoundsSafetyImplicitImpl( + Expr *E, llvm::SmallPtrSetImpl<const OpaqueValueExpr *> &BoundValues) { + + if (auto *FPPE = dyn_cast<BoundsSafetyPointerPromotionExpr>(E)) + return FPPE->getSubExpr(); + + if (auto *AE = dyn_cast<AssumptionExpr>(E)) + return AE->getWrappedExpr(); + + if (auto *MSE = dyn_cast<MaterializeSequenceExpr>(E)) { + if (!MSE->isUnbinding()) + BoundValues.insert(MSE->opaquevalues_begin(), MSE->opaquevalues_end()); + return MSE->getWrappedExpr(); + } + + if (auto *BCE = dyn_cast<BoundsCheckExpr>(E)) { + BoundValues.insert(BCE->opaquevalues_begin(), BCE->opaquevalues_end()); + return BCE->getGuardedExpr(); + } + + if (auto *Opaque = dyn_cast<OpaqueValueExpr>(E)) + if (BoundValues.count(Opaque)) + return Opaque->getSourceExpr(); + + return E; +} +/* TO_UPSTREAM(BoundsSafety) OFF */ + inline Expr *IgnoreImplicitCastsSingleStep(Expr *E) { if (auto *ICE = dyn_cast<ImplicitCastExpr>(E)) return ICE->getSubExpr(); diff --git a/clang/include/clang/AST/NestedNameSpecifier.h b/clang/include/clang/AST/NestedNameSpecifier.h index a1d9e30e660d1..ddac18294c06c 100644 --- a/clang/include/clang/AST/NestedNameSpecifier.h +++ b/clang/include/clang/AST/NestedNameSpecifier.h @@ -236,6 +236,23 @@ class NestedNameSpecifier : public llvm::FoldingSetNode { void dump() const; void dump(llvm::raw_ostream &OS) const; void dump(llvm::raw_ostream &OS, const LangOptions &LO) const; + + /// \brief Compute the qualification required to get from the current context + /// (\p CurContext) to the target context (\p TargetContext). + /// + /// \param Context the AST context in which the qualification will be used. + /// + /// \param CurContext the context where an entity is being named, which is + /// typically based on the current scope. + /// + /// \param TargetContext the context in which the named entity actually + /// resides. + /// + /// \returns a nested name specifier that refers into the target context, or + /// NULL if no qualification is needed. + static NestedNameSpecifier * + getRequiredQualification(ASTContext &Context, const DeclContext *CurContext, + const DeclContext *TargetContext); }; /// A C++ nested-name-specifier augmented with source location diff --git a/clang/include/clang/AST/NonTrivialTypeVisitor.h b/clang/include/clang/AST/NonTrivialTypeVisitor.h index cf320c8a478af..80f247347f1b3 100644 --- a/clang/include/clang/AST/NonTrivialTypeVisitor.h +++ b/clang/include/clang/AST/NonTrivialTypeVisitor.h @@ -93,6 +93,8 @@ struct CopiedTypeVisitor { return asDerived().visitARCStrong(FT, std::forward<Ts>(Args)...); case QualType::PCK_ARCWeak: return asDerived().visitARCWeak(FT, std::forward<Ts>(Args)...); + case QualType::PCK_PtrAuth: + return asDerived().visitPtrAuth(FT, std::forward<Ts>(Args)...); case QualType::PCK_Struct: return asDerived().visitStruct(FT, std::forward<Ts>(Args)...); case QualType::PCK_Trivial: diff --git a/clang/include/clang/AST/ObjCMethodReferenceInfo.h b/clang/include/clang/AST/ObjCMethodReferenceInfo.h new file mode 100644 index 0000000000000..0a3046f2a6593 --- /dev/null +++ b/clang/include/clang/AST/ObjCMethodReferenceInfo.h @@ -0,0 +1,43 @@ +//===- ObjcMethodReferenceInfo.h - API for ObjC method tracing --*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines APIs for ObjC method tracing. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_OBJC_METHOD_REFERENCE_INFO_H +#define LLVM_CLANG_OBJC_METHOD_REFERENCE_INFO_H + +#include "llvm/ADT/SmallVector.h" +#include "llvm/Support/raw_ostream.h" +#include <map> +#include <string> + +namespace clang { + +class ObjCMethodDecl; + +struct ObjCMethodReferenceInfo { + static constexpr unsigned FormatVersion = 1; + std::string ToolName, ToolVersion; + std::string Target, TargetVariant; + + /// Paths to the files in which ObjC methods are referenced. + llvm::SmallVector<std::string, 4> FilePaths; + + /// A map from the index of a file in FilePaths to the list of ObjC methods. + std::map<unsigned, llvm::SmallVector<const ObjCMethodDecl *, 4>> References; +}; + +/// This function serializes the ObjC message tracing information in JSON. +void serializeObjCMethodReferencesAsJson(const ObjCMethodReferenceInfo &Info, + llvm::raw_ostream &OS); + +} // namespace clang + +#endif // LLVM_CLANG_OBJC_METHOD_REFERENCE_INFO_H diff --git a/clang/include/clang/AST/OperationKinds.def b/clang/include/clang/AST/OperationKinds.def index 8788b8ff0ef0a..c9d631b09cdda 100644 --- a/clang/include/clang/AST/OperationKinds.def +++ b/clang/include/clang/AST/OperationKinds.def @@ -361,6 +361,9 @@ CAST_OPERATION(AddressSpaceConversion) // Convert an integer initializer to an OpenCL sampler. CAST_OPERATION(IntToOCLSampler) +// BoundsSafety: Casting between pointer types with different -fbounds-safety attributes +CAST_OPERATION(BoundsSafetyPointerCast) + // Truncate a vector type by dropping elements from the end (HLSL only). CAST_OPERATION(HLSLVectorTruncation) diff --git a/clang/include/clang/AST/PrettyPrinter.h b/clang/include/clang/AST/PrettyPrinter.h index 332ac3c6a004a..18bb674351737 100644 --- a/clang/include/clang/AST/PrettyPrinter.h +++ b/clang/include/clang/AST/PrettyPrinter.h @@ -58,6 +58,7 @@ struct PrintingPolicy { /// Create a default printing policy for the specified language. PrintingPolicy(const LangOptions &LO) : Indentation(2), SuppressSpecifiers(false), + SupressStorageClassSpecifiers(false), SuppressTagKeyword(LO.CPlusPlus), IncludeTagDefinition(false), SuppressScope(false), SuppressUnwrittenScope(false), SuppressInlineNamespace(true), SuppressElaboration(false), @@ -73,11 +74,21 @@ struct PrintingPolicy { PolishForDeclaration(false), Half(LO.Half), MSWChar(LO.MicrosoftExt && !LO.WChar), IncludeNewlines(true), MSVCFormatting(false), ConstantsAsWritten(false), - SuppressImplicitBase(false), FullyQualifiedName(false), + SuppressImplicitBase(false), UseStdFunctionForLambda(false), FullyQualifiedName(false), PrintCanonicalTypes(false), PrintInjectedClassNameWithArguments(true), UsePreferredNames(true), AlwaysIncludeTypeForTemplateArgument(false), CleanUglifiedParameters(false), EntireContentsOfLargeArray(true), - UseEnumerators(true), UseHLSLTypes(LO.HLSL) {} + UseEnumerators(true), UseHLSLTypes(LO.HLSL), + /* TO_UPSTREAM(BoundsSafety) ON */ + CountedByInArrayBracket(false), DelayedArrayQual() { + // GNU Attributes cannot be placed inside the array bracket, hence 'counted_by' + // attribute in the upstream is printed after the array bracket. Internally, + // 'counted_by' can be either inside or outside the bracket, while the inside + // has been the primary style. For now, we print it inside the bracket when + // -fbounds-safety is enabled, and outside the bracket otherwise. + CountedByInArrayBracket = LO.BoundsSafety; + /* TO_UPSTREAM(BoundsSafety) OFF */ + } /// Adjust this printing policy for cases where it's known that we're /// printing C++ code (for instance, if AST dumping reaches a C++-only @@ -109,6 +120,10 @@ struct PrintingPolicy { LLVM_PREFERRED_TYPE(bool) unsigned SuppressSpecifiers : 1; + /// \brief Whether we should supress the printing of the actual storage class + /// specifiers for the given declaration. + bool SupressStorageClassSpecifiers : 1; + /// Whether type printing should skip printing the tag keyword. /// /// This is used when printing the inner type of elaborated types, @@ -301,6 +316,9 @@ struct PrintingPolicy { LLVM_PREFERRED_TYPE(bool) unsigned SuppressImplicitBase : 1; + /// \brief Whether we should use std::function<...> for lambda record types. + bool UseStdFunctionForLambda : 1; + /// When true, print the fully qualified name of function declarations. /// This is the opposite of SuppressScope and thus overrules it. LLVM_PREFERRED_TYPE(bool) @@ -347,8 +365,14 @@ struct PrintingPolicy { LLVM_PREFERRED_TYPE(bool) unsigned UseHLSLTypes : 1; + /// Whether to print 'counted_by' attribute inside the array bracket. + LLVM_PREFERRED_TYPE(bool) + unsigned CountedByInArrayBracket : 1; + /// Callbacks to use to allow the behavior of printing to be customized. const PrintingCallbacks *Callbacks = nullptr; + + std::string DelayedArrayQual; }; } // end namespace clang diff --git a/clang/include/clang/AST/PropertiesBase.td b/clang/include/clang/AST/PropertiesBase.td index 5f7d619518762..38ead93da8bf3 100644 --- a/clang/include/clang/AST/PropertiesBase.td +++ b/clang/include/clang/AST/PropertiesBase.td @@ -76,6 +76,7 @@ def APValue : PropertyType { let PassByReference = 1; } def APValueKind : EnumPropertyType<"APValue::ValueKind">; def ArraySizeModifier : EnumPropertyType<"ArraySizeModifier">; def AttrKind : EnumPropertyType<"attr::Kind">; +def Attr : PropertyType<"const Attr *">; def AutoTypeKeyword : EnumPropertyType; def Bool : PropertyType<"bool">; def BuiltinTypeKind : EnumPropertyType<"BuiltinType::Kind">; @@ -134,6 +135,7 @@ def QualType : DefaultValuePropertyType; def RefQualifierKind : EnumPropertyType; def Selector : PropertyType; def SourceLocation : PropertyType; +def BoundsSafetyPointerAttributes : PropertyType; def StmtRef : RefPropertyType<"Stmt"> { let ConstWhenWriting = 1; } def ExprRef : SubclassPropertyType<"Expr", StmtRef>; def TemplateArgument : PropertyType; diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h index e3c0cb46799f7..2f1786bf7dd10 100644 --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -1143,6 +1143,16 @@ DEF_TRAVERSE_TYPE(CountAttributedType, { TRY_TO(TraverseType(T->desugar())); }) +/* TO_UPSTREAM(BoundsSafety) ON */ +DEF_TRAVERSE_TYPE(DynamicRangePointerType, { + if (auto *EndPtr = T->getEndPointer()) + TRY_TO(TraverseStmt(EndPtr)); + TRY_TO(TraverseType(T->desugar())); +}) + +DEF_TRAVERSE_TYPE(ValueTerminatedType, { TRY_TO(TraverseType(T->desugar())); }) +/* TO_UPSTREAM(BoundsSafety) OFF */ + DEF_TRAVERSE_TYPE(BTFTagAttributedType, { TRY_TO(TraverseType(T->getWrappedType())); }) @@ -1442,6 +1452,14 @@ DEF_TRAVERSE_TYPELOC(AttributedType, DEF_TRAVERSE_TYPELOC(CountAttributedType, { TRY_TO(TraverseTypeLoc(TL.getInnerLoc())); }) +/* TO_UPSTREAM(BoundsSafety) ON */ +DEF_TRAVERSE_TYPELOC(DynamicRangePointerType, + { TRY_TO(TraverseTypeLoc(TL.getInnerLoc())); }) + +DEF_TRAVERSE_TYPELOC(ValueTerminatedType, + { TRY_TO(TraverseTypeLoc(TL.getOriginalLoc())); }) +/* TO_UPSTREAM(BoundsSafety) OFF */ + DEF_TRAVERSE_TYPELOC(BTFTagAttributedType, { TRY_TO(TraverseTypeLoc(TL.getWrappedLoc())); }) @@ -2537,6 +2555,24 @@ DEF_TRAVERSE_STMT(BuiltinBitCastExpr, { TRY_TO(TraverseTypeLoc(S->getTypeInfoAsWritten()->getTypeLoc())); }) +DEF_TRAVERSE_STMT(BoundsSafetyPointerPromotionExpr, {}) +DEF_TRAVERSE_STMT(AssumptionExpr, {}) +DEF_TRAVERSE_STMT(ForgePtrExpr, {}) +DEF_TRAVERSE_STMT(GetBoundExpr, {}) +DEF_TRAVERSE_STMT(PredefinedBoundsCheckExpr, {}) +DEF_TRAVERSE_STMT(BoundsCheckExpr, {}) +DEF_TRAVERSE_STMT(MaterializeSequenceExpr, { + if (!S->isUnbinding()) { + for (auto *OVE : S->opaquevalues()) { + TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(OVE->getSourceExpr()); + } + TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getWrappedExpr()); + ShouldVisitChildren = false; + } +}) +DEF_TRAVERSE_STMT(TerminatedByToIndexableExpr, {}) +DEF_TRAVERSE_STMT(TerminatedByFromIndexableExpr, {}) + template <typename Derived> bool RecursiveASTVisitor<Derived>::TraverseSynOrSemInitListExpr( InitListExpr *S, DataRecursionQueue *Queue) { diff --git a/clang/include/clang/AST/StableHash.h b/clang/include/clang/AST/StableHash.h new file mode 100644 index 0000000000000..5c6f900a37289 --- /dev/null +++ b/clang/include/clang/AST/StableHash.h @@ -0,0 +1,46 @@ +//===--- StableHash.h - An ABI-stable string hash ---------------*- 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 +// +//===----------------------------------------------------------------------===// +// +// The interface to an ABI-stable string hash algorithm. +// +//===----------------------------------------------------------------------===// + +#ifndef CLANG_AST_STABLEHASH_H +#define CLANG_AST_STABLEHASH_H + +#include <cstdint> + +namespace llvm { +class StringRef; +} + +namespace clang { +class ASTContext; + +/// Compute a stable 64-bit hash of the given string. +/// +/// The exact algorithm is the little-endian interpretation of the +/// non-doubled (i.e. 64-bit) result of applying a SipHash-2-4 using +/// a specific key value which can be found in the source. +/// +/// By "stable" we mean that the result of this hash algorithm will +/// the same across different compiler versions and target platforms. +uint64_t getStableStringHash(llvm::StringRef string); + +/// Compute a pointer-auth extra discriminator for the given string, +/// suitable for both the blend operation and the __ptrauth qualifier. +/// +/// The result of this hash will be the same across different compiler +/// versions but may vary between targets due to differences in the +/// range of discriminators desired by the target. +uint64_t getPointerAuthStringDiscriminator(const ASTContext &ctx, + llvm::StringRef string); + +} // end namespace clang + +#endif diff --git a/clang/include/clang/AST/Stmt.h b/clang/include/clang/AST/Stmt.h index bbd7634bcc3bf..eed1cb833210e 100644 --- a/clang/include/clang/AST/Stmt.h +++ b/clang/include/clang/AST/Stmt.h @@ -724,6 +724,55 @@ class alignas(void *) Stmt { unsigned TemplateDepth; }; + class AssumptionExprBitfields { + friend class ASTStmtReader; + friend class AssumptionExpr; + + unsigned : NumExprBits; + unsigned NumExprs : 4; + }; + + class PredefinedBoundsCheckExprBitfields { + friend class ASTStmtReader; + friend class PredefinedBoundsCheckExpr; + + unsigned : NumExprBits; + + unsigned Kind : 6; + unsigned NumChildren; + }; + + class BoundsCheckExprBitfields { + friend class ASTStmtReader; + friend class BoundsCheckExpr; + + unsigned : NumExprBits; + + unsigned NumChildren : 31; + }; + + class BoundsSafetyPointerPromotionExprBitfields { + friend class ASTStmtReader; + friend class BoundsSafetyPointerPromotionExpr; + + unsigned : NumExprBits; + + /// If this field is is set, CodeGen should perform a null check on the + /// pointer and skip evaluating the bounds to avoid introducing unexpected + /// null dereference. + unsigned NullCheck : 1; + }; + + class MaterializeSequenceExprBitfields { + friend class ASTStmtReader; + friend class MaterializeSequenceExpr; + + unsigned : NumExprBits; + + unsigned NumExprs : 32 - 1; + unsigned Unbind : 1; + }; + //===--- C++ Expression bitfields classes ---===// class CXXOperatorCallExprBitfields { @@ -1177,6 +1226,15 @@ class alignas(void *) Stmt { unsigned ShouldCopy : 1; }; + class ObjCAvailabilityCheckExprBitfields { + friend class ObjCAvailabilityCheckExpr; + LLVM_PREFERRED_TYPE(ExprBitfields) + unsigned : NumExprBits; + + LLVM_PREFERRED_TYPE(bool) + unsigned HasDomainName : 1; + }; + //===--- Clang Extensions bitfields classes ---===// class OpaqueValueExprBitfields { @@ -1237,6 +1295,13 @@ class alignas(void *) Stmt { // GNU Extensions. StmtExprBitfields StmtExprBits; + // BoundsSafety Expression + AssumptionExprBitfields AssumptionExprBits; + PredefinedBoundsCheckExprBitfields PredefinedBoundsCheckExprBits; + BoundsCheckExprBitfields BoundsCheckExprBits; + BoundsSafetyPointerPromotionExprBitfields BoundsSafetyPointerPromotionExprBits; + MaterializeSequenceExprBitfields MaterializeSequenceExprBits; + // C++ Expressions CXXOperatorCallExprBitfields CXXOperatorCallExprBits; CXXRewrittenBinaryOperatorBitfields CXXRewrittenBinaryOperatorBits; @@ -1268,6 +1333,7 @@ class alignas(void *) Stmt { // Obj-C Expressions ObjCIndirectCopyRestoreExprBitfields ObjCIndirectCopyRestoreExprBits; + ObjCAvailabilityCheckExprBitfields ObjCAvailabilityCheckExprBits; // Clang Extensions OpaqueValueExprBitfields OpaqueValueExprBits; diff --git a/clang/include/clang/AST/StmtIterator.h b/clang/include/clang/AST/StmtIterator.h index e98408c51a505..9adb3f21492d0 100644 --- a/clang/include/clang/AST/StmtIterator.h +++ b/clang/include/clang/AST/StmtIterator.h @@ -133,10 +133,10 @@ struct StmtIterator : public StmtIteratorImpl<StmtIterator, Stmt*&> { StmtIterator(const VariableArrayType *t) : StmtIteratorImpl<StmtIterator, Stmt*&>(t) {} -private: StmtIterator(const StmtIteratorBase &RHS) : StmtIteratorImpl<StmtIterator, Stmt *&>(RHS) {} +private: inline friend StmtIterator cast_away_const(const ConstStmtIterator &RHS); }; diff --git a/clang/include/clang/AST/TemplateArgumentVisitor.h b/clang/include/clang/AST/TemplateArgumentVisitor.h index cf0d322015806..923f045a99570 100644 --- a/clang/include/clang/AST/TemplateArgumentVisitor.h +++ b/clang/include/clang/AST/TemplateArgumentVisitor.h @@ -52,7 +52,8 @@ class Base { #define VISIT_METHOD(CATEGORY) \ RetTy Visit##CATEGORY##TemplateArgument(REF(TemplateArgument) TA, \ ParamTys... P) { \ - return VisitTemplateArgument(TA, std::forward<ParamTys>(P)...); \ + return static_cast<ImplClass *>(this)->VisitTemplateArgument( \ + TA, std::forward<ParamTys>(P)...); \ } VISIT_METHOD(Null); diff --git a/clang/include/clang/AST/TemplateName.h b/clang/include/clang/AST/TemplateName.h index e3b7dd261535d..e7313dee01281 100644 --- a/clang/include/clang/AST/TemplateName.h +++ b/clang/include/clang/AST/TemplateName.h @@ -347,9 +347,7 @@ class TemplateName { /// error. void dump() const; - void Profile(llvm::FoldingSetNodeID &ID) { - ID.AddPointer(Storage.getOpaqueValue()); - } + void Profile(llvm::FoldingSetNodeID &ID); /// Retrieve the template name as a void pointer. void *getAsVoidPointer() const { return Storage.getOpaqueValue(); } diff --git a/clang/include/clang/AST/TextNodeDumper.h b/clang/include/clang/AST/TextNodeDumper.h index 39dd1f515c9eb..0582fd2c67612 100644 --- a/clang/include/clang/AST/TextNodeDumper.h +++ b/clang/include/clang/AST/TextNodeDumper.h @@ -318,6 +318,10 @@ class TextNodeDumper void VisitObjCSubscriptRefExpr(const ObjCSubscriptRefExpr *Node); void VisitObjCIvarRefExpr(const ObjCIvarRefExpr *Node); void VisitObjCBoolLiteralExpr(const ObjCBoolLiteralExpr *Node); + void VisitMaterializeSequenceExpr(const MaterializeSequenceExpr *Node); + void VisitBoundsCheckExpr(const BoundsCheckExpr *Node); + void VisitPredefinedBoundsCheckExpr(const PredefinedBoundsCheckExpr *Node); + void VisitGetBoundExpr(const GetBoundExpr *Node); void VisitOMPIteratorExpr(const OMPIteratorExpr *Node); void VisitConceptSpecializationExpr(const ConceptSpecializationExpr *Node); void VisitRequiresExpr(const RequiresExpr *Node); diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h index 25defea58c2dc..971cbddd8ed00 100644 --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -48,6 +48,7 @@ #include "llvm/Support/PointerLikeTypeTraits.h" #include "llvm/Support/TrailingObjects.h" #include "llvm/Support/type_traits.h" +#include <bitset> #include <cassert> #include <cstddef> #include <cstdint> @@ -67,6 +68,7 @@ class ValueDecl; class TagDecl; class TemplateParameterList; class Type; +class Attr; enum { TypeAlignmentInBits = 4, @@ -118,6 +120,8 @@ class EnumDecl; class Expr; class ExtQualsTypeCommonBase; class FunctionDecl; +class FunctionEffectsRef; +class FunctionEffectKindSet; class FunctionEffectSet; class IdentifierInfo; class NamedDecl; @@ -307,6 +311,12 @@ class PointerAuthQualifier { return Result; } + std::string getAsString() const; + std::string getAsString(const PrintingPolicy &Policy) const; + + bool isEmptyWhenPrinted(const PrintingPolicy &Policy) const; + void print(raw_ostream &OS, const PrintingPolicy &Policy) const; + void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(Data); } }; @@ -366,6 +376,8 @@ class Qualifiers { FastMask = (1 << FastWidth) - 1 }; + Qualifiers() : Mask(0) {} + /// Returns the common set of qualifiers while removing them from /// the given sets. static Qualifiers removeCommonQualifiers(Qualifiers &L, Qualifiers &R) { @@ -410,6 +422,7 @@ class Qualifiers { L.removeAddressSpace(); R.removeAddressSpace(); } + return Q; } @@ -475,22 +488,22 @@ class Qualifiers { unsigned getCVRQualifiers() const { return Mask & CVRMask; } unsigned getCVRUQualifiers() const { return Mask & (CVRMask | UMask); } - void setCVRQualifiers(unsigned mask) { + void setCVRQualifiers(uint64_t mask) { assert(!(mask & ~CVRMask) && "bitmask contains non-CVR bits"); Mask = (Mask & ~CVRMask) | mask; } - void removeCVRQualifiers(unsigned mask) { + void removeCVRQualifiers(uint64_t mask) { assert(!(mask & ~CVRMask) && "bitmask contains non-CVR bits"); Mask &= ~static_cast<uint64_t>(mask); } void removeCVRQualifiers() { removeCVRQualifiers(CVRMask); } - void addCVRQualifiers(unsigned mask) { + void addCVRQualifiers(uint64_t mask) { assert(!(mask & ~CVRMask) && "bitmask contains non-CVR bits"); Mask |= mask; } - void addCVRUQualifiers(unsigned mask) { + void addCVRUQualifiers(uint64_t mask) { assert(!(mask & ~CVRMask & ~UMask) && "bitmask contains non-CVRU bits"); Mask |= mask; } @@ -556,7 +569,7 @@ class Qualifiers { bool hasAddressSpace() const { return Mask & AddressSpaceMask; } LangAS getAddressSpace() const { - return static_cast<LangAS>(Mask >> AddressSpaceShift); + return static_cast<LangAS>((Mask & AddressSpaceMask) >> AddressSpaceShift); } bool hasTargetSpecificAddressSpace() const { return isTargetAddressSpace(getAddressSpace()); @@ -604,25 +617,27 @@ class Qualifiers { // on a QualType object. bool hasFastQualifiers() const { return getFastQualifiers(); } unsigned getFastQualifiers() const { return Mask & FastMask; } - void setFastQualifiers(unsigned mask) { + void setFastQualifiers(uint64_t mask) { assert(!(mask & ~FastMask) && "bitmask contains non-fast qualifier bits"); Mask = (Mask & ~FastMask) | mask; } - void removeFastQualifiers(unsigned mask) { + void removeFastQualifiers(uint64_t mask) { assert(!(mask & ~FastMask) && "bitmask contains non-fast qualifier bits"); Mask &= ~static_cast<uint64_t>(mask); } void removeFastQualifiers() { removeFastQualifiers(FastMask); } - void addFastQualifiers(unsigned mask) { + void addFastQualifiers(uint64_t mask) { assert(!(mask & ~FastMask) && "bitmask contains non-fast qualifier bits"); Mask |= mask; } /// Return true if the set contains any qualifiers which require an ExtQuals /// node to be allocated. - bool hasNonFastQualifiers() const { return Mask & ~FastMask; } + bool hasNonFastQualifiers() const { + return (Mask & ~FastMask); + } Qualifiers getNonFastQualifiers() const { Qualifiers Quals = *this; Quals.setFastQualifiers(0); @@ -631,7 +646,7 @@ class Qualifiers { /// Return true if the set contains any qualifiers. bool hasQualifiers() const { return Mask; } - bool empty() const { return !Mask; } + bool empty() const { return !hasQualifiers(); } /// Add the qualifiers from the given set to this set. void addQualifiers(Qualifiers Q) { @@ -821,11 +836,11 @@ class Qualifiers { static constexpr uint64_t GCAttrShift = 4; static constexpr uint64_t LifetimeMask = 0x1C0; static constexpr uint64_t LifetimeShift = 6; - static constexpr uint64_t AddressSpaceMask = - ~(CVRMask | UMask | GCAttrMask | LifetimeMask); static constexpr uint64_t AddressSpaceShift = 9; static constexpr uint64_t PtrAuthShift = 32; static constexpr uint64_t PtrAuthMask = uint64_t(0xffffffff) << PtrAuthShift; + static constexpr uint64_t AddressSpaceMask = + ~(CVRMask | UMask | GCAttrMask | LifetimeMask | PtrAuthMask); }; class QualifiersAndAtomic { @@ -1460,6 +1475,12 @@ class QualType { return getQualifiers().getPointerAuth(); } + bool hasAddressDiscriminatedPointerAuth() const { + if (auto ptrauth = getPointerAuth()) + return ptrauth.isAddressDiscriminated(); + return false; + } + enum PrimitiveDefaultInitializeKind { /// The type does not fall into any of the following categories. Note that /// this case is zero-valued so that values of this enum can be used as a @@ -1505,6 +1526,9 @@ class QualType { /// with the ARC __weak qualifier. PCK_ARCWeak, + /// The type is an address-discriminated signed pointer type. + PCK_PtrAuth, + /// The type is a struct containing a field whose type is neither /// PCK_Trivial nor PCK_VolatileTrivial. /// Note that a C++ struct type does not necessarily match this; C++ copying @@ -2241,6 +2265,17 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase { }; static_assert(sizeof(CountAttributedTypeBitfields) <= sizeof(unsigned)); + /* TO_UPSTREAM(BoundsSafety) ON */ + class DynamicRangePointerTypeBitfields { + friend class DynamicRangePointerType; + + unsigned : NumTypeBits; + + unsigned NumEndPtrDecls : 16; + unsigned NumStartPtrDecls : 16; + }; + /* TO_UPSTREAM(BoundsSafety) OFF */ + union { TypeBitfields TypeBits; ArrayTypeBitfields ArrayTypeBits; @@ -2264,6 +2299,9 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase { DependentTemplateSpecializationTypeBits; PackExpansionTypeBitfields PackExpansionTypeBits; CountAttributedTypeBitfields CountAttributedTypeBits; + /* TO_UPSTREAM(BoundsSafety) ON */ + DynamicRangePointerTypeBitfields DynamicRangePointerTypeBits; + /* TO_UPSTREAM(BoundsSafety) OFF */ }; private: @@ -2411,12 +2449,48 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase { /// class), will be set to the declaration. bool isIncompleteType(NamedDecl **Def = nullptr) const; + /* TO_UPSTREAM(BoundsSafety) ON*/ + /// Return true if the size of this type is "meaningless". For a meaninglessly + /// sized type T, sizeof(T) is either ill-formed or has a useless or + /// misleading result. This could be because the type is a function type, is a + /// void type, is a sizeless type, is an incomplete type (including an + /// incomplete array type), or is a record type with a flexible array member. + bool isSizeMeaningless() const; + /* TO_UPSTREAM(BoundsSafety) OFF*/ + /// Return true if this is an incomplete or object /// type, in other words, not a function type. bool isIncompleteOrObjectType() const { return !isFunctionType(); } + /* TO_UPSTREAM(BoundsSafety) ON*/ + /// Return true if this is an incomplete or sizeless type including a function type. + bool isIncompleteOrSizelessType() const { + return isIncompleteType() || isSizelessType() || isFunctionType(); + } + + /// \returns True if the type is incomplete and it is also a type that + /// cannot be completed by a later type definition. + /// + /// E.g. For `void` this is true but for `struct ForwardDecl;` this is false + /// because a definition for `ForwardDecl` could be provided later on in the + /// translation unit. + /// + /// Note even for types that this function returns true for it is still + /// possible for the declarations that contain this type to later have a + /// complete type in a translation unit. E.g.: + /// + /// \code{.c} + /// // This decl has type 'char[]' which is incomplete and cannot be later + /// // completed by another by another type declaration. + /// extern char foo[]; + /// // This decl how has complete type 'char[5]'. + /// char foo[5]; // foo has a complete type + /// \endcode + bool isIncompletableIncompleteType() const; + /* TO_UPSTREAM(BoundsSafety) OFF*/ + /// Determine whether this type is an object type. bool isObjectType() const { // C++ [basic.types]p8: @@ -2510,8 +2584,23 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase { bool isFunctionProtoType() const { return getAs<FunctionProtoType>(); } bool isPointerType() const; bool isSignableType() const; + /* TO_UPSTREAM(BoundsSafety) ON */ + bool isUnsafeIndexablePointerType() const; + bool isSinglePointerType() const; + bool isIndexablePointerType() const; + bool isBidiIndexablePointerType() const; + bool isUnspecifiedPointerType() const; + bool isSafePointerType() const; + /* TO_UPSTREAM(BoundsSafety) OFF */ bool isAnyPointerType() const; // Any C pointer or ObjC object pointer bool isCountAttributedType() const; + /* TO_UPSTREAM(BoundsSafety) ON */ + bool isAnyVaListType(ASTContext &) const; + bool isDynamicRangePointerType() const; + bool isBoundsAttributedType() const; + bool isValueTerminatedType() const; + bool isImplicitlyNullTerminatedType(const ASTContext &) const; + /* TO_UPSTREAM(BoundsSafety) OFF */ bool isBlockPointerType() const; bool isVoidPointerType() const; bool isReferenceType() const; @@ -2604,6 +2693,8 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase { bool isAtomicType() const; // C11 _Atomic() bool isUndeducedAutoType() const; // C++11 auto or // C++14 decltype(auto) + // TO_UPSTREAM(BoundsSafety) + bool isPointerTypeWithBounds() const; // BoundsSafety __indexable or __bidi_indexable bool isTypedefNameType() const; // typedef or alias template #define IMAGE_TYPE(ImgType, Id, SingletonId, Access, Suffix) \ @@ -2983,6 +3074,16 @@ template <> const BoundsAttributedType *Type::getAs() const; /// sugar until it reaches an CountAttributedType or a non-sugared type. template <> const CountAttributedType *Type::getAs() const; +/* TO_UPSTREAM(BoundsSafety) ON */ +/// This will check for a DynamicRangePointerType by removing any existing +/// sugar until it reaches an DynamicRangePointerType or a non-sugared type. +template <> const DynamicRangePointerType *Type::getAs() const; + +/// This will check for a ValueTerminatedType by removing any existing sugar +/// until it reaches a ValueTerminatedType or a non-sugared type. +template <> const ValueTerminatedType *Type::getAs() const; +/* TO_UPSTREAM(BoundsSafety) OFF */ + // We can do canonical leaf types faster, because we don't have to // worry about preserving child type decoration. #define TYPE(Class, Base) @@ -3157,28 +3258,253 @@ class ParenType : public Type, public llvm::FoldingSetNode { static bool classof(const Type *T) { return T->getTypeClass() == Paren; } }; +class BoundsSafetyPointerAttributes { +public: + // This ordering of attributes also serves as precedence to create a composite + // type between two pointer types. We currently use the precedence for conditional + // operators for which '__unsafe_indexable' has the highest precedence while + // '__single' being the lowest. + enum BK { + Unspecified = 0, + UnsafeIndexable = 1, + BidiIndexable = 2, + Indexable = 3, + Single = 4, + BoundsWidth = 3, + BoundsMask = (1 << BoundsWidth) - 1 + }; + + enum TK { + UnsafeCastable = 1, + NoCastable = 2, + Castable = 3, + TypeWidth = 2, + TypeMask = (1 << TypeWidth) - 1, + ShiftedTypeMask = TypeMask << BoundsWidth + }; + + static BoundsSafetyPointerAttributes unsafeIndexable() { + BoundsSafetyPointerAttributes FA; + FA.setUnsafeIndexable(); + return FA; + } + + static BoundsSafetyPointerAttributes bidiIndexable() { + BoundsSafetyPointerAttributes FA; + FA.setBidiIndexable(); + return FA; + } + + static BoundsSafetyPointerAttributes indexable() { + BoundsSafetyPointerAttributes FA; + FA.setIndexable(); + return FA; + } + + static BoundsSafetyPointerAttributes single() { + BoundsSafetyPointerAttributes FA; + FA.setSingle(); + return FA; + } + + static BoundsSafetyPointerAttributes unspecified() { + BoundsSafetyPointerAttributes FA; + return FA; + } + + uint32_t getBoundsAttr() const { return BoundsAttr; } + + uint32_t getTypeAttr() const { return TypeAttr; } + + uint32_t getAsOpaqueValue() const { + return (TypeAttr << BoundsWidth) | BoundsAttr; + } + + bool isUnspecified() const { return getBoundsAttr() == Unspecified; } + bool isUnsafeIndexable() const { return getBoundsAttr() == UnsafeIndexable; } + bool isSingle() const { return getBoundsAttr() == Single; } + bool isIndexable() const { return getBoundsAttr() == Indexable; } + bool isBidiIndexable() const { return getBoundsAttr() == BidiIndexable; } + bool isUnsafeOrUnspecified() const { return isUnsafeIndexable() || isUnspecified(); } + + void setUnspecified() { setBoundsAttr(Unspecified); } + void setUnsafeIndexable() { setBoundsAttr(UnsafeIndexable); } + void setSingle() { setBoundsAttr(Single); } + void setIndexable() { setBoundsAttr(Indexable); } + void setBidiIndexable() { setBoundsAttr(BidiIndexable); } + void copyBoundsAttr(BoundsSafetyPointerAttributes FAttr) { + setBoundsAttr(FAttr.getBoundsAttr()); + } + + void copyTypeAttr(BoundsSafetyPointerAttributes FAttr) { + setTypeAttr(FAttr.getTypeAttr()); + } + + BoundsSafetyPointerAttributes(uint32_t value = 0) + : BoundsAttr(value & BoundsMask), + TypeAttr((value & ShiftedTypeMask) >> BoundsWidth) {} + + void print(raw_ostream &OS) const { + switch (getBoundsAttr()) { + case Unspecified: + break; + case UnsafeIndexable: + OS << "__unsafe_indexable"; + break; + case Single: + OS << "__single"; + break; + case Indexable: + OS << "__indexable"; + break; + case BidiIndexable: + OS << "__bidi_indexable"; + break; + default: + llvm_unreachable("Unknown -fbounds-safety bounds only attributes"); + } + + switch (getTypeAttr()) { + case Unspecified: + break; + default: + llvm_unreachable("Unknown -fbounds-safety type attributes"); + } + } + + bool operator==(BoundsSafetyPointerAttributes other) const { + return getAsOpaqueValue() == other.getAsOpaqueValue(); + } + + bool operator!=(BoundsSafetyPointerAttributes other) const { + return !(*this == other); + } + + bool hasRawPointerLayout() const { + return isUnspecified() || isSingle() || isUnsafeIndexable(); + } + + bool hasLowerBound() const { return isBidiIndexable(); } + + bool hasUpperBound() const { return isBidiIndexable() || isIndexable(); } + + void takeBoundsAttr(BoundsSafetyPointerAttributes Other) { + setBoundsAttr(Other.getBoundsAttr()); + } + + static bool areEquivalentLayouts(BoundsSafetyPointerAttributes A1, + BoundsSafetyPointerAttributes A2) { + if (A1 == A2) + return true; + + if (A1.getTypeAttr() != A2.getTypeAttr()) + return false; + + // Single and unsafe have equivalent layout. + if ((A1.isUnsafeOrUnspecified() && A2.hasRawPointerLayout()) || + (A1.hasRawPointerLayout() && A2.isUnsafeOrUnspecified())) + return true; + + return false; + } + + + static bool areCompatible(BoundsSafetyPointerAttributes Left, + BoundsSafetyPointerAttributes Right) { + if (Left == Right) + return true; + + // Allow unspecified pointer attributes to merge with either of + // unsafe_indexable and single. + if (Left.isUnspecified()) + return Right.hasRawPointerLayout(); + if (Right.isUnspecified()) + return Left.hasRawPointerLayout(); + + return false; + } + + static BoundsSafetyPointerAttributes fromOpaqueValue(uint32_t value) { + return BoundsSafetyPointerAttributes(value); + } + + // BoundsSafetyPointerAttributes precedence in conditional operator + // conversion (bounds only): + // 1) __unsafe_indexable or unspecified + // 2) __bidi_indexable + // 3) __indexable + // 4) __single + static BoundsSafetyPointerAttributes merge(BoundsSafetyPointerAttributes A1, + BoundsSafetyPointerAttributes A2) { + uint32_t NewBoundsAtt = A1.getBoundsAttr() > A2.getBoundsAttr() + ? A2.getBoundsAttr() + : A1.getBoundsAttr(); + + uint32_t NewTypeAtt = A1.getTypeAttr() > A2.getTypeAttr() + ? A2.getTypeAttr() + : A1.getTypeAttr(); + + BoundsSafetyPointerAttributes NewAtt; + NewAtt.setBoundsAttr(NewBoundsAtt); + NewAtt.setTypeAttr(NewTypeAtt); + return NewAtt; + } + +private: + void setBoundsAttr(uint32_t attr) { + assert(!(attr & ~BoundsMask) && "attr contains non-bounds bits"); + BoundsAttr = attr; + } + void setTypeAttr(uint32_t attr) { + assert(!(attr & ~TypeMask) && "attr contains non-type bits"); + TypeAttr = attr; + } + +private: + // |0 .. 2|3 .. 4|5 .. 31| + // |Bounds|Type |Reserved| + uint32_t BoundsAttr : 3; + uint32_t TypeAttr : 2; +}; + /// PointerType - C99 6.7.5.1 - Pointer Declarators. class PointerType : public Type, public llvm::FoldingSetNode { friend class ASTContext; // ASTContext creates these. + using FA = BoundsSafetyPointerAttributes; + QualType PointeeType; + FA FAttr; - PointerType(QualType Pointee, QualType CanonicalPtr) + PointerType(QualType Pointee, QualType CanonicalPtr, FA FAttr) : Type(Pointer, CanonicalPtr, Pointee->getDependence()), - PointeeType(Pointee) {} + PointeeType(Pointee), FAttr(FAttr) {} public: QualType getPointeeType() const { return PointeeType; } + FA getPointerAttributes() const { return FAttr; } bool isSugared() const { return false; } QualType desugar() const { return QualType(this, 0); } + bool hasRawPointerLayout() const { return FAttr.hasRawPointerLayout(); } + bool isSafePointer() const { + return !(FAttr.isUnsafeIndexable() || FAttr.isUnspecified()); + } + + bool isSingle() const { return FAttr.isSingle(); } + bool isIndexable() const { return FAttr.isIndexable(); } + bool isBidiIndexable() const { return FAttr.isBidiIndexable(); } + bool isUnsafeIndexable() const { return FAttr.isUnsafeIndexable(); } + bool isUnspecified() const { return FAttr.isUnspecified(); } + void Profile(llvm::FoldingSetNodeID &ID) { - Profile(ID, getPointeeType()); + Profile(ID, getPointeeType(), getPointerAttributes()); } - static void Profile(llvm::FoldingSetNodeID &ID, QualType Pointee) { + static void Profile(llvm::FoldingSetNodeID &ID, QualType Pointee, FA FAttr) { ID.AddPointer(Pointee.getAsOpaquePtr()); + ID.AddInteger(FAttr.getAsOpaqueValue()); } static bool classof(const Type *T) { return T->getTypeClass() == Pointer; } @@ -3188,12 +3514,16 @@ class PointerType : public Type, public llvm::FoldingSetNode { /// arguments of the `counted_by` attribute and the likes. class TypeCoupledDeclRefInfo { public: - using BaseTy = llvm::PointerIntPair<ValueDecl *, 1, unsigned>; + using BaseTy = llvm::PointerIntPair<ValueDecl *, 2, unsigned>; private: enum { DerefShift = 0, DerefMask = 1, + /*TO_UPSTREAM(BoundsSafety) ON*/ + MemberShift = 1, + MemberMask = 1, + /*TO_UPSTREAM(BoundsSafety) OFF*/ }; BaseTy Data; @@ -3201,9 +3531,17 @@ class TypeCoupledDeclRefInfo { /// \p D is to a declaration referenced by the argument of attribute. \p Deref /// indicates whether \p D is referenced as a dereferenced form, e.g., \p /// Deref is true for `*n` in `int *__counted_by(*n)`. - TypeCoupledDeclRefInfo(ValueDecl *D = nullptr, bool Deref = false); + /* TO_UPSTREAM(BoundsSafety) ON */ + /// \p Member indicates that \p D is referenced as the member for a struct, + /// e.g __counted_by(hdr.len) `hdr`, although a FieldDecl is referred to by + /// a DeclRefExpr, while `len` is referred to by a MemberExpr. In this + /// example `Member` is false for `hdr` and true for `len`. + TypeCoupledDeclRefInfo(ValueDecl *D = nullptr, bool Deref = false, + bool Member = false); + /* TO_UPSTREAM(BoundsSafety) OFF */ bool isDeref() const; + bool isMember() const; ValueDecl *getDecl() const; unsigned getInt() const; void *getOpaqueValue() const; @@ -3227,6 +3565,16 @@ class BoundsAttributedType : public Type, public llvm::FoldingSetNode { BoundsAttributedType(TypeClass TC, QualType Wrapped, QualType Canon); public: + /* TO_UPSTREAM(BoundsSafety) ON*/ + enum BoundsAttrKind { + CountedBy = 0, + SizedBy, + CountedByOrNull, + SizedByOrNull, + EndedBy + }; + /* TO_UPSTREAM(BoundsSafety) OFF*/ + bool isSugared() const { return true; } QualType desugar() const { return WrappedTy; } @@ -3254,6 +3602,9 @@ class BoundsAttributedType : public Type, public llvm::FoldingSetNode { // annotations. switch (T->getTypeClass()) { case CountAttributed: +/* TO_UPSTREAM(BoundsSafety) ON */ + case DynamicRangePointer: +/* TO_UPSTREAM(BoundsSafety) OFF */ return true; default: return false; @@ -3285,22 +3636,17 @@ class CountAttributedType final } public: - enum DynamicCountPointerKind { - CountedBy = 0, - SizedBy, - CountedByOrNull, - SizedByOrNull, - }; - Expr *getCountExpr() const { return CountExpr; } bool isCountInBytes() const { return CountAttributedTypeBits.CountInBytes; } bool isOrNull() const { return CountAttributedTypeBits.OrNull; } - DynamicCountPointerKind getKind() const { + /* TO_UPSTREAM(BoundsSafety) ON*/ + BoundsAttrKind getKind() const { if (isOrNull()) return isCountInBytes() ? SizedByOrNull : CountedByOrNull; return isCountInBytes() ? SizedBy : CountedBy; } + /* TO_UPSTREAM(BoundsSafety) OFF*/ void Profile(llvm::FoldingSetNodeID &ID) { Profile(ID, desugar(), CountExpr, isCountInBytes(), isOrNull()); @@ -3312,8 +3658,125 @@ class CountAttributedType final static bool classof(const Type *T) { return T->getTypeClass() == CountAttributed; } + + /* TO_UPSTREAM(BoundsSafety) ON*/ + StringRef GetAttributeName(bool WithMacroPrefix) const; + /* TO_UPSTREAM(BoundsSafety) OFF*/ }; +/* TO_UPSTREAM(BoundsSafety) ON */ +class DynamicRangePointerType final + : public BoundsAttributedType, + public llvm::TrailingObjects<DynamicRangePointerType, + TypeCoupledDeclRefInfo> { + friend class ASTContext; + + // We have 0 or 1 primary start and end pointer expressions and 0 or more + // start and end declarations that are associated with the type. + Expr *StartPtr; + Expr *EndPtr; + DynamicRangePointerType(QualType PointerTy, QualType CanPointerTy, + Expr *StartPtr, Expr *EndPtr, + ArrayRef<TypeCoupledDeclRefInfo> StartPtrDecls, + ArrayRef<TypeCoupledDeclRefInfo> EndPtrDecls); + + unsigned numTrailingObjects(OverloadToken<TypeCoupledDeclRefInfo>) const { + return DynamicRangePointerTypeBits.NumEndPtrDecls + + DynamicRangePointerTypeBits.NumStartPtrDecls; + } + +public: + BoundsAttrKind getKind() const { return EndedBy; } + + Expr *getStartPointer() const { return StartPtr; } + Expr *getEndPointer() const { return EndPtr; } + + unsigned getNumStartPtrDecls() const { + return DynamicRangePointerTypeBits.NumStartPtrDecls; + } + + unsigned getNumEndPtrDecls() const { + return DynamicRangePointerTypeBits.NumEndPtrDecls; + } + + decl_iterator startptr_decl_begin() const { + return getTrailingObjects<TypeCoupledDeclRefInfo>(); + } + + decl_iterator startptr_decl_end() const { + return startptr_decl_begin() + getNumStartPtrDecls(); + } + + decl_range startptr_decls() const { + return decl_range(startptr_decl_begin(), startptr_decl_end()); + } + + ArrayRef<TypeCoupledDeclRefInfo> getStartPtrDecls() const { + return {startptr_decl_begin(), startptr_decl_end()}; + } + + decl_iterator endptr_decl_begin() const { + return startptr_decl_end(); + } + + decl_iterator endptr_decl_end() const { + return endptr_decl_begin() + getNumEndPtrDecls(); + } + + decl_range endptr_decls() const { + return decl_range(endptr_decl_begin(), endptr_decl_end()); + } + + ArrayRef<TypeCoupledDeclRefInfo> getEndPtrDecls() const { + return {endptr_decl_begin(), endptr_decl_end()}; + } + + void Profile(llvm::FoldingSetNodeID &ID) { + Profile(ID, desugar(), StartPtr, EndPtr, getNumStartPtrDecls(), + getNumEndPtrDecls()); + } + + static void Profile(llvm::FoldingSetNodeID &ID, QualType PointerTy, + Expr *StartPtr, Expr *EndPtr, unsigned NumStartDecls, + unsigned NumEndDecls); + + static bool classof(const Type *T) { + return T->getTypeClass() == DynamicRangePointer; + } +}; + +class ValueTerminatedType final : public Type, public llvm::FoldingSetNode { + friend class ASTContext; + + QualType OriginalTy; + Expr *TerminatorExpr; + + explicit ValueTerminatedType(QualType OriginalTy, QualType CanOriginalTy, + Expr *TerminatorExpr) + : Type(ValueTerminated, CanOriginalTy, OriginalTy->getDependence()), + OriginalTy(OriginalTy), TerminatorExpr(TerminatorExpr) {} + +public: + bool isSugared() const { return true; } + QualType desugar() const { return OriginalTy; } + + Expr *getTerminatorExpr() const { return TerminatorExpr; } + + llvm::APSInt getTerminatorValue(const ASTContext &Ctx) const; + + void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Ctx) const { + Profile(ID, Ctx, OriginalTy, TerminatorExpr); + } + + static void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Ctx, + QualType OriginalTy, const Expr *TerminatorExpr); + + static bool classof(const Type *T) { + return T->getTypeClass() == ValueTerminated; + } +}; +/* TO_UPSTREAM(BoundsSafety) OFF */ + /// Represents a type which was implicitly adjusted by the semantic /// engine for arbitrary reasons. For example, array and function types can /// decay, and function types can have their calling conventions adjusted. @@ -4674,12 +5137,13 @@ class FunctionEffect { public: /// Identifies the particular effect. enum class Kind : uint8_t { - None = 0, - NonBlocking = 1, - NonAllocating = 2, - Blocking = 3, - Allocating = 4 + NonBlocking, + NonAllocating, + Blocking, + Allocating, + Last = Allocating }; + constexpr static size_t KindCount = static_cast<size_t>(Kind::Last) + 1; /// Flags describing some behaviors of the effect. using Flags = unsigned; @@ -4698,26 +5162,23 @@ class FunctionEffect { }; private: - LLVM_PREFERRED_TYPE(Kind) - unsigned FKind : 3; + Kind FKind; // Expansion: for hypothetical TCB+types, there could be one Kind for TCB, // then ~16(?) bits "SubKind" to map to a specific named TCB. SubKind would // be considered for uniqueness. public: - FunctionEffect() : FKind(unsigned(Kind::None)) {} - - explicit FunctionEffect(Kind K) : FKind(unsigned(K)) {} + explicit FunctionEffect(Kind K) : FKind(K) {} /// The kind of the effect. - Kind kind() const { return Kind(FKind); } + Kind kind() const { return FKind; } /// Return the opposite kind, for effects which have opposites. Kind oppositeKind() const; /// For serialization. - uint32_t toOpaqueInt32() const { return FKind; } + uint32_t toOpaqueInt32() const { return uint32_t(FKind); } static FunctionEffect fromOpaqueInt32(uint32_t Value) { return FunctionEffect(Kind(Value)); } @@ -4736,8 +5197,6 @@ class FunctionEffect { case Kind::Blocking: case Kind::Allocating: return 0; - case Kind::None: - break; } llvm_unreachable("unknown effect kind"); } @@ -4745,26 +5204,36 @@ class FunctionEffect { /// The description printed in diagnostics, e.g. 'nonblocking'. StringRef name() const; - /// Return true if the effect is allowed to be inferred on the callee, - /// which is either a FunctionDecl or BlockDecl. + friend raw_ostream &operator<<(raw_ostream &OS, + const FunctionEffect &Effect) { + OS << Effect.name(); + return OS; + } + + /// Determine whether the effect is allowed to be inferred on the callee, + /// which is either a FunctionDecl or BlockDecl. If the returned optional + /// is empty, inference is permitted; otherwise it holds the effect which + /// blocked inference. /// Example: This allows nonblocking(false) to prevent inference for the /// function. - bool canInferOnFunction(const Decl &Callee) const; + std::optional<FunctionEffect> + effectProhibitingInference(const Decl &Callee, + FunctionEffectKindSet CalleeFX) const; // Return false for success. When true is returned for a direct call, then the // FE_InferrableOnCallees flag may trigger inference rather than an immediate // diagnostic. Caller should be assumed to have the effect (it may not have it // explicitly when inferring). bool shouldDiagnoseFunctionCall(bool Direct, - ArrayRef<FunctionEffect> CalleeFX) const; + FunctionEffectKindSet CalleeFX) const; - friend bool operator==(const FunctionEffect &LHS, const FunctionEffect &RHS) { + friend bool operator==(FunctionEffect LHS, FunctionEffect RHS) { return LHS.FKind == RHS.FKind; } - friend bool operator!=(const FunctionEffect &LHS, const FunctionEffect &RHS) { + friend bool operator!=(FunctionEffect LHS, FunctionEffect RHS) { return !(LHS == RHS); } - friend bool operator<(const FunctionEffect &LHS, const FunctionEffect &RHS) { + friend bool operator<(FunctionEffect LHS, FunctionEffect RHS) { return LHS.FKind < RHS.FKind; } }; @@ -4792,13 +5261,14 @@ struct FunctionEffectWithCondition { FunctionEffect Effect; EffectConditionExpr Cond; - FunctionEffectWithCondition() = default; - FunctionEffectWithCondition(const FunctionEffect &E, - const EffectConditionExpr &C) + FunctionEffectWithCondition(FunctionEffect E, const EffectConditionExpr &C) : Effect(E), Cond(C) {} /// Return a textual description of the effect, and its condition, if any. std::string description() const; + + friend raw_ostream &operator<<(raw_ostream &OS, + const FunctionEffectWithCondition &CFE); }; /// Support iteration in parallel through a pair of FunctionEffect and @@ -4903,6 +5373,85 @@ class FunctionEffectsRef { void dump(llvm::raw_ostream &OS) const; }; +/// A mutable set of FunctionEffect::Kind. +class FunctionEffectKindSet { + // For now this only needs to be a bitmap. + constexpr static size_t EndBitPos = FunctionEffect::KindCount; + using KindBitsT = std::bitset<EndBitPos>; + + KindBitsT KindBits{}; + + explicit FunctionEffectKindSet(KindBitsT KB) : KindBits(KB) {} + + // Functions to translate between an effect kind, starting at 1, and a + // position in the bitset. + + constexpr static size_t kindToPos(FunctionEffect::Kind K) { + return static_cast<size_t>(K); + } + + constexpr static FunctionEffect::Kind posToKind(size_t Pos) { + return static_cast<FunctionEffect::Kind>(Pos); + } + + // Iterates through the bits which are set. + class iterator { + const FunctionEffectKindSet *Outer = nullptr; + size_t Idx = 0; + + // If Idx does not reference a set bit, advance it until it does, + // or until it reaches EndBitPos. + void advanceToNextSetBit() { + while (Idx < EndBitPos && !Outer->KindBits.test(Idx)) + ++Idx; + } + + public: + iterator(); + iterator(const FunctionEffectKindSet &O, size_t I) : Outer(&O), Idx(I) { + advanceToNextSetBit(); + } + bool operator==(const iterator &Other) const { return Idx == Other.Idx; } + bool operator!=(const iterator &Other) const { return Idx != Other.Idx; } + + iterator operator++() { + ++Idx; + advanceToNextSetBit(); + return *this; + } + + FunctionEffect operator*() const { + assert(Idx < EndBitPos && "Dereference of end iterator"); + return FunctionEffect(posToKind(Idx)); + } + }; + +public: + FunctionEffectKindSet() = default; + explicit FunctionEffectKindSet(FunctionEffectsRef FX) { insert(FX); } + + iterator begin() const { return iterator(*this, 0); } + iterator end() const { return iterator(*this, EndBitPos); } + + void insert(FunctionEffect Effect) { KindBits.set(kindToPos(Effect.kind())); } + void insert(FunctionEffectsRef FX) { + for (FunctionEffect Item : FX.effects()) + insert(Item); + } + void insert(FunctionEffectKindSet Set) { KindBits |= Set.KindBits; } + + bool empty() const { return KindBits.none(); } + bool contains(const FunctionEffect::Kind EK) const { + return KindBits.test(kindToPos(EK)); + } + void dump(llvm::raw_ostream &OS) const; + + static FunctionEffectKindSet difference(FunctionEffectKindSet LHS, + FunctionEffectKindSet RHS) { + return FunctionEffectKindSet(LHS.KindBits & ~RHS.KindBits); + } +}; + /// A mutable set of FunctionEffects and possibly conditions attached to them. /// Used to compare and merge effects on declarations. /// @@ -5995,13 +6544,24 @@ class AttributedType : public Type, public llvm::FoldingSetNode { private: friend class ASTContext; // ASTContext creates these + const Attr *Attribute; + QualType ModifiedType; QualType EquivalentType; AttributedType(QualType canon, attr::Kind attrKind, QualType modified, QualType equivalent) + : AttributedType(canon, attrKind, nullptr, modified, equivalent) {} + + AttributedType(QualType canon, const Attr *attr, QualType modified, + QualType equivalent); + +private: + AttributedType(QualType canon, attr::Kind attrKind, const Attr *attr, + QualType modified, QualType equivalent) : Type(Attributed, canon, equivalent->getDependence()), - ModifiedType(modified), EquivalentType(equivalent) { + Attribute(attr), ModifiedType(modified), + EquivalentType(equivalent) { AttributedTypeBits.AttrKind = attrKind; } @@ -6010,6 +6570,8 @@ class AttributedType : public Type, public llvm::FoldingSetNode { return static_cast<Kind>(AttributedTypeBits.AttrKind); } + const Attr *getAttr() const { return Attribute; } + QualType getModifiedType() const { return ModifiedType; } QualType getEquivalentType() const { return EquivalentType; } @@ -6041,25 +6603,6 @@ class AttributedType : public Type, public llvm::FoldingSetNode { std::optional<NullabilityKind> getImmediateNullability() const; - /// Retrieve the attribute kind corresponding to the given - /// nullability kind. - static Kind getNullabilityAttrKind(NullabilityKind kind) { - switch (kind) { - case NullabilityKind::NonNull: - return attr::TypeNonNull; - - case NullabilityKind::Nullable: - return attr::TypeNullable; - - case NullabilityKind::NullableResult: - return attr::TypeNullableResult; - - case NullabilityKind::Unspecified: - return attr::TypeNullUnspecified; - } - llvm_unreachable("Unknown nullability kind."); - } - /// Strip off the top-level nullability annotation on the given /// type, if it's there. /// @@ -6421,27 +6964,30 @@ class DeducedTemplateSpecializationType : public DeducedType, DeducedTemplateSpecializationType(TemplateName Template, QualType DeducedAsType, - bool IsDeducedAsDependent, QualType Canon) + bool IsDeducedAsDependent) : DeducedType(DeducedTemplateSpecialization, DeducedAsType, toTypeDependence(Template.getDependence()) | (IsDeducedAsDependent ? TypeDependence::DependentInstantiation : TypeDependence::None), - Canon), + DeducedAsType.isNull() ? QualType(this, 0) + : DeducedAsType.getCanonicalType()), Template(Template) {} public: /// Retrieve the name of the template that we are deducing. TemplateName getTemplateName() const { return Template;} - void Profile(llvm::FoldingSetNodeID &ID) const { + void Profile(llvm::FoldingSetNodeID &ID) { Profile(ID, getTemplateName(), getDeducedType(), isDependentType()); } static void Profile(llvm::FoldingSetNodeID &ID, TemplateName Template, QualType Deduced, bool IsDependent) { Template.Profile(ID); - Deduced.Profile(ID); + QualType CanonicalType = + Deduced.isNull() ? Deduced : Deduced.getCanonicalType(); + ID.AddPointer(CanonicalType.getAsOpaquePtr()); ID.AddBoolean(IsDependent || Template.isDependent()); } @@ -8003,6 +8549,59 @@ inline bool Type::isAnyPointerType() const { inline bool Type::isSignableType() const { return isPointerType(); } +/* TO_UPSTREAM(BoundsSafety) ON */ +inline bool Type::isUnsafeIndexablePointerType() const { + const auto *PT = dyn_cast<PointerType>(CanonicalType); + if (!PT) + return false; + if (PT->isUnsafeIndexable()) + return true; + if (PT->isUnspecified() && hasAttr(attr::PtrUnsafeIndexable)) + return true; + return false; +} + +inline bool Type::isSinglePointerType() const { + const auto *PT = dyn_cast<PointerType>(CanonicalType); + if (!PT) + return false; + if (PT->isSingle()) + return true; + if (PT->isUnspecified() && hasAttr(attr::PtrSingle)) + return true; + return false; +} + +inline bool Type::isBidiIndexablePointerType() const { + const auto *PT = dyn_cast<PointerType>(CanonicalType); + return PT && PT->isBidiIndexable(); +} + +inline bool Type::isIndexablePointerType() const { + const auto *PT = dyn_cast<PointerType>(CanonicalType); + return PT && PT->isIndexable(); +} + +inline bool Type::isUnspecifiedPointerType() const { + return isPointerType() && + !(isUnsafeIndexablePointerType() || isSinglePointerType() || + isBidiIndexablePointerType() || isIndexablePointerType() || + isBoundsAttributedType() || isValueTerminatedType()); +} + +inline bool Type::isSafePointerType() const { + return isPointerType() && + (isSinglePointerType() || isBidiIndexablePointerType() || + isIndexablePointerType() || isBoundsAttributedType() || + isValueTerminatedType()); +} + +inline bool Type::isPointerTypeWithBounds() const { + const auto *PT = dyn_cast<PointerType>(CanonicalType); + return PT && PT->getPointerAttributes().hasUpperBound(); +} +/* TO_UPSTREAM(BoundsSafety) OFF */ + inline bool Type::isBlockPointerType() const { return isa<BlockPointerType>(CanonicalType); } @@ -8434,7 +9033,7 @@ inline bool Type::isUndeducedType() const { /// Determines whether this is a type for which one can define /// an overloaded operator. inline bool Type::isOverloadableType() const { - if (!CanonicalType->isDependentType()) + if (!isDependentType()) return isRecordType() || isEnumeralType(); return !isArrayType() && !isFunctionType() && !isAnyPointerType() && !isMemberPointerType(); diff --git a/clang/include/clang/AST/TypeLoc.h b/clang/include/clang/AST/TypeLoc.h index 9f2dff7a782cb..0acf15245b684 100644 --- a/clang/include/clang/AST/TypeLoc.h +++ b/clang/include/clang/AST/TypeLoc.h @@ -1146,6 +1146,40 @@ class CountAttributedTypeLoc final SourceRange getLocalSourceRange() const; }; +/* TO_UPSTREAM(BoundsSafety) ON */ +class DynamicRangePointerTypeLoc final + : public InheritingConcreteTypeLoc<BoundsAttributedTypeLoc, + DynamicRangePointerTypeLoc, + DynamicRangePointerType> { +public: + Expr *getStartPointer() const { return getTypePtr()->getStartPointer(); } + Expr *getEndPointer() const { return getTypePtr()->getEndPointer(); } + + SourceRange getLocalSourceRange() const; +}; + +struct ValueTerminatedTypeLocInfo {}; + +class ValueTerminatedTypeLoc final + : public ConcreteTypeLoc<UnqualTypeLoc, ValueTerminatedTypeLoc, + ValueTerminatedType, ValueTerminatedTypeLocInfo> { +public: + TypeLoc getOriginalLoc() const { return getInnerTypeLoc(); } + + void initializeLocal(ASTContext &Context, SourceLocation Loc) { + // do nothing + } + + QualType getInnerType() const { return getTypePtr()->desugar(); } + + SourceRange getLocalSourceRange() const { return {}; } + + unsigned getLocalDataSize() const { return 0; } + + Expr *getTerminatorExpr() const { return getTypePtr()->getTerminatorExpr(); } +}; +/* TO_UPSTREAM(BoundsSafety) OFF */ + struct MacroQualifiedLocInfo { SourceLocation ExpansionLoc; }; @@ -1307,6 +1341,10 @@ class PointerTypeLoc : public PointerLikeTypeLoc<PointerTypeLoc, void setStarLoc(SourceLocation Loc) { setSigilLoc(Loc); } + + BoundsSafetyPointerAttributes getPointerAttributes() const { + return this->getTypePtr()->getPointerAttributes(); + } }; /// Wrapper for source info for block pointers. diff --git a/clang/include/clang/AST/TypeProperties.td b/clang/include/clang/AST/TypeProperties.td index 7d4353c2773a3..52b110fff244c 100644 --- a/clang/include/clang/AST/TypeProperties.td +++ b/clang/include/clang/AST/TypeProperties.td @@ -22,7 +22,13 @@ let Class = PointerType in { let Read = [{ node->getPointeeType() }]; } - def : Creator<[{ return ctx.getPointerType(pointeeType); }]>; + // TO_UPSTREAM(BoundsSafety) ON + def : Property<"pointerAttributes", BoundsSafetyPointerAttributes> { + let Read = [{ node->getPointerAttributes() }]; + } + + def : Creator<[{ return ctx.getPointerType(pointeeType, pointerAttributes); }]>; + // TO_UPSTREAM(BoundsSafety) OFF } let Class = CountAttributedType in { @@ -44,6 +50,40 @@ let Class = CountAttributedType in { def : Creator<[{ return ctx.getCountAttributedType(WrappedTy, CountExpr, CountInBytes, OrNull, CoupledDecls); }]>; } +// TO_UPSTREAM(BoundsSafety) ON +let Class = DynamicRangePointerType in { + def : Property<"PointerTy", QualType> { + let Read = [{ node->desugar() }]; + } + def : Property<"StartPtr", ExprRef> { + let Read = [{ node->getStartPointer() }]; + } + def : Property<"EndPtr", ExprRef> { + let Read = [{ node->getEndPointer() }]; + } + def : Property<"StartPtrDecls", Array<TypeCoupledDeclRefInfo>> { + let Read = [{ node->getStartPtrDecls() }]; + } + def : Property<"EndPtrDecls", Array<TypeCoupledDeclRefInfo>> { + let Read = [{ node->getEndPtrDecls() }]; + } + def : Creator<[{ return ctx.getDynamicRangePointerType(PointerTy, StartPtr, EndPtr, + StartPtrDecls, EndPtrDecls); }]>; +} + +let Class = ValueTerminatedType in { + def : Property<"originalType", QualType> { + let Read = [{ node->desugar() }]; + } + def : Property<"terminatorExpr", ExprRef> { + let Read = [{ node->getTerminatorExpr() }]; + } + def : Creator<[{ + return ctx.getValueTerminatedType(originalType, terminatorExpr); + }]>; +} +// TO_UPSTREAM(BoundsSafety) OFF + let Class = AdjustedType in { def : Property<"originalType", QualType> { let Read = [{ node->getOriginalType() }]; @@ -665,12 +705,16 @@ let Class = AttributedType in { def : Property<"equivalentType", QualType> { let Read = [{ node->getEquivalentType() }]; } - def : Property<"attribute", AttrKind> { + def : Property<"attrKind", AttrKind> { let Read = [{ node->getAttrKind() }]; } + def : Property<"attribute", Attr> { + let Read = [{ node->getAttr() }]; + } def : Creator<[{ - return ctx.getAttributedType(attribute, modifiedType, equivalentType); + return ctx.getAttributedType(attrKind, modifiedType, + equivalentType, attribute); }]>; } diff --git a/clang/include/clang/ASTMatchers/ASTMatchers.h b/clang/include/clang/ASTMatchers/ASTMatchers.h index ca44c3ee08565..2f554020aa321 100644 --- a/clang/include/clang/ASTMatchers/ASTMatchers.h +++ b/clang/include/clang/ASTMatchers/ASTMatchers.h @@ -4033,7 +4033,7 @@ AST_POLYMORPHIC_MATCHER_P_OVERLOAD( AST_POLYMORPHIC_MATCHER_P_OVERLOAD( hasType, AST_POLYMORPHIC_SUPPORTED_TYPES(Expr, FriendDecl, ValueDecl, - CXXBaseSpecifier), + CXXBaseSpecifier, ObjCInterfaceDecl), internal::Matcher<Decl>, InnerMatcher, 1) { QualType QT = internal::getUnderlyingType(Node); if (!QT.isNull()) @@ -7433,7 +7433,8 @@ extern const AstTypeMatcher<RValueReferenceType> rValueReferenceType; AST_TYPELOC_TRAVERSE_MATCHER_DECL( pointee, getPointee, AST_POLYMORPHIC_SUPPORTED_TYPES(BlockPointerType, MemberPointerType, - PointerType, ReferenceType)); + PointerType, ReferenceType, + ObjCObjectPointerType)); /// Matches typedef types. /// diff --git a/clang/include/clang/ASTMatchers/ASTMatchersInternal.h b/clang/include/clang/ASTMatchers/ASTMatchersInternal.h index c1cc63fdb7433..91e76772ece52 100644 --- a/clang/include/clang/ASTMatchers/ASTMatchersInternal.h +++ b/clang/include/clang/ASTMatchers/ASTMatchersInternal.h @@ -161,6 +161,9 @@ inline QualType getUnderlyingType(const FriendDecl &Node) { inline QualType getUnderlyingType(const CXXBaseSpecifier &Node) { return Node.getType(); } +inline QualType getUnderlyingType(const ObjCInterfaceDecl &Node) { + return Node.getTypeForDecl()->getPointeeType(); +} /// Unifies obtaining a `TypeSourceInfo` from different node types. template <typename T, @@ -1113,6 +1116,11 @@ class HasDeclarationMatcher : public MatcherInterface<T> { return matchesDecl(Node.getDecl(), Finder, Builder); } + bool matchesSpecialized(const ObjCInterfaceDecl &Node, ASTMatchFinder *Finder, + BoundNodesTreeBuilder *Builder) const { + return matchesDecl(Node.getCanonicalDecl(), Finder, Builder); + } + /// Extracts the operator new of the new call and returns whether the /// inner matcher matches on it. bool matchesSpecialized(const CXXNewExpr &Node, @@ -1213,7 +1221,7 @@ using HasDeclarationSupportedTypes = ElaboratedType, InjectedClassNameType, LabelStmt, AddrLabelExpr, MemberExpr, QualType, RecordType, TagType, TemplateSpecializationType, TemplateTypeParmType, TypedefType, - UnresolvedUsingType, ObjCIvarRefExpr>; + UnresolvedUsingType, ObjCIvarRefExpr, ObjCInterfaceDecl>; /// A Matcher that allows binding the node it matches to an id. /// diff --git a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h index 228b4ae1e3e11..d3c3dfe1fa3be 100644 --- a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h +++ b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h @@ -15,6 +15,7 @@ #define LLVM_CLANG_ANALYSIS_ANALYSES_UNSAFEBUFFERUSAGE_H #include "clang/AST/Decl.h" +#include "clang/AST/Expr.h" #include "clang/AST/Stmt.h" #include "clang/Basic/SourceLocation.h" #include "llvm/Support/Debug.h" @@ -106,11 +107,42 @@ class UnsafeBufferUsageHandler { virtual void handleUnsafeOperation(const Stmt *Operation, bool IsRelatedToDecl, ASTContext &Ctx) = 0; + /// Invoked when a call to an unsafe libc function is found. + /// \param PrintfInfo + /// is 0 if the callee function is not a member of the printf family; + /// is 1 if the callee is `sprintf`; + /// is 2 if arguments of the call have `__size_by` relation but are not in a + /// safe pattern; + /// is 3 if string arguments do not guarantee null-termination + /// is 4 if the callee takes va_list + /// \param UnsafeArg one of the actual arguments that is unsafe, non-null + /// only when `2 <= PrintfInfo <= 3` + virtual void handleUnsafeLibcCall(const CallExpr *Call, unsigned PrintfInfo, + ASTContext &Ctx, + const Expr *UnsafeArg = nullptr) = 0; + /// Invoked when an unsafe operation with a std container is found. virtual void handleUnsafeOperationInContainer(const Stmt *Operation, bool IsRelatedToDecl, ASTContext &Ctx) = 0; + /* TO_UPSTREAM(BoundsSafety) ON */ + /// Invoked when an unsafe passing to count-attributed pointer is found. + virtual void handleUnsafeCountAttributedPointerArgument(const CallExpr *Call, + const Expr *Arg, + bool IsRelatedToDecl, + ASTContext &Ctx) { + handleUnsafeOperation(Arg, IsRelatedToDecl, Ctx); + } + + /// Invoked when an unsafe passing to __single pointer is found. + virtual void handleUnsafeSinglePointerArgument(const Expr *Arg, + bool IsRelatedToDecl, + ASTContext &Ctx) { + handleUnsafeOperation(Arg, IsRelatedToDecl, Ctx); + } + /* TO_UPSTREAM(BoundsSafety) OFF */ + /// Invoked when a fix is suggested against a variable. This function groups /// all variables that must be fixed together (i.e their types must be changed /// to the same target type to prevent type mismatches) into a single fixit. @@ -151,6 +183,10 @@ class UnsafeBufferUsageHandler { virtual bool ignoreUnsafeBufferInContainer(const SourceLocation &Loc) const = 0; + /// \return true iff unsafe libc call should NOT be reported at `Loc` + virtual bool + ignoreUnsafeBufferInLibcCall(const SourceLocation &Loc) const = 0; + virtual std::string getUnsafeBufferUsageAttributeTextAt(SourceLocation Loc, StringRef WSSuffix = "") const = 0; diff --git a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def index 242ad763ba62b..3bf40f29b33c6 100644 --- a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def +++ b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def @@ -18,10 +18,10 @@ #define WARNING_GADGET(name) GADGET(name) #endif -/// A `WARNING_GADGET` subset, where the code pattern of each gadget -/// corresponds uses of a (possibly hardened) contatiner (e.g., `std::span`). -#ifndef WARNING_CONTAINER_GADGET -#define WARNING_CONTAINER_GADGET(name) WARNING_GADGET(name) +/// A `WARNING_GADGET` subset, each of which may be enable/disable separately +/// with different flags +#ifndef WARNING_OPTIONAL_GADGET +#define WARNING_OPTIONAL_GADGET(name) WARNING_GADGET(name) #endif /// Safe gadgets correspond to code patterns that aren't unsafe but need to be @@ -38,7 +38,12 @@ WARNING_GADGET(PointerArithmetic) WARNING_GADGET(UnsafeBufferUsageAttr) WARNING_GADGET(UnsafeBufferUsageCtorAttr) WARNING_GADGET(DataInvocation) -WARNING_CONTAINER_GADGET(SpanTwoParamConstructor) // Uses of `std::span(arg0, arg1)` +// TO_UPSTREAM(BoundsSafety) ON +WARNING_GADGET(CountAttributedPointerArgument) +WARNING_GADGET(SinglePointerArgument) +// TO_UPSTREAM(BoundsSafety) OFF +WARNING_OPTIONAL_GADGET(UnsafeLibcFunctionCall) +WARNING_OPTIONAL_GADGET(SpanTwoParamConstructor) // Uses of `std::span(arg0, arg1)` FIXABLE_GADGET(ULCArraySubscript) // `DRE[any]` in an Unspecified Lvalue Context FIXABLE_GADGET(DerefSimplePtrArithFixable) FIXABLE_GADGET(PointerDereference) @@ -52,5 +57,5 @@ FIXABLE_GADGET(PointerInit) #undef FIXABLE_GADGET #undef WARNING_GADGET -#undef WARNING_CONTAINER_GADGET +#undef WARNING_OPTIONAL_GADGET #undef GADGET diff --git a/clang/include/clang/Basic/AllDiagnostics.h b/clang/include/clang/Basic/AllDiagnostics.h index e64634cc138f7..b71748483b065 100644 --- a/clang/include/clang/Basic/AllDiagnostics.h +++ b/clang/include/clang/Basic/AllDiagnostics.h @@ -16,6 +16,7 @@ #include "clang/Basic/DiagnosticAST.h" #include "clang/Basic/DiagnosticAnalysis.h" +#include "clang/Basic/DiagnosticCAS.h" #include "clang/Basic/DiagnosticComment.h" #include "clang/Basic/DiagnosticCrossTU.h" #include "clang/Basic/DiagnosticDriver.h" diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 4825979a974d2..930770601aa5a 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -120,6 +120,10 @@ def NonStaticNonConstCXXMethod [{!S->isStatic() && !S->isConst()}], "non-static non-const member functions">; +def ObjCClassMethod : SubsetSubject<ObjCMethod, + [{!S->isInstanceMethod()}], + "Objective-C class methods">; + def ObjCInstanceMethod : SubsetSubject<ObjCMethod, [{S->isInstanceMethod()}], "Objective-C instance methods">; @@ -253,6 +257,8 @@ class VariadicUnsignedArgument<string name> : Argument<name, 1>; class VariadicExprArgument<string name> : Argument<name, 1>; class VariadicStringArgument<string name> : Argument<name, 1>; class VariadicIdentifierArgument<string name> : Argument<name, 1>; +// TO_UPSTREAM(BoundsSafety) +class VariadicDeclArgument<string name> : Argument<name, 1>; // Like VariadicUnsignedArgument except values are ParamIdx. class VariadicParamIdxArgument<string name> : Argument<name, 1>; @@ -423,6 +429,19 @@ def HLSL : LangOpt<"HLSL">; // Language option for CMSE extensions def Cmse : LangOpt<"Cmse">; +// TO_UPSTREAM(BoundsSafety) ON +def BoundsSafety : LangOpt<"BoundsSafety">; +def BoundsSafetyAttributes : LangOpt<"BoundsSafetyAttributes">; + +// 'counted_by' is now available without -fbounds-safety for COnly (!LangOpts.CPlusPlus). +// Sema still ignores the attribute (with a warning) if it's applied to subjects other than +// flexible array members. +def CountedBySupported : LangOpt<"", [{ + // LangOpts.BoundsSafetyAttributes is to make it still available for experimental C++/Obj-C++. + !LangOpts.CPlusPlus || LangOpts.BoundsSafetyAttributes +}]>; +// TO_UPSTREAM(BoundsSafety) OFF + // Defines targets for target-specific attributes. Empty lists are unchecked. class TargetSpec { // Specifies Architectures for which the target applies, based off the @@ -477,6 +496,9 @@ def TargetELF : TargetSpec { def TargetELFOrMachO : TargetSpec { let ObjectFormats = ["ELF", "MachO"]; } +def TargetWindowsArm64EC : TargetSpec { + let CustomCode = [{ Target.getTriple().isWindowsArm64EC() }]; +} def TargetSupportsInitPriority : TargetSpec { let CustomCode = [{ !Target.getTriple().isOSzOS() }]; @@ -873,6 +895,12 @@ def Artificial : InheritableAttr { let SimpleHandler = 1; } +def TransparentStepping: InheritableAttr { + let Spellings = [Clang<"transparent_stepping">]; + let Subjects = SubjectList<[Function]>; + let Documentation = [TransparentSteppingDocs]; +} + def XRayInstrument : InheritableAttr { let Spellings = [Clang<"xray_always_instrument">, Clang<"xray_never_instrument">]; @@ -1096,6 +1124,25 @@ static llvm::Triple::EnvironmentType getEnvironmentType(llvm::StringRef Environm let Documentation = [AvailabilityDocs]; } +def DomainAvailability : InheritableAttr { + // This attribute has no spellings as it is created implicitly when + // __attribute__((availability(domain:...))) is seen. + let Spellings = []; + let Args = [StringArgument<"domain">, BoolArgument<"unavailable">]; + let AdditionalMembers = [{ + std::string getFeatureAttributeStr() const; + }]; + let Documentation = [InternalOnly]; +} + +// This attribute is used to annotate structs of type `_AvailabilityDomain`. +def AvailabilityDomain : InheritableAttr { + let Spellings = [Clang<"availability_domain">]; + let Args = [IdentifierArgument<"name">]; + let Subjects = SubjectList<[Var]>; + let Documentation = [Undocumented]; +} + def ExternalSourceSymbol : InheritableAttr { let Spellings = [Clang<"external_source_symbol", /*allowInC=*/1, /*version=*/20230206>]; @@ -1742,6 +1789,16 @@ def Format : InheritableAttr { let Documentation = [FormatDocs]; } +def FormatMatches : InheritableAttr { + let Spellings = [GCC<"format_matches">]; + let Args = [IdentifierArgument<"Type">, IntArgument<"FormatIdx">, ExprArgument<"ExpectedFormat">]; + let AdditionalMembers = [{ + StringLiteral *getFormatString() const; + }]; + let Subjects = SubjectList<[ObjCMethod, Block, HasFunctionProto]>; + let Documentation = [FormatMatchesDocs]; +} + def FormatArg : InheritableAttr { let Spellings = [GCC<"format_arg">]; let Args = [ParamIdxArgument<"FormatIdx">]; @@ -1824,10 +1881,40 @@ def LifetimeBound : DeclOrTypeAttr { let Spellings = [Clang<"lifetimebound", 0>]; let Subjects = SubjectList<[ParmVar, ImplicitObjectParameter], ErrorDiag>; let Documentation = [LifetimeBoundDocs]; - let LangOpts = [CPlusPlus]; let SimpleHandler = 1; } +def LifetimeCaptureBy : DeclOrTypeAttr { + let Spellings = [Clang<"lifetime_capture_by", 0>]; + let Subjects = SubjectList<[ParmVar, ImplicitObjectParameter], ErrorDiag>; + let Args = [VariadicParamOrParamIdxArgument<"Params">]; + let Documentation = [LifetimeCaptureByDocs]; + let AdditionalMembers = [{ +private: + ArrayRef<IdentifierInfo*> ArgIdents; + ArrayRef<SourceLocation> ArgLocs; + +public: + static constexpr int THIS = 0; + static constexpr int INVALID = -1; + static constexpr int UNKNOWN = -2; + static constexpr int GLOBAL = -3; + + void setArgs(ArrayRef<IdentifierInfo*> Idents, ArrayRef<SourceLocation> Locs) { + assert(Idents.size() == params_Size); + assert(Locs.size() == params_Size); + ArgIdents = Idents; + ArgLocs = Locs; + } + auto getArgIdents() const { return ArgIdents; } + auto getArgLocs() const { return ArgLocs; } + void setParamIdx(size_t Idx, int Val) { + assert(Idx < params_Size); + params_[Idx] = Val; + } +}]; +} + def TrivialABI : InheritableAttr { // This attribute does not have a C [[]] spelling because it requires the // CPlusPlus language option. @@ -2284,44 +2371,150 @@ def TypeNullUnspecified : TypeAttr { def CountedBy : DeclOrTypeAttr { let Spellings = [Clang<"counted_by">]; - let Subjects = SubjectList<[Field], ErrorDiag>; + // TO_UPSTREAM(BoundsSafety) ON + // `Subjects` which is present upstream is commented out because internally + // the attribute can go on Decls other than FieldDecl. + // + // let Subjects = SubjectList<[Field], ErrorDiag>; + // let Args = [ExprArgument<"Count">, IntArgument<"NestedLevel", 1>]; let LateParsed = LateAttrParseExperimentalExt; let ParseArgumentsAsUnevaluated = 1; let Documentation = [CountedByDocs]; - let LangOpts = [COnly]; + let LangOpts = [CountedBySupported]; + // TO_UPSTREAM(BoundsSafety) OFF } def CountedByOrNull : DeclOrTypeAttr { + // TO_UPSTREAM(BoundsSafety) ON + // `Subjects` which is present upstream is commented out because internally + // the attribute can go on Decls other than FieldDecl. + // + // let Subjects = SubjectList<[Field], ErrorDiag>; + // let Spellings = [Clang<"counted_by_or_null">]; - let Subjects = SubjectList<[Field], ErrorDiag>; let Args = [ExprArgument<"Count">, IntArgument<"NestedLevel", 1>]; let LateParsed = LateAttrParseExperimentalExt; let ParseArgumentsAsUnevaluated = 1; let Documentation = [CountedByDocs]; - let LangOpts = [COnly]; + let LangOpts = [CountedBySupported]; + // TO_UPSTREAM(BoundsSafety) OFF } def SizedBy : DeclOrTypeAttr { + // TO_UPSTREAM(BoundsSafety) ON + // `Subjects` which is present upstream is commented out because internally + // the attribute can go on Decls other than FieldDecl. + // + // let Subjects = SubjectList<[Field], ErrorDiag>; + // let Spellings = [Clang<"sized_by">]; - let Subjects = SubjectList<[Field], ErrorDiag>; let Args = [ExprArgument<"Size">, IntArgument<"NestedLevel", 1>]; let LateParsed = LateAttrParseExperimentalExt; let ParseArgumentsAsUnevaluated = 1; let Documentation = [CountedByDocs]; - let LangOpts = [COnly]; + let LangOpts = [CountedBySupported]; + // TO_UPSTREAM(BoundsSafety) OFF } def SizedByOrNull : DeclOrTypeAttr { + // TO_UPSTREAM(BoundsSafety) ON + // `Subjects` which is present upstream is commented out because internally + // the attribute can go on Decls other than FieldDecl. + // + // let Subjects = SubjectList<[Field], ErrorDiag>; + // let Spellings = [Clang<"sized_by_or_null">]; - let Subjects = SubjectList<[Field], ErrorDiag>; let Args = [ExprArgument<"Size">, IntArgument<"NestedLevel", 1>]; let LateParsed = LateAttrParseExperimentalExt; let ParseArgumentsAsUnevaluated = 1; let Documentation = [CountedByDocs]; - let LangOpts = [COnly]; + let LangOpts = [CountedBySupported]; + // TO_UPSTREAM(BoundsSafety) OFF +} + +// TO_UPSTREAM(BoundsSafety) ON +// BoundsSafety pointer type attributes. +def PtrUnsafeIndexable : TypeAttr { + let Spellings = [Clang<"unsafe_indexable">]; + let LangOpts = [BoundsSafetyAttributes]; + let Documentation = [Undocumented]; +} + +def PtrSingle : TypeAttr { + let Spellings = [Clang<"single">]; + let LangOpts = [BoundsSafetyAttributes]; + let Documentation = [Undocumented]; +} + +def PtrIndexable : TypeAttr { + let Spellings = [Clang<"indexable">]; + let LangOpts = [BoundsSafety]; + let Documentation = [Undocumented]; +} + +def PtrBidiIndexable : TypeAttr { + let Spellings = [Clang<"bidi_indexable">]; + let LangOpts = [BoundsSafety]; + let Documentation = [Undocumented]; +} + +def PtrEndedBy : DeclOrTypeAttr { + let Spellings = [Clang<"ended_by">]; + let Args = [ExprArgument<"EndPtr">, IntArgument<"NestedLevel">]; + let LateParsed = LateAttrParseExperimentalExt; + let ParseArgumentsAsUnevaluated = 1; + let LangOpts = [BoundsSafetyAttributes]; + let Documentation = [Undocumented]; } +def PtrTerminatedBy : TypeAttr { + let Spellings = [Clang<"terminated_by">]; + let Args = [ExprArgument<"Terminator">]; + let LangOpts = [BoundsSafetyAttributes]; + let Documentation = [Undocumented]; +} + +def UnsafeLateConst : InheritableAttr { + let Spellings = [Clang<"unsafe_late_const">]; + let Subjects = SubjectList<[GlobalVar], ErrorDiag>; + let LangOpts = [BoundsSafetyAttributes]; + let Documentation = [Undocumented]; +} + +def ArrayDecayDiscardsCountInParameters : TypeAttr { + let Spellings = [Clang<"decay_discards_count_in_parameters">]; + let LangOpts = [BoundsSafetyAttributes]; + let Documentation = [Undocumented]; +} + +// BoundsSafety: Don't need to gate implicit attributes. +def PtrAutoAttr : TypeAttr { + // This attribute has no spellings as it is only ever created implicitly + // during -fbounds-safety's implicit pointer type bounding. + let Spellings = []; + let SemaHandler = 0; + let Documentation = [InternalOnly]; +} + +def PtrAutoNullTerminatedAttr : TypeAttr { + // This attribute has no spellings as it is only ever created implicitly + // during -fbounds-safety's implicit pointer type bounding. + let Spellings = []; + let SemaHandler = 0; + let Documentation = [InternalOnly]; +} + +def DependerDecls : InheritableAttr { + let Spellings = []; + let Args = [BoolArgument<"IsDeref">, + VariadicDeclArgument<"DependerDecls">, + VariadicUnsignedArgument<"DependerLevels">]; + let Subjects = SubjectList<[Field, Var]>; + let Documentation = [InternalOnly]; +} +// TO_UPSTREAM(BoundsSafety) OFF + // This is a marker used to indicate that an __unsafe_unretained qualifier was // ignored because ARC is not enabled. The usual representation for this // qualifier is as an ObjCOwnership attribute with Kind == "none". @@ -2555,6 +2748,12 @@ def ObjCSubclassingRestricted : InheritableAttr { let SimpleHandler = 1; } +def ObjCCompleteDefinition : InheritableAttr { + let Spellings = [GNU<"objc_complete_definition">]; + let Subjects = SubjectList<[ObjCInterface], ErrorDiag>; + let Documentation = [Undocumented]; +} + def ObjCExplicitProtocolImpl : InheritableAttr { let Spellings = [Clang<"objc_protocol_requires_explicit_implementation">]; let Subjects = SubjectList<[ObjCProtocol], ErrorDiag>; @@ -2773,7 +2972,7 @@ def SwiftAsyncName : InheritableAttr { let Documentation = [SwiftAsyncNameDocs]; } -def SwiftAttr : InheritableAttr { +def SwiftAttr : DeclOrTypeAttr { let Spellings = [GNU<"swift_attr">]; let Args = [StringArgument<"Attribute">]; let Documentation = [SwiftAttrDocs]; @@ -2821,7 +3020,7 @@ def SwiftImportAsNonGeneric : InheritableAttr { let Documentation = [InternalOnly]; } -def SwiftImportPropertyAsAccessors : InheritableAttr { +def SwiftImportPropertyAsAccessors : InheritableAttr { // This attribute has no spellings as it is only ever created implicitly // from API notes. let Spellings = []; @@ -3339,6 +3538,14 @@ def ObjCRequiresPropertyDefs : InheritableAttr { let SimpleHandler = 1; } +def PointerAuth : TypeAttr { + let Spellings = [CustomKeyword<"__ptrauth">]; + let Args = [IntArgument<"Key">, + BoolArgument<"AddressDiscriminated", 1>, + IntArgument<"ExtraDiscriminator", 1>]; + let Documentation = [PtrAuthDocs]; +} + def Unused : InheritableAttr { let Spellings = [CXX11<"", "maybe_unused", 201603>, GCC<"unused">, C23<"", "maybe_unused", 202106>]; @@ -4027,6 +4234,12 @@ def SelectAny : InheritableAttr { let SimpleHandler = 1; } +def HybridPatchable : InheritableAttr, TargetSpecificAttr<TargetWindowsArm64EC> { + let Spellings = [Declspec<"hybrid_patchable">, Clang<"hybrid_patchable">]; + let Subjects = SubjectList<[Function]>; + let Documentation = [HybridPatchableDocs]; +} + def Thread : Attr { let Spellings = [Declspec<"thread">]; let LangOpts = [MicrosoftExt]; @@ -4447,7 +4660,7 @@ def ReleaseHandle : InheritableParamAttr { def UnsafeBufferUsage : InheritableAttr { let Spellings = [Clang<"unsafe_buffer_usage">]; - let Subjects = SubjectList<[Function]>; + let Subjects = SubjectList<[Function, Field]>; let Documentation = [UnsafeBufferUsageDocs]; } diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index 99738812c8157..833329bf6157c 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -1758,6 +1758,165 @@ Also see the documentation for `@available }]; } +def PtrAuthDocs : Documentation { + let Category = DocCatVariable; + let Content = [{ +The ``__ptrauth`` qualifier allows the programmer to directly control +how pointers are signed when they are stored in a particular variable. +This can be used to strengthen the default protections of pointer +authentication and make it more difficult for an attacker to escalate +an ability to alter memory into full control of a process. + +.. code-block:: c + + #include <ptrauth.h> + + typedef void (*my_callback)(const void*); + my_callback __ptrauth(ptrauth_key_process_dependent_code, 1, 0xe27a) callback; + +The first argument to ``__ptrauth`` is the name of the signing key. +Valid key names for the target are defined in ``<ptrauth.h>``. + +On ARM64, there are four keys: + +- ``ptrauth_key_process_independent_data`` +- ``ptrauth_key_process_dependent_data`` +- ``ptrauth_key_process_independent_code`` +- ``ptrauth_key_process_dependent_code`` + +In general, prefer using a code key for function pointers and a data key +for object pointers. The ARM64 architecture allows loads and calls to +execute more efficiently when the pointer is signed with an appropriate +key. Using code keys only for function pointers also substantially lessens +the risk of creating a so-called "signing oracle" for function pointers; +see the general pointer authentication language documentation. + +Using a process-dependent key provides stronger protection against +cross-process attacks. However, it also inhibits certain memory +optimizations when a shared library is loaded into multiple processes. +Using a process-independent key also allows signed pointers to be passed +in shared memory. Note that even the process-independent keys may change +after a reboot, so signed values should never be serialized. + +The second argument to ``__ptrauth`` is a flag (0 or 1) specifying whether +the object should use address discrimination. If only one argument is +given, the flag defaults to 0. Address discrimination provides strong +protection against attacks which copy signed pointers around in memory. +An attacker cannot usefully copy an arbitrary signed pointer over an +address-discriminated object. Nor can a value taken from an +address-discriminated object be usefully copied over some other signed +pointer. However, it is more expensive to copy values from one +address-discriminated object to another, even if the other arguments to +``__ptrauth`` are the same, and it is not valid to copy them with +``memcpy``. It is also not valid to map memory containing an +address-discriminated object into different places in the address +space, e.g. with ``mmap``. + +The third argument to ``__ptrauth`` is a small non-negative integer +which allows additional discrimination between objects. Using a +unique extra discriminator provides strong protection against attacks +which work by substituting one signed value for another. For example, +an attacker cannot usefully overwrite an object with a pointer from an +object using a different extra discriminator; this protection is similar +to the protection offered by address discrimination. A unique extra +discriminator also protects against "slide" attacks where an attacker +alters a pointer instead of altering the memory that the pointer points to. +The extra discriminator must be a constant expression. On ARM64, +its value must be between 0 and 65535. If the argument is not provided, +the default value is 0. It is generally preferable not to use the value 0, +especially with the process-independent keys, as this combination is used +in various places in the standard language ABI. + +The type qualified by ``__ptrauth`` must be a pointer type. Currently +only C pointer types are allowed and not block pointers, Objective-C +object pointers, or C++ references. ``__ptrauth`` is parsed and interpreted +using the same language rules as qualifiers like ``const`` and ``volatile``. +For example: + +.. code-block:: c + + __ptrauth(...) int *ex0; /* invalid: qualifies 'int', which is not a pointer type */ + int * __ptrauth(...) ex1; /* valid: ex1 has qualified type */ + int * __ptrauth(...) *ex2; /* valid: ex2 is a pointer to a qualified object */ + + typedef int *intp; + __ptrauth(...) intp ex3; /* valid: ex3 has qualified type */ + intp __ptrauth(...) ex4; /* valid: means the exact same thing as ex3 */ + +Assigning a non-null pointer to a ``__ptrauth``-qualified l-value, or +initializing a ``__ptrauth``-qualified object with a non-null pointer, +causes the pointer to be signed according to the described schema before +being stored into memory. If such an initialization is a constant +initialization, then the signing is also done as part of constant +initialization: that is, it is done when the program is loaded, before +any dynamic initialization occurs. Loading a non-null pointer from a +``__ptrauth``-qualified l-value causes the pointer to be authenticated +according to the describe schema before being produced as the result +of the expression. A null pointer keeps its standard representation when +stored in a ``__ptrauth``-qualified object; on a typical target where this +is an all-zero pattern, this means that operations like ``bzero`` and +``calloc`` do still correctly initialize objects with null. + +If a ``__ptrauth``-qualified l-value of function pointer type is +used as the function operand of a call expression, the function pointer +will be authenticated "atomically" with the call, such that an attacker +will not be able to corrupt the destination of the call even in the +presence of undefined behavior. (That is, the compiler must not +leave an un-signed pointer that it will later unconditionally trust +in a place where it could be feasibly overwritten by an attacker, +such as the stack or a callee-save register during an intervening call. +The compiler is not required to protect against improbable attacks +such as corruption of the register file, as might occur with a +corrupted kernel. It also need not guard against jumps to an arbitrary +place in the instruction stream, since such jumps would require an +attacker to already fully control the PC.) + +If the ABI specifies that a pointer is always signed --- that is, +if the pointer is a function pointer and the target uses ABI function +pointer authentication --- then signing and authenticating it as part +of a load/store actually means resigning it to/from the standard ABI +signature schema. Similarly, if both operands of a simple assignment +operator are ``__ptrauth``-qualified, the pointer copied by the +assignment is resigned from the right-hand operand's schema to the +left-hand operand's schema. These resigning operations are also done +"atomically" in the same sense as above. + +As a final guarantee, if the right-hand operand of an assignment or +the expression used to initialize a ``__ptrauth``-qualified object is +a direct reference to an object or function (e.g. ``&my_var``), the +signing of that pointer is atomic with the evaluaton of the reference +in this same sense. + +Otherwise, there are no guarantees of atomicity, and it is the +programmer's responsibility to avoid allowing a store into a +``__ptrauth``-qualified object to create a potential "signing oracle" +which an attacker could use to sign an arbitrary pointer of their choice. +Such oracles are particularly problematic when the signing uses a code +key because the oracle could potentially be used to allow an attacker +to construct a validly-signed function pointer, v-table entry, or +return address that points to an arbitrary instruction, allowing them +to completely take over the PC. Programmers attempting to use +``__ptrauth`` to protect a data pointer, or to protect function pointers +on targets that do not use ABI function pointer authentication, should +aim to maintain a "chain of authentication" from initialization all +the way to the point at which the pointer is used. If this is infeasible, +they should consider using ``ptrauth_sign_generic_data`` instead. + +Types that are written in r-value positions, such as return types, +parameter types, and cast types, may not be ``__ptrauth``-qualified +at the outermost level. This may be supported in the future. + +In C++, the arguments to ``__ptrauth`` may not be instantiation-dependent. +This may be supported in the future. + +This feature may be tested for with ``__has_feature(ptrauth_qualifier)``. +It is enabled whenever the ``ptrauth`` intrinsics are enabled. + +``<ptrauth.h>`` provides predefined qualifiers for various language +features that implicitly use pointer authentication. + }]; +} + def ExternalSourceSymbolDocs : Documentation { let Category = DocCatDecl; let Content = [{ @@ -3485,6 +3644,132 @@ behavior of the program is undefined. }]; } +def FormatMatchesDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ + +The ``format`` attribute is the basis for the enforcement of diagnostics in the +``-Wformat`` family, but it only handles the case where the format string is +passed along with the arguments it is going to format. It cannot handle the case +where the format string and the format arguments are passed separately from each +other. For instance: + +.. code-block:: c + + static const char *first_name; + static double todays_temperature; + static int wind_speed; + + void say_hi(const char *fmt) { + printf(fmt, first_name, todays_temperature); + // ^ warning: format string is not a string literal + printf(fmt, first_name, wind_speed); + // ^ warning: format string is not a string literal + } + + int main() { + say_hi("hello %s, it is %g degrees outside"); + say_hi("hello %s, it is %d degrees outside!"); + // ^ no diagnostic, but %d cannot format doubles + } + +In this example, ``fmt`` is expected to format a ``const char *`` and a +``double``, but these values are not passed to ``say_hi``. Without the +``format`` attribute (which cannot apply in this case), the -Wformat-nonliteral +diagnostic unnecessarily triggers in the body of ``say_hi``, and incorrect +``say_hi`` call sites do not trigger a diagnostic. + +To complement the ``format`` attribute, Clang also defines the +``format_matches`` attribute. Its syntax is similar to the ``format`` +attribute's, but instead of taking the index of the first formatted value +argument, it takes a C string literal with the expected specifiers: + +.. code-block:: c + + static const char *first_name; + static double todays_temperature; + static int wind_speed; + + __attribute__((__format_matches__(printf, 1, "%s %g"))) + void say_hi(const char *fmt) { + printf(fmt, first_name, todays_temperature); // no dignostic + printf(fmt, first_name, wind_speed); // warning: format specifies type 'int' but the argument has type 'double' + } + + int main() { + say_hi("hello %s, it is %g degrees outside"); + say_hi("it is %g degrees outside, have a good day %s!"); + // warning: format specifies 'double' where 'const char *' is required + // warning: format specifies 'const char *' where 'double' is required + } + +The third argument to ``format_matches`` is expected to evaluate to a **C string +literal** even when the format string would normally be a different type for the +given flavor, like a ``CFStringRef`` or a ``NSString *``. + +The only requirement on the format string literal is that it has specifiers +that are compatible with the arguments that will be used. It can contain +arbitrary non-format characters. For instance, for the purposes of compile-time +validation, ``"%s scored %g%% on her test"`` and ``"%s%g"`` are interchangeable +as the format string argument. As a means of self-documentation, users may +prefer the former when it provides a useful example of an expected format +string. + +In the implementation of a function with the ``format_matches`` attribute, +format verification works as if the format string was identical to the one +specified in the attribute. + +.. code-block:: c + + __attribute__((__format_matches__(printf, 1, "%s %g"))) + void say_hi(const char *fmt) { + printf(fmt, "person", 546); + // ^ warning: format specifies type 'double' but the + // argument has type 'int' + // note: format string is defined here: + // __attribute__((__format_matches__(printf, 1, "%s %g"))) + // ^~ + } + + +At the call sites of functions with the ``format_matches`` attribute, format +verification instead compares the two format strings to evaluate their +equivalence. Each format flavor defines equivalence between format specifiers. +Generally speaking, two specifiers are equivalent if they format the same type. +For instance, in the ``printf`` flavor, ``%2i`` and ``%-0.5d`` are compatible. +When ``-Wformat-signedness`` is disabled, ``%d`` and ``%u`` are compatible. For +a negative example, ``%ld`` is incompatible with ``%d``. + +Do note the following un-obvious cases: + +* Passing ``NULL`` as the format string does not trigger format diagnostics. +* When the format string is not NULL, it cannot _miss_ specifiers, even in + trailing positions. For instance, ``%d`` is not accepted when the required + format is ``%d %d %d``. +* While checks for the ``format`` attribute tolerate sone size mismatches + that standard argument promotion renders immaterial (such as formatting an + ``int`` with ``%hhd``, which specifies a ``char``-sized integer), checks for + ``format_matches`` require specified argument sizes to match exactly. +* Format strings expecting a variable modifier (such as ``%*s``) are + incompatible with format strings that would itemize the variable modifiers + (such as ``%i %s``), even if the two specify ABI-compatible argument lists. +* All pointer specifiers, modifiers aside, are mutually incompatible. For + instance, ``%s`` is not compatible with ``%p``, and ``%p`` is not compatible + with ``%n``, and ``%hhn`` is incompatible with ``%s``, even if the pointers + are ABI-compatible or identical on the selected platform. However, ``%0.5s`` + is compatible with ``%s``, since the difference only exists in modifier flags. + This is not overridable with ``-Wformat-pedantic`` or its inverse, which + control similar behavior in ``-Wformat``. + +At this time, clang implements ``format_matches`` only for format types in the +``printf`` family. This includes variants such as Apple's NSString format and +the FreeBSD ``kprintf``, but excludes ``scanf``. Using a known but unsupported +format silently fails in order to be compatible with other implementations that +would support these formats. + + }]; +} + def FlagEnumDocs : Documentation { let Category = DocCatDecl; let Content = [{ @@ -3654,6 +3939,75 @@ have their lifetimes extended. }]; } +def LifetimeCaptureByDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ + Similar to `lifetimebound`_, the ``lifetime_capture_by(X)`` attribute on a function +parameter or implicit object parameter indicates that that objects that are referred to +by that parameter may also be referred to by the capturing entity ``X``. + +By default, a reference is considered to refer to its referenced object, a +pointer is considered to refer to its pointee, a ``std::initializer_list<T>`` +is considered to refer to its underlying array, and aggregates (arrays and +simple ``struct``\s) are considered to refer to all objects that their +transitive subobjects refer to. + +The capturing entity ``X`` can be one of the following: +- Another (named) function parameter. + + .. code-block:: c++ + + void addToSet(std::string_view a [[clang::lifetime_capture_by(s)]], std::set<std::string_view>& s) { + s.insert(a); + } + +- ``this`` (in case of member functions). + + .. code-block:: c++ + + class S { + void addToSet(std::string_view a [[clang::lifetime_capture_by(this)]]) { + s.insert(a); + } + std::set<std::string_view> s; + }; + +- 'global', 'unknown' (without quotes). + + .. code-block:: c++ + + std::set<std::string_view> s; + void addToSet(std::string_view a [[clang::lifetime_capture_by(global)]]) { + s.insert(a); + } + void addSomewhere(std::string_view a [[clang::lifetime_capture_by(unknown)]]); + +The attribute can be applied to the implicit ``this`` parameter of a member +function by writing the attribute after the function type: + +.. code-block:: c++ + + struct S { + const char *data(std::set<S*>& s) [[clang::lifetime_capture_by(s)]] { + s.insert(this); + } + }; + +The attribute supports specifying more than one capturing entities: + +.. code-block:: c++ + + void addToSets(std::string_view a [[clang::lifetime_capture_by(s1, s2)]], + std::set<std::string_view>& s1, + std::set<std::string_view>& s2) { + s1.insert(a); + s2.insert(a); + } + +.. _`lifetimebound`: https://clang.llvm.org/docs/AttributeReference.html#lifetimebound + }]; +} + def TrivialABIDocs : Documentation { let Category = DocCatDecl; let Content = [{ @@ -4416,8 +4770,8 @@ def SwiftAttrDocs : Documentation { let Heading = "swift_attr"; let Content = [{ The ``swift_attr`` provides a Swift-specific annotation for the declaration -to which the attribute appertains to. It can be used on any declaration -in Clang. This kind of annotation is ignored by Clang as it doesn't have any +or type to which the attribute appertains to. It can be used on any declaration +or type in Clang. This kind of annotation is ignored by Clang as it doesn't have any semantic meaning in languages supported by Clang. The Swift compiler can interpret these annotations according to its own rules when importing C or Objective-C declarations. @@ -5985,6 +6339,16 @@ For more information see or `msvc documentation <https://docs.microsoft.com/pl-pl/cpp/cpp/selectany>`_. }]; } +def HybridPatchableDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +The ``hybrid_patchable`` attribute declares an ARM64EC function with an additional +x86-64 thunk, which may be patched at runtime. + +For more information see +`ARM64EC ABI documentation <https://learn.microsoft.com/en-us/windows/arm/arm64ec-abi>`_. +}]; } + def WebAssemblyExportNameDocs : Documentation { let Category = DocCatFunction; let Content = [{ @@ -6764,77 +7128,103 @@ def UnsafeBufferUsageDocs : Documentation { let Category = DocCatFunction; let Content = [{ The attribute ``[[clang::unsafe_buffer_usage]]`` should be placed on functions -that need to be avoided as they are prone to buffer overflows. It is designed to -work together with the off-by-default compiler warning ``-Wunsafe-buffer-usage`` -to help codebases transition away from raw pointer based buffer management, -in favor of safer abstractions such as C++20 ``std::span``. The attribute causes -``-Wunsafe-buffer-usage`` to warn on every use of the function, and it may -enable ``-Wunsafe-buffer-usage`` to emit automatic fix-it hints -which would help the user replace such unsafe functions with safe +that need to be avoided as they are prone to buffer overflows or unsafe buffer +struct fields. It is designed to work together with the off-by-default compiler +warning ``-Wunsafe-buffer-usage``to help codebases transition away from raw pointer +based buffer management, in favor of safer abstractions such as C++20 ``std::span``. +The attribute causes ``-Wunsafe-buffer-usage`` to warn on every use of the function or +the field it is attached to, and it may also lead to emission of automatic fix-it +hints which would help the user replace the use of unsafe functions(/fields) with safe alternatives, though the attribute can be used even when the fix can't be automated. -The attribute does not suppress ``-Wunsafe-buffer-usage`` inside the function -to which it is attached. These warnings still need to be addressed. +* Attribute attached to functions: The attribute does not suppress + ``-Wunsafe-buffer-usage`` inside the function to which it is attached. + These warnings still need to be addressed. -The attribute is warranted even if the only way a function can overflow -the buffer is by violating the function's preconditions. For example, it -would make sense to put the attribute on function ``foo()`` below because -passing an incorrect size parameter would cause a buffer overflow: + The attribute is warranted even if the only way a function can overflow + the buffer is by violating the function's preconditions. For example, it + would make sense to put the attribute on function ``foo()`` below because + passing an incorrect size parameter would cause a buffer overflow: -.. code-block:: c++ + .. code-block:: c++ - [[clang::unsafe_buffer_usage]] - void foo(int *buf, size_t size) { - for (size_t i = 0; i < size; ++i) { - buf[i] = i; + [[clang::unsafe_buffer_usage]] + void foo(int *buf, size_t size) { + for (size_t i = 0; i < size; ++i) { + buf[i] = i; + } } - } -The attribute is NOT warranted when the function uses safe abstractions, -assuming that these abstractions weren't misused outside the function. -For example, function ``bar()`` below doesn't need the attribute, -because assuming that the container ``buf`` is well-formed (has size that -fits the original buffer it refers to), overflow cannot occur: + The attribute is NOT warranted when the function uses safe abstractions, + assuming that these abstractions weren't misused outside the function. + For example, function ``bar()`` below doesn't need the attribute, + because assuming that the container ``buf`` is well-formed (has size that + fits the original buffer it refers to), overflow cannot occur: -.. code-block:: c++ + .. code-block:: c++ - void bar(std::span<int> buf) { - for (size_t i = 0; i < buf.size(); ++i) { - buf[i] = i; + void bar(std::span<int> buf) { + for (size_t i = 0; i < buf.size(); ++i) { + buf[i] = i; + } } - } -In this case function ``bar()`` enables the user to keep the buffer -"containerized" in a span for as long as possible. On the other hand, -Function ``foo()`` in the previous example may have internal -consistency, but by accepting a raw buffer it requires the user to unwrap -their span, which is undesirable according to the programming model -behind ``-Wunsafe-buffer-usage``. + In this case function ``bar()`` enables the user to keep the buffer + "containerized" in a span for as long as possible. On the other hand, + Function ``foo()`` in the previous example may have internal + consistency, but by accepting a raw buffer it requires the user to unwrap + their span, which is undesirable according to the programming model + behind ``-Wunsafe-buffer-usage``. -The attribute is warranted when a function accepts a raw buffer only to -immediately put it into a span: + The attribute is warranted when a function accepts a raw buffer only to + immediately put it into a span: -.. code-block:: c++ + .. code-block:: c++ - [[clang::unsafe_buffer_usage]] - void baz(int *buf, size_t size) { - std::span<int> sp{ buf, size }; - for (size_t i = 0; i < sp.size(); ++i) { - sp[i] = i; + [[clang::unsafe_buffer_usage]] + void baz(int *buf, size_t size) { + std::span<int> sp{ buf, size }; + for (size_t i = 0; i < sp.size(); ++i) { + sp[i] = i; + } } - } -In this case ``baz()`` does not contain any unsafe operations, but the awkward -parameter type causes the caller to unwrap the span unnecessarily. -Note that regardless of the attribute, code inside ``baz()`` isn't flagged -by ``-Wunsafe-buffer-usage`` as unsafe. It is definitely undesirable, -but if ``baz()`` is on an API surface, there is no way to improve it -to make it as safe as ``bar()`` without breaking the source and binary -compatibility with existing users of the function. In such cases -the proper solution would be to create a different function (possibly -an overload of ``baz()``) that accepts a safe container like ``bar()``, -and then use the attribute on the original ``baz()`` to help the users -update their code to use the new function. + In this case ``baz()`` does not contain any unsafe operations, but the awkward + parameter type causes the caller to unwrap the span unnecessarily. + Note that regardless of the attribute, code inside ``baz()`` isn't flagged + by ``-Wunsafe-buffer-usage`` as unsafe. It is definitely undesirable, + but if ``baz()`` is on an API surface, there is no way to improve it + to make it as safe as ``bar()`` without breaking the source and binary + compatibility with existing users of the function. In such cases + the proper solution would be to create a different function (possibly + an overload of ``baz()``) that accepts a safe container like ``bar()``, + and then use the attribute on the original ``baz()`` to help the users + update their code to use the new function. + +* Attribute attached to fields: The attribute should only be attached to + struct fields, if the fields can not be updated to a safe type with bounds + check, such as std::span. In other words, the buffers prone to unsafe accesses + should always be updated to use safe containers/views and attaching the attribute + must be last resort when such an update is infeasible. + + The attribute can be placed on individual fields or a set of them as shown below. + + .. code-block:: c++ + + struct A { + [[clang::unsafe_buffer_usage]] + int *ptr1; + + [[clang::unsafe_buffer_usage]] + int *ptr2, buf[10]; + + [[clang::unsafe_buffer_usage]] + size_t sz; + }; + + Here, every read/write to the fields ptr1, ptr2, buf and sz will trigger a warning + that the field has been explcitly marked as unsafe due to unsafe-buffer operations. + }]; } @@ -7700,6 +8090,66 @@ As such, this function attribute is currently only supported on X86 targets. }]; } +def TransparentSteppingDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +The ``transparent_stepping`` attribute is intended as a hint for debuggers that this +function itself is not interesting, but it calls a function that might be. So, when +stepping in arrives at a function with this attribute, debuggers should transparently +step-in through it into the functions called by the annotated function (but not by +subsequent calls made by those functions), stopping at the first one its normal rules +for whether to stop says to stop at - or stepping out again if none qualify. Also, when +stepping out arrives at a function with this attribute, the debugger should continue +stepping out to its caller. + +For example: + +.. code-block:: c + + int bar(void) { + return 42; + } + + __attribute__((transparent_stepping)) + int foo(void) { + return bar(); + } + + int caller(void) { + return foo(); + } + +Stepping into ``foo`` should step directly into ``bar`` instead, and stepping out of ``bar`` +should stop in ``caller``. + +Functions with the ``transparent_stepping`` attribute can be chained together: + +.. code-block:: c + + int baz(void) { + return 42; + } + + __attribute__((transparent_stepping)) + int bar(void) { + return baz(); + } + + __attribute__((transparent_stepping)) + int foo(void) { + return bar(); + } + + int caller(void) { + return foo(); + } + +In this example, stepping into ``foo`` should step directly into ``baz``, and stepping out of +``baz`` should stop in ``caller``. + }]; +} + + def ReadOnlyPlacementDocs : Documentation { let Category = DocCatType; let Content = [{This attribute is attached to a structure, class or union declaration. @@ -8221,9 +8671,9 @@ compiler warnings: - A redeclaration of a ``nonblocking`` or ``nonallocating`` function must also be declared with the same attribute (or a stronger one). A redeclaration may add an attribute. -The warnings are controlled by ``-Wfunction-effects``, which is enabled by default. +The warnings are controlled by ``-Wfunction-effects``, which is disabled by default. -In a future commit, the compiler will diagnose function calls from ``nonblocking`` and ``nonallocating`` +The compiler also diagnoses function calls from ``nonblocking`` and ``nonallocating`` functions to other functions which lack the appropriate attribute. }]; } diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td index 4133f6ff40cf3..eb51e30495411 100644 --- a/clang/include/clang/Basic/Builtins.td +++ b/clang/include/clang/Basic/Builtins.td @@ -2559,7 +2559,7 @@ def Rotl : MSLangBuiltin { def Lrotl : MSLangBuiltin { let Spellings = ["_lrotl"]; let Attributes = [NoThrow, Constexpr]; - let Prototype = "unsigned long int(unsigned long int, int)"; + let Prototype = "msuint32_t(msuint32_t, int)"; } def Rotl64 : MSLangBuiltin { @@ -2589,7 +2589,7 @@ def Rotr : MSLangBuiltin { def Lrotr : MSLangBuiltin { let Spellings = ["_lrotr"]; let Attributes = [NoThrow, Constexpr]; - let Prototype = "unsigned long int(unsigned long int, int)"; + let Prototype = "msuint32_t(msuint32_t, int)"; } def Rotr64 : MSLangBuiltin { diff --git a/clang/include/clang/Basic/BuiltinsLoongArchLASX.def b/clang/include/clang/Basic/BuiltinsLoongArchLASX.def index 4cf51cc000f6f..f644b820a6189 100644 --- a/clang/include/clang/Basic/BuiltinsLoongArchLASX.def +++ b/clang/include/clang/Basic/BuiltinsLoongArchLASX.def @@ -12,29 +12,29 @@ // //===----------------------------------------------------------------------===// -TARGET_BUILTIN(__builtin_lasx_xvadd_b, "V32cV32cV32c", "nc", "lasx") +TARGET_BUILTIN(__builtin_lasx_xvadd_b, "V32ScV32ScV32Sc", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvadd_h, "V16sV16sV16s", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvadd_w, "V8iV8iV8i", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvadd_d, "V4LLiV4LLiV4LLi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvadd_q, "V4LLiV4LLiV4LLi", "nc", "lasx") -TARGET_BUILTIN(__builtin_lasx_xvsub_b, "V32cV32cV32c", "nc", "lasx") +TARGET_BUILTIN(__builtin_lasx_xvsub_b, "V32ScV32ScV32Sc", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvsub_h, "V16sV16sV16s", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvsub_w, "V8iV8iV8i", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvsub_d, "V4LLiV4LLiV4LLi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvsub_q, "V4LLiV4LLiV4LLi", "nc", "lasx") -TARGET_BUILTIN(__builtin_lasx_xvaddi_bu, "V32cV32cIUi", "nc", "lasx") +TARGET_BUILTIN(__builtin_lasx_xvaddi_bu, "V32ScV32ScIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvaddi_hu, "V16sV16sIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvaddi_wu, "V8iV8iIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvaddi_du, "V4LLiV4LLiIUi", "nc", "lasx") -TARGET_BUILTIN(__builtin_lasx_xvsubi_bu, "V32cV32cIUi", "nc", "lasx") +TARGET_BUILTIN(__builtin_lasx_xvsubi_bu, "V32ScV32ScIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvsubi_hu, "V16sV16sIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvsubi_wu, "V8iV8iIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvsubi_du, "V4LLiV4LLiIUi", "nc", "lasx") -TARGET_BUILTIN(__builtin_lasx_xvneg_b, "V32cV32c", "nc", "lasx") +TARGET_BUILTIN(__builtin_lasx_xvneg_b, "V32ScV32Sc", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvneg_h, "V16sV16s", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvneg_w, "V8iV8i", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvneg_d, "V4LLiV4LLi", "nc", "lasx") @@ -79,22 +79,22 @@ TARGET_BUILTIN(__builtin_lasx_xvhsubw_wu_hu, "V8UiV16UsV16Us", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvhsubw_du_wu, "V4ULLiV8UiV8Ui", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvhsubw_qu_du, "V4ULLiV4ULLiV4ULLi", "nc", "lasx") -TARGET_BUILTIN(__builtin_lasx_xvaddwev_h_b, "V16sV32cV32c", "nc", "lasx") +TARGET_BUILTIN(__builtin_lasx_xvaddwev_h_b, "V16sV32ScV32Sc", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvaddwev_w_h, "V8SiV16sV16s", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvaddwev_d_w, "V4LLiV8SiV8Si", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvaddwev_q_d, "V4LLiV4LLiV4LLi", "nc", "lasx") -TARGET_BUILTIN(__builtin_lasx_xvaddwod_h_b, "V16sV32cV32c", "nc", "lasx") +TARGET_BUILTIN(__builtin_lasx_xvaddwod_h_b, "V16sV32ScV32Sc", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvaddwod_w_h, "V8SiV16sV16s", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvaddwod_d_w, "V4LLiV8SiV8Si", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvaddwod_q_d, "V4LLiV4LLiV4LLi", "nc", "lasx") -TARGET_BUILTIN(__builtin_lasx_xvsubwev_h_b, "V16sV32cV32c", "nc", "lasx") +TARGET_BUILTIN(__builtin_lasx_xvsubwev_h_b, "V16sV32ScV32Sc", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvsubwev_w_h, "V8SiV16sV16s", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvsubwev_d_w, "V4LLiV8SiV8Si", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvsubwev_q_d, "V4LLiV4LLiV4LLi", "nc", "lasx") -TARGET_BUILTIN(__builtin_lasx_xvsubwod_h_b, "V16sV32cV32c", "nc", "lasx") +TARGET_BUILTIN(__builtin_lasx_xvsubwod_h_b, "V16sV32ScV32Sc", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvsubwod_w_h, "V8SiV16sV16s", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvsubwod_d_w, "V4LLiV8SiV8Si", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvsubwod_q_d, "V4LLiV4LLiV4LLi", "nc", "lasx") @@ -119,12 +119,12 @@ TARGET_BUILTIN(__builtin_lasx_xvsubwod_w_hu, "V8SiV16UsV16Us", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvsubwod_d_wu, "V4LLiV8UiV8Ui", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvsubwod_q_du, "V4LLiV4ULLiV4ULLi", "nc", "lasx") -TARGET_BUILTIN(__builtin_lasx_xvaddwev_h_bu_b, "V16sV32UcV32c", "nc", "lasx") +TARGET_BUILTIN(__builtin_lasx_xvaddwev_h_bu_b, "V16sV32UcV32Sc", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvaddwev_w_hu_h, "V8SiV16UsV16s", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvaddwev_d_wu_w, "V4LLiV8UiV8Si", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvaddwev_q_du_d, "V4LLiV4ULLiV4LLi", "nc", "lasx") -TARGET_BUILTIN(__builtin_lasx_xvaddwod_h_bu_b, "V16sV32UcV32c", "nc", "lasx") +TARGET_BUILTIN(__builtin_lasx_xvaddwod_h_bu_b, "V16sV32UcV32Sc", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvaddwod_w_hu_h, "V8SiV16UsV16s", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvaddwod_d_wu_w, "V4LLiV8UiV8Si", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvaddwod_q_du_d, "V4LLiV4ULLiV4LLi", "nc", "lasx") @@ -209,7 +209,7 @@ TARGET_BUILTIN(__builtin_lasx_xvmul_h, "V16SsV16SsV16Ss", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvmul_w, "V8SiV8SiV8Si", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvmul_d, "V4SLLiV4SLLiV4SLLi", "nc", "lasx") -TARGET_BUILTIN(__builtin_lasx_xvmuh_b, "V32cV32cV32c", "nc", "lasx") +TARGET_BUILTIN(__builtin_lasx_xvmuh_b, "V32ScV32ScV32Sc", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvmuh_h, "V16sV16sV16s", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvmuh_w, "V8iV8iV8i", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvmuh_d, "V4LLiV4LLiV4LLi", "nc", "lasx") @@ -219,12 +219,12 @@ TARGET_BUILTIN(__builtin_lasx_xvmuh_hu, "V16UsV16UsV16Us", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvmuh_wu, "V8UiV8UiV8Ui", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvmuh_du, "V4ULLiV4ULLiV4ULLi", "nc", "lasx") -TARGET_BUILTIN(__builtin_lasx_xvmulwev_h_b, "V16sV32cV32c", "nc", "lasx") +TARGET_BUILTIN(__builtin_lasx_xvmulwev_h_b, "V16sV32ScV32Sc", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvmulwev_w_h, "V8SiV16sV16s", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvmulwev_d_w, "V4LLiV8SiV8Si", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvmulwev_q_d, "V4LLiV4LLiV4LLi", "nc", "lasx") -TARGET_BUILTIN(__builtin_lasx_xvmulwod_h_b, "V16sV32cV32c", "nc", "lasx") +TARGET_BUILTIN(__builtin_lasx_xvmulwod_h_b, "V16sV32ScV32Sc", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvmulwod_w_h, "V8SiV16sV16s", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvmulwod_d_w, "V4LLiV8SiV8Si", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvmulwod_q_d, "V4LLiV4LLiV4LLi", "nc", "lasx") @@ -239,12 +239,12 @@ TARGET_BUILTIN(__builtin_lasx_xvmulwod_w_hu, "V8SiV16UsV16Us", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvmulwod_d_wu, "V4LLiV8UiV8Ui", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvmulwod_q_du, "V4LLiV4ULLiV4ULLi", "nc", "lasx") -TARGET_BUILTIN(__builtin_lasx_xvmulwev_h_bu_b, "V16sV32UcV32c", "nc", "lasx") +TARGET_BUILTIN(__builtin_lasx_xvmulwev_h_bu_b, "V16sV32UcV32Sc", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvmulwev_w_hu_h, "V8SiV16UsV16s", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvmulwev_d_wu_w, "V4LLiV8UiV8Si", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvmulwev_q_du_d, "V4LLiV4ULLiV4LLi", "nc", "lasx") -TARGET_BUILTIN(__builtin_lasx_xvmulwod_h_bu_b, "V16sV32UcV32c", "nc", "lasx") +TARGET_BUILTIN(__builtin_lasx_xvmulwod_h_bu_b, "V16sV32UcV32Sc", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvmulwod_w_hu_h, "V8SiV16UsV16s", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvmulwod_d_wu_w, "V4LLiV8UiV8Si", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvmulwod_q_du_d, "V4LLiV4ULLiV4LLi", "nc", "lasx") @@ -259,12 +259,12 @@ TARGET_BUILTIN(__builtin_lasx_xvmsub_h, "V16SsV16SsV16SsV16Ss", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvmsub_w, "V8SiV8SiV8SiV8Si", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvmsub_d, "V4SLLiV4SLLiV4SLLiV4SLLi", "nc", "lasx") -TARGET_BUILTIN(__builtin_lasx_xvmaddwev_h_b, "V16sV16sV32cV32c", "nc", "lasx") +TARGET_BUILTIN(__builtin_lasx_xvmaddwev_h_b, "V16sV16sV32ScV32Sc", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvmaddwev_w_h, "V8SiV8SiV16sV16s", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvmaddwev_d_w, "V4LLiV4LLiV8SiV8Si", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvmaddwev_q_d, "V4LLiV4LLiV4LLiV4LLi", "nc", "lasx") -TARGET_BUILTIN(__builtin_lasx_xvmaddwod_h_b, "V16sV16sV32cV32c", "nc", "lasx") +TARGET_BUILTIN(__builtin_lasx_xvmaddwod_h_b, "V16sV16sV32ScV32Sc", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvmaddwod_w_h, "V8SiV8SiV16sV16s", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvmaddwod_d_w, "V4LLiV4LLiV8SiV8Si", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvmaddwod_q_d, "V4LLiV4LLiV4LLiV4LLi", "nc", "lasx") @@ -279,12 +279,12 @@ TARGET_BUILTIN(__builtin_lasx_xvmaddwod_w_hu, "V8UiV8UiV16UsV16Us", "nc", "lasx" TARGET_BUILTIN(__builtin_lasx_xvmaddwod_d_wu, "V4ULLiV4ULLiV8UiV8Ui", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvmaddwod_q_du, "V4ULLiV4ULLiV4ULLiV4ULLi", "nc", "lasx") -TARGET_BUILTIN(__builtin_lasx_xvmaddwev_h_bu_b, "V16sV16sV32UcV32c", "nc", "lasx") +TARGET_BUILTIN(__builtin_lasx_xvmaddwev_h_bu_b, "V16sV16sV32UcV32Sc", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvmaddwev_w_hu_h, "V8SiV8SiV16UsV16s", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvmaddwev_d_wu_w, "V4LLiV4LLiV8UiV8Si", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvmaddwev_q_du_d, "V4LLiV4LLiV4ULLiV4LLi", "nc", "lasx") -TARGET_BUILTIN(__builtin_lasx_xvmaddwod_h_bu_b, "V16sV16sV32UcV32c", "nc", "lasx") +TARGET_BUILTIN(__builtin_lasx_xvmaddwod_h_bu_b, "V16sV16sV32UcV32Sc", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvmaddwod_w_hu_h, "V8SiV8SiV16UsV16s", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvmaddwod_d_wu_w, "V4LLiV4LLiV8UiV8Si", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvmaddwod_q_du_d, "V4LLiV4LLiV4ULLiV4LLi", "nc", "lasx") @@ -320,7 +320,7 @@ TARGET_BUILTIN(__builtin_lasx_xvsat_hu, "V16UsV16UsIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvsat_wu, "V8UiV8UiIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvsat_du, "V4ULLiV4ULLiIUi", "nc", "lasx") -TARGET_BUILTIN(__builtin_lasx_xvexth_h_b, "V16sV32c", "nc", "lasx") +TARGET_BUILTIN(__builtin_lasx_xvexth_h_b, "V16sV32Sc", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvexth_w_h, "V8SiV16s", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvexth_d_w, "V4LLiV8Si", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvexth_q_d, "V4LLiV4LLi", "nc", "lasx") @@ -330,17 +330,17 @@ TARGET_BUILTIN(__builtin_lasx_xvexth_wu_hu, "V8UiV16Us", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvexth_du_wu, "V4ULLiV8Ui", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvexth_qu_du, "V4ULLiV4ULLi", "nc", "lasx") -TARGET_BUILTIN(__builtin_lasx_vext2xv_h_b, "V16sV32c", "nc", "lasx") -TARGET_BUILTIN(__builtin_lasx_vext2xv_w_b, "V8SiV32c", "nc", "lasx") -TARGET_BUILTIN(__builtin_lasx_vext2xv_d_b, "V4LLiV32c", "nc", "lasx") +TARGET_BUILTIN(__builtin_lasx_vext2xv_h_b, "V16sV32Sc", "nc", "lasx") +TARGET_BUILTIN(__builtin_lasx_vext2xv_w_b, "V8SiV32Sc", "nc", "lasx") +TARGET_BUILTIN(__builtin_lasx_vext2xv_d_b, "V4LLiV32Sc", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_vext2xv_w_h, "V8SiV16s", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_vext2xv_d_h, "V4LLiV16s", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_vext2xv_d_w, "V4LLiV8Si", "nc", "lasx") -TARGET_BUILTIN(__builtin_lasx_vext2xv_hu_bu, "V16sV32c", "nc", "lasx") -TARGET_BUILTIN(__builtin_lasx_vext2xv_wu_bu, "V8SiV32c", "nc", "lasx") -TARGET_BUILTIN(__builtin_lasx_vext2xv_du_bu, "V4LLiV32c", "nc", "lasx") +TARGET_BUILTIN(__builtin_lasx_vext2xv_hu_bu, "V16sV32Sc", "nc", "lasx") +TARGET_BUILTIN(__builtin_lasx_vext2xv_wu_bu, "V8SiV32Sc", "nc", "lasx") +TARGET_BUILTIN(__builtin_lasx_vext2xv_du_bu, "V4LLiV32Sc", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_vext2xv_wu_hu, "V8SiV16s", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_vext2xv_du_hu, "V4LLiV16s", "nc", "lasx") @@ -351,16 +351,16 @@ TARGET_BUILTIN(__builtin_lasx_xvsigncov_h, "V16SsV16SsV16Ss", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvsigncov_w, "V8SiV8SiV8Si", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvsigncov_d, "V4SLLiV4SLLiV4SLLi", "nc", "lasx") -TARGET_BUILTIN(__builtin_lasx_xvmskltz_b, "V32cV32c", "nc", "lasx") +TARGET_BUILTIN(__builtin_lasx_xvmskltz_b, "V32ScV32Sc", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvmskltz_h, "V16sV16s", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvmskltz_w, "V8iV8i", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvmskltz_d, "V4LLiV4LLi", "nc", "lasx") -TARGET_BUILTIN(__builtin_lasx_xvmskgez_b, "V32cV32c", "nc", "lasx") -TARGET_BUILTIN(__builtin_lasx_xvmsknz_b, "V16sV16s", "nc", "lasx") +TARGET_BUILTIN(__builtin_lasx_xvmskgez_b, "V32ScV32Sc", "nc", "lasx") +TARGET_BUILTIN(__builtin_lasx_xvmsknz_b, "V32ScV32Sc", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvldi, "V4LLiIi", "nc", "lasx") -TARGET_BUILTIN(__builtin_lasx_xvrepli_b, "V32cIi", "nc", "lasx") +TARGET_BUILTIN(__builtin_lasx_xvrepli_b, "V32ScIi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvrepli_h, "V16sIi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvrepli_w, "V8iIi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvrepli_d, "V4LLiIi", "nc", "lasx") @@ -368,7 +368,7 @@ TARGET_BUILTIN(__builtin_lasx_xvrepli_d, "V4LLiIi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvand_v, "V32UcV32UcV32Uc", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvor_v, "V32UcV32UcV32Uc", "nc", "lasx") -TARGET_BUILTIN(__builtin_lasx_xvxor_v, "V32cV32cV32c", "nc", "lasx") +TARGET_BUILTIN(__builtin_lasx_xvxor_v, "V32UcV32UcV32Uc", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvnor_v, "V32UcV32UcV32Uc", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvandn_v, "V32UcV32UcV32Uc", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvorn_v, "V32ScV32ScV32Sc", "nc", "lasx") @@ -378,47 +378,47 @@ TARGET_BUILTIN(__builtin_lasx_xvori_b, "V32UcV32UcIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvxori_b, "V32UcV32UcIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvnori_b, "V32UcV32UcIUi", "nc", "lasx") -TARGET_BUILTIN(__builtin_lasx_xvsll_b, "V32cV32cV32c", "nc", "lasx") +TARGET_BUILTIN(__builtin_lasx_xvsll_b, "V32ScV32ScV32Sc", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvsll_h, "V16sV16sV16s", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvsll_w, "V8iV8iV8i", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvsll_d, "V4LLiV4LLiV4LLi", "nc", "lasx") -TARGET_BUILTIN(__builtin_lasx_xvslli_b, "V32cV32cIUi", "nc", "lasx") +TARGET_BUILTIN(__builtin_lasx_xvslli_b, "V32ScV32ScIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvslli_h, "V16sV16sIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvslli_w, "V8iV8iIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvslli_d, "V4LLiV4LLiIUi", "nc", "lasx") -TARGET_BUILTIN(__builtin_lasx_xvsrl_b, "V32cV32cV32c", "nc", "lasx") +TARGET_BUILTIN(__builtin_lasx_xvsrl_b, "V32ScV32ScV32Sc", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvsrl_h, "V16sV16sV16s", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvsrl_w, "V8iV8iV8i", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvsrl_d, "V4LLiV4LLiV4LLi", "nc", "lasx") -TARGET_BUILTIN(__builtin_lasx_xvsrli_b, "V32cV32cIUi", "nc", "lasx") +TARGET_BUILTIN(__builtin_lasx_xvsrli_b, "V32ScV32ScIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvsrli_h, "V16sV16sIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvsrli_w, "V8iV8iIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvsrli_d, "V4LLiV4LLiIUi", "nc", "lasx") -TARGET_BUILTIN(__builtin_lasx_xvsra_b, "V32cV32cV32c", "nc", "lasx") +TARGET_BUILTIN(__builtin_lasx_xvsra_b, "V32ScV32ScV32Sc", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvsra_h, "V16sV16sV16s", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvsra_w, "V8iV8iV8i", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvsra_d, "V4LLiV4LLiV4LLi", "nc", "lasx") -TARGET_BUILTIN(__builtin_lasx_xvsrai_b, "V32cV32cIUi", "nc", "lasx") +TARGET_BUILTIN(__builtin_lasx_xvsrai_b, "V32ScV32ScIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvsrai_h, "V16sV16sIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvsrai_w, "V8iV8iIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvsrai_d, "V4LLiV4LLiIUi", "nc", "lasx") -TARGET_BUILTIN(__builtin_lasx_xvrotr_b, "V32cV32cV32c", "nc", "lasx") +TARGET_BUILTIN(__builtin_lasx_xvrotr_b, "V32ScV32ScV32Sc", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvrotr_h, "V16sV16sV16s", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvrotr_w, "V8iV8iV8i", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvrotr_d, "V4LLiV4LLiV4LLi", "nc", "lasx") -TARGET_BUILTIN(__builtin_lasx_xvrotri_b, "V32cV32cIUi", "nc", "lasx") +TARGET_BUILTIN(__builtin_lasx_xvrotri_b, "V32ScV32ScIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvrotri_h, "V16sV16sIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvrotri_w, "V8iV8iIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvrotri_d, "V4LLiV4LLiIUi", "nc", "lasx") -TARGET_BUILTIN(__builtin_lasx_xvsllwil_h_b, "V16sV32cIUi", "nc", "lasx") +TARGET_BUILTIN(__builtin_lasx_xvsllwil_h_b, "V16sV32ScIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvsllwil_w_h, "V8SiV16sIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvsllwil_d_w, "V4LLiV8SiIUi", "nc", "lasx") @@ -430,22 +430,22 @@ TARGET_BUILTIN(__builtin_lasx_xvsllwil_du_wu, "V4ULLiV8UiIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvextl_qu_du, "V4LLiV4ULLi", "nc", "lasx") -TARGET_BUILTIN(__builtin_lasx_xvsrlr_b, "V32cV32cV32c", "nc", "lasx") +TARGET_BUILTIN(__builtin_lasx_xvsrlr_b, "V32ScV32ScV32Sc", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvsrlr_h, "V16sV16sV16s", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvsrlr_w, "V8iV8iV8i", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvsrlr_d, "V4LLiV4LLiV4LLi", "nc", "lasx") -TARGET_BUILTIN(__builtin_lasx_xvsrlri_b, "V32cV32cIUi", "nc", "lasx") +TARGET_BUILTIN(__builtin_lasx_xvsrlri_b, "V32ScV32ScIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvsrlri_h, "V16sV16sIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvsrlri_w, "V8iV8iIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvsrlri_d, "V4LLiV4LLiIUi", "nc", "lasx") -TARGET_BUILTIN(__builtin_lasx_xvsrar_b, "V32cV32cV32c", "nc", "lasx") +TARGET_BUILTIN(__builtin_lasx_xvsrar_b, "V32ScV32ScV32Sc", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvsrar_h, "V16sV16sV16s", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvsrar_w, "V8iV8iV8i", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvsrar_d, "V4LLiV4LLiV4LLi", "nc", "lasx") -TARGET_BUILTIN(__builtin_lasx_xvsrari_b, "V32cV32cIUi", "nc", "lasx") +TARGET_BUILTIN(__builtin_lasx_xvsrari_b, "V32ScV32ScIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvsrari_h, "V16sV16sIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvsrari_w, "V8iV8iIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvsrari_d, "V4LLiV4LLiIUi", "nc", "lasx") @@ -458,12 +458,12 @@ TARGET_BUILTIN(__builtin_lasx_xvsran_b_h, "V32ScV16sV16s", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvsran_h_w, "V16sV8SiV8Si", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvsran_w_d, "V8SiV4LLiV4LLi", "nc", "lasx") -TARGET_BUILTIN(__builtin_lasx_xvsrlni_b_h, "V32cV32cV32cIUi", "nc", "lasx") +TARGET_BUILTIN(__builtin_lasx_xvsrlni_b_h, "V32ScV32ScV32ScIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvsrlni_h_w, "V16sV16sV16sIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvsrlni_w_d, "V8iV8iV8iIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvsrlni_d_q, "V4LLiV4LLiV4LLiIUi", "nc", "lasx") -TARGET_BUILTIN(__builtin_lasx_xvsrani_b_h, "V32cV32cV32cIUi", "nc", "lasx") +TARGET_BUILTIN(__builtin_lasx_xvsrani_b_h, "V32ScV32ScV32ScIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvsrani_h_w, "V16sV16sV16sIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvsrani_w_d, "V8iV8iV8iIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvsrani_d_q, "V4LLiV4LLiV4LLiIUi", "nc", "lasx") @@ -476,12 +476,12 @@ TARGET_BUILTIN(__builtin_lasx_xvsrarn_b_h, "V32ScV16sV16s", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvsrarn_h_w, "V16sV8SiV8Si", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvsrarn_w_d, "V8SiV4LLiV4LLi", "nc", "lasx") -TARGET_BUILTIN(__builtin_lasx_xvsrlrni_b_h, "V32cV32cV32cIUi", "nc", "lasx") +TARGET_BUILTIN(__builtin_lasx_xvsrlrni_b_h, "V32ScV32ScV32ScIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvsrlrni_h_w, "V16sV16sV16sIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvsrlrni_w_d, "V8iV8iV8iIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvsrlrni_d_q, "V4LLiV4LLiV4LLiIUi", "nc", "lasx") -TARGET_BUILTIN(__builtin_lasx_xvsrarni_b_h, "V32cV32cV32cIUi", "nc", "lasx") +TARGET_BUILTIN(__builtin_lasx_xvsrarni_b_h, "V32ScV32ScV32ScIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvsrarni_h_w, "V16sV16sV16sIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvsrarni_w_d, "V8iV8iV8iIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvsrarni_d_q, "V4LLiV4LLiV4LLiIUi", "nc", "lasx") @@ -502,22 +502,22 @@ TARGET_BUILTIN(__builtin_lasx_xvssran_bu_h, "V32UcV16UsV16Us", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvssran_hu_w, "V16UsV8UiV8Ui", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvssran_wu_d, "V8UiV4ULLiV4ULLi", "nc", "lasx") -TARGET_BUILTIN(__builtin_lasx_xvssrlni_b_h, "V32cV32cV32cIUi", "nc", "lasx") +TARGET_BUILTIN(__builtin_lasx_xvssrlni_b_h, "V32ScV32ScV32ScIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvssrlni_h_w, "V16sV16sV16sIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvssrlni_w_d, "V8iV8iV8iIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvssrlni_d_q, "V4LLiV4LLiV4LLiIUi", "nc", "lasx") -TARGET_BUILTIN(__builtin_lasx_xvssrani_b_h, "V32cV32cV32cIUi", "nc", "lasx") +TARGET_BUILTIN(__builtin_lasx_xvssrani_b_h, "V32ScV32ScV32ScIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvssrani_h_w, "V16sV16sV16sIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvssrani_w_d, "V8iV8iV8iIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvssrani_d_q, "V4LLiV4LLiV4LLiIUi", "nc", "lasx") -TARGET_BUILTIN(__builtin_lasx_xvssrlrni_bu_h, "V32cV32cV32cIUi", "nc", "lasx") +TARGET_BUILTIN(__builtin_lasx_xvssrlrni_bu_h, "V32ScV32ScV32ScIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvssrlrni_hu_w, "V16sV16sV16sIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvssrlrni_wu_d, "V8iV8iV8iIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvssrlrni_du_q, "V4LLiV4LLiV4LLiIUi", "nc", "lasx") -TARGET_BUILTIN(__builtin_lasx_xvssrani_bu_h, "V32cV32cV32cIUi", "nc", "lasx") +TARGET_BUILTIN(__builtin_lasx_xvssrani_bu_h, "V32ScV32ScV32ScIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvssrani_hu_w, "V16sV16sV16sIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvssrani_wu_d, "V8iV8iV8iIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvssrani_du_q, "V4LLiV4LLiV4LLiIUi", "nc", "lasx") @@ -538,22 +538,22 @@ TARGET_BUILTIN(__builtin_lasx_xvssrarn_bu_h, "V32UcV16UsV16Us", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvssrarn_hu_w, "V16UsV8UiV8Ui", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvssrarn_wu_d, "V8UiV4ULLiV4ULLi", "nc", "lasx") -TARGET_BUILTIN(__builtin_lasx_xvssrlrni_b_h, "V32cV32cV32cIUi", "nc", "lasx") +TARGET_BUILTIN(__builtin_lasx_xvssrlrni_b_h, "V32ScV32ScV32ScIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvssrlrni_h_w, "V16sV16sV16sIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvssrlrni_w_d, "V8iV8iV8iIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvssrlrni_d_q, "V4LLiV4LLiV4LLiIUi", "nc", "lasx") -TARGET_BUILTIN(__builtin_lasx_xvssrarni_b_h, "V32cV32cV32cIUi", "nc", "lasx") +TARGET_BUILTIN(__builtin_lasx_xvssrarni_b_h, "V32ScV32ScV32ScIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvssrarni_h_w, "V16sV16sV16sIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvssrarni_w_d, "V8iV8iV8iIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvssrarni_d_q, "V4LLiV4LLiV4LLiIUi", "nc", "lasx") -TARGET_BUILTIN(__builtin_lasx_xvssrlni_bu_h, "V32cV32cV32cIUi", "nc", "lasx") +TARGET_BUILTIN(__builtin_lasx_xvssrlni_bu_h, "V32ScV32ScV32ScIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvssrlni_hu_w, "V16sV16sV16sIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvssrlni_wu_d, "V8iV8iV8iIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvssrlni_du_q, "V4LLiV4LLiV4LLiIUi", "nc", "lasx") -TARGET_BUILTIN(__builtin_lasx_xvssrarni_bu_h, "V32cV32cV32cIUi", "nc", "lasx") +TARGET_BUILTIN(__builtin_lasx_xvssrarni_bu_h, "V32ScV32ScV32ScIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvssrarni_hu_w, "V16sV16sV16sIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvssrarni_wu_d, "V8iV8iV8iIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvssrarni_du_q, "V4LLiV4LLiV4LLiIUi", "nc", "lasx") @@ -606,7 +606,7 @@ TARGET_BUILTIN(__builtin_lasx_xvbitrevi_d, "V4ULLiV4ULLiIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvfrstp_b, "V32ScV32ScV32ScV32Sc", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvfrstp_h, "V16SsV16SsV16SsV16Ss", "nc", "lasx") -TARGET_BUILTIN(__builtin_lasx_xvfrstpi_b, "V32cV32cV32cIUi", "nc", "lasx") +TARGET_BUILTIN(__builtin_lasx_xvfrstpi_b, "V32ScV32ScV32ScIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvfrstpi_h, "V16sV16sV16sIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvfadd_s, "V8fV8fV8f", "nc", "lasx") @@ -877,12 +877,12 @@ TARGET_BUILTIN(__builtin_lasx_xvpickve2gr_d, "LLiV4SLLiIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvpickve2gr_wu, "iV8UiIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvpickve2gr_du, "LLiV4ULLiIUi", "nc", "lasx") -TARGET_BUILTIN(__builtin_lasx_xvreplve_b, "V32cV32cUi", "nc", "lasx") +TARGET_BUILTIN(__builtin_lasx_xvreplve_b, "V32ScV32ScUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvreplve_h, "V16sV16sUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvreplve_w, "V8iV8iUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvreplve_d, "V4LLiV4LLiUi", "nc", "lasx") -TARGET_BUILTIN(__builtin_lasx_xvrepl128vei_b, "V32cV32cIUi", "nc", "lasx") +TARGET_BUILTIN(__builtin_lasx_xvrepl128vei_b, "V32ScV32ScIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvrepl128vei_h, "V16sV16sIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvrepl128vei_w, "V8iV8iIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvrepl128vei_d, "V4LLiV4LLiIUi", "nc", "lasx") @@ -902,40 +902,40 @@ TARGET_BUILTIN(__builtin_lasx_xvpickve_d, "V4LLiV4LLiIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvpickve_w_f, "V8fV8fIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvpickve_d_f, "V4dV4dIUi", "nc", "lasx") -TARGET_BUILTIN(__builtin_lasx_xvbsll_v, "V32cV32cIUi", "nc", "lasx") -TARGET_BUILTIN(__builtin_lasx_xvbsrl_v, "V32cV32cIUi", "nc", "lasx") +TARGET_BUILTIN(__builtin_lasx_xvbsll_v, "V32ScV32ScIUi", "nc", "lasx") +TARGET_BUILTIN(__builtin_lasx_xvbsrl_v, "V32ScV32ScIUi", "nc", "lasx") -TARGET_BUILTIN(__builtin_lasx_xvpackev_b, "V32cV32cV32c", "nc", "lasx") +TARGET_BUILTIN(__builtin_lasx_xvpackev_b, "V32ScV32ScV32Sc", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvpackev_h, "V16sV16sV16s", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvpackev_w, "V8iV8iV8i", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvpackev_d, "V4LLiV4LLiV4LLi", "nc", "lasx") -TARGET_BUILTIN(__builtin_lasx_xvpackod_b, "V32cV32cV32c", "nc", "lasx") +TARGET_BUILTIN(__builtin_lasx_xvpackod_b, "V32ScV32ScV32Sc", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvpackod_h, "V16sV16sV16s", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvpackod_w, "V8iV8iV8i", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvpackod_d, "V4LLiV4LLiV4LLi", "nc", "lasx") -TARGET_BUILTIN(__builtin_lasx_xvpickev_b, "V32cV32cV32c", "nc", "lasx") +TARGET_BUILTIN(__builtin_lasx_xvpickev_b, "V32ScV32ScV32Sc", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvpickev_h, "V16sV16sV16s", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvpickev_w, "V8iV8iV8i", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvpickev_d, "V4LLiV4LLiV4LLi", "nc", "lasx") -TARGET_BUILTIN(__builtin_lasx_xvpickod_b, "V32cV32cV32c", "nc", "lasx") +TARGET_BUILTIN(__builtin_lasx_xvpickod_b, "V32ScV32ScV32Sc", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvpickod_h, "V16sV16sV16s", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvpickod_w, "V8iV8iV8i", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvpickod_d, "V4LLiV4LLiV4LLi", "nc", "lasx") -TARGET_BUILTIN(__builtin_lasx_xvilvl_b, "V32cV32cV32c", "nc", "lasx") +TARGET_BUILTIN(__builtin_lasx_xvilvl_b, "V32ScV32ScV32Sc", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvilvl_h, "V16sV16sV16s", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvilvl_w, "V8iV8iV8i", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvilvl_d, "V4LLiV4LLiV4LLi", "nc", "lasx") -TARGET_BUILTIN(__builtin_lasx_xvilvh_b, "V32cV32cV32c", "nc", "lasx") +TARGET_BUILTIN(__builtin_lasx_xvilvh_b, "V32ScV32ScV32Sc", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvilvh_h, "V16sV16sV16s", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvilvh_w, "V8iV8iV8i", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvilvh_d, "V4LLiV4LLiV4LLi", "nc", "lasx") -TARGET_BUILTIN(__builtin_lasx_xvshuf_b, "V32UcV32UcV32UcV32Uc", "nc", "lasx") +TARGET_BUILTIN(__builtin_lasx_xvshuf_b, "V32ScV32ScV32ScV32Sc", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvshuf_h, "V16sV16sV16sV16s", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvshuf_w, "V8iV8iV8iV8i", "nc", "lasx") @@ -943,16 +943,16 @@ TARGET_BUILTIN(__builtin_lasx_xvshuf_d, "V4LLiV4LLiV4LLiV4LLi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvperm_w, "V8iV8iV8i", "nc", "lasx") -TARGET_BUILTIN(__builtin_lasx_xvshuf4i_b, "V32cV32cIUi", "nc", "lasx") +TARGET_BUILTIN(__builtin_lasx_xvshuf4i_b, "V32ScV32ScIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvshuf4i_h, "V16sV16sIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvshuf4i_w, "V8iV8iIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvshuf4i_d, "V4LLiV4LLiV4LLiIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvpermi_w, "V8iV8iV8iIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvpermi_d, "V4LLiV4LLiIUi", "nc", "lasx") -TARGET_BUILTIN(__builtin_lasx_xvpermi_q, "V32cV32cV32cIUi", "nc", "lasx") +TARGET_BUILTIN(__builtin_lasx_xvpermi_q, "V32ScV32ScV32ScIUi", "nc", "lasx") -TARGET_BUILTIN(__builtin_lasx_xvextrins_b, "V32cV32cV32cIUi", "nc", "lasx") +TARGET_BUILTIN(__builtin_lasx_xvextrins_b, "V32ScV32ScV32ScIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvextrins_h, "V16sV16sV16sIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvextrins_w, "V8iV8iV8iIUi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvextrins_d, "V4LLiV4LLiV4LLiIUi", "nc", "lasx") @@ -963,7 +963,7 @@ TARGET_BUILTIN(__builtin_lasx_xvst, "vV32Scv*Ii", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvldx, "V32ScvC*LLi", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvstx, "vV32Scv*LLi", "nc", "lasx") -TARGET_BUILTIN(__builtin_lasx_xvldrepl_b, "V32cvC*Ii", "nc", "lasx") +TARGET_BUILTIN(__builtin_lasx_xvldrepl_b, "V32ScvC*Ii", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvldrepl_h, "V16svC*Ii", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvldrepl_w, "V8ivC*Ii", "nc", "lasx") TARGET_BUILTIN(__builtin_lasx_xvldrepl_d, "V4LLivC*Ii", "nc", "lasx") diff --git a/clang/include/clang/Basic/BuiltinsLoongArchLSX.def b/clang/include/clang/Basic/BuiltinsLoongArchLSX.def index c90f4dc5458fa..b3056971986d1 100644 --- a/clang/include/clang/Basic/BuiltinsLoongArchLSX.def +++ b/clang/include/clang/Basic/BuiltinsLoongArchLSX.def @@ -12,29 +12,29 @@ // //===----------------------------------------------------------------------===// -TARGET_BUILTIN(__builtin_lsx_vadd_b, "V16cV16cV16c", "nc", "lsx") +TARGET_BUILTIN(__builtin_lsx_vadd_b, "V16ScV16ScV16Sc", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vadd_h, "V8sV8sV8s", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vadd_w, "V4iV4iV4i", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vadd_d, "V2LLiV2LLiV2LLi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vadd_q, "V2LLiV2LLiV2LLi", "nc", "lsx") -TARGET_BUILTIN(__builtin_lsx_vsub_b, "V16cV16cV16c", "nc", "lsx") +TARGET_BUILTIN(__builtin_lsx_vsub_b, "V16ScV16ScV16Sc", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vsub_h, "V8sV8sV8s", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vsub_w, "V4iV4iV4i", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vsub_d, "V2LLiV2LLiV2LLi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vsub_q, "V2LLiV2LLiV2LLi", "nc", "lsx") -TARGET_BUILTIN(__builtin_lsx_vaddi_bu, "V16cV16cIUi", "nc", "lsx") +TARGET_BUILTIN(__builtin_lsx_vaddi_bu, "V16ScV16ScIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vaddi_hu, "V8sV8sIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vaddi_wu, "V4iV4iIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vaddi_du, "V2LLiV2LLiIUi", "nc", "lsx") -TARGET_BUILTIN(__builtin_lsx_vsubi_bu, "V16cV16cIUi", "nc", "lsx") +TARGET_BUILTIN(__builtin_lsx_vsubi_bu, "V16ScV16ScIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vsubi_hu, "V8sV8sIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vsubi_wu, "V4iV4iIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vsubi_du, "V2LLiV2LLiIUi", "nc", "lsx") -TARGET_BUILTIN(__builtin_lsx_vneg_b, "V16cV16c", "nc", "lsx") +TARGET_BUILTIN(__builtin_lsx_vneg_b, "V16ScV16Sc", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vneg_h, "V8sV8s", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vneg_w, "V4iV4i", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vneg_d, "V2LLiV2LLi", "nc", "lsx") @@ -79,22 +79,22 @@ TARGET_BUILTIN(__builtin_lsx_vhsubw_wu_hu, "V4UiV8UsV8Us", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vhsubw_du_wu, "V2ULLiV4UiV4Ui", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vhsubw_qu_du, "V2ULLiV2ULLiV2ULLi", "nc", "lsx") -TARGET_BUILTIN(__builtin_lsx_vaddwev_h_b, "V8sV16cV16c", "nc", "lsx") +TARGET_BUILTIN(__builtin_lsx_vaddwev_h_b, "V8sV16ScV16Sc", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vaddwev_w_h, "V4SiV8sV8s", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vaddwev_d_w, "V2LLiV4SiV4Si", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vaddwev_q_d, "V2LLiV2LLiV2LLi", "nc", "lsx") -TARGET_BUILTIN(__builtin_lsx_vaddwod_h_b, "V8sV16cV16c", "nc", "lsx") +TARGET_BUILTIN(__builtin_lsx_vaddwod_h_b, "V8sV16ScV16Sc", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vaddwod_w_h, "V4SiV8sV8s", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vaddwod_d_w, "V2LLiV4SiV4Si", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vaddwod_q_d, "V2LLiV2LLiV2LLi", "nc", "lsx") -TARGET_BUILTIN(__builtin_lsx_vsubwev_h_b, "V8sV16cV16c", "nc", "lsx") +TARGET_BUILTIN(__builtin_lsx_vsubwev_h_b, "V8sV16ScV16Sc", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vsubwev_w_h, "V4SiV8sV8s", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vsubwev_d_w, "V2LLiV4SiV4Si", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vsubwev_q_d, "V2LLiV2LLiV2LLi", "nc", "lsx") -TARGET_BUILTIN(__builtin_lsx_vsubwod_h_b, "V8sV16cV16c", "nc", "lsx") +TARGET_BUILTIN(__builtin_lsx_vsubwod_h_b, "V8sV16ScV16Sc", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vsubwod_w_h, "V4SiV8sV8s", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vsubwod_d_w, "V2LLiV4SiV4Si", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vsubwod_q_d, "V2LLiV2LLiV2LLi", "nc", "lsx") @@ -119,12 +119,12 @@ TARGET_BUILTIN(__builtin_lsx_vsubwod_w_hu, "V4SiV8UsV8Us", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vsubwod_d_wu, "V2LLiV4UiV4Ui", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vsubwod_q_du, "V2LLiV2ULLiV2ULLi", "nc", "lsx") -TARGET_BUILTIN(__builtin_lsx_vaddwev_h_bu_b, "V8sV16UcV16c", "nc", "lsx") +TARGET_BUILTIN(__builtin_lsx_vaddwev_h_bu_b, "V8sV16UcV16Sc", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vaddwev_w_hu_h, "V4SiV8UsV8s", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vaddwev_d_wu_w, "V2LLiV4UiV4Si", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vaddwev_q_du_d, "V2LLiV2ULLiV2LLi", "nc", "lsx") -TARGET_BUILTIN(__builtin_lsx_vaddwod_h_bu_b, "V8sV16UcV16c", "nc", "lsx") +TARGET_BUILTIN(__builtin_lsx_vaddwod_h_bu_b, "V8sV16UcV16Sc", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vaddwod_w_hu_h, "V4SiV8UsV8s", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vaddwod_d_wu_w, "V2LLiV4UiV4Si", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vaddwod_q_du_d, "V2LLiV2ULLiV2LLi", "nc", "lsx") @@ -209,7 +209,7 @@ TARGET_BUILTIN(__builtin_lsx_vmul_h, "V8SsV8SsV8Ss", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vmul_w, "V4SiV4SiV4Si", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vmul_d, "V2SLLiV2SLLiV2SLLi", "nc", "lsx") -TARGET_BUILTIN(__builtin_lsx_vmuh_b, "V16cV16cV16c", "nc", "lsx") +TARGET_BUILTIN(__builtin_lsx_vmuh_b, "V16ScV16ScV16Sc", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vmuh_h, "V8sV8sV8s", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vmuh_w, "V4iV4iV4i", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vmuh_d, "V2LLiV2LLiV2LLi", "nc", "lsx") @@ -219,12 +219,12 @@ TARGET_BUILTIN(__builtin_lsx_vmuh_hu, "V8UsV8UsV8Us", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vmuh_wu, "V4UiV4UiV4Ui", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vmuh_du, "V2ULLiV2ULLiV2ULLi", "nc", "lsx") -TARGET_BUILTIN(__builtin_lsx_vmulwev_h_b, "V8sV16cV16c", "nc", "lsx") +TARGET_BUILTIN(__builtin_lsx_vmulwev_h_b, "V8sV16ScV16Sc", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vmulwev_w_h, "V4SiV8sV8s", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vmulwev_d_w, "V2LLiV4SiV4Si", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vmulwev_q_d, "V2LLiV2LLiV2LLi", "nc", "lsx") -TARGET_BUILTIN(__builtin_lsx_vmulwod_h_b, "V8sV16cV16c", "nc", "lsx") +TARGET_BUILTIN(__builtin_lsx_vmulwod_h_b, "V8sV16ScV16Sc", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vmulwod_w_h, "V4SiV8sV8s", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vmulwod_d_w, "V2LLiV4SiV4Si", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vmulwod_q_d, "V2LLiV2LLiV2LLi", "nc", "lsx") @@ -239,12 +239,12 @@ TARGET_BUILTIN(__builtin_lsx_vmulwod_w_hu, "V4SiV8UsV8Us", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vmulwod_d_wu, "V2LLiV4UiV4Ui", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vmulwod_q_du, "V2LLiV2ULLiV2ULLi", "nc", "lsx") -TARGET_BUILTIN(__builtin_lsx_vmulwev_h_bu_b, "V8sV16UcV16c", "nc", "lsx") +TARGET_BUILTIN(__builtin_lsx_vmulwev_h_bu_b, "V8sV16UcV16Sc", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vmulwev_w_hu_h, "V4SiV8UsV8s", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vmulwev_d_wu_w, "V2LLiV4UiV4Si", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vmulwev_q_du_d, "V2LLiV2ULLiV2LLi", "nc", "lsx") -TARGET_BUILTIN(__builtin_lsx_vmulwod_h_bu_b, "V8sV16UcV16c", "nc", "lsx") +TARGET_BUILTIN(__builtin_lsx_vmulwod_h_bu_b, "V8sV16UcV16Sc", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vmulwod_w_hu_h, "V4SiV8UsV8s", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vmulwod_d_wu_w, "V2LLiV4UiV4Si", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vmulwod_q_du_d, "V2LLiV2ULLiV2LLi", "nc", "lsx") @@ -259,12 +259,12 @@ TARGET_BUILTIN(__builtin_lsx_vmsub_h, "V8SsV8SsV8SsV8Ss", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vmsub_w, "V4SiV4SiV4SiV4Si", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vmsub_d, "V2SLLiV2SLLiV2SLLiV2SLLi", "nc", "lsx") -TARGET_BUILTIN(__builtin_lsx_vmaddwev_h_b, "V8sV8sV16cV16c", "nc", "lsx") +TARGET_BUILTIN(__builtin_lsx_vmaddwev_h_b, "V8sV8sV16ScV16Sc", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vmaddwev_w_h, "V4SiV4SiV8sV8s", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vmaddwev_d_w, "V2LLiV2LLiV4SiV4Si", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vmaddwev_q_d, "V2LLiV2LLiV2LLiV2LLi", "nc", "lsx") -TARGET_BUILTIN(__builtin_lsx_vmaddwod_h_b, "V8sV8sV16cV16c", "nc", "lsx") +TARGET_BUILTIN(__builtin_lsx_vmaddwod_h_b, "V8sV8sV16ScV16Sc", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vmaddwod_w_h, "V4SiV4SiV8sV8s", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vmaddwod_d_w, "V2LLiV2LLiV4SiV4Si", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vmaddwod_q_d, "V2LLiV2LLiV2LLiV2LLi", "nc", "lsx") @@ -279,12 +279,12 @@ TARGET_BUILTIN(__builtin_lsx_vmaddwod_w_hu, "V4UiV4UiV8UsV8Us", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vmaddwod_d_wu, "V2ULLiV2ULLiV4UiV4Ui", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vmaddwod_q_du, "V2ULLiV2ULLiV2ULLiV2ULLi", "nc", "lsx") -TARGET_BUILTIN(__builtin_lsx_vmaddwev_h_bu_b, "V8sV8sV16UcV16c", "nc", "lsx") +TARGET_BUILTIN(__builtin_lsx_vmaddwev_h_bu_b, "V8sV8sV16UcV16Sc", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vmaddwev_w_hu_h, "V4SiV4SiV8UsV8s", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vmaddwev_d_wu_w, "V2LLiV2LLiV4UiV4Si", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vmaddwev_q_du_d, "V2LLiV2LLiV2ULLiV2LLi", "nc", "lsx") -TARGET_BUILTIN(__builtin_lsx_vmaddwod_h_bu_b, "V8sV8sV16UcV16c", "nc", "lsx") +TARGET_BUILTIN(__builtin_lsx_vmaddwod_h_bu_b, "V8sV8sV16UcV16Sc", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vmaddwod_w_hu_h, "V4SiV4SiV8UsV8s", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vmaddwod_d_wu_w, "V2LLiV2LLiV4UiV4Si", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vmaddwod_q_du_d, "V2LLiV2LLiV2ULLiV2LLi", "nc", "lsx") @@ -320,7 +320,7 @@ TARGET_BUILTIN(__builtin_lsx_vsat_hu, "V8UsV8UsIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vsat_wu, "V4UiV4UiIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vsat_du, "V2ULLiV2ULLiIUi", "nc", "lsx") -TARGET_BUILTIN(__builtin_lsx_vexth_h_b, "V8sV16c", "nc", "lsx") +TARGET_BUILTIN(__builtin_lsx_vexth_h_b, "V8sV16Sc", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vexth_w_h, "V4SiV8s", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vexth_d_w, "V2LLiV4Si", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vexth_q_d, "V2LLiV2LLi", "nc", "lsx") @@ -335,16 +335,16 @@ TARGET_BUILTIN(__builtin_lsx_vsigncov_h, "V8SsV8SsV8Ss", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vsigncov_w, "V4SiV4SiV4Si", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vsigncov_d, "V2SLLiV2SLLiV2SLLi", "nc", "lsx") -TARGET_BUILTIN(__builtin_lsx_vmskltz_b, "V16cV16c", "nc", "lsx") +TARGET_BUILTIN(__builtin_lsx_vmskltz_b, "V16ScV16Sc", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vmskltz_h, "V8sV8s", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vmskltz_w, "V4iV4i", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vmskltz_d, "V2LLiV2LLi", "nc", "lsx") -TARGET_BUILTIN(__builtin_lsx_vmskgez_b, "V16cV16c", "nc", "lsx") -TARGET_BUILTIN(__builtin_lsx_vmsknz_b, "V8sV8s", "nc", "lsx") +TARGET_BUILTIN(__builtin_lsx_vmskgez_b, "V16ScV16Sc", "nc", "lsx") +TARGET_BUILTIN(__builtin_lsx_vmsknz_b, "V16ScV16Sc", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vldi, "V2LLiIi", "nc", "lsx") -TARGET_BUILTIN(__builtin_lsx_vrepli_b, "V16cIi", "nc", "lsx") +TARGET_BUILTIN(__builtin_lsx_vrepli_b, "V16ScIi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vrepli_h, "V8sIi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vrepli_w, "V4iIi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vrepli_d, "V2LLiIi", "nc", "lsx") @@ -352,7 +352,7 @@ TARGET_BUILTIN(__builtin_lsx_vrepli_d, "V2LLiIi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vand_v, "V16UcV16UcV16Uc", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vor_v, "V16UcV16UcV16Uc", "nc", "lsx") -TARGET_BUILTIN(__builtin_lsx_vxor_v, "V16cV16cV16c", "nc", "lsx") +TARGET_BUILTIN(__builtin_lsx_vxor_v, "V16UcV16UcV16Uc", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vnor_v, "V16UcV16UcV16Uc", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vandn_v, "V16UcV16UcV16Uc", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vorn_v, "V16ScV16ScV16Sc", "nc", "lsx") @@ -362,47 +362,47 @@ TARGET_BUILTIN(__builtin_lsx_vori_b, "V16UcV16UcIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vxori_b, "V16UcV16UcIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vnori_b, "V16UcV16UcIUi", "nc", "lsx") -TARGET_BUILTIN(__builtin_lsx_vsll_b, "V16cV16cV16c", "nc", "lsx") +TARGET_BUILTIN(__builtin_lsx_vsll_b, "V16ScV16ScV16Sc", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vsll_h, "V8sV8sV8s", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vsll_w, "V4iV4iV4i", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vsll_d, "V2LLiV2LLiV2LLi", "nc", "lsx") -TARGET_BUILTIN(__builtin_lsx_vslli_b, "V16cV16cIUi", "nc", "lsx") +TARGET_BUILTIN(__builtin_lsx_vslli_b, "V16ScV16ScIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vslli_h, "V8sV8sIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vslli_w, "V4iV4iIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vslli_d, "V2LLiV2LLiIUi", "nc", "lsx") -TARGET_BUILTIN(__builtin_lsx_vsrl_b, "V16cV16cV16c", "nc", "lsx") +TARGET_BUILTIN(__builtin_lsx_vsrl_b, "V16ScV16ScV16Sc", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vsrl_h, "V8sV8sV8s", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vsrl_w, "V4iV4iV4i", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vsrl_d, "V2LLiV2LLiV2LLi", "nc", "lsx") -TARGET_BUILTIN(__builtin_lsx_vsrli_b, "V16cV16cIUi", "nc", "lsx") +TARGET_BUILTIN(__builtin_lsx_vsrli_b, "V16ScV16ScIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vsrli_h, "V8sV8sIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vsrli_w, "V4iV4iIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vsrli_d, "V2LLiV2LLiIUi", "nc", "lsx") -TARGET_BUILTIN(__builtin_lsx_vsra_b, "V16cV16cV16c", "nc", "lsx") +TARGET_BUILTIN(__builtin_lsx_vsra_b, "V16ScV16ScV16Sc", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vsra_h, "V8sV8sV8s", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vsra_w, "V4iV4iV4i", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vsra_d, "V2LLiV2LLiV2LLi", "nc", "lsx") -TARGET_BUILTIN(__builtin_lsx_vsrai_b, "V16cV16cIUi", "nc", "lsx") +TARGET_BUILTIN(__builtin_lsx_vsrai_b, "V16ScV16ScIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vsrai_h, "V8sV8sIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vsrai_w, "V4iV4iIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vsrai_d, "V2LLiV2LLiIUi", "nc", "lsx") -TARGET_BUILTIN(__builtin_lsx_vrotr_b, "V16cV16cV16c", "nc", "lsx") +TARGET_BUILTIN(__builtin_lsx_vrotr_b, "V16ScV16ScV16Sc", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vrotr_h, "V8sV8sV8s", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vrotr_w, "V4iV4iV4i", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vrotr_d, "V2LLiV2LLiV2LLi", "nc", "lsx") -TARGET_BUILTIN(__builtin_lsx_vrotri_b, "V16cV16cIUi", "nc", "lsx") +TARGET_BUILTIN(__builtin_lsx_vrotri_b, "V16ScV16ScIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vrotri_h, "V8sV8sIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vrotri_w, "V4iV4iIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vrotri_d, "V2LLiV2LLiIUi", "nc", "lsx") -TARGET_BUILTIN(__builtin_lsx_vsllwil_h_b, "V8sV16cIUi", "nc", "lsx") +TARGET_BUILTIN(__builtin_lsx_vsllwil_h_b, "V8sV16ScIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vsllwil_w_h, "V4SiV8sIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vsllwil_d_w, "V2LLiV4SiIUi", "nc", "lsx") @@ -414,22 +414,22 @@ TARGET_BUILTIN(__builtin_lsx_vsllwil_du_wu, "V2ULLiV4UiIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vextl_qu_du, "V2LLiV2ULLi", "nc", "lsx") -TARGET_BUILTIN(__builtin_lsx_vsrlr_b, "V16cV16cV16c", "nc", "lsx") +TARGET_BUILTIN(__builtin_lsx_vsrlr_b, "V16ScV16ScV16Sc", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vsrlr_h, "V8sV8sV8s", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vsrlr_w, "V4iV4iV4i", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vsrlr_d, "V2LLiV2LLiV2LLi", "nc", "lsx") -TARGET_BUILTIN(__builtin_lsx_vsrlri_b, "V16cV16cIUi", "nc", "lsx") +TARGET_BUILTIN(__builtin_lsx_vsrlri_b, "V16ScV16ScIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vsrlri_h, "V8sV8sIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vsrlri_w, "V4iV4iIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vsrlri_d, "V2LLiV2LLiIUi", "nc", "lsx") -TARGET_BUILTIN(__builtin_lsx_vsrar_b, "V16cV16cV16c", "nc", "lsx") +TARGET_BUILTIN(__builtin_lsx_vsrar_b, "V16ScV16ScV16Sc", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vsrar_h, "V8sV8sV8s", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vsrar_w, "V4iV4iV4i", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vsrar_d, "V2LLiV2LLiV2LLi", "nc", "lsx") -TARGET_BUILTIN(__builtin_lsx_vsrari_b, "V16cV16cIUi", "nc", "lsx") +TARGET_BUILTIN(__builtin_lsx_vsrari_b, "V16ScV16ScIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vsrari_h, "V8sV8sIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vsrari_w, "V4iV4iIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vsrari_d, "V2LLiV2LLiIUi", "nc", "lsx") @@ -442,12 +442,12 @@ TARGET_BUILTIN(__builtin_lsx_vsran_b_h, "V16ScV8sV8s", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vsran_h_w, "V8sV4SiV4Si", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vsran_w_d, "V4SiV2LLiV2LLi", "nc", "lsx") -TARGET_BUILTIN(__builtin_lsx_vsrlni_b_h, "V16cV16cV16cIUi", "nc", "lsx") +TARGET_BUILTIN(__builtin_lsx_vsrlni_b_h, "V16ScV16ScV16ScIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vsrlni_h_w, "V8sV8sV8sIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vsrlni_w_d, "V4iV4iV4iIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vsrlni_d_q, "V2LLiV2LLiV2LLiIUi", "nc", "lsx") -TARGET_BUILTIN(__builtin_lsx_vsrani_b_h, "V16cV16cV16cIUi", "nc", "lsx") +TARGET_BUILTIN(__builtin_lsx_vsrani_b_h, "V16ScV16ScV16ScIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vsrani_h_w, "V8sV8sV8sIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vsrani_w_d, "V4iV4iV4iIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vsrani_d_q, "V2LLiV2LLiV2LLiIUi", "nc", "lsx") @@ -460,12 +460,12 @@ TARGET_BUILTIN(__builtin_lsx_vsrarn_b_h, "V16ScV8sV8s", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vsrarn_h_w, "V8sV4SiV4Si", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vsrarn_w_d, "V4SiV2LLiV2LLi", "nc", "lsx") -TARGET_BUILTIN(__builtin_lsx_vsrlrni_b_h, "V16cV16cV16cIUi", "nc", "lsx") +TARGET_BUILTIN(__builtin_lsx_vsrlrni_b_h, "V16ScV16ScV16ScIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vsrlrni_h_w, "V8sV8sV8sIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vsrlrni_w_d, "V4iV4iV4iIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vsrlrni_d_q, "V2LLiV2LLiV2LLiIUi", "nc", "lsx") -TARGET_BUILTIN(__builtin_lsx_vsrarni_b_h, "V16cV16cV16cIUi", "nc", "lsx") +TARGET_BUILTIN(__builtin_lsx_vsrarni_b_h, "V16ScV16ScV16ScIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vsrarni_h_w, "V8sV8sV8sIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vsrarni_w_d, "V4iV4iV4iIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vsrarni_d_q, "V2LLiV2LLiV2LLiIUi", "nc", "lsx") @@ -486,22 +486,22 @@ TARGET_BUILTIN(__builtin_lsx_vssran_bu_h, "V16UcV8UsV8Us", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vssran_hu_w, "V8UsV4UiV4Ui", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vssran_wu_d, "V4UiV2ULLiV2ULLi", "nc", "lsx") -TARGET_BUILTIN(__builtin_lsx_vssrlni_b_h, "V16cV16cV16cIUi", "nc", "lsx") +TARGET_BUILTIN(__builtin_lsx_vssrlni_b_h, "V16ScV16ScV16ScIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vssrlni_h_w, "V8sV8sV8sIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vssrlni_w_d, "V4iV4iV4iIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vssrlni_d_q, "V2LLiV2LLiV2LLiIUi", "nc", "lsx") -TARGET_BUILTIN(__builtin_lsx_vssrani_b_h, "V16cV16cV16cIUi", "nc", "lsx") +TARGET_BUILTIN(__builtin_lsx_vssrani_b_h, "V16ScV16ScV16ScIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vssrani_h_w, "V8sV8sV8sIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vssrani_w_d, "V4iV4iV4iIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vssrani_d_q, "V2LLiV2LLiV2LLiIUi", "nc", "lsx") -TARGET_BUILTIN(__builtin_lsx_vssrlrni_bu_h, "V16cV16cV16cIUi", "nc", "lsx") +TARGET_BUILTIN(__builtin_lsx_vssrlrni_bu_h, "V16ScV16ScV16ScIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vssrlrni_hu_w, "V8sV8sV8sIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vssrlrni_wu_d, "V4iV4iV4iIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vssrlrni_du_q, "V2LLiV2LLiV2LLiIUi", "nc", "lsx") -TARGET_BUILTIN(__builtin_lsx_vssrani_bu_h, "V16cV16cV16cIUi", "nc", "lsx") +TARGET_BUILTIN(__builtin_lsx_vssrani_bu_h, "V16ScV16ScV16ScIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vssrani_hu_w, "V8sV8sV8sIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vssrani_wu_d, "V4iV4iV4iIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vssrani_du_q, "V2LLiV2LLiV2LLiIUi", "nc", "lsx") @@ -522,22 +522,22 @@ TARGET_BUILTIN(__builtin_lsx_vssrarn_bu_h, "V16UcV8UsV8Us", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vssrarn_hu_w, "V8UsV4UiV4Ui", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vssrarn_wu_d, "V4UiV2ULLiV2ULLi", "nc", "lsx") -TARGET_BUILTIN(__builtin_lsx_vssrlrni_b_h, "V16cV16cV16cIUi", "nc", "lsx") +TARGET_BUILTIN(__builtin_lsx_vssrlrni_b_h, "V16ScV16ScV16ScIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vssrlrni_h_w, "V8sV8sV8sIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vssrlrni_w_d, "V4iV4iV4iIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vssrlrni_d_q, "V2LLiV2LLiV2LLiIUi", "nc", "lsx") -TARGET_BUILTIN(__builtin_lsx_vssrarni_b_h, "V16cV16cV16cIUi", "nc", "lsx") +TARGET_BUILTIN(__builtin_lsx_vssrarni_b_h, "V16ScV16ScV16ScIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vssrarni_h_w, "V8sV8sV8sIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vssrarni_w_d, "V4iV4iV4iIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vssrarni_d_q, "V2LLiV2LLiV2LLiIUi", "nc", "lsx") -TARGET_BUILTIN(__builtin_lsx_vssrlni_bu_h, "V16cV16cV16cIUi", "nc", "lsx") +TARGET_BUILTIN(__builtin_lsx_vssrlni_bu_h, "V16ScV16ScV16ScIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vssrlni_hu_w, "V8sV8sV8sIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vssrlni_wu_d, "V4iV4iV4iIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vssrlni_du_q, "V2LLiV2LLiV2LLiIUi", "nc", "lsx") -TARGET_BUILTIN(__builtin_lsx_vssrarni_bu_h, "V16cV16cV16cIUi", "nc", "lsx") +TARGET_BUILTIN(__builtin_lsx_vssrarni_bu_h, "V16ScV16ScV16ScIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vssrarni_hu_w, "V8sV8sV8sIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vssrarni_wu_d, "V4iV4iV4iIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vssrarni_du_q, "V2LLiV2LLiV2LLiIUi", "nc", "lsx") @@ -590,7 +590,7 @@ TARGET_BUILTIN(__builtin_lsx_vbitrevi_d, "V2ULLiV2ULLiIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vfrstp_b, "V16ScV16ScV16ScV16Sc", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vfrstp_h, "V8SsV8SsV8SsV8Ss", "nc", "lsx") -TARGET_BUILTIN(__builtin_lsx_vfrstpi_b, "V16cV16cV16cIUi", "nc", "lsx") +TARGET_BUILTIN(__builtin_lsx_vfrstpi_b, "V16ScV16ScV16ScIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vfrstpi_h, "V8sV8sV8sIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vfadd_s, "V4fV4fV4f", "nc", "lsx") @@ -867,63 +867,63 @@ TARGET_BUILTIN(__builtin_lsx_vpickve2gr_hu, "iV8UsIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vpickve2gr_wu, "iV4UiIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vpickve2gr_du, "LLiV2ULLiIUi", "nc", "lsx") -TARGET_BUILTIN(__builtin_lsx_vreplve_b, "V16cV16cUi", "nc", "lsx") +TARGET_BUILTIN(__builtin_lsx_vreplve_b, "V16ScV16ScUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vreplve_h, "V8sV8sUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vreplve_w, "V4iV4iUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vreplve_d, "V2LLiV2LLiUi", "nc", "lsx") -TARGET_BUILTIN(__builtin_lsx_vreplvei_b, "V16cV16cIUi", "nc", "lsx") +TARGET_BUILTIN(__builtin_lsx_vreplvei_b, "V16ScV16ScIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vreplvei_h, "V8sV8sIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vreplvei_w, "V4iV4iIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vreplvei_d, "V2LLiV2LLiIUi", "nc", "lsx") -TARGET_BUILTIN(__builtin_lsx_vbsll_v, "V16cV16cIUi", "nc", "lsx") -TARGET_BUILTIN(__builtin_lsx_vbsrl_v, "V16cV16cIUi", "nc", "lsx") +TARGET_BUILTIN(__builtin_lsx_vbsll_v, "V16ScV16ScIUi", "nc", "lsx") +TARGET_BUILTIN(__builtin_lsx_vbsrl_v, "V16ScV16ScIUi", "nc", "lsx") -TARGET_BUILTIN(__builtin_lsx_vpackev_b, "V16cV16cV16c", "nc", "lsx") +TARGET_BUILTIN(__builtin_lsx_vpackev_b, "V16ScV16ScV16Sc", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vpackev_h, "V8sV8sV8s", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vpackev_w, "V4iV4iV4i", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vpackev_d, "V2LLiV2LLiV2LLi", "nc", "lsx") -TARGET_BUILTIN(__builtin_lsx_vpackod_b, "V16cV16cV16c", "nc", "lsx") +TARGET_BUILTIN(__builtin_lsx_vpackod_b, "V16ScV16ScV16Sc", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vpackod_h, "V8sV8sV8s", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vpackod_w, "V4iV4iV4i", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vpackod_d, "V2LLiV2LLiV2LLi", "nc", "lsx") -TARGET_BUILTIN(__builtin_lsx_vpickev_b, "V16cV16cV16c", "nc", "lsx") +TARGET_BUILTIN(__builtin_lsx_vpickev_b, "V16ScV16ScV16Sc", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vpickev_h, "V8sV8sV8s", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vpickev_w, "V4iV4iV4i", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vpickev_d, "V2LLiV2LLiV2LLi", "nc", "lsx") -TARGET_BUILTIN(__builtin_lsx_vpickod_b, "V16cV16cV16c", "nc", "lsx") +TARGET_BUILTIN(__builtin_lsx_vpickod_b, "V16ScV16ScV16Sc", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vpickod_h, "V8sV8sV8s", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vpickod_w, "V4iV4iV4i", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vpickod_d, "V2LLiV2LLiV2LLi", "nc", "lsx") -TARGET_BUILTIN(__builtin_lsx_vilvl_b, "V16cV16cV16c", "nc", "lsx") +TARGET_BUILTIN(__builtin_lsx_vilvl_b, "V16ScV16ScV16Sc", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vilvl_h, "V8sV8sV8s", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vilvl_w, "V4iV4iV4i", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vilvl_d, "V2LLiV2LLiV2LLi", "nc", "lsx") -TARGET_BUILTIN(__builtin_lsx_vilvh_b, "V16cV16cV16c", "nc", "lsx") +TARGET_BUILTIN(__builtin_lsx_vilvh_b, "V16ScV16ScV16Sc", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vilvh_h, "V8sV8sV8s", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vilvh_w, "V4iV4iV4i", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vilvh_d, "V2LLiV2LLiV2LLi", "nc", "lsx") -TARGET_BUILTIN(__builtin_lsx_vshuf_b, "V16UcV16UcV16UcV16Uc", "nc", "lsx") +TARGET_BUILTIN(__builtin_lsx_vshuf_b, "V16ScV16ScV16ScV16Sc", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vshuf_h, "V8sV8sV8sV8s", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vshuf_w, "V4iV4iV4iV4i", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vshuf_d, "V2LLiV2LLiV2LLiV2LLi", "nc", "lsx") -TARGET_BUILTIN(__builtin_lsx_vshuf4i_b, "V16cV16cIUi", "nc", "lsx") +TARGET_BUILTIN(__builtin_lsx_vshuf4i_b, "V16ScV16ScIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vshuf4i_h, "V8sV8sIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vshuf4i_w, "V4iV4iIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vshuf4i_d, "V2LLiV2LLiV2LLiIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vpermi_w, "V4iV4iV4iIUi", "nc", "lsx") -TARGET_BUILTIN(__builtin_lsx_vextrins_b, "V16cV16cV16cIUi", "nc", "lsx") +TARGET_BUILTIN(__builtin_lsx_vextrins_b, "V16ScV16ScV16ScIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vextrins_h, "V8sV8sV8sIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vextrins_w, "V4iV4iV4iIUi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vextrins_d, "V2LLiV2LLiV2LLiIUi", "nc", "lsx") @@ -934,7 +934,7 @@ TARGET_BUILTIN(__builtin_lsx_vst, "vV16Scv*Ii", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vldx, "V16ScvC*LLi", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vstx, "vV16Scv*LLi", "nc", "lsx") -TARGET_BUILTIN(__builtin_lsx_vldrepl_b, "V16cvC*Ii", "nc", "lsx") +TARGET_BUILTIN(__builtin_lsx_vldrepl_b, "V16ScvC*Ii", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vldrepl_h, "V8svC*Ii", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vldrepl_w, "V4ivC*Ii", "nc", "lsx") TARGET_BUILTIN(__builtin_lsx_vldrepl_d, "V2LLivC*Ii", "nc", "lsx") diff --git a/clang/include/clang/Basic/CMakeLists.txt b/clang/include/clang/Basic/CMakeLists.txt index 2ef6ddc68f4bf..4dc66eeec3dd9 100644 --- a/clang/include/clang/Basic/CMakeLists.txt +++ b/clang/include/clang/Basic/CMakeLists.txt @@ -7,6 +7,7 @@ endmacro(clang_diag_gen) clang_diag_gen(Analysis) clang_diag_gen(AST) +clang_diag_gen(CAS) clang_diag_gen(Comment) clang_diag_gen(Common) clang_diag_gen(CrossTU) diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def index 12808eb275fa4..dfc4952fe534c 100644 --- a/clang/include/clang/Basic/CodeGenOptions.def +++ b/clang/include/clang/Basic/CodeGenOptions.def @@ -176,6 +176,7 @@ CODEGENOPT(IncrementalLinkerCompatible, 1, 0) ///< Emit an object file which can ///< linker. CODEGENOPT(MergeAllConstants , 1, 1) ///< Merge identical constants. CODEGENOPT(MergeFunctions , 1, 0) ///< Set when -fmerge-functions is enabled. +CODEGENOPT(SplitColdCode , 1, 0) ///< Set when -fsplit-cold-code is enabled. CODEGENOPT(NoCommon , 1, 0) ///< Set when -fno-common or C++ is enabled. CODEGENOPT(NoExecStack , 1, 0) ///< Set when -Wa,--noexecstack is enabled. CODEGENOPT(FatalWarnings , 1, 0) ///< Set when -Wa,--fatal-warnings is @@ -320,6 +321,15 @@ CODEGENOPT(VectorizeLoop , 1, 0) ///< Run loop vectorizer. CODEGENOPT(VectorizeSLP , 1, 0) ///< Run SLP vectorizer. CODEGENOPT(ProfileSampleAccurate, 1, 0) ///< Sample profile is accurate. +/* TO_UPSTREAM(BoundsSafety) ON*/ +CODEGENOPT(TrapFuncReturns , 1, 0) ///< When true, the function specified with + ///< -ftrap-function may return normally +CODEGENOPT( + UniqueTrapBlocks, 1, + 0) ///< When true, basic blocks that contain traps they will be prevented + ///< from being merged by optimization passes and the backends. +/* TO_UPSTREAM(BoundsSafety) OFF*/ + /// Treat loops as finite: language, always, never. ENUM_CODEGENOPT(FiniteLoops, FiniteLoopsKind, 2, FiniteLoopsKind::Language) @@ -450,10 +460,24 @@ CODEGENOPT(SkipRaxSetup, 1, 0) ENUM_CODEGENOPT(ZeroCallUsedRegs, llvm::ZeroCallUsedRegs::ZeroCallUsedRegsKind, 5, llvm::ZeroCallUsedRegs::ZeroCallUsedRegsKind::Skip) +/// Whether to use CASID backend. +CODEGENOPT(UseCASBackend, 1, 0) + +/// Whether to emit a .casid File. +CODEGENOPT(EmitCASIDFile, 1, 0) + +/// The CASObjectFormat used when CAS output is used. +ENUM_CODEGENOPT(CASObjMode, llvm::CASBackendMode, + 2, llvm::CASBackendMode::Native) + /// Modify C++ ABI to returning `this` pointer from constructors and /// non-deleting destructors. (No effect on Microsoft ABI.) CODEGENOPT(CtorDtorReturnThis, 1, 0) +/// Controls whether we generate code for static linking of libclosure +/// (BlocksRuntime) on Windows. +CODEGENOPT(StaticClosure, 1, 0) + /// FIXME: Make DebugOptions its own top-level .def file. #include "DebugOptions.def" diff --git a/clang/include/clang/Basic/CodeGenOptions.h b/clang/include/clang/Basic/CodeGenOptions.h index f2a707a8ba8d7..076b46d5bb0a2 100644 --- a/clang/include/clang/Basic/CodeGenOptions.h +++ b/clang/include/clang/Basic/CodeGenOptions.h @@ -167,6 +167,8 @@ class CodeGenOptions : public CodeGenOptionsBase { Enabled, Forced, }; + /// The callback for mc result. + std::optional<llvm::MCTargetOptions::ResultCallBackTy> MCCallBack; /// The code model to use (-mcmodel). std::string CodeModel; diff --git a/clang/include/clang/Basic/DebugOptions.def b/clang/include/clang/Basic/DebugOptions.def index bc96d5dfdf890..0b4a6712f7857 100644 --- a/clang/include/clang/Basic/DebugOptions.def +++ b/clang/include/clang/Basic/DebugOptions.def @@ -107,6 +107,9 @@ ENUM_DEBUGOPT(DebugSimpleTemplateNames, ENUM_DEBUGOPT(DebugInfo, llvm::codegenoptions::DebugInfoKind, 4, llvm::codegenoptions::NoDebugInfo) +/// Whether to make debug info reproducible. +DEBUGOPT(ReproducibleDebugInfo, 1, 0) + /// Whether to generate macro debug info. DEBUGOPT(MacroDebugInfo, 1, 0) diff --git a/clang/include/clang/Basic/Diagnostic.h b/clang/include/clang/Basic/Diagnostic.h index 0c7836c2ea569..1e5626229ce2e 100644 --- a/clang/include/clang/Basic/Diagnostic.h +++ b/clang/include/clang/Basic/Diagnostic.h @@ -39,6 +39,7 @@ namespace llvm { class Error; +class format_object_base; class raw_ostream; } // namespace llvm @@ -1557,6 +1558,9 @@ inline DiagnosticBuilder DiagnosticsEngine::Report(SourceLocation Loc, const StreamingDiagnostic &operator<<(const StreamingDiagnostic &DB, llvm::Error &&E); +const StreamingDiagnostic &operator<<(const StreamingDiagnostic &DB, + const llvm::format_object_base &Fmt); + inline DiagnosticBuilder DiagnosticsEngine::Report(unsigned DiagID) { return Report(SourceLocation(), DiagID); } diff --git a/clang/include/clang/Basic/Diagnostic.td b/clang/include/clang/Basic/Diagnostic.td index 0b8b3af939ba0..0d602d02f53ef 100644 --- a/clang/include/clang/Basic/Diagnostic.td +++ b/clang/include/clang/Basic/Diagnostic.td @@ -157,6 +157,7 @@ class DefaultRemark { Severity DefaultSeverity = SEV_Remark; } // Definitions for Diagnostics. include "DiagnosticASTKinds.td" +include "DiagnosticCASKinds.td" include "DiagnosticCommentKinds.td" include "DiagnosticCommonKinds.td" include "DiagnosticCrossTUKinds.td" diff --git a/clang/include/clang/Basic/DiagnosticASTKinds.td b/clang/include/clang/Basic/DiagnosticASTKinds.td index a024f9b2a9f8c..87d35096beb74 100644 --- a/clang/include/clang/Basic/DiagnosticASTKinds.td +++ b/clang/include/clang/Basic/DiagnosticASTKinds.td @@ -1030,6 +1030,11 @@ def warn_unpacked_field InGroup<PackedNonPod>, DefaultIgnore; +// TO_UPSTREAM(BoundsSafety) ON +def err_bounds_safety_evaluate_no_bounds : Error< + "cannot get %select{lower,upper}0 bound because object size is unknown">; +// TO_UPSTREAM(BoundsSafety) OFF + // -Wunaligned-access def warn_unaligned_access : Warning< "field %1 within %0 is less aligned than %2 and is usually due to %0 being " diff --git a/clang/include/clang/Basic/DiagnosticCAS.h b/clang/include/clang/Basic/DiagnosticCAS.h new file mode 100644 index 0000000000000..dce073b4e3923 --- /dev/null +++ b/clang/include/clang/Basic/DiagnosticCAS.h @@ -0,0 +1,28 @@ +//===--- DiagnosticCAS.h - Diagnostics for the CAS --------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_BASIC_DIAGNOSTICCAS_H +#define LLVM_CLANG_BASIC_DIAGNOSTICCAS_H + +#include "clang/Basic/Diagnostic.h" + +namespace clang { +namespace diag { +enum { +#define DIAG(ENUM, FLAGS, DEFAULT_MAPPING, DESC, GROUP, SFINAE, NOWERROR, \ + SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY) \ + ENUM, +#define CASSTART +#include "clang/Basic/DiagnosticCASKinds.inc" +#undef DIAG + NUM_BUILTIN_CAS_DIAGNOSTICS +}; +} // end namespace diag +} // end namespace clang + +#endif // LLVM_CLANG_BASIC_DIAGNOSTICCAS_H diff --git a/clang/include/clang/Basic/DiagnosticCASKinds.td b/clang/include/clang/Basic/DiagnosticCASKinds.td new file mode 100644 index 0000000000000..e8cfead1d1419 --- /dev/null +++ b/clang/include/clang/Basic/DiagnosticCASKinds.td @@ -0,0 +1,78 @@ +//===- DiagnosticCASKinds.td - CAS diagnostics ----------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +let Component = "CAS" in { + +def err_cas_cannot_be_initialized : Error< + "CAS cannot be initialized from the specified '-fcas-*' options: %0">, + DefaultFatal; +def err_cas_cannot_parse_root_id : Error< + "CAS cannot parse root-id '%0' specified by -fcas-fs">, DefaultFatal; +def err_cas_filesystem_cannot_be_initialized : Error< + "CAS filesystem cannot be initialized from root-id '%0': %1">, DefaultFatal; +def err_cas_filesystem_cannot_set_working_directory : Error< + "CAS filesystem cannot set working directory to '%0' specified by" + " -fcas-fs-working-directory">, DefaultFatal; +def err_cas_depscan_daemon_connection: Error< + "failed to establish connection with depscan daemon: %0">, DefaultFatal; +def err_cas_depscan_failed: Error< + "CAS-based dependency scan failed: %0">, DefaultFatal; +def err_cas_store: Error<"failed to store to CAS: %0">, DefaultFatal; +def err_cas_cannot_get_module_cache_key : Error< + "CAS cannot load module with key '%0' from %1: %2">, DefaultFatal; +def err_cas_missing_root_id : Error< + "CAS missing expected root-id '%0'">, DefaultFatal; +def err_cas_cannot_parse_root_id_for_module : Error< + "CAS cannot parse root-id '%0' for module '%1'">, DefaultFatal; +def err_cas_cannot_parse_include_tree_id : Error< + "CAS cannot parse include-tree-id '%0'">, DefaultFatal; +def err_cas_missing_include_tree_id : Error< + "CAS missing expected include-tree '%0'">, DefaultFatal; +def err_cas_cannot_load_api_notes_include_tree : Error< + "cannot load APINotes from include-tree-id '%0'">, DefaultFatal; + +def warn_clang_cache_disabled_caching: Warning< + "caching disabled because %0">, + InGroup<DiagGroup<"clang-cache">>; +def err_clang_cache_failed_execution: Error< + "clang-cache failed to execute compiler: %0">; +def err_clang_cache_cannot_find_binary: Error< + "clang-cache cannot find compiler binary %0">; +def err_clang_cache_missing_compiler_command: Error< + "missing compiler command for clang-cache">; +def err_clang_cache_scanserve_missing_args: Error< + "missing arguments for dep-scanning server mode">; +def err_caching_backend_fail: Error< + "caching backend error: %0">, DefaultFatal; +def err_caching_output_non_deterministic: Error< + "caching failed because the output can be non-deterministic">; + +def remark_compile_job_cache_hit : Remark< + "compile job cache hit for '%0' => '%1'">, InGroup<CompileJobCacheHit>; +def remark_compile_job_cache_miss : Remark< + "compile job cache miss for '%0'">, InGroup<CompileJobCacheMiss>; +def remark_compile_job_cache_miss_result_not_found : Remark< + "compile job cache miss for '%0' (result not found: '%1')">, + InGroup<CompileJobCacheMiss>; +def remark_compile_job_cache_backend_output_not_found : Remark< + "compile job cache backend did not find output '%0' for key '%1' (output ID: '%2')">, + InGroup<CompileJobCacheMiss>; +def remark_compile_job_cache_skipped : Remark< + "compile job cache skipped for '%0'">, InGroup<CompileJobCache>; +def remark_compile_job_cache_timing_depscan : Remark< + "compile job dependency scanning time: %0">, InGroup<CompileJobCacheTiming>; +def remark_compile_job_cache_timing_backend_key_query : Remark< + "compile job cache backend key query time: %0">, InGroup<CompileJobCacheTiming>; +def remark_compile_job_cache_timing_backend_key_update : Remark< + "compile job cache backend key update time: %0">, InGroup<CompileJobCacheTiming>; +def remark_compile_job_cache_timing_backend_load : Remark< + "compile job cache backend load artifacts time: %0">, InGroup<CompileJobCacheTiming>; +def remark_compile_job_cache_timing_backend_store : Remark< + "compile job cache backend store artifacts time: %0">, InGroup<CompileJobCacheTiming>; + +} // let Component = "CAS" in diff --git a/clang/include/clang/Basic/DiagnosticDriverKinds.td b/clang/include/clang/Basic/DiagnosticDriverKinds.td index 3d8240f8357b4..f0faa876a168c 100644 --- a/clang/include/clang/Basic/DiagnosticDriverKinds.td +++ b/clang/include/clang/Basic/DiagnosticDriverKinds.td @@ -265,6 +265,10 @@ def err_drv_cannot_read_config_file : Error< "cannot read configuration file '%0': %1">; def err_drv_arg_requires_bitcode_input: Error< "option '%0' requires input to be LLVM bitcode">; +/* TO_UPSTREAM(BoundsSafety) ON*/ +def err_drv_option_requires_option: Error< + "cannot use '%0' without using '%1'">; +/* TO_UPSTREAM(BoundsSafety) OFF*/ def err_target_unsupported_arch : Error<"the target architecture '%0' is not supported by the target '%1'">; @@ -300,6 +304,10 @@ def err_drv_invalid_value : Error<"invalid value '%1' in '%0'">; def err_drv_invalid_int_value : Error<"invalid integral value '%1' in '%0'">; def err_drv_invalid_value_with_suggestion : Error< "invalid value '%1' in '%0', expected one of: %2">; +/* TO_UPSTREAM(BoundsSafety) ON*/ +def err_drv_invalid_value_with_flag_suggestion : Error< + "invalid value '%1' in '%0'; did you mean '%2'?">; +/* TO_UPSTREAM(BoundsSafety) OFF*/ def err_drv_alignment_not_power_of_two : Error<"alignment is not a power of 2 in '%0'">; def err_drv_invalid_remap_file : Error< "invalid option '%0' not of the form <from-file>;<to-file>">; @@ -353,6 +361,9 @@ def err_drv_omp_host_ir_file_not_found : Error< "target regions but cannot be found">; def err_drv_omp_host_target_not_supported : Error< "target '%0' is not a supported OpenMP host target">; +def err_drv_ptrauth_not_supported : Error< + "target '%0' does not support native pointer authentication">; + def err_drv_expecting_fopenmp_with_fopenmp_targets : Error< "'-fopenmp-targets' must be used in conjunction with a '-fopenmp' option " "compatible with offloading; e.g., '-fopenmp=libomp' or '-fopenmp=libiomp5'">; @@ -547,6 +558,9 @@ def err_test_module_file_extension_format : Error< def err_drv_module_output_with_multiple_arch : Error< "option '-fmodule-output' can't be used with multiple arch options">; +def err_modules_no_lsv : Error<"standard C++ modules require " + "the -fmodules-local-submodule-visibility -cc1 option">; + def warn_drv_delayed_template_parsing_after_cxx20 : Warning< "-fdelayed-template-parsing is deprecated after C++20">, InGroup<DiagGroup<"delayed-template-parsing-in-cxx20">>; @@ -758,6 +772,13 @@ def warn_target_override_arm64ec : Warning< def err_drv_target_variant_invalid : Error< "unsupported '%0' value '%1'; use 'ios-macabi' instead">; +def err_drv_inputs_and_include_tree : Error< + "passing input files is incompatible with '-fcas-include-tree'">; +def err_drv_incompatible_option_include_tree : Error< + "passing incompatible option '%0' with '-fcas-include-tree'">; +def err_drv_include_tree_miss_input_kind : Error< + "missing '-x' input kind when using '-fcas-include-tree'">; + def err_drv_invalid_directx_shader_module : Error< "invalid profile : %0">; def err_drv_dxc_missing_target_profile : Error< diff --git a/clang/include/clang/Basic/DiagnosticFrontendKinds.td b/clang/include/clang/Basic/DiagnosticFrontendKinds.td index 12a4617c64d87..4e4395782924c 100644 --- a/clang/include/clang/Basic/DiagnosticFrontendKinds.td +++ b/clang/include/clang/Basic/DiagnosticFrontendKinds.td @@ -12,6 +12,7 @@ let Component = "Frontend" in { def err_fe_error_opening : Error<"error opening '%0': %1">; def err_fe_error_reading : Error<"error reading '%0': %1">; +def err_fe_error_writing : Error<"error writing '%0': %1">; def err_fe_error_reading_stdin : Error<"error reading stdin: %0">; def err_fe_error_backend : Error<"error in backend: %0">, DefaultFatal; @@ -143,6 +144,16 @@ def err_fe_invalid_exception_model def err_fe_invalid_source_date_epoch : Error< "environment variable 'SOURCE_DATE_EPOCH' ('%0') must be a non-negative decimal integer <= %1">; +def err_feature_availability_flag_invalid_value : Error< + "invalid value '%0' passed to '-ffeature-availability='; expected <feature>:<on|off>">; + +def err_fe_incompatible_option_with_remote_cache : Error< + "'%0' is incompatible with remote caching backend">; +def err_fe_unable_to_load_include_tree : Error< + "unable to load the CAS include-tree '%0': '%1'">; +def err_unable_to_load_include_tree_node : Error< + "unable to load a node from the CAS include-tree: '%0'">; + def err_fe_unable_to_load_basic_block_sections_file : Error< "unable to load basic block sections function list: '%0'">; @@ -229,6 +240,8 @@ def err_missing_module_name : Error< def err_file_is_not_module : Error<"file '%0' is not a module file">, DefaultFatal; def err_missing_module : Error< "no module named '%0' declared in module map file '%1'">, DefaultFatal; +def err_missing_module_include_tree : Error< + "no module named '%0' declared in include-tree module map '%1'">, DefaultFatal; def err_no_submodule : Error<"no submodule named %0 in module '%1'">; def err_no_submodule_suggest : Error< "no submodule named %0 in module '%1'; did you mean '%2'?">; @@ -263,6 +276,10 @@ def err_modules_embed_file_not_found : def err_module_header_file_not_found : Error<"module header file '%0' not found">, DefaultFatal; +def remark_index_producing_module_file_data : Remark<"producing index data for " + "module file '%0'">, + InGroup<IndexStore>; + def err_test_module_file_extension_version : Error< "test module file extension '%0' has different version (%1.%2) than expected " "(%3.%4)">; @@ -288,6 +305,9 @@ def err_function_needs_feature : Error< let CategoryName = "Codegen ABI Check" in { def err_function_always_inline_attribute_mismatch : Error< "always_inline function %1 and its caller %0 have mismatching %2 attributes">; +def warn_function_always_inline_attribute_mismatch : Warning< + "always_inline function %1 and its caller %0 have mismatching %2 attributes, " + "inlining may change runtime behaviour">, InGroup<AArch64SMEAttributes>; def err_function_always_inline_new_za : Error< "always_inline function %0 has new za state">; @@ -346,6 +366,38 @@ def warn_alias_with_section : Warning< "as the %select{aliasee|resolver}2">, InGroup<IgnoredAttributes>; +// TO_UPSTREAM(BoundsSafety) ON +def error_bounds_safety_lang_not_supported : Error< + "-fbounds-safety is supported only for C language">; + +def err_bounds_safety_attributes_cannot_be_disabled : Error< + "-fexperimental-bounds-safety-attributes cannot be disabled when " + "-fbounds-safety is enabled">; + +def err_bounds_safety_initializer_out_of_range : Error< + "initializing value is out of valid range">; + +def warn_bounds_attributes_cxx_experimental_ignored : Warning< + "-fbounds-attributes-cxx-experimental without -fbounds-attributes is " + "ignored">, + InGroup<BoundsAttributesCXXExperimentalIgnored>; + +def warn_bounds_attributes_objc_experimental_ignored : Warning< + "-fbounds-attributes-objc-experimental without -fbounds-attributes is " + "ignored">, + InGroup<BoundsAttributesObjCExperimentalIgnored>; + +def warn_bounds_safety_relaxed_system_headers_ignored : Warning< + "-fno-bounds-safety-relaxed-system-headers without -fbounds-safety is " + "ignored">, + InGroup<BoundsSafetyRelaxedSystemHeadersIgnored>; + +def warn_bounds_safety_adoption_mode_ignored : Warning< + "-fbounds-safety-adoption-mode without -fbounds-safety is " + "ignored">, + InGroup<BoundsSafetyAdoptionModeIgnored>; +// TO_UPSTREAM(BoundsSafety) OFF + let CategoryName = "Instrumentation Issue" in { def warn_profile_data_out_of_date : Warning< "profile data may be out of date: of %0 function%s0, %1 %plural{1:has|:have}1" diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td index 19c3f1e043349..e9c08054e3d84 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -291,11 +291,13 @@ def : DiagGroup<"c++1z-compat-mangling", [CXX17CompatMangling]>; // Name of this warning in GCC. def NoexceptType : DiagGroup<"noexcept-type", [CXX17CompatMangling]>; +def VariadicMacroArgumentsOmitted : DiagGroup<"variadic-macro-arguments-omitted">; + // Warnings for C code which is not compatible with previous C standards. def CPre11Compat : DiagGroup<"pre-c11-compat">; def CPre11CompatPedantic : DiagGroup<"pre-c11-compat-pedantic", [CPre11Compat]>; -def CPre23Compat : DiagGroup<"pre-c23-compat">; +def CPre23Compat : DiagGroup<"pre-c23-compat", [VariadicMacroArgumentsOmitted]>; def CPre23CompatPedantic : DiagGroup<"pre-c23-compat-pedantic", [CPre23Compat]>; def : DiagGroup<"pre-c2x-compat", [CPre23Compat]>; @@ -556,6 +558,7 @@ def ModuleImport : DiagGroup<"module-import">; def ModuleConflict : DiagGroup<"module-conflict">; def ModuleFileExtension : DiagGroup<"module-file-extension">; def ModuleIncludeDirectiveTranslation : DiagGroup<"module-include-translation">; +def IndexStore : DiagGroup<"index-store">; def RoundTripCC1Args : DiagGroup<"round-trip-cc1-args">; def NewlineEOF : DiagGroup<"newline-eof">; def Nullability : DiagGroup<"nullability">; @@ -883,8 +886,7 @@ def DeallocInCategory:DiagGroup<"dealloc-in-category">; def SelectorTypeMismatch : DiagGroup<"selector-type-mismatch">; def Selector : DiagGroup<"selector", [SelectorTypeMismatch]>; def Protocol : DiagGroup<"protocol">; -// No longer in use, preserve for backwards compatibility. -def : DiagGroup<"at-protocol">; +def AtProtocol : DiagGroup<"at-protocol">; def PropertyAccessDotSyntax: DiagGroup<"property-access-dot-syntax">; def PropertyAttr : DiagGroup<"property-attribute-mismatch">; def SuperSubClassMismatch : DiagGroup<"super-class-method-mismatch">; @@ -900,7 +902,7 @@ def VolatileRegisterVar : DiagGroup<"volatile-register-var">; def Visibility : DiagGroup<"visibility">; def ZeroLengthArray : DiagGroup<"zero-length-array">; def GNUZeroLineDirective : DiagGroup<"gnu-zero-line-directive">; -def GNUZeroVariadicMacroArguments : DiagGroup<"gnu-zero-variadic-macro-arguments">; +def GNUZeroVariadicMacroArguments : DiagGroup<"gnu-zero-variadic-macro-arguments", [VariadicMacroArgumentsOmitted]>; def MisleadingIndentation : DiagGroup<"misleading-indentation">; def PtrAuthNullPointers : DiagGroup<"ptrauth-null-pointers">; @@ -1125,6 +1127,11 @@ def ThreadSafety : DiagGroup<"thread-safety", def ThreadSafetyVerbose : DiagGroup<"thread-safety-verbose">; def ThreadSafetyBeta : DiagGroup<"thread-safety-beta">; +// Warnings and notes related to the function effects system which underlies +// the nonblocking and nonallocating attributes. +def FunctionEffects : DiagGroup<"function-effects">; +def PerfConstraintImpliesNoexcept : DiagGroup<"perf-constraint-implies-noexcept">; + // Uniqueness Analysis warnings def Consumed : DiagGroup<"consumed">; @@ -1133,7 +1140,7 @@ def Consumed : DiagGroup<"consumed">; // DefaultIgnore in addition to putting it here. def All : DiagGroup<"all", [Most, Parentheses, Switch, SwitchBool, MisleadingIndentation, PackedNonPod, - VLACxxExtension]>; + VLACxxExtension, PerfConstraintImpliesNoexcept]>; // Warnings that should be in clang-cl /w4. def : DiagGroup<"CL4", [All, Extra]>; @@ -1188,7 +1195,7 @@ def CXX17 : DiagGroup<"c++17-extensions", [CXX17Attrs]>; // A warning group for warnings about using C++20 features as extensions in // earlier C++ versions. -def CXX20 : DiagGroup<"c++20-extensions", [CXX20Designator, CXX20Attrs]>; +def CXX20 : DiagGroup<"c++20-extensions", [CXX20Designator, CXX20Attrs, VariadicMacroArgumentsOmitted]>; // A warning group for warnings about using C++23 features as extensions in // earlier C++ versions. @@ -1215,7 +1222,7 @@ def C11 : DiagGroup<"c11-extensions">; def C99 : DiagGroup<"c99-extensions", [C99Designator]>; // A warning group for warnings about using C23 features as extensions. -def C23 : DiagGroup<"c23-extensions">; +def C23 : DiagGroup<"c23-extensions", [VariadicMacroArgumentsOmitted]>; def : DiagGroup<"c2x-extensions", [C23]>; @@ -1472,6 +1479,46 @@ def FunctionMultiVersioning def NoDeref : DiagGroup<"noderef">; +// TO_UPSTREAM(BoundsSafety) ON +def BoundsSafetyIncompleteArray : DiagGroup<"bounds-safety-incomplete-array">; +def BoundsSafetySingleToCount : DiagGroup<"bounds-safety-single-to-count">; +def BoundsSafetyInitListPartialNull : DiagGroup<"bounds-safety-init-list-partial-null">; +def BoundsSafetyInitList : DiagGroup<"bounds-safety-init-list">; +def BoundsAttributesExternArrayCount : DiagGroup<"bounds-attributes-extern-array-count">; +def BoundsAttributesInitSideEffect + : DiagGroup<"bounds-attributes-init-list-side-effect">; +def BoundsAttributesCXXExperimentalIgnored + : DiagGroup<"bounds-attributes-cxx-experimental-ignored">; +def BoundsAttributesObjCExperimentalIgnored + : DiagGroup<"bounds-attributes-objc-experimental-ignored">; +def BoundsSafetyRelaxedSystemHeadersIgnored + : DiagGroup<"bounds-safety-relaxed-system-headers-ignore">; +def BoundsAttributesImplicitConvSingleToExplicitIndexable : + DiagGroup< + "bounds-attributes-implicit-conversion-single-to-explicit-indexable">; +def BoundsSafetyConvSingleToImplicitIndexable : + DiagGroup< + "bounds-safety-conversion-single-to-implicit-indexable">; +def BoundsSafetyConvSingleToImplicitIndexableThenConvertToLargerType : + DiagGroup< + "bounds-safety-conversion-single-to-implicit-indexable-then-convert-to-larger-type">; +// FIXME: This name is wrong but we have to keep it because some adopter's +// build system currently expects (rdar://119832922). +def BoundsSafetyConvSingleToImplicitIndexableThenConvertToDynamicCountPtr : + DiagGroup< + "bounds-safety-conversion-single-to-implicit-indexable-then-convert-at-call-site">; +def BoundsSafetyConvertSingleToIndexableBoundsTruncated + : DiagGroup<"bounds-safety-single-to-indexable-bounds-truncated">; +def BoundsSafetyRedundantAttribute : DiagGroup<"bounds-attributes-redundant">; +def BoundsSafetyNullability : DiagGroup<"bounds-safety-nullability">; +def BoundsSafetyAdoptionModeIgnored + : DiagGroup<"bounds-safety-adoption-mode-ignored">; +def BoundsSafetyStrictTerminatedByCast + : DiagGroup<"bounds-safety-strict-terminated-by-cast">; +def BoundsSafetyCountAttrArithConstantCount : + DiagGroup<"bounds-safety-externally-counted-ptr-arith-constant-count">; +// TO_UPSTREAM(BoundsSafety) OFF + // -fbounds-safety and bounds annotation related warnings def BoundsSafetyCountedByEltTyUnknownSize : DiagGroup<"bounds-safety-counted-by-elt-type-unknown-size">; @@ -1524,6 +1571,13 @@ def RTTI : DiagGroup<"rtti">; def OpenCLCoreFeaturesDiagGroup : DiagGroup<"pedantic-core-features">; +// Remarks for cached -cc1. +def CompileJobCacheMiss : DiagGroup<"compile-job-cache-miss">; +def CompileJobCacheHit : DiagGroup<"compile-job-cache-hit">; +def CompileJobCacheTiming : DiagGroup<"compile-job-cache-timing">; +def CompileJobCache : DiagGroup<"compile-job-cache", [CompileJobCacheMiss, + CompileJobCacheHit]>; + // Warnings and extensions to make preprocessor macro usage pedantic. def PedanticMacros : DiagGroup<"pedantic-macros", [DeprecatedPragma, @@ -1552,11 +1606,8 @@ def ReadOnlyPlacementChecks : DiagGroup<"read-only-types">; // Warnings and fixes to support the "safe buffers" programming model. def UnsafeBufferUsageInContainer : DiagGroup<"unsafe-buffer-usage-in-container">; -def UnsafeBufferUsage : DiagGroup<"unsafe-buffer-usage", [UnsafeBufferUsageInContainer]>; - -// Warnings and notes related to the function effects system underlying -// the nonblocking and nonallocating attributes. -def FunctionEffects : DiagGroup<"function-effects">; +def UnsafeBufferUsageInLibcCall : DiagGroup<"unsafe-buffer-usage-in-libc-call">; +def UnsafeBufferUsage : DiagGroup<"unsafe-buffer-usage", [UnsafeBufferUsageInContainer, UnsafeBufferUsageInLibcCall]>; // Warnings and notes InstallAPI verification. def InstallAPIViolation : DiagGroup<"installapi-violation">; diff --git a/clang/include/clang/Basic/DiagnosticIDs.h b/clang/include/clang/Basic/DiagnosticIDs.h index bce7605b95ba4..bdaebbd57fa3d 100644 --- a/clang/include/clang/Basic/DiagnosticIDs.h +++ b/clang/include/clang/Basic/DiagnosticIDs.h @@ -39,10 +39,12 @@ namespace clang { DIAG_SIZE_AST = 300, DIAG_SIZE_COMMENT = 100, DIAG_SIZE_CROSSTU = 100, - DIAG_SIZE_SEMA = 4500, + // TO_UPSTREAM(BoundsSafety) + DIAG_SIZE_SEMA = 4500 + 240, DIAG_SIZE_ANALYSIS = 100, DIAG_SIZE_REFACTORING = 1000, DIAG_SIZE_INSTALLAPI = 100, + DIAG_SIZE_CAS = 100, }; // Start position for diagnostics. enum { @@ -59,7 +61,8 @@ namespace clang { DIAG_START_ANALYSIS = DIAG_START_SEMA + static_cast<int>(DIAG_SIZE_SEMA), DIAG_START_REFACTORING = DIAG_START_ANALYSIS + static_cast<int>(DIAG_SIZE_ANALYSIS), DIAG_START_INSTALLAPI = DIAG_START_REFACTORING + static_cast<int>(DIAG_SIZE_REFACTORING), - DIAG_UPPER_LIMIT = DIAG_START_INSTALLAPI + static_cast<int>(DIAG_SIZE_INSTALLAPI) + DIAG_START_CAS = DIAG_START_INSTALLAPI + static_cast<int>(DIAG_START_INSTALLAPI), + DIAG_UPPER_LIMIT = DIAG_START_CAS + static_cast<int>(DIAG_SIZE_CAS) }; class CustomDiagInfo; diff --git a/clang/include/clang/Basic/DiagnosticLexKinds.td b/clang/include/clang/Basic/DiagnosticLexKinds.td index 12d7b8c0205ee..f27acbe64f05c 100644 --- a/clang/include/clang/Basic/DiagnosticLexKinds.td +++ b/clang/include/clang/Basic/DiagnosticLexKinds.td @@ -483,14 +483,14 @@ def ext_embedded_directive : Extension< InGroup<DiagGroup<"embedded-directive">>; def ext_c_missing_varargs_arg : Extension< "passing no argument for the '...' parameter of a variadic macro is " - "a C23 extension">, InGroup<C23>; + "a C23 extension">, InGroup<VariadicMacroArgumentsOmitted>; def ext_cxx_missing_varargs_arg : Extension< "passing no argument for the '...' parameter of a variadic macro is " - "a C++20 extension">, InGroup<CXX20>; + "a C++20 extension">, InGroup<VariadicMacroArgumentsOmitted>; def warn_c17_compat_missing_varargs_arg : Warning< "passing no argument for the '...' parameter of a variadic macro is " "incompatible with C standards before C23">, - InGroup<CPre23Compat>, DefaultIgnore; + InGroup<VariadicMacroArgumentsOmitted>, DefaultIgnore; def warn_cxx17_compat_missing_varargs_arg : Warning< "passing no argument for the '...' parameter of a variadic macro is " "incompatible with C++ standards before C++20">, @@ -817,6 +817,13 @@ def warn_pp_date_time : Warning< "expansion of date or time macro is not reproducible">, ShowInSystemHeader, DefaultIgnore, InGroup<DiagGroup<"date-time">>; +def warn_pp_encounter_nonreproducible: Warning< + "encountered non-reproducible token, caching will be skipped">, + InGroup<DiagGroup<"reproducible-caching">>, + DefaultWarnNoWerror, ShowInSystemHeader, ShowInSystemMacro; +def err_pp_encounter_nonreproducible: Error< + "encountered non-reproducible token, caching failed">; + // Module map parsing def err_mmap_unknown_token : Error<"skipping stray token">; def err_mmap_expected_module : Error<"expected module declaration">; @@ -916,7 +923,7 @@ def warn_quoted_include_in_framework_header : Warning< >, InGroup<FrameworkHdrQuotedInclude>, DefaultIgnore; def warn_framework_include_private_from_public : Warning< "public framework header includes private framework header '%0'" - >, InGroup<FrameworkIncludePrivateFromPublic>; + >, InGroup<FrameworkIncludePrivateFromPublic>, DefaultIgnore; def warn_deprecated_module_dot_map : Warning< "'%0' as a module map name is deprecated, rename it to %select{module.modulemap|module.private.modulemap}1%select{| in the 'Modules' directory of the framework}2">, InGroup<DeprecatedModuleDotMap>; @@ -977,6 +984,9 @@ def warn_defined_in_function_type_macro : Extension< "macro expansion producing 'defined' has undefined behavior">, InGroup<ExpansionToDefined>; +def err_pp_missing_module_include_tree : Error< + "no module named '%0' declared in include-tree module map">, DefaultFatal; + let CategoryName = "Nullability Issue" in { def err_pp_assume_nonnull_syntax : Error<"expected 'begin' or 'end'">; @@ -992,6 +1002,15 @@ def err_pp_eof_in_assume_nonnull : Error< } +// TO_UPSTREAM(BoundsSafety) ON +let CategoryName = "BoundsSafety Pointer Attributes Issue" in { + +def err_pp_abi_ptr_attr_set_syntax : Error< + "expected 'set(attr1 [attr2 ...])'">; + +} +// TO_UPSTREAM(BoundsSafety) OFF + let CategoryName = "Dependency Directive Source Scanner Issue" in { def err_dep_source_scanner_missing_semi_after_at_import : Error< diff --git a/clang/include/clang/Basic/DiagnosticOptions.def b/clang/include/clang/Basic/DiagnosticOptions.def index 6d0c1b14acc12..ba8ce8f2e6d4f 100644 --- a/clang/include/clang/Basic/DiagnosticOptions.def +++ b/clang/include/clang/Basic/DiagnosticOptions.def @@ -98,6 +98,8 @@ VALUE_DIAGOPT(TabStop, 32, DefaultTabStop) /// The distance between tab stops. VALUE_DIAGOPT(MessageLength, 32, 0) DIAGOPT(ShowSafeBufferUsageSuggestions, 1, 0) +// TO_UPSTREAM(BoundsSafety) +DIAGOPT(BoundsSafetyAdoptionMode, 1, 0) #undef DIAGOPT #undef ENUM_DIAGOPT diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td index f8d50d12bb935..fe600f15809d1 100644 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -340,7 +340,7 @@ def err_atimport : Error< def warn_atimport_in_framework_header : Warning< "use of '@import' in framework header is discouraged, " "including this header requires -fmodules">, - InGroup<FrameworkHdrAtImport>; + InGroup<FrameworkHdrAtImport>, DefaultIgnore; def err_invalid_reference_qualifier_application : Error< "'%0' qualifier may not be applied to a reference">; @@ -1122,6 +1122,11 @@ def err_availability_expected_platform : Error< def err_availability_expected_environment : Error< "expected an environment name, e.g., 'compute'">; +def err_features_domain_name : Error< + "expected a domain name">; +def err_feature_invalid_availability_check : Error< + "cannot pass a domain argument along with other arguments">; + // objc_bridge_related attribute def err_objcbridge_related_expected_related_class : Error< "expected a related Objective-C class name, e.g., 'NSColor'">; @@ -1260,9 +1265,6 @@ def warn_pragma_intrinsic_builtin : Warning< def warn_pragma_unused_expected_var : Warning< "expected '#pragma unused' argument to be a variable name">, InGroup<IgnoredPragmas>; -// - #pragma mc_func -def err_pragma_mc_func_not_supported : - Error<"#pragma mc_func is not supported">; // - #pragma init_seg def warn_pragma_init_seg_unsupported_target : Warning< "'#pragma init_seg' is only supported when targeting a " diff --git a/clang/include/clang/Basic/DiagnosticRefactoringKinds.td b/clang/include/clang/Basic/DiagnosticRefactoringKinds.td index 5446b32efbdd4..0c7da26de7aef 100644 --- a/clang/include/clang/Basic/DiagnosticRefactoringKinds.td +++ b/clang/include/clang/Basic/DiagnosticRefactoringKinds.td @@ -30,4 +30,29 @@ def err_refactor_extract_prohibited_expression : Error<"the selected " } +// On github swift-clang only; to be upstreamed: + +let CategoryName = "Rename Issue" in { +def err_rename_builtin_function : Error<"%0 is a builtin function that " + "cannot be renamed">; +def err_rename_sys_header : Error<"%0 cannot be renamed because it is " + "declared in a system header">; +def err_method_rename_override_sys_framework : Error<"method %0 cannot be " + "renamed because it overrides a method declared in a system framework">; +def err_rename_external_source_symbol : Error<"%0 is declared in a %1 file; " + "rename can be initiated in a %1 file only">; +} + +let CategoryName = "Refactoring Continuation Issue" in { + +def err_ref_continuation_missing_implementation : Error< + "no %select{implementation file|@implementation declaration}0 for the " + "selected %select{declaration|@interface}0 %1; please add one and run the " + "refactoring action again">; + +def err_implement_declared_methods_all_implemented : Error< + "the selected methods are already implemented">; + +} + } // end of Refactoring diagnostics diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index b8d97a6b14fe6..82e2341f5be6f 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -915,6 +915,7 @@ def warn_fortify_scanf_overflow : Warning< "%2, but the corresponding specifier may require size %3">, InGroup<FortifySource>; + def err_function_start_invalid_type: Error< "argument must be a function">; @@ -940,8 +941,35 @@ def warn_ptrauth_sign_null_pointer : def warn_ptrauth_auth_null_pointer : Warning<"authenticating a null pointer will almost certainly trap">, InGroup<PtrAuthNullPointers>; + +def err_ptrauth_disabled_target : + Error<"this target does not support pointer authentication">; + def err_ptrauth_string_not_literal : Error< "argument must be a string literal%select{| of char type}0">; +def err_ptrauth_type_disc_undiscriminated : Error< + "cannot pass undiscriminated type %0 to " + "'__builtin_ptrauth_type_discriminator'">; + +// __ptrauth qualifier +def err_ptrauth_qualifier_return : Error< + "return types may not be qualified with __ptrauth; type is %0">; +def err_ptrauth_qualifier_param : Error< + "parameter types may not be qualified with __ptrauth; type is %0">; +def err_ptrauth_qualifier_cast : Error< + "cast types may not be qualified with __ptrauth; type is %0">; +def err_ptrauth_qualifier_nonpointer : Error< + "__ptrauth qualifier may only be applied to pointer types; type here is %0">; +def err_ptrauth_qualifier_redundant : Error< + "type %0 is already __ptrauth-qualified">; +def err_ptrauth_qualifier_bad_arg_count : Error< + "__ptrauth qualifier must take between 1 and 3 arguments">; +def err_ptrauth_qualifier_arg_not_ice : Error< + "argument to __ptrauth must be an integer constant expression">; +def err_ptrauth_qualifier_address_discrimination_invalid : Error< + "address discrimination flag for __ptrauth must be 0 or 1; value is %0">; +def err_ptrauth_qualifier_extra_discriminator_invalid : Error< + "extra discriminator for __ptrauth must be between 0 and %1; value is %0">; def note_ptrauth_virtual_function_pointer_incomplete_arg_ret : Note<"cannot take an address of a virtual member function if its return or " @@ -1167,8 +1195,8 @@ def err_protocol_has_circular_dependency : Error< "protocol has circular dependency">; def err_undeclared_protocol : Error<"cannot find protocol declaration for %0">; def warn_undef_protocolref : Warning<"cannot find protocol definition for %0">; -def err_atprotocol_protocol : Error< - "@protocol is using a forward protocol declaration of %0">; +def warn_atprotocol_protocol : Warning< + "@protocol is using a forward protocol declaration of %0">, InGroup<AtProtocol>; def warn_readonly_property : Warning< "attribute 'readonly' of property %0 restricts attribute " "'readwrite' of property inherited from %1">, @@ -1595,6 +1623,12 @@ def warn_unimplemented_selector: Warning< InGroup<Selector>, DefaultIgnore; def warn_unimplemented_protocol_method : Warning< "method %0 in protocol %1 not implemented">, InGroup<Protocol>; +def warn_class_does_not_conform_protocol : Warning< + "%select{class|category}0 %1 does not conform to protocol" + "%plural{1: %3|2:s %3 and %4|3:s %3, %4 and %5|:s %3, %4, %5, ...}2">, + InGroup<Protocol>; +def note_add_missing_protocol_stubs : Note< + "add stubs for missing protocol requirements">; def warn_multiple_selectors: Warning< "several methods with selector %0 of mismatched types are found " "for the @selector expression">, @@ -3321,6 +3355,9 @@ def warn_attribute_return_pointers_refs_only : Warning< def warn_attribute_pointer_or_reference_only : Warning< "%0 attribute only applies to a pointer or reference (%1 is invalid)">, InGroup<IgnoredAttributes>; +def warn_attribute_pointer_or_reference_or_record_only : Warning< + "%0 attribute only applies to a pointer, reference, class, struct, or union (%1 is invalid)">, + InGroup<IgnoredAttributes>; def err_attribute_no_member_pointers : Error< "%0 attribute cannot be used with pointers to members">; def err_attribute_invalid_implicit_this_argument : Error< @@ -3352,6 +3389,20 @@ def err_callback_callee_is_variadic : Error< "'callback' attribute callee may not be variadic">; def err_callback_implicit_this_not_available : Error< "'callback' argument at position %0 references unavailable implicit 'this'">; + +def err_capture_by_attribute_multiple : Error< + "multiple 'lifetime_capture' attributes specified">; +def err_capture_by_attribute_no_entity : Error< + "'lifetime_capture_by' attribute specifies no capturing entity">; +def err_capture_by_implicit_this_not_available : Error< + "'lifetime_capture_by' argument references unavailable implicit 'this'">; +def err_capture_by_attribute_argument_unknown : Error< + "'lifetime_capture_by' attribute argument %0 is not a known function parameter" + "; must be a function parameter, 'this', 'global' or 'unknown'">; +def err_capture_by_references_itself : Error<"'lifetime_capture_by' argument references itself">; +def err_capture_by_param_uses_reserved_name : Error< + "parameter cannot be named '%select{global|unknown}0' while using 'lifetime_capture_by(%select{global|unknown}0)'">; + def err_init_method_bad_return_type : Error< "init methods must return an object pointer type, not %0">; def err_attribute_invalid_size : Error< @@ -3674,6 +3725,9 @@ def err_attribute_weak_static : Error< "weak declaration cannot have internal linkage">; def err_attribute_selectany_non_extern_data : Error< "'selectany' can only be applied to data items with external linkage">; +def warn_attribute_hybrid_patchable_non_extern : Warning< + "'hybrid_patchable' is ignored on functions without external linkage">, + InGroup<IgnoredAttributes>; def err_declspec_thread_on_thread_variable : Error< "'__declspec(thread)' applied to variable that already has a " "thread-local storage specifier">; @@ -3805,8 +3859,6 @@ def warn_sme_locally_streaming_has_vl_args_returns : Warning< InGroup<AArch64SMEAttributes>, DefaultIgnore; def err_conflicting_attributes_arm_state : Error< "conflicting attributes for state '%0'">; -def err_sme_streaming_cannot_be_multiversioned : Error< - "streaming function cannot be multi-versioned">; def err_unknown_arm_state : Error< "unknown state '%0'">; def err_missing_arm_state : Error< @@ -3863,7 +3915,8 @@ def note_cannot_use_trivial_abi_reason : Note< "its copy constructors and move constructors are all deleted|" "it is polymorphic|" "it has a base of a non-trivial class type|it has a virtual base|" - "it has a __weak field|it has a field of a non-trivial class type}1">; + "it has a __weak field|it has a field of a non-trivial class type|" + "it has an address-discriminated __ptrauth field}1">; // Availability attribute def warn_availability_unknown_platform : Warning< @@ -3907,6 +3960,28 @@ def err_availability_unexpected_parameter: Error< def warn_unguarded_availability : Warning<"%0 is only available %select{|in %4 environment }3on %1 %2 or newer">, InGroup<UnguardedAvailability>, DefaultIgnore; +def err_unguarded_feature : Error< + "use of %0 requires feature '%1' to be %select{available|unavailable}2">; +def err_label_in_conditionally_guarded_feature : Error< + "labels cannot appear in regions conditionally guarded by features">; +def err_features_invalid_for_decl : Error< + "feature attributes cannot be applied to %0">; +def err_features_invalid_name : Error< + "cannot find definition of feature attribute '%0'">; +def err_features_invalid__arg : Error< + "invalid argument %0: must evaluate to 0 or 1">; +def err_feature_invalid_for_decl : Error< + "feature attribute '%0(%1)' cannot be applied to this decl">; +def err_feature_merge_incompatible : Error< + "cannot merge incompatible feature attribute to this decl">; +def err_new_feature_redeclaration : Error< + "new feature attributes cannot be added to redeclarations">; +def err_feature_invalid_added : Error< + "cannot add feature availability to this decl">; +def note_feature_incompatible0 : Note< + "feature attribute %0">; +def note_feature_incompatible1 : Note< + "is incompatible with %0">; def warn_unguarded_availability_unavailable : Warning<"%0 is unavailable">, InGroup<UnguardedAvailability>, DefaultIgnore; @@ -4264,6 +4339,16 @@ def note_reference_is_return_value : Note<"%0 returns a reference">; def note_pointer_declared_here : Note< "pointer %0 declared here">; +/* TO_UPSTREAM(BoundsSafety) ON */ +def note_pointer_declared_here_quoted : Note< + "pointer '%0' declared here">; +def note_pointer_initialized_here : Note< + "pointer %0 initialized here">; +def note_pointer_assigned_here : Note< + "pointer %0 assigned here">; +def note_unnamed_pointer_declared_here : Note< + "pointer declared here">; +/* TO_UPSTREAM(BoundsSafety) OFF */ def warn_division_sizeof_ptr : Warning< "'%0' will return the size of the pointer, not the array itself">, InGroup<DiagGroup<"sizeof-pointer-div">>; @@ -4357,6 +4442,8 @@ def err_mismatched_visibility: Error<"visibility does not match previous declara def note_previous_attribute : Note<"previous attribute is here">; def note_conflicting_attribute : Note<"conflicting attribute is here">; def note_attribute : Note<"attribute is here">; +// TO_UPSTREAM(BoundsSafety) +def note_named_attribute : Note<"%0 attribute is here">; def err_mismatched_ms_inheritance : Error< "inheritance model does not match %select{definition|previous declaration}0">; def warn_ignored_ms_inheritance : Warning< @@ -4386,6 +4473,9 @@ def warn_attribute_nonnull_no_pointers : Warning< def warn_attribute_nonnull_parm_no_args : Warning< "'nonnull' attribute when used on parameters takes no arguments">, InGroup<IgnoredAttributes>; +def warn_attribute_noescape_non_pointer : Warning< + "'noescape' attribute ignored on parameter of non-pointer type %0">, + InGroup<IgnoredAttributes>; def warn_function_stmt_attribute_precedence : Warning< "statement attribute %0 has higher precedence than function attribute " "'%select{always_inline|flatten|noinline}1'">, @@ -4626,6 +4716,9 @@ def err_attr_swift_error_no_error_parameter : Error< def err_attr_swift_error_return_type : Error< "%0 attribute with '%1' convention can only be applied to a " "%select{function|method}2 returning %select{an integral type|a pointer}3">; +def warn_swift_newtype_attribute_non_typedef : Warning< + "'swift_newtype' attribute may be put on a typedef only; " + "attribute is ignored">, InGroup<DiagGroup<"swift-newtype-attribute">>; def err_swift_async_no_access : Error< "first argument to 'swift_async' must be either 'none', 'swift_private', or " @@ -4920,6 +5013,10 @@ def note_ovl_candidate_bad_ownership : Note< "%select{no|__unsafe_unretained|__strong|__weak|__autoreleasing}4 ownership," " but parameter has %select{no|__unsafe_unretained|__strong|__weak|" "__autoreleasing}5 ownership">; +def note_ovl_candidate_bad_ptrauth : Note< + "candidate %sub{select_ovl_candidate_kind}0,1,2 not viable: " + "%ordinal8 argument (%3) has %select{no ptrauth|%5}4 qualifier," + " but parameter has %select{no ptrauth|%7}6 qualifier">; def note_ovl_candidate_bad_cvr_this : Note< "candidate %sub{select_ovl_candidate_kind}0,1,2 not viable: " "'this' argument has type %3, but method is not marked " @@ -5998,7 +6095,7 @@ def note_deleted_special_member_class_subobject : Note< "%select{default|corresponding|default|default|default}4 constructor}0|" "destructor}5" "%select{||s||}4" - "|is an ObjC pointer}6">; + "|is an ObjC pointer|has an address-discriminated ptrauth qualifier}6">; def note_deleted_default_ctor_uninit_field : Note< "%select{default constructor of|constructor inherited by}0 " "%1 is implicitly deleted because field %2 of " @@ -7613,6 +7710,7 @@ def warn_format_nonliteral_noargs : Warning< def warn_format_nonliteral : Warning< "format string is not a string literal">, InGroup<FormatNonLiteral>, DefaultIgnore; +def err_format_nonliteral : Error<"format string is not a string literal">; def err_unexpected_interface : Error< "unexpected interface name %0: expected expression">; @@ -8804,6 +8902,19 @@ def err_typecheck_incompatible_ownership : Error< "sending to parameter of different type}0,1" "|%diff{casting $ to type $|casting between types}0,1}2" " changes retain/release properties of pointer">; +def err_typecheck_incompatible_ptrauth : Error< + "%select{%diff{assigning $ to $|assigning to different types}1,0" + "|%diff{passing $ to parameter of type $|" + "passing to parameter of different type}0,1" + "|%diff{returning $ from a function with result type $|" + "returning from function with different return type}0,1" + "|%diff{converting $ to type $|converting between types}0,1" + "|%diff{initializing $ with an expression of type $|" + "initializing with expression of different type}0,1" + "|%diff{sending $ to parameter of type $|" + "sending to parameter of different type}0,1" + "|%diff{casting $ to type $|casting between types}0,1}2" + " changes pointer-authentication of pointee type">; def err_typecheck_comparison_of_distinct_blocks : Error< "comparison of distinct block types%diff{ ($ and $)|}0,1">; @@ -9198,6 +9309,8 @@ def ext_typecheck_cond_pointer_integer_mismatch : ExtWarn< "pointer/integer type mismatch in conditional expression" "%diff{ ($ and $)|}0,1">, InGroup<DiagGroup<"conditional-type-mismatch">>; +def err_typecheck_cond_incompatible_ptrauth : Error< + "__ptrauth qualification mismatch%diff{ ($ and $)|}0,1">; def err_typecheck_choose_expr_requires_constant : Error< "'__builtin_choose_expr' requires a constant expression">; def warn_unused_expr : Warning<"expression result unused">, @@ -9918,6 +10031,8 @@ def note_previous_declaration_as : Note< def warn_printf_insufficient_data_args : Warning< "more '%%' conversions than data arguments">, InGroup<FormatInsufficientArgs>; +def warn_format_cmp_specifier_arity : Warning< + "%select{fewer|more}0 specifiers in format string than expected">, InGroup<FormatInsufficientArgs>; def warn_printf_data_arg_not_used : Warning< "data argument not used by format string">, InGroup<FormatExtraArgs>; def warn_format_invalid_conversion : Warning< @@ -10035,6 +10150,27 @@ def note_format_fix_specifier : Note<"did you mean to use '%0'?">; def note_printf_c_str: Note<"did you mean to call the %0 method?">; def note_format_security_fixit: Note< "treat the string as an argument to avoid this">; +def warn_format_string_type_incompatible : Warning< + "passing '%0' format string where '%1' format string is expected">, + InGroup<Format>; +def warn_format_cmp_role_mismatch : Warning< + "format argument is %select{a value|an indirect field width|an indirect " + "precision|an auxiliary value}0, but it should be %select{a value|an indirect " + "field width|an indirect precision|an auxiliary value}1">, InGroup<Format>; +def warn_format_cmp_modifierfor_mismatch : Warning< + "format argument modifies specifier at position %0, but it should modify " + "specifier at position %1">, InGroup<Format>; +def warn_format_cmp_sensitivity_mismatch : Warning< + "argument sensitivity is %select{unspecified|private|public|sensitive}0, but " + "it should be %select{unspecified|private|public|sensitive}1">, InGroup<Format>; +def warn_format_cmp_specifier_mismatch : Warning< + "format specifier '%0' is incompatible with '%1'">, InGroup<Format>; +def warn_format_cmp_specifier_sign_mismatch : Warning< + "signedness of format specifier '%0' is incompatible with '%1'">, InGroup<Format>; +def warn_format_cmp_specifier_mismatch_pedantic : Extension< + warn_format_cmp_specifier_sign_mismatch.Summary>, InGroup<FormatPedantic>; +def note_format_cmp_with : Note< + "comparing with this %select{specifier|format string}0">; def warn_null_arg : Warning< "null passed to a callee that requires a non-null argument">, @@ -10298,6 +10434,7 @@ def warn_missing_case : Warning<"%plural{" "3:enumeration values %1, %2, and %3 not handled in switch|" ":%0 enumeration values not handled in switch: %1, %2, %3...}0">, InGroup<Switch>; +def note_fill_in_missing_cases : Note<"add missing switch cases">; def warn_switch_default : Warning<"'switch' missing 'default' label">, InGroup<SwitchDefault>, DefaultIgnore; @@ -10928,19 +11065,68 @@ def warn_imp_cast_drops_unaligned : Warning< InGroup<DiagGroup<"unaligned-qualifier-implicit-cast">>; // Function effects +def warn_func_effect_violation : Warning< + "%select{function|constructor|destructor|lambda|block|member initializer of constructor}0 " + "with '%1' attribute " + "must not %select{allocate or deallocate memory|throw or catch exceptions|" + "have static local variables|use thread-local variables|access ObjC methods or properties}2">, + InGroup<FunctionEffects>, DefaultIgnore; +def note_func_effect_violation : Note< + "%select{function|constructor|destructor|lambda|block|member initializer}0 " + "cannot be inferred '%1' because it " + "%select{allocates or deallocates memory|throws or catches exceptions|" + "has a static local variable|uses a thread-local variable|" + "accesses an ObjC method or property}2">; +def warn_func_effect_calls_func_without_effect : Warning< + "%select{function|constructor|destructor|lambda|block|member initializer of constructor}0 " + "with '%1' attribute " + "must not call non-'%1' " + "%select{function|constructor|destructor|lambda|block}2 " + "'%3'">, + InGroup<FunctionEffects>, DefaultIgnore; +def note_func_effect_calls_func_without_effect : Note< + "%select{function|constructor|destructor|lambda|block|member initializer}0 " + "cannot be inferred '%1' because it calls non-'%1' " + "%select{function|constructor|destructor|lambda|block}2 " + "'%3'">; +def warn_func_effect_calls_expr_without_effect : Warning< + "%select{function|constructor|destructor|lambda|block|member initializer of constructor}0 " + "with '%1' attribute " + "must not call non-'%1' expression">, + InGroup<FunctionEffects>, DefaultIgnore; +def note_func_effect_call_extern : Note< + "declaration cannot be inferred '%0' because it has no definition in this translation unit">; +def note_func_effect_call_disallows_inference : Note< + "%select{function|constructor|destructor|lambda|block}0 " + "does not permit inference of '%1' because it is declared '%2'">; +def note_func_effect_call_indirect : Note< + "%select{virtual method|function pointer}0 cannot be inferred '%1'">; +def warn_perf_constraint_implies_noexcept : Warning< + "%select{function|constructor|destructor|lambda|block}0 " + "with '%1' attribute should be declared noexcept">, + InGroup<PerfConstraintImpliesNoexcept>, DefaultIgnore; + +// FIXME: It would be nice if we could provide fuller template expansion notes. +def note_func_effect_from_template : Note< + "in template expansion here">; +def note_func_effect_in_constructor : Note< + "in%select{| implicit}0 constructor here">; +def note_in_evaluating_default_argument : Note< + "in evaluating default argument here">; + // spoofing nonblocking/nonallocating def warn_invalid_add_func_effects : Warning< "attribute '%0' should not be added via type conversion">, - InGroup<FunctionEffects>; + InGroup<FunctionEffects>, DefaultIgnore; def warn_mismatched_func_effect_override : Warning< "attribute '%0' on overriding function does not match base declaration">, - InGroup<FunctionEffects>; + InGroup<FunctionEffects>, DefaultIgnore; def warn_mismatched_func_effect_redeclaration : Warning< "attribute '%0' on function does not match previous declaration">, - InGroup<FunctionEffects>; + InGroup<FunctionEffects>, DefaultIgnore; def warn_conflicting_func_effects : Warning< "effects conflict when merging declarations; kept '%0', discarded '%1'">, - InGroup<FunctionEffects>; + InGroup<FunctionEffects>, DefaultIgnore; def err_func_with_effects_no_prototype : Error< "'%0' function must have a prototype">; @@ -12117,6 +12303,840 @@ def warn_target_clone_no_impact_options : Warning<"version list contains entries that don't impact code generation">, InGroup<FunctionMultiVersioning>; +// TO_UPSTREAM(BoundsSafety) ON +let CategoryName = "BoundsSafety Pointer Attributes Issue" in { + +def err_bounds_safety_conflicting_pointer_attributes : Error< + "%select{array|pointer}0 cannot have more than one %select{bound|type|count|end|terminator}1 attribute">; +def note_bounds_safety_conflicting_pointer_attribute_args : Note< + "conflicting arguments for %select{count|end|terminator}0 were '%1' and '%2'">; +def warn_bounds_safety_duplicate_pointer_attributes : Warning< + "%select{array|pointer}0 annotated with %select{__unsafe_indexable|__bidi_indexable|__indexable|__single|__terminated_by}1 " + "multiple times. Annotate only once to remove this warning">, InGroup<BoundsSafetyRedundantAttribute>; +def err_bounds_safety_complete_array_with_count : Error< + "arrays with an explicit size decay to counted pointers and cannot also have " + "a count attribute">; +def warn_bounds_safety_promoting_incomplete_array_without_count : Warning< + "accessing elements of an unannotated incomplete array always fails at runtime">, InGroup<BoundsSafetyIncompleteArray>; +def err_bounds_safety_conflicting_count_bound_attributes : Error< + "pointer cannot be %0 and '__%select{bidi_indexable|indexable}1' at the same time">; +def err_bounds_safety_end_pointer_single : Error< + "end-pointer must be '__single'">; +def err_bounds_safety_conflicting_count_range_attributes : Error< + "pointer cannot have count and range at the same time">; +def err_flexible_array_member_passed_by_copy : Error< + "-fbounds-safety forbids passing %0 by copy because it has a flexible array " + "member">; +def err_bounds_safety_dynamic_count_function_call : Error< + "argument of '%select{__counted_by|__sized_by|__counted_by_or_null|__sized_by_or_null}0' attribute can only reference " + "function with 'const' attribute">; +def err_bounds_safety_dynamic_count_function_call_argument : Error< + "argument of function call '%0' in '%select{__counted_by|__sized_by|__counted_by_or_null|__sized_by_or_null}1' attribute is not a constant expression">; +def err_attribute_invalid_argument_expression_for_pointer_bounds_attribute : Error< + "invalid argument expression to bounds attribute">; +def err_invalid_decl_kind_bounds_safety_dynamic_count : Error< + "count expression %select{on struct field|in function declaration}0 may only " + "reference %select{other fields of the same struct|parameters of that function}0">; +def err_invalid_decl_kind_bounds_safety_union_count : Error< + "cannot use %0 on union fields">; +def err_multiple_coupled_decls_in_bounds_safety_dynamic_count : Error< + "multiple coupled declarations in a -fbounds-safety attribute are not supported yet">; +def err_attribute_argument_type_for_bounds_safety_count : Error< + "%0 attribute requires an integer type argument">; +def err_attribute_argument_type_for_bounds_safety_range : Error< + "%0 attribute requires a pointer type argument">; +def err_bounds_safety_function_pointers_cannot_be_indexable : Error< + "function pointers cannot be indexable">; +def err_bounds_safety_voidptr_must_use_byte_count : Error< + "void pointers must use a byte count attribute instead of an item count">; +def err_bounds_safety_array_decay_to_single : Error< + "parameter of array type %0 decays to a __single pointer, and will not allow " + "arithmetic">; +def note_bounds_safety_array_decay_use_count_annotation : Note< + "add a count attribute within the declarator brackets or convert the " + "parameter to a pointer with a count or size attribute">; +def err_nested_bounds_safety_pointer_attribute_mismatch : Error< + "%select{%diff{assigning to $ from $|assigning to incompatible nested pointer type}0,1" + "|%diff{passing $ to parameter of incompatible nested pointer type $|" + "passing to parameter of incompatible nested pointer type}0,1" + "|%diff{returning $ from a function with result of incompatible nested pointer type $|" + "returning from function with return of incompatible nested pointer type}0,1" + "|%diff{converting $ to incompatible nested pointer type $|" + "converting between incompatible nested pointer types}0,1" + "|%diff{initializing $ with an expression of incompatible nested pointer type $|" + "initializing with expression of incompatible nested pointer type}0,1" + "|%diff{sending $ to parameter of incompatible nested pointer type $|" + "sending to parameter of incompatible nested pointer type}0,1" + "|%diff{casting $ to incompatible nested pointer type $|" + "casting between incompatible nested pointer types}0,1}2; " + "use explicit cast to perform this conversion">; +def err_cond_expr_nested_bounds_safety_pointer_attribute_mismatch : Error< + "conditional expression evaluates %select{values with incompatible nested pointer types" + "|functions with incompatible bound attributes|values with mismatching __terminated_by attributes}2" + "%diff{ $ and $|}0,1">; +def err_typecheck_cond_incompatible_pointers : Error< + "conditional expression evaluates values with incompatible pointee types" + "%diff{ $ and $|}0,1; use explicit casts to perform this conversion">; +def err_bounds_safety_non_to_pointer : Error< + "non-pointer to safe pointer conversion is not allowed with -fbounds-safety; use " + "'__unsafe_forge_single' or '__unsafe_forge_bidi_indexable'">; +def err_incompatible_bounds_safety_function_pointer : Error< + "conversion between pointers to functions with incompatible bound attributes">; +def err_bounds_safety_unsafe_to_safe : Error< + "%select{" + "%diff{assigning to $ from incompatible type $|" + "assigning type from incompatible type}0,1|" + "%diff{passing $ to parameter of incompatible type $|" + "passing type to parameter of incompatible type}0,1|" + "%diff{returning $ from a function with incompatible result type $|" + "returning type from a function with incompatible result type}0,1|" + "%diff{converting $ to incompatible type $|" + "converting type to incompatible type}0,1|" + "%diff{initializing $ with an expression of incompatible type $|" + "initializing type with an expression of incompatible type}0,1|" + "%diff{sending $ to parameter of incompatible type $|" + "sending type to parameter of incompatible type}0,1|" + "%diff{casting $ to incompatible type $|" + "casting type to incompatible type}0,1|" + "}2 casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' " + "or '__unsafe_forge_bidi_indexable' to perform this conversion">; +// %2 is AssignmentAction enum +def err_bounds_safety_incomplete_single_to_indexable : Error< + "cannot %select{" + "assign to indexable pointer with type %0 from __single pointer to incomplete type %1%select{|; consider declaring pointer '%3' as '__single'}4|" // assign + "pass __single pointer to incomplete type argument %0 when parameter is an indexable pointer %1; consider making the%select{| '%3'}4 parameter '__single'|" // pass + "return __single pointer to incomplete type %0 when return type is an indexable pointer %1; consider making the return type '__single'|" // return + "convert __single pointer to incomplete type %0 to indexable pointer %1; consider making the destination '__single'|" // convert + "initialize indexable pointer with type %0 from __single pointer to incomplete type %1%select{|; consider declaring pointer '%3' as '__single'}4|" // initialize + "send __single to incomplete type %0 to indexable pointer %1; consider making the destination '__single'|" // send + "cast from __single pointer to incomplete type %0 to indexable pointer type %1" // cast + "}2">; +def warn_bounds_safety_implicit_conv_single_to_explicit_indexable : Warning< + "%select{" + "%diff{assigning to%select{ __indexable| __bidi_indexable|}3 type $ from%select{ __single|}4 type $|" + "assigning%select{ __indexable| __bidi_indexable|}3 type from%select{ __single|}4 type}0,1|" + "%diff{passing%select{ __single|}4 type $ to parameter of%select{ __indexable| __bidi_indexable|}3 type $|" + "passing%select{ __single|}4 type to parameter of%select{ __indexable| __bidi_indexable|}3 type}0,1|" + "%diff{returning%select{ __single|}4 type $ from a function with%select{ __indexable| __bidi_indexable|}3 result type $|" + "returning%select{ __single|}4 type from a function with%select{ __indexable| __bidi_indexable|}3 result type}0,1|" + "%diff{converting%select{ __single|}4 type $ to%select{ __indexable| __bidi_indexable|}3 type $|" + "converting%select{ __single|}4 type to%select{ __indexable| __bidi_indexable|}3 type}0,1|" + "%diff{initializing%select{ __indexable| __bidi_indexable|}3 type $ with an expression of%select{ __single|}4 type $|" + "initializing%select{ __indexable| __bidi_indexable|}3 type with an expression of%select{ __single|}4 type}0,1|" + "%diff{sending%select{ __single|}4 type $ to parameter of%select{ __indexable| __bidi_indexable|}3 type $|" + "sending%select{ __single|}4 type to parameter of%select{ __indexable| __bidi_indexable|}3 type}0,1|" + "%diff{casting%select{ __single|}4 type $ to%select{ __indexable| __bidi_indexable|}3 type $|" + "casting%select{ __single|}4 type to%select{ __indexable| __bidi_indexable|}3 type}0,1|" + "}2 results in %select{an __indexable|a __bidi_indexable}5 pointer that will trap if a non-zero offset " + "is dereferenced%select{|. consider adding '__counted_by' to '%7'}6">, + InGroup<BoundsAttributesImplicitConvSingleToExplicitIndexable>; +def warn_bounds_safety_conv_single_to_implicit_indexable : Warning< + "%select{indexing into|pointer arithmetic over}1 a __bidi_indexable local " + "variable %2 %select{assigned|initialized}3 from __single %select{" + "parameter|" // AssignmentSourceKind::Parameter + "global|" // AssignmentSourceKind::GlobalVar + "local variable|" // AssignmentSourceKind::LocalVar + "return value from call to|" // AssignmentSourceKind::FunctionCallReturnValue + "element from array|" // AssignmentSourceKind::ArrayElement + "struct member|" // AssignmentSourceKind::StructMember + "union member" // AssignmentSourceKind::UnionMember + "}0 %4 results in %select{" + "a trap|" // UnsafeOpKind::Index + "an out-of-bounds pointer" // UnsafeOpKind::Arithmetic + "}1" + "%select{" + "|" // PtrArithOOBKind:::ALWAYS_OOB_BASE_OOB + "|" // PtrArithOOBKind::ALWAYS_OOB_CONSTANT_OFFSET + " if the %select{index expression|offset}1 is >= %6|" // PtrArithOOBKind::OOB_IF_EFFECTIVE_OFFSET_GTE_MINIMUM_OOB_POSITIVE_OFFSET + " if the %select{index expression|offset}1 is >= %6 or < 0|" // PtrArithOOBKind::OOB_IF_EFFECTIVE_OFFSET_GTE_MINIMUM_OOB_POSITIVE_OFFSET_OR_LT_ZERO + " if the %select{index expression|offset}1 is < 0" // PtrArithOOBKind::OOB_IF_EFFECTIVE_OFFSET_LT_ZERO + "}5">, + InGroup<BoundsSafetyConvSingleToImplicitIndexable>; + +def warn_bounds_safety_conv_single_to_implicit_indexable_multiple_assignments : + Warning< "%select{indexing into|pointer arithmetic over}0 __bidi_indexable " + "local variable %1 that is assigned from a __single pointer results in " + "%select{" + "a trap|" // UnsafeOpKind::Index + "an out-of-bounds pointer" // UnsafeOpKind::Arithmetic + "}0" + "%select{" + "|" // PtrArithOOBKind:::ALWAYS_OOB_BASE_OOB + "|" // PtrArithOOBKind::ALWAYS_OOB_CONSTANT_OFFSET + " if the %select{index expression|offset}0 is >= %3|" // PtrArithOOBKind::OOB_IF_EFFECTIVE_OFFSET_GTE_MINIMUM_OOB_POSITIVE_OFFSET + " if the %select{index expression|offset}0 is >= %3 or < 0|" // PtrArithOOBKind::OOB_IF_EFFECTIVE_OFFSET_GTE_MINIMUM_OOB_POSITIVE_OFFSET_OR_LT_ZERO + " if the %select{index expression|offset}0 is < 0" // PtrArithOOBKind::OOB_IF_EFFECTIVE_OFFSET_LT_ZERO + "}2">, + InGroup<BoundsSafetyConvSingleToImplicitIndexable>; +def note_single_entity_assigned_here : Note< + "__single %select{" + "parameter|" // AssignmentSourceKind::Parameter + "global|" // AssignmentSourceKind::GlobalVar + "local variable|" // AssignmentSourceKind::LocalVar + "return value from call to|" // AssignmentSourceKind::FunctionCallReturnValue + "element from array|" // AssignmentSourceKind::ArrayElement + "struct member|" // AssignmentSourceKind::StructMember + "union member" // AssignmentSourceKind::UnionMember + "}0 %1 assigned to %2 here">; + +def warn_bounds_safety_conv_single_to_implicit_indexable_unsafe_zeroth_element : + Warning<"%select{" + "indexing|" // UnsafeOpKind::Index + "UNUSED|" // UnsafeOpKind::Arithmetic + "dereferencing|" // UnsafeOpKind::Deref + "accessing field %5 through|" // UnsafeOpKind::MemberAccess + "assigning from|" // UnsafeOpKind::Assignment + "returning|" // UnsafeOpKind::Return + "passing|" // UnsafeOpKind::CallArg + "UNUSED" // UnsafeOpKind::Cast + "}0 __bidi_indexable '%1'" + "%select{" + " at any index|||||||}0 " + "will %select{" + "access out-of-bounds memory and will trap in a future compiler version|" // UnsafeOpKind::Index + "UNUSED|" // UnsafeOpKind::Arithmetic + "access out-of-bounds memory and will trap in a future compiler version|" // UnsafeOpKind::Deref + "always trap|" // UnsafeOpKind::MemberAccess + "propagate an out-of-bounds pointer|" // UnsafeOpKind::Assignment + "return an out-of-bounds pointer|" // UnsafeOpKind::Return + "pass an out-of-bounds pointer|" // UnsafeOpKind::CallArg + "UNUSED" // UnsafeOpKind::Cast + "}0. At runtime %4 is assigned a __single pointer that" + " results in '%1' having bounds smaller than a single %2 (%3 byte%s3)">, + InGroup<BoundsSafetyConvSingleToImplicitIndexableThenConvertToLargerType>; + +def warn_bounds_safety_conv_single_to_implicit_indexable_unsafely_cast : + Warning< + "%select{explicit|implicit}0 cast of __bidi_indexable %1 to a larger pointee" + " type creates an out-of-bounds pointer. Later uses of the result may trap">, + InGroup<BoundsSafetyConvSingleToImplicitIndexableThenConvertToLargerType>; + +def warn_bounds_safety_conv_single_to_implicit_indexable_unsafely_cast_to_single_trap : + Warning< + "%select{explicit|implicit}0 cast of out-of-bounds __bidi_indexable to " + "__single will trap in a future compiler version due to the bounds of %1 " + "being too small to access a single element of type %2">, + InGroup<BoundsSafetyConvSingleToImplicitIndexableThenConvertToLargerType>; + +def note_single_entity_assigned_here_with_pointee_sizes : Note< + "__single %select{" + "parameter|" // AssignmentSourceKind::Parameter + "global|" // AssignmentSourceKind::GlobalVar + "local variable|" // AssignmentSourceKind::LocalVar + "return value from call to|" // AssignmentSourceKind::FunctionCallReturnValue + "element from array|" // AssignmentSourceKind::ArrayElement + "struct member|" // AssignmentSourceKind::StructMember + "union member" // AssignmentSourceKind::UnionMember + "}0 %1 %select{assigned to|used to initialize}9 %2 here results in %2 having the bounds of a single %3 " + "(%4 bytes) but %select{'%8'|cast of %2 to %8}7 has pointee type %5 (%6 bytes)">; + +def warn_bounds_safety_conv_single_to_implicit_indexable_dynamic_count_conversion : + Warning< + "%select{" + "UNUSED|" // UnsafeOpKind::Index + "UNUSED|" // UnsafeOpKind::Arithmetic + "UNUSED|" // UnsafeOpKind::Deref + "UNUSED|" // UnsafeOpKind::MemberAccess + "assigning from|" // UnsafeOpKind::Assignment + "returning|" // UnsafeOpKind::Return + "passing|" // UnsafeOpKind::CallArg + "casting" // // UnsafeOpKind::Cast + "}0 __bidi_indexable local variable %1 will %select{" + "UNUSED|" // WillTrapKind::NoTrap + "likely trap|" // WillTrapKind::Unknown + "trap|" // WillTrapKind::Trap + "trap (unless %1 is null)|" // WillTrapKind::TrapIffPtrNotNull + "UNUSED" // WillTrapKind::TrapIffPtrNull + "}2%select{" + " |" + " in a future compiler version " + "}6" + "when converting to %3 due to %1 having the bounds of a __single pointer" + "%select{" + // Non-constant count + ". If %1 is non-null then any %select{" + "count|" // __counted_by(or_null) + "size" // __sized_by(or_null) + "}7 > %8 will trap" + "%select{" + // __counted_by/__sized_by + ". If %1 is null then any %select{count|size}7 != 0 will trap|" + // __counted_by_or_null/__sized_by_or_null + "" + "}5|" + // Constant count + "" + "}4">, + InGroup<BoundsSafetyConvSingleToImplicitIndexableThenConvertToDynamicCountPtr>; + + +def note_single_entity_assigned_here_with_dyn_count_conversion: Note< + "__single %select{" + "parameter|" // AssignmentSourceKind::Parameter + "global|" // AssignmentSourceKind::GlobalVar + "local variable|" // AssignmentSourceKind::LocalVar + "return value from call to|" // AssignmentSourceKind::FunctionCallReturnValue + "element from array|" // AssignmentSourceKind::ArrayElement + "struct member|" // AssignmentSourceKind::StructMember + "union member" // AssignmentSourceKind::UnionMember + "}0 %1 assigned to %2 here results in %2 having the bounds of a single %3 " + "(%4 bytes)" + "%select{" + // non-constant count + "|" + // constant count + " but conversion of %2 to %6 requires %7 bytes or more" + "}5">; + +def err_bounds_safety_dynamic_bound_redeclaration : Error< + "conflicting '%select{__counted_by|__sized_by|__counted_by_or_null|__sized_by_or_null|__ended_by|__terminated_by}0' " + "attribute with the previous %select{function|variable}1 declaration">; +def err_bounds_safety_nullable_fam : Error< + "%select{array objects|flexible array members}1 cannot be null; did you mean %select{__counted_by|__sized_by}0 instead?">; +def warn_bounds_safety_nullable_dynamic_count_nonnullable : Warning< + "combining %select{'__counted_by_or_null'|'__sized_by_or_null'}0 and '_Nonnull'; did you mean %select{'__counted_by'|'__sized_by'}0 instead?">, InGroup<BoundsSafetyNullability>; +def warn_bounds_safety_nonnullable_dynamic_count_nullable : Warning< + "combining %select{'__counted_by'|'__sized_by'}0 with non-zero %select{count|size}0 (which cannot be null) and '_Nullable'; did you mean %select{'__counted_by_or_null'|'__sized_by_or_null'}0 instead?">, InGroup<BoundsSafetyNullability>; +def err_bounds_safety_nullable_dynamic_count_nonnullable : Error< + "cannot combine %select{'__counted_by_or_null'|'__sized_by_or_null'}0 and %1; did you mean %select{'__counted_by'|'__sized_by'}0 instead?">; + +def warn_bounds_safety_single_bitcast_lose_bounds : Warning< + "casting %0 to %1 creates a '%select{__indexable|__bidi_indexable}2' pointer " + "%select{with bounds containing only one %3|with zero length due to %3 having unknown size)}4">, + InGroup<BoundsSafetyConvertSingleToIndexableBoundsTruncated>; + +def note_bounds_safety_single_bitcast_lose_bounds_keep_bound : Note< + "cast to %0 first to keep bounds of %1">; + +def note_bounds_safety_single_bitcast_lose_bounds_silence : Note< + "silence by making the destination '__single'">; + +def err_bounds_safety_atomic_unsupported_attribute : Error< + "_Atomic on " + "'%select{__indexable|__bidi_indexable|__counted_by|__sized_by|__counted_by_or_null|__sized_by_or_null|__ended_by|__terminated_by|end}0' " + "pointer is not yet supported">; +def err_bounds_safety_atomic_op_unsupported_attribute : Error< + "atomic operation on " + "'%select{__indexable|__bidi_indexable|__counted_by|__sized_by|__counted_by_or_null|__sized_by_or_null|__ended_by}0' " + "pointer is not yet supported">; +def err_bounds_safety_atomic_op_arithmetic_needs_unsafe_pointer : Error< + "address argument to atomic arithmetic operation must be a pointer to " + "'__unsafe_indexable' pointer (%0 invalid)">; + +def err_bounds_safety_typeof_dbpt : Error< + "__typeof__ on an expression of type %0 is not yet supported">; + +def err_bounds_safety_single_pointer_arithmetic : Error< + "pointer arithmetic on single pointer '%0' is out of bounds%select{|; consider adding '__counted_by' to '%2'}1">; +def err_bounds_safety_flexible_array_member_record_pointer_arithmetic : Error< + "-fbounds-safety forbids arithmetic on pointers to types with a flexible array " + "member">; +def err_bounds_safety_member_reference_null_base : Error< + "base of member reference is a null pointer">; +def err_bounds_safety_counted_by_without_size : Error< + "cannot apply '__counted_by%select{|_or_null}2' attribute to %0 because %1 has unknown size" + "%select{|; did you mean to use '__sized_by%select{|_or_null}2' instead?}3">; +def err_bounds_safety_counted_by_on_incomplete_type_on_use : Error < + "cannot %select{" + "use '%1'|" // Generic expr + "call '%1'" // CallExpr + "}0 with%select{" + "|" // Generic expr + " return" // CallExpr + "}0 type %2 because the pointee type %3 is incomplete " + "and the '%4' attribute requires the pointee type be complete in this context; " + "consider providing a complete definition for %3 or using the " + "'__sized_by%select{|_or_null}5' attribute" +>; + +def err_bounds_safety_counted_by_on_incomplete_type_on_assign : Error < + "cannot %select{" + "assign to %select{object|'%1'}2 that has|" // AA_Assigning, + "pass argument to %select{parameter|parameter '%1'}2 that has|" // AA_Passing, + "return|" // AA_Returning, + "convert to|" // AA_Converting (UNUSED) + "%select{|implicitly }3initialize %select{object|'%1'}2 that has|" // AA_Initializing, + "pass argument to parameter that has|" // AA_Sending (UNUSED) + "cast to|" // AA_Casting (UNUSED) + "pass argument to parameter that has" // AA_Passing_CFAudited (UNUSED) + "}0 type %4 because the pointee type %6 is incomplete and the '%5' attribute " + "requires the pointee type be complete when %select{" + "assigning|" // AA_Assigning, + "passing|" // AA_Passing, + "returning|" // AA_Returning, + "converting|" // AA_Converting (UNUSED) + "initializing|" // AA_Initializing, + "passing|" // AA_Sending (UNUSED) + "casting|" // AA_Casting (UNUSED) + "passing" // AA_Passing_CFAudited (UNUSED) + "}0; " + "consider providing a complete definition for %6 or using the " + "'__sized_by%select{|_or_null}7' attribute" +>; + +def err_bounds_safety_counted_by_on_incomplete_type_on_func_def : Error< + "cannot apply '%0' attribute to %select{" + "return|" // return type + "parameter%select{" // parameter type + "|" // unnamed parameter + " '%3'" // named parameter + "}2 with" + "}1 type %4 on a function definition because the pointee type %5 is " + "incomplete; consider providing a complete definition for %5 before the " + "function body or using the '__sized_by%select{|_or_null}6' attribute" +>; + +def err_bounds_safety_counted_by_on_incomplete_type_on_var_decl : Error< + "cannot apply '%0' attribute to%select{| tentative}1 variable definition " + "'%2' with type %3 because the pointee type %4 is " + "incomplete; consider providing a complete definition for %4 before this " + "definition or using the '__sized_by%select{|_or_null}5' attribute" +>; + +def err_bounds_safety_pointer_subscript : Error< + "array subscript on %select{single|'__terminated_by'}0 pointer '%1' " + "%select{must use a constant index of 0 to be in bounds|is not allowed}0">; +def err_bounds_safety_indexable_pointer_arithmetic : Error< + "decremented indexable pointer '%0' is out of bounds">; +def err_bounds_safety_indexable_pointer_subscript : Error< + "array subscript with a negative index on indexable pointer '%0' is out of bounds">; +def err_bounds_safety_terminated_by_pointer_arithmetic_dec : Error< + "cannot decrement '__terminated_by' pointer '%0'">; +def err_bounds_safety_terminated_by_pointer_arithmetic : Error< + "pointer arithmetic on '__terminated_by' pointer '%0' can only increase the " + "value by one">; + +def err_bounds_safety_dynamic_bound_pointer_unary_arithmetic : Error< + "%select{negative|positive}0 pointer arithmetic on " + "%select{'__counted_by' |'__sized_by' |'__counted_by_or_null' |'__sized_by_or_null' |end |}1pointer" + "%select{||||| that starts the '__ended_by' chain}1 always traps">; + +// TODO: This should be an Error, not a Warning (rdar://138902282) +def warn_bounds_safety_count_attr_pointer_unary_arithmetic_constant_count : Warning< + "%select{negative|positive}0 pointer arithmetic on " + "'%1' attributed pointer with constant %select{count|size}2 " + "%select{of %4|%4}3 always traps">, + InGroup<BoundsSafetyCountAttrArithConstantCount>, DefaultError; + +// TODO: This should be an Error, not a Warning (rdar://138902282) +def warn_bounds_safety_count_attr_pointer_binary_assign_constant_count : Warning< + "compound %select{subtraction|addition}0-assignment on " + "'%1' attributed pointer with constant %select{count|size}2 " + "%select{of %4|%4}3 always traps">, + InGroup<BoundsSafetyCountAttrArithConstantCount>, DefaultError; + +def err_bounds_safety_dependent_vars_assign_side_effect : Error< + "assignments to dependent variables should not have side effects between them">; +def err_bounds_safety_missing_dependent_var_assignment : Error< + "assignment to %select{%2 |}3'%0' requires corresponding assignment to %select{|%2 }3'%1'; " + "add self assignment '%1 = %1' if the value has not changed">; +def err_bounds_safety_no_preceding_fam_base_assignment : Error< + "assignment to '%0' requires an %select{|immediately preceding }2assignment to '%1' with a wide pointer%select{ immediately preceding the group of dependent field assignments|}2">; +def note_bounds_safety_flex_base_assign : Note< + "'%0' is initialized with a '__single' pointer">; +def note_bounds_safety_first_dep_assign: Note< + "group of dependent field assignments starts here, place assignment to '%0' before it">; +def err_bounds_safety_multiple_assignments_to_dynamic_bound_decl : Error< + "multiple consecutive assignments to a %select{dynamic count|dynamic count pointer|ranged pointer}0 %1 " + "must be simplified; keep only one of the assignments">; +def note_bounds_safety_previous_assignment : Note< + "previously assigned here">; +def err_bounds_safety_non_adjacent_dependent_var_decl : Error< + "local variable %0 must be declared right next to its dependent decl">; +def err_bounds_safety_dependent_out_count_assign : Error< + "not allowed to change out parameter used as dependent count expression of other parameter or return type">; +def err_bounds_safety_dependent_out_count_buf_assign : Error< + "not allowed to change out parameter with dependent count">; +def err_bounds_safety_read_only_dynamic_count_pointer : Error< + "parameter '%0' with '%select{__counted_by|__sized_by|__counted_by_or_null|__sized_by_or_null}1' attribute depending " + "on an indirect count is implicitly read-only">; +def err_bounds_safety_read_only_dynamic_range_pointer : Error< + "parameter '%0' %select{with '__ended_by' attribute depending " + "on an indirect end pointer|referred to by an indirect '__ended_by' pointer}1 " + "is implicitly read-only">; +def err_bounds_safety_read_only_dependent_param_in_return : Error< + "parameter '%0' is implicitly read-only due to being used by the " + "'%select{__counted_by|__sized_by|__counted_by_or_null|__sized_by_or_null|__ended_by}1' " + "attribute in the return type of '%2' (%3)">; +def err_bounds_safety_cannot_pass_read_only_dynamic_bound_pointer : Error< + "parameter '%0' with '%select{__counted_by|__sized_by|__counted_by_or_null|__sized_by_or_null|__ended_by}1' attribute " + "depending on an indirect count is implicitly read-only and cannot be passed " + "as an indirect argument">; +def err_bounds_safety_cannot_pass_read_only_dependent_param_in_return : Error< + "parameter '%0' is implicitly read-only and cannot be passed as an indirect " + "argument due to being used by the " + "'%select{__counted_by|__sized_by|__counted_by_or_null|__sized_by_or_null|__ended_by}1' " + "attribute in the return type of '%2' (%3)">; +def err_deref_in_bounds_safety_count_non_parm_decl : Error< + "dereference operator in '%select{__counted_by|__sized_by|__counted_by_or_null|__sized_by_or_null|__ended_by}0' is only allowed for function parameters">; +def err_bounds_safety_dynamic_bound_arg_different_scope : Error< + "argument of '%select{__counted_by|__sized_by|__counted_by_or_null|__sized_by_or_null|__ended_by}0' attribute cannot " + "refer to declaration from a different scope">; +def err_bounds_safety_dynamic_bound_arg_different_lifetime : Error< + "argument of '%select{__counted_by|__sized_by|__counted_by_or_null|__sized_by_or_null|__ended_by}0' attribute cannot " + "refer to declaration of a different lifetime">; +def err_bounds_safety_global_dynamic_bound : Error< + "pointer with '%select{__counted_by|__sized_by|__counted_by_or_null|__sized_by_or_null|__ended_by}0' " + "and the argument of the attribute must be defined in the same translation unit">; +def warn_bounds_safety_extern_array_dynamic_count : Warning< + "array with '%select{__counted_by|__sized_by}0' " + "and the argument of the attribute should be defined in the same translation unit">, + InGroup<BoundsAttributesExternArrayCount>; +def err_bounds_safety_local_dependent_count_block : Error< + "local dependent count decl should be declared side by side to its dependent pointer">; +def err_bounds_safety_external_bound_share : Error< + "%select{variable|field}1 %0 referred to by %select{%select{__counted_by|__sized_by|__counted_by_or_null|__sized_by_or_null|__ended_by}2 variable|flexible array member}1 cannot be used in other dynamic bounds attributes">; +def err_bounds_safety_nested_dynamic_bound : Error< + "%0 attribute on nested pointer type is only allowed on indirect parameters">; +def err_bounds_safety_nested_dynamic_range : Error< + "%0 is not allowed for nested pointers">; +def err_bounds_safety_increment_dynamic_count : Error< + "incrementing '%0' without updating '%1' always traps">; +def err_bounds_safety_increment_dynamic_count_nodep : Error< + "incrementing '%0' always traps">; +def err_bounds_safety_dynamic_bound_pointer_compound_assign_side_effect : Error< + "compound assignment on dynamic bound pointer type %0 with side effects is not yet supported;" + "instead, use seperate assignment and binary expressions">; +def err_bounds_safety_dependent_assignments_order : Error< + "cannot reference '%0' after it is changed during consecutive assignments">; +def note_bounds_safety_decl_assignment : Note< + "'%0' has been assigned here">; +def err_bounds_safety_dependent_struct_assignment : Error< + "cannot assign '%0' because it contains field %1 referred to by flexible array member %2">; +def note_bounds_safety_count_param_loc : Note< + "referred to by count parameter here">; +def err_bounds_safety_dependent_field_duplicates : Error< + "cannot depend on nested field %0 because it exists in multiple instances in struct %1">; +def note_bounds_safety_struct_fields_only_in_fam : Note< + "nested struct member in count parameter only supported for flexible array members">; +def error_bounds_safety_no_arrow_members : Error< + "arrow notation not allowed for struct member in count parameter">; +def error_bounds_safety_no_count_in_unions : Error< + "count parameter refers to union '%0' of type %1">; + +def err_bounds_safety_taking_address_dynamic_bound_dependent : Error< + "%select{variable|field}1 referred to by '%select{__counted_by|__sized_by|__counted_by_or_null|__sized_by_or_null|__ended_by}0' " + "cannot be pointed to by any other variable; exception is when the pointer " + "is passed as a compatible argument to a function">; +def err_bounds_safety_taking_address_dynamic_bound_pointer : Error< + "%select{pointer|array}1 with '%select{__counted_by|__sized_by|__counted_by_or_null|__sized_by_or_null|__ended_by}0' cannot be " + "pointed to by any other variable; exception is when the variable is passed " + "as a compatible argument to a function">; + +def err_bounds_safety_incompatible_dynamic_count_argument: Error< + "incompatible dynamic count pointer argument to parameter of type %0">; +def err_bounds_safety_incompatible_indirect_count_parameter: Error< + "passing%select{| address of}1 %0 referred to by " + "'%select{__counted_by|__sized_by|__counted_by_or_null|__sized_by_or_null}2' to a parameter that is not referred to " + "by the same attribute">; +def err_bounds_safety_incompatible_dynamic_bound_argument: Error< + "type of %0, %1, is incompatible with parameter of type %2">; +def err_bounds_safety_mismatching_count_type_argument: Error< + "incompatible pointer types assigning %0 with an expression with mismatching size attributes %1">; +def err_bounds_safety_incompatible_count_expression: Error< + "incompatible count expression (%0) vs. (%1) in argument to function">; +def err_bounds_safety_unsynchronized_indirect_param: Error< + "passing%select{| address of}1 %0 as an indirect parameter; " + "must also pass %2 %select{|or its address }3because the type of %select{%0|%2}4, " + "%5, refers to %select{%2|%0}4">; + +def err_bounds_safety_dynamic_count_negative : Error< + "negative %select{count|size}1 value of %2 for%select{| '%4' of type}3 %0">; +def err_bounds_safety_dynamic_count_from_array_bad_size : Error< + "%select{" + "assigning to%select{| '%7' of type}6 %1 with %select{count|size}2 value of %4 from array %3 (which has %5 %select{elements|bytes}2)|" + "passing array %3 (which has %5 %select{elements|bytes}2) to parameter%select{| '%7'}6 of type %1 with %select{count|size}2 value of %4|" + "returning array %3 (which has %5 %select{elements|bytes}2) from a function with result type %1 and %select{count|size}2 value of %4|" + "converting array %3 (which has %5 %select{elements|bytes}2) to type %1 with %select{count|size}2 value of %4|" + "initializing%select{| '%7' of type}6 %1 and %select{count|size}2 value of %4 with array %3 (which has %5 %select{elements|bytes}2)|" + "sending array %3 (which has %5 %select{elements|bytes}2) to parameter%select{| '%7'}6 of type %1 with %select{count|size}2 value of %4|" + "casting array %3 (which has %5 %select{elements|bytes}2) to type %1 with %select{count|size}2 value of %4" + "}0 always fails">; +def err_bounds_safety_dynamic_count_from_unbounded_bad_size : Error< + "%select{" + "assigning to%select{| '%7' of type}6 %1 with %select{count|size}2 value of %4 from %3%select{| with pointee of size %5}2|" + "passing %3%select{| with pointee of size %5}2 to parameter%select{| '%7'}6 of type %1 with %select{count|size}2 value of %4|" + "returning %3%select{| with pointee of size %5}2 from a function with result type %1 and %select{count|size}2 value of %4|" + "converting %3%select{| with pointee of size %5}2 to type %1 with %select{count|size}2 value of %4|" + "initializing%select{| '%7' of type}6 %1 and %select{count|size}2 value of %4 with %3%select{| and pointee of size %5}2|" + "sending %3%select{| with pointee of size %5}2 to parameter%select{| '%7'}6 of type %1 with %select{count|size}2 value of %4|" + "casting %3%select{| with pointee of size %5}2 to type %1 with %select{count|size}2 value of %4" + "}0 always fails">; +def err_bounds_safety_dynamic_count_from_null_nonzero_count : Error< + "%select{" + "assigning null to%select{| '%5' of type}4 %1 with %select{count|size}2 value of %3|" + "passing null to parameter%select{| '%5'}4 of type %1 with %select{count|size}2 value of %3|" + "returning null from a function with result type %1 and %select{count|size}2 value of %3|" + "converting null to type %1 with %select{count|size}2 value of %3|" + "%select{|implicitly }6initializing%select{| '%5' of type}4 %1 and %select{count|size}2 value of %3 with null|" + "sending null to parameter%select{| '%5'}4 of type %1 with %select{count|size}2 value of %3|" + "casting null to type %1 with %select{count|size}2 value of %3" + "}0 always fails">; + +def warn_bounds_safety_dynamic_count_from_nonnull_implicit_zero_count : Warning< + "possibly %select{" + "assigning non-null to%select{| '%4' of type}3 %1 with implicit %select{count|size}2 value of 0|" + "passing non-null to parameter%select{| '%4'}3 of type %1 with implicit %select{count|size}2 value of 0|" + "returning non-null from a function with result type %1 and implicit %select{count|size}2 value of 0|" + "converting non-null to type %1 with implicit %select{count|size}2 value of 0|" + "initializing%select{| '%4' of type}3 %1 and implicit %select{count|size}2 value of 0 with non-null|" + "sending non-null to parameter%select{| '%4'}3 of type %1 with implicit %select{count|size}2 value of 0|" + "casting non-null to type %1 with implicit %select{count|size}2 value of 0" + "}0, which creates a non-dereferenceable pointer; " + "explicitly set %select{count|size}2 value to 0 to remove this warning">, + InGroup<BoundsSafetyInitList>; +def warn_bounds_safety_dynamic_count_from_nonnull_negative_count : Warning< + "possibly %select{" + "assigning non-null to%select{| '%5' of type}4 %1 with %select{count|size}2 value of %3|" + "passing non-null to parameter%select{| '%5'}4 of type %1 with %select{count|size}2 value of %3|" + "returning non-null from a function with result type %1 with %select{count|size}2 value of %3|" + "converting non-null to type %1 with %select{count|size}2 value of %3|" + "initializing%select{| '%5' of type}4 %1 and %select{count|size}2 value of %3 with non-null|" + "sending non-null to parameter%select{| '%5'}4 of type %1 with %select{count|size}2 value of %3|" + "casting non-null to type %1 with %select{count|size}2 value of %3" + "}0; explicitly %select{assign|pass|return|convert|initialize|send|cast}0 null to remove this warning">, + InGroup<BoundsSafetyInitList>, DefaultError; + +def warn_bounds_safety_dynamic_count_from_unbounded : Warning< + "%select{count|size}2 value is not statically known: " + "%select{" + "assigning to%select{| '%6' of type}5 %1 from %4|" + "passing %4 to parameter%select{| '%6'}5 of type %1|" + "returning %4 from a function with result type %1|" + "converting %4 to type %1|" + "initializing%select{| '%6' of type}5 %1 with %4|" + "sending %4 to parameter%select{| '%6'}5 of type %1|" + "casting %4 to type %1" + "}0 is invalid for any %select{count other than 0 or 1|size greater than %7}2" + "%select{| unless the pointer is null}3">, + InGroup<BoundsSafetySingleToCount>; +def note_bounds_safety_dynamic_count_from_unbounded_count_location : Note< + "%select{count|size}1 %select{assigned|passed|returned|converted|initialized|sent|cast}0 here">; +def note_bounds_safety_consider_adding_to : Note< + "consider adding '%1' to '%0'">; +def note_bounds_safety_consider_adding_to_return : Note< + "consider adding '%1' to %0 return type of '%2'">; + +def warn_bounds_safety_initlist_range_partial_null : Warning< + "%select{|implicitly }0initializing field %1 of type %2 to NULL while " + "'%3' is initialized with a value rarely succeeds">, InGroup<BoundsSafetyInitListPartialNull>; + +def err_bounds_safety_dynamic_bound_init_side_effect : Error< + "initalizer for %select{count|size|'__counted_by' pointer|'__sized_by' pointer|'__counted_by_or_null' pointer|'__sized_by_or_null' pointer|'__ended_by' pointer|end pointer}0 with side effects is not yet supported">; + +def warn_bounds_safety_struct_init_side_effect : Warning< + "initializer %0 has a side effect; this may lead to an unexpected result " + "because the evaluation order of initialization list expressions is " + "indeterminate">, InGroup<BoundsAttributesInitSideEffect>; + +def err_bounds_safety_flexible_global_wrong_count : Error< + "flexible array member is initialized with %0 element%select{|s}2, but count value is " + "initialized to %1">; +def err_bounds_safety_flexible_global_not_counted : Error< + "flexible array member is initialized without a count">; +def err_bounds_safety_flexible_global_non_int_count_init : Error< + "count '%0' has non-integer value '%1' of type %2">; + +def err_bounds_safety_local_external_dynamic_count : Error< + "attribute %0 is not allowed for local variables with external storage">; +def err_bounds_safety_scope_dynamic_range : Error< + "attribute %0 is only allowed for struct fields or function parameters">; +def err_bounds_safety_typedef_dynamic_bound : Error< + "%0 inside typedef is only allowed for function type">; +def err_bounds_safety_sized_by_array : Error< + "%0 cannot apply to arrays: use 'counted_by' instead">; +def err_bounds_safety_forge_addr_type : Error< + "'__unsafe_forge_%select{bidi_indexable|single|terminated_by}0' requires a pointer, array " + "or integer address argument">; +def err_bounds_safety_forge_bidi_size_type : Error< + "'__unsafe_forge_bidi_indexable' requires an integer size argument">; +def err_bounds_safety_forge_negative : Error< + "negative %select{address|size}0 argument to '__unsafe_forge_" + "%select{bidi_indexable|single|terminated_by}1'">; + +def err_bounds_safety_abi_ptr_attr_invalid : Error< + "%0 cannot be set as a default pointer attribute">; + +def err_bounds_safety_no_bounds : Error< + "cannot extract the %select{lower|upper}0 bound of %1 because %select{" + "it is not a pointer|it has no bounds specification|" + "its bounds are not contained within the pointer}2">; + +def err_bounds_safety_ptr_with_bounds_in_assembly : Error< + "pointer with bounds cannot be used with inline assembly">; +def err_bounds_safety_dyn_count_in_assembly : Error< + "external count of a pointer cannot be used with inline assembly">; + +def err_bounds_safety_ptrauth_on_indexable_pointer : Error< + "pointer authentication is currently unsupported on indexable pointers">; + +def err_bounds_safety_auto_dynamic_bound : Error< + "passing %select{'__counted_by'|'__sized_by'|'__counted_by_or_null'|'__sized_by_or_null'|'__ended_by'|end}0 pointer " + "as __auto_type initializer is not yet supported">; + +def err_bounds_safety_unsupported_address_of_incomplete_array : Error< + "cannot take address of incomplete __counted_by array">; +def note_bounds_safety_remove_address_of_operator : Note< + "remove '&' to get address as %0 instead of %1">; +def err_bounds_safety_unsupported_address_of_incomplete_array_type : Error< + "pointer to incomplete __counted_by array type %0 not allowed; " + "did you mean to use a nested pointer type?">; +def err_bounds_safety_unsupported_address_of_incomplete_array_count : Error< + "cannot take address of field referred to by __counted_by on a flexible array member">; + +def err_bounds_safety_terminated_by_wrong_type : Error< + "'__terminated_by' attribute can be applied to pointers, constant-length " + "arrays or incomplete arrays">; +def err_bounds_safety_terminated_by_wrong_pointer_type : Error< + "'__terminated_by' attribute currently can be applied only to '__single' " + "pointers">; +def err_bounds_safety_terminated_by_wrong_elem_or_pointee_type : Error< + "%select{element|pointee}0 type of %select{array|pointer}0 with " + "'__terminated_by' attribute must be an integer or a non-wide pointer">; +def err_bounds_safety_terminated_by_empty_array : Error< + "'__terminated_by' attribute cannot be applied to empty arrays">; + +def err_bounds_safety_terminated_by_terminator_must_be_const : Error< + "terminator value is not a constant expression">; + +def err_bounds_safety_terminated_by_to_indexable_wrong_ptr_type : Error< + "pointer argument must be a '__terminated_by' pointer (%0 invalid)">; +def err_bounds_safety_terminated_by_to_indexable_wrong_terminator : Error< + "pointer argument must be terminated by %0 (got %1)">; + +def err_bounds_safety_terminated_by_from_indexable_wrong_ptr_type : Error< + "pointer argument must be a safe pointer (%0 invalid)">; +def err_bounds_safety_terminated_by_from_indexable_wrong_pointee_type : Error< + "pointee type of the pointer argument must be an integer or a non-wide " + "pointer">; +def err_bounds_safety_terminated_by_from_indexable_wrong_ptr_to_term_type : Error< + "pointer to terminator argument must be a pointer (%0 invalid)">; +def err_bounds_safety_terminated_by_from_indexable_pointee_mismatch : Error< + "pointee types of the pointer and pointer to terminator arguments must be " + "the same">; + +def err_bounds_safety_incompatible_string_literal_to_terminated_by : Error< + "'__terminated_by' pointer converted from a string literal must be " + "NUL-terminated">; + +def note_fixit_null_terminated_to_indexable: Note<"consider using '%select{__null_terminated_to_indexable|__unsafe_null_terminated_to_indexable}0()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound %select{excludes|includes}0 the null terminator">; +def note_fixit_unsafe_null_terminated_from_indexable : Note<"consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator">; +def note_fixit_unsafe_null_terminated_from_indexable_ptr : Note<"consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time">; + +def err_bounds_safety_incompatible_terminated_by_terminators : Error< + "pointers with incompatible terminators " + "%select{" + "%diff{assigning to $ from incompatible type $|" + "assigning type from incompatible type}0,1|" + "%diff{passing $ to parameter of incompatible type $|" + "passing type to parameter of incompatible type}0,1|" + "%diff{returning $ from a function with incompatible result type $|" + "returning type from a function with incompatible result type}0,1|" + "%diff{converting $ to incompatible type $|" + "converting type to incompatible type}0,1|" + "%diff{initializing $ with an expression of incompatible type $|" + "initializing type with an expression of incompatible type}0,1|" + "%diff{sending $ to parameter of incompatible type $|" + "sending type to parameter of incompatible type}0,1|" + "%diff{casting $ to incompatible type $|" + "casting type to incompatible type}0,1|" + "}2">; +def err_bounds_safety_incompatible_terminated_by_to_non_terminated_by : Error< + "%select{" + "%diff{assigning to $ from incompatible type $|" + "assigning type from incompatible type}0,1|" + "%diff{passing $ to parameter of incompatible type $|" + "passing type to parameter of incompatible type}0,1|" + "%diff{returning $ from a function with incompatible result type $|" + "returning type from a function with incompatible result type}0,1|" + "%diff{converting $ to incompatible type $|" + "converting type to incompatible type}0,1|" + "%diff{initializing $ with an expression of incompatible type $|" + "initializing type with an expression of incompatible type}0,1|" + "%diff{sending $ to parameter of incompatible type $|" + "sending type to parameter of incompatible type}0,1|" + "%diff{casting $ to incompatible type $|" + "casting type to incompatible type}0,1|" + "}2 requires a linear search for the terminator; use " + "'%select{__terminated_by_to_indexable|__null_terminated_to_indexable}3()' to perform this conversion explicitly">; +def warn_bounds_safety_incompatible_non_terminated_by_to_terminated_by : Warning< + "%select{" + "%diff{assigning to $ from incompatible type $|" + "assigning type from incompatible type}0,1|" + "%diff{passing $ to parameter of incompatible type $|" + "passing type to parameter of incompatible type}0,1|" + "%diff{returning $ from a function with incompatible result type $|" + "returning type from a function with incompatible result type}0,1|" + "%diff{converting $ to incompatible type $|" + "converting type to incompatible type}0,1|" + "%diff{initializing $ with an expression of incompatible type $|" + "initializing type with an expression of incompatible type}0,1|" + "%diff{sending $ to parameter of incompatible type $|" + "sending type to parameter of incompatible type}0,1|" + "%diff{casting $ to incompatible type $|" + "casting type to incompatible type}0,1|" + "}2 is an unsafe operation" + "%select{; use '__unsafe_terminated_by_from_indexable()' or '__unsafe_forge_terminated_by()' to perform this conversion|" + "; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion|" + "}3">, InGroup<BoundsSafetyStrictTerminatedByCast>, DefaultError; +def err_bounds_safety_incompatible_non_terminated_by_to_terminated_by : Error<warn_bounds_safety_incompatible_non_terminated_by_to_terminated_by.Summary>; +def err_bounds_safety_incompatible_terminated_by_to_non_terminated_by_mismatch : Error< + "%select{" + "%diff{assigning to $ from incompatible type $|" + "assigning type from incompatible type}0,1|" + "%diff{passing $ to parameter of incompatible type $|" + "passing type to parameter of incompatible type}0,1|" + "%diff{returning $ from a function with incompatible result type $|" + "returning type from a function with incompatible result type}0,1|" + "%diff{converting $ to incompatible type $|" + "converting type to incompatible type}0,1|" + "%diff{initializing $ with an expression of incompatible type $|" + "initializing type with an expression of incompatible type}0,1|" + "%diff{sending $ to parameter of incompatible type $|" + "sending type to parameter of incompatible type}0,1|" + "%diff{casting $ to incompatible type $|" + "casting type to incompatible type}0,1|" + "}2 that discards '__terminated_by' attribute is not allowed">; +def warn_bounds_safety_incompatible_non_terminated_by_to_terminated_by_mismatch : Warning< + "%select{" + "%diff{assigning to $ from incompatible type $|" + "assigning type from incompatible type}0,1|" + "%diff{passing $ to parameter of incompatible type $|" + "passing type to parameter of incompatible type}0,1|" + "%diff{returning $ from a function with incompatible result type $|" + "returning type from a function with incompatible result type}0,1|" + "%diff{converting $ to incompatible type $|" + "converting type to incompatible type}0,1|" + "%diff{initializing $ with an expression of incompatible type $|" + "initializing type with an expression of incompatible type}0,1|" + "%diff{sending $ to parameter of incompatible type $|" + "sending type to parameter of incompatible type}0,1|" + "%diff{casting $ to incompatible type $|" + "casting type to incompatible type}0,1|" + "}2 that adds '__terminated_by' attribute is not allowed">, InGroup<BoundsSafetyStrictTerminatedByCast>, DefaultError; +def err_bounds_safety_incompatible_non_terminated_by_to_terminated_by_mismatch : Error<warn_bounds_safety_incompatible_non_terminated_by_to_terminated_by_mismatch.Summary>; + +def err_bounds_safety_terminated_by_uninitialized : Error< + "array '%0' with '__terminated_by' attribute must be initialized">; +def err_bounds_safety_terminated_by_incomplete_array_empty_init : Error< + "incomplete array '%0' with '__terminated_by' attribute must be initialized " + "with at least one element">; +def err_bounds_safety_terminated_by_terminator_in_array_must_be_const : Error< + "terminator in array '%0' must be a compile-time constant">; +def err_bounds_safety_terminated_by_wrong_initializer_kind : Error< + "array '%0' with '__terminated_by' attribute must be initialized with a " + "string literal or an initializer list">; +def err_bounds_safety_terminated_by_terminator_mismatch : Error< + "array '%0' with '__terminated_by' attribute is initialized with an " + "incorrect terminator (expected: %1; got %2)">; + +def err_bounds_safety_irrecoverable_attr : Error< + "bounds attribute '%select{__bidi_indexable|__indexable|__single|__unsafe_indexable}0' cannot be applied to attributed type %1 in this context%select{| due to the surrounding 'typeof' specifier}2">; +def err_bounds_safety_undeduced : Error< + "bounds attribute '%select{__bidi_indexable|__indexable|__single|__unsafe_indexable}0' cannot be applied to an undeduced type">; +} // End of CategoryName = "BoundsSafety Pointer Attributes Issue" +// TO_UPSTREAM(BoundsSafety) OFF + // three-way comparison operator diagnostics def err_implied_comparison_category_type_not_found : Error< "cannot %select{use builtin operator '<=>'|default 'operator<=>'}1 " @@ -12364,8 +13384,16 @@ def warn_unsafe_buffer_variable : Warning< InGroup<UnsafeBufferUsage>, DefaultIgnore; def warn_unsafe_buffer_operation : Warning< "%select{unsafe pointer operation|unsafe pointer arithmetic|" - "unsafe buffer access|function introduces unsafe buffer manipulation|unsafe invocation of span::data}0">, + "unsafe buffer access|function introduces unsafe buffer manipulation|unsafe invocation of %1|" + "field %1 prone to unsafe buffer manipulation}0">, InGroup<UnsafeBufferUsage>, DefaultIgnore; +def warn_unsafe_buffer_libc_call : Warning< + "function %0 is unsafe">, + InGroup<UnsafeBufferUsageInLibcCall>, DefaultIgnore; +def note_unsafe_buffer_printf_call : Note< + "%select{|change to 'snprintf' for explicit bounds checking | buffer pointer and size may not match" + "|string argument is not guaranteed to be null-terminated" + "|'va_list' is unsafe}0">; def note_unsafe_buffer_operation : Note< "used%select{| in pointer arithmetic| in buffer access}0 here">; def note_unsafe_buffer_variable_fixit_group : Note< @@ -12378,6 +13406,16 @@ def note_safe_buffer_usage_suggestions_disabled : Note< def warn_unsafe_buffer_usage_in_container : Warning< "the two-parameter std::span construction is unsafe as it can introduce mismatch between buffer size and the bound information">, InGroup<UnsafeBufferUsageInContainer>, DefaultIgnore; +def warn_unsafe_count_attributed_pointer_argument : Warning< + "unsafe assignment to function parameter of count-attributed type">, + InGroup<UnsafeBufferUsage>, DefaultIgnore; +def note_unsafe_count_attributed_pointer_argument : Note< + "consider using %select{|a safe container and passing '.data()' to the " + "parameter%select{| '%3'}2 and '.size()' to its dependent parameter '%4' or }0" + "'std::span' and passing '.first(...).data()' to the parameter%select{| '%3'}2">; +def warn_unsafe_single_pointer_argument : Warning< + "unsafe assignment to function parameter of __single pointer type">, + InGroup<UnsafeBufferUsage>, DefaultIgnore; #ifndef NDEBUG // Not a user-facing diagnostic. Useful for debugging false negatives in // -fsafe-buffer-usage-suggestions (i.e. lack of -Wunsafe-buffer-usage fixits). diff --git a/clang/include/clang/Basic/Features.def b/clang/include/clang/Basic/Features.def index dc71ef8f98692..bb3b50c23ddd4 100644 --- a/clang/include/clang/Basic/Features.def +++ b/clang/include/clang/Basic/Features.def @@ -63,6 +63,7 @@ FEATURE(attribute_availability_app_extension, true) FEATURE(attribute_availability_with_version_underscores, true) FEATURE(attribute_availability_tvos, true) FEATURE(attribute_availability_watchos, true) +FEATURE(attribute_availability_swift, true) FEATURE(attribute_availability_driverkit, true) FEATURE(attribute_availability_with_strict, true) FEATURE(attribute_availability_with_replacement, true) @@ -85,6 +86,7 @@ FEATURE(attribute_overloadable, true) FEATURE(attribute_unavailable_with_message, true) FEATURE(attribute_unused_on_fields, true) FEATURE(attribute_diagnose_if_objc, true) +FEATURE(attribute_noescape_nonpointer, true) FEATURE(blocks, LangOpts.Blocks) FEATURE(c_thread_safety_attributes, true) FEATURE(cxx_exceptions, LangOpts.CXXExceptions) @@ -92,6 +94,7 @@ FEATURE(cxx_rtti, LangOpts.RTTI &&LangOpts.RTTIData) EXTENSION(define_target_os_macros, PP.getPreprocessorOpts().DefineTargetOSMacros) FEATURE(enumerator_attributes, true) +FEATURE(generalized_swift_name, true) FEATURE(nullability, true) FEATURE(nullability_on_arrays, true) FEATURE(nullability_on_classes, true) @@ -102,6 +105,11 @@ FEATURE(memory_sanitizer, SanitizerKind::KernelMemory)) FEATURE(thread_sanitizer, LangOpts.Sanitize.has(SanitizerKind::Thread)) FEATURE(dataflow_sanitizer, LangOpts.Sanitize.has(SanitizerKind::DataFlow)) +FEATURE(ptrauth_intrinsics, LangOpts.PointerAuthIntrinsics) +FEATURE(ptrauth_qualifier, LangOpts.PointerAuthIntrinsics) +FEATURE(ptrauth_calls, LangOpts.PointerAuthCalls) +FEATURE(ptrauth_returns, LangOpts.PointerAuthReturns) +FEATURE(ptrauth_indirect_gotos, LangOpts.PointerAuthIndirectGotos) FEATURE(scudo, LangOpts.Sanitize.hasOneOf(SanitizerKind::Scudo)) FEATURE(ptrauth_intrinsics, LangOpts.PointerAuthIntrinsics) FEATURE(ptrauth_calls, LangOpts.PointerAuthCalls) @@ -119,6 +127,14 @@ EXTENSION(swiftcc, EXTENSION(swiftasynccc, PP.getTargetInfo().checkCallingConvention(CC_SwiftAsync) == clang::TargetInfo::CCCR_OK) + +// We have the extension above. Keep the feature for older swift stdlib, +// since it only learned to check the extensions recently. +// rdar://133628186 +FEATURE(swiftasynccc, + PP.getTargetInfo().checkCallingConvention(CC_SwiftAsync) == + clang::TargetInfo::CCCR_OK) + FEATURE(pragma_stdc_cx_limited_range, true) // Objective-C features FEATURE(objc_arr, LangOpts.ObjCAutoRefCount) // FIXME: REMOVE? @@ -308,5 +324,12 @@ FEATURE(cxx_abi_relative_vtable, LangOpts.CPlusPlus && LangOpts.RelativeCXXABIVT FEATURE(cuda_noinline_keyword, LangOpts.CUDA) EXTENSION(cuda_implicit_host_device_templates, LangOpts.CUDA && LangOpts.OffloadImplicitHostDeviceTemplates) +// TODO(BoundsSafety): Deprecate this feature name +FEATURE(bounds_attributes, LangOpts.BoundsSafety) +/* TO_UPSTREAM(BoundsSafety) ON*/ +FEATURE(bounds_safety, LangOpts.BoundsSafety) +FEATURE(bounds_safety_attributes, LangOpts.BoundsSafetyAttributes) +/* TO_UPSTREAM(BoundsSafety) OFF*/ + #undef EXTENSION #undef FEATURE diff --git a/clang/include/clang/Basic/FileManager.h b/clang/include/clang/Basic/FileManager.h index 527bbef24793e..d40f390d01bc8 100644 --- a/clang/include/clang/Basic/FileManager.h +++ b/clang/include/clang/Basic/FileManager.h @@ -287,20 +287,30 @@ class FileManager : public RefCountedBase<FileManager> { llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> getBufferForFile(FileEntryRef Entry, bool isVolatile = false, bool RequiresNullTerminator = true, - std::optional<int64_t> MaybeLimit = std::nullopt); + std::optional<int64_t> MaybeLimit = std::nullopt, + std::optional<cas::ObjectRef> *CASContents = nullptr); llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> getBufferForFile(StringRef Filename, bool isVolatile = false, bool RequiresNullTerminator = true, - std::optional<int64_t> MaybeLimit = std::nullopt) const { + std::optional<int64_t> MaybeLimit = std::nullopt, + std::optional<cas::ObjectRef> *CASContents = nullptr) const { return getBufferForFileImpl(Filename, /*FileSize=*/(MaybeLimit ? *MaybeLimit : -1), - isVolatile, RequiresNullTerminator); + isVolatile, RequiresNullTerminator, CASContents); } + /// This is a convenience method that opens a file, gets the \p cas::ObjectRef + /// for its contents if supported by the file system, and then closes the + /// file. If both the buffer and its `cas::ObjectRef` are needed use \p + /// getBufferForFile to avoid the extra file lookup. + llvm::ErrorOr<std::optional<cas::ObjectRef>> + getObjectRefForFileContent(const Twine &Filename); + private: llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> getBufferForFileImpl(StringRef Filename, int64_t FileSize, bool isVolatile, - bool RequiresNullTerminator) const; + bool RequiresNullTerminator, + std::optional<cas::ObjectRef> *CASContents) const; DirectoryEntry *&getRealDirEntry(const llvm::vfs::Status &Status); diff --git a/clang/include/clang/Basic/FileSystemOptions.h b/clang/include/clang/Basic/FileSystemOptions.h index 458af0c7b6592..d1c6a67884fdd 100644 --- a/clang/include/clang/Basic/FileSystemOptions.h +++ b/clang/include/clang/Basic/FileSystemOptions.h @@ -24,6 +24,14 @@ class FileSystemOptions { /// If set, paths are resolved as if the working directory was /// set to the value of WorkingDir. std::string WorkingDir; + + /// If set, uses this root ID with \a CASFileSystem. + std::string CASFileSystemRootID; + + /// If set, used as the working directory for -fcas-fs. + /// + /// FIXME: Merge with WorkingDir? + std::string CASFileSystemWorkingDirectory; }; } // end namespace clang diff --git a/clang/include/clang/Basic/IdentifierTable.h b/clang/include/clang/Basic/IdentifierTable.h index ae9ebd9f59154..23b326989cdc3 100644 --- a/clang/include/clang/Basic/IdentifierTable.h +++ b/clang/include/clang/Basic/IdentifierTable.h @@ -162,11 +162,6 @@ class alignas(IdentifierInfoAlignment) IdentifierInfo { LLVM_PREFERRED_TYPE(bool) unsigned ChangedAfterLoad : 1; - // True if the identifier's frontend information has changed from the - // definition loaded from an AST file. - LLVM_PREFERRED_TYPE(bool) - unsigned FEChangedAfterLoad : 1; - // True if revertTokenIDToIdentifier was called. LLVM_PREFERRED_TYPE(bool) unsigned RevertedTokenID : 1; @@ -210,8 +205,8 @@ class alignas(IdentifierInfoAlignment) IdentifierInfo { HasMacro(false), HadMacro(false), IsExtension(false), IsFutureCompatKeyword(false), IsPoisoned(false), IsCPPOperatorKeyword(false), NeedsHandleIdentifier(false), - IsFromAST(false), ChangedAfterLoad(false), FEChangedAfterLoad(false), - RevertedTokenID(false), OutOfDate(false), IsModulesImport(false), + IsFromAST(false), ChangedAfterLoad(false), RevertedTokenID(false), + OutOfDate(false), IsModulesImport(false), IsMangledOpenMPVariantName(false), IsDeprecatedMacro(false), IsRestrictExpansion(false), IsFinal(false) {} @@ -482,18 +477,6 @@ class alignas(IdentifierInfoAlignment) IdentifierInfo { ChangedAfterLoad = true; } - /// Determine whether the frontend token information for this - /// identifier has changed since it was loaded from an AST file. - bool hasFETokenInfoChangedSinceDeserialization() const { - return FEChangedAfterLoad; - } - - /// Note that the frontend token information for this identifier has - /// changed since it was loaded from an AST file. - void setFETokenInfoChangedSinceDeserialization() { - FEChangedAfterLoad = true; - } - /// Determine whether the information for this identifier is out of /// date with respect to the external source. bool isOutOfDate() const { return OutOfDate; } diff --git a/clang/include/clang/Basic/LLVM.h b/clang/include/clang/Basic/LLVM.h index f4956cd16cbcf..1328f0c938af3 100644 --- a/clang/include/clang/Basic/LLVM.h +++ b/clang/include/clang/Basic/LLVM.h @@ -47,6 +47,14 @@ namespace llvm { class raw_ostream; class raw_pwrite_stream; // TODO: DenseMap, ... + + namespace cas { + class ActionCache; + class ObjectStore; + class CASID; + class ObjectProxy; + class ObjectRef; + } // namespace cas } @@ -84,6 +92,14 @@ namespace clang { using llvm::raw_ostream; using llvm::raw_pwrite_stream; + + namespace cas { + using llvm::cas::ActionCache; + using llvm::cas::CASID; + using llvm::cas::ObjectProxy; + using llvm::cas::ObjectRef; + using llvm::cas::ObjectStore; + } // namespace cas } // end namespace clang. #endif diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def index 834a6f6cd43e3..d903be7e65474 100644 --- a/clang/include/clang/Basic/LangOptions.def +++ b/clang/include/clang/Basic/LangOptions.def @@ -160,12 +160,13 @@ LANGOPT(Coroutines , 1, 0, "C++20 coroutines") LANGOPT(CoroAlignedAllocation, 1, 0, "prefer Aligned Allocation according to P2014 Option 2") LANGOPT(DllExportInlines , 1, 1, "dllexported classes dllexport inline methods") LANGOPT(RelaxedTemplateTemplateArgs, 1, 1, "C++17 relaxed matching of template template arguments") + +LANGOPT(Unstable , 1, 0, "Enable unstable and experimental features") LANGOPT(ExperimentalLibrary, 1, 0, "enable unstable and experimental library features") LANGOPT(PointerAuthIntrinsics, 1, 0, "pointer authentication intrinsics") LANGOPT(PointerAuthCalls , 1, 0, "function pointer authentication") LANGOPT(PointerAuthReturns, 1, 0, "return pointer authentication") -LANGOPT(PointerAuthIndirectGotos, 1, 0, "indirect gotos pointer authentication") LANGOPT(PointerAuthAuthTraps, 1, 0, "pointer authentication failure traps") LANGOPT(PointerAuthVTPtrAddressDiscrimination, 1, 0, "incorporate address discrimination in authenticated vtable pointers") LANGOPT(PointerAuthVTPtrTypeDiscrimination, 1, 0, "incorporate type discrimination in authenticated vtable pointers") @@ -173,6 +174,11 @@ LANGOPT(PointerAuthTypeInfoVTPtrDiscrimination, 1, 0, "incorporate type and addr LANGOPT(PointerAuthInitFini, 1, 0, "sign function pointers in init/fini arrays") BENIGN_LANGOPT(PointerAuthFunctionTypeDiscrimination, 1, 0, "Use type discrimination when signing function pointers") +LANGOPT(PointerAuthIndirectGotos, 1, 0, "indirect gotos pointer authentication") +LANGOPT(SoftPointerAuth , 1, 0, "software emulation of pointer authentication") +VALUE_LANGOPT(PointerAuthABIVersion, 32, 0, "pointer authentication ABI version") +LANGOPT(PointerAuthKernelABIVersion, 1, 0, "controls whether the pointer auth abi version represents a kernel ABI") +LANGOPT(PointerAuthABIVersionEncoded, 1, 0, "controls whether the pointer auth abi version should be encoded in the IR") LANGOPT(DoubleSquareBracketAttributes, 1, 0, "'[[]]' attributes extension for all language standard modes") LANGOPT(ExperimentalLateParseAttributes, 1, 0, "experimental late parsing of attributes") @@ -203,6 +209,7 @@ COMPATIBLE_LANGOPT(ModulesValidateTextualHeaderIncludes, 1, 1, "validation of te BENIGN_LANGOPT(ModulesErrorRecovery, 1, 1, "automatically importing modules as needed when performing error recovery") BENIGN_LANGOPT(ImplicitModules, 1, 1, "building modules that are not specified via -fmodule-file") COMPATIBLE_LANGOPT(ModulesLocalVisibility, 1, 0, "local submodule visibility") +COMPATIBLE_LANGOPT(ModulesHashErrorDiags, 1, 0, "hash out diagnostic errors as part of the module hash") COMPATIBLE_LANGOPT(Optimize , 1, 0, "__OPTIMIZE__ predefined macro") COMPATIBLE_LANGOPT(OptimizeSize , 1, 0, "__OPTIMIZE_SIZE__ predefined macro") COMPATIBLE_LANGOPT(Static , 1, 0, "__STATIC__ predefined macro (as opposed to __DYNAMIC__)") @@ -430,6 +437,7 @@ LANGOPT(RetainCommentsFromSystemHeaders, 1, 0, "retain documentation comments fr LANGOPT(APINotes, 1, 0, "use external API notes") LANGOPT(APINotesModules, 1, 0, "use module-based external API notes") +LANGOPT(NeededByPCHOrCompilationUsesPCH, 1, 0, "compilation involves pch") LANGOPT(SanitizeAddressFieldPadding, 2, 0, "controls how aggressive is ASan " "field padding (0: none, 1:least " @@ -507,6 +515,8 @@ VALUE_LANGOPT(FuchsiaAPILevel, 32, 0, "Fuchsia API level") // on large _BitInts. BENIGN_VALUE_LANGOPT(MaxBitIntWidth, 32, 128, "Maximum width of a _BitInt") +LANGOPT(IgnoreConflictingTypes, 1, 0, "Suppress conflicting type errors from mismatching declarations") + COMPATIBLE_LANGOPT(IncrementalExtensions, 1, 0, " True if we want to process statements" "on the global scope, ignore EOF token and continue later on (thus " "avoid tearing the Lexer and etc. down). Controlled by " @@ -524,6 +534,15 @@ BENIGN_LANGOPT(CheckConstexprFunctionBodies, 1, 1, "be used in a constant expression.") LANGOPT(BoundsSafety, 1, 0, "Bounds safety extension for C") +/* TO_UPSTREAM(BoundsSafety) ON*/ +LANGOPT(BoundsSafetyAttributes, 1, 0, "Experimental bounds safety attributes") +LANGOPT(BoundsAttributesCXXExperimental, 1, 0, "Experimental bounds attributes for C++") +LANGOPT(BoundsAttributesObjCExperimental, 1, 0, "Experimental bounds attributes for Objective-C") +LANGOPT(BoundsSafetyRelaxedSystemHeaders, 1, 1, + "Relax bounds safety assignment rules in system headers") +LANGOPT(BoundsSafetyBringUpMissingChecks, 7, clang::LangOptions::getDefaultBoundsSafetyNewChecksMask(), + "Bring up new -fbounds-safety run-time checks") +/* TO_UPSTREAM(BoundsSafety) OFF*/ #undef LANGOPT #undef COMPATIBLE_LANGOPT diff --git a/clang/include/clang/Basic/LangOptions.h b/clang/include/clang/Basic/LangOptions.h index 91f1c2f2e6239..fb23ff6647eaf 100644 --- a/clang/include/clang/Basic/LangOptions.h +++ b/clang/include/clang/Basic/LangOptions.h @@ -64,6 +64,8 @@ enum class PointerAuthenticationMode : unsigned { SignAndAuth }; +enum class FeatureAvailKind { None, Available, Unavailable, Dynamic }; + /// Bitfields of LangOptions, split out from LangOptions in order to ensure that /// this large collection of bitfields is a trivial class type. class LangOptionsBase { @@ -408,6 +410,25 @@ class LangOptionsBase { IncompleteOnly = 3, }; + /* TODO(BoundsSafety) Deprecate the flag */ + enum BoundsSafetyNewChecks { + BS_CHK_None = 0, + + BS_CHK_AccessSize = 1 << 0, // rdar://72252593 + BS_CHK_IndirectCountUpdate = 1 << 1, // rdar://98749526 + BS_CHK_ReturnSize = 1 << 2, // rdar://83900556 + BS_CHK_EndedByLowerBound = 1 << 3, // rdar://91596663 + BS_CHK_CompoundLiteralInit = 1 << 4, // rdar://110871666 + BS_CHK_LibCAttributes = 1 << 5, // rdar://84733153 + BS_CHK_ArraySubscriptAgg = 1 << 6, // rdar://145020583 + }; + using BoundsSafetyNewChecksMaskIntTy = + std::underlying_type_t<BoundsSafetyNewChecks>; + + static BoundsSafetyNewChecksMaskIntTy + getBoundsSafetyNewChecksMaskForGroup(StringRef GroupName); + static BoundsSafetyNewChecksMaskIntTy getDefaultBoundsSafetyNewChecksMask(); + /// Controls the various implementations for complex multiplication and // division. enum ComplexRangeKind { @@ -521,6 +542,8 @@ class LangOptions : public LangOptionsBase { /// This list is sorted. std::vector<std::string> ModuleFeatures; + std::vector<std::string> FeatureAvailability; + /// Options for parsing comments. CommentOptions CommentOpts; @@ -538,6 +561,16 @@ class LangOptions : public LangOptionsBase { /// host code generation. std::string OMPHostIRFile; + llvm::StringMap<FeatureAvailKind> FeatureAvailabilityMap; + + void setFeatureAvailability(StringRef Feature, FeatureAvailKind Kind) { + FeatureAvailabilityMap[Feature] = Kind; + } + + FeatureAvailKind getFeatureAvailability(StringRef Feature) const { + return FeatureAvailabilityMap.lookup(Feature); + } + /// The user provided compilation unit ID, if non-empty. This is used to /// externalize static variables which is needed to support accessing static /// device variables in host code for single source offloading languages @@ -760,6 +793,36 @@ class LangOptions : public LangOptionsBase { return FPExceptionModeKind::FPE_Ignore; return EM; } + + /* TO_UPSTREAM(BoundsSafety) ON */ + // Returns true iff -fbounds-safety is enabled. This is a convenience function + // that can be called from Xcode disclosed clang without hitting a compilation + // error (unlike directly accessing the `BoundsSafety` field). + bool hasBoundsSafety() const { + return BoundsSafety; + } + + // Returns true iff -fbounds-safety-attributes is enabled. This is a + // convenience function that can be called from Xcode disclosed clang without + // hitting a compilation error (unlike directly accessing the + // `BoundsSafetyAttributes` field). + bool hasBoundsSafetyAttributes() const { + return BoundsSafetyAttributes; + } + + // Returns true iff we are compiling in bounds-safety's attribute-only mode. + bool isBoundsSafetyAttributeOnlyMode() const { + return hasBoundsSafetyAttributes() && !hasBoundsSafety(); + } + + // Take a BoundsSafetyNewChecksMask (or check mask) and return true if + // the check (or mask) is set. New -fbounds-safety run-time checks should use + // this helper to decide whether to enable/disable the checks. + bool hasNewBoundsSafetyCheck(BoundsSafetyNewChecksMaskIntTy ChkMask) const { + return (BoundsSafetyBringUpMissingChecks & ChkMask) != 0; + } + + /* TO_UPSTREAM(BoundsSafety) OFF */ }; /// Floating point control options diff --git a/clang/include/clang/Basic/Module.h b/clang/include/clang/Basic/Module.h index e86f4303d732b..db929fbe109e6 100644 --- a/clang/include/clang/Basic/Module.h +++ b/clang/include/clang/Basic/Module.h @@ -48,6 +48,7 @@ namespace clang { class FileManager; class LangOptions; +class ModuleMap; class TargetInfo; /// Describes the name of a module. @@ -99,6 +100,15 @@ struct ASTFileSignature : std::array<uint8_t, 20> { } }; +/// Required to construct a Module. +/// +/// This tag type is only constructible by ModuleMap, guaranteeing it ownership +/// of all Module instances. +class ModuleConstructorTag { + explicit ModuleConstructorTag() = default; + friend ModuleMap; +}; + /// Describes a module or submodule. /// /// Aligned to 8 bytes to allow for llvm::PointerIntPair<Module *, 3>. @@ -121,6 +131,10 @@ class alignas(8) Module { /// This is a C++20 header unit. ModuleHeaderUnit, + /// This is a module that was defined by a module map and built out + /// of header files as part of an \c IncludeTree. + IncludeTreeModuleMap, + /// This is a C++20 module interface unit. ModuleInterfaceUnit, @@ -209,7 +223,9 @@ class alignas(8) Module { bool isPrivateModule() const { return Kind == PrivateModuleFragment; } - bool isModuleMapModule() const { return Kind == ModuleMapModule; } + bool isModuleMapModule() const { + return Kind == ModuleMapModule || Kind == IncludeTreeModuleMap; + } private: /// The submodules of this module, indexed by name. @@ -217,12 +233,15 @@ class alignas(8) Module { /// A mapping from the submodule name to the index into the /// \c SubModules vector at which that submodule resides. - llvm::StringMap<unsigned> SubModuleIndex; + mutable llvm::StringMap<unsigned> SubModuleIndex; /// The AST file if this is a top-level module which has a /// corresponding serialized AST file, or null otherwise. OptionalFileEntryRef ASTFile; + /// The \c ActionCache key for this module, if any. + std::optional<std::string> ModuleCacheKey; + /// The top-level headers associated with this module. llvm::SmallSetVector<FileEntryRef, 2> TopHeaders; @@ -243,8 +262,6 @@ class alignas(8) Module { HK_PrivateTextual, HK_Excluded }; - static const int NumHeaderKinds = HK_Excluded + 1; - /// Information about a header directive as found in the module map /// file. struct Header { @@ -253,17 +270,36 @@ class alignas(8) Module { FileEntryRef Entry; }; - /// Information about a directory name as found in the module map - /// file. +private: + static const int NumHeaderKinds = HK_Excluded + 1; + // The begin index for a HeaderKind also acts the end index of HeaderKind - 1. + // The extra element at the end acts as the end index of the last HeaderKind. + unsigned HeaderKindBeginIndex[NumHeaderKinds + 1] = {}; + SmallVector<Header, 2> HeadersStorage; + +public: + ArrayRef<Header> getAllHeaders() const { return HeadersStorage; } + ArrayRef<Header> getHeaders(HeaderKind HK) const { + assert(HK < NumHeaderKinds && "Invalid Module::HeaderKind"); + auto BeginIt = HeadersStorage.begin() + HeaderKindBeginIndex[HK]; + auto EndIt = HeadersStorage.begin() + HeaderKindBeginIndex[HK + 1]; + return {BeginIt, EndIt}; + } + void addHeader(HeaderKind HK, Header H) { + assert(HK < NumHeaderKinds && "Invalid Module::HeaderKind"); + auto EndIt = HeadersStorage.begin() + HeaderKindBeginIndex[HK + 1]; + HeadersStorage.insert(EndIt, std::move(H)); + for (unsigned HKI = HK + 1; HKI != NumHeaderKinds + 1; ++HKI) + ++HeaderKindBeginIndex[HKI]; + } + + /// Information about a directory name as found in the module map file. struct DirectoryName { std::string NameAsWritten; std::string PathRelativeToRootModuleDirectory; DirectoryEntryRef Entry; }; - /// The headers that are part of this module. - SmallVector<Header, 2> Headers[5]; - /// Stored information about a header directive that was found in the /// module map file but has not been resolved to a file. struct UnresolvedHeaderDirective { @@ -342,6 +378,11 @@ class alignas(8) Module { LLVM_PREFERRED_TYPE(bool) unsigned IsInferred : 1; + /// Whether this is an inferred submodule that's missing from the umbrella + /// header. + LLVM_PREFERRED_TYPE(bool) + unsigned IsInferredMissingFromUmbrellaHeader : 1; + /// Whether we should infer submodules for this module based on /// the headers. /// @@ -377,6 +418,9 @@ class alignas(8) Module { LLVM_PREFERRED_TYPE(bool) unsigned ModuleMapIsPrivate : 1; + /// \brief Whether this is a module who has its swift_names inferred. + unsigned IsSwiftInferImportAsMember : 1; + /// Whether this C++20 named modules doesn't need an initializer. /// This is only meaningful for C++20 modules. LLVM_PREFERRED_TYPE(bool) @@ -497,8 +541,9 @@ class alignas(8) Module { std::vector<Conflict> Conflicts; /// Construct a new module or submodule. - Module(StringRef Name, SourceLocation DefinitionLoc, Module *Parent, - bool IsFramework, bool IsExplicit, unsigned VisibilityID); + Module(ModuleConstructorTag, StringRef Name, SourceLocation DefinitionLoc, + Module *Parent, bool IsFramework, bool IsExplicit, + unsigned VisibilityID); ~Module(); @@ -584,7 +629,6 @@ class alignas(8) Module { void setParent(Module *M) { assert(!Parent); Parent = M; - Parent->SubModuleIndex[Name] = Parent->SubModules.size(); Parent->SubModules.push_back(this); } @@ -688,6 +732,15 @@ class alignas(8) Module { getTopLevelModule()->ASTFile = File; } + std::optional<std::string> getModuleCacheKey() const { + return getTopLevelModule()->ModuleCacheKey; + } + + void setModuleCacheKey(std::string Key) { + assert(!getModuleCacheKey() || *getModuleCacheKey() == Key); + getTopLevelModule()->ModuleCacheKey = std::move(Key); + } + /// Retrieve the umbrella directory as written. std::optional<DirectoryName> getUmbrellaDirAsWritten() const { if (const auto *Dir = std::get_if<DirectoryEntryRef>(&Umbrella)) @@ -749,7 +802,6 @@ class alignas(8) Module { /// /// \returns The submodule if found, or NULL otherwise. Module *findSubmodule(StringRef Name) const; - Module *findOrInferSubmodule(StringRef Name); /// Get the Global Module Fragment (sub-module) for this module, it there is /// one. @@ -855,10 +907,11 @@ class VisibleModuleSet { StringRef Message)>; /// Make a specific module visible. - void setVisible(Module *M, SourceLocation Loc, - VisibleCallback Vis = [](Module *) {}, - ConflictCallback Cb = [](ArrayRef<Module *>, Module *, - StringRef) {}); + void setVisible( + Module *M, SourceLocation Loc, bool IncludeExports = true, + VisibleCallback Vis = [](Module *) {}, + ConflictCallback Cb = [](ArrayRef<Module *>, Module *, StringRef) {}); + private: /// Import locations for each visible module. Indexed by the module's /// VisibilityID. diff --git a/clang/include/clang/Basic/PathRemapper.h b/clang/include/clang/Basic/PathRemapper.h new file mode 100644 index 0000000000000..e035a290f1966 --- /dev/null +++ b/clang/include/clang/Basic/PathRemapper.h @@ -0,0 +1,74 @@ +//===--- PathRemapper.h - Remap filepath prefixes ---------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines a data structure that stores a string-to-string +// mapping used to transform file paths based on a prefix mapping. It +// is optimized for the common case of having 0, 1, or 2 mappings. +// +// Remappings are stored such that they are applied in the order they +// are passed on the command line, with the first one winning - a path will +// only be remapped by a single mapping, if any, not multiple. The ordering +// would only matter if one source mapping was a prefix of another. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_BASIC_PATHREMAPPER_H +#define LLVM_CLANG_BASIC_PATHREMAPPER_H + +#include "clang/Basic/LLVM.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/Twine.h" +#include "llvm/Support/Path.h" + +#include <string> +#include <utility> + +namespace clang { + +class PathRemapper { + SmallVector<std::pair<std::string, std::string>, 2> PathMappings; +public: + /// Adds a mapping such that any paths starting with `FromPrefix` have that + /// portion replaced with `ToPrefix`. + void addMapping(StringRef FromPrefix, StringRef ToPrefix) { + PathMappings.emplace_back(FromPrefix.str(), ToPrefix.str()); + } + + /// Returns a remapped `Path` if it starts with a prefix in the remapper; + /// otherwise the original path is returned. + std::string remapPath(StringRef Path) const { + for (const auto &Mapping : PathMappings) + if (Path.starts_with(Mapping.first)) + return (Twine(Mapping.second) + + Path.substr(Mapping.first.size())).str(); + return Path.str(); + } + + /// Remaps `PathBuf` if it starts with a prefix in the remapper. Avoids any + /// heap allocations if the path is not remapped. + void remapPath(SmallVectorImpl<char> &PathBuf) const { + for (const auto &E : PathMappings) + if (llvm::sys::path::replace_path_prefix(PathBuf, E.first, E.second)) + break; + } + + /// Return true if there are no path remappings (meaning remapPath will always + /// return the path given). + bool empty() const { + return PathMappings.empty(); + } + + ArrayRef<std::pair<std::string, std::string>> getMappings() const { + return PathMappings; + } +}; + +} // end namespace clang + +#endif diff --git a/clang/include/clang/Basic/PointerAuthOptions.h b/clang/include/clang/Basic/PointerAuthOptions.h index 417b4b00648c7..b84e6fc9f810b 100644 --- a/clang/include/clang/Basic/PointerAuthOptions.h +++ b/clang/include/clang/Basic/PointerAuthOptions.h @@ -34,9 +34,22 @@ class PointerAuthSchema { public: enum class Kind : unsigned { None, + Soft, ARM8_3, }; + /// Software pointer-signing "keys". If you add a new key, make sure this->Key + /// has a large enough bit-width. + enum class SoftKey : unsigned { + FunctionPointers = 0, + BlockInvocationFunctionPointers = 1, + BlockHelperFunctionPointers = 2, + ObjCMethodListFunctionPointers = 3, + CXXVTablePointers = 4, + CXXVirtualFunctionPointers = 5, + CXXMemberFunctionPointers = 6, + }; + /// Hardware pointer-signing keys in ARM8.3. /// /// These values are the same used in ptrauth.h. @@ -69,7 +82,7 @@ class PointerAuthSchema { unsigned AuthenticatesNullValues : 1; PointerAuthenticationMode SelectedAuthenticationMode : 2; Discrimination DiscriminationKind : 2; - unsigned Key : 2; + unsigned Key : 3; unsigned ConstantDiscriminator : 16; public: @@ -93,6 +106,24 @@ class PointerAuthSchema { ConstantDiscriminator = *ConstantDiscriminatorOrNone; } + PointerAuthSchema( + SoftKey Key, bool IsAddressDiscriminated, + PointerAuthenticationMode AuthenticationMode, + Discrimination OtherDiscrimination, + std::optional<uint16_t> ConstantDiscriminatorOrNone = std::nullopt, + bool IsIsaPointer = false, bool AuthenticatesNullValues = false) + : TheKind(Kind::Soft), IsAddressDiscriminated(IsAddressDiscriminated), + IsIsaPointer(IsIsaPointer), + AuthenticatesNullValues(AuthenticatesNullValues), + SelectedAuthenticationMode(AuthenticationMode), + DiscriminationKind(OtherDiscrimination), Key(unsigned(Key)) { + assert((getOtherDiscrimination() != Discrimination::Constant || + ConstantDiscriminatorOrNone) && + "constant discrimination requires a constant!"); + if (ConstantDiscriminatorOrNone) + ConstantDiscriminator = *ConstantDiscriminatorOrNone; + } + PointerAuthSchema( ARM8_3Key Key, bool IsAddressDiscriminated, Discrimination OtherDiscrimination, @@ -103,6 +134,17 @@ class PointerAuthSchema { OtherDiscrimination, ConstantDiscriminatorOrNone, IsIsaPointer, AuthenticatesNullValues) {} + PointerAuthSchema( + SoftKey Key, bool IsAddressDiscriminated, + Discrimination OtherDiscrimination, + std::optional<uint16_t> ConstantDiscriminatorOrNone = std::nullopt, + bool IsIsaPointer = false, bool AuthenticatesNullValues = false) + : PointerAuthSchema(Key, IsAddressDiscriminated, + PointerAuthenticationMode::SignAndAuth, + OtherDiscrimination, ConstantDiscriminatorOrNone, + IsIsaPointer, AuthenticatesNullValues) {} + + Kind getKind() const { return TheKind; } explicit operator bool() const { return isEnabled(); } @@ -142,6 +184,8 @@ class PointerAuthSchema { switch (getKind()) { case Kind::None: llvm_unreachable("calling getKey() on disabled schema"); + case Kind::Soft: + return unsigned(getSoftKey()); case Kind::ARM8_3: return llvm::to_underlying(getARM8_3Key()); } @@ -152,6 +196,11 @@ class PointerAuthSchema { return SelectedAuthenticationMode; } + SoftKey getSoftKey() const { + assert(getKind() == Kind::Soft); + return SoftKey(Key); + } + ARM8_3Key getARM8_3Key() const { assert(getKind() == Kind::ARM8_3); return ARM8_3Key(Key); @@ -159,6 +208,16 @@ class PointerAuthSchema { }; struct PointerAuthOptions { + /// Do member function pointers to virtual functions need to be built + /// as thunks? + bool ThunkCXXVirtualMemberPointers = false; + + /// Should return addresses be authenticated? + bool ReturnAddresses = false; + + /// Do authentication failures cause a trap? + bool AuthTraps = false; + /// Do indirect goto label addresses need to be authenticated? bool IndirectGotos = false; @@ -186,6 +245,18 @@ struct PointerAuthOptions { /// The ABI for C++ member function pointers. PointerAuthSchema CXXMemberFunctionPointers; + + /// The ABI for block invocation function pointers. + PointerAuthSchema BlockInvocationFunctionPointers; + + /// The ABI for block object copy/destroy function pointers. + PointerAuthSchema BlockHelperFunctionPointers; + + /// The ABI for __block variable copy/destroy function pointers. + PointerAuthSchema BlockByrefHelperFunctionPointers; + + /// The ABI for Objective-C method lists. + PointerAuthSchema ObjCMethodListFunctionPointers; }; } // end namespace clang diff --git a/clang/include/clang/Basic/SourceManager.h b/clang/include/clang/Basic/SourceManager.h index d3ccc7ef81c07..5df9f59bdfdf8 100644 --- a/clang/include/clang/Basic/SourceManager.h +++ b/clang/include/clang/Basic/SourceManager.h @@ -389,6 +389,11 @@ class ExpansionInfo { : ExpansionLocEnd; } + /// The actual value of \c ExpansionLocEnd, useful for serialization purposes. + SourceLocation getUnderlyingExpansionLocEnd() const { + return ExpansionLocEnd; + } + bool isExpansionTokenRange() const { return ExpansionIsTokenRange; } CharSourceRange getExpansionLocRange() const { diff --git a/clang/include/clang/Basic/StmtNodes.td b/clang/include/clang/Basic/StmtNodes.td index 9bf23fae50a9e..4489db576759f 100644 --- a/clang/include/clang/Basic/StmtNodes.td +++ b/clang/include/clang/Basic/StmtNodes.td @@ -216,6 +216,19 @@ def SEHFinallyStmt : StmtNode<Stmt>; def SEHLeaveStmt : StmtNode<Stmt>; def MSDependentExistsStmt : StmtNode<Stmt>; +// TO_UPSTREAM(BoundsSafety) ON +// BoundsSafety Extensions. +def AssumptionExpr : StmtNode<Expr>; +def BoundsSafetyPointerPromotionExpr : StmtNode<Expr>; +def ForgePtrExpr : StmtNode<Expr>; +def GetBoundExpr : StmtNode<Expr>; +def PredefinedBoundsCheckExpr : StmtNode<Expr>; +def BoundsCheckExpr : StmtNode<Expr>; +def MaterializeSequenceExpr : StmtNode<Expr>; +def TerminatedByToIndexableExpr : StmtNode<Expr>; +def TerminatedByFromIndexableExpr : StmtNode<Expr>; +// TO_UPSTREAM(BoundsSafety) OFF + // OpenCL Extensions. def AsTypeExpr : StmtNode<Expr>; diff --git a/clang/include/clang/Basic/TargetInfo.h b/clang/include/clang/Basic/TargetInfo.h index a58fb5f979272..d716072ad890c 100644 --- a/clang/include/clang/Basic/TargetInfo.h +++ b/clang/include/clang/Basic/TargetInfo.h @@ -277,6 +277,8 @@ class TargetInfo : public TransferrableTargetInfo, unsigned ARMCDECoprocMask : 8; + unsigned PointerAuthSupported : 1; + unsigned MaxOpenCLWorkGroupSize; std::optional<unsigned> MaxBitIntWidth; @@ -1577,6 +1579,14 @@ class TargetInfo : public TransferrableTargetInfo, return TLSSupported; } + /// \brief Whether the target supports pointer authentication at all. + /// + /// Whether pointer authentication is actually being used is determined + /// by the language option. + bool isPointerAuthSupported() const { + return PointerAuthSupported; + } + /// Return the maximum alignment (in bits) of a TLS variable /// /// Gets the maximum alignment (in bits) of a TLS variable on this target. diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def index 7f4912b9bcd96..517ef718c9598 100644 --- a/clang/include/clang/Basic/TokenKinds.def +++ b/clang/include/clang/Basic/TokenKinds.def @@ -64,6 +64,10 @@ #ifndef EXPRESSION_TRAIT #define EXPRESSION_TRAIT(I,E,K) KEYWORD(I,K) #endif +#ifndef TRANSFORM_TYPE_TRAIT_DEF +#define TRANSFORM_TYPE_TRAIT_DEF(K, Trait) KEYWORD(__##Trait, KEYCXX) +#endif + #ifndef ALIAS #define ALIAS(X,Y,Z) #endif @@ -346,6 +350,7 @@ KEYWORD(_Thread_local , KEYALL) KEYWORD(__func__ , KEYALL) KEYWORD(__objc_yes , KEYALL) KEYWORD(__objc_no , KEYALL) +KEYWORD(__ptrauth , KEYALL) // C++ 2.11p1: Keywords. @@ -534,7 +539,6 @@ TYPE_TRAIT_1(__has_unique_object_representations, TYPE_TRAIT_2(__is_layout_compatible, IsLayoutCompatible, KEYCXX) TYPE_TRAIT_2(__is_pointer_interconvertible_base_of, IsPointerInterconvertibleBaseOf, KEYCXX) -#define TRANSFORM_TYPE_TRAIT_DEF(_, Trait) KEYWORD(__##Trait, KEYCXX) #include "clang/Basic/TransformTypeTraits.def" // Clang-only C++ Type Traits @@ -596,6 +600,8 @@ ALIAS("__is_same_as", __is_same, KEYCXX) KEYWORD(__private_extern__ , KEYALL) KEYWORD(__module_private__ , KEYALL) +UNARY_EXPR_OR_TYPE_TRAIT(__builtin_ptrauth_type_discriminator, PtrAuthTypeDiscriminator, KEYALL) + // Extension that will be enabled for Microsoft, Borland and PS4, but can be // disabled via '-fno-declspec'. KEYWORD(__declspec , 0) @@ -791,6 +797,17 @@ KEYWORD(__builtin_sycl_unique_stable_name, KEYSYCL) // Clang-specific keywords enabled only in testing. TESTING_KEYWORD(__unknown_anytype , KEYALL) +/* TO_UPSTREAM(BoundsSafety) ON */ +// Bounds-safety extensions. +KEYWORD(__builtin_unsafe_forge_bidi_indexable , KEYBOUNDSSAFETY) +KEYWORD(__builtin_unsafe_forge_single , KEYBOUNDSSAFETYATTRIBUTES) +KEYWORD(__builtin_unsafe_forge_terminated_by , KEYBOUNDSSAFETYATTRIBUTES) +KEYWORD(__builtin_get_pointer_lower_bound , KEYBOUNDSSAFETY) +KEYWORD(__builtin_get_pointer_upper_bound , KEYBOUNDSSAFETY) +KEYWORD(__builtin_terminated_by_to_indexable , KEYBOUNDSSAFETY) +KEYWORD(__builtin_unsafe_terminated_by_to_indexable , KEYBOUNDSSAFETY) +KEYWORD(__builtin_unsafe_terminated_by_from_indexable, KEYBOUNDSSAFETY) +/* TO_UPSTREAM(BoundsSafety) OFF */ //===----------------------------------------------------------------------===// // Objective-C @-preceded keywords. diff --git a/clang/include/clang/Basic/TypeNodes.td b/clang/include/clang/Basic/TypeNodes.td index fee49cf4326df..4f20727d23bf3 100644 --- a/clang/include/clang/Basic/TypeNodes.td +++ b/clang/include/clang/Basic/TypeNodes.td @@ -111,6 +111,10 @@ def ObjCInterfaceType : TypeNode<ObjCObjectType>, LeafType; def ObjCObjectPointerType : TypeNode<Type>; def BoundsAttributedType : TypeNode<Type, 1>; def CountAttributedType : TypeNode<BoundsAttributedType>, NeverCanonical; +// TO_UPSTREAM(BoundsSafety) ON +def DynamicRangePointerType : TypeNode<BoundsAttributedType>, NeverCanonical; +def ValueTerminatedType : TypeNode<Type>, NeverCanonical; +// TO_UPSTREAM(BoundsSafety) OFF def PipeType : TypeNode<Type>; def AtomicType : TypeNode<Type>; def BitIntType : TypeNode<Type>; diff --git a/clang/include/clang/Basic/Version.h b/clang/include/clang/Basic/Version.h index 8e4e6928fded5..bbfb1052447e2 100644 --- a/clang/include/clang/Basic/Version.h +++ b/clang/include/clang/Basic/Version.h @@ -59,6 +59,9 @@ namespace clang { /// for use in the CPP __VERSION__ macro, which includes the clang version /// number, the repository version, and the vendor tag. std::string getClangFullCPPVersion(); + + /// Returns the major version number of clang. + unsigned getClangMajorVersionNumber(); } #endif // LLVM_CLANG_BASIC_VERSION_H diff --git a/clang/include/clang/Basic/arm_sve.td b/clang/include/clang/Basic/arm_sve.td index 94c093d891156..fb11d743fd647 100644 --- a/clang/include/clang/Basic/arm_sve.td +++ b/clang/include/clang/Basic/arm_sve.td @@ -2116,7 +2116,7 @@ def SVFCLAMP_BF : SInst<"svclamp[_{d}]", "dddd", "b", MergeNone, "aarch64_sve_ multiclass MinMaxIntr<string i, string zm, string mul, string t> { def SVS # NAME : SInst<"sv" # i # "[" # zm # "_{d}_" # mul # "]", t, "csil", MergeNone, "aarch64_sve_s" # i # zm # "_" # mul, [IsStreaming], []>; def SVU # NAME : SInst<"sv" # i # "[" # zm # "_{d}_" # mul # "]", t, "UcUsUiUl", MergeNone, "aarch64_sve_u" # i # zm # "_" # mul, [IsStreaming], []>; - def SVF # NAME : SInst<"sv" # i # "[" # zm # "_{d}_" # mul # "]", t, "bhfd", MergeNone, "aarch64_sve_f" # i # zm # "_" # mul, [IsStreaming], []>; + def SVF # NAME : SInst<"sv" # i # "[" # zm # "_{d}_" # mul # "]", t, "hfd", MergeNone, "aarch64_sve_f" # i # zm # "_" # mul, [IsStreaming], []>; } let SVETargetGuard = InvalidMode, SMETargetGuard = "sme2" in { @@ -2134,11 +2134,11 @@ let SVETargetGuard = InvalidMode, SMETargetGuard = "sme2" in { } multiclass SInstMinMaxByVector<string name> { - def NAME # _SINGLE_X2 : SInst<"sv" # name # "nm[_single_{d}_x2]", "22d", "bhfd", MergeNone, "aarch64_sve_f" # name # "nm_single_x2", [IsStreaming], []>; - def NAME # _SINGLE_X4 : SInst<"sv" # name # "nm[_single_{d}_x4]", "44d", "bhfd", MergeNone, "aarch64_sve_f" # name # "nm_single_x4", [IsStreaming], []>; + def NAME # _SINGLE_X2 : SInst<"sv" # name # "nm[_single_{d}_x2]", "22d", "hfd", MergeNone, "aarch64_sve_f" # name # "nm_single_x2", [IsStreaming], []>; + def NAME # _SINGLE_X4 : SInst<"sv" # name # "nm[_single_{d}_x4]", "44d", "hfd", MergeNone, "aarch64_sve_f" # name # "nm_single_x4", [IsStreaming], []>; - def NAME # _X2 : SInst<"sv" # name # "nm[_{d}_x2]", "222", "bhfd", MergeNone, "aarch64_sve_f" # name # "nm_x2", [IsStreaming], []>; - def NAME # _X4 : SInst<"sv" # name # "nm[_{d}_x4]", "444", "bhfd", MergeNone, "aarch64_sve_f" # name # "nm_x4", [IsStreaming], []>; + def NAME # _X2 : SInst<"sv" # name # "nm[_{d}_x2]", "222", "hfd", MergeNone, "aarch64_sve_f" # name # "nm_x2", [IsStreaming], []>; + def NAME # _X4 : SInst<"sv" # name # "nm[_{d}_x4]", "444", "hfd", MergeNone, "aarch64_sve_f" # name # "nm_x4", [IsStreaming], []>; } let SVETargetGuard = InvalidMode, SMETargetGuard = "sme2" in { @@ -2172,9 +2172,25 @@ let SVETargetGuard = InvalidMode, SMETargetGuard = "sme2" in { def SVFCLAMP_X4 : SInst<"svclamp[_single_{d}_x4]", "44dd", "hfd", MergeNone, "aarch64_sve_fclamp_single_x4", [IsStreaming], []>; } +multiclass BfSingleMultiVector<string name> { + def NAME # _SINGLE_X2 : SInst<"sv" # name # "[_single_{d}_x2]", "22d", "b", MergeNone, "aarch64_sve_f" # name # "_single_x2", [IsStreaming], []>; + def NAME # _SINGLE_X4 : SInst<"sv" # name # "[_single_{d}_x4]", "44d", "b", MergeNone, "aarch64_sve_f" # name # "_single_x4", [IsStreaming], []>; + + def NAME # _X2 : SInst<"sv" # name # "[_{d}_x2]", "222", "b", MergeNone, "aarch64_sve_f" # name # "_x2", [IsStreaming], []>; + def NAME # _X4 : SInst<"sv" # name # "[_{d}_x4]", "444", "b", MergeNone, "aarch64_sve_f" # name # "_x4", [IsStreaming], []>; +} + let SVETargetGuard = InvalidMode, SMETargetGuard = "sme2,b16b16"in { def SVBFCLAMP_X2 : SInst<"svclamp[_single_{d}_x2]", "22dd", "b", MergeNone, "aarch64_sve_bfclamp_single_x2", [IsStreaming], []>; def SVBFCLAMP_X4 : SInst<"svclamp[_single_{d}_x4]", "44dd", "b", MergeNone, "aarch64_sve_bfclamp_single_x4", [IsStreaming], []>; + + // bfmin, bfmax (single, multi) + defm SVBFMIN : BfSingleMultiVector<"min">; + defm SVBFMAX : BfSingleMultiVector<"max">; + + // bfminnm, bfmaxnm (single, multi) + defm SVBFMINNM : BfSingleMultiVector<"minnm">; + defm SVBFMAXNM : BfSingleMultiVector<"maxnm">; } let SVETargetGuard = InvalidMode, SMETargetGuard = "sme2" in { diff --git a/clang/include/clang/CAS/CASOptions.h b/clang/include/clang/CAS/CASOptions.h new file mode 100644 index 0000000000000..03fb00a7fbff0 --- /dev/null +++ b/clang/include/clang/CAS/CASOptions.h @@ -0,0 +1,138 @@ +//===- CASOptions.h - Options for configuring the CAS -----------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Defines the clang::CASOptions interface. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_CAS_CASOPTIONS_H +#define LLVM_CLANG_CAS_CASOPTIONS_H + +#include "llvm/ADT/SmallVector.h" +#include "llvm/Support/Error.h" +#include <string> +#include <vector> + +namespace llvm { +namespace cas { +class ActionCache; +class ObjectStore; +} // end namespace cas +} // end namespace llvm + +namespace clang { + +class DiagnosticsEngine; + +/// Base class for options configuring which CAS to use. Separated for the +/// fields where we don't need special move/copy logic. +/// +/// TODO: Add appropriate options once we support plugins. +class CASConfiguration { +public: + enum CASKind { + UnknownCAS, + InMemoryCAS, + OnDiskCAS, + }; + + /// Kind of CAS to use. + CASKind getKind() const { + return IsFrozen ? UnknownCAS : CASPath.empty() ? InMemoryCAS : OnDiskCAS; + } + + /// Path to a persistent backing store on-disk. This is optional, although \a + /// CASFileSystemRootID is unlikely to work without it. + /// + /// - "" means there is none; falls back to in-memory. + /// - "auto" is an alias for an automatically chosen location in the user's + /// system cache. + std::string CASPath; + + std::string PluginPath; + /// Each entry is a (<option-name>, <value>) pair. + std::vector<std::pair<std::string, std::string>> PluginOptions; + + friend bool operator==(const CASConfiguration &LHS, + const CASConfiguration &RHS) { + return LHS.CASPath == RHS.CASPath && LHS.PluginPath == RHS.PluginPath && + LHS.PluginOptions == RHS.PluginOptions; + } + friend bool operator!=(const CASConfiguration &LHS, + const CASConfiguration &RHS) { + return !(LHS == RHS); + } + +private: + /// Whether the configuration has been "frozen", in order to hide the kind of + /// CAS that's in use. + bool IsFrozen = false; + friend class CASOptions; +}; + +/// Options configuring which CAS to use. User-accessible fields should be +/// defined in CASConfiguration to enable caching a CAS instance. +/// +/// CASOptions includes \a getOrCreateDatabases() for creating CAS and +/// ActionCache. +/// +/// FIXME: The the caching is done here, instead of as a field in \a +/// CompilerInstance, in order to ensure that \a +/// clang::createVFSFromCompilerInvocation() uses the same CAS instance that +/// the rest of the compiler job does, without updating all callers. Probably +/// it would be better to update all callers and remove it from here. +class CASOptions : public CASConfiguration { +public: + /// Get a CAS & ActionCache defined by the options above. Future calls will + /// return the same instances... unless the configuration has changed, in + /// which case new ones will be created. + /// + /// If \p CreateEmptyDBsOnFailure, returns empty in-memory databases on + /// failure. Else, returns \c nullptr on failure. + std::pair<std::shared_ptr<llvm::cas::ObjectStore>, + std::shared_ptr<llvm::cas::ActionCache>> + getOrCreateDatabases(DiagnosticsEngine &Diags, + bool CreateEmptyDBsOnFailure = false) const; + + llvm::Expected<std::pair<std::shared_ptr<llvm::cas::ObjectStore>, + std::shared_ptr<llvm::cas::ActionCache>>> + getOrCreateDatabases() const; + + /// Freeze CAS Configuration. Future calls will return the same + /// CAS instance, even if the configuration changes again later. + /// + /// The configuration will be wiped out to prevent it being observable or + /// affecting the output of something that takes \a CASOptions as an input. + /// This also "locks in" the return value of \a getOrCreateDatabases(): + /// future calls will not check if the configuration has changed. + void freezeConfig(DiagnosticsEngine &Diags); + + /// If the configuration is not for a persistent store, it modifies it to the + /// default on-disk CAS, otherwise this is a noop. + void ensurePersistentCAS(); + +private: + /// Initialize Cached CAS and ActionCache. + llvm::Error initCache() const; + + struct CachedCAS { + /// A cached CAS instance. + std::shared_ptr<llvm::cas::ObjectStore> CAS; + /// An ActionCache instnace. + std::shared_ptr<llvm::cas::ActionCache> AC; + + /// Remember how the CAS was created. + CASConfiguration Config; + }; + mutable CachedCAS Cache; +}; + +} // end namespace clang + +#endif // LLVM_CLANG_CAS_CASOPTIONS_H diff --git a/clang/include/clang/CAS/IncludeTree.h b/clang/include/clang/CAS/IncludeTree.h new file mode 100644 index 0000000000000..761f55c3a29d3 --- /dev/null +++ b/clang/include/clang/CAS/IncludeTree.h @@ -0,0 +1,881 @@ +//===- IncludeTree.h - Include-tree CAS graph -------------------*- 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_CAS_CASINCLUDETREE_H +#define LLVM_CLANG_CAS_CASINCLUDETREE_H + +#include "clang/Basic/SourceManager.h" +#include "llvm/CAS/ObjectStore.h" + +namespace llvm { +class SmallBitVector; +} + +namespace clang { +namespace cas { + +/// Base class for include-tree related nodes. It makes it convenient to +/// add/skip/check the "node kind identifier" (\p getNodeKind()) that is put +/// at the beginning of the object data for every include-tree related node. +template <typename NodeT> class IncludeTreeBase : public ObjectProxy { +protected: + explicit IncludeTreeBase(ObjectProxy Node) : ObjectProxy(std::move(Node)) { + assert(isValid(*this)); + } + + StringRef getData() const { + return ObjectProxy::getData().substr(NodeT::getNodeKind().size()); + } + + static Expected<NodeT> create(ObjectStore &DB, ArrayRef<ObjectRef> Refs, + ArrayRef<char> Data); + + static bool isValid(const ObjectProxy &Node) { + return Node.getData().starts_with(NodeT::getNodeKind()); + } + + friend NodeT; +}; + +/// Represents a DAG of included files by the preprocessor and module imports. +/// Each node in the DAG represents a particular inclusion of a file or module +/// that encompasses inclusions of other files as sub-trees, along with all the +/// \p __has_include() preprocessor checks that occurred during preprocessing +/// of that file. +class IncludeTree : public IncludeTreeBase<IncludeTree> { +public: + static constexpr StringRef getNodeKind() { return "Tree"; } + + class File; + class FileList; + class Node; + class Module; + class ModuleImport; + class SpuriousImport; + class ModuleMap; + class APINotes; + + Expected<File> getBaseFile(); + + /// The include file that resulted in this include-tree. + ObjectRef getBaseFileRef() const { return getReference(0); } + + struct FileInfo { + StringRef Filename; + StringRef Contents; + }; + + Expected<FileInfo> getBaseFileInfo(); + + SrcMgr::CharacteristicKind getFileCharacteristic() const { + return (SrcMgr::CharacteristicKind)(getData().front() & ~IsSubmoduleBit); + } + + bool isSubmodule() const { return (getData().front() & IsSubmoduleBit) != 0; } + + std::optional<ObjectRef> getSubmoduleNameRef() const { + if (isSubmodule()) + return getReference(getNumReferences() - 1); + return std::nullopt; + } + + /// If \c getBaseFile() is a modular header, get its submodule name. + Expected<std::optional<StringRef>> getSubmoduleName() { + auto Ref = getSubmoduleNameRef(); + if (!Ref) + return std::nullopt; + auto Node = getCAS().getProxy(*Ref); + if (!Node) + return Node.takeError(); + return Node->getData(); + } + + size_t getNumIncludes() const { + return getNumReferences() - (isSubmodule() ? 2 : 1); + }; + + ObjectRef getIncludeRef(size_t I) const { + assert(I < getNumIncludes()); + return getReference(I + 1); + } + + enum class NodeKind : uint8_t { + Tree, + ModuleImport, + SpuriousImport, + }; + + /// The kind of node included at the given index. + NodeKind getIncludeKind(size_t I) const; + + /// The sub-include-tree or module import for the given index. + Expected<Node> getIncludeNode(size_t I); + + /// The sub-include-trees of included files, in the order that they occurred. + Expected<IncludeTree> getIncludeTree(size_t I) { + assert(getIncludeKind(I) == NodeKind::Tree); + return getIncludeTree(getIncludeRef(I)); + } + + /// The source byte offset for a particular include, pointing to the beginning + /// of the line just after the #include directive. The offset represents the + /// location at which point the include-tree would have been processed by the + /// preprocessor and parser. + /// + /// For example: + /// \code + /// #include "a.h" -> include-tree("a.h") + /// | <- include-tree-offset("a.h") + /// \endcode + /// + /// Using the "after #include" offset makes it trivial to identify the part + /// of the source file that encompasses a set of include-trees (for example in + /// the case where want to identify the "includes preamble" of the main file. + uint32_t getIncludeOffset(size_t I) const; + + /// The \p __has_include() preprocessor checks, in the order that they + /// occurred. The source offsets for the checks are not tracked, "replaying" + /// the include-tree depends on the invariant that the same exact checks will + /// occur in the same order. + bool getCheckResult(size_t I) const; + + /// Passes pairs of (Node, include offset) to \p Callback. + llvm::Error forEachInclude( + llvm::function_ref<llvm::Error(std::pair<Node, uint32_t>)> Callback); + + struct IncludeInfo { + ObjectRef Ref; ///< IncludeTree or IncludeTreeModuleImport + uint32_t Offset; + NodeKind Kind; + }; + + static Expected<IncludeTree> + create(ObjectStore &DB, SrcMgr::CharacteristicKind FileCharacteristic, + ObjectRef BaseFile, ArrayRef<IncludeInfo> Includes, + std::optional<ObjectRef> SubmoduleName, llvm::SmallBitVector Checks); + + static Expected<IncludeTree> get(ObjectStore &DB, ObjectRef Ref); + + llvm::Error print(llvm::raw_ostream &OS, unsigned Indent = 0); + +private: + friend class IncludeTreeBase<IncludeTree>; + friend class IncludeTreeRoot; + + static constexpr char IsSubmoduleBit = 0x80; + + explicit IncludeTree(ObjectProxy Node) : IncludeTreeBase(std::move(Node)) { + assert(isValid(*this)); + } + + Expected<IncludeTree> getIncludeTree(ObjectRef Ref) { + auto Node = getCAS().getProxy(Ref); + if (!Node) + return Node.takeError(); + return IncludeTree(std::move(*Node)); + } + + Expected<Node> getIncludeNode(ObjectRef Ref, NodeKind Kind); + + StringRef dataSkippingFlags() const { return getData().drop_front(); } + StringRef dataSkippingIncludes() const { + return dataSkippingFlags().drop_front(getNumIncludes() * + (sizeof(uint32_t) + 1)); + } + + static bool isValid(const ObjectProxy &Node); + static bool isValid(ObjectStore &DB, ObjectRef Ref) { + auto Node = DB.getProxy(Ref); + if (!Node) { + llvm::consumeError(Node.takeError()); + return false; + } + return isValid(*Node); + } +}; + +/// Represents a \p SourceManager file (or buffer in the case of preprocessor +/// predefines) that got included by the preprocessor. +class IncludeTree::File : public IncludeTreeBase<File> { +public: + static constexpr StringRef getNodeKind() { return "File"; } + + ObjectRef getFilenameRef() const { return getReference(0); } + ObjectRef getContentsRef() const { return getReference(1); } + + Expected<ObjectProxy> getFilename() { + return getCAS().getProxy(getFilenameRef()); + } + + Expected<ObjectProxy> getContents() { + return getCAS().getProxy(getContentsRef()); + } + + Expected<FileInfo> getFileInfo() { + auto Filename = getFilename(); + if (!Filename) + return Filename.takeError(); + auto Contents = getContents(); + if (!Contents) + return Contents.takeError(); + return IncludeTree::FileInfo{Filename->getData(), Contents->getData()}; + } + + Expected<std::unique_ptr<llvm::MemoryBuffer>> getMemoryBuffer() { + auto Filename = getFilename(); + if (!Filename) + return Filename.takeError(); + auto Contents = getContents(); + if (!Contents) + return Contents.takeError(); + return Contents->getMemoryBuffer(Filename->getData()); + } + + static Expected<File> create(ObjectStore &DB, StringRef Filename, + ObjectRef Contents); + + static Expected<File> get(ObjectStore &DB, ObjectRef Ref) { + auto Node = DB.getProxy(Ref); + if (!Node) + return Node.takeError(); + return File(std::move(*Node)); + } + + llvm::Error print(llvm::raw_ostream &OS, unsigned Indent = 0); + + static bool isValid(const ObjectProxy &Node) { + if (!IncludeTreeBase::isValid(Node)) + return false; + IncludeTreeBase Base(Node); + return Base.getNumReferences() == 2 && Base.getData().empty(); + } + static bool isValid(ObjectStore &DB, ObjectRef Ref) { + auto Node = DB.getProxy(Ref); + if (!Node) { + llvm::consumeError(Node.takeError()); + return false; + } + return isValid(*Node); + } + +private: + friend class IncludeTreeBase<File>; + friend class FileList; + friend class IncludeTree; + friend class IncludeTreeRoot; + + explicit File(ObjectProxy Node) : IncludeTreeBase(std::move(Node)) { + assert(isValid(*this)); + } +}; + +/// A list of \c File entries. Multiple \c FileList can be combined without +/// copying their contents. This is used along with a simple implementation of a +/// \p vfs::FileSystem produced via \p createIncludeTreeFileSystem(). +class IncludeTree::FileList : public IncludeTreeBase<FileList> { +public: + static constexpr StringRef getNodeKind() { return "List"; } + + using FileSizeTy = uint32_t; + + /// \returns each \c File entry along with its file size. + llvm::Error + forEachFile(llvm::function_ref<llvm::Error(File, FileSizeTy)> Callback); + + /// We record the file size as well to avoid needing to materialize the + /// underlying buffer for the \p IncludeTreeFileSystem::status() + /// implementation to provide the file size. + struct FileEntry { + ObjectRef FileRef; + FileSizeTy Size; + }; + static Expected<FileList> create(ObjectStore &DB, ArrayRef<FileEntry> Files, + ArrayRef<ObjectRef> FileLists); + + static Expected<FileList> get(ObjectStore &CAS, ObjectRef Ref); + + llvm::Error print(llvm::raw_ostream &OS, unsigned Indent = 0); + +private: + friend class IncludeTreeBase<FileList>; + friend class IncludeTreeRoot; + + explicit FileList(ObjectProxy Node) : IncludeTreeBase(std::move(Node)) { + assert(isValid(*this)); + } + + size_t getNumFilesCurrentList() const; + FileSizeTy getFileSize(size_t I) const; + + llvm::Error + forEachFileImpl(llvm::DenseSet<ObjectRef> &Seen, + llvm::function_ref<llvm::Error(File, FileSizeTy)> Callback); + + static bool isValid(const ObjectProxy &Node); + static bool isValid(ObjectStore &CAS, ObjectRef Ref) { + auto Node = CAS.getProxy(Ref); + if (!Node) { + llvm::consumeError(Node.takeError()); + return false; + } + return isValid(*Node); + } +}; + +/// Represents a module imported by an IncludeTree. +class IncludeTree::ModuleImport : public IncludeTreeBase<ModuleImport> { +public: + static constexpr StringRef getNodeKind() { return "ModI"; } + + static Expected<ModuleImport> create(ObjectStore &DB, StringRef ModuleName, + bool VisibilityOnly); + + StringRef getModuleName() const { return getData().drop_front(); } + /// Whether this module should only be "marked visible" rather than imported. + bool visibilityOnly() const { return (bool)getData()[0]; } + + llvm::Error print(llvm::raw_ostream &OS, unsigned Indent = 0, + char End = '\n'); + + static bool isValid(const ObjectProxy &Node) { + if (!IncludeTreeBase::isValid(Node)) + return false; + IncludeTreeBase Base(Node); + return Base.getNumReferences() == 0 && Base.getData().size() > 1; + } + static bool isValid(ObjectStore &DB, ObjectRef Ref) { + auto Node = DB.getProxy(Ref); + if (!Node) { + llvm::consumeError(Node.takeError()); + return false; + } + return isValid(*Node); + } + +private: + friend class IncludeTreeBase<ModuleImport>; + friend class SpuriousImport; + friend class Node; + + explicit ModuleImport(ObjectProxy Node) : IncludeTreeBase(std::move(Node)) { + assert(isValid(*this)); + } +}; + +class IncludeTree::SpuriousImport + : public IncludeTreeBase<SpuriousImport> { +public: + static Expected<SpuriousImport> + create(ObjectStore &DB, ObjectRef ImportRef, ObjectRef TreeRef); + + static constexpr StringRef getNodeKind() { return "SpIm"; } + + Expected<ModuleImport> getModuleImport() { + std::optional<ObjectProxy> Proxy; + if (llvm::Error Err = getCAS().getProxy(getReference(0)).moveInto(Proxy)) + return std::move(Err); + return ModuleImport(*Proxy); + } + + Expected<IncludeTree> getIncludeTree() { + std::optional<ObjectProxy> Proxy; + if (llvm::Error Err = getCAS().getProxy(getReference(1)).moveInto(Proxy)) + return std::move(Err); + return IncludeTree(*Proxy); + } + + llvm::Error print(llvm::raw_ostream &OS, unsigned Indent = 0); + + static bool isValid(const ObjectProxy &Node) { + if (!IncludeTreeBase::isValid(Node)) + return false; + IncludeTreeBase Base(Node); + if (Base.getNumReferences() != 2 && Base.getData().size() != 0) + return false; + return ModuleImport::isValid(Base.getCAS(), Base.getReference(0)) && + IncludeTree::isValid(Base.getCAS(), Base.getReference(1)); + } + + static bool isValid(ObjectStore &DB, ObjectRef Ref) { + auto Node = DB.getProxy(Ref); + if (!Node) { + llvm::consumeError(Node.takeError()); + return false; + } + return isValid(*Node); + } + +private: + friend class IncludeTreeBase; + friend class Node; + + explicit SpuriousImport(ObjectProxy Node) : IncludeTreeBase(std::move(Node)) { + assert(isValid(*this)); + } +}; + +/// Represents an \c IncludeTree or \c ModuleImport. +class IncludeTree::Node { +public: + IncludeTree getIncludeTree() const { + assert(K == NodeKind::Tree); + return IncludeTree(N); + } + ModuleImport getModuleImport() const { + assert(K == NodeKind::ModuleImport); + return ModuleImport(N); + } + SpuriousImport getSpuriousImport() const { + assert(K == NodeKind::SpuriousImport); + return SpuriousImport(N); + } + NodeKind getKind() const { return K; } + + llvm::Error print(llvm::raw_ostream &OS, unsigned Indent = 0); + +private: + friend class IncludeTree; + Node(ObjectProxy N, NodeKind K) : N(std::move(N)), K(K) {} + ObjectProxy N; + NodeKind K; +}; + +/// Module or submodule declaration as part of the \c IncludeTreeRoot module +/// map structure. +class IncludeTree::Module : public IncludeTreeBase<Module> { +public: + static constexpr StringRef getNodeKind() { return "Modu"; } + + class ExportList; + class LinkLibraryList; + + struct ModuleFlags { + bool IsFramework : 1; + bool IsExplicit : 1; + bool IsExternC : 1; + bool IsSystem : 1; + bool InferSubmodules : 1; + bool InferExplicitSubmodules : 1; + bool InferExportWildcard : 1; + bool UseExportAsModuleLinkName: 1; + ModuleFlags() + : IsFramework(false), IsExplicit(false), IsExternC(false), + IsSystem(false), InferSubmodules(false), + InferExplicitSubmodules(false), InferExportWildcard(false), + UseExportAsModuleLinkName(false) {} + }; + + ModuleFlags getFlags() const; + + /// The name of the current (sub)module. + StringRef getName() const { + return dataAfterFlags().split('\0').first; + } + + StringRef getExportAsModule() const { + return dataAfterFlags().split('\0').second; + } + + size_t getNumSubmodules() const; + + Expected<Module> getSubmodule(size_t I) { + assert(I < getNumSubmodules()); + auto Node = getCAS().getProxy(getReference(I)); + if (!Node) + return Node.takeError(); + return Module(*Node); + } + + llvm::Error forEachSubmodule(llvm::function_ref<llvm::Error(Module)> CB); + + std::optional<ObjectRef> getExportsRef() const { + if (std::optional<unsigned> Index = getExportsIndex()) + return getReference(*Index); + return std::nullopt; + } + std::optional<ObjectRef> getLinkLibrariesRef() const { + if (std::optional<unsigned> Index = getLinkLibrariesIndex()) + return getReference(*Index); + return std::nullopt; + } + + /// The list of modules that this submodule re-exports. + Expected<std::optional<ExportList>> getExports(); + + /// The list of modules that this submodule re-exports. + Expected<std::optional<LinkLibraryList>> getLinkLibraries(); + + llvm::Error print(llvm::raw_ostream &OS, unsigned Indent = 0); + + static Expected<Module> create(ObjectStore &DB, StringRef ModuleName, + StringRef ExportAs, ModuleFlags Flags, + ArrayRef<ObjectRef> Submodules, + std::optional<ObjectRef> ExportList, + std::optional<ObjectRef> LinkLibraries); + + static bool isValid(const ObjectProxy &Node) { + if (!IncludeTreeBase::isValid(Node)) + return false; + IncludeTreeBase Base(Node); + return Base.getData().size() > 2; + } + static bool isValid(ObjectStore &DB, ObjectRef Ref) { + auto Node = DB.getProxy(Ref); + if (!Node) { + llvm::consumeError(Node.takeError()); + return false; + } + return isValid(*Node); + } + +private: + uint16_t rawFlags() const; + StringRef dataAfterFlags() const { return getData().drop_front(2); } + bool hasExports() const; + bool hasLinkLibraries() const; + std::optional<unsigned> getExportsIndex() const; + std::optional<unsigned> getLinkLibrariesIndex() const; + + explicit Module(ObjectProxy Node) : IncludeTreeBase(std::move(Node)) { + assert(isValid(*this)); + } + + friend class IncludeTreeBase<Module>; + friend class IncludeTreeRoot; + friend class ModuleMap; +}; + +/// The set of modules re-exported by another module. +class IncludeTree::Module::ExportList : public IncludeTreeBase<ExportList> { +public: + static constexpr StringRef getNodeKind() { return "ExpL"; } + + /// An explicit export. + struct Export { + StringRef ModuleName; + bool Wildcard; + }; + + /// Whether this module exports all imported modules (`export *`). + bool hasGlobalWildcard() const; + + size_t getNumExplicitExports() const { return getNumReferences(); } + + /// Whether the explicit export at \p I has a wildcard (`export MyModule.*`). + bool exportHasWildcard(size_t I) const; + + Expected<Export> getExplicitExport(size_t I); + + /// Calls \p CB for each explicit export declaration. + llvm::Error forEachExplicitExport(llvm::function_ref<llvm::Error(Export)> CB); + + llvm::Error print(llvm::raw_ostream &OS, unsigned Indent = 0); + + static Expected<ExportList> create(ObjectStore &DB, ArrayRef<Export> Exports, + bool GlobalWildcard); + + static bool isValid(const ObjectProxy &Node) { + if (!IncludeTreeBase::isValid(Node)) + return false; + IncludeTreeBase Base(Node); + size_t ExpectedBitSize = Base.getNumReferences() + 1; + size_t ExpectedSize = + ExpectedBitSize / 8 + (ExpectedBitSize % 8 == 0 ? 0 : 1); + return Base.getData().size() == ExpectedSize; + } + static bool isValid(ObjectStore &DB, ObjectRef Ref) { + auto Node = DB.getProxy(Ref); + if (!Node) { + llvm::consumeError(Node.takeError()); + return false; + } + return isValid(*Node); + } + +private: + explicit ExportList(ObjectProxy Node) : IncludeTreeBase(std::move(Node)) { + assert(isValid(*this)); + } + + friend class IncludeTreeBase<ExportList>; + friend class Module; +}; + +/// The set of libraries to link against when using a module. +class IncludeTree::Module::LinkLibraryList + : public IncludeTreeBase<LinkLibraryList> { +public: + static constexpr StringRef getNodeKind() { return "LnkL"; } + + struct LinkLibrary { + StringRef Library; + bool IsFramework; + }; + + size_t getNumLibraries() const { return getNumReferences(); } + + /// Whether the library at \p I is a framework. + bool isFramework(size_t I) const; + + ObjectRef getLibraryNameRef(size_t I) const { return getReference(I); } + + Expected<LinkLibrary> getLinkLibrary(size_t I) { + auto Name = getCAS().getProxy(getLibraryNameRef(I)); + if (!Name) + return Name.takeError(); + return LinkLibrary{Name->getData(), isFramework(I)}; + } + + /// Calls \p CB for each link libary. + llvm::Error + forEachLinkLibrary(llvm::function_ref<llvm::Error(LinkLibrary)> CB); + + llvm::Error print(llvm::raw_ostream &OS, unsigned Indent = 0); + + static Expected<LinkLibraryList> create(ObjectStore &DB, + ArrayRef<LinkLibrary> Exports); + + static bool isValid(const ObjectProxy &Node) { + if (!IncludeTreeBase::isValid(Node)) + return false; + IncludeTreeBase Base(Node); + size_t ExpectedBitSize = Base.getNumReferences(); + size_t ExpectedSize = + ExpectedBitSize / 8 + (ExpectedBitSize % 8 == 0 ? 0 : 1); + return Base.getData().size() == ExpectedSize; + } + static bool isValid(ObjectStore &DB, ObjectRef Ref) { + auto Node = DB.getProxy(Ref); + if (!Node) { + llvm::consumeError(Node.takeError()); + return false; + } + return isValid(*Node); + } + +private: + explicit LinkLibraryList(ObjectProxy Node) + : IncludeTreeBase(std::move(Node)) { + assert(isValid(*this)); + } + + friend class IncludeTreeBase<LinkLibraryList>; + friend class Module; +}; + +class IncludeTree::ModuleMap : public IncludeTreeBase<ModuleMap> { +public: + static constexpr StringRef getNodeKind() { return "ModM"; } + + size_t getNumModules() const { return getNumReferences(); } + + Expected<Module> getModule(size_t I) { + auto Node = getCAS().getProxy(getReference(I)); + if (!Node) + return Node.takeError(); + return Module(*Node); + } + + /// Calls \p CB for each module declaration. + llvm::Error forEachModule(llvm::function_ref<llvm::Error(Module)> CB); + + llvm::Error print(llvm::raw_ostream &OS, unsigned Indent = 0); + + static Expected<ModuleMap> create(ObjectStore &DB, + ArrayRef<ObjectRef> Modules); + + static bool isValid(const ObjectProxy &Node) { + if (!IncludeTreeBase::isValid(Node)) + return false; + IncludeTreeBase Base(Node); + return Base.getData().empty(); + } + static bool isValid(ObjectStore &DB, ObjectRef Ref) { + auto Node = DB.getProxy(Ref); + if (!Node) { + llvm::consumeError(Node.takeError()); + return false; + } + return isValid(*Node); + } + +private: + explicit ModuleMap(ObjectProxy Node) : IncludeTreeBase(std::move(Node)) { + assert(isValid(*this)); + } + + friend class IncludeTreeBase<ModuleMap>; + friend class IncludeTreeRoot; +}; + +/// A list of \c APINotes that is compiled and loaded. +class IncludeTree::APINotes : public IncludeTreeBase<APINotes> { +public: + static constexpr StringRef getNodeKind() { return "APIN"; } + + llvm::Error + forEachAPINotes(llvm::function_ref<llvm::Error(StringRef)> Callback); + + static Expected<APINotes> create(ObjectStore &DB, + ArrayRef<ObjectRef> APINoteList); + + static Expected<APINotes> get(ObjectStore &CAS, ObjectRef Ref); + + llvm::Error print(llvm::raw_ostream &OS, unsigned Indent = 0); + +private: + friend class IncludeTreeBase<APINotes>; + friend class IncludeTreeRoot; + + explicit APINotes(ObjectProxy Node) : IncludeTreeBase(std::move(Node)) { + assert(isValid(*this)); + } + + static bool isValid(const ObjectProxy &Node) { + if (!IncludeTreeBase::isValid(Node)) + return false; + IncludeTreeBase Base(Node); + return Base.getData().empty() && Base.getNumReferences() < 2; + } + + static bool isValid(ObjectStore &CAS, ObjectRef Ref) { + auto Node = CAS.getProxy(Ref); + if (!Node) { + llvm::consumeError(Node.takeError()); + return false; + } + return isValid(*Node); + } +}; + +/// Represents the include-tree result for a translation unit. +class IncludeTreeRoot : public IncludeTreeBase<IncludeTreeRoot> { +public: + static constexpr StringRef getNodeKind() { return "Root"; } + + ObjectRef getMainFileTreeRef() const { return getReference(0); } + + ObjectRef getFileListRef() const { return getReference(1); } + + std::optional<ObjectRef> getPCHRef() const { + if (auto Index = getPCHRefIndex()) + return getReference(*Index); + return std::nullopt; + } + + std::optional<ObjectRef> getModuleMapRef() const { + if (auto Index = getModuleMapRefIndex()) + return getReference(*Index); + return std::nullopt; + } + + std::optional<ObjectRef> getAPINotesRef() const { + if (auto Index = getAPINotesRefIndex()) + return getReference(*Index); + return std::nullopt; + } + + Expected<IncludeTree> getMainFileTree() { + auto Node = getCAS().getProxy(getMainFileTreeRef()); + if (!Node) + return Node.takeError(); + return IncludeTree(std::move(*Node)); + } + + Expected<IncludeTree::FileList> getFileList() { + auto Node = getCAS().getProxy(getFileListRef()); + if (!Node) + return Node.takeError(); + return IncludeTree::FileList(std::move(*Node)); + } + + Expected<std::optional<IncludeTree::File>> getPCH() { + if (std::optional<ObjectRef> Ref = getPCHRef()) { + auto Node = getCAS().getProxy(*Ref); + if (!Node) + return Node.takeError(); + return IncludeTree::File(std::move(*Node)); + } + return std::nullopt; + } + + Expected<std::optional<IncludeTree::ModuleMap>> getModuleMap() { + if (std::optional<ObjectRef> Ref = getModuleMapRef()) { + auto Node = getCAS().getProxy(*Ref); + if (!Node) + return Node.takeError(); + return IncludeTree::ModuleMap(*Node); + } + return std::nullopt; + } + + Expected<std::optional<IncludeTree::APINotes>> getAPINotes() { + if (std::optional<ObjectRef> Ref = getAPINotesRef()) { + auto Node = getCAS().getProxy(*Ref); + if (!Node) + return Node.takeError(); + return IncludeTree::APINotes(*Node); + } + return std::nullopt; + } + + static Expected<IncludeTreeRoot> + create(ObjectStore &DB, ObjectRef MainFileTree, ObjectRef FileList, + std::optional<ObjectRef> PCHRef, std::optional<ObjectRef> ModuleMapRef, + std::optional<ObjectRef> APINotesRef); + + static Expected<IncludeTreeRoot> get(ObjectStore &DB, ObjectRef Ref); + + llvm::Error print(llvm::raw_ostream &OS, unsigned Indent = 0); + + static bool isValid(const ObjectProxy &Node) { + if (!IncludeTreeBase::isValid(Node)) + return false; + IncludeTreeBase Base(Node); + return (Base.getNumReferences() >= 2 && Base.getNumReferences() <= 4) && + Base.getData().size() == 1; + } + static bool isValid(ObjectStore &DB, ObjectRef Ref) { + auto Node = DB.getProxy(Ref); + if (!Node) { + llvm::consumeError(Node.takeError()); + return false; + } + return isValid(*Node); + } + +private: + friend class IncludeTreeBase<IncludeTreeRoot>; + + std::optional<unsigned> getPCHRefIndex() const; + std::optional<unsigned> getModuleMapRefIndex() const; + std::optional<unsigned> getAPINotesRefIndex() const; + + explicit IncludeTreeRoot(ObjectProxy Node) + : IncludeTreeBase(std::move(Node)) { + assert(isValid(*this)); + } +}; + +/// An implementation of a \p vfs::FileSystem that supports the simple queries +/// of the preprocessor, for creating \p FileEntries using a file path, while +/// "replaying" an \p IncludeTreeRoot. It is not intended to be a complete +/// implementation of a file system. +Expected<IntrusiveRefCntPtr<llvm::vfs::FileSystem>> +createIncludeTreeFileSystem(IncludeTreeRoot &Root); + +/// Create the same IncludeTreeFileSystem but from +/// ArrayRef<IncludeTree::FileEntry>. +Expected<IntrusiveRefCntPtr<llvm::vfs::FileSystem>> createIncludeTreeFileSystem( + llvm::cas::ObjectStore &CAS, + llvm::ArrayRef<IncludeTree::FileList::FileEntry> List); + +} // namespace cas +} // namespace clang + +#endif diff --git a/clang/include/clang/CodeGen/BackendUtil.h b/clang/include/clang/CodeGen/BackendUtil.h index fc8ed4f011f92..e024cc97791ba 100644 --- a/clang/include/clang/CodeGen/BackendUtil.h +++ b/clang/include/clang/CodeGen/BackendUtil.h @@ -30,6 +30,7 @@ namespace clang { class CodeGenOptions; class TargetOptions; class LangOptions; + class CASOptions; // MCCAS class BackendConsumer; enum BackendAction { @@ -44,9 +45,11 @@ namespace clang { void EmitBackendOutput(DiagnosticsEngine &Diags, const HeaderSearchOptions &, const CodeGenOptions &CGOpts, const TargetOptions &TOpts, const LangOptions &LOpts, + const CASOptions &CASOpts, // MCCAS StringRef TDesc, llvm::Module *M, BackendAction Action, llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS, std::unique_ptr<raw_pwrite_stream> OS, + std::unique_ptr<raw_pwrite_stream> CasidOS = nullptr, BackendConsumer *BC = nullptr); void EmbedBitcode(llvm::Module *M, const CodeGenOptions &CGOpts, diff --git a/clang/include/clang/CodeGen/CodeGenABITypes.h b/clang/include/clang/CodeGen/CodeGenABITypes.h index 9cbc5a8a2a3f4..cd66be84f8d68 100644 --- a/clang/include/clang/CodeGen/CodeGenABITypes.h +++ b/clang/include/clang/CodeGen/CodeGenABITypes.h @@ -32,6 +32,7 @@ namespace llvm { class AttrBuilder; class Constant; +class ConstantInt; class Function; class FunctionType; class Type; @@ -75,11 +76,25 @@ const CGFunctionInfo &arrangeCXXMethodType(CodeGenModule &CGM, const FunctionProtoType *FTP, const CXXMethodDecl *MD); -const CGFunctionInfo &arrangeFreeFunctionCall(CodeGenModule &CGM, - CanQualType returnType, - ArrayRef<CanQualType> argTypes, - FunctionType::ExtInfo info, - RequiredArgs args); +const CGFunctionInfo & +arrangeCXXMethodCall(CodeGenModule &CGM, CanQualType returnType, + ArrayRef<CanQualType> argTypes, FunctionType::ExtInfo info, + ArrayRef<FunctionProtoType::ExtParameterInfo> paramInfos, + RequiredArgs args); + +const CGFunctionInfo &arrangeFreeFunctionCall( + CodeGenModule &CGM, CanQualType returnType, ArrayRef<CanQualType> argTypes, + FunctionType::ExtInfo info, + ArrayRef<FunctionProtoType::ExtParameterInfo> paramInfos, + RequiredArgs args); + +// An overload with an empty `paramInfos` +inline const CGFunctionInfo & +arrangeFreeFunctionCall(CodeGenModule &CGM, CanQualType returnType, + ArrayRef<CanQualType> argTypes, + FunctionType::ExtInfo info, RequiredArgs args) { + return arrangeFreeFunctionCall(CGM, returnType, argTypes, info, {}, args); +} /// Returns the implicit arguments to add to a complete, non-delegating C++ /// constructor call. @@ -105,6 +120,13 @@ llvm::Type *convertTypeForMemory(CodeGenModule &CGM, QualType T); unsigned getLLVMFieldNumber(CodeGenModule &CGM, const RecordDecl *RD, const FieldDecl *FD); +/// Compute a stable hash of the given string. +/// +/// The exact algorithm is the little-endian interpretation of the +/// non-doubled (i.e. 64-bit) result of applying a SipHash-2-4 using +/// a specific key value which can be found in the source. +uint64_t computeStableStringHash(StringRef string); + /// Return a declaration discriminator for the given global decl. uint16_t getPointerAuthDeclDiscriminator(CodeGenModule &CGM, GlobalDecl GD); @@ -112,6 +134,13 @@ uint16_t getPointerAuthDeclDiscriminator(CodeGenModule &CGM, GlobalDecl GD); uint16_t getPointerAuthTypeDiscriminator(CodeGenModule &CGM, QualType FunctionType); +/// Return a signed constant pointer. +llvm::Constant *getConstantSignedPointer(CodeGenModule &CGM, + llvm::Constant *pointer, + unsigned key, + llvm::Constant *storageAddress, + llvm::ConstantInt *otherDiscriminator); + /// Given the language and code-generation options that Clang was configured /// with, set the default LLVM IR attributes for a function definition. /// The attributes set here are mostly global target-configuration and diff --git a/clang/include/clang/CodeGen/ConstantInitBuilder.h b/clang/include/clang/CodeGen/ConstantInitBuilder.h index 28d4764b6d60b..2560aa27dfa73 100644 --- a/clang/include/clang/CodeGen/ConstantInitBuilder.h +++ b/clang/include/clang/CodeGen/ConstantInitBuilder.h @@ -207,6 +207,12 @@ class ConstantAggregateBuilderBase { const PointerAuthSchema &Schema, GlobalDecl CalleeDecl, QualType CalleeType); + /// Add a signed pointer using the given pointer authentication schema. + void addSignedPointer(llvm::Constant *pointer, + unsigned key, + bool useAddressDiscrimination, + llvm::ConstantInt *otherDiscriminator); + /// Add a null pointer of a specific type. void addNullPointer(llvm::PointerType *ptrTy) { add(llvm::ConstantPointerNull::get(ptrTy)); diff --git a/clang/include/clang/CodeGen/ObjectFilePCHContainerOperations.h b/clang/include/clang/CodeGen/ObjectFilePCHContainerOperations.h deleted file mode 100644 index 7a02d8725885a..0000000000000 --- a/clang/include/clang/CodeGen/ObjectFilePCHContainerOperations.h +++ /dev/null @@ -1,42 +0,0 @@ -//===-- CodeGen/ObjectFilePCHContainerOperations.h - ------------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_CLANG_CODEGEN_OBJECTFILEPCHCONTAINEROPERATIONS_H -#define LLVM_CLANG_CODEGEN_OBJECTFILEPCHCONTAINEROPERATIONS_H - -#include "clang/Frontend/PCHContainerOperations.h" - -namespace clang { - -/// A PCHContainerWriter implementation that uses LLVM to -/// wraps Clang modules inside a COFF, ELF, or Mach-O container. -class ObjectFilePCHContainerWriter : public PCHContainerWriter { - StringRef getFormat() const override { return "obj"; } - - /// Return an ASTConsumer that can be chained with a - /// PCHGenerator that produces a wrapper file format - /// that also contains full debug info for the module. - std::unique_ptr<ASTConsumer> - CreatePCHContainerGenerator(CompilerInstance &CI, - const std::string &MainFileName, - const std::string &OutputFileName, - std::unique_ptr<llvm::raw_pwrite_stream> OS, - std::shared_ptr<PCHBuffer> Buffer) const override; -}; - -/// A PCHContainerReader implementation that uses LLVM to -/// wraps Clang modules inside a COFF, ELF, or Mach-O container. -class ObjectFilePCHContainerReader : public PCHContainerReader { - ArrayRef<StringRef> getFormats() const override; - - /// Returns the serialized AST inside the PCH container Buffer. - StringRef ExtractPCH(llvm::MemoryBufferRef Buffer) const override; -}; -} - -#endif diff --git a/clang/include/clang/CodeGen/ObjectFilePCHContainerWriter.h b/clang/include/clang/CodeGen/ObjectFilePCHContainerWriter.h new file mode 100644 index 0000000000000..26ee9f22258c1 --- /dev/null +++ b/clang/include/clang/CodeGen/ObjectFilePCHContainerWriter.h @@ -0,0 +1,34 @@ +//===-- CodeGen/ObjectFilePCHContainerWriter.h ------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_CODEGEN_OBJECTFILEPCHCONTAINEROPERATIONS_H +#define LLVM_CLANG_CODEGEN_OBJECTFILEPCHCONTAINEROPERATIONS_H + +#include "clang/Frontend/PCHContainerOperations.h" + +namespace clang { + +/// A PCHContainerWriter implementation that uses LLVM to +/// wraps Clang modules inside a COFF, ELF, or Mach-O container. +class ObjectFilePCHContainerWriter : public PCHContainerWriter { + StringRef getFormat() const override { return "obj"; } + + /// Return an ASTConsumer that can be chained with a + /// PCHGenerator that produces a wrapper file format + /// that also contains full debug info for the module. + std::unique_ptr<ASTConsumer> + CreatePCHContainerGenerator(CompilerInstance &CI, + const std::string &MainFileName, + const std::string &OutputFileName, + std::unique_ptr<llvm::raw_pwrite_stream> OS, + std::shared_ptr<PCHBuffer> Buffer) const override; +}; + +} + +#endif diff --git a/clang/include/clang/CodeGen/SwiftCallingConv.h b/clang/include/clang/CodeGen/SwiftCallingConv.h index d7a0c84699ab0..806c3f13ffe62 100644 --- a/clang/include/clang/CodeGen/SwiftCallingConv.h +++ b/clang/include/clang/CodeGen/SwiftCallingConv.h @@ -88,6 +88,8 @@ class SwiftAggLowering { /// passed indirectly as an argument bool shouldPassIndirectly(bool asReturnValue) const; + bool shouldReturnTypedErrorIndirectly() const; + using EnumerationCallback = llvm::function_ref<void(CharUnits offset, CharUnits end, llvm::Type *type)>; diff --git a/clang/include/clang/Driver/Action.h b/clang/include/clang/Driver/Action.h index 04fa8b01b418f..80e15138cb896 100644 --- a/clang/include/clang/Driver/Action.h +++ b/clang/include/clang/Driver/Action.h @@ -56,6 +56,7 @@ class Action { InputClass = 0, BindArchClass, OffloadClass, + DepscanJobClass, PreprocessJobClass, PrecompileJobClass, ExtractAPIJobClass, @@ -77,7 +78,7 @@ class Action { StaticLibJobClass, BinaryAnalyzeJobClass, - JobClassFirst = PreprocessJobClass, + JobClassFirst = DepscanJobClass, JobClassLast = BinaryAnalyzeJobClass }; @@ -410,6 +411,23 @@ class JobAction : public Action { } }; +class DepscanJobAction : public JobAction { + void anchor() override; + +public: + DepscanJobAction(Action *Input, types::ID OutputType); + + static bool classof(const Action *A) { + return A->getKind() == DepscanJobClass; + } + + const JobAction &getScanningJobAction() const { return *JA; } + void setScanningJobAction(const JobAction *Job) { JA = Job; } + +private: + const JobAction *JA; +}; + class PreprocessJobAction : public JobAction { void anchor() override; diff --git a/clang/include/clang/Driver/BoundsSafetyArgs.h b/clang/include/clang/Driver/BoundsSafetyArgs.h new file mode 100644 index 0000000000000..310bdf8a7faf6 --- /dev/null +++ b/clang/include/clang/Driver/BoundsSafetyArgs.h @@ -0,0 +1,23 @@ +//===- BoundsSafetyArgs.h ---------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_CLANG_BASIC_BOUNDS_SAFETY_ARGS_H +#define LLVM_CLANG_BASIC_BOUNDS_SAFETY_ARGS_H +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/LangOptions.h" +#include "llvm/Option/ArgList.h" + +namespace clang { +namespace driver { + +LangOptions::BoundsSafetyNewChecksMaskIntTy +ParseBoundsSafetyNewChecksMaskFromArgs(const llvm::opt::ArgList &Args, + DiagnosticsEngine *Diags); +} // namespace driver +} // namespace clang + +#endif diff --git a/clang/include/clang/Driver/Driver.h b/clang/include/clang/Driver/Driver.h index 04b46782467d6..e9c980b2e75cc 100644 --- a/clang/include/clang/Driver/Driver.h +++ b/clang/include/clang/Driver/Driver.h @@ -379,8 +379,7 @@ class Driver { /// Takes the path to a binary that's either in bin/ or lib/ and returns /// the path to clang's resource directory. - static std::string GetResourcesPath(StringRef BinaryPath, - StringRef CustomResourceDir = ""); + static std::string GetResourcesPath(StringRef BinaryPath); Driver(StringRef ClangExecutable, StringRef TargetTriple, DiagnosticsEngine &Diags, std::string Title = "clang LLVM compiler", @@ -752,6 +751,7 @@ class Driver { /// compilation based on which -f(no-)?lto(=.*)? option occurs last. void setLTOMode(const llvm::opt::ArgList &Args); +public: /// Retrieves a ToolChain for a particular \p Target triple. /// /// Will cache ToolChains for the life of the driver object, and create them @@ -760,6 +760,7 @@ class Driver { const llvm::Triple &Target) const; /// @} +private: /// Retrieves a ToolChain for a particular device \p Target triple /// @@ -845,6 +846,9 @@ llvm::Error expandResponseFiles(SmallVectorImpl<const char *> &Args, bool ClangCLMode, llvm::BumpPtrAllocator &Alloc, llvm::vfs::FileSystem *FS = nullptr); +/// Checks whether the value produced by getDriverMode is for 'cache' mode. +bool isClangCache(StringRef DriverMode); + /// Apply a space separated list of edits to the input argument lists. /// See applyOneOverrideOption. void applyOverrideOptions(SmallVectorImpl<const char *> &Args, diff --git a/clang/include/clang/Driver/Job.h b/clang/include/clang/Driver/Job.h index df9449463c53b..8a3d9a1060068 100644 --- a/clang/include/clang/Driver/Job.h +++ b/clang/include/clang/Driver/Job.h @@ -33,9 +33,11 @@ class Tool; struct CrashReportInfo { StringRef Filename; StringRef VFSPath; + StringRef IndexStorePath; - CrashReportInfo(StringRef Filename, StringRef VFSPath) - : Filename(Filename), VFSPath(VFSPath) {} + CrashReportInfo(StringRef Filename, StringRef VFSPath, + StringRef IndexStorePath) + : Filename(Filename), VFSPath(VFSPath), IndexStorePath(IndexStorePath) {} }; // Encodes the kind of response file supported for a command invocation. @@ -143,6 +145,7 @@ class Command { /// See Command::setEnvironment std::vector<const char *> Environment; + std::vector<const char *> EnvironmentDisplay; /// Optional redirection for stdin, stdout, stderr. std::vector<std::optional<std::string>> RedirectFiles; @@ -218,6 +221,9 @@ class Command { Arguments = std::move(List); } + /// Sets the environment to display in `-###`. + virtual void setEnvironmentDisplay(llvm::ArrayRef<const char *> Display); + void replaceExecutable(const char *Exe) { Executable = Exe; } const char *getExecutable() const { return Executable; } @@ -258,6 +264,24 @@ class CC1Command : public Command { void setEnvironment(llvm::ArrayRef<const char *> NewEnvironment) override; }; +/// Automatically cache -cc1 commands when possible. +class CachingCC1Command : public CC1Command { +public: + CachingCC1Command(const Action &Source, const Tool &Creator, + ResponseFileSupport ResponseSupport, const char *Executable, + const llvm::opt::ArgStringList &Arguments, + ArrayRef<InputInfo> Inputs, + ArrayRef<InputInfo> Outputs = std::nullopt); + + void Print(llvm::raw_ostream &OS, const char *Terminator, bool Quote, + CrashReportInfo *CrashInfo = nullptr) const override; + + int Execute(ArrayRef<std::optional<StringRef>> Redirects, std::string *ErrMsg, + bool *ExecutionFailed) const override; + + void setEnvironment(llvm::ArrayRef<const char *> NewEnvironment) override; +}; + /// JobList - A sequence of jobs to perform. class JobList { public: diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index 69269cf7537b0..e22c0754324cb 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -328,6 +328,7 @@ class PreprocessorOutputOpts<string base> : KeyPathAndMacro<"PreprocessorOutputOpts.", base, "PREPROCESSOR_OUTPUT_"> {} class DependencyOutputOpts<string base> : KeyPathAndMacro<"DependencyOutputOpts.", base, "DEPENDENCY_OUTPUT_"> {} +class CASOpts<string base> : KeyPathAndMacro<"CASOpts.", base, "CAS_"> {} class CodeGenOpts<string base> : KeyPathAndMacro<"CodeGenOpts.", base, "CODEGEN_"> {} class HeaderSearchOpts<string base> @@ -632,6 +633,7 @@ defvar hip = LangOpts<"HIP">; defvar gnu_mode = LangOpts<"GNUMode">; defvar asm_preprocessor = LangOpts<"AsmPreprocessor">; defvar hlsl = LangOpts<"HLSL">; +defvar objc = LangOpts<"ObjC">; defvar std = !strconcat("LangStandard::getLangStandardForKind(", lang_std.KeyPath, ")"); @@ -803,6 +805,31 @@ def : Joined<["-"], "objcmt-white-list-dir-path=">, Visibility<[ClangOption, CC1Option]>, Alias<objcmt_allowlist_dir_path>; +def index_store_path : Separate<["-"], "index-store-path">, + Visibility<[ClangOption, CC1Option]>, + HelpText<"Enable indexing with the specified data store path">, + MarshallingInfoString<FrontendOpts<"IndexStorePath">>; +def index_ignore_system_symbols : Flag<["-"], "index-ignore-system-symbols">, + Visibility<[ClangOption, CC1Option]>, + HelpText<"Ignore symbols from system headers">, + MarshallingInfoFlag<FrontendOpts<"IndexIgnoreSystemSymbols">>; +def index_record_codegen_name : Flag<["-"], "index-record-codegen-name">, + Visibility<[ClangOption, CC1Option]>, + HelpText<"Record the codegen name for symbols">, + MarshallingInfoFlag<FrontendOpts<"IndexRecordCodegenName">>; +def index_unit_output_path : Separate<["-"], "index-unit-output-path">, MetaVarName<"<path>">, + Visibility<[ClangOption, CC1Option]>, + HelpText<"Use <path> as the output path for this compilation unit in the index unit file">, + MarshallingInfoString<FrontendOpts<"IndexUnitOutputPath">>; +def index_ignore_macros : Flag<["-"], "index-ignore-macros">, + Visibility<[ClangOption, CC1Option]>, + HelpText<"Ignore macros during indexing">, + MarshallingInfoFlag<FrontendOpts<"IndexIgnoreMacros">>; +def index_ignore_pcms : Flag<["-"], "index-ignore-pcms">, + Visibility<[ClangOption, CC1Option]>, + HelpText<"Ignore symbols from imported pcm modules">, + MarshallingInfoFlag<FrontendOpts<"IndexIgnorePcms">>; + // Make sure all other -ccc- options are rejected. def ccc_ : Joined<["-"], "ccc-">, Group<internal_Group>, Flags<[Unsupported]>; @@ -932,8 +959,9 @@ def O_flag : Flag<["-"], "O">, Visibility<[ClangOption, CC1Option, FC1Option]>, Alias<O>, AliasArgs<["1"]>; def Ofast : Joined<["-"], "Ofast">, Group<O_Group>, Visibility<[ClangOption, CC1Option, FlangOption]>, - HelpText<"Deprecated; use '-O3 -ffast-math' for the same behavior," - " or '-O3' to enable only conforming optimizations">; + HelpTextForVariants<[ClangOption, CC1Option], + "Deprecated; use '-O3 -ffast-math' for the same behavior," + " or '-O3' to enable only conforming optimizations">; def P : Flag<["-"], "P">, Visibility<[ClangOption, CC1Option, FlangOption, FC1Option]>, Group<Preprocessor_Group>, @@ -1065,7 +1093,7 @@ def all__load : Flag<["-"], "all_load">; def allowable__client : Separate<["-"], "allowable_client">; def ansi : Flag<["-", "--"], "ansi">, Group<CompileOnly_Group>; def arch__errors__fatal : Flag<["-"], "arch_errors_fatal">; -def arch : Separate<["-"], "arch">, Flags<[NoXarchOption,TargetSpecific]>; +def arch : Separate<["-"], "arch">, Flags<[NoXarchOption]>; def arch__only : Separate<["-"], "arch_only">; def autocomplete : Joined<["--"], "autocomplete=">; def bind__at__load : Flag<["-"], "bind_at_load">; @@ -1165,19 +1193,19 @@ def client__name : JoinedOrSeparate<["-"], "client_name">; def combine : Flag<["-", "--"], "combine">, Flags<[NoXarchOption, Unsupported]>; def compatibility__version : JoinedOrSeparate<["-"], "compatibility_version">; def config : Joined<["--"], "config=">, Flags<[NoXarchOption]>, - Visibility<[ClangOption, CLOption, DXCOption]>, MetaVarName<"<file>">, + Visibility<[ClangOption, CLOption, DXCOption, FlangOption]>, MetaVarName<"<file>">, HelpText<"Specify configuration file">; -def : Separate<["--"], "config">, Alias<config>; +def : Separate<["--"], "config">, Visibility<[ClangOption, CLOption, DXCOption, FlangOption]>, Alias<config>; def no_default_config : Flag<["--"], "no-default-config">, - Flags<[NoXarchOption]>, Visibility<[ClangOption, CLOption, DXCOption]>, + Flags<[NoXarchOption]>, Visibility<[ClangOption, CLOption, DXCOption, FlangOption]>, HelpText<"Disable loading default configuration files">; def config_system_dir_EQ : Joined<["--"], "config-system-dir=">, Flags<[NoXarchOption, HelpHidden]>, - Visibility<[ClangOption, CLOption, DXCOption]>, + Visibility<[ClangOption, CLOption, DXCOption, FlangOption]>, HelpText<"System directory for configuration files">; def config_user_dir_EQ : Joined<["--"], "config-user-dir=">, Flags<[NoXarchOption, HelpHidden]>, - Visibility<[ClangOption, CLOption, DXCOption]>, + Visibility<[ClangOption, CLOption, DXCOption, FlangOption]>, HelpText<"User directory for configuration files">; def coverage : Flag<["-", "--"], "coverage">, Group<Link_Group>, Visibility<[ClangOption, CLOption]>; @@ -1910,6 +1938,9 @@ def fapinotes_swift_version : Joined<["-"], "fapinotes-swift-version=">, Group<f_clang_Group>, Visibility<[ClangOption, CC1Option]>, MetaVarName<"<version>">, HelpText<"Specify the Swift version to use when filtering API notes">; +def fapinotes_cache_path : Joined<["-"], "fapinotes-cache-path=">, + Group<i_Group>, Flags<[NoXarchOption]>, MetaVarName<"<directory>">, + HelpText<"Does nothing; API notes are no longer cached separately from modules">; defm bounds_safety : BoolFOption< "experimental-bounds-safety", @@ -1919,6 +1950,77 @@ defm bounds_safety : BoolFOption< BothFlags<[], [CC1Option], " experimental bounds safety extension for C">>; +// TO_UPSTREAM(BoundsSafety) ON +// Apple internal clients currently use `-fbounds-safety` so provide +// aliases to upstream's `-fexperimental-bounds-safety` flag. When +// upstream eventually drops the `experimental-` prefix these aliases +// can be removed. +def : Flag<["-"], "fbounds-safety">, Visibility<[ClangOption, CC1Option]>, + Alias<fbounds_safety>, HelpText<"Enable bounds safety extension for C">; +def : Flag<["-"], "fno-bounds-safety">, Visibility<[ClangOption, CC1Option]>, + Alias<fno_bounds_safety>, HelpText<"Disable bounds safety extension for C">; + +// Aliases for -fbounds-safety to remove once these are stripped from adopters +def : Flag<["-"], "fbounds-attributes">, Visibility<[ClangOption, CC1Option]>, + Alias<fbounds_safety>; +def : Flag<["-"], "fno-bounds-attributes">, Visibility<[ClangOption, CC1Option]>, + Alias<fno_bounds_safety>; +defm experimental_bounds_safety_attributes : BoolFOption< + "experimental-bounds-safety-attributes", + LangOpts<"BoundsSafetyAttributes">, DefaultFalse, + PosFlag<SetTrue, [], [ClangOption], "Enable">, + NegFlag<SetFalse, [], [ClangOption], "Disable">, + BothFlags<[], [ClangOption, CC1Option], " experimental attributes">>; +defm bounds_attributes_cxx_experimental : BoolFOption< + "bounds-attributes-cxx-experimental", + LangOpts<"BoundsAttributesCXXExperimental">, DefaultFalse, + PosFlag<SetTrue, [], [ClangOption], "Enable">, + NegFlag<SetFalse, [], [ClangOption], "Disable">, + BothFlags<[], [ClangOption, CC1Option], + " experimental bounds attributes for C++">>; +defm bounds_attributes_objc_experimental : BoolFOption< + "bounds-attributes-objc-experimental", + LangOpts<"BoundsAttributesObjCExperimental">, DefaultFalse, + PosFlag<SetTrue, [], [ClangOption], "Enable">, + NegFlag<SetFalse, [], [ClangOption], "Disable">, + BothFlags<[], [ClangOption, CC1Option], + " experimental bounds attributes for ObjC">>; +defm bounds_safety_relaxed_system_headers : BoolFOption< + "bounds-safety-relaxed-system-headers", + LangOpts<"BoundsSafetyRelaxedSystemHeaders">, DefaultTrue, + PosFlag<SetTrue, [], [ClangOption], "Enable">, + NegFlag<SetFalse, [], [ClangOption], "Disable">, + BothFlags<[], [ClangOption, CC1Option], + " relaxed bounds safety rules for code in system headers">>; +defm bounds_safety_adoption_mode : BoolFOption<"bounds-safety-adoption-mode", + DiagnosticOpts<"BoundsSafetyAdoptionMode">, DefaultFalse, + PosFlag<SetTrue, [], [ClangOption], "Enable">, + NegFlag<SetFalse, [], [ClangOption], "Disable">, + BothFlags<[], [ClangOption, CC1Option], + " generating improved -fbounds-safety diagnostics to aid engineers " + "adopting -fbounds-safety. This is off by default to avoid impacting " + "compile times">>; + +def fbounds_safety_bringup_missing_checks_EQ : CommaJoined<["-"], "fbounds-safety-bringup-missing-checks=">, Group<f_Group>, + MetaVarName<"<check>">, + Visibility<[ClangOption, CC1Option]>, + HelpText<"Enable a set of new -fbounds-safety run-time checks, (option: access_size, indirect_count_update, return_size, ended_by_lower_bound, compound_literal_init, libc_attributes, array_subscript_agg, all, batch_0)">; + +def fno_bounds_safety_bringup_missing_checks_EQ : CommaJoined<["-"], "fno-bounds-safety-bringup-missing-checks=">, Group<f_Group>, + MetaVarName<"<check>">, + Visibility<[ClangOption, CC1Option]>, + HelpText<"Disable a set of new -fbounds-safety run-time checks, (option: access_size, indirect_count_update, return_size, ended_by_lower_bound, compound_literal_init, libc_attributes, array_subscript_agg, all, batch_0)">; + +def fbounds_safety_bringup_missing_checks : Flag<["-"], "fbounds-safety-bringup-missing-checks">, Group<f_Group>, + Visibility<[ClangOption]>, + Alias<fbounds_safety_bringup_missing_checks_EQ>, AliasArgs<["all"]>, + HelpText<"Enable new -fbounds-safety run-time checks">; + +def fno_bounds_safety_bringup_missing_checks : Flag<["-"], "fno-bounds-safety-bringup-missing-checks">, Group<f_Group>, + Visibility<[ClangOption]>, + Alias<fno_bounds_safety_bringup_missing_checks_EQ>, AliasArgs<["all"]>, + HelpText<"Disable new -fbounds-safety run-time checks">; + defm addrsig : BoolFOption<"addrsig", CodeGenOpts<"Addrsig">, DefaultFalse, PosFlag<SetTrue, [], [ClangOption, CC1Option], "Emit">, @@ -2054,9 +2156,10 @@ defm async_exceptions: BoolFOption<"async-exceptions", "Enable EH Asynchronous exceptions">, NegFlag<SetFalse>>; defm cxx_modules : BoolFOption<"cxx-modules", - LangOpts<"CPlusPlusModules">, Default<cpp20.KeyPath>, + // FIXME: improve tblgen to not need strconcat here. + LangOpts<"CPlusPlusModules">, Default<!strconcat(cpp20.KeyPath, " && !", objc.KeyPath)>, NegFlag<SetFalse, [], [ClangOption, CC1Option], "Disable">, - PosFlag<SetTrue, [], [ClangOption], "Enable">, + PosFlag<SetTrue, [], [ClangOption, CC1Option], "Enable">, BothFlags<[], [], " modules for C++">>, ShouldParseIf<cplusplus.KeyPath>; def fdebug_pass_arguments : Flag<["-"], "fdebug-pass-arguments">, Group<f_Group>; @@ -3106,20 +3209,23 @@ def fmodules_user_build_path : Separate<["-"], "fmodules-user-build-path">, Grou HelpText<"Specify the module user build path">, MarshallingInfoString<HeaderSearchOpts<"ModuleUserBuildPath">>; def fprebuilt_module_path : Joined<["-"], "fprebuilt-module-path=">, Group<i_Group>, - Flags<[]>, Visibility<[ClangOption, CC1Option]>, + Flags<[]>, Visibility<[ClangOption, CLOption, CC1Option]>, MetaVarName<"<directory>">, HelpText<"Specify the prebuilt module path">; defm prebuilt_implicit_modules : BoolFOption<"prebuilt-implicit-modules", HeaderSearchOpts<"EnablePrebuiltImplicitModules">, DefaultFalse, PosFlag<SetTrue, [], [ClangOption], "Look up implicit modules in the prebuilt module path">, NegFlag<SetFalse>, BothFlags<[], [ClangOption, CC1Option]>>; +def fmodule_related_to_pch : Flag<["-"], "fmodule-related-to-pch">, + Group<f_Group>, Visibility<[ClangOption, CC1Option]>, + HelpText<"Mark module as related to a PCH">; def fmodule_output_EQ : Joined<["-"], "fmodule-output=">, - Flags<[NoXarchOption]>, Visibility<[ClangOption, CC1Option]>, + Flags<[NoXarchOption]>, Visibility<[ClangOption, CLOption, CC1Option]>, MarshallingInfoString<FrontendOpts<"ModuleOutputPath">>, HelpText<"Save intermediate module file results when compiling a standard C++ module unit.">; def fmodule_output : Flag<["-"], "fmodule-output">, Flags<[NoXarchOption]>, - Visibility<[ClangOption, CC1Option]>, + Visibility<[ClangOption, CLOption, 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", @@ -3228,10 +3334,10 @@ defm pch_codegen: OptInCC1FFlag<"pch-codegen", "Generate ", "Do not generate ", defm pch_debuginfo: OptInCC1FFlag<"pch-debuginfo", "Generate ", "Do not generate ", "debug info for types in an object file built from this PCH and do not generate them elsewhere">; -def fimplicit_module_maps : Flag <["-"], "fimplicit-module-maps">, Group<f_Group>, - Visibility<[ClangOption, CC1Option, CLOption]>, - HelpText<"Implicitly search the file system for module map files.">, - MarshallingInfoFlag<HeaderSearchOpts<"ImplicitModuleMaps">>; +defm implicit_module_maps : BoolFOption<"implicit-module-maps", + HeaderSearchOpts<"ImplicitModuleMaps">, DefaultFalse, + PosFlag<SetTrue, [], [], "Implicitly search the file system for module map files.">, + NegFlag<SetFalse>, BothFlags<[], [ClangOption, CC1Option, CLOption]>>; defm modules : BoolFOption<"modules", LangOpts<"Modules">, Default<fcxx_modules.KeyPath>, PosFlag<SetTrue, [], [ClangOption, CC1Option], @@ -3299,8 +3405,10 @@ def fretain_comments_from_system_headers : Flag<["-"], "fretain-comments-from-sy Visibility<[ClangOption, CC1Option]>, MarshallingInfoFlag<LangOpts<"RetainCommentsFromSystemHeaders">>; def fmodule_header : Flag <["-"], "fmodule-header">, Group<f_Group>, + Visibility<[ClangOption, CLOption]>, HelpText<"Build a C++20 Header Unit from a header">; def fmodule_header_EQ : Joined<["-"], "fmodule-header=">, Group<f_Group>, + Visibility<[ClangOption, CLOption]>, MetaVarName<"<kind>">, HelpText<"Build a C++20 Header Unit from a header that should be found in the user (fmodule-header=user) or system (fmodule-header=system) search path.">; @@ -3325,6 +3433,10 @@ def fno_builtin : Flag<["-"], "fno-builtin">, Group<f_Group>, def fno_builtin_ : Joined<["-"], "fno-builtin-">, Group<f_Group>, Visibility<[ClangOption, CC1Option, CLOption, DXCOption]>, HelpText<"Disable implicit builtin knowledge of a specific function">; +def ffeature_availability_EQ : Joined<["-"], "ffeature-availability=">, Group<f_Group>, + Visibility<[CC1Option]>, + HelpText<"feature availability">, + MarshallingInfoStringVector<LangOpts<"FeatureAvailability">>; def fno_common : Flag<["-"], "fno-common">, Group<f_Group>, Visibility<[ClangOption, CC1Option]>, HelpText<"Compile common globals like normal definitions">; @@ -3352,7 +3464,6 @@ def fveclib : Joined<["-"], "fveclib=">, Group<f_Group>, MarshallingInfoEnum<CodeGenOpts<"VecLib">, "NoLibrary">; def fno_lax_vector_conversions : Flag<["-"], "fno-lax-vector-conversions">, Group<f_Group>, Alias<flax_vector_conversions_EQ>, AliasArgs<["none"]>; -def fno_implicit_module_maps : Flag <["-"], "fno-implicit-module-maps">, Group<f_Group>; def fno_module_maps : Flag <["-"], "fno-module-maps">, Alias<fno_implicit_module_maps>; def fno_modules_strict_decluse : Flag <["-"], "fno-strict-modules-decluse">, Group<f_Group>; def fmodule_file_deps : Flag <["-"], "fmodule-file-deps">, Group<f_Group>; @@ -4028,6 +4139,24 @@ def ftrap_function_EQ : Joined<["-"], "ftrap-function=">, Group<f_Group>, Visibility<[ClangOption, CC1Option]>, HelpText<"Issue call to specified function rather than a trap instruction">, MarshallingInfoString<CodeGenOpts<"TrapFuncName">>; +/* TO_UPSTREAM(BoundsSafety) ON */ +defm trap_function_returns : BoolFOption<"trap-function-returns", + CodeGenOpts<"TrapFuncReturns">, DefaultFalse, + PosFlag<SetTrue, [], [ClangOption, CC1Option], + "The trap function specified by -ftrap-function may return normally">, + NegFlag<SetFalse, [], [ClangOption, CC1Option], + "The trap function specified by -ftrap-function never returns">>; +defm unique_traps : BoolFOption<"unique-traps", + CodeGenOpts<"UniqueTrapBlocks">, DefaultFalse, + PosFlag<SetTrue, [], [CC1Option], + "Make trap instructions unique by preventing traps from being merged by the" + " optimizer. This is useful for preserving the debug information on traps " + "in optimized code. This makes it easier to debug programs at the cost of " + "increased code size">, + NegFlag<SetFalse, [], [CC1Option], + "Don't try to make trap instructions unique. This allows trap instructions " + "to be merged by the optimizer.">>; +/* TO_UPSTREAM(BoundsSafety) OFF */ def funroll_loops : Flag<["-"], "funroll-loops">, Group<f_Group>, HelpText<"Turn on loop unroller">, Visibility<[ClangOption, CC1Option]>; def fno_unroll_loops : Flag<["-"], "fno-unroll-loops">, Group<f_Group>, @@ -4240,6 +4369,28 @@ defm strict_return : BoolFOption<"strict-return", " of a non-void function as unreachable">, PosFlag<SetTrue>>; +let Group = f_Group in { + let Visibility = [ClangOption, CC1Option] in { + def fptrauth_soft : Flag<["-"], "fptrauth-soft">, + HelpText<"Enable software lowering of pointer authentication">; + } + def fno_ptrauth_soft : Flag<["-"], "fno-ptrauth-soft">; +} + +let Group = f_Group in { + def fptrauth_abi_version_EQ : Joined<["-"], "fptrauth-abi-version=">, + Visibility<[ClangOption, CC1Option, CC1AsOption]>, + HelpText<"Pointer Authentication ABI version">; + def fno_ptrauth_abi_version : Flag<["-"], "fno-ptrauth-abi-version">, + HelpText<"Disable Pointer Authentication ABI versioning">; + + def fptrauth_kernel_abi_version : Flag<["-"], "fptrauth-kernel-abi-version">, + Visibility<[ClangOption, CC1Option, CC1AsOption]>, + HelpText<"Enable Pointer Authentication kernel ABI version">; + def fno_ptrauth_kernel_abi_version : Flag<["-"], "fno-ptrauth-kernel-abi-version">, + HelpText<"Disable Pointer Authentication kernel ABI versioning">; +} + let Flags = [TargetSpecific] in { defm ptrauth_intrinsics : OptInCC1FFlag<"ptrauth-intrinsics", "Enable pointer authentication intrinsics">; defm ptrauth_calls : OptInCC1FFlag<"ptrauth-calls", "Enable signing and authentication of all indirect calls">; @@ -4402,6 +4553,11 @@ def gcoff : Joined<["-"], "gcoff">, Group<g_Group>, Flags<[Unsupported]>; def gxcoff : Joined<["-"], "gxcoff">, Group<g_Group>, Flags<[Unsupported]>; def gvms : Joined<["-"], "gvms">, Group<g_Group>, Flags<[Unsupported]>; def gtoggle : Flag<["-"], "gtoggle">, Group<g_flags_Group>, Flags<[Unsupported]>; +defm reproducible : BoolOption<"g", "reproducible", + CodeGenOpts<"ReproducibleDebugInfo">, DefaultFalse, + PosFlag<SetTrue, [], [ClangOption, CC1Option], + "Tune debug info to be reproducible.">, + NegFlag<SetFalse>>, Group<g_flags_Group>; def grecord_command_line : Flag<["-"], "grecord-command-line">, Group<g_flags_Group>; def gno_record_command_line : Flag<["-"], "gno-record-command-line">, @@ -4479,9 +4635,6 @@ def ibuiltininc : Flag<["-"], "ibuiltininc">, Group<clang_i_Group>, HelpText<"Enable builtin #include directories even when -nostdinc is used " "before or after -ibuiltininc. " "Using -nobuiltininc after the option disables it">; -def index_header_map : Flag<["-"], "index-header-map">, - Visibility<[ClangOption, CC1Option]>, - HelpText<"Make the next included directory (-I or -F) an indexer header map">; def iapinotes_modules : JoinedOrSeparate<["-"], "iapinotes-modules">, Group<clang_i_Group>, Visibility<[ClangOption, CC1Option]>, HelpText<"Add directory to the API notes search path referenced by module name">, MetaVarName<"<directory>">; @@ -5714,6 +5867,9 @@ def start_no_unused_arguments : Flag<["--"], "start-no-unused-arguments">, HelpText<"Don't emit warnings about unused arguments for the following arguments">; def static_libgcc : Flag<["-"], "static-libgcc">; def static_libstdcxx : Flag<["-"], "static-libstdc++">; +def static_libclosure : Flag<["-"], "static-libclosure">, + Visibility<[ClangOption, CC1Option]>, + HelpText<"Generate code for statically linking libclosure (BlocksRuntime)">; def static : Flag<["-", "--"], "static">, Group<Link_Group>, Visibility<[ClangOption, CLOption, DXCOption, FlangOption]>, Flags<[NoArgumentUnused]>; @@ -5945,6 +6101,7 @@ def _output : Separate<["--"], "output">, Alias<o>; def _param : Separate<["--"], "param">, Group<CompileOnly_Group>; def _param_EQ : Joined<["--"], "param=">, Alias<_param>; def _precompile : Flag<["--"], "precompile">, Flags<[NoXarchOption]>, + Visibility<[ClangOption, CLOption]>, Group<Action_Group>, HelpText<"Only precompile the input">; def _prefix_EQ : Joined<["--"], "prefix=">, Alias<B>; def _prefix : Separate<["--"], "prefix">, Alias<B>; @@ -7168,6 +7325,10 @@ def fdump_vtable_layouts : Flag<["-"], "fdump-vtable-layouts">, def fmerge_functions : Flag<["-"], "fmerge-functions">, HelpText<"Permit merging of identical functions when optimizing.">, MarshallingInfoFlag<CodeGenOpts<"MergeFunctions">>; +def fsplit_cold_code : Flag<["-"], "fsplit-cold-code">, + HelpText<"Permit splitting of cold code when optimizing (off by default).">; +def fno_split_cold_code : Flag<["-"], "fno-split-cold-code">, + HelpText<"Disable splitting of cold code when optimizing.">; def : Joined<["-"], "coverage-data-file=">, MarshallingInfoString<CodeGenOpts<"CoverageDataFile">>; def : Joined<["-"], "coverage-notes-file=">, @@ -7410,6 +7571,9 @@ let Visibility = [CC1Option] in { def sys_header_deps : Flag<["-"], "sys-header-deps">, HelpText<"Include system headers in dependency output">, MarshallingInfoFlag<DependencyOutputOpts<"IncludeSystemHeaders">>; +def skip_unused_modulemap_file_deps : Flag<["-"], "skip-unused-modulemap-deps">, + HelpText<"Include module map files only for imported modules in dependency output">, + MarshallingInfoFlag<DependencyOutputOpts<"SkipUnusedModuleMaps">>; def module_file_deps : Flag<["-"], "module-file-deps">, HelpText<"Include module files in dependency output">, MarshallingInfoFlag<DependencyOutputOpts<"IncludeModuleFiles">>; @@ -7594,12 +7758,13 @@ defm fimplicit_modules_use_lock : BoolOption<"f", "implicit-modules-use-lock", "duplicating work in competing clang invocations.">>; // FIXME: We only need this in C++ modules if we might textually // enter a different module (eg, when building a header unit). -def fmodules_local_submodule_visibility : - Flag<["-"], "fmodules-local-submodule-visibility">, - HelpText<"Enforce name visibility rules across submodules of the same " - "top-level module.">, - MarshallingInfoFlag<LangOpts<"ModulesLocalVisibility">>, - ImpliedByAnyOf<[fcxx_modules.KeyPath]>; +defm modules_local_submodule_visibility : + BoolFOption<"modules-local-submodule-visibility", + LangOpts<"ModulesLocalVisibility">, + Default<fcxx_modules.KeyPath>, + PosFlag<SetTrue, [], [ClangOption], "Enforce">, + NegFlag<SetFalse, [], [ClangOption], "Do not enforce">, + BothFlags<[], [ClangOption], " name visibility rules across submodules of the same top-level module">>; def fmodules_codegen : Flag<["-"], "fmodules-codegen">, HelpText<"Generate code for uses of this module that assumes an explicit " @@ -7614,10 +7779,19 @@ def fmodule_format_EQ : Joined<["-"], "fmodule-format=">, HelpText<"Select the container format for clang modules and PCH. " "Supported options are 'raw' and 'obj'.">, MarshallingInfoString<HeaderSearchOpts<"ModuleFormat">, [{"raw"}]>; +def fmodules_hash_error_diagnostics : Flag<["-"], "fmodules-hash-error-diagnostics">, + HelpText<"Use a separate module cache for modules compiled with conflicting -Werror options">, + MarshallingInfoFlag<LangOpts<"ModulesHashErrorDiags">>; def ftest_module_file_extension_EQ : Joined<["-"], "ftest-module-file-extension=">, HelpText<"introduce a module file extension for testing purposes. " "The argument is parsed as blockname:major:minor:hashed:user info">; +def fmodule_file_cache_key : MultiArg<["-"], "fmodule-file-cache-key", 2>, + MetaVarName<"<path> <key>">, + HelpText<"Make the module with the given compile job cache key available as " + "if it were at <path>. This option may be combined with " + "-fmodule-file= to import the module. The module must have " + "previously been built with -fcache-compile-job.">; defm recovery_ast : BoolOption<"f", "recovery-ast", LangOpts<"RecoveryAST">, DefaultTrue, @@ -8086,13 +8260,6 @@ def source_date_epoch : Separate<["-"], "source-date-epoch">, } // let Visibility = [CC1Option] -defm err_pragma_mc_func_aix : BoolFOption<"err-pragma-mc-func-aix", - PreprocessorOpts<"ErrorOnPragmaMcfuncOnAIX">, DefaultFalse, - PosFlag<SetTrue, [], [ClangOption, CC1Option], - "Treat uses of #pragma mc_func as errors">, - NegFlag<SetFalse,[], [ClangOption, CC1Option], - "Ignore uses of #pragma mc_func">>; - //===----------------------------------------------------------------------===// // CUDA Options //===----------------------------------------------------------------------===// @@ -8141,6 +8308,231 @@ def fsycl_is_host : Flag<["-"], "fsycl-is-host">, Visibility<[CC1Option]>, MarshallingInfoFlag<LangOpts<"SYCLIsHost">>; +//===----------------------------------------------------------------------===// +// CAS Options +//===----------------------------------------------------------------------===// + +// Example command-lines we expect: +// +// For building against some pre-ingested, CAS-based filesystem in the builtin +// on-disk CAS: +// ``` +// % clang -fcas-path=auto -fcas-fs=llvmcas://1af12fa2afa1af2a1fa +// % clang -fcas-path=path/to/cas -fcas-fs=llvmcas://1af12fa2afa1af2a1fa +// ``` +// +// For building against some pre-ingested, CAS-based filesystem using a plugin: +// ``` +// % clang -fcas-plugin=path/to/some/cas-plugind \ +// -fcas-plugin-args=-X,-Y,-Z \ +// -fcas-fs=llvmcas://1af12fa2afa1af2a1fa +// ``` +// +// For automatically ingesting from the live filesystem into a CAS, +// canonicalizing the paths, and running the `-cc1` against the CAS tree: +// ``` +// % clang -fdepscan \ +// -fdepscan-prefix-map=/my/sources=/^src \ +// -fdepscan-prefix-map=/my/build/dir=/^build +// ``` +// +// For pruning and canonicalizing even when starting from a pre-ingested, +// CAS-based filesystem: +// ``` +// % clang -fcas-path=path/to/the/cas \ +// -fcas-fs=llvmcas://1af12fa2afa1af2a1fa \ +// -fdepscan \ +// -fdepscan-prefix-map=/my/sources=/^src \ +// -fdepscan-prefix-map=/my/build/dir=/^build +// ``` + +// Driver CAS options. +def fdepscan_EQ : Joined<["-"], "fdepscan=">, + Group<f_Group>, + HelpText<"Scan for dependencies ahead of compiling, generating a" + " pruned CAS tree to send to -fcas-fs. Values are" + " 'auto'," + " 'daemon' (see -fdepscan-share and -fdepscan-share-parent)," + " 'inline', or" + " 'off' (default).">; +def fdepscan : Flag<["-"], "fdepscan">, + Group<f_Group>, HelpText<"Turn on -fdepscan=auto.">, + Alias<fdepscan_EQ>, AliasArgs<["auto"]>; +def fno_depscan : Flag<["-"], "fno-depscan">, + Group<f_Group>, + Alias<fdepscan_EQ>, AliasArgs<["off"]>; +def fdepscan_share_EQ : Joined<["-"], "fdepscan-share=">, + Group<f_Group>, + HelpText<"If the argument is the name of a command in the process tree," + " share state based on its PID." + " E.g., -fdepscan -fdepscan-share=ninja will search for 'ninja'" + " in the process tree and share state based on its PID if found." + " See also -fdepscan-share-stop.">; +def fdepscan_share_parent_EQ : Joined<["-"], "fdepscan-share-parent=">, + Group<f_Group>, + HelpText<"Share state based on the PID of the parent command if the name" + " matches." + " See also -fdepscan-share-stop.">; +def fdepscan_share_parent : Flag<["-"], "fdepscan-share-parent">, + Group<f_Group>, + HelpText<"Share state based on the PID of the parent command." + " See also -fdepscan-share-stop.">; +def fno_depscan_share : Flag<["-"], "fno-depscan-share">, + Group<f_Group>, + HelpText<"Turn off -fdepscan-share and -fdepscan-share-parent.">; +def fdepscan_share_stop_EQ : Joined<["-"], "fdepscan-share-stop=">, + Group<f_Group>, + HelpText<"Stop looking for the command named by -fdepscan-share if a" + " process with the name of the provided argument is found first." + " Also blocks -fdepscan-share=parent if the parent has this name." + " E.g., -fdepscan -fdepscan-share=ninja" + " -fdepscan-share-stop=cmake looks for 'ninja' and 'cmake' in the" + " process tree; if 'ninja' is found first, state is shared based" + " on ninja's PID; if 'cmake' is found first, state is not" + " shared.">; +def fdepscan_share_identifier : Separate<["-"], "fdepscan-share-identifier">, + Group<f_Group>, + HelpText<"Share depscan daemon for Clang invocations using the same string " + "identifier.">; +def fdepscan_share_identifier_EQ : Joined<["-"], "fdepscan-share-identifier=">, + Alias<fdepscan_share_identifier>; +def fdepscan_daemon_EQ : Joined<["-"], "fdepscan-daemon=">, Group<f_Group>, + HelpText<"Specify the path to the daemon to be used. Clang will use the" + " daemon specified, rather than try to spawn its own based on" + " parent processes.">; + +def fdepscan_include_tree : Flag<["-"], "fdepscan-include-tree">, + Group<f_Group>, HelpText<"Set dep-scanner to produce the include tree">; + +// CAS prefix map options. +// +// FIXME: Add DepscanOption flag. +def fdepscan_prefix_map_EQ : Joined<["-"], "fdepscan-prefix-map=">, + Group<f_Group>, Visibility<[ClangOption, CC1Option]>, + MetaVarName<"<old>=<new>">, + HelpText<"With -fdepscan, seamlessly filter the CAS filesystem to" + " apply the given prefix, updating the command-line to match.">, + MarshallingInfoStringVector<FrontendOpts<"PathPrefixMappings">>; +def fdepscan_prefix_map_sdk_EQ : + Joined<["-"], "fdepscan-prefix-map-sdk=">, + Group<f_Group>, MetaVarName<"<new>">, + HelpText<"With -fdepscan, auto-detect the SDK path on-disk and remap" + " it to the given path (see -fdepscan-prefix-map=).">; +def fdepscan_prefix_map_toolchain_EQ : + Joined<["-"], "fdepscan-prefix-map-toolchain=">, + Group<f_Group>, MetaVarName<"<new>">, + HelpText<"With -fdepscan, auto-detect the toolchain path on-disk and" + " remap it to the given path (see -fdepscan-prefix-map=).">; + +// -cc1depscan options. +// +// FIXME: Add to their own group; add NoDriverOption and DepscanOption flags. +def cc1_args : Option<["-"], "cc1-args", KIND_REMAINING_ARGS>, + HelpText<"pass cc1 options to depscan afterwards">; +def dump_depscan_tree_EQ : Option<["-"], "dump-depscan-tree=", KIND_JOINED>, + HelpText<"emit the CAS identifier for the tree instead of the full -cc1">; + +// -cc1 options. These should be available in the driver too, but the driver +// doesn't currently support most of them. For example, the driver should +// read only from -fcas-fs if specified, in which case the -fdepscan +// should scan to prune the CAS filesystem. Also, -fdepscan shouldn't +// override -fcas / etc., it should just add those if not already specified. +let Visibility = [CC1Option] in { + +// FIXME: Add to driver once it's supported by (not clobbered by) -fdepscan. +// +// The driver should also have: +// - '-fcas-path=<dir>|auto' ('='-joined alias). +// - '-fno-cas-path' (same as '-fcas-path ""', the default). +def fcas_path : Separate<["-"], "fcas-path">, + Group<f_Group>, MetaVarName<"<dir>|auto">, + HelpText<"Path to a persistent on-disk backing store for the builtin CAS." + " '-fcas-path=auto' chooses a path in the user's system cache.">, + MarshallingInfoString<CASOpts<"CASPath">>; + +def fcas_plugin_path : Separate<["-"], "fcas-plugin-path">, + Group<f_Group>, MetaVarName<"<pathname>">, + HelpText<"Path to a shared library implementing the LLVM CAS plugin API.">, + MarshallingInfoString<CASOpts<"PluginPath">>; + +def fcas_plugin_option : Separate<["-"], "fcas-plugin-option">, + Group<f_Group>, MetaVarName<"<name>=<value>">, + HelpText<"Option to pass to the CAS plugin.">, + MarshallingInfoStringPairVector<CASOpts<"PluginOptions">>; + +// FIXME: Add to driver once it's supported by -fdepscan. +def fcas_fs : Separate<["-"], "fcas-fs">, + Group<f_Group>, MetaVarName<"<tree>">, + HelpText<"Configure the filesystem to read from the provided CAS tree." + " See also -fcas-builtin-path for loading a tree.">, + MarshallingInfoString<FileSystemOpts<"CASFileSystemRootID">>; + +// FIXME: Remove / merge with -fworking-directory? +def fcas_fs_working_directory : Separate<["-"], "fcas-fs-working-directory">, + Group<f_Group>, MetaVarName<"<dir>">, + HelpText<"Working directory for -fcas-fs (if not the root).">, + ShouldParseIf<!strconcat("!", fcas_fs.KeyPath, ".empty()")>, + MarshallingInfoString<FileSystemOpts<"CASFileSystemWorkingDirectory">>; + +def fcas_include_tree : Separate<["-"], "fcas-include-tree">, + Group<f_Group>, MetaVarName<"<tree>">, + HelpText<"Configure the frontend to use a CAS include tree.">, + MarshallingInfoString<FrontendOpts<"CASIncludeTreeID">>; + +defm module_load_ignore_cas : BoolFOption<"module-load-ignore-cas", + FrontendOpts<"ModuleLoadIgnoreCAS">, DefaultFalse, + PosFlag<SetTrue, [], [ClangOption], "Ignore CAS info when loading modules or PCHs">, + NegFlag<SetFalse>>; + +// FIXME: Add to driver under -fexperimental-cache=compile-job. +defm cache_compile_job : BoolFOption<"cache-compile-job", + FrontendOpts<"CacheCompileJob">, DefaultFalse, + PosFlag<SetTrue, [], [ClangOption], "Try to cache full compilation job (-cc1). Ignored" + " (for now) without -fcas-fs.">, + NegFlag<SetFalse>>; + +defm cache_disable_replay : BoolFOption<"cache-disable-replay", + FrontendOpts<"DisableCachedCompileJobReplay">, DefaultFalse, + PosFlag<SetTrue, [], [ClangOption], "Disable replaying a cached compile job">, + NegFlag<SetFalse>>; + +def fcompilation_caching_service_path : Separate<["-"], "fcompilation-caching-service-path">, + Group<f_Group>, MetaVarName<"<pathname>">, + HelpText<"Specify the socket path for connecting to a remote caching service">, + MarshallingInfoString<FrontendOpts<"CompilationCachingServicePath">>; + +defm casid_output : BoolFOption<"casid-output", + FrontendOpts<"WriteOutputAsCASID">, DefaultFalse, + PosFlag<SetTrue, [], [ClangOption], "When using -fexperimental-cache-compile," + " write a CASID for the output file.">, + NegFlag<SetFalse>>; + +defm include_tree_preserve_pch_path : BoolFOption<"include-tree-preserve-pch-path", + FrontendOpts<"IncludeTreePreservePCHPath">, DefaultFalse, + PosFlag<SetTrue, [], [], "Keep the original PCH path in include-tree rather than canonicalizing">, + NegFlag<SetFalse>>; + +/// BEGIN MCCAS +defm cas_backend : BoolFOption<"cas-backend", + CodeGenOpts<"UseCASBackend">, DefaultFalse, + PosFlag<SetTrue, [], [ClangOption], "Enable using CASBackend for object file output">, + NegFlag<SetFalse>>; + +defm cas_emit_casid_file : BoolFOption<"cas-emit-casid-file", + CodeGenOpts<"EmitCASIDFile">, DefaultFalse, + PosFlag<SetTrue, [], [], "Emit .casid file next to object file when using cas backend">, + NegFlag<SetFalse>>; + +def fcas_backend_mode : Joined<["-"], "fcas-backend-mode=">, + Group<f_Group>, MetaVarName<"<native|casid|verify>">, + HelpText<"CASBackendMode for output kind">, + Values<"native,casid,verify">, NormalizedValuesScope<"llvm::CASBackendMode">, + NormalizedValues<["Native", "CASID", "Verify"]>, + MarshallingInfoEnum<CodeGenOpts<"CASObjMode">, "Native">; +/// END MCCAS + +} // let Visibility = [CC1Option] + def sycl_std_EQ : Joined<["-"], "sycl-std=">, Group<sycl_Group>, Flags<[NoArgumentUnused]>, Visibility<[ClangOption, CC1Option, CLOption]>, @@ -8836,6 +9228,12 @@ def dxc_hlsl_version : Option<["/", "-"], "HV", KIND_JOINED_OR_SEPARATE>, Visibility<[DXCOption]>, HelpText<"HLSL Version">, Values<"2016, 2017, 2018, 2021, 202x">; + +def fsuppress_conflicting_types: Flag<["-"], "fsuppress-conflicting-types">, Group<f_Group>, + MarshallingInfoFlag<LangOpts<"IgnoreConflictingTypes">>, + HelpText<"Ignore errors from conflicting types in function declarations">, + Visibility<[ClangOption, CC1Option]>; + def dxc_validator_path_EQ : Joined<["--"], "dxv-path=">, Group<dxc_Group>, HelpText<"DXIL validator installation path">; def dxc_disable_validation : DXCFlag<"Vd">, diff --git a/clang/include/clang/Driver/Phases.h b/clang/include/clang/Driver/Phases.h index 9003c58573513..e30d33926f84d 100644 --- a/clang/include/clang/Driver/Phases.h +++ b/clang/include/clang/Driver/Phases.h @@ -15,6 +15,7 @@ namespace phases { /// ID - Ordered values for successive stages in the /// compilation process which interact with user options. enum ID { + Depscan, Preprocess, Precompile, Compile, diff --git a/clang/include/clang/Driver/Types.def b/clang/include/clang/Driver/Types.def index 0e0cae5fb7068..122e8279455af 100644 --- a/clang/include/clang/Driver/Types.def +++ b/clang/include/clang/Driver/Types.def @@ -112,3 +112,6 @@ TYPE("hip-fatbin", HIP_FATBIN, INVALID, "hipfb", phases TYPE("api-information", API_INFO, INVALID, "json", phases::Precompile) TYPE("dx-container", DX_CONTAINER, INVALID, "dxo", phases::Compile, phases::Backend) TYPE("none", Nothing, INVALID, nullptr, phases::Compile, phases::Backend, phases::Assemble, phases::Link) + +// Generic command line type, used to pass command as response file to the next action. +TYPE("response-file", ResponseFile, INVALID, "rsp", phases::Depscan) diff --git a/clang/include/clang/Edit/RefactoringFixits.h b/clang/include/clang/Edit/RefactoringFixits.h new file mode 100644 index 0000000000000..385f078080747 --- /dev/null +++ b/clang/include/clang/Edit/RefactoringFixits.h @@ -0,0 +1,65 @@ +//===--- RefactoringFixits.h - Fixit producers for refactorings -*- 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_EDIT_REFACTORING_FIXITS_H +#define LLVM_CLANG_EDIT_REFACTORING_FIXITS_H + +#include "clang/Basic/Diagnostic.h" +#include "llvm/ADT/STLExtras.h" + +namespace clang { + +class ASTContext; +class SwitchStmt; +class EnumDecl; +class ObjCContainerDecl; + +namespace edit { + +/** + * Generates the fix-its that perform the "add missing switch cases" refactoring + * operation. + */ +void fillInMissingSwitchEnumCases( + ASTContext &Context, const SwitchStmt *Switch, const EnumDecl *Enum, + const DeclContext *SwitchContext, + llvm::function_ref<void(const FixItHint &)> Consumer); + +/// Responsible for the fix-its that perform the +/// "add missing protocol requirements" refactoring operation. +namespace fillInMissingProtocolStubs { + +class FillInMissingProtocolStubsImpl; +class FillInMissingProtocolStubs { + std::unique_ptr<FillInMissingProtocolStubsImpl> Impl; + +public: + FillInMissingProtocolStubs(); + ~FillInMissingProtocolStubs(); + FillInMissingProtocolStubs(FillInMissingProtocolStubs &&); + FillInMissingProtocolStubs &operator=(FillInMissingProtocolStubs &&); + + /// Initiate the FillInMissingProtocolStubs edit. + /// + /// \returns true on Error. + bool initiate(ASTContext &Context, const ObjCContainerDecl *Container); + bool hasMissingRequiredMethodStubs(); + void perform(ASTContext &Context, + llvm::function_ref<void(const FixItHint &)> Consumer); +}; + +void addMissingProtocolStubs( + ASTContext &Context, const ObjCContainerDecl *Container, + llvm::function_ref<void(const FixItHint &)> Consumer); + +} // end namespace fillInMissingProtocolStubs + +} // end namespace edit +} // end namespace clang + +#endif // LLVM_CLANG_EDIT_REFACTORING_FIXITS_H diff --git a/clang/include/clang/ExtractAPI/API.h b/clang/include/clang/ExtractAPI/API.h index bf291074fd061..c30e6fac66d6b 100644 --- a/clang/include/clang/ExtractAPI/API.h +++ b/clang/include/clang/ExtractAPI/API.h @@ -19,21 +19,14 @@ #define LLVM_CLANG_EXTRACTAPI_API_H #include "clang/AST/Availability.h" -#include "clang/AST/Decl.h" #include "clang/AST/DeclBase.h" -#include "clang/AST/DeclObjC.h" #include "clang/AST/RawCommentList.h" #include "clang/Basic/SourceLocation.h" -#include "clang/Basic/Specifiers.h" #include "clang/ExtractAPI/DeclarationFragments.h" -#include "llvm/ADT/ArrayRef.h" -#include "llvm/ADT/MapVector.h" -#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/SmallVector.h" #include "llvm/Support/Allocator.h" #include "llvm/Support/Casting.h" #include "llvm/Support/Compiler.h" -#include "llvm/Support/ErrorHandling.h" -#include "llvm/Support/raw_ostream.h" #include "llvm/TargetParser/Triple.h" #include <cstddef> #include <iterator> @@ -328,6 +321,8 @@ class RecordContext { /// chain. void stealRecordChain(RecordContext &Other); + void removeFromRecordChain(APIRecord *Record); + APIRecord::RecordKind getKind() const { return Kind; } struct record_iterator { @@ -621,7 +616,24 @@ struct TagRecord : APIRecord, RecordContext { return classofKind(Record->getKind()); } static bool classofKind(RecordKind K) { - return K == RK_Struct || K == RK_Union || K == RK_Enum; + switch (K) { + case RK_Enum: + LLVM_FALLTHROUGH; + case RK_Struct: + LLVM_FALLTHROUGH; + case RK_Union: + LLVM_FALLTHROUGH; + case RK_CXXClass: + LLVM_FALLTHROUGH; + case RK_ClassTemplate: + LLVM_FALLTHROUGH; + case RK_ClassTemplateSpecialization: + LLVM_FALLTHROUGH; + case RK_ClassTemplatePartialSpecialization: + return true; + default: + return false; + } } bool IsEmbeddedInVarDeclarator; @@ -690,7 +702,22 @@ struct RecordRecord : TagRecord { return classofKind(Record->getKind()); } static bool classofKind(RecordKind K) { - return K == RK_Struct || K == RK_Union; + switch (K) { + case RK_Struct: + LLVM_FALLTHROUGH; + case RK_Union: + LLVM_FALLTHROUGH; + case RK_CXXClass: + LLVM_FALLTHROUGH; + case RK_ClassTemplate: + LLVM_FALLTHROUGH; + case RK_ClassTemplateSpecialization: + LLVM_FALLTHROUGH; + case RK_ClassTemplatePartialSpecialization: + return true; + default: + return false; + } } bool isAnonymousWithNoTypedef() { return Name.empty(); } @@ -1430,6 +1457,10 @@ class APISet { return TopLevelRecords; } + void removeRecord(StringRef USR); + + void removeRecord(APIRecord *Record); + APISet(const llvm::Triple &Target, Language Lang, const std::string &ProductName) : Target(Target), Lang(Lang), ProductName(ProductName) {} @@ -1456,7 +1487,7 @@ class APISet { // lives in the BumpPtrAllocator. using APIRecordStoredPtr = std::unique_ptr<APIRecord, APIRecordDeleter>; llvm::DenseMap<StringRef, APIRecordStoredPtr> USRBasedLookupTable; - std::vector<const APIRecord *> TopLevelRecords; + llvm::SmallVector<const APIRecord *, 32> TopLevelRecords; public: const std::string ProductName; diff --git a/clang/include/clang/ExtractAPI/DeclarationFragments.h b/clang/include/clang/ExtractAPI/DeclarationFragments.h index 535da90b98284..4ac744459031e 100644 --- a/clang/include/clang/ExtractAPI/DeclarationFragments.h +++ b/clang/include/clang/ExtractAPI/DeclarationFragments.h @@ -411,9 +411,9 @@ class DeclarationFragmentsBuilder { /// Build DeclarationFragments for a macro. /// /// \param Name name of the macro. - /// \param MD the associated MacroDirective. + /// \param MI the associated MacroInfo. static DeclarationFragments getFragmentsForMacro(StringRef Name, - const MacroDirective *MD); + const MacroInfo *MI); /// Build DeclarationFragments for a typedef \p TypedefNameDecl. static DeclarationFragments diff --git a/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h b/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h index 1b27027621666..e60440e14a9fe 100644 --- a/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h +++ b/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h @@ -40,6 +40,8 @@ namespace impl { template <typename Derived> class ExtractAPIVisitorBase : public RecursiveASTVisitor<Derived> { + using Base = RecursiveASTVisitor<Derived>; + protected: ExtractAPIVisitorBase(ASTContext &Context, APISet &API) : Context(Context), API(API) {} @@ -81,8 +83,10 @@ class ExtractAPIVisitorBase : public RecursiveASTVisitor<Derived> { bool VisitNamespaceDecl(const NamespaceDecl *Decl); + bool TraverseRecordDecl(RecordDecl *Decl); bool VisitRecordDecl(const RecordDecl *Decl); + bool TraverseCXXRecordDecl(CXXRecordDecl *Decl); bool VisitCXXRecordDecl(const CXXRecordDecl *Decl); bool VisitCXXMethodDecl(const CXXMethodDecl *Decl); @@ -209,7 +213,7 @@ class ExtractAPIVisitorBase : public RecursiveASTVisitor<Derived> { StringRef getOwningModuleName(const Decl &D) { if (auto *OwningModule = D.getImportedOwningModule()) - return OwningModule->Name; + return OwningModule->getTopLevelModule()->Name; return {}; } @@ -240,7 +244,7 @@ class ExtractAPIVisitorBase : public RecursiveASTVisitor<Derived> { bool isEmbeddedInVarDeclarator(const TagDecl &D) { return D.getName().empty() && getTypedefName(&D).empty() && - D.isEmbeddedInDeclarator(); + D.isEmbeddedInDeclarator() && !D.isFreeStanding(); } void maybeMergeWithAnonymousTag(const DeclaratorDecl &D, @@ -248,12 +252,19 @@ class ExtractAPIVisitorBase : public RecursiveASTVisitor<Derived> { if (!NewRecordContext) return; auto *Tag = D.getType()->getAsTagDecl(); + if (!Tag) { + if (const auto *AT = D.getASTContext().getAsArrayType(D.getType())) { + Tag = AT->getElementType()->getAsTagDecl(); + } + } SmallString<128> TagUSR; clang::index::generateUSRForDecl(Tag, TagUSR); if (auto *Record = llvm::dyn_cast_if_present<TagRecord>( API.findRecordForUSR(TagUSR))) { - if (Record->IsEmbeddedInVarDeclarator) + if (Record->IsEmbeddedInVarDeclarator) { NewRecordContext->stealRecordChain(*Record); + API.removeRecord(Record); + } } } }; @@ -548,6 +559,19 @@ bool ExtractAPIVisitorBase<Derived>::VisitNamespaceDecl( return true; } +template <typename Derived> +bool ExtractAPIVisitorBase<Derived>::TraverseRecordDecl(RecordDecl *Decl) { + bool Ret = Base::TraverseRecordDecl(Decl); + + if (!isEmbeddedInVarDeclarator(*Decl) && Decl->isAnonymousStructOrUnion()) { + SmallString<128> USR; + index::generateUSRForDecl(Decl, USR); + API.removeRecord(USR); + } + + return Ret; +} + template <typename Derived> bool ExtractAPIVisitorBase<Derived>::VisitRecordDecl(const RecordDecl *Decl) { if (!getDerivedExtractAPIVisitor().shouldDeclBeIncluded(Decl)) @@ -588,6 +612,20 @@ bool ExtractAPIVisitorBase<Derived>::VisitRecordDecl(const RecordDecl *Decl) { return true; } +template <typename Derived> +bool ExtractAPIVisitorBase<Derived>::TraverseCXXRecordDecl( + CXXRecordDecl *Decl) { + bool Ret = Base::TraverseCXXRecordDecl(Decl); + + if (!isEmbeddedInVarDeclarator(*Decl) && Decl->isAnonymousStructOrUnion()) { + SmallString<128> USR; + index::generateUSRForDecl(Decl, USR); + API.removeRecord(USR); + } + + return Ret; +} + template <typename Derived> bool ExtractAPIVisitorBase<Derived>::VisitCXXRecordDecl( const CXXRecordDecl *Decl) { @@ -1108,11 +1146,29 @@ bool ExtractAPIVisitorBase<Derived>::VisitTypedefNameDecl( StringRef Name = Decl->getName(); + auto nameMatches = [&Name](TagDecl *TagDecl) { + StringRef TagName = TagDecl->getName(); + + if (TagName == Name) + return true; + + // Also check whether the tag decl's name is the same as the typedef name + // with prefixed underscores + if (TagName.starts_with('_')) { + StringRef StrippedName = TagName.ltrim('_'); + + if (StrippedName == Name) + return true; + } + + return false; + }; + // If the underlying type was defined as part of the typedef modify it's // fragments directly and pretend the typedef doesn't exist. if (auto *TagDecl = Decl->getUnderlyingType()->getAsTagDecl()) { if (TagDecl->isEmbeddedInDeclarator() && TagDecl->isCompleteDefinition() && - Decl->getName() == TagDecl->getName()) { + nameMatches(TagDecl)) { SmallString<128> TagUSR; index::generateUSRForDecl(TagDecl, TagUSR); if (auto *Record = API.findRecordForUSR(TagUSR)) { @@ -1126,6 +1182,11 @@ bool ExtractAPIVisitorBase<Derived>::VisitTypedefNameDecl( .append(Name, DeclarationFragments::FragmentKind::Identifier) .appendSemicolon(); + // Replace the name and subheading in case it's underscored so we can + // use the non-underscored version + Record->Name = Name; + Record->SubHeading = DeclarationFragmentsBuilder::getSubHeading(Decl); + return true; } } diff --git a/clang/include/clang/Frontend/CASDependencyCollector.h b/clang/include/clang/Frontend/CASDependencyCollector.h new file mode 100644 index 0000000000000..1fa101bec866c --- /dev/null +++ b/clang/include/clang/Frontend/CASDependencyCollector.h @@ -0,0 +1,54 @@ +//===- CASDependencyCollector.h ---------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_FRONTEND_CASDEPENDENCYCOLLECTOR_H +#define LLVM_CLANG_FRONTEND_CASDEPENDENCYCOLLECTOR_H + +#include "clang/Frontend/Utils.h" +#include "llvm/CAS/CASReference.h" + +namespace clang { + +/// Collects dependencies when attached to a Preprocessor (for includes) and +/// ASTReader (for module imports), and writes it to the CAS in a manner +/// suitable to be replayed into a DependencyFileGenerator. +class CASDependencyCollector : public DependencyFileGenerator { +public: + /// Create a \CASDependencyCollector for the given output options. + /// + /// \param Opts Output options. Only options that affect the list of + /// dependency files are significant. + /// \param CAS The CAS to write the dependency list to. + /// \param Callback Callback that receives the resulting dependencies on + /// completion, or \c None if an error occurred. + CASDependencyCollector( + DependencyOutputOptions Opts, cas::ObjectStore &CAS, + std::function<void(std::optional<cas::ObjectRef>)> Callback); + + /// Replay the given result, which should have been created by a + /// \c CASDependencyCollector instance. + /// + /// \param Opts Output options. Only options that affect the output format of + /// a dependency file are signficant. + /// \param CAS The CAS to read the result from. + /// \param Deps The dependencies. + /// \param OS The output stream to write the dependency file to. + static llvm::Error replay(const DependencyOutputOptions &Opts, + cas::ObjectStore &CAS, cas::ObjectProxy Deps, + llvm::raw_ostream &OS); + +private: + void finishedMainFile(DiagnosticsEngine &Diags) override; + + cas::ObjectStore &CAS; + std::function<void(std::optional<cas::ObjectRef>)> Callback; +}; + +} // namespace clang + +#endif // LLVM_CLANG_FRONTEND_CASDEPENDENCYCOLLECTOR_H diff --git a/clang/include/clang/Frontend/CompileJobCache.h b/clang/include/clang/Frontend/CompileJobCache.h new file mode 100644 index 0000000000000..2ca3f343afbd7 --- /dev/null +++ b/clang/include/clang/Frontend/CompileJobCache.h @@ -0,0 +1,114 @@ +//===- CompileJobCache.h ----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_FRONTEND_COMPILEJOBCACHE_H +#define LLVM_CLANG_FRONTEND_COMPILEJOBCACHE_H + +#include "clang/Frontend/CompileJobCacheResult.h" + +namespace clang { + +class CompilerInstance; +class CompilerInvocation; +class DiagnosticsEngine; + +// Manage caching and replay of compile jobs. +// +// The high-level model is: +// +// 1. Extract options from the CompilerInvocation: +// - that can be simulated and +// - that don't affect the compile job's result. +// 2. Canonicalize the options extracted in (1). +// 3. Compute the result of the compile job using the canonicalized +// CompilerInvocation, with hooks installed to redirect outputs and +// enable live-streaming of a running compile job to stdout or stderr. +// - Compute a cache key. +// - Check the cache, and run the compile job if there's a cache miss. +// - Store the result of the compile job in the cache. +// 4. Replay the compile job, using the options extracted in (1). +// +// An example (albeit not yet implemented) is handling options controlling +// output of diagnostics. The CompilerInvocation can be canonicalized to +// serialize the diagnostics to a virtual path (<output>.diag or something). +// +// - On a cache miss, the compile job runs, and the diagnostics are +// serialized and stored in the cache per the canonicalized options +// from (2). +// - Either way, the diagnostics are replayed according to the options +// extracted from (1) during (4). +// +// The above will produce the correct output for diagnostics, but the experience +// will be degraded in the common command-line case (emitting to stderr) +// because the diagnostics will not be streamed live. This can be improved: +// +// - Change (3) to accept a hook: a DiagnosticsConsumer that diagnostics +// are mirrored to (in addition to canonicalized options from (2)). +// - If diagnostics would be live-streamed, send in a diagnostics consumer +// that matches (1). Otherwise, send in an IgnoringDiagnosticsConsumer. +// - In step (4), only skip replaying the diagnostics if they were already +// handled. +class CompileJobCache { +public: + CompileJobCache(); + ~CompileJobCache(); + + using OutputKind = clang::cas::CompileJobCacheResult::OutputKind; + + StringRef getPathForOutputKind(OutputKind Kind); + + /// Canonicalize \p Clang. + /// + /// \returns status if should exit immediately, otherwise None. + /// + /// TODO: Refactor \a cc1_main() so that instead this canonicalizes the + /// CompilerInvocation before Clang gets access to command-line arguments, to + /// control what might leak. + std::optional<int> initialize(CompilerInstance &Clang); + + /// Try looking up a cached result and replaying it. + /// + /// \returns status if should exit immediately, otherwise None. + std::optional<int> tryReplayCachedResult(CompilerInstance &Clang); + + /// Finish writing outputs from a computed result, after a cache miss. + /// + /// \returns true if finished successfully. + bool finishComputedResult(CompilerInstance &Clang, bool Success); + + static llvm::Expected<std::optional<int>> + replayCachedResult(std::shared_ptr<CompilerInvocation> Invok, + StringRef WorkingDir, const llvm::cas::CASID &CacheKey, + cas::CompileJobCacheResult &CachedResult, + SmallVectorImpl<char> &DiagText, + bool WriteOutputAsCASID = false, + std::optional<llvm::cas::CASID> *MCOutputID = nullptr); + + class CachingOutputs; + +private: + /// \returns true if the output from the compilation is not supported for + /// caching. + Expected<bool> + maybeIngestNonVirtualOutputFromFileSystem(CompilerInstance &Clang); + int reportCachingBackendError(DiagnosticsEngine &Diag, llvm::Error &&E); + + bool CacheCompileJob = false; + bool DisableCachedCompileJobReplay = false; + std::optional<llvm::cas::CASID> MCOutputID; + + std::shared_ptr<llvm::cas::ObjectStore> CAS; + std::shared_ptr<llvm::cas::ActionCache> Cache; + std::optional<llvm::cas::CASID> ResultCacheKey; + + std::unique_ptr<CachingOutputs> CacheBackend; +}; + +} // namespace clang + +#endif // LLVM_CLANG_FRONTEND_COMPILEJOBCACHE_H diff --git a/clang/include/clang/Frontend/CompileJobCacheKey.h b/clang/include/clang/Frontend/CompileJobCacheKey.h new file mode 100644 index 0000000000000..94656352be0bf --- /dev/null +++ b/clang/include/clang/Frontend/CompileJobCacheKey.h @@ -0,0 +1,71 @@ +//===- CompileJobCacheKey.h -------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file Functions for working with compile job cache keys. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_FRONTEND_COMPILEJOBCACHEKEY_H +#define LLVM_CLANG_FRONTEND_COMPILEJOBCACHEKEY_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/CAS/CASID.h" +#include "llvm/Support/Error.h" + +namespace llvm { +namespace cas { +class ObjectStore; +} +class raw_ostream; +} // namespace llvm + +namespace clang { + +class CompilerInvocation; +class CowCompilerInvocation; +class DiagnosticsEngine; + +/// Caching-related options for a given \c CompilerInvocation that are +/// canonicalized away by the cache key. See \c canonicalizeAndCreateCacheKey. +struct CompileJobCachingOptions { + /// See \c FrontendOptions::CompilationCachingServicePath. + std::string CompilationCachingServicePath; + /// See \c FrontendOptions::DisableCachedCompileJobReplay. + bool DisableCachedCompileJobReplay; + /// See \c FrontendOptions::WriteOutputAsCASID. + bool WriteOutputAsCASID; + /// See \c FrontendOptions::PathPrefixMappings. + std::vector<std::string> PathPrefixMappings; +}; + +/// Create a cache key for the given \c CompilerInvocation as a \c CASID. If \p +/// Invocation will later be used to compile code, use \c +/// canonicalizeAndCreateCacheKey instead. +std::optional<llvm::cas::CASID> +createCompileJobCacheKey(llvm::cas::ObjectStore &CAS, DiagnosticsEngine &Diags, + const CompilerInvocation &Invocation); +std::optional<llvm::cas::CASID> +createCompileJobCacheKey(llvm::cas::ObjectStore &CAS, DiagnosticsEngine &Diags, + const CowCompilerInvocation &Invocation); + +/// Perform any destructive changes needed to canonicalize \p Invocation for +/// caching, extracting the settings that affect compilation even if they do not +/// affect caching, and return the resulting cache key as a \c CASID. +std::optional<llvm::cas::CASID> canonicalizeAndCreateCacheKey( + llvm::cas::ObjectStore &CAS, DiagnosticsEngine &Diags, + CompilerInvocation &Invocation, CompileJobCachingOptions &Opts); + +/// Print the structure of the cache key given by \p Key to \p OS. Returns an +/// error if the key object does not exist in \p CAS, or is malformed. +llvm::Error printCompileJobCacheKey(llvm::cas::ObjectStore &CAS, + const llvm::cas::CASID &Key, + llvm::raw_ostream &OS); + +} // namespace clang + +#endif // LLVM_CLANG_FRONTEND_COMPILEJOBCACHEKEY_H diff --git a/clang/include/clang/Frontend/CompileJobCacheResult.h b/clang/include/clang/Frontend/CompileJobCacheResult.h new file mode 100644 index 0000000000000..349ab316e742d --- /dev/null +++ b/clang/include/clang/Frontend/CompileJobCacheResult.h @@ -0,0 +1,126 @@ +//===- CompileJobCacheResult.h ----------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_FRONTEND_COMPILEJOBCACHERESULT_H +#define LLVM_CLANG_FRONTEND_COMPILEJOBCACHERESULT_H + +#include "clang/Basic/LLVM.h" +#include "llvm/CAS/CASNodeSchema.h" +#include "llvm/CAS/ObjectStore.h" + +namespace clang { +namespace cas { +class CompileJobResultSchema; + +class CompileJobCacheResult : public ObjectProxy { +public: + /// Categorization for the output kinds that is used to decouple the + /// compilation cache key from the specific output paths. + enum class OutputKind : char { + MainOutput, ///< Main output file, e.g. object file, pcm file, etc. + SerializedDiagnostics, + Dependencies, + }; + + /// Returns all \c OutputKind values. + static ArrayRef<OutputKind> getAllOutputKinds(); + + /// A single output file or stream. + struct Output { + /// The CAS object for this output. + ObjectRef Object; + /// The output kind. + OutputKind Kind; + + bool operator==(const Output &Other) const { + return Object == Other.Object && Kind == Other.Kind; + } + }; + + /// Retrieves each \c Output from this result. + llvm::Error + forEachOutput(llvm::function_ref<llvm::Error(Output)> Callback) const; + + /// Loads all outputs concurrently and passes the resulting \c ObjectProxy + /// objects to \p Callback. If there was an error during loading then the + /// callback will not be invoked. + llvm::Error forEachLoadedOutput( + llvm::function_ref<llvm::Error(Output, std::optional<ObjectProxy>)> + Callback); + + size_t getNumOutputs() const; + + Output getOutput(size_t I) const; + + /// Retrieves a specific output specified by \p Kind, if it exists. + std::optional<Output> getOutput(OutputKind Kind) const; + + /// \returns a string for the given \p Kind. + static StringRef getOutputKindName(OutputKind Kind); + + /// Print this result to \p OS. + llvm::Error print(llvm::raw_ostream &OS); + + /// Helper to build a \c CompileJobCacheResult from individual outputs. + class Builder { + public: + Builder(); + ~Builder(); + /// Treat outputs added for \p Path as having the given \p Kind. Otherwise + /// they will have kind \c Unknown. + void addKindMap(OutputKind Kind, StringRef Path); + /// Add an output with an explicit \p Kind. + void addOutput(OutputKind Kind, ObjectRef Object); + /// Add an output for the given \p Path. There must be a a kind map for it. + llvm::Error addOutput(StringRef Path, ObjectRef Object); + /// Build a single \c ObjectRef representing the provided outputs. The + /// result can be used with \c CompileJobResultSchema to retrieve the + /// original outputs. + Expected<ObjectRef> build(ObjectStore &CAS); + + private: + struct PrivateImpl; + PrivateImpl &Impl; + }; + +private: + ObjectRef getOutputObject(size_t I) const; + ObjectRef getPathsListRef() const; + OutputKind getOutputKind(size_t I) const; + Expected<ObjectRef> getOutputPath(size_t I) const; + +private: + friend class CompileJobResultSchema; + CompileJobCacheResult(const ObjectProxy &); +}; + +class CompileJobResultSchema + : public llvm::RTTIExtends<CompileJobResultSchema, llvm::cas::NodeSchema> { +public: + static char ID; + + CompileJobResultSchema(ObjectStore &CAS); + + /// Attempt to load \p Ref as a \c CompileJobCacheResult if it matches the + /// schema. + Expected<CompileJobCacheResult> load(ObjectRef Ref) const; + + bool isRootNode(const ObjectProxy &Node) const final; + bool isNode(const ObjectProxy &Node) const final; + + /// Get this schema's marker node. + ObjectRef getKindRef() const { return KindRef; } + +private: + ObjectRef KindRef; +}; + +} // namespace cas +} // namespace clang + +#endif // LLVM_CLANG_FRONTEND_COMPILEJOBCACHERESULT_H diff --git a/clang/include/clang/Frontend/CompilerInstance.h b/clang/include/clang/Frontend/CompilerInstance.h index 3464654284f19..2c0d4dcb91873 100644 --- a/clang/include/clang/Frontend/CompilerInstance.h +++ b/clang/include/clang/Frontend/CompilerInstance.h @@ -22,8 +22,12 @@ #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/IntrusiveRefCntPtr.h" #include "llvm/ADT/StringRef.h" +#include "llvm/CAS/CASID.h" +#include "llvm/CAS/CASOutputBackend.h" #include "llvm/Support/BuryPointer.h" #include "llvm/Support/FileSystem.h" +#include "llvm/Support/PrefixMapper.h" +#include "llvm/Support/VirtualOutputBackend.h" #include <cassert> #include <list> #include <memory> @@ -89,9 +93,24 @@ class CompilerInstance : public ModuleLoader { /// Auxiliary Target info. IntrusiveRefCntPtr<TargetInfo> AuxTarget; + /// The CAS, if any. + std::shared_ptr<llvm::cas::ObjectStore> CAS; + + /// The ActionCache, if any. + std::shared_ptr<llvm::cas::ActionCache> ActionCache; + + /// The \c ActionCache key for this compilation, if caching is enabled. + std::optional<cas::CASID> CompileJobCacheKey; + + /// The prefix mapper; empty by default. + llvm::PrefixMapper PrefixMapper; + /// The file manager. IntrusiveRefCntPtr<FileManager> FileMgr; + /// The output context. + IntrusiveRefCntPtr<llvm::vfs::OutputBackend> TheOutputBackend; + /// The source manager. IntrusiveRefCntPtr<SourceManager> SourceMgr; @@ -182,26 +201,22 @@ class CompilerInstance : public ModuleLoader { /// The stream for verbose output. raw_ostream *VerboseOutputStream = &llvm::errs(); - /// Holds information about the output file. - /// - /// If TempFilename is not empty we must rename it to Filename at the end. - /// TempFilename may be empty and Filename non-empty if creating the temporary - /// failed. - struct OutputFile { - std::string Filename; - std::optional<llvm::sys::fs::TempFile> File; + /// The list of active output files. + std::list<llvm::vfs::OutputFile> OutputFiles; - OutputFile(std::string filename, - std::optional<llvm::sys::fs::TempFile> file) - : Filename(std::move(filename)), File(std::move(file)) {} - }; + using GenModuleActionWrapperFunc = + std::function<std::unique_ptr<FrontendAction>( + const FrontendOptions &opts, std::unique_ptr<FrontendAction> action)>; - /// The list of active output files. - std::list<OutputFile> OutputFiles; + /// An optional callback function used to wrap all FrontendActions + /// produced to generate imported modules before they are executed. + GenModuleActionWrapperFunc GenModuleActionWrapper; /// Force an output buffer. std::unique_ptr<llvm::raw_pwrite_stream> OutputStream; + void createCASDatabases(); + CompilerInstance(const CompilerInstance &) = delete; void operator=(const CompilerInstance &) = delete; public: @@ -357,6 +372,26 @@ class CompilerInstance : public ModuleLoader { return Invocation->getTargetOpts(); } + CASOptions &getCASOpts() { + return Invocation->getCASOpts(); + } + const CASOptions &getCASOpts() const { + return Invocation->getCASOpts(); + } + + std::optional<cas::CASID> getCompileJobCacheKey() const { + return CompileJobCacheKey; + } + void setCompileJobCacheKey(cas::CASID Key) { + assert(!CompileJobCacheKey || CompileJobCacheKey == Key); + CompileJobCacheKey = std::move(Key); + } + bool isSourceNonReproducible() const; + + llvm::PrefixMapper &getPrefixMapper() { return PrefixMapper; } + + void setPrefixMapper(llvm::PrefixMapper PM) { PrefixMapper = std::move(PM); } + /// @} /// @name Diagnostics Engine /// @{ @@ -460,6 +495,21 @@ class CompilerInstance : public ModuleLoader { /// Replace the current file manager and virtual file system. void setFileManager(FileManager *Value); + /// Set the output manager. + void setOutputBackend(IntrusiveRefCntPtr<llvm::vfs::OutputBackend> NewOutputs); + + /// Create an output manager. + void createOutputBackend(); + + bool hasOutputBackend() const { return bool(TheOutputBackend); } + + llvm::vfs::OutputBackend &getOutputBackend(); + llvm::vfs::OutputBackend &getOrCreateOutputBackend(); + + /// Get the CAS, or create it using the configuration in CompilerInvocation. + llvm::cas::ObjectStore &getOrCreateObjectStore(); + llvm::cas::ActionCache &getOrCreateActionCache(); + /// @} /// @name Source Manager /// @{ @@ -722,7 +772,8 @@ class CompilerInstance : public ModuleLoader { std::string getSpecificModuleCachePath(StringRef ModuleHash); std::string getSpecificModuleCachePath() { - return getSpecificModuleCachePath(getInvocation().getModuleHash()); + return getSpecificModuleCachePath( + getInvocation().getModuleHash(getDiagnostics())); } /// Create the AST context. @@ -733,7 +784,8 @@ class CompilerInstance : public ModuleLoader { void createPCHExternalASTSource( StringRef Path, DisableValidationForModuleKind DisableValidation, bool AllowPCHWithCompilerErrors, void *DeserializationListener, - bool OwnDeserializationListener); + bool OwnDeserializationListener, + std::unique_ptr<llvm::MemoryBuffer> PCHBuffer = nullptr); /// Create an external AST source to read a PCH file. /// @@ -747,7 +799,9 @@ class CompilerInstance : public ModuleLoader { ArrayRef<std::shared_ptr<ModuleFileExtension>> Extensions, ArrayRef<std::shared_ptr<DependencyCollector>> DependencyCollectors, void *DeserializationListener, bool OwnDeserializationListener, - bool Preamble, bool UseGlobalModuleIndex); + bool Preamble, bool UseGlobalModuleIndex, + cas::ObjectStore &CAS, cas::ActionCache &Cache, bool ignoreCAS, + std::unique_ptr<llvm::MemoryBuffer> PCHBuffer = nullptr); /// Create a code completion consumer using the invocation; note that this /// will cause the source manager to truncate the input source file at the @@ -834,6 +888,9 @@ class CompilerInstance : public ModuleLoader { FileManager &FileMgr, SourceManager &SourceMgr); + /// Initialize inputs from CAS. + void initializeDelayedInputFileFromCAS(); + /// @} void setOutputStream(std::unique_ptr<llvm::raw_pwrite_stream> OutStream) { @@ -885,12 +942,30 @@ class CompilerInstance : public ModuleLoader { bool lookupMissingImports(StringRef Name, SourceLocation TriggerLoc) override; + void setGenModuleActionWrapper(GenModuleActionWrapperFunc Wrapper) { + GenModuleActionWrapper = Wrapper; + } + + GenModuleActionWrapperFunc getGenModuleActionWrapper() const { + return GenModuleActionWrapper; + } + void addDependencyCollector(std::shared_ptr<DependencyCollector> Listener) { DependencyCollectors.push_back(std::move(Listener)); } void setExternalSemaSource(IntrusiveRefCntPtr<ExternalSemaSource> ESS); + /// Adds a module to the \c InMemoryModuleCache at \p Path by retrieving the + /// pcm output from the \c ActionCache for \p CacheKey. + /// + /// \param Provider description of what provided this cache key, e.g. + /// "-fmodule-file-cache-key", or an imported pcm file. Used in diagnostics. + /// + /// \returns true on failure. + bool addCachedModuleFile(StringRef Path, StringRef CacheKey, + StringRef Provider); + InMemoryModuleCache &getModuleCache() const { return *ModuleCache; } }; diff --git a/clang/include/clang/Frontend/CompilerInvocation.h b/clang/include/clang/Frontend/CompilerInvocation.h index 9daa0a1ecf948..e43689af70f12 100644 --- a/clang/include/clang/Frontend/CompilerInvocation.h +++ b/clang/include/clang/Frontend/CompilerInvocation.h @@ -16,6 +16,7 @@ #include "clang/Basic/LLVM.h" #include "clang/Basic/LangOptions.h" #include "clang/Basic/LangStandard.h" +#include "clang/CAS/CASOptions.h" #include "clang/Frontend/DependencyOutputOptions.h" #include "clang/Frontend/FrontendOptions.h" #include "clang/Frontend/MigratorOptions.h" @@ -42,6 +43,11 @@ class FileSystem; } // namespace vfs +namespace cas { + +class ObjectStore; +} + } // namespace llvm namespace clang { @@ -96,6 +102,9 @@ class CompilerInvocationBase { /// Options controlling API notes. std::shared_ptr<APINotesOptions> APINotesOpts; + /// Options configuring the CAS. + std::shared_ptr<CASOptions> CASOpts; + /// Options controlling IRgen and the backend. std::shared_ptr<CodeGenOptions> CodeGenOpts; @@ -136,6 +145,7 @@ class CompilerInvocationBase { const AnalyzerOptions &getAnalyzerOpts() const { return *AnalyzerOpts; } const MigratorOptions &getMigratorOpts() const { return *MigratorOpts; } const APINotesOptions &getAPINotesOpts() const { return *APINotesOpts; } + const CASOptions &getCASOpts() const { return *CASOpts; } const CodeGenOptions &getCodeGenOpts() const { return *CodeGenOpts; } const FileSystemOptions &getFileSystemOpts() const { return *FSOpts; } const FrontendOptions &getFrontendOpts() const { return *FrontendOpts; } @@ -199,6 +209,21 @@ class CompilerInvocationBase { const std::string &OutputFile, const LangOptions *LangOpts); /// @} + +public: + /// Generate command line options from CASOptions. + static void GenerateCASArgs(const CASOptions &Opts, + ArgumentConsumer Consumer); + static void GenerateCASArgs(const CASOptions &Opts, + SmallVectorImpl<const char *> &Args, + StringAllocator SA) { + GenerateCASArgs(Opts, [&](const Twine &Arg) { + // No need to allocate static string literals. + Args.push_back(Arg.isSingleStringLiteral() + ? Arg.getSingleStringRef().data() + : SA(Arg)); + }); + } }; class CowCompilerInvocation; @@ -237,6 +262,7 @@ class CompilerInvocation : public CompilerInvocationBase { using CompilerInvocationBase::getAnalyzerOpts; using CompilerInvocationBase::getMigratorOpts; using CompilerInvocationBase::getAPINotesOpts; + using CompilerInvocationBase::getCASOpts; using CompilerInvocationBase::getCodeGenOpts; using CompilerInvocationBase::getFileSystemOpts; using CompilerInvocationBase::getFrontendOpts; @@ -254,6 +280,7 @@ class CompilerInvocation : public CompilerInvocationBase { AnalyzerOptions &getAnalyzerOpts() { return *AnalyzerOpts; } MigratorOptions &getMigratorOpts() { return *MigratorOpts; } APINotesOptions &getAPINotesOpts() { return *APINotesOpts; } + CASOptions &getCASOpts() { return *CASOpts; } CodeGenOptions &getCodeGenOpts() { return *CodeGenOpts; } FileSystemOptions &getFileSystemOpts() { return *FSOpts; } FrontendOptions &getFrontendOpts() { return *FrontendOpts; } @@ -277,6 +304,10 @@ class CompilerInvocation : public CompilerInvocationBase { return PPOpts; } std::shared_ptr<LangOptions> getLangOptsPtr() { return LangOpts; } + std::shared_ptr<CASOptions> getCASOptsPtr() { return CASOpts; } + void setCASOption(std::shared_ptr<CASOptions> CASOpts) { + this->CASOpts = CASOpts; + } /// @} /// Create a compiler invocation from a list of input options. @@ -316,7 +347,7 @@ class CompilerInvocation : public CompilerInvocationBase { /// Retrieve a module hash string that is suitable for uniquely /// identifying the conditions under which the module was built. - std::string getModuleHash() const; + std::string getModuleHash(DiagnosticsEngine &Diags) const; /// Check that \p Args can be parsed and re-serialized without change, /// emiting diagnostics for any differences. @@ -337,6 +368,10 @@ class CompilerInvocation : public CompilerInvocationBase { /// implicit modules. void clearImplicitModuleBuildOptions(); + /// Parse command line options that map to \p CASOptions. + static bool ParseCASArgs(CASOptions &Opts, const llvm::opt::ArgList &Args, + DiagnosticsEngine &Diags); + private: static bool CreateFromArgsImpl(CompilerInvocation &Res, ArrayRef<const char *> CommandLineArgs, @@ -353,7 +388,10 @@ class CompilerInvocation : public CompilerInvocationBase { InputKind IK, DiagnosticsEngine &Diags, const llvm::Triple &T, const std::string &OutputFile, - const LangOptions &LangOptsRef); + const LangOptions &LangOptsRef, + const FileSystemOptions &FSOpts, + const FrontendOptions &FEOpts, + const CASOptions &CASOpts); }; /// Same as \c CompilerInvocation, but with copy-on-write optimization. @@ -391,6 +429,7 @@ class CowCompilerInvocation : public CompilerInvocationBase { AnalyzerOptions &getMutAnalyzerOpts(); MigratorOptions &getMutMigratorOpts(); APINotesOptions &getMutAPINotesOpts(); + CASOptions &getMutCASOpts(); CodeGenOptions &getMutCodeGenOpts(); FileSystemOptions &getMutFileSystemOpts(); FrontendOptions &getMutFrontendOpts(); @@ -399,9 +438,9 @@ class CowCompilerInvocation : public CompilerInvocationBase { /// @} }; -IntrusiveRefCntPtr<llvm::vfs::FileSystem> -createVFSFromCompilerInvocation(const CompilerInvocation &CI, - DiagnosticsEngine &Diags); +IntrusiveRefCntPtr<llvm::vfs::FileSystem> createVFSFromCompilerInvocation( + const CompilerInvocation &CI, DiagnosticsEngine &Diags, + std::shared_ptr<llvm::cas::ObjectStore> OverrideCAS = nullptr); IntrusiveRefCntPtr<llvm::vfs::FileSystem> createVFSFromCompilerInvocation( const CompilerInvocation &CI, DiagnosticsEngine &Diags, diff --git a/clang/include/clang/Frontend/DependencyOutputOptions.h b/clang/include/clang/Frontend/DependencyOutputOptions.h index d92a87d78d7c5..dcf38aae4461a 100644 --- a/clang/include/clang/Frontend/DependencyOutputOptions.h +++ b/clang/include/clang/Frontend/DependencyOutputOptions.h @@ -46,6 +46,8 @@ class DependencyOutputOptions { LLVM_PREFERRED_TYPE(bool) unsigned IncludeModuleFiles : 1; ///< Include module file dependencies. LLVM_PREFERRED_TYPE(bool) + unsigned SkipUnusedModuleMaps : 1; ///< Skip unused module map dependencies. + LLVM_PREFERRED_TYPE(bool) unsigned ShowSkippedHeaderIncludes : 1; ///< With ShowHeaderIncludes, show /// also includes that were skipped /// due to the "include guard @@ -89,7 +91,7 @@ class DependencyOutputOptions { public: DependencyOutputOptions() : IncludeSystemHeaders(0), ShowHeaderIncludes(0), UsePhonyTargets(0), - AddMissingHeaderDeps(0), IncludeModuleFiles(0), + AddMissingHeaderDeps(0), IncludeModuleFiles(0), SkipUnusedModuleMaps(0), ShowSkippedHeaderIncludes(0), HeaderIncludeFormat(HIFMT_Textual), HeaderIncludeFiltering(HIFIL_None) {} }; diff --git a/clang/include/clang/Frontend/FrontendOptions.h b/clang/include/clang/Frontend/FrontendOptions.h index 8241925c98476..4e0c4ce4b4b83 100644 --- a/clang/include/clang/Frontend/FrontendOptions.h +++ b/clang/include/clang/Frontend/FrontendOptions.h @@ -15,6 +15,7 @@ #include "clang/Sema/CodeCompleteOptions.h" #include "clang/Serialization/ModuleFileExtension.h" #include "llvm/ADT/StringRef.h" +#include "llvm/CAS/CASReference.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/MemoryBuffer.h" #include <cassert> @@ -238,6 +239,9 @@ class FrontendInputFile { /// that it outlives any users. std::optional<llvm::MemoryBufferRef> Buffer; + /// The input, if it comes from \p FrontendOptions::CASIncludeTreeID. + std::optional<cas::ObjectRef> IncludeTree; + /// The kind of input, e.g., C source, AST file, LLVM IR. InputKind Kind; @@ -251,6 +255,14 @@ class FrontendInputFile { FrontendInputFile(llvm::MemoryBufferRef Buffer, InputKind Kind, bool IsSystem = false) : Buffer(Buffer), Kind(Kind), IsSystem(IsSystem) {} + FrontendInputFile(cas::ObjectRef Tree, StringRef File, InputKind Kind, + bool IsSystem = false) + : File(File.str()), IncludeTree(std::move(Tree)), Kind(Kind), + IsSystem(IsSystem) {} + FrontendInputFile(cas::ObjectRef Tree, llvm::MemoryBufferRef Buffer, + InputKind Kind, bool IsSystem = false) + : Buffer(Buffer), IncludeTree(std::move(Tree)), Kind(Kind), + IsSystem(IsSystem) {} InputKind getKind() const { return Kind; } bool isSystem() const { return IsSystem; } @@ -258,6 +270,7 @@ class FrontendInputFile { bool isEmpty() const { return File.empty() && Buffer == std::nullopt; } bool isFile() const { return !isBuffer(); } bool isBuffer() const { return Buffer != std::nullopt; } + bool isIncludeTree() const { return IncludeTree.has_value(); } bool isPreprocessed() const { return Kind.isPreprocessed(); } bool isHeader() const { return Kind.isHeader(); } InputKind::HeaderUnitKind getHeaderUnitKind() const { @@ -273,6 +286,11 @@ class FrontendInputFile { assert(isBuffer()); return *Buffer; } + + cas::ObjectRef getIncludeTree() const { + assert(isIncludeTree()); + return *IncludeTree; + } }; /// FrontendOptions - Options for controlling the behavior of the frontend. @@ -387,6 +405,37 @@ class FrontendOptions { LLVM_PREFERRED_TYPE(bool) unsigned IsSystemModule : 1; + unsigned IndexIgnoreSystemSymbols : 1; + unsigned IndexRecordCodegenName : 1; + unsigned IndexIgnoreMacros : 1; + unsigned IndexIgnorePcms : 1; + + /// Cache -cc1 compilations when possible. Ignored unless CASFileSystemRootID + /// is specified. + unsigned CacheCompileJob : 1; + + /// Whether this invocation is dependency scanning for include-tree. Used to + /// separate module cache for include-tree from cas-fs. + unsigned ForIncludeTreeScan : 1; + + /// Avoid checking if the compile job is already cached, force compilation and + /// caching of compilation outputs. This is used for testing purposes. + unsigned DisableCachedCompileJobReplay : 1; + + /// Whether to preserve the original PCH path in the include-tree, or to + /// canonicalize it to a fixed value. Setting this to \c true allows the use + /// of gmodules with PCH and include tree. + unsigned IncludeTreePreservePCHPath : 1; + + /// Keep the diagnostic client open for receiving diagnostics after the source + /// files have been processed. + unsigned MayEmitDiagnosticsAfterProcessingSourceFiles : 1; + + /// When using CacheCompileJob, write a CASID for the output file. + /// + /// FIXME: Add clang tests for this functionality. + unsigned WriteOutputAsCASID : 1; + /// Output (and read) PCM files regardless of compiler errors. LLVM_PREFERRED_TYPE(bool) unsigned AllowPCMWithCompilerErrors : 1; @@ -490,6 +539,9 @@ class FrontendOptions { std::string MTMigrateDir; std::string ARCMTMigrateReportOut; + std::string IndexStorePath; + std::string IndexUnitOutputPath; + /// The input kind, either specified via -x argument or deduced from the input /// file name. InputKind DashX; @@ -497,6 +549,12 @@ class FrontendOptions { /// The input files and their types. SmallVector<FrontendInputFile, 0> Inputs; + /// Use the provided CAS include tree. + std::string CASIncludeTreeID; + + /// If ignore all the CAS info from serialized AST like modules and PCHs. + bool ModuleLoadIgnoreCAS = false; + /// When the input is a module map, the original module map file from which /// that map was inferred, if any (for umbrella modules). std::string OriginalModuleMap; @@ -523,6 +581,14 @@ class FrontendOptions { /// The name of the product the input files belong too. std::string ProductName; + /// Socket path for remote caching service. + std::string CompilationCachingServicePath; + + /// When caching is enabled, represents remappings for all the file paths that + /// the compilation may access. This is useful for canonicalizing the + /// compilation for caching purposes. + std::vector<std::string> PathPrefixMappings; + // Currently this is only used as part of the `-extract-api` action. // A comma separated list of files providing a list of APIs to // ignore when extracting documentation. @@ -557,6 +623,10 @@ class FrontendOptions { /// The list of AST files to merge. std::vector<std::string> ASTMergeFiles; + /// The list of prebuilt module file paths to make available by reading their + /// contents from the \c ActionCache with the given compile job cache key. + std::vector<std::pair<std::string, std::string>> ModuleCacheKeys; + /// A list of arguments to forward to LLVM's option processing; this /// should only be used for debugging and experimental features. std::vector<std::string> LLVMArgs; @@ -601,7 +671,10 @@ class FrontendOptions { GenerateGlobalModuleIndex(true), ASTDumpDecls(false), ASTDumpLookups(false), BuildingImplicitModule(false), BuildingImplicitModuleUsesLock(true), ModulesEmbedAllFiles(false), - IncludeTimestamps(true), UseTemporary(true), + IncludeTimestamps(true), UseTemporary(true), CacheCompileJob(false), + ForIncludeTreeScan(false), DisableCachedCompileJobReplay(false), + IncludeTreePreservePCHPath(false), + MayEmitDiagnosticsAfterProcessingSourceFiles(false), WriteOutputAsCASID(false), AllowPCMWithCompilerErrors(false), ModulesShareFileManager(true), EmitSymbolGraph(false), EmitExtensionSymbolGraphs(false), EmitSymbolGraphSymbolLabelsForTesting(false), diff --git a/clang/include/clang/Frontend/IncludeTreePPActions.h b/clang/include/clang/Frontend/IncludeTreePPActions.h new file mode 100644 index 0000000000000..a2a0460a25613 --- /dev/null +++ b/clang/include/clang/Frontend/IncludeTreePPActions.h @@ -0,0 +1,32 @@ +//===- IncludeTreePPActions.h - PP actions using include-tree ---*- 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 +// +//===----------------------------------------------------------------------===// +// +// Uses the info from an include-tree to drive the preprocessor via +// \p PPCachedActions. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_FRONTEND_INCLUDETREEPPACTIONS_H +#define LLVM_CLANG_FRONTEND_INCLUDETREEPPACTIONS_H + +#include "clang/Basic/LLVM.h" + +namespace clang { + +class PPCachedActions; + +namespace cas { +class IncludeTreeRoot; +} + +Expected<std::unique_ptr<PPCachedActions>> +createPPActionsFromIncludeTree(cas::IncludeTreeRoot &Root); + +} // namespace clang + +#endif diff --git a/clang/include/clang/Frontend/SerializedDiagnosticPrinter.h b/clang/include/clang/Frontend/SerializedDiagnosticPrinter.h index 5586ef65e393f..2c6253f80e03a 100644 --- a/clang/include/clang/Frontend/SerializedDiagnosticPrinter.h +++ b/clang/include/clang/Frontend/SerializedDiagnosticPrinter.h @@ -31,9 +31,16 @@ namespace serialized_diags { /// This allows wrapper tools for Clang to get diagnostics from Clang /// (via libclang) without needing to parse Clang's command line output. /// -std::unique_ptr<DiagnosticConsumer> create(StringRef OutputFile, - DiagnosticOptions *Diags, - bool MergeChildRecords = false); +/// \param OS optional stream to output the serialized diagnostics buffer, +/// instead of writing out directly to a file. +/// FIXME: \p OS is temporary transition until we have structured diagnostics +/// caching in which case we won't need to manage serialized diagnostics files +/// explicitly for caching purposes and the changes to add \p OS in this +/// function should be reverted. +std::unique_ptr<DiagnosticConsumer> +create(StringRef OutputFile, DiagnosticOptions *Diags, + bool MergeChildRecords = false, + std::unique_ptr<raw_ostream> OS = nullptr); } // end serialized_diags namespace } // end clang namespace diff --git a/clang/include/clang/Frontend/SerializedDiagnosticReader.h b/clang/include/clang/Frontend/SerializedDiagnosticReader.h index f7c2012a7662a..c42ae8759d440 100644 --- a/clang/include/clang/Frontend/SerializedDiagnosticReader.h +++ b/clang/include/clang/Frontend/SerializedDiagnosticReader.h @@ -65,6 +65,9 @@ class SerializedDiagnosticReader { /// Read the diagnostics in \c File std::error_code readDiagnostics(StringRef File); + /// Read the diagnostics in \c Buffer. + std::error_code readDiagnostics(llvm::MemoryBufferRef Buffer); + private: enum class Cursor; @@ -109,6 +112,16 @@ class SerializedDiagnosticReader { return {}; } + /// Visit file contents. This associates the file's \c ID with the + /// contents of + virtual std::error_code visitSourceFileContentsRecord( + unsigned ID, + const Location &OriginalStartLoc, + const Location &OriginalEndLoc, + StringRef Contents) { + return {}; + } + /// Visit a fixit hint. virtual std::error_code visitFixitRecord(const Location &Start, const Location &End, StringRef Text) { diff --git a/clang/include/clang/Frontend/SerializedDiagnostics.h b/clang/include/clang/Frontend/SerializedDiagnostics.h index 6464693c14820..3dda959d44f44 100644 --- a/clang/include/clang/Frontend/SerializedDiagnostics.h +++ b/clang/include/clang/Frontend/SerializedDiagnostics.h @@ -32,8 +32,9 @@ enum RecordIDs { RECORD_CATEGORY, RECORD_FILENAME, RECORD_FIXIT, + RECORD_SOURCE_FILE_CONTENTS, RECORD_FIRST = RECORD_VERSION, - RECORD_LAST = RECORD_FIXIT + RECORD_LAST = RECORD_SOURCE_FILE_CONTENTS }; /// A stable version of DiagnosticIDs::Level. diff --git a/clang/include/clang/Frontend/Utils.h b/clang/include/clang/Frontend/Utils.h index 604e42067a3f1..4e76006905f5a 100644 --- a/clang/include/clang/Frontend/Utils.h +++ b/clang/include/clang/Frontend/Utils.h @@ -24,6 +24,7 @@ #include "llvm/ADT/StringSet.h" #include "llvm/Support/FileCollector.h" #include "llvm/Support/VirtualFileSystem.h" +#include "llvm/Support/VirtualOutputBackend.h" #include <cstdint> #include <memory> #include <string> @@ -34,6 +35,7 @@ namespace clang { class ASTReader; +class CASOptions; class CompilerInstance; class CompilerInvocation; class DiagnosticsEngine; @@ -103,7 +105,12 @@ class DependencyCollector { /// loaded. class DependencyFileGenerator : public DependencyCollector { public: - DependencyFileGenerator(const DependencyOutputOptions &Opts); + /// Constructs a \c DependencyFileGenerator with the given options and output + /// backend. If \p OutputBackend is null, a default on-disk backend will be + /// used. + DependencyFileGenerator( + const DependencyOutputOptions &Opts, + IntrusiveRefCntPtr<llvm::vfs::OutputBackend> OutputBackend = nullptr); void attachToPreprocessor(Preprocessor &PP) override; @@ -120,6 +127,7 @@ class DependencyFileGenerator : public DependencyCollector { private: void outputDependencyFile(DiagnosticsEngine &Diags); + IntrusiveRefCntPtr<llvm::vfs::OutputBackend> OutputBackend; std::string OutputFile; std::vector<std::string> Targets; bool IncludeSystemHeaders; @@ -127,6 +135,7 @@ class DependencyFileGenerator : public DependencyCollector { bool AddMissingHeaderDeps; bool SeenMissingHeader; bool IncludeModuleFiles; + bool SkipUnusedModuleMaps; DependencyOutputFormat OutputFormat; unsigned InputFileIndex; }; diff --git a/clang/include/clang/Index/IndexDataStore.h b/clang/include/clang/Index/IndexDataStore.h new file mode 100644 index 0000000000000..694943e847629 --- /dev/null +++ b/clang/include/clang/Index/IndexDataStore.h @@ -0,0 +1,76 @@ +//===--- IndexDataStore.h - Index data store info -------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_INDEX_INDEXDATASTORE_H +#define LLVM_CLANG_INDEX_INDEXDATASTORE_H + +#include "clang/Basic/LLVM.h" +#include "clang/Basic/PathRemapper.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Chrono.h" +#include <functional> +#include <memory> +#include <string> + +namespace clang { +namespace index { + +class IndexDataStore { +public: + ~IndexDataStore(); + + static std::unique_ptr<IndexDataStore> create(StringRef IndexStorePath, + const PathRemapper &Remapper, + std::string &Error); + + StringRef getFilePath() const; + const PathRemapper &getPathRemapper() const; + bool foreachUnitName(bool sorted, + llvm::function_ref<bool(StringRef unitName)> receiver); + + static unsigned getFormatVersion(); + + enum class UnitEventKind { + Removed, + Modified, + /// The directory got deleted. No more events will follow. + DirectoryDeleted, + Failure + }; + struct UnitEvent { + UnitEventKind Kind; + StringRef UnitName; + }; + struct UnitEventNotification { + bool IsInitial; + ArrayRef<UnitEvent> Events; + }; + typedef std::function<void(UnitEventNotification)> UnitEventHandler; + + void setUnitEventHandler(UnitEventHandler Handler); + /// \returns true if an error occurred. + bool startEventListening(bool waitInitialSync, std::string &Error); + void stopEventListening(); + + void discardUnit(StringRef UnitName); + void discardRecord(StringRef RecordName); + + void purgeStaleData(); + +private: + IndexDataStore(void *Impl) : Impl(Impl) {} + + void *Impl; // An IndexDataStoreImpl. +}; + +} // namespace index +} // namespace clang + +#endif diff --git a/clang/include/clang/Index/IndexDataStoreSymbolUtils.h b/clang/include/clang/Index/IndexDataStoreSymbolUtils.h new file mode 100644 index 0000000000000..dd9a35f8865fd --- /dev/null +++ b/clang/include/clang/Index/IndexDataStoreSymbolUtils.h @@ -0,0 +1,52 @@ +//===--- IndexDataStoreSymbolUtils.h - Utilities for indexstore symbols ---===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_INDEX_INDEXDATASTORESYMBOLUTILS_H +#define LLVM_CLANG_INDEX_INDEXDATASTORESYMBOLUTILS_H + +#include "indexstore/indexstore.h" +#include "clang/Index/IndexSymbol.h" + +namespace clang { +namespace index { + +/// Map an indexstore_symbol_kind_t to a SymbolKind, handling unknown values. +SymbolKind getSymbolKind(indexstore_symbol_kind_t K); + +SymbolSubKind getSymbolSubKind(indexstore_symbol_subkind_t K); + +/// Map an indexstore_symbol_language_t to a SymbolLanguage, handling unknown +/// values. +SymbolLanguage getSymbolLanguage(indexstore_symbol_language_t L); + +/// Map an indexstore representation to a SymbolPropertySet, handling +/// unknown values. +SymbolPropertySet getSymbolProperties(uint64_t Props); + +/// Map an indexstore representation to a SymbolRoleSet, handling unknown +/// values. +SymbolRoleSet getSymbolRoles(uint64_t Roles); + +/// Map a SymbolLanguage to a indexstore_symbol_language_t. +indexstore_symbol_kind_t getIndexStoreKind(SymbolKind K); + +indexstore_symbol_subkind_t getIndexStoreSubKind(SymbolSubKind K); + +/// Map a SymbolLanguage to a indexstore_symbol_language_t. +indexstore_symbol_language_t getIndexStoreLang(SymbolLanguage L); + +/// Map a SymbolPropertySet to its indexstore representation. +indexstore_symbol_property_t getIndexStoreProperties(SymbolPropertySet Props); + +/// Map a SymbolRoleSet to its indexstore representation. +indexstore_symbol_role_t getIndexStoreRoles(SymbolRoleSet Roles); + +} // end namespace index +} // end namespace clang + +#endif // LLVM_CLANG_INDEX_INDEXDATASTORESYMBOLUTILS_H diff --git a/clang/include/clang/Index/IndexRecordReader.h b/clang/include/clang/Index/IndexRecordReader.h new file mode 100644 index 0000000000000..a572b9e84be10 --- /dev/null +++ b/clang/include/clang/Index/IndexRecordReader.h @@ -0,0 +1,108 @@ +//===--- IndexRecordReader.h - Index record deserialization ---------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_INDEX_INDEXRECORDREADER_H +#define LLVM_CLANG_INDEX_INDEXRECORDREADER_H + +#include "clang/Index/IndexSymbol.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include <memory> + +namespace llvm { + class MemoryBuffer; +} + +namespace clang { +namespace index { + +struct IndexRecordDecl { + unsigned DeclID; + SymbolInfo SymInfo; + SymbolRoleSet Roles; + SymbolRoleSet RelatedRoles; + StringRef Name; + StringRef USR; + StringRef CodeGenName; +}; + +struct IndexRecordRelation { + SymbolRoleSet Roles; + const IndexRecordDecl *Dcl = nullptr; + + IndexRecordRelation() = default; + IndexRecordRelation(SymbolRoleSet Roles, const IndexRecordDecl *Dcl) + : Roles(Roles), Dcl(Dcl) {} +}; + +struct IndexRecordOccurrence { + const IndexRecordDecl *Dcl; + SmallVector<IndexRecordRelation, 4> Relations; + SymbolRoleSet Roles; + unsigned Line; + unsigned Column; +}; + +class IndexRecordReader { + IndexRecordReader(); + +public: + static std::unique_ptr<IndexRecordReader> + createWithRecordFilename(StringRef RecordFilename, StringRef StorePath, + std::string &Error); + static std::unique_ptr<IndexRecordReader> + createWithFilePath(StringRef FilePath, std::string &Error); + static std::unique_ptr<IndexRecordReader> + createWithBuffer(std::unique_ptr<llvm::MemoryBuffer> Buffer, + std::string &Error); + + ~IndexRecordReader(); + + struct DeclSearchReturn { + bool AcceptDecl; + bool ContinueSearch; + }; + typedef DeclSearchReturn(DeclSearchCheck)(const IndexRecordDecl &); + + /// Goes through and passes record decls, after filtering using a \c Checker + /// function. + /// + /// Resulting decls can be used as filter for \c foreachOccurrence. This + /// allows allocating memory only for the record decls that the caller is + /// interested in. + bool searchDecls(llvm::function_ref<DeclSearchCheck> Checker, + llvm::function_ref<void(const IndexRecordDecl *)> Receiver); + + /// \param NoCache if true, avoids allocating memory for the decls. + /// Useful when the caller does not intend to keep \c IndexRecordReader + /// for more queries. + bool foreachDecl(bool NoCache, + llvm::function_ref<bool(const IndexRecordDecl *)> Receiver); + + /// \param DeclsFilter if non-empty indicates the list of decls that we want + /// to get occurrences for. An empty array indicates that we want occurrences + /// for all decls. + /// \param RelatedDeclsFilter Same as \c DeclsFilter but for related decls. + bool foreachOccurrence(ArrayRef<const IndexRecordDecl *> DeclsFilter, + ArrayRef<const IndexRecordDecl *> RelatedDeclsFilter, + llvm::function_ref<bool(const IndexRecordOccurrence &)> Receiver); + bool foreachOccurrence( + llvm::function_ref<bool(const IndexRecordOccurrence &)> Receiver); + + bool foreachOccurrenceInLineRange(unsigned lineStart, unsigned lineCount, + llvm::function_ref<bool(const IndexRecordOccurrence &)> Receiver); + + struct Implementation; +private: + Implementation &Impl; +}; + +} // namespace index +} // namespace clang + +#endif diff --git a/clang/include/clang/Index/IndexRecordWriter.h b/clang/include/clang/Index/IndexRecordWriter.h new file mode 100644 index 0000000000000..e8020aa6ad620 --- /dev/null +++ b/clang/include/clang/Index/IndexRecordWriter.h @@ -0,0 +1,101 @@ +//===--- IndexRecordWriter.h - Index record serialization -----------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_INDEX_INDEXRECORDWRITER_H +#define LLVM_CLANG_INDEX_INDEXRECORDWRITER_H + +#include "clang/Index/IndexSymbol.h" +#include "llvm/ADT/SmallString.h" + +namespace clang { +namespace index { + +namespace writer { +/// An opaque pointer to a declaration or other symbol used by the +/// IndexRecordWriter to identify when two occurrences refer to the same symbol, +/// and as a token for getting information about a symbol from the caller. +typedef const void *OpaqueDecl; + +/// An indexer symbol suitable for serialization. +/// +/// This includes all the information about the symbol that will be serialized +/// except for roles, which are synthesized by looking at all the occurrences. +/// +/// \seealso IndexRecordDecl +/// \note this struct is generally accompanied by a buffer that owns the string +/// storage. It should not be stored permanently. +struct Symbol { + SymbolInfo SymInfo; + StringRef Name; + StringRef USR; + StringRef CodeGenName; +}; + +/// An relation to an opaque symbol. +/// \seealso IndexRecordRelation +struct SymbolRelation { + OpaqueDecl RelatedSymbol; + SymbolRoleSet Roles; +}; + +typedef llvm::function_ref<Symbol(OpaqueDecl, SmallVectorImpl<char> &Scratch)> + SymbolWriterCallback; +} // end namespace writer + +/// A language-independent utility for serializing index record files. +/// +/// Internally, this class is a small state machine. Users should first call +/// beginRecord, and if the file does not already exist, then proceed to add +/// all symbol occurrences (addOccurrence) and finally finish with endRecord. +class IndexRecordWriter { + SmallString<64> RecordsPath; ///< The records directory path. + void *Record = nullptr; ///< The state of the current record. +public: + IndexRecordWriter(StringRef IndexPath); + + enum class Result { + Success, + Failure, + AlreadyExists, + }; + + /// Begin writing a record for the file \p Filename with contents uniquely + /// identified by \p RecordHash. + /// + /// \param Filename the name of the file this is a record for. + /// \param RecordHash the unique hash of the record contents. + /// \param Error on failure, set to the error message. + /// \param RecordFile if non-null, this is set to the name of the record file. + /// + /// \returns Success if we should continue writing this record, AlreadyExists + /// if the record file has already been written, or Failure if there was an + /// error, in which case \p Error will be set. + Result beginRecord(StringRef Filename, uint64_t RecordHash, + std::string &Error, std::string *RecordFile = nullptr); + + /// Finish writing the record file. + /// + /// \param Error on failure, set to the error message. + /// \param GetSymbolForDecl a callback mapping an writer::OpaqueDecl to its + /// writer::Symbol. This is how the language-specific symbol information is + /// provided to the IndexRecordWriter. The scratch parameter can be used for + /// any necessary storage. + /// + /// \return Success, or Failure and sets \p Error. + Result endRecord(std::string &Error, + writer::SymbolWriterCallback GetSymbolForDecl); + + /// Add an occurrence of the symbol \p D with the given \p Roles and location. + void addOccurrence(writer::OpaqueDecl D, SymbolRoleSet Roles, unsigned Line, + unsigned Column, ArrayRef<writer::SymbolRelation> Related); +}; + +} // end namespace index +} // end namespace clang + +#endif // LLVM_CLANG_INDEX_INDEXRECORDWRITER_H diff --git a/clang/include/clang/Index/IndexSymbol.h b/clang/include/clang/Index/IndexSymbol.h index 59e90fced3dda..b4b784510ebf0 100644 --- a/clang/include/clang/Index/IndexSymbol.h +++ b/clang/include/clang/Index/IndexSymbol.h @@ -54,6 +54,8 @@ enum class SymbolKind : uint8_t { Parameter, Using, + + CommentTag, TemplateTypeParm, TemplateTemplateParm, NonTypeTemplateParm, @@ -78,9 +80,32 @@ enum class SymbolSubKind : uint8_t { UsingTypename, UsingValue, UsingEnum, + + // Swift sub-kinds + + SwiftAccessorWillSet, + SwiftAccessorDidSet, + SwiftAccessorAddressor, + SwiftAccessorMutableAddressor, + SwiftAccessorRead, + SwiftAccessorModify, + + SwiftExtensionOfStruct, + SwiftExtensionOfClass, + SwiftExtensionOfEnum, + SwiftExtensionOfProtocol, + + SwiftPrefixOperator, + SwiftPostfixOperator, + SwiftInfixOperator, + + SwiftSubscript, + SwiftAssociatedType, + SwiftGenericTypeParam, + SwiftAccessorInit, }; -typedef uint16_t SymbolPropertySet; +typedef uint32_t SymbolPropertySet; /// Set of properties that provide additional info about a symbol. enum class SymbolProperty : SymbolPropertySet { Generic = 1 << 0, @@ -93,8 +118,10 @@ enum class SymbolProperty : SymbolPropertySet { Local = 1 << 7, /// Symbol is part of a protocol interface. ProtocolInterface = 1 << 8, + + /// Swift-only properties + SwiftAsync = 1 << 16, }; -static const unsigned SymbolPropertyBitNum = 9; /// Set of roles that are attributed to symbol occurrences. /// diff --git a/clang/include/clang/Index/IndexUnitReader.h b/clang/include/clang/Index/IndexUnitReader.h new file mode 100644 index 0000000000000..9b209ae576d19 --- /dev/null +++ b/clang/include/clang/Index/IndexUnitReader.h @@ -0,0 +1,85 @@ +//===--- IndexUnitReader.h - Index unit deserialization -------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_INDEX_INDEXUNITREADER_H +#define LLVM_CLANG_INDEX_INDEXUNITREADER_H + +#include "clang/Basic/LLVM.h" +#include "clang/Basic/PathRemapper.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/Support/Chrono.h" +#include <optional> + +namespace clang { +namespace index { + +class IndexUnitReader { +public: + enum class DependencyKind { + Unit, + Record, + File, + }; + + ~IndexUnitReader(); + + static std::unique_ptr<IndexUnitReader> + createWithUnitFilename(StringRef UnitFilename, StringRef StorePath, + const PathRemapper &Remapper, std::string &Error); + static std::unique_ptr<IndexUnitReader> + createWithFilePath(StringRef FilePath, const PathRemapper &Remapper, + std::string &Error); + + static std::optional<llvm::sys::TimePoint<>> + getModificationTimeForUnit(StringRef UnitFilename, StringRef StorePath, + std::string &Error); + + StringRef getProviderIdentifier() const; + StringRef getProviderVersion() const; + + llvm::sys::TimePoint<> getModificationTime() const; + StringRef getWorkingDirectory() const; + StringRef getOutputFile() const; + StringRef getSysrootPath() const; + StringRef getMainFilePath() const; + StringRef getModuleName() const; + StringRef getTarget() const; + bool hasMainFile() const; + bool isSystemUnit() const; + bool isModuleUnit() const; + bool isDebugCompilation() const; + + struct DependencyInfo { + DependencyKind Kind; + bool IsSystem; + StringRef UnitOrRecordName; + StringRef FilePath; + StringRef ModuleName; + }; + struct IncludeInfo { + StringRef SourcePath; + unsigned SourceLine; + StringRef TargetPath; + }; + /// Unit dependencies are provided ahead of record ones, record ones + /// ahead of the file ones. + bool foreachDependency(llvm::function_ref<bool(const DependencyInfo &Info)> Receiver); + + bool foreachInclude(llvm::function_ref<bool(const IncludeInfo &Info)> Receiver); + +private: + IndexUnitReader(void *Impl) : Impl(Impl) {} + + void *Impl; // An IndexUnitReaderImpl. +}; + +} // namespace index +} // namespace clang + +#endif diff --git a/clang/include/clang/Index/IndexUnitWriter.h b/clang/include/clang/Index/IndexUnitWriter.h new file mode 100644 index 0000000000000..1edc51ac2dd9d --- /dev/null +++ b/clang/include/clang/Index/IndexUnitWriter.h @@ -0,0 +1,148 @@ +//===--- IndexUnitWriter.h - Index unit serialization ---------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_INDEX_INDEXUNITWRITER_H +#define LLVM_CLANG_INDEX_INDEXUNITWRITER_H + +#include "clang/Basic/FileEntry.h" +#include "clang/Basic/LLVM.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/SmallString.h" +#include <string> +#include <vector> + +namespace llvm { + class BitstreamWriter; +} + +namespace clang { + class FileEntry; + class FileManager; + class PathRemapper; + +namespace index { + +namespace writer { +/// An opaque pointer to a module used by the IndexUnitWriter to associate +/// record and file dependencies with a module, and as a token for getting +/// information about the module from the caller. +typedef const void *OpaqueModule; + +/// Module info suitable for serialization. +/// +/// This is used for top-level modules and sub-modules. +struct ModuleInfo { + /// Full, dot-separate, module name. + StringRef Name; +}; + +typedef llvm::function_ref<ModuleInfo(OpaqueModule, SmallVectorImpl<char> &Scratch)> + ModuleInfoWriterCallback; +} // end namespace writer + +class IndexUnitWriter { + FileManager &FileMgr; + SmallString<64> UnitsPath; + std::string ProviderIdentifier; + std::string ProviderVersion; + std::string OutputFile; + std::string ModuleName; + OptionalFileEntryRef MainFile; + bool IsSystemUnit; + bool IsModuleUnit; + bool IsDebugCompilation; + std::string TargetTriple; + std::string WorkDir; + std::string SysrootPath; + const PathRemapper &Remapper; + std::function<writer::ModuleInfo(writer::OpaqueModule, + SmallVectorImpl<char> &Scratch)> GetInfoForModuleFn; + struct FileInclude { + int Index; + unsigned Line; + }; + struct FileEntryData { + FileEntryRef File; + bool IsSystem; + int ModuleIndex; + std::vector<FileInclude> Includes; + }; + std::vector<FileEntryData> Files; + std::vector<writer::OpaqueModule> Modules; + llvm::DenseMap<const FileEntry *, int> IndexByFile; + llvm::DenseMap<writer::OpaqueModule, int> IndexByModule; + llvm::DenseSet<const FileEntry *> SeenASTFiles; + struct RecordOrUnitData { + std::string Name; + int FileIndex; + int ModuleIndex; + bool IsSystem; + }; + std::vector<RecordOrUnitData> Records; + std::vector<RecordOrUnitData> ASTFileUnits; + +public: + /// \param MainFile the main file for a compiled source file. This should be + /// null for PCH and module units. + /// \param IsSystem true for system module units, false otherwise. + /// \param Remapper Remapper to use to standardize file paths to make them + /// hermetic/reproducible. This applies to all paths emitted in the unit file. + IndexUnitWriter(FileManager &FileMgr, + StringRef StorePath, + StringRef ProviderIdentifier, StringRef ProviderVersion, + StringRef OutputFile, + StringRef ModuleName, + OptionalFileEntryRef MainFile, + bool IsSystem, + bool IsModuleUnit, + bool IsDebugCompilation, + StringRef TargetTriple, + StringRef SysrootPath, + const PathRemapper &Remapper, + writer::ModuleInfoWriterCallback GetInfoForModule); + ~IndexUnitWriter(); + + int addFileDependency(OptionalFileEntryRef File, bool IsSystem, + writer::OpaqueModule Mod); + void addRecordFile(StringRef RecordFile, OptionalFileEntryRef File, + bool IsSystem, writer::OpaqueModule Mod); + void addASTFileDependency(OptionalFileEntryRef File, bool IsSystem, + writer::OpaqueModule Mod, + bool withoutUnitName = false); + void addUnitDependency(StringRef UnitFile, OptionalFileEntryRef File, + bool IsSystem, writer::OpaqueModule Mod); + bool addInclude(const FileEntry *Source, unsigned Line, const FileEntry *Target); + + bool write(std::string &Error); + + void getUnitNameForOutputFile(StringRef FilePath, SmallVectorImpl<char> &Str); + void getUnitPathForOutputFile(StringRef FilePath, SmallVectorImpl<char> &Str); + /// If the unit file exists and \p timeCompareFilePath is provided, it will + /// return true if \p timeCompareFilePath is older than the unit file. + std::optional<bool> + isUnitUpToDateForOutputFile(StringRef FilePath, + std::optional<StringRef> TimeCompareFilePath, + std::string &Error); + static void getUnitNameForAbsoluteOutputFile(StringRef FilePath, SmallVectorImpl<char> &Str, + const PathRemapper &Remapper); + static bool initIndexDirectory(StringRef StorePath, std::string &Error); + +private: + class PathStorage; + int addModule(writer::OpaqueModule Mod); + void writeUnitInfo(llvm::BitstreamWriter &Stream, PathStorage &PathStore); + void writeDependencies(llvm::BitstreamWriter &Stream, PathStorage &PathStore); + void writeIncludes(llvm::BitstreamWriter &Stream, PathStorage &PathStore); + void writePaths(llvm::BitstreamWriter &Stream, PathStorage &PathStore); + void writeModules(llvm::BitstreamWriter &Stream); +}; + +} // end namespace index +} // end namespace clang + +#endif diff --git a/clang/include/clang/Index/IndexingAction.h b/clang/include/clang/Index/IndexingAction.h index 4baa2d5e72603..37022964301a8 100644 --- a/clang/include/clang/Index/IndexingAction.h +++ b/clang/include/clang/Index/IndexingAction.h @@ -16,14 +16,18 @@ #include "clang/Lex/Preprocessor.h" #include "llvm/ADT/ArrayRef.h" #include <memory> +#include <string> namespace clang { class ASTContext; class ASTConsumer; class ASTReader; class ASTUnit; + class CompilerInstance; class Decl; class FrontendAction; + class FrontendOptions; + class Module; namespace serialization { class ModuleFile; @@ -31,7 +35,20 @@ namespace serialization { namespace index { class IndexDataConsumer; +class IndexUnitWriter; +struct RecordingOptions { + enum class IncludesRecordingKind { + None, + UserOnly, // only record includes inside non-system files. + All, + }; + + std::string DataDirPath; + bool RecordSymbolCodeGenName = false; + bool RecordSystemDependencies = true; + IncludesRecordingKind RecordIncludes = IncludesRecordingKind::UserOnly; +}; /// Creates an ASTConsumer that indexes all symbols (macros and AST decls). std::unique_ptr<ASTConsumer> createIndexingASTConsumer(std::shared_ptr<IndexDataConsumer> DataConsumer, @@ -68,6 +85,18 @@ std::unique_ptr<PPCallbacks> indexMacrosCallback(IndexDataConsumer &Consumer, void indexModuleFile(serialization::ModuleFile &Mod, ASTReader &Reader, IndexDataConsumer &DataConsumer, IndexingOptions Opts); +/// \param WrappedAction another frontend action to wrap over or null. +std::unique_ptr<FrontendAction> +createIndexDataRecordingAction(const FrontendOptions &FEOpts, + std::unique_ptr<FrontendAction> WrappedAction); + +/// Checks if the unit file exists for the module file, if it doesn't it +/// generates index data for it. +/// +/// \returns true if the index data were generated, false otherwise. +bool emitIndexDataForModuleFile(const Module *Mod, const CompilerInstance &CI, + IndexUnitWriter &ParentUnitWriter); + } // namespace index } // namespace clang diff --git a/clang/include/clang/Index/IndexingOptions.h b/clang/include/clang/Index/IndexingOptions.h index 97847dd7d5d88..b52f96c2c0129 100644 --- a/clang/include/clang/Index/IndexingOptions.h +++ b/clang/include/clang/Index/IndexingOptions.h @@ -33,6 +33,7 @@ struct IndexingOptions { // callback is not available (e.g. after parsing has finished). Note that // macro references are not available in Preprocessor. bool IndexMacrosInPreprocessor = false; + bool IndexPcms = true; // Has no effect if IndexFunctionLocals are false. bool IndexParametersInDeclarations = false; bool IndexTemplateParameters = false; diff --git a/clang/include/clang/Lex/DirectoryLookup.h b/clang/include/clang/Lex/DirectoryLookup.h index 81680d3b271e0..bb703dfad2b28 100644 --- a/clang/include/clang/Lex/DirectoryLookup.h +++ b/clang/include/clang/Lex/DirectoryLookup.h @@ -58,10 +58,6 @@ class DirectoryLookup { LLVM_PREFERRED_TYPE(LookupType_t) unsigned LookupType : 2; - /// Whether this is a header map used when building a framework. - LLVM_PREFERRED_TYPE(bool) - unsigned IsIndexHeaderMap : 1; - /// Whether we've performed an exhaustive search for module maps /// within the subdirectories of this directory. LLVM_PREFERRED_TYPE(bool) @@ -73,13 +69,12 @@ class DirectoryLookup { bool isFramework) : u(Dir), DirCharacteristic(DT), LookupType(isFramework ? LT_Framework : LT_NormalDir), - IsIndexHeaderMap(false), SearchedAllModuleMaps(false) {} + SearchedAllModuleMaps(false) {} /// This ctor *does not take ownership* of 'Map'. - DirectoryLookup(const HeaderMap *Map, SrcMgr::CharacteristicKind DT, - bool isIndexHeaderMap) + DirectoryLookup(const HeaderMap *Map, SrcMgr::CharacteristicKind DT) : u(Map), DirCharacteristic(DT), LookupType(LT_HeaderMap), - IsIndexHeaderMap(isIndexHeaderMap), SearchedAllModuleMaps(false) {} + SearchedAllModuleMaps(false) {} /// getLookupType - Return the kind of directory lookup that this is: either a /// normal directory, a framework path, or a HeaderMap. @@ -146,11 +141,6 @@ class DirectoryLookup { return getDirCharacteristic() != SrcMgr::C_User; } - /// Whether this header map is building a framework or not. - bool isIndexHeaderMap() const { - return isHeaderMap() && IsIndexHeaderMap; - } - /// LookupFile - Lookup the specified file in this search path, returning it /// if it exists or returning null if not. /// diff --git a/clang/include/clang/Lex/HeaderSearch.h b/clang/include/clang/Lex/HeaderSearch.h index df75c192c700a..a10adae17998b 100644 --- a/clang/include/clang/Lex/HeaderSearch.h +++ b/clang/include/clang/Lex/HeaderSearch.h @@ -108,16 +108,6 @@ struct HeaderFileInfo { LLVM_PREFERRED_TYPE(bool) unsigned Resolved : 1; - /// Whether this is a header inside a framework that is currently - /// being built. - /// - /// When a framework is being built, the headers have not yet been placed - /// into the appropriate framework subdirectories, and therefore are - /// provided via a header map. This bit indicates when this is one of - /// those framework headers. - LLVM_PREFERRED_TYPE(bool) - unsigned IndexHeaderMapHeader : 1; - /// Whether this file has been looked up as a header. LLVM_PREFERRED_TYPE(bool) unsigned IsValid : 1; @@ -132,15 +122,11 @@ struct HeaderFileInfo { /// external storage. LazyIdentifierInfoPtr LazyControllingMacro; - /// If this header came from a framework include, this is the name - /// of the framework. - StringRef Framework; - HeaderFileInfo() : IsLocallyIncluded(false), isImport(false), isPragmaOnce(false), DirInfo(SrcMgr::C_User), External(false), isModuleHeader(false), isTextualModuleHeader(false), isCompilingModuleHeader(false), - Resolved(false), IndexHeaderMapHeader(false), IsValid(false) {} + Resolved(false), IsValid(false) {} /// Retrieve the controlling macro for this header file, if /// any. @@ -154,6 +140,8 @@ struct HeaderFileInfo { void mergeModuleMembership(ModuleMap::ModuleHeaderRole Role); }; +static_assert(sizeof(HeaderFileInfo) <= 16); + /// An external source of header file information, which may supply /// information about header files already included. class ExternalHeaderFileInfoSource { diff --git a/clang/include/clang/Lex/HeaderSearchOptions.h b/clang/include/clang/Lex/HeaderSearchOptions.h index 17635146a1614..db137420f7ebf 100644 --- a/clang/include/clang/Lex/HeaderSearchOptions.h +++ b/clang/include/clang/Lex/HeaderSearchOptions.h @@ -35,9 +35,6 @@ enum IncludeDirGroup { /// Paths for '\#include <>' added by '-I'. Angled, - /// Like Angled, but marks header maps used when building frameworks. - IndexHeaderMap, - /// Like Angled, but marks system directories. System, @@ -258,6 +255,10 @@ class HeaderSearchOptions { LLVM_PREFERRED_TYPE(bool) unsigned ModulesHashContent : 1; + /// Whether AST files should only contain the preprocessor information. + LLVM_PREFERRED_TYPE(bool) + unsigned ModulesSerializeOnlyPreprocessor : 1; + /// Whether we should include all things that could impact the module in the /// hash. /// @@ -285,6 +286,7 @@ class HeaderSearchOptions { ModulesSkipHeaderSearchPaths(false), ModulesSkipPragmaDiagnosticMappings(false), ModulesPruneNonAffectingModuleMaps(true), ModulesHashContent(false), + ModulesSerializeOnlyPreprocessor(false), ModulesStrictContextHash(false), ModulesIncludeVFSUsage(false) {} /// AddPath - Add the \p Path path to the specified \p Group list. diff --git a/clang/include/clang/Lex/Lexer.h b/clang/include/clang/Lex/Lexer.h index b6ecc7e5ded9e..912c690e127e0 100644 --- a/clang/include/clang/Lex/Lexer.h +++ b/clang/include/clang/Lex/Lexer.h @@ -224,6 +224,10 @@ class Lexer : public PreprocessorLexer { /// isPragmaLexer - Returns true if this Lexer is being used to lex a pragma. bool isPragmaLexer() const { return Is_PragmaLexer; } + /// Note that this Lexer is being used to lex a pragma, or something like it + /// that has simple end-of-file behavior. + void setIsPragmaLexer(bool value) { Is_PragmaLexer = value; } + private: /// IndirectLex - An indirect call to 'Lex' that can be invoked via /// the PreprocessorLexer interface. @@ -567,6 +571,14 @@ class Lexer : public PreprocessorLexer { const LangOptions &LangOpts, bool SkipTrailingWhitespaceAndNewLine); + /// \brief Returns the source location of the token that comes after the + /// token located at the given location \p Loc (excluding any comments and + /// whitespace). The returned source location will be invalid if the location + /// is inside a macro. + static SourceLocation + findNextTokenLocationAfterTokenAt(SourceLocation Loc, const SourceManager &SM, + const LangOptions &LangOpts); + /// Returns true if the given character could appear in an identifier. static bool isAsciiIdentifierContinueChar(char c, const LangOptions &LangOpts); diff --git a/clang/include/clang/Lex/LiteralSupport.h b/clang/include/clang/Lex/LiteralSupport.h index 705021fcfa5b1..ea5f63bc20399 100644 --- a/clang/include/clang/Lex/LiteralSupport.h +++ b/clang/include/clang/Lex/LiteralSupport.h @@ -82,8 +82,8 @@ class NumericLiteralParser { bool isAccum : 1; // 1.0hk/k/lk/uhk/uk/ulk bool isBitInt : 1; // 1wb, 1uwb (C23) or 1__wb, 1__uwb (Clang extension in C++ // mode) - uint8_t MicrosoftInteger; // Microsoft suffix extension i8, i16, i32, or i64. - + uint8_t MicrosoftInteger; // Microsoft suffix extension i8, i16, i32, i64, or + // i128. bool isFixedPointLiteral() const { return (saw_period || saw_exponent) && saw_fixed_point_suffix; diff --git a/clang/include/clang/Lex/ModuleMap.h b/clang/include/clang/Lex/ModuleMap.h index 2e28ff6823cb2..e0deef617beba 100644 --- a/clang/include/clang/Lex/ModuleMap.h +++ b/clang/include/clang/Lex/ModuleMap.h @@ -60,6 +60,14 @@ class ModuleMapCallbacks { virtual void moduleMapFileRead(SourceLocation FileStart, FileEntryRef File, bool IsSystem) {} + /// Called when a module map file matches a module lookup + /// + /// \param File The file itself. + /// \param M The module found that matches this module map. + /// \param IsSystem Whether this is a module map from a system include path. + virtual void moduleMapFoundForModule(FileEntryRef File, const Module *M, + bool IsSystem) {} + /// Called when a header is added during module map parsing. /// /// \param Filename The header file itself. @@ -93,9 +101,12 @@ class ModuleMap { /// named LangOpts::CurrentModule, if we've loaded it). Module *SourceModule = nullptr; + /// The allocator for all (sub)modules. + llvm::SpecificBumpPtrAllocator<Module> ModulesAlloc; + /// Submodules of the current module that have not yet been attached to it. - /// (Ownership is transferred if/when we create an enclosing module.) - llvm::SmallVector<std::unique_ptr<Module>, 8> PendingSubmodules; + /// (Relationship is set up if/when we create an enclosing module.) + llvm::SmallVector<Module *, 8> PendingSubmodules; /// The top-level modules that are known. llvm::StringMap<Module *> Modules; @@ -243,6 +254,9 @@ class ModuleMap { LLVM_PREFERRED_TYPE(bool) unsigned IsExhaustive : 1; + /// \brief Whether this is a module who has its swift_names inferred. + unsigned IsSwiftInferImportAsMember : 1; + /// Whether files in this module can only include non-modular headers /// and headers from used modules. LLVM_PREFERRED_TYPE(bool) @@ -502,6 +516,8 @@ class ModuleMap { /// \returns The named module, if known; otherwise, returns null. Module *findModule(StringRef Name) const; + Module *findOrInferSubmodule(Module *Parent, StringRef Name); + /// Retrieve a module with the given name using lexical name lookup, /// starting at the given context. /// @@ -541,6 +557,17 @@ class ModuleMap { std::pair<Module *, bool> findOrCreateModule(StringRef Name, Module *Parent, bool IsFramework, bool IsExplicit); + /// Call \c ModuleMap::findOrCreateModule and throw away the information + /// whether the module was found or created. + Module *findOrCreateModuleFirst(StringRef Name, Module *Parent, + bool IsFramework, bool IsExplicit) { + return findOrCreateModule(Name, Parent, IsFramework, IsExplicit).first; + } + /// Create new submodule, assuming it does not exist. This function can only + /// be called when it is guaranteed that this submodule does not exist yet. + /// The parameters have same semantics as \c ModuleMap::findOrCreateModule. + Module *createModule(StringRef Name, Module *Parent, bool IsFramework, + bool IsExplicit); /// Create a global module fragment for a C++ module unit. /// diff --git a/clang/include/clang/Lex/PPCachedActions.h b/clang/include/clang/Lex/PPCachedActions.h new file mode 100644 index 0000000000000..3e2571cf24c75 --- /dev/null +++ b/clang/include/clang/Lex/PPCachedActions.h @@ -0,0 +1,77 @@ +//===--- PPCachedActions.h - Callbacks for PP cached actions ----*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Defines the PPCachedActions interface. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LEX_PPCACHEDACTIONS_H +#define LLVM_CLANG_LEX_PPCACHEDACTIONS_H + +#include "clang/Basic/SourceLocation.h" +#include "llvm/ADT/SmallVector.h" +#include <variant> + +namespace clang { + +class IdentifierInfo; +class Module; +class Preprocessor; + +/// This interface provides a way to override the actions of the preprocessor as +/// it does its thing. +/// +/// A client can use this to control how include directives are resolved. +class PPCachedActions { + virtual void anchor(); + +public: + /// The file that is included by an \c #include directive. + struct IncludeFile { + FileID FID; + Module *Submodule; + }; + /// The module that is imported by an \c #include directive or \c \@import. + struct IncludeModule { + SmallVector<std::pair<IdentifierInfo *, SourceLocation>, 2> ImportPath; + // Whether this module should only be "marked visible" rather than imported. + bool VisibilityOnly; + }; + /// The module that is loaded and discarded for an \c #include directive, and + /// the file that is included instead. + struct SpuriousImport { + IncludeModule IM; + IncludeFile IF; + }; + + virtual ~PPCachedActions() = default; + + /// \returns the \p FileID that should be used for predefines. + virtual FileID handlePredefines(Preprocessor &PP) = 0; + + /// \returns the evaluation result for a \p __has_include check. + virtual bool evaluateHasInclude(Preprocessor &PP, SourceLocation Loc, + bool IsIncludeNext) = 0; + + /// \returns the file that should be entered or module that should be imported + /// for an \c #include directive. \c {} indicates that the directive + /// should be skipped. + virtual std::variant<std::monostate, IncludeFile, IncludeModule, + SpuriousImport> + handleIncludeDirective(Preprocessor &PP, SourceLocation IncludeLoc, + SourceLocation AfterDirectiveLoc) = 0; + + /// Notifies the \p PPCachedActions implementation that the preprocessor + /// finished lexing an include file. + virtual void exitedFile(Preprocessor &PP, FileID FID) {} +}; + +} // namespace clang + +#endif diff --git a/clang/include/clang/Lex/PPCallbacks.h b/clang/include/clang/Lex/PPCallbacks.h index 46cc564086f1c..ed94952114ee3 100644 --- a/clang/include/clang/Lex/PPCallbacks.h +++ b/clang/include/clang/Lex/PPCallbacks.h @@ -334,6 +334,14 @@ class PPCallbacks { /// is read. virtual void PragmaAssumeNonNullEnd(SourceLocation Loc) {} + /* TO_UPSTREAM(BoundsSafety) ON */ + /// Callback invoked when a \#pragma clang abi_ptr_attr set (*ATTR*) + /// directive is read. + virtual void + PragmaAbiPointerAttributesSet(SourceLocation Loc, + ArrayRef<const IdentifierInfo *> Spec) {} + /* TO_UPSTREAM(BoundsSafety) OFF */ + /// Called by Preprocessor::HandleMacroExpandedIdentifier when a /// macro invocation is found. virtual void MacroExpands(const Token &MacroNameTok, @@ -665,6 +673,15 @@ class PPChainedCallbacks : public PPCallbacks { Second->PragmaAssumeNonNullEnd(Loc); } + /* TO_UPSTREAM(BoundsSafety) ON */ + void PragmaAbiPointerAttributesSet(SourceLocation Loc, + ArrayRef<const IdentifierInfo *> Spec) + override { + First->PragmaAbiPointerAttributesSet(Loc, Spec); + Second->PragmaAbiPointerAttributesSet(Loc, Spec); + } + /* TO_UPSTREAM(BoundsSafety) OFF */ + void MacroExpands(const Token &MacroNameTok, const MacroDefinition &MD, SourceRange Range, const MacroArgs *Args) override { First->MacroExpands(MacroNameTok, MD, Range, Args); diff --git a/clang/include/clang/Lex/PreprocessingRecord.h b/clang/include/clang/Lex/PreprocessingRecord.h index 437d8e4cc174e..71d7b2874b851 100644 --- a/clang/include/clang/Lex/PreprocessingRecord.h +++ b/clang/include/clang/Lex/PreprocessingRecord.h @@ -297,9 +297,6 @@ class Token; FileID FID) { return std::nullopt; } - - /// Read a preallocated skipped range from the external source. - virtual SourceRange ReadSkippedRange(unsigned Index) = 0; }; /// A record of the steps taken while preprocessing a source file, @@ -325,8 +322,6 @@ class Token; /// The set of ranges that were skipped by the preprocessor, std::vector<SourceRange> SkippedRanges; - bool SkippedRangesAllLoaded = true; - /// Global (loaded or local) ID for a preprocessed entity. /// Negative values are used to indicate preprocessed entities /// loaded from the external source while non-negative values are used to @@ -382,16 +377,6 @@ class Token; /// corresponds to the first newly-allocated entity. unsigned allocateLoadedEntities(unsigned NumEntities); - /// Allocate space for a new set of loaded preprocessed skipped - /// ranges. - /// - /// \returns The index into the set of loaded preprocessed ranges, which - /// corresponds to the first newly-allocated range. - unsigned allocateSkippedRanges(unsigned NumRanges); - - /// Ensures that all external skipped ranges have been loaded. - void ensureSkippedRangesLoaded(); - /// Register a new macro definition. void RegisterMacroDefinition(MacroInfo *Macro, MacroDefinitionRecord *Def); @@ -515,7 +500,6 @@ class Token; /// Retrieve all ranges that got skipped while preprocessing. const std::vector<SourceRange> &getSkippedRanges() { - ensureSkippedRangesLoaded(); return SkippedRanges; } diff --git a/clang/include/clang/Lex/Preprocessor.h b/clang/include/clang/Lex/Preprocessor.h index fc7d0053f2323..5ef1bda89462c 100644 --- a/clang/include/clang/Lex/Preprocessor.h +++ b/clang/include/clang/Lex/Preprocessor.h @@ -28,6 +28,7 @@ #include "clang/Lex/MacroInfo.h" #include "clang/Lex/ModuleLoader.h" #include "clang/Lex/ModuleMap.h" +#include "clang/Lex/PPCachedActions.h" #include "clang/Lex/PPCallbacks.h" #include "clang/Lex/PPEmbedParameters.h" #include "clang/Lex/Token.h" @@ -254,6 +255,9 @@ class Preprocessor { /// True if we are pre-expanding macro arguments. bool InMacroArgPreExpansion; + /// True if we encountered any of the non-deterministic macros. + bool IsSourceNonReproducible; + /// Mapping/lookup information for all identifiers in /// the program, including program keywords. mutable IdentifierTable Identifiers; @@ -808,6 +812,10 @@ class Preprocessor { /// encountered (e.g. a file is \#included, etc). std::unique_ptr<PPCallbacks> Callbacks; + /// Actions that can override certain preprocessor activities, like handling + /// of \#include directives. + std::unique_ptr<PPCachedActions> CachedActions; + struct MacroExpandsInfo { Token Tok; MacroDefinition MD; @@ -1262,6 +1270,8 @@ class Preprocessor { void setPragmasEnabled(bool Enabled) { PragmasEnabled = Enabled; } bool getPragmasEnabled() const { return PragmasEnabled; } + bool isSourceNonReproducible() const { return IsSourceNonReproducible; } + void SetSuppressIncludeNotFoundError(bool Suppress) { SuppressIncludeNotFoundError = Suppress; } @@ -1318,6 +1328,11 @@ class Preprocessor { } /// \} + PPCachedActions *getPPCachedActions() const { return CachedActions.get(); } + void setPPCachedActions(std::unique_ptr<PPCachedActions> CA) { + CachedActions = std::move(CA); + } + /// Get the number of tokens processed so far. unsigned getTokenCount() const { return TokenCount; } @@ -1500,7 +1515,7 @@ class Preprocessor { /// Mark the file as included. /// Returns true if this is the first time the file was included. bool markIncluded(FileEntryRef File) { - HeaderInfo.getFileInfo(File); + HeaderInfo.getFileInfo(File).IsLocallyIncluded = true; return IncludedFiles.insert(File).second; } @@ -1751,7 +1766,8 @@ class Preprocessor { bool LexAfterModuleImport(Token &Result); void CollectPpImportSuffix(SmallVectorImpl<Token> &Toks); - void makeModuleVisible(Module *M, SourceLocation Loc); + void makeModuleVisible(Module *M, SourceLocation Loc, + bool IncludeExports = true); SourceLocation getModuleImportLoc(Module *M) const { return CurSubmoduleState->VisibleModules.getImportLoc(M); @@ -2413,6 +2429,28 @@ class Preprocessor { !IsAtImport; } +private: + /// The include tree that is being built, if any. + /// See \c FrontendOptions::CASIncludeTreeID. + std::optional<std::string> CASIncludeTreeID; + + /// The cas-fs tree that is being built, if any. + /// See \c FileSystemOptions::CASFileSystemRootID. + std::optional<std::string> CASFileSystemRootID; + +public: + std::optional<std::string> getCASIncludeTreeID() const { + return CASIncludeTreeID; + } + void setCASIncludeTreeID(std::string ID) { CASIncludeTreeID = std::move(ID); } + + std::optional<std::string> getCASFileSystemRootID() const { + return CASFileSystemRootID; + } + void setCASFileSystemRootID(std::string ID) { + CASFileSystemRootID = std::move(ID); + } + /// Allocate a new MacroInfo object with the provided SourceLocation. MacroInfo *AllocateMacroInfo(SourceLocation L); @@ -2980,6 +3018,11 @@ class Preprocessor { } LoadedSafeBufferOptOutMap; public: + // FIXME: The result of saving Safe Buffers opt-out regions in PP is that + // clang needs to check two places---PP and DiagnosticsEngine, in order to + // confirm if C++ Safe Buffers is enabled at a location. This might not be + // ideal as developers can forget to check one of the places. + // /// \return true iff the given `Loc` is in a "-Wunsafe-buffer-usage" opt-out /// region. This `Loc` must be a source location that has been pre-processed. bool isSafeBufferOptOut(const SourceManager&SourceMgr, const SourceLocation &Loc) const; diff --git a/clang/include/clang/Lex/PreprocessorOptions.h b/clang/include/clang/Lex/PreprocessorOptions.h index 3f7dd9db18ba7..3464f984f205a 100644 --- a/clang/include/clang/Lex/PreprocessorOptions.h +++ b/clang/include/clang/Lex/PreprocessorOptions.h @@ -61,6 +61,18 @@ enum class DisableValidationForModuleKind { LLVM_MARK_AS_BITMASK_ENUM(Module) }; +/// Diagnostic options for caching related behaviors. +enum class CachingDiagKind { + /// Do not emit diagnosis for caching. + None = 0, + + /// Warning about nondeterministic caching. + Warning = 1, + + /// Error about nondeterministic caching. + Error = 2 +}; + /// PreprocessorOptions - This class is used for passing the various options /// used in preprocessor initialization to InitializePreprocessor(). class PreprocessorOptions { @@ -208,13 +220,12 @@ class PreprocessorOptions { /// Prevents intended crashes when using #pragma clang __debug. For testing. bool DisablePragmaDebugCrash = false; + /// Should diagnose caching related issues. + CachingDiagKind CachingDiagOption = CachingDiagKind::None; + /// If set, the UNIX timestamp specified by SOURCE_DATE_EPOCH. std::optional<uint64_t> SourceDateEpoch; - /// If set, the preprocessor reports an error when processing #pragma mc_func - /// on AIX. - bool ErrorOnPragmaMcfuncOnAIX = false; - public: PreprocessorOptions() : PrecompiledPreambleBytes(0, false) {} @@ -252,7 +263,6 @@ class PreprocessorOptions { PrecompiledPreambleBytes.first = 0; PrecompiledPreambleBytes.second = false; RetainExcludedConditionalBlocks = false; - ErrorOnPragmaMcfuncOnAIX = false; } }; diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index 613bab9120dfc..52a3eb82165d6 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -221,7 +221,6 @@ class Parser : public CodeCompletionHandler { std::unique_ptr<PragmaHandler> MaxTokensHerePragmaHandler; std::unique_ptr<PragmaHandler> MaxTokensTotalPragmaHandler; std::unique_ptr<PragmaHandler> RISCVPragmaHandler; - std::unique_ptr<PragmaHandler> MCFuncPragmaHandler; std::unique_ptr<CommentHandler> CommentSemaHandler; @@ -1374,10 +1373,12 @@ class Parser : public CodeCompletionHandler { IdentifierInfo *MacroII = nullptr; SourceLocation AttrNameLoc; SmallVector<Decl*, 2> Decls; + // TO_UPSTREAM(BoundsSafety) + unsigned NestedTypeLevel; explicit LateParsedAttribute(Parser *P, IdentifierInfo &Name, - SourceLocation Loc) - : Self(P), AttrName(Name), AttrNameLoc(Loc) {} + SourceLocation Loc, unsigned Level = 0) + : Self(P), AttrName(Name), AttrNameLoc(Loc), NestedTypeLevel(Level) {} void ParseLexedAttributes() override; @@ -1697,7 +1698,9 @@ class Parser : public CodeCompletionHandler { void SkipFunctionBody(); Decl *ParseFunctionDefinition(ParsingDeclarator &D, const ParsedTemplateInfo &TemplateInfo = ParsedTemplateInfo(), - LateParsedAttrList *LateParsedAttrs = nullptr); + LateParsedAttrList *LateParsedAttrs = nullptr, + // TO_UPSTREAM(BoundsSafety) + LateParsedAttrList *BoundsSafetyLateAttrs = nullptr); void ParseKNRParamDeclarations(Declarator &D); // EndLoc is filled with the location of the last token of the simple-asm. ExprResult ParseSimpleAsm(bool ForAsmLabel, SourceLocation *EndLoc); @@ -2018,6 +2021,36 @@ class Parser : public CodeCompletionHandler { /// Parse a __builtin_bit_cast(T, E), used to implement C++2a std::bit_cast. ExprResult ParseBuiltinBitCast(); + /* TO_UPSTREAM(BoundsSafety) ON */ + //===--------------------------------------------------------------------===// + /// BoundsSafety: __builtin_unsafe_forge_bidi_indexable(expr, size) + ExprResult ParseUnsafeForgeBidiIndexable(); + + //===--------------------------------------------------------------------===// + /// BoundsSafety: __builtin_unsafe_forge_single(expr) + ExprResult ParseUnsafeForgeSingle(); + + //===--------------------------------------------------------------------===// + /// BoundsSafety: __builtin_unsafe_forge_terminated_by(expr, terminator) + ExprResult ParseUnsafeForgeTerminatedBy(); + + //===--------------------------------------------------------------------===// + /// BoundsSafety: __builtin_get_pointer_lower_bound(expr) + enum PointerBoundKind { PBK_Lower, PBK_Upper }; + ExprResult ParseGetPointerBound(PointerBoundKind K); + + //===--------------------------------------------------------------------===// + /// BoundsSafety: + /// __builtin_terminated_by_to_indexable(pointer [, terminator]) + /// __builtin_unsafe_terminated_by_to_indexable(pointer [, terminator]) + ExprResult ParseTerminatedByToIndexable(bool Unsafe); + + //===--------------------------------------------------------------------===// + /// BoundsSafety: __builtin_unsafe_terminated_by_from_indexable(terminator, + /// pointer [, pointer-to-terminator]) + ExprResult ParseUnsafeTerminatedByFromIndexable(); + /* TO_UPSTREAM(BoundsSafety) OFF*/ + //===--------------------------------------------------------------------===// // C++ 5.2p1: C++ Type Identification ExprResult ParseCXXTypeid(); @@ -2480,7 +2513,8 @@ class Parser : public CodeCompletionHandler { ParsedAttributes &Attrs, ParsedTemplateInfo &TemplateInfo, SourceLocation *DeclEnd = nullptr, - ForRangeInit *FRI = nullptr); + ForRangeInit *FRI = nullptr, + LateParsedAttrList *BoundsSafetyLateAttrs = nullptr); Decl *ParseDeclarationAfterDeclarator(Declarator &D, const ParsedTemplateInfo &TemplateInfo = ParsedTemplateInfo()); bool ParseAsmAttributesAfterDeclarator(Declarator &D); @@ -2527,14 +2561,19 @@ class Parser : public CodeCompletionHandler { void ParseSpecifierQualifierList( DeclSpec &DS, AccessSpecifier AS = AS_none, - DeclSpecContext DSC = DeclSpecContext::DSC_normal) { - ParseSpecifierQualifierList(DS, getImplicitTypenameContext(DSC), AS, DSC); + DeclSpecContext DSC = DeclSpecContext::DSC_normal, + // TO_UPSTREAM(BoundsSafety) + LateParsedAttrList *LateAttrs = nullptr) { + ParseSpecifierQualifierList(DS, getImplicitTypenameContext(DSC), AS, DSC, + LateAttrs); } void ParseSpecifierQualifierList( DeclSpec &DS, ImplicitTypenameContext AllowImplicitTypename, AccessSpecifier AS = AS_none, - DeclSpecContext DSC = DeclSpecContext::DSC_normal); + DeclSpecContext DSC = DeclSpecContext::DSC_normal, + // TO_UPSTREAM(BoundsSafety) + LateParsedAttrList *LateAttrs = nullptr); void ParseObjCTypeQualifierList(ObjCDeclSpec &DS, DeclaratorContext Context); @@ -2791,7 +2830,9 @@ class Parser : public CodeCompletionHandler { ParseTypeName(SourceRange *Range = nullptr, DeclaratorContext Context = DeclaratorContext::TypeName, AccessSpecifier AS = AS_none, Decl **OwnedType = nullptr, - ParsedAttributes *Attrs = nullptr); + ParsedAttributes *Attrs = nullptr, + // TO_UPSTREAM(BoundsSafety) + LateParsedAttrList *LateAttrs = nullptr); private: void ParseBlockId(SourceLocation CaretLoc); @@ -2946,11 +2987,15 @@ class Parser : public CodeCompletionHandler { void ParseGNUAttributes(ParsedAttributes &Attrs, LateParsedAttrList *LateAttrs = nullptr, Declarator *D = nullptr); + /* TO_UPSTREAM(BoundsSafety) ON */ + // NestedTypeLevel is not a parameter in upstream void ParseGNUAttributeArgs(IdentifierInfo *AttrName, SourceLocation AttrNameLoc, ParsedAttributes &Attrs, SourceLocation *EndLoc, IdentifierInfo *ScopeName, SourceLocation ScopeLoc, - ParsedAttr::Form Form, Declarator *D); + ParsedAttr::Form Form, Declarator *D, + size_t NestedTypeLevel=0); + /* TO_UPSTREAM(BoundsSafety) OFF */ IdentifierLoc *ParseIdentifierLoc(); unsigned @@ -3076,10 +3121,18 @@ class Parser : public CodeCompletionHandler { void ParseOpenCLQualifiers(ParsedAttributes &Attrs); void ParseNullabilityTypeSpecifiers(ParsedAttributes &attrs); void ParseCUDAFunctionAttributes(ParsedAttributes &attrs); + // TO_UPSTREAM(BoundsSafety) + void ParseBoundsSafetyTypeSpecifiers(ParsedAttributes &attrs); bool isHLSLQualifier(const Token &Tok) const; void ParseHLSLQualifiers(ParsedAttributes &Attrs); VersionTuple ParseVersionTuple(SourceRange &Range); + void ParseFeatureAvailabilityAttribute( + IdentifierInfo &Availability, SourceLocation AvailabilityLoc, + ParsedAttributes &attrs, SourceLocation *endLoc, + IdentifierInfo *ScopeName, SourceLocation ScopeLoc, ParsedAttr::Form Form, + BalancedDelimiterTracker &T); + void ParseAvailabilityAttribute(IdentifierInfo &Availability, SourceLocation AvailabilityLoc, ParsedAttributes &attrs, @@ -3130,19 +3183,27 @@ class Parser : public CodeCompletionHandler { SourceLocation ScopeLoc, ParsedAttr::Form Form); - void DistributeCLateParsedAttrs(Decl *Dcl, LateParsedAttrList *LateAttrs); + /* TO_UPSTREAM(BoundsSafety) ON */ + // Upstream doesn't have the `Declarator` parameter + void DistributeCLateParsedAttrs(Declarator &D, Decl *Dcl, + LateParsedAttrList *LateAttrs); + /* TO_UPSTREAM(BoundsSafety) OFF */ void ParseBoundsAttribute(IdentifierInfo &AttrName, SourceLocation AttrNameLoc, ParsedAttributes &Attrs, IdentifierInfo *ScopeName, SourceLocation ScopeLoc, - ParsedAttr::Form Form); + ParsedAttr::Form Form, + // TO_UPSTREAM(BoundsSafety) + unsigned NestedTypeLevel = 0); void ParseTypeofSpecifier(DeclSpec &DS); SourceLocation ParseDecltypeSpecifier(DeclSpec &DS); void AnnotateExistingDecltypeSpecifier(const DeclSpec &DS, SourceLocation StartLoc, SourceLocation EndLoc); - void ParseAtomicSpecifier(DeclSpec &DS); + void ParseAtomicSpecifier(DeclSpec &DS, + // TO_UPSTREAM(BoundsSafety) + LateParsedAttrList *LateAttrs = nullptr); ExprResult ParseAlignArgument(StringRef KWName, SourceLocation Start, SourceLocation &EllipsisLoc, bool &IsType, @@ -3151,6 +3212,8 @@ class Parser : public CodeCompletionHandler { SourceLocation *endLoc = nullptr); ExprResult ParseExtIntegerArgument(); + void ParsePtrauthQualifier(ParsedAttributes &Attrs); + VirtSpecifiers::Specifier isCXX11VirtSpecifier(const Token &Tok) const; VirtSpecifiers::Specifier isCXX11VirtSpecifier() const { return isCXX11VirtSpecifier(Tok); @@ -3218,7 +3281,9 @@ class Parser : public CodeCompletionHandler { DeclSpec &DS, unsigned AttrReqs = AR_AllAttributesParsed, bool AtomicAllowed = true, bool IdentifierRequired = false, std::optional<llvm::function_ref<void()>> CodeCompletionHandler = - std::nullopt); + std::nullopt, + // TO_UPSTREAM(BoundsSafety) + LateParsedAttrList *LateAttrs = nullptr); void ParseDirectDeclarator(Declarator &D); void ParseDecompositionDeclarator(Declarator &D); void ParseParenDeclarator(Declarator &D); @@ -3237,16 +3302,21 @@ class Parser : public CodeCompletionHandler { void ParseParameterDeclarationClause( Declarator &D, ParsedAttributes &attrs, SmallVectorImpl<DeclaratorChunk::ParamInfo> &ParamInfo, - SourceLocation &EllipsisLoc) { + SourceLocation &EllipsisLoc, + // TO_UPSTREAM(BoundsSafety) + LateParsedAttrList *LateParamAttrs = nullptr) { return ParseParameterDeclarationClause( D.getContext(), attrs, ParamInfo, EllipsisLoc, D.getCXXScopeSpec().isSet() && - D.isFunctionDeclaratorAFunctionDeclaration()); + D.isFunctionDeclaratorAFunctionDeclaration(), + LateParamAttrs); } void ParseParameterDeclarationClause( DeclaratorContext DeclaratorContext, ParsedAttributes &attrs, SmallVectorImpl<DeclaratorChunk::ParamInfo> &ParamInfo, - SourceLocation &EllipsisLoc, bool IsACXXFunctionDeclaration = false); + SourceLocation &EllipsisLoc, bool IsACXXFunctionDeclaration = false, + // TO_UPSTREAM(BoundsSafety) + LateParsedAttrList *LateParamAttrs = nullptr); void ParseBracketDeclarator(Declarator &D); void ParseMisplacedBracketDeclarator(Declarator &D); @@ -3356,10 +3426,9 @@ class Parser : public CodeCompletionHandler { AccessSpecifier AS, ParsedAttributes &Attr, ParsedTemplateInfo &TemplateInfo, ParsingDeclRAIIObject *DiagsFromTParams = nullptr); - DeclGroupPtrTy - ParseCXXClassMemberDeclarationWithPragmas(AccessSpecifier &AS, - ParsedAttributes &AccessAttrs, - DeclSpec::TST TagType, Decl *Tag); + DeclGroupPtrTy ParseCXXClassMemberDeclarationWithPragmas( + AccessSpecifier &AS, ParsedAttributes &AccessAttrs, DeclSpec::TST TagType, + Decl *Tag); void ParseConstructorInitializer(Decl *ConstructorDecl); MemInitResult ParseMemInitializer(Decl *ConstructorDecl); void HandleMemberFunctionDeclDelays(Declarator& DeclaratorInfo, @@ -3861,6 +3930,25 @@ class Parser : public CodeCompletionHandler { /// \param IncludeLoc The location at which this parse was triggered. TypeResult ParseTypeFromString(StringRef TypeStr, StringRef Context, SourceLocation IncludeLoc); + /* TO_UPSTREAM(BoundsSafety) ON */ + /// Parse the given string as an expression in the argument position for a + /// bounds safety attribute. + /// + /// This is a dangerous utility function currently employed only by API notes. + /// It is not a general entry-point for safely parsing expressions from + /// strings. + /// + /// \param ExprStr The string to be parsed as an expression. + /// \param Context The name of the context in which this string is being + /// parsed, which will be used in diagnostics. + /// \param ParentDecl If a function or method is provided, the parameters are + /// added to the current parsing context. + /// \param IncludeLoc The location at which this parse was triggered. + ExprResult ParseBoundsAttributeArgFromString(StringRef ExprStr, + StringRef Context, + Decl *ParentDecl, + SourceLocation IncludeLoc); + /* TO_UPSTREAM(BoundsSafety) OFF */ //===--------------------------------------------------------------------===// // Modules @@ -3890,6 +3978,8 @@ class Parser : public CodeCompletionHandler { ExprResult ParseArrayTypeTrait(); ExprResult ParseExpressionTrait(); + ExprResult ParseBuiltinPtrauthTypeDiscriminator(); + //===--------------------------------------------------------------------===// // Preprocessor code-completion pass-through void CodeCompleteDirective(bool InConditional) override; diff --git a/clang/include/clang/Sema/BoundsSafetySuggestions.h b/clang/include/clang/Sema/BoundsSafetySuggestions.h new file mode 100644 index 0000000000000..037b0f0b0684c --- /dev/null +++ b/clang/include/clang/Sema/BoundsSafetySuggestions.h @@ -0,0 +1,147 @@ +/* TO_UPSTREAM(BoundsSafety) ON */ +//===- BoundsSafetySuggestions.h - -fbounds-safety suggestions --*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines a collection of analyses that aid adoption of +// -fbounds-safety annotations. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_SEMA_BOUNDSSAFETYSUGGESTIONS_H +#define LLVM_CLANG_SEMA_BOUNDSSAFETYSUGGESTIONS_H + +#include "clang/AST/Type.h" +#include "clang/Basic/Diagnostic.h" +#include "llvm/ADT/ArrayRef.h" +namespace clang { + +// Forward-declare to reduce bloat. +class ASTContext; +class Decl; +class Expr; +class FieldDecl; +class FunctionDecl; +class NamedDecl; +class ParmVarDecl; +class Sema; +class Stmt; +class VarDecl; +// An abstract interface to the bounds safety suggestion machine. +class BoundsSafetySuggestionHandler { +public: + enum class UnsafeOpKind { + Index, + Arithmetic, + Deref, + MemberAccess, + Assignment, + Return, + CallArg, + Cast + }; + + enum class AssignmentSourceKind { + Parameter = 0, + GlobalVar, + LocalVar, + FunctionCallReturnValue, + ArrayElement, + StructMember, + UnionMember, + }; + + enum class WillTrapKind { + NoTrap, + Unknown, + Trap, + TrapIffPtrNotNull, + TrapIffPtrNull + }; + + enum class PtrArithOOBKind { + NEVER_OOB = 0, + ALWAYS_OOB_BASE_OOB, + ALWAYS_OOB_CONSTANT_OFFSET, + OOB_IF_EFFECTIVE_OFFSET_GTE_MINIMUM_OOB_POSITIVE_OFFSET, + OOB_IF_EFFECTIVE_OFFSET_GTE_MINIMUM_OOB_POSITIVE_OFFSET_OR_LT_ZERO, + OOB_IF_EFFECTIVE_OFFSET_LT_ZERO, + UNKNOWN + }; + + struct SingleEntity { + AssignmentSourceKind Kind; + const NamedDecl *Entity; + const Expr *AssignmentExpr; + + // The bounds of the __bidi_indexable will store the bounds of a single + // `SinglePointeeTy`. Not necessarily the same as the + // `AssignmentExpr->getType()` due to additional casts. + const QualType SinglePointeeTy; + }; + + /// Callback for when analysis in UnsafeOperationVisitor detects that an + /// implicitly __bidi_indexable local variable (that participates in + /// potentially unsafe pointer arithmetic or indexing) is initialized or + /// assigned from a an entity that is a __single pointer. + virtual void handleSingleEntitiesFlowingToIndexableVariableIndexOrPtrArith( + const llvm::ArrayRef<SingleEntity> Entities, + const VarDecl *IndexableLocalVar, const Stmt *UnsafeOp, UnsafeOpKind Kind, + PtrArithOOBKind IsOOB, size_t MinimumPtrArithOOBOffset) = 0; + + /// Callback for when analysis in UnsafeOperationVisitor detects that an + /// implicitly __bidi_indexable local variable (that participates in + /// an unsafe buffer operation that accesses the 0th element) is initialized + /// or assigned from an entity that is a __single pointer. + virtual void handleSingleEntitiesFlowingToIndexableVariableWithEltZeroOOB( + const llvm::ArrayRef<SingleEntity> Entities, + const VarDecl *IndexableLocalVar, const Stmt *UnsafeOp, + const Expr *Operand, UnsafeOpKind Kind) = 0; + + /// Callback for when analysis in UnsafeOperationVisitor detects that an + /// implicitly __bidi_indexable local variable (that is + /// "unsafely cast") is initialized + /// or assigned from an entity that is a __single pointer. + /// + /// The following types of cast are considered unsafe: + /// + /// 1. `__bidi_indexable` -> `__single` (CK_BoundsSafetyPointerCast) where + /// `local` has insufficient bounds to allow access to a single element of the + /// pointee type. + /// + /// 2. Bit cast where the pointee type of the pointer (CK_BitCast) is changed + /// to a larger type than `local` has the bounds for. I.e. the resulting + /// pointer is an out-of-bounds pointer. + virtual void handleSingleEntitiesFlowingToIndexableVariableUnsafelyCasted( + const llvm::ArrayRef<SingleEntity> Entities, + const VarDecl *IndexableLocalVar, const Stmt *UnsafeOp, UnsafeOpKind Kind, + const Expr *Operand) = 0; + + virtual void handleSingleEntitiesFlowingToIndexableDynamicCountConversion( + const llvm::ArrayRef<SingleEntity> Entities, + const VarDecl *IndexableLocalVar, const Stmt *UnsafeOp, UnsafeOpKind Kind, + const Expr *Operand, const QualType DCPT, WillTrapKind WillTrap, + std::optional<llvm::APInt> ConstantCount, size_t MaxSafeSizeOrCount) = 0; + + // Always provide a virtual destructor! + virtual ~BoundsSafetySuggestionHandler() = default; +}; + +void checkBoundsSafetySuggestions(const Decl *D, + BoundsSafetySuggestionHandler &H, Sema &S); + +inline const StreamingDiagnostic & +operator<<(const StreamingDiagnostic &PD, + const BoundsSafetySuggestionHandler::AssignmentSourceKind Kind) { + PD.AddTaggedVal(static_cast<uint64_t>(Kind), DiagnosticsEngine::ak_uint); + return PD; +} + +} // namespace clang + +#endif /* LLVM_CLANG_SEMA_BOUNDSSAFETYSUGGESTIONS_H */ +/* TO_UPSTREAM(BoundsSafety) OFF */ diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h index 425b6e2a0b30c..92830645e712a 100644 --- a/clang/include/clang/Sema/DeclSpec.h +++ b/clang/include/clang/Sema/DeclSpec.h @@ -1265,6 +1265,18 @@ struct DeclaratorChunk { ParsedAttributesView AttrList; + /* TO_UPSTREAM(BoundsSafety) ON */ + struct LateParsedAttrInfo { + CachedTokens Toks; + IdentifierInfo &AttrName; + SourceLocation AttrNameLoc; + + explicit LateParsedAttrInfo(CachedTokens Toks, IdentifierInfo &AttrName, + SourceLocation AttrNameLoc) + : Toks(Toks), AttrName(AttrName), AttrNameLoc(AttrNameLoc) {} + }; + /* TO_UPSTREAM(BoundsSafety) OFF */ + struct PointerTypeInfo { /// The type qualifiers: const/volatile/restrict/unaligned/atomic. LLVM_PREFERRED_TYPE(DeclSpec::TQ) @@ -1285,8 +1297,26 @@ struct DeclaratorChunk { /// The location of the __unaligned-qualifier, if any. SourceLocation UnalignedQualLoc; + /* TO_UPSTREAM(BoundsSafety) ON */ + // LateParsedAttrInfo objects and their count. + unsigned NumLateParsedAttrs; + LateParsedAttrInfo **LateAttrInfos; + /* TO_UPSTREAM(BoundsSafety) OFF */ + void destroy() { + /* TO_UPSTREAM(BoundsSafety) ON */ + for (unsigned i = 0; i < NumLateParsedAttrs; ++i) + delete LateAttrInfos[i]; + if (NumLateParsedAttrs != 0) + delete[] LateAttrInfos; + /* TO_UPSTREAM(BoundsSafety) OFF */ + } + + /* TO_UPSTREAM(BoundsSafety) ON */ + ArrayRef<LateParsedAttrInfo *> getLateParsedAttrInfos() const { + return {LateAttrInfos, NumLateParsedAttrs}; } + /* TO_UPSTREAM(BoundsSafety) OFF */ }; struct ReferenceTypeInfo { @@ -1317,7 +1347,26 @@ struct DeclaratorChunk { /// expression class on all clients, NumElts is untyped. Expr *NumElts; - void destroy() {} + /* TO_UPSTREAM(BoundsSafety) ON */ + // LateParsedAttrInfo objects and their count. + unsigned NumLateParsedAttrs; + LateParsedAttrInfo **LateAttrInfos; + /* TO_UPSTREAM(BoundsSafety) OFF */ + + void destroy() { + /* TO_UPSTREAM(BoundsSafety) ON */ + for (unsigned i = 0; i < NumLateParsedAttrs; ++i) + delete LateAttrInfos[i]; + if (NumLateParsedAttrs != 0) + delete[] LateAttrInfos; + /* TO_UPSTREAM(BoundsSafety) OFF */ + } + + /* TO_UPSTREAM(BoundsSafety) ON */ + ArrayRef<LateParsedAttrInfo *> getLateParsedAttrInfos() const { + return {LateAttrInfos, NumLateParsedAttrs}; + } + /* TO_UPSTREAM(BoundsSafety) OFF */ }; /// ParamInfo - An array of paraminfo objects is allocated whenever a function @@ -1666,7 +1715,9 @@ struct DeclaratorChunk { SourceLocation VolatileQualLoc, SourceLocation RestrictQualLoc, SourceLocation AtomicQualLoc, - SourceLocation UnalignedQualLoc) { + SourceLocation UnalignedQualLoc, + // TO_UPSTREAM(BoundsSafety) + ArrayRef<LateParsedAttrInfo*> LateAttrInfos = {}) { DeclaratorChunk I; I.Kind = Pointer; I.Loc = Loc; @@ -1677,6 +1728,14 @@ struct DeclaratorChunk { I.Ptr.RestrictQualLoc = RestrictQualLoc; I.Ptr.AtomicQualLoc = AtomicQualLoc; I.Ptr.UnalignedQualLoc = UnalignedQualLoc; + /* TO_UPSTREAM(BoundsSafety) ON */ + I.Ptr.NumLateParsedAttrs = LateAttrInfos.size(); + if (!LateAttrInfos.empty()) { + I.Ptr.LateAttrInfos = new LateParsedAttrInfo *[LateAttrInfos.size()]; + for (size_t J = 0; J < LateAttrInfos.size(); ++J) + I.Ptr.LateAttrInfos[J] = LateAttrInfos[J]; + } + /* TO_UPSTREAM(BoundsSafety) OFF */ return I; } @@ -1694,7 +1753,9 @@ struct DeclaratorChunk { /// Return a DeclaratorChunk for an array. static DeclaratorChunk getArray(unsigned TypeQuals, bool isStatic, bool isStar, Expr *NumElts, - SourceLocation LBLoc, SourceLocation RBLoc) { + SourceLocation LBLoc, SourceLocation RBLoc, + // TO_UPSTREAM(BoundsSafety) + ArrayRef<LateParsedAttrInfo*> LateAttrInfos = {}) { DeclaratorChunk I; I.Kind = Array; I.Loc = LBLoc; @@ -1703,6 +1764,14 @@ struct DeclaratorChunk { I.Arr.hasStatic = isStatic; I.Arr.isStar = isStar; I.Arr.NumElts = NumElts; + /* TO_UPSTREAM(BoundsSafety) ON */ + I.Arr.NumLateParsedAttrs = LateAttrInfos.size(); + if (!LateAttrInfos.empty()) { + I.Arr.LateAttrInfos = new LateParsedAttrInfo *[LateAttrInfos.size()]; + for (size_t J = 0; J < LateAttrInfos.size(); ++J) + I.Arr.LateAttrInfos[J] = LateAttrInfos[J]; + } + /* TO_UPSTREAM(BoundsSafety) OFF */ return I; } @@ -2404,6 +2473,21 @@ class Declarator { return DeclTypeInfo[i]; } + /*TO_UPSTREAM(BoundsSafety) ON*/ + /// Add all DeclaratorChunks from Other to the end of this declarator, and + /// remove them from Other. This transfers ownership of the DeclaratorChunks + /// and their attributes to this object. + void TakeTypeObjects(Declarator &Other) { + DeclTypeInfo.append(Other.DeclTypeInfo); // Keep the ordering + while (!Other.DeclTypeInfo.empty()) { + // Remove without calling destroy(), so it won't be destroyed when + // calling the destructor on Other + DeclaratorChunk Removed = Other.DeclTypeInfo.pop_back_val(); + getAttributePool().takeFrom(Removed.getAttrs(), Other.getAttributePool()); + } + } + /*TO_UPSTREAM(BoundsSafety) OFF*/ + typedef SmallVectorImpl<DeclaratorChunk>::const_iterator type_object_iterator; typedef llvm::iterator_range<type_object_iterator> type_object_range; diff --git a/clang/include/clang/Sema/DelayedDiagnostic.h b/clang/include/clang/Sema/DelayedDiagnostic.h index 0105089a393f1..93ecae3529231 100644 --- a/clang/include/clang/Sema/DelayedDiagnostic.h +++ b/clang/include/clang/Sema/DelayedDiagnostic.h @@ -125,7 +125,12 @@ class AccessedEntity { /// the complete parsing of the current declaration. class DelayedDiagnostic { public: - enum DDKind : unsigned char { Availability, Access, ForbiddenType }; + enum DDKind : unsigned char { + Availability, + FeatureAvailability, + Access, + ForbiddenType + }; DDKind Kind; bool Triggered; @@ -143,6 +148,9 @@ class DelayedDiagnostic { StringRef Msg, bool ObjCPropertyAccess); + static DelayedDiagnostic + makeFeatureAvailability(NamedDecl *D, ArrayRef<SourceLocation> Locs); + static DelayedDiagnostic makeAccess(SourceLocation Loc, const AccessedEntity &Entity) { DelayedDiagnostic DD; @@ -201,6 +209,12 @@ class DelayedDiagnostic { return AvailabilityData.AR; } + const NamedDecl *getFeatureAvailabilityDecl() const { + assert(Kind == FeatureAvailability && + "Not a feature availability diagnostic."); + return FeatureAvailabilityData.Decl; + } + /// The diagnostic ID to emit. Used like so: /// Diag(diag.Loc, diag.getForbiddenTypeDiagnostic()) /// << diag.getForbiddenTypeOperand() @@ -246,6 +260,10 @@ class DelayedDiagnostic { bool ObjCPropertyAccess; }; + struct FAD { + const NamedDecl *Decl; + }; + struct FTD { unsigned Diagnostic; unsigned Argument; @@ -254,6 +272,7 @@ class DelayedDiagnostic { union { struct AD AvailabilityData; + struct FAD FeatureAvailabilityData; struct FTD ForbiddenTypeData; /// Access control. diff --git a/clang/include/clang/Sema/DynamicCountPointerAssignmentAnalysisExported.h b/clang/include/clang/Sema/DynamicCountPointerAssignmentAnalysisExported.h new file mode 100644 index 0000000000000..f1a64f01911a2 --- /dev/null +++ b/clang/include/clang/Sema/DynamicCountPointerAssignmentAnalysisExported.h @@ -0,0 +1,31 @@ +//===--- DynamicCountPointerAssignmentAnalysisExported.h --------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file exports DynamicCountPointer Analysis utilities to other libraries. +// +// This is a workaround necessitated in the fact that Sema/TreeTransform.h is a +// private header file (not in the include path that other libraries can use). +// The "right" solution here would be to move the header so it is public but +// that would create a conflict with upstream which is really not desirable. +// +// So instead this file exports a really simple interface that hides the +// `TreeTransform` type (and its dependencies) +//===----------------------------------------------------------------------===// +#ifndef LLVM_CLANG_SEMA_DYNAMIC_COUNT_POINTER_ASSIGNMENT_ANALYSIS_EXPORTED_H +#define LLVM_CLANG_SEMA_DYNAMIC_COUNT_POINTER_ASSIGNMENT_ANALYSIS_EXPORTED_H +#include "clang/Sema/Ownership.h" +#include "clang/Sema/Sema.h" + +namespace clang { + +ExprResult ReplaceCountExprParamsWithArgsFromCall(const Expr *CountExpr, + const CallExpr *CE, Sema &S); + +} + +#endif diff --git a/clang/include/clang/Sema/Overload.h b/clang/include/clang/Sema/Overload.h index 9d8b797af6663..26ffe057c74a2 100644 --- a/clang/include/clang/Sema/Overload.h +++ b/clang/include/clang/Sema/Overload.h @@ -998,7 +998,9 @@ class Sema; private: friend class OverloadCandidateSet; OverloadCandidate() - : IsSurrogate(false), IsADLCandidate(CallExpr::NotADL), RewriteKind(CRK_None) {} + : IsSurrogate(false), IgnoreObjectArgument(false), + TookAddressOfOverload(false), IsADLCandidate(CallExpr::NotADL), + RewriteKind(CRK_None) {} }; /// OverloadCandidateSet - A set of overload candidates, used in C++ diff --git a/clang/include/clang/Sema/ParsedAttr.h b/clang/include/clang/Sema/ParsedAttr.h index 22cbd0d90ee43..58b880ee339a8 100644 --- a/clang/include/clang/Sema/ParsedAttr.h +++ b/clang/include/clang/Sema/ParsedAttr.h @@ -191,6 +191,10 @@ class ParsedAttr final LLVM_PREFERRED_TYPE(bool) mutable unsigned IsPragmaClangAttribute : 1; + /// Determines if the attribute will be printed as macro in the diagnostics. + LLVM_PREFERRED_TYPE(bool) + mutable unsigned PrintAsMacro : 1; + /// The location of the 'unavailable' keyword in an /// availability attribute. SourceLocation UnavailableLoc; @@ -225,7 +229,7 @@ class ParsedAttr final UsedAsTypeAttr(false), IsAvailability(false), IsTypeTagForDatatype(false), IsProperty(false), HasParsedType(false), HasProcessingCache(false), IsPragmaClangAttribute(false), - Info(ParsedAttrInfo::get(*this)) { + PrintAsMacro(false), Info(ParsedAttrInfo::get(*this)) { if (numArgs) memcpy(getArgsBuffer(), args, numArgs * sizeof(ArgsUnion)); } @@ -242,8 +246,8 @@ class ParsedAttr final NumArgs(1), Invalid(false), UsedAsTypeAttr(false), IsAvailability(true), IsTypeTagForDatatype(false), IsProperty(false), HasParsedType(false), HasProcessingCache(false), IsPragmaClangAttribute(false), - UnavailableLoc(unavailable), MessageExpr(messageExpr), - Info(ParsedAttrInfo::get(*this)) { + PrintAsMacro(false), UnavailableLoc(unavailable), + MessageExpr(messageExpr), Info(ParsedAttrInfo::get(*this)) { ArgsUnion PVal(Parm); memcpy(getArgsBuffer(), &PVal, sizeof(ArgsUnion)); new (getAvailabilityData()) @@ -260,7 +264,8 @@ class ParsedAttr final NumArgs(3), Invalid(false), UsedAsTypeAttr(false), IsAvailability(false), IsTypeTagForDatatype(false), IsProperty(false), HasParsedType(false), HasProcessingCache(false), - IsPragmaClangAttribute(false), Info(ParsedAttrInfo::get(*this)) { + IsPragmaClangAttribute(false), PrintAsMacro(false), + Info(ParsedAttrInfo::get(*this)) { ArgsUnion *Args = getArgsBuffer(); Args[0] = Parm1; Args[1] = Parm2; @@ -276,7 +281,8 @@ class ParsedAttr final NumArgs(1), Invalid(false), UsedAsTypeAttr(false), IsAvailability(false), IsTypeTagForDatatype(true), IsProperty(false), HasParsedType(false), HasProcessingCache(false), - IsPragmaClangAttribute(false), Info(ParsedAttrInfo::get(*this)) { + IsPragmaClangAttribute(false), PrintAsMacro(false), + Info(ParsedAttrInfo::get(*this)) { ArgsUnion PVal(ArgKind); memcpy(getArgsBuffer(), &PVal, sizeof(ArgsUnion)); detail::TypeTagForDatatypeData &ExtraData = getTypeTagForDatatypeDataSlot(); @@ -294,7 +300,7 @@ class ParsedAttr final UsedAsTypeAttr(false), IsAvailability(false), IsTypeTagForDatatype(false), IsProperty(false), HasParsedType(true), HasProcessingCache(false), IsPragmaClangAttribute(false), - Info(ParsedAttrInfo::get(*this)) { + PrintAsMacro(false), Info(ParsedAttrInfo::get(*this)) { new (&getTypeBuffer()) ParsedType(typeArg); } @@ -306,7 +312,8 @@ class ParsedAttr final NumArgs(0), Invalid(false), UsedAsTypeAttr(false), IsAvailability(false), IsTypeTagForDatatype(false), IsProperty(true), HasParsedType(false), HasProcessingCache(false), - IsPragmaClangAttribute(false), Info(ParsedAttrInfo::get(*this)) { + IsPragmaClangAttribute(false), PrintAsMacro(false), + Info(ParsedAttrInfo::get(*this)) { new (&getPropertyDataBuffer()) detail::PropertyData(getterId, setterId); } @@ -356,6 +363,8 @@ class ParsedAttr final return IsProperty; } + bool isAvailabilityAttribute() const { return IsAvailability; } + bool isInvalid() const { return Invalid; } void setInvalid(bool b = true) const { Invalid = b; } @@ -493,9 +502,11 @@ class ParsedAttr final /// Set the macro identifier info object that this parsed attribute was /// declared in if it was declared in a macro. Also set the expansion location /// of the macro. - void setMacroIdentifier(IdentifierInfo *MacroName, SourceLocation Loc) { + void setMacroIdentifier(IdentifierInfo *MacroName, SourceLocation Loc, + bool PrintAsMac) { MacroII = MacroName; MacroExpansionLoc = Loc; + PrintAsMacro = PrintAsMac; } /// Returns true if this attribute was declared in a macro. @@ -511,6 +522,12 @@ class ParsedAttr final return MacroExpansionLoc; } + bool printAsMacro() const { + if (!hasMacroIdentifier()) + return false; + return PrintAsMacro; + } + /// Check if the attribute has exactly as many args as Num. May output an /// error. Returns false if a diagnostic is produced. bool checkExactlyNumArgs(class Sema &S, unsigned Num) const; @@ -1105,16 +1122,16 @@ enum AttributeDeclKind { inline const StreamingDiagnostic &operator<<(const StreamingDiagnostic &DB, const ParsedAttr &At) { - DB.AddTaggedVal(reinterpret_cast<uint64_t>(At.getAttrName()), + const IdentifierInfo *AttrName = + At.printAsMacro() ? At.getMacroIdentifier() : At.getAttrName(); + DB.AddTaggedVal(reinterpret_cast<uint64_t>(AttrName), DiagnosticsEngine::ak_identifierinfo); return DB; } inline const StreamingDiagnostic &operator<<(const StreamingDiagnostic &DB, const ParsedAttr *At) { - DB.AddTaggedVal(reinterpret_cast<uint64_t>(At->getAttrName()), - DiagnosticsEngine::ak_identifierinfo); - return DB; + return DB << *At; } /// AttributeCommonInfo has a non-explicit constructor which takes an diff --git a/clang/include/clang/Sema/ScopeInfo.h b/clang/include/clang/Sema/ScopeInfo.h index 700e361ef83f1..5fcc0de201d38 100644 --- a/clang/include/clang/Sema/ScopeInfo.h +++ b/clang/include/clang/Sema/ScopeInfo.h @@ -144,6 +144,8 @@ class FunctionScopeInfo { /// unavailable. bool HasPotentialAvailabilityViolations : 1; + bool HasPotentialFeatureAvailabilityViolations : 1; + /// A flag that is set when parsing a method that must call super's /// implementation, such as \c -dealloc, \c -finalize, or any method marked /// with \c __attribute__((objc_requires_super)). @@ -394,7 +396,8 @@ class FunctionScopeInfo { HasBranchIntoScope(false), HasIndirectGoto(false), HasMustTail(false), HasDroppedStmt(false), HasOMPDeclareReductionCombiner(false), HasFallthroughStmt(false), UsesFPIntrin(false), - HasPotentialAvailabilityViolations(false), ObjCShouldCallSuper(false), + HasPotentialAvailabilityViolations(false), + HasPotentialFeatureAvailabilityViolations(false), ObjCShouldCallSuper(false), ObjCIsDesignatedInit(false), ObjCWarnForNoDesignatedInitChain(false), ObjCIsSecondaryInit(false), ObjCWarnForNoInitDelegation(false), NeedsCoroutineSuspends(true), FoundImmediateEscalatingExpression(false), diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index d638d31e050dc..1cfe0e89cb1b9 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -228,6 +228,8 @@ class VisibleDeclConsumer; class IndirectFieldDecl; struct DeductionFailureInfo; class TemplateSpecCandidateSet; +// TO_UPSTREAM(BoundsSafety) +class CopyExpr; namespace sema { class AccessedEntity; @@ -367,6 +369,145 @@ class PreferredTypeBuilder { llvm::function_ref<QualType()> ComputeType; }; +/* TO_UPSTREAM(BoundsSafety) ON */ +/// Groups together the parameters to emit a bounds check. +class BoundsCheckBuilder { + // (the larger pointer we are slicing) + /// The wide pointer representing the range that is being sliced into. Either + /// it or both of WideLowerBound and WideUpperBound must be set. + Expr *WidePointerExpr; + + /// The lower bound of the range that is being sliced into. Either it and + /// WideUpperBound must be set, or WidePointerExpr must be set. + Expr *WideLowerBound; + + /// The upper bound of the range that is being sliced into. Either it and + /// WideLowerBound must be set, or WidePointerExpr must be set. + Expr *WideUpperBound; + + /// An index from the lower bound that is being accessed. At most one of it or + /// AccessStartIndex must be set. If neither is set, this is assumed to be + /// dereferencing the wide pointer. + Expr *AccessStartIndex; + + /// The start of the range being accessed and that is presumed to lie within + /// the wide pointer. At most one of it or AccessStartIndex must be set. If + /// neither is set, this is assumed to be dereferencing the wide pointer. + Expr *AccessLowerBound; + + /// The end of the range being accessed and that is presumed to lie within + /// the wide pointer. At most one of it or AccessCount must be set. If neither + /// is set, behaves as if AccessCount was the integer literal 1. + Expr *AccessUpperBound; + + /// The number of items being accessed, starting from AccessLowerBound. At + /// most one of it or AccessUpperBound must be set. If neither is set, behaves + /// as if AccessCount was the integer literal 1. + Expr *AccessCount; + + llvm::SmallVectorImpl<OpaqueValueExpr *> &OVEs; + + /// If set to 1, AccessCount is a byte count. Must be 0 if AccessUpperBound is + /// set. + unsigned CountInBytes : 1; + /// If set to 1, the accessed bounds may be wider than the sliced bounds + /// (which are typically 0 when the sliced pointer is null). + unsigned OrNull : 1; + + OpaqueValueExpr *OpaqueWrap(Sema &S, Expr *E); + bool BuildIndexBounds(Sema &S, Expr *Min, Expr *Max, + llvm::SmallVectorImpl<Expr *> &Result); + bool BuildPtrBounds(Sema &S, Expr *Min, Expr *Max, + llvm::SmallVectorImpl<Expr *> &Result); + +public: + BoundsCheckBuilder(llvm::SmallVectorImpl<OpaqueValueExpr *> &OVEs) + : WidePointerExpr(), WideLowerBound(), WideUpperBound(), + AccessStartIndex(), AccessLowerBound(), AccessUpperBound(), + AccessCount(), OVEs(OVEs), CountInBytes(0), OrNull(0) {} + + Expr *getWidePointer() { return WidePointerExpr; } + std::pair<Expr *, Expr *> getWidePointerBounds() { + assert(!WideLowerBound == !WideUpperBound); + return std::make_pair(WideLowerBound, WideUpperBound); + } + + Expr *getAccessStartIndex() { return AccessStartIndex; } + Expr *getAccessLowerBound() { return AccessLowerBound; } + Expr *getAccessUpperBound() { return AccessUpperBound; } + Expr *getAccessCount() { return AccessCount; } + bool isCountInBytes() { assert(AccessCount); return CountInBytes; } + bool isOrNull() { return OrNull; } + + void setWidePointer(Expr *Wide) { + WidePointerExpr = Wide; + WideLowerBound = nullptr; + WideUpperBound = nullptr; + } + + void setWidePointer(Expr *Lower, Expr *Upper) { + WideLowerBound = Lower; + WideUpperBound = Upper; + WidePointerExpr = nullptr; + } + + void setAccessStartIndex(Expr *StartIndex) { + AccessStartIndex = StartIndex; + AccessLowerBound = nullptr; + } + + void setAccessLowerBound(Expr *Lower) { + AccessLowerBound = Lower; + AccessStartIndex = nullptr; + } + + void setAccessUpperBound(Expr *Upper) { + AccessUpperBound = Upper; + AccessCount = nullptr; + CountInBytes = 0; + OrNull = 0; + } + + void setAccessCount(Expr *Count, bool IsByteCount, bool IsOrNull) { + AccessCount = Count; + CountInBytes = IsByteCount; + OrNull = IsOrNull; + AccessUpperBound = nullptr; + } + + /// Create the bounds check expression. The value the bounds check evaluates + /// to when it succeeds will be GuardedValue. + ExprResult Build(Sema &S, Expr *GuardedValue); + + /// Create the bounds check expression to emit inequality `<=` checks with elements of \p Bounds in the order. + /// The function materializes elements of \p Bounds when they are reused and add the materialized values to + /// \p OVEs. + static ExprResult BuildLEChecks(Sema &S, SourceLocation Loc, + ArrayRef<Expr *> Bounds, + SmallVectorImpl<OpaqueValueExpr *> &OVEs); + + /// Create the bounds check expression that verifies that the flexible array + /// member does not exceed the bounds of the enclosing pointer. + static ExprResult + CheckFlexibleArrayMemberSize(Sema &S, SourceLocation Loc, BoundsCheckKind BCK, + Expr *FAMPtr, CopyExpr *DeclReplacer = nullptr); + static ExprResult + CheckFlexibleArrayMemberSizeWithOVEs(Sema &S, SourceLocation Loc, + BoundsCheckKind BCK, Expr *FAMPtr, + SmallVectorImpl<OpaqueValueExpr *> &OVEs, + CopyExpr *DeclReplacer = nullptr); + +private: + static ExprResult + CheckFlexibleArrayMemberSizeImpl(Sema &S, SourceLocation Loc, + BoundsCheckKind BCK, Expr *FAMPtr, + SmallVectorImpl<OpaqueValueExpr *> &OVEs, + Expr *GuardedValue, CopyExpr *DeclReplacer); + + ExprResult BuildImplicitPointerArith(Sema &S, BinaryOperatorKind Opc, Expr *LHS, Expr *RHS); +}; +/* TO_UPSTREAM(BoundsSafety) OFF */ + struct SkipBodyInfo { SkipBodyInfo() = default; bool ShouldSkip = false; @@ -481,55 +622,6 @@ enum class FunctionEffectMode : uint8_t { Dependent // effect(expr) where expr is dependent. }; -struct FunctionEffectDiff { - enum class Kind { Added, Removed, ConditionMismatch }; - - FunctionEffect::Kind EffectKind; - Kind DiffKind; - FunctionEffectWithCondition Old; // invalid when Added. - FunctionEffectWithCondition New; // invalid when Removed. - - StringRef effectName() const { - if (Old.Effect.kind() != FunctionEffect::Kind::None) - return Old.Effect.name(); - return New.Effect.name(); - } - - /// Describes the result of effects differing between a base class's virtual - /// method and an overriding method in a subclass. - enum class OverrideResult { - NoAction, - Warn, - Merge // Merge missing effect from base to derived. - }; - - /// Return true if adding or removing the effect as part of a type conversion - /// should generate a diagnostic. - bool shouldDiagnoseConversion(QualType SrcType, - const FunctionEffectsRef &SrcFX, - QualType DstType, - const FunctionEffectsRef &DstFX) const; - - /// Return true if adding or removing the effect in a redeclaration should - /// generate a diagnostic. - bool shouldDiagnoseRedeclaration(const FunctionDecl &OldFunction, - const FunctionEffectsRef &OldFX, - const FunctionDecl &NewFunction, - const FunctionEffectsRef &NewFX) const; - - /// Return true if adding or removing the effect in a C++ virtual method - /// override should generate a diagnostic. - OverrideResult shouldDiagnoseMethodOverride( - const CXXMethodDecl &OldMethod, const FunctionEffectsRef &OldFX, - const CXXMethodDecl &NewMethod, const FunctionEffectsRef &NewFX) const; -}; - -struct FunctionEffectDifferences : public SmallVector<FunctionEffectDiff> { - /// Caller should short-circuit by checking for equality first. - FunctionEffectDifferences(const FunctionEffectsRef &Old, - const FunctionEffectsRef &New); -}; - /// Sema - This implements semantic analysis and AST building for C. /// \nosubgrouping class Sema final : public SemaBase { @@ -564,10 +656,11 @@ class Sema final : public SemaBase { // 27. C++ Template Instantiation (SemaTemplateInstantiate.cpp) // 28. C++ Template Declaration Instantiation // (SemaTemplateInstantiateDecl.cpp) - // 29. C++ Variadic Templates (SemaTemplateVariadic.cpp) - // 30. Constraints and Concepts (SemaConcept.cpp) - // 31. Types (SemaType.cpp) - // 32. FixIt Helpers (SemaFixItUtils.cpp) + // 31. C++ Variadic Templates (SemaTemplateVariadic.cpp) + // 32. Constraints and Concepts (SemaConcept.cpp) + // 33. Types (SemaType.cpp) + // 34. FixIt Helpers (SemaFixItUtils.cpp) + // 35. Function Effects (SemaFunctionEffects.cpp) /// \name Semantic Analysis /// Implementations are in Sema.cpp @@ -840,7 +933,9 @@ class Sema final : public SemaBase { ExprResult ImpCastExprToType( Expr *E, QualType Type, CastKind CK, ExprValueKind VK = VK_PRValue, const CXXCastPath *BasePath = nullptr, - CheckedConversionKind CCK = CheckedConversionKind::Implicit); + CheckedConversionKind CCK = CheckedConversionKind::Implicit, + // TO_UPSTREAM(BoundsSafety) + bool DiagnoseBoundsSafetyIncompleteArrayPromotion = true); /// ScalarTypeToBooleanCastKind - Returns the cast kind corresponding /// to the conversion from scalar type ScalarTy to the Boolean type. @@ -873,30 +968,10 @@ class Sema final : public SemaBase { /// Warn when implicitly casting 0 to nullptr. void diagnoseZeroToNullptrConversion(CastKind Kind, const Expr *E); - // ----- function effects --- - /// Warn when implicitly changing function effects. void diagnoseFunctionEffectConversion(QualType DstType, QualType SrcType, SourceLocation Loc); - /// Warn and return true if adding an effect to a set would create a conflict. - bool diagnoseConflictingFunctionEffect(const FunctionEffectsRef &FX, - const FunctionEffectWithCondition &EC, - SourceLocation NewAttrLoc); - - // Report a failure to merge function effects between declarations due to a - // conflict. - void - diagnoseFunctionEffectMergeConflicts(const FunctionEffectSet::Conflicts &Errs, - SourceLocation NewLoc, - SourceLocation OldLoc); - - /// Try to parse the conditional expression attached to an effect attribute - /// (e.g. 'nonblocking'). (c.f. Sema::ActOnNoexceptSpec). Return an empty - /// optional on error. - std::optional<FunctionEffectMode> - ActOnEffectExpression(Expr *CondExpr, StringRef AttributeName); - /// makeUnavailableInSystemHeader - There is an error in the current /// context. If we're still in a system header, and we can plausibly /// make the relevant declaration unavailable instead of erroring, do @@ -1056,6 +1131,12 @@ class Sema final : public SemaBase { std::function<TypeResult(StringRef, StringRef, SourceLocation)> ParseTypeFromStringCallback; + /* TO_UPSTREAM(BoundsSafety) ON */ + /// Callback to the parser to parse a type expressed as a string. + std::function<ExprResult(StringRef, StringRef, Decl *, SourceLocation)> + ParseBoundsAttributeArgFromStringCallback; + /* TO_UPSTREAM(BoundsSafety) OFF */ + /// VAListTagName - The declaration name corresponding to __va_list_tag. /// This is used as part of a hack to omit that class from ADL results. DeclarationName VAListTagName; @@ -1476,6 +1557,18 @@ class Sema final : public SemaBase { /// Source location for newly created implicit MSInheritanceAttrs SourceLocation ImplicitMSInheritanceAttrLoc; + /* TO_UPSTREAM(BoundsSafety) ON */ + /// Default BoundsSafety pointer attributes for ABI-visible pointers. Applied to + /// ABI-visible pointers which do not have explicit BoundsSafety attributes. Can + /// be controlled with \#pragma clang abi_ptr_attr set(*ATTR*). Defaults to + /// __single in user code and __unsafe_indexable in system headers. + BoundsSafetyPointerAttributes CurPointerAbi; + + /// \return true iff `-Wunsafe-buffer-usage` is enabled for `Loc` and Bounds + /// Safety attribute-only mode is on. + bool isCXXSafeBuffersBoundsSafetyInteropEnabledAt(SourceLocation Loc) const; + /* TO_UPSTREAM(BoundsSafety) OFF */ + /// pragma clang section kind enum PragmaClangSectionKind { PCSK_Invalid = 0, @@ -1830,6 +1923,14 @@ class Sema final : public SemaBase { /// Add [[gsl::Pointer]] attributes for std:: types. void inferGslPointerAttribute(TypedefNameDecl *TD); + LifetimeCaptureByAttr *ParseLifetimeCaptureByAttr(const ParsedAttr &AL, + StringRef ParamName); + // Processes the argument 'X' in [[clang::lifetime_capture_by(X)]]. Since 'X' + // can be the name of a function parameter, we need to parse the function + // declaration and rest of the parameters before processesing 'X'. Therefore + // do this lazily instead of processing while parsing the annotation itself. + void LazyProcessLifetimeCaptureByParams(FunctionDecl *FD); + /// Add _Nullable attributes for std:: types. void inferNullableClassAttribute(CXXRecordDecl *CRD); @@ -2075,8 +2176,16 @@ class Sema final : public SemaBase { /// Issue any -Wunguarded-availability warnings in \c FD void DiagnoseUnguardedAvailabilityViolations(Decl *FD); + void DiagnoseUnguardedFeatureAvailabilityViolations(Decl *FD); + void handleDelayedAvailabilityCheck(sema::DelayedDiagnostic &DD, Decl *Ctx); + void handleDelayedFeatureAvailabilityCheck(sema::DelayedDiagnostic &DD, + Decl *Ctx); + + void DiagnoseFeatureAvailabilityOfDecl(NamedDecl *D, + ArrayRef<SourceLocation> Locs); + /// Retrieve the current function, if any, that should be analyzed for /// potential availability violations. sema::FunctionScopeInfo *getCurFunctionAvailabilityContext(); @@ -2122,9 +2231,86 @@ class Sema final : public SemaBase { ExprResult Operand, SourceLocation RParenLoc); + /* TO_UPSTREAM(BoundsSafety) ON */ ExprResult BuildBuiltinBitCastExpr(SourceLocation KWLoc, TypeSourceInfo *TSI, Expr *Operand, SourceLocation RParenLoc); + ExprResult ActOnForgeBidiIndexable(SourceLocation KWLoc, Expr *Addr, + Expr *Size, SourceLocation RParenLoc); + + ExprResult ActOnForgeSingle(SourceLocation KWLoc, Expr *Addr, + SourceLocation RParenLoc); + + ExprResult ActOnForgeTerminatedBy(SourceLocation KWLoc, Expr *Addr, + Expr *Terminator, SourceLocation RParenLoc); + + ExprResult ActOnBoundsSafetyCall(ExprResult Call); + + ExprResult BuildForgePtrExpr(SourceLocation KWLoc, SourceLocation RParenLoc, + QualType ResultType, Expr *Addr, + Expr *Size = nullptr, Expr *Term = nullptr); + + /// Build a bounds check based on individual components. Each value of + /// Bounds is sequentially compared to be <= the next one. + ExprResult BuildBoundsCheckExpr(Expr *GuardedExpr, Expr *Cond, + ArrayRef<OpaqueValueExpr *> CommonExprs); + + ExprResult BuildPredefinedBoundsCheckExpr(Expr *GuardedExpr, + BoundsCheckKind Kind, + ArrayRef<Expr *> CheckArgs); + + /// Build an expression that resolves to the lower bound of the pointer + /// represented by SubExpr. + ExprResult BuildLowerBoundExpr(Expr *SubExpr, + SourceLocation BuiltinLoc, + SourceLocation RParenLoc = SourceLocation(), + bool RawPointer = false); + + ExprResult ActOnGetLowerBound(Expr *SubExpr, SourceLocation BuiltinLoc, + SourceLocation RParenLoc); + + /// Build an expression that resolves to the upper bound of the pointer + /// represented by SubExpr. + ExprResult BuildUpperBoundExpr(Expr *SubExpr, + SourceLocation BuiltinLoc, + SourceLocation RParenLoc = SourceLocation(), + bool RawPointer = false); + + ExprResult ActOnGetUpperBound(Expr *SubExpr, SourceLocation BuiltinLoc, + SourceLocation RParenLoc); + + ExprResult BuildMaterializeSequenceExpr(Expr *WrappedExpr, + ArrayRef<OpaqueValueExpr*> Values); + + ExprResult BuildTerminatedByToIndexableExpr(Expr *PointerExpr, + Expr *TerminatorExpr, + bool IncludeTerminator, + SourceLocation BuiltinLoc, + SourceLocation RParenLoc); + + ExprResult ActOnTerminatedByToIndexable(Expr *PointerExpr, + Expr *TerminatorExpr, + SourceLocation BuiltinLoc, + SourceLocation RParenLoc); + + ExprResult ActOnUnsafeTerminatedByToIndexable(Expr *PointerExpr, + Expr *TerminatorExpr, + SourceLocation BuiltinLoc, + SourceLocation RParenLoc); + + ExprResult BuildTerminatedByFromIndexableExpr(Expr *TerminatorExpr, + Expr *PointerExpr, + Expr *PointerToTerminatorExpr, + SourceLocation BuiltinLoc, + SourceLocation RParenLoc); + + ExprResult ActOnUnsafeTerminatedByFromIndexable(Expr *TerminatorExpr, + Expr *PointerExpr, + Expr *PointerToTerminatorExpr, + SourceLocation BuiltinLoc, + SourceLocation RParenLoc); + /* TO_UPSTREAM(BoundsSafety) OFF */ + // Checks that reinterpret casts don't have undefined behavior. void CheckCompatibleReinterpretCast(QualType SrcType, QualType DestType, bool IsDereference, SourceRange Range); @@ -2179,6 +2365,7 @@ class Sema final : public SemaBase { FAPK_Fixed, // values to format are fixed (no C-style variadic arguments) FAPK_Variadic, // values to format are passed as variadic arguments FAPK_VAList, // values to format are passed in a va_list + FAPK_Elsewhere, // values to format are not passed to this function }; // Used to grab the relevant information from a FormatAttr and a @@ -2189,12 +2376,15 @@ class Sema final : public SemaBase { FormatArgumentPassingKind ArgPassingKind; }; - /// Given a FunctionDecl's FormatAttr, attempts to populate the - /// FomatStringInfo parameter with the FormatAttr's correct format_idx and - /// firstDataArg. Returns true when the format fits the function and the - /// FormatStringInfo has been populated. - static bool getFormatStringInfo(const FormatAttr *Format, bool IsCXXMember, - bool IsVariadic, FormatStringInfo *FSI); + /// Given a function and its FormatAttr or FormatMatchesAttr info, attempts to + /// populate the FomatStringInfo parameter with the attribute's correct + /// format_idx and firstDataArg. Returns true when the format fits the + /// function and the FormatStringInfo has been populated. + static bool getFormatStringInfo(const Decl *Function, unsigned FormatIdx, + unsigned FirstArg, FormatStringInfo *FSI); + static bool getFormatStringInfo(unsigned FormatIdx, unsigned FirstArg, + bool IsCXXMember, bool IsVariadic, + FormatStringInfo *FSI); // Used by C++ template instantiation. ExprResult BuiltinShuffleVector(CallExpr *TheCall); @@ -2216,7 +2406,10 @@ class Sema final : public SemaBase { FST_OSLog, FST_Unknown }; + static StringRef GetFormatStringTypeName(FormatStringType FST); + static FormatStringType GetFormatStringType(StringRef FormatFlavor); static FormatStringType GetFormatStringType(const FormatAttr *Format); + static FormatStringType GetFormatStringType(const FormatMatchesAttr *Format); bool FormatStringHasSArg(const StringLiteral *FExpr); @@ -2358,6 +2551,25 @@ class Sema final : public SemaBase { bool IsMemberFunction, SourceLocation Loc, SourceRange Range, VariadicCallType CallType); + /// Verify that two format strings (as understood by attribute(format) and + /// attribute(format_matches) are compatible. If they are incompatible, + /// diagnostics are emitted with the assumption that \c + /// AuthoritativeFormatString is correct and + /// \c TestedFormatString is wrong. If \c FunctionCallArg is provided, + /// diagnostics will point to it and a note will refer to \c + /// TestedFormatString or \c AuthoritativeFormatString as appropriate. + bool + CheckFormatStringsCompatible(FormatStringType FST, + const StringLiteral *AuthoritativeFormatString, + const StringLiteral *TestedFormatString, + const Expr *FunctionCallArg = nullptr); + + /// Verify that one format string (as understood by attribute(format)) is + /// self-consistent; for instance, that it doesn't have multiple positional + /// arguments referring to the same argument in incompatible ways. Diagnose + /// if it isn't. + bool ValidateFormatString(FormatStringType FST, const StringLiteral *Str); + /// \brief Enforce the bounds of a TCB /// CheckTCBEnforcement - Enforces that every function in a named TCB only /// directly calls other functions in the same TCB as marked by the @@ -2559,11 +2771,17 @@ class Sema final : public SemaBase { VariadicCallType CallType, SourceLocation Loc, SourceRange Range, llvm::SmallBitVector &CheckedVarArgs); + bool CheckFormatString(const FormatMatchesAttr *Format, + ArrayRef<const Expr *> Args, bool IsCXXMember, + VariadicCallType CallType, SourceLocation Loc, + SourceRange Range, + llvm::SmallBitVector &CheckedVarArgs); bool CheckFormatArguments(ArrayRef<const Expr *> Args, - FormatArgumentPassingKind FAPK, unsigned format_idx, - unsigned firstDataArg, FormatStringType Type, - VariadicCallType CallType, SourceLocation Loc, - SourceRange range, + FormatArgumentPassingKind FAPK, + const StringLiteral *ReferenceFormatString, + unsigned format_idx, unsigned firstDataArg, + FormatStringType Type, VariadicCallType CallType, + SourceLocation Loc, SourceRange range, llvm::SmallBitVector &CheckedVarArgs); void CheckInfNaNFunction(const CallExpr *Call, const FunctionDecl *FDecl); @@ -2665,6 +2883,9 @@ class Sema final : public SemaBase { /// Adds an expression to the set of gathered misaligned members. void AddPotentialMisalignedMembers(Expr *E, RecordDecl *RD, ValueDecl *MD, CharUnits Alignment); + + // TO_UPSTREAM(BoundsSafety) + llvm::SmallPtrSet<const DeclaratorDecl *, 8> BoundsSafetyDeclsWithFixIts; ///@} // @@ -3456,6 +3677,10 @@ class Sema final : public SemaBase { TemplateIdAnnotation *TemplateId, bool IsMemberSpecialization); + void diagnosePointerAuthDisabled(SourceLocation loc, SourceRange range); + + bool checkPointerAuthEnabled(SourceLocation Loc, SourceRange Range); + bool checkConstantPointerAuthKey(Expr *keyExpr, unsigned &key); /// Diagnose function specifiers on a declaration of an identifier that @@ -4226,6 +4451,13 @@ class Sema final : public SemaBase { void deduceOpenCLAddressSpace(ValueDecl *decl); + /* TO_UPSTREAM(BoundsSafety) ON*/ + void deduceBoundsSafetyPointerTypes(ValueDecl *decl); + void deduceBoundsSafetyFunctionTypes(FunctionDecl *decl); + + QualType deduceCastPointerAttributes(QualType OrigTy, QualType OpTy); + /* TO_UPSTREAM(BoundsSafety) OFF*/ + /// Adjust the \c DeclContext for a function or variable that might be a /// function-local external declaration. static bool adjustContextForLocalExternDecl(DeclContext *&DC); @@ -4481,12 +4713,6 @@ class Sema final : public SemaBase { StringRef &Str, SourceLocation *ArgLocation = nullptr); - /// Determine if type T is a valid subject for a nonnull and similar - /// attributes. By default, we look through references (the behavior used by - /// nonnull), but if the second parameter is true, then we treat a reference - /// type as valid. - bool isValidPointerAttrType(QualType T, bool RefOkay = false); - /// AddAssumeAlignedAttr - Adds an assume_aligned attribute to a particular /// declaration. void AddAssumeAlignedAttr(Decl *D, const AttributeCommonInfo &CI, Expr *E, @@ -4500,6 +4726,11 @@ class Sema final : public SemaBase { bool CheckAttrTarget(const ParsedAttr &CurrAttr); bool CheckAttrNoArgs(const ParsedAttr &CurrAttr); + void copyFeatureAvailability(Decl *Dst, Decl *Src); + + void copyFeatureAvailabilityCheck(Decl *Dst, NamedDecl *Src, + bool Redeclaration = false); + AvailabilityAttr *mergeAvailabilityAttr( NamedDecl *D, const AttributeCommonInfo &CI, IdentifierInfo *Platform, bool Implicit, VersionTuple Introduced, VersionTuple Deprecated, @@ -4545,6 +4776,11 @@ class Sema final : public SemaBase { FormatAttr *mergeFormatAttr(Decl *D, const AttributeCommonInfo &CI, IdentifierInfo *Format, int FormatIdx, int FirstArg); + FormatMatchesAttr *mergeFormatMatchesAttr(Decl *D, + const AttributeCommonInfo &CI, + IdentifierInfo *Format, + int FormatIdx, + StringLiteral *FormatStr); /// AddAlignedAttr - Adds an aligned attribute to a particular declaration. void AddAlignedAttr(Decl *D, const AttributeCommonInfo &CI, Expr *E, @@ -6764,6 +7000,11 @@ class Sema final : public SemaBase { bool UseArgumentDependentLookup(const CXXScopeSpec &SS, const LookupResult &R, bool HasTrailingLParen); + /* TO_UPSTREAM(BoundsSafety) ON*/ + ExprResult InstantiateDeclRefField(Expr *BaseExpr, bool IsArrow, + Expr *FieldRef); + /* TO_UPSTREAM(BoundsSafety) OFF*/ + /// BuildQualifiedDeclarationNameExpr - Build a C++ qualified /// declaration name, generally during template instantiation. /// There's a large number of things which don't need to be done along @@ -7509,18 +7750,30 @@ class Sema final : public SemaBase { // DefaultFunctionArrayConversion - converts functions and arrays // to their respective pointers (C99 6.3.2.1). - ExprResult DefaultFunctionArrayConversion(Expr *E, bool Diagnose = true); + ExprResult DefaultFunctionArrayConversion( + Expr *E, bool Diagnose = true, + /* TO_UPSTREAM(BoundsSafety) ON */ + bool DiagnoseBoundsSafetyIncompleteArrayPromotion = true, + bool DisableFlexibleArrayPromotion = false); + /* TO_UPSTREAM(BoundsSafety) OFF */ // DefaultFunctionArrayLvalueConversion - converts functions and // arrays to their respective pointers and performs the // lvalue-to-rvalue conversion. - ExprResult DefaultFunctionArrayLvalueConversion(Expr *E, - bool Diagnose = true); + ExprResult DefaultFunctionArrayLvalueConversion( + Expr *E, bool Diagnose = true, + /* TO_UPSTREAM(BoundsSafety) ON */ + bool DiagnoseBoundsSafetyIncompleteArrayPromotion = true, + bool DisableFlexibleArrayPromotion = false); + /* TO_UPSTREAM(BoundsSafety) ON */ // DefaultLvalueConversion - performs lvalue-to-rvalue conversion on // the operand. This function is a no-op if the operand has a function type // or an array type. - ExprResult DefaultLvalueConversion(Expr *E); + ExprResult + DefaultLvalueConversion(Expr *E, + // TO_UPSTREAM(BoundsSafety) + bool DisableFlexibleArrayPromotion = false); // DefaultArgumentPromotion (C99 6.5.2.2p6). Used for function calls that // do not have a prototype. Integer promotions are performed on each @@ -7590,6 +7843,66 @@ class Sema final : public SemaBase { /// accept as an extension. IntToPointer, + /* TO_UPSTREAM(BoundsSafety) ON */ + /// IncompatibleIntToSafePointer - The assignment converts an int to a + /// -fbounds-safety safe pointer, which is not permitted by -fbounds-safety. + IncompatibleIntToSafePointer, + + /// IncompatibleUnsafeToSafePointer - The assignment converts an unsafe + /// pointer to a safe pointer (single or counted_by), which -fbounds-safety + /// doesn't allow. + IncompatibleUnsafeToSafePointer, + + /// IncompatibleUnsafeToIndexablePointer - The assignment converts an unsafe + /// pointer to an indexable pointer (indexable or bidi_indexable), + /// which -fbounds-safety doesn't allow and may not know how to create an upper + /// bound for. + IncompatibleUnsafeToIndexablePointer, + + // FIXME: Turn it into an error (rdar://85587619) + /// IncompleteSingleToIndexablePointer - The assignment converts an single + /// pointer with opaque element type to an indexable pointer, for which + /// -fbounds-safety + /// doesn't know how to create an upper bound. + IncompleteSingleToIndexablePointer, + + // CompatibleSingleToIndexablePointer - The assignment converts a single + // pointer to an explicitly indexable pointer. This is allowed but is likely + // a mistake. + CompatibleSingleToExplicitIndexablePointer, + + /// IncompatibleStringLiteralToValueTerminatedPointer - The assignment of a + /// string literal to value-terminated pointer with a terminator different + /// than NUL. + IncompatibleStringLiteralToValueTerminatedPointer, + + /// IncompatibleValueTerminatedTerminators - The assignment is between + /// value terminated pointers with incompatible terminators. + IncompatibleValueTerminatedTerminators, + + /// IncompatibleValueTerminatedToNonValueTerminatedPointer - The assignment + /// of a value-terminated pointer to a non-value-terminated pointer. + /// BoundsSafety doesn't allow it, __terminated_by_to_indexable() should be + /// used instead. + IncompatibleValueTerminatedToNonValueTerminatedPointer, + + /// IncompatibleNonValueTerminatedToValueTerminatedPointer - The assignment + /// of a non-value-terminated pointer to a value-terminated pointer. + /// BoundsSafety doesn't allow it, __unsafe_terminated_by_from_wide() should + /// be used instead. + IncompatibleNonValueTerminatedToValueTerminatedPointer, + + /// IncompatibleNestedValueTerminatedToNonValueTerminatedPointer - The + /// assignment is between two nested pointer types, where at a nested + /// level the source is value-terminated type, but the destination isn't. + IncompatibleNestedValueTerminatedToNonValueTerminatedPointer, + + /// IncompatibleNestedNonValueTerminatedToValueTerminatedPointer - The + /// assignment is between two nested pointer types, where at a nested + /// level the destination is value-terminated type, but the source isn't. + IncompatibleNestedNonValueTerminatedToValueTerminatedPointer, + /* TO_UPSTREAM(BoundsSafety) OFF */ + /// FunctionVoidPointer - The assignment is between a function pointer and /// void*, which the standard doesn't allow, but we accept as an extension. FunctionVoidPointer, @@ -7636,6 +7949,18 @@ class Sema final : public SemaBase { /// extension. IncompatibleNestedPointerQualifiers, + /* TO_UPSTREAM(BoundsSafety) ON */ + /// IncompatibleNestedBoundsSafetyPointerAttributes - The assignment is + /// between two nested pointer types with different -fbounds-safety pointer + /// attributes. + IncompatibleNestedBoundsSafetyPointerAttributes, + + /// IncompatibleBoundsSafetyFunctionPointer - The assignment is + /// between two function pointer types with incompatible -fbounds-safety pointer + /// attributes. + IncompatibleBoundsSafetyFunctionPointer, + /* TO_UPSTREAM(BoundsSafety) OFF */ + /// IncompatibleVectors - The assignment is between two vector types that /// have the same size, which we accept as an extension. IncompatibleVectors, @@ -7662,13 +7987,78 @@ class Sema final : public SemaBase { Incompatible }; + /* TO_UPSTREAM(BoundsSafety) ON*/ + /// @brief Check whether it's ABI safe to convert between two types that would + /// otherwise be forbidden by -fbounds-safety. Returns false if \c ConvTy ... + /// - is not an incompatibility + /// - is not a -fbounds-safety specific incompatibility + /// - represents an incompatible conversion between pointers of different + /// widths + /// Otherwise returns true. + bool isCompatibleBoundsUnsafeAssignment(AssignConvertType ConvTy) const; + + /// @brief Check whether the conditions justify making an exception to + /// -fbounds-safety safety rules in system headers (since the threshold to fixing + /// errors is much higher there). In short the error should be in a system + /// header that hasn't (fully) adopted -fbounds-safety. NB: + /// If using this function to make the actual decision of ignoring the type + /// clash then \c isCompatibleBoundsUnsafeAssignment should be called first. + /// If using this function after type checking (e.g. to determine whether to + /// skip bounds checks) the only remaining cases where it returns true are + /// then ones that have previously passed \c + /// isCompatibleBoundsUnsafeAssignment during type checking, so that need not + /// be called again. + /// @param DestTy Type declared at LHS of variable assignment or function to + /// be called + /// @param SourceValue RHS of variable assignment, or function argument + /// @param AssignmentLoc Location of the actual assignment or function call + /// @return true when the -fbounds-safety rules can be skipped + bool allowBoundsUnsafePointerAssignment(const QualType DestTy, + const Expr *SourceValue, + SourceLocation AssignmentLoc) const; + + /// @brief Helper function wrapping allowBoundsUnsafePointerAssignment. + /// Only for function calls, not for assignments or returns. + bool allowBoundsUnsafeFunctionArg(const CallExpr *CallE, + unsigned ParamIdx) const; + + /// @brief Check whether the conditions justify making an exception to + /// -fbounds-safety safety rules in system headers (since the threshold to fixing + /// errors is much higher there). In short the error should be in a system + /// header that hasn't (fully) adopted -fbounds-safety. NB: + /// If determining whether a pointer assignment should be allowed, use \c + /// allowBoundsUnsafePointerAssignment (which will call this function, in + /// addition to other checks). This function can be called directly for + /// determining whether unsafe count parameter assignments should be allowed. + /// @param AssignmentLoc Location of an expression assigning a new value to + /// a bounds safe variable. + bool allowBoundsUnsafeAssignment(SourceLocation AssignmentLoc) const; + + void TryFixAssigningNullTerminatedToImplicitBidiIndexablePtr( + const ValueDecl *Assignee, Expr *SrcExpr, QualType DstType, AssignmentAction Action); + void + TryFixAssigningImplicitBidiIndexableToNullTerminatedPtr(Expr *SrcExpr, + QualType DstType); + void TryFixAssigningBidiIndexableExprToNullTerminated(Expr *SrcExpr, + QualType DstType); + void TryFixAssigningNullTerminatedToBidiIndexableExpr(Expr *SrcExpr, + QualType DstType); + void TryFixAssigningSinglePtrToNullTerminated(Expr *SrcExpr, QualType SrcType, + QualType DstType); + void TryFixAssigningConstArrayToNullTerminated(const Expr *SrcExpr, + QualType SrcType, + QualType DstType); + /* TO_UPSTREAM(BoundsSafety) OFF*/ + /// DiagnoseAssignmentResult - Emit a diagnostic, if required, for the /// assignment conversion type specified by ConvTy. This returns true if the /// conversion was invalid or false if the conversion was accepted. bool DiagnoseAssignmentResult(AssignConvertType ConvTy, SourceLocation Loc, QualType DstType, QualType SrcType, Expr *SrcExpr, AssignmentAction Action, - bool *Complained = nullptr); + bool *Complained = nullptr, + // TO_UPSTREAM(BoundsSafety) + const ValueDecl *Assignee = nullptr); /// CheckAssignmentConstraints - Perform type checking for assignment, /// argument passing, variable initialization, and function return values. @@ -7707,6 +8097,25 @@ class Sema final : public SemaBase { AssignConvertType CheckTransparentUnionArgumentConstraints(QualType ArgType, ExprResult &RHS); + /* TO_UPSTREAM(BoundsSafety) ON*/ + bool CheckDynamicBoundVariableEscape(QualType LHSType, Expr *RHSExp); + + using DependentValuesMap = + llvm::DenseMap<const ValueDecl *, std::pair<Expr *, /*level*/ unsigned>>; + bool CheckDynamicCountSizeForAssignment(QualType LHSTy, Expr *RHSExpr, + AssignmentAction Action, + SourceLocation Loc, + const Twine &Designator, + DependentValuesMap &DependentValues, + Expr *LHSMemberBase = nullptr); + + void DiagnoseSingleToWideLosingBounds(QualType LHSType, QualType RHSType, + const Expr *RHSExp); + bool DiagnoseDynamicCountVarZeroInit(VarDecl *VD); + Sema::AssignConvertType + CheckValueTerminatedAssignmentConstraints(QualType LHSType, Expr *RHSExpr); + /* TO_UPSTREAM(BoundsSafety) OFF*/ + /// the following "Check" methods will return a valid/converted QualType /// or a null QualType (indicating an error diagnostic was issued). @@ -7731,6 +8140,9 @@ class Sema final : public SemaBase { BinaryOperatorKind Opc, QualType *CompLHSTy = nullptr); QualType CheckSubtractionOperands( // C99 6.5.6 ExprResult &LHS, ExprResult &RHS, SourceLocation Loc, + /*TO_UPSTREAM(BoundsSafety) ON*/ + BinaryOperatorKind Opc, + /*TO_UPSTREAM(BoundsSafety) OFF*/ QualType *CompLHSTy = nullptr); QualType CheckShiftOperands( // C99 6.5.7 ExprResult &LHS, ExprResult &RHS, SourceLocation Loc, @@ -12149,6 +12561,10 @@ class Sema final : public SemaBase { /// \param A the argument type. bool isSameOrCompatibleFunctionType(QualType Param, QualType Arg); + /* TO_UPSTREAM(BoundsSafety) ON*/ + void CheckValueTerminatedUninitialized(const VarDecl *VD); + /* TO_UPSTREAM(BoundsSafety) OFF*/ + /// Allocate a TemplateArgumentLoc where all locations have /// been initialized to the given location. /// @@ -14577,8 +14993,8 @@ class Sema final : public SemaBase { /// /// \returns A suitable pointer type, if there are no /// errors. Otherwise, returns a NULL type. - QualType BuildPointerType(QualType T, SourceLocation Loc, - DeclarationName Entity); + QualType BuildPointerType(QualType T, BoundsSafetyPointerAttributes A, + SourceLocation Loc, DeclarationName Entity); /// Build a reference type. /// @@ -14881,6 +15297,54 @@ class Sema final : public SemaBase { QualType BuiltinChangeSignedness(QualType BaseType, UTTKind UKind, SourceLocation Loc); + /* TO_UPSTREAM(BoundsSafety) ON*/ + // Lifetime check for variables with function scope. + enum class LifetimeCheckKind { + // Do not check. + None, + + // Dependent variable must be a non-static local variable. + NonStaticLocal, + + // Dependent variable must be a static local variable. + StaticLocal, + + // Dependent variable must be an extern variable including extern local + // variable declaration. + Extern, + + // Dependent variable must be a private extern variable. + PrivateExtern, + + // Dependent variable must be a static global variable. + StaticGlobal, + + // Dependent variable must be a global variable definition. + // Technically, this doesn't tell the lifetime difference compared to + // Extern, + // but we keep this kind to track globals defined in this translation unit. + GlobalDefinition, + }; + + static LifetimeCheckKind getLifetimeCheckKind(const VarDecl *VD); + + /// Attach \c DependerDeclsAttr to declarations referred to by \c counted_by + /// or \c sized_by attributes. This doesn't apply to \c ended_by because it + /// adds a type sugar (i.e., \c DynamicRangePointerType) instead for its + /// dependent declaration. + void AttachDependerDeclsAttr(ValueDecl *NewDepender, + const CountAttributedType *NewDependerCountTy, + unsigned Level); + + QualType BuildCountAttributedType(QualType PointerTy, Expr *CountExpr, + bool CountInBytes = false, + bool OrNull = false, + bool ScopeCheck = false); + + QualType BuildDynamicRangePointerType(QualType PointerTy, Expr *StartPtr, + Expr *EndPtr, bool ScopeCheck = false); + /* TO_UPSTREAM(BoundsSafety) OFF*/ + /// Ensure that the type T is a literal type. /// /// This routine checks whether the type @p T is a literal type. If @p T is an @@ -15002,6 +15466,12 @@ class Sema final : public SemaBase { return hasAcceptableDefinition(D, &Hidden, Kind); } + /// Try to parse the conditional expression attached to an effect attribute + /// (e.g. 'nonblocking'). (c.f. Sema::ActOnNoexceptSpec). Return an empty + /// optional on error. + std::optional<FunctionEffectMode> + ActOnEffectExpression(Expr *CondExpr, StringRef AttributeName); + private: /// The implementation of RequireCompleteType bool RequireCompleteTypeImpl(SourceLocation Loc, QualType T, @@ -15088,6 +15558,250 @@ class Sema final : public SemaBase { llvm::SmallVectorImpl<TypeCoupledDeclRefInfo> &Decls, bool CountInBytes, bool OrNull); + /* TO_UPSTREAM(BoundsSafety) ON */ + void applyPtrCountedByEndedByAttr(Decl *D, unsigned Level, + AttributeCommonInfo::Kind Kind, + Expr *AttrArg, SourceLocation Loc, + SourceRange Range, StringRef DiagName, + bool OriginatesInAPINotes = false, + bool InInstantiatedTemplate = false); + + /// Perform Bounds Safety Semantic checks for assigning to a `__counted_by` or + /// `__counted_by_or_null` pointer type \param LHSTy. + /// + /// \param LHSTy The type being assigned to. Checks will only be performed if + /// the type is a `counted_by` or `counted_by_or_null ` pointer. + /// \param RHSExpr The expression being assigned from. + /// \param Action The type assignment being performed + /// \param Loc The SourceLocation to use for error diagnostics + /// \param ComputeAssignee If provided this function will be called before + /// emitting a diagnostic. The function should return the name of + /// entity being assigned to or an empty string if this cannot be + /// determined. + /// + /// \returns True iff no diagnostic where emitted, false otherwise. + bool BoundsSafetyCheckAssignmentToCountAttrPtr( + QualType LHSTy, Expr *RHSExpr, Sema::AssignmentAction Action, + SourceLocation Loc, + std::function<std::string()> ComputeAssignee = nullptr); + + /// Perform Checks for assigning to a `__counted_by` or + /// `__counted_by_or_null` pointer type \param LHSTy where the pointee type + /// is incomplete which is invalid. + /// + /// \param LHSTy The type being assigned to. Checks will only be performed if + /// the type is a `counted_by` or `counted_by_or_null ` pointer. + /// \param RHSExpr The expression being assigned from. + /// \param Action The type assignment being performed + /// \param Loc The SourceLocation to use for error diagnostics + /// \param ComputeAssignee If provided this function will be called before + /// emitting a diagnostic. The function should return the name of + /// entity being assigned to or an empty string if this cannot be + /// determined. + /// + /// \returns True iff no diagnostic where emitted, false otherwise. + bool BoundsSafetyCheckAssignmentToCountAttrPtrWithIncompletePointeeTy( + QualType LHSTy, Expr *RHSExpr, Sema::AssignmentAction Action, + SourceLocation Loc, std::function<std::string()> ComputeAssignee); + + /// Perform Bounds Safety Semantic checks for initializing a Bounds Safety + /// pointer. + /// + /// \param Entity The entity being initialized + /// \param Kind The kind of initialization being performed + /// \param Action The type assignment being performed + /// \param LHSTy The type being assigned to. Checks will only be performed if + /// the type is a `counted_by` or `counted_by_or_null ` pointer. + /// \param RHSExpr The expression being used for initialization. + /// + /// \returns True iff no diagnostic where emitted, false otherwise. + bool BoundsSafetyCheckInitialization(const InitializedEntity &Entity, + const InitializationKind &Kind, + Sema::AssignmentAction Action, + QualType LHSType, Expr *RHSExpr); + + /// Perform Bounds Safety semantic checks on function parameters on a function + /// definition. This only performs checks that can be made by looking at + /// \param PVD in isolation (i.e. not looking at other parameters in the + /// function definition). + /// + /// \returns True iff no diagnostic where emitted, false otherwise. + bool BoundsSafetyCheckParamForFunctionDef(const ParmVarDecl *PVD); + + /// Perform Bounds Safety semantic check for CallExpr \param Call. + /// + /// \param FDecl For direct calls this should be the Function being called + /// by \param Call. This can be null. + /// \param Call The CallExpr to be checked. This cannot be null. + /// \param ProtoType For indirect calls this should be the FunctionProtoType + /// used to type check the call. This can be null. + /// + /// \returns True iff no diagnostic where emitted, false otherwise. + bool BoundsSafetyCheckResolvedCall(FunctionDecl *FDecl, CallExpr *Call, + const FunctionProtoType *ProtoType); + + /// Perform Bounds Safety semantic checks on a function definition's return + /// type. + /// + /// \param FD The FunctionDecl whose return type will be checked. + /// + /// \returns True iff no diagnostic where emitted, false otherwise. + bool BoundsSafetyCheckReturnTyForFunctionDef(FunctionDecl *FD); + + /// Perform Bounds Safety semantic checks for uses of invalid uses counted_by + /// or counted_by_or_null pointers in \param E. + /// + /// \param E the expression to check + /// + /// \returns True iff no diagnostic where emitted, false otherwise. + bool BoundsSafetyCheckUseOfCountAttrPtr(Expr *E); + + /// Perform Bounds Safety semantic checks on variable declaration \param VD. + /// + /// \param VD The VarDecl to check + /// \param CheckTentativeDefinitions If false and \param VD is a tentative + /// definition then checking is skipping and + /// the function returns true. If true then + /// checking of tentative definitions will + /// not be skipped. + /// + /// \returns True iff no diagnostic where emitted, false otherwise. + bool BoundsSafetyCheckVarDecl(const VarDecl *VD, + bool CheckTentativeDefinitions); + + // TODO: This can be moved back into SemaExpr.cpp as a static function once + // support for -fno-bounds-safety-bringup-missing-checks=indirect_count_update + // is removed (rdar://135833598). + bool BoundsSafetyCheckCountAttributedTypeHasConstantCountForAssignmentOp( + const CountAttributedType *CATTy, Expr *Operand, + std::variant<bool, BinaryOperatorKind> OpInfo); + + + /// Used to record or retrieve if a VarDecl/FieldDecl has a BoundsSafety FixIt + /// emitted on it. The primary use case for this is preventing emitting + /// multiple FixIts on the same VarDecl/FieldDecl which can result in invalid + /// code. + /// + /// Note: This is not linked to the DiagnosticEngine in anyway. So callers + /// are required to manually maintain this information. + /// + /// \param DD - the VarDecl/FieldDecl to record or retrieve information about. + /// \param Set - If `Set` is `false` then this is a retrieval operation. The + /// method will return `true` if the `DD` already had a FixIt emitted and + /// `false` otherwise.. If `Set` is `true` then this is method records that + /// the `DD` had a FixIt emitted against it. The returned value will + /// always be `true` in this case. + /// + /// \returns `true` iff it was recorded that a FixIt was emitted for the + /// VarDecl `VD`. + /// + bool BoundsSafetyFixItWasEmittedFor(const DeclaratorDecl *DD, + bool Set = false); + /* TO_UPSTREAM(BoundsSafety) OFF*/ + + + ///@} + + // + // + // ------------------------------------------------------------------------- + // + // + + /// \name Function Effects + /// Implementations are in SemaFunctionEffects.cpp + ///@{ +public: + struct FunctionEffectDiff { + enum class Kind { Added, Removed, ConditionMismatch }; + + FunctionEffect::Kind EffectKind; + Kind DiffKind; + std::optional<FunctionEffectWithCondition> + Old; // Invalid when 'Kind' is 'Added'. + std::optional<FunctionEffectWithCondition> + New; // Invalid when 'Kind' is 'Removed'. + + StringRef effectName() const { + if (Old) + return Old.value().Effect.name(); + return New.value().Effect.name(); + } + + /// Describes the result of effects differing between a base class's virtual + /// method and an overriding method in a subclass. + enum class OverrideResult { + NoAction, + Warn, + Merge // Merge missing effect from base to derived. + }; + + /// Return true if adding or removing the effect as part of a type + /// conversion should generate a diagnostic. + bool shouldDiagnoseConversion(QualType SrcType, + const FunctionEffectsRef &SrcFX, + QualType DstType, + const FunctionEffectsRef &DstFX) const; + + /// Return true if adding or removing the effect in a redeclaration should + /// generate a diagnostic. + bool shouldDiagnoseRedeclaration(const FunctionDecl &OldFunction, + const FunctionEffectsRef &OldFX, + const FunctionDecl &NewFunction, + const FunctionEffectsRef &NewFX) const; + + /// Return true if adding or removing the effect in a C++ virtual method + /// override should generate a diagnostic. + OverrideResult shouldDiagnoseMethodOverride( + const CXXMethodDecl &OldMethod, const FunctionEffectsRef &OldFX, + const CXXMethodDecl &NewMethod, const FunctionEffectsRef &NewFX) const; + }; + + struct FunctionEffectDiffVector : public SmallVector<FunctionEffectDiff> { + /// Caller should short-circuit by checking for equality first. + FunctionEffectDiffVector(const FunctionEffectsRef &Old, + const FunctionEffectsRef &New); + }; + + /// All functions/lambdas/blocks which have bodies and which have a non-empty + /// FunctionEffectsRef to be verified. + SmallVector<const Decl *> DeclsWithEffectsToVerify; + + /// The union of all effects present on DeclsWithEffectsToVerify. Conditions + /// are all null. + FunctionEffectKindSet AllEffectsToVerify; + +public: + /// Warn and return true if adding a function effect to a set would create a + /// conflict. + bool diagnoseConflictingFunctionEffect(const FunctionEffectsRef &FX, + const FunctionEffectWithCondition &EC, + SourceLocation NewAttrLoc); + + // Report a failure to merge function effects between declarations due to a + // conflict. + void + diagnoseFunctionEffectMergeConflicts(const FunctionEffectSet::Conflicts &Errs, + SourceLocation NewLoc, + SourceLocation OldLoc); + + /// Inline checks from the start of maybeAddDeclWithEffects, to + /// minimize performance impact on code not using effects. + template <class FuncOrBlockDecl> + void maybeAddDeclWithEffects(FuncOrBlockDecl *D) { + if (Context.hasAnyFunctionEffects()) + if (FunctionEffectsRef FX = D->getFunctionEffects(); !FX.empty()) + maybeAddDeclWithEffects(D, FX); + } + + /// Potentially add a FunctionDecl or BlockDecl to DeclsWithEffectsToVerify. + void maybeAddDeclWithEffects(const Decl *D, const FunctionEffectsRef &FX); + + /// Unconditionally add a Decl to DeclsWithEfffectsToVerify. + void addDeclWithEffects(const Decl *D, const FunctionEffectsRef &FX); + + void performFunctionEffectAnalysis(TranslationUnitDecl *TU); + ///@} }; diff --git a/clang/include/clang/Sema/SemaFixItUtils.h b/clang/include/clang/Sema/SemaFixItUtils.h index df9bc42976943..da1ecb901289e 100644 --- a/clang/include/clang/Sema/SemaFixItUtils.h +++ b/clang/include/clang/Sema/SemaFixItUtils.h @@ -86,5 +86,80 @@ struct ConversionFixItGenerator { } }; +/* TO_UPSTREAM(BoundsSafety) ON */ +class BoundsSafetyFixItUtils { +public: + /// Try to find the SourceLocation where a bounds-safety attribute could + /// be inserted on a pointer. Note this method does not check if there is an + /// attribute already present. Clients should handle this themselves. + /// + /// + /// \param TL - TypeLoc that the attribute could be added to + /// \param S - Sema instance + /// + /// \return a tuple of the SourceLocation where insertion could be performed + /// and a boolean that is true iff a space should be inserted after the + /// inserted attribute. If the returned SourceLocation is invalid no insertion + /// point could be found. + static std::tuple<SourceLocation, bool> + FindPointerAttrInsertPoint(const TypeLoc TL, Sema &S); + + /// Try to create a FixItHint that adds the provided bounds-safety attribute + /// as a new attribute to the variable declaration. Note this method does + /// not check for existing attributes. Clients should have this themselves. + /// + /// \param VD - Variable Declaration to suggest FixIt for. This Variable + /// should have a pointer type. + /// \param Attribute - The string representation of the Attribute to add. + /// \param S - Sema instance + /// + /// \return A FixIt hint that adds the supplied Attribute to the type + /// specifier on the variable declaration. If creating the FixIt fails the + /// returned FixIt will be invalid. + static FixItHint + CreateAnnotatePointerDeclFixIt(const VarDecl *VD, + const llvm::StringRef Attribute, Sema &S); + + /// Try to create a FixItHint that adds the provided bounds-safety attribute + /// as a new attribute to the field declaration. Note this method does + /// not check for existing attributes. Clients should have this themselves. + /// + /// \param VD - Field Declaration to suggest FixIt for. This field + /// should have a pointer type. + /// \param Attribute - The string representation of the Attribute to add. + /// \param S - Sema instance + /// + /// \return A FixIt hint that adds the supplied Attribute to the type + /// specifier on the field declaration. If creating the FixIt fails the + /// returned FixIt will be invalid. + static FixItHint + CreateAnnotatePointerDeclFixIt(const FieldDecl *FD, + const llvm::StringRef Attribute, Sema &S); + + /// Try to create a FixItHint that adds the provided bounds-safety attribute + /// as a new attribute to the variable declaration and all of its previous + /// declarations. Note only global variables may have previous declarations. + /// Note this method does not check for existing attributes. + /// Clients should have this themselves. + /// + /// \param VD - Variable Declaration to suggest FixIt for. This Variable + /// should have a pointer type. + /// \param Attribute - The string representation of the Attribute to add. + /// \param S - Sema instance + /// \param FixIts - A SmallVector of (FixIts hint, VarDecl) tuples. Every + /// valid FixHit that can be created will be added to this SmallVector. Each + /// FixIt hint adds the supplied Attribute to the type specifier on each of + /// the variable declarations. + static void CreateAnnotateAllPointerDeclsFixIts( + const VarDecl *VD, const llvm::StringRef Attribute, Sema &S, + llvm::SmallVectorImpl<std::tuple<FixItHint, const DeclaratorDecl *>> + &FixIts); + +private: + static FixItHint CreateAnnotateVarDeclOrFieldDeclFixIt( + const DeclaratorDecl *VD, const llvm::StringRef Attribute, Sema &S); +}; +/* TO_UPSTREAM(BoundsSafety) OFF */ + } // endof namespace clang #endif diff --git a/clang/include/clang/Sema/SemaObjC.h b/clang/include/clang/Sema/SemaObjC.h index 07c3c1a06be16..9b3da1507aa3a 100644 --- a/clang/include/clang/Sema/SemaObjC.h +++ b/clang/include/clang/Sema/SemaObjC.h @@ -363,6 +363,10 @@ class SemaObjC : public SemaBase { ParsedAttributesView ArgAttrs; }; + ParmVarDecl *ActOnMethodParmDeclaration(Scope *S, ObjCArgInfo &ArgInfo, + int ParamIndex, + bool MethodDefinition); + Decl *ActOnMethodDeclaration( Scope *S, SourceLocation BeginLoc, // location of the + or -. @@ -371,7 +375,7 @@ class SemaObjC : public SemaBase { ArrayRef<SourceLocation> SelectorLocs, Selector Sel, // optional arguments. The number of types/arguments is obtained // from the Sel.getNumArgs(). - ObjCArgInfo *ArgInfo, DeclaratorChunk::ParamInfo *CParamInfo, + ParmVarDecl **ArgInfo, DeclaratorChunk::ParamInfo *CParamInfo, unsigned CNumArgs, // c-style args const ParsedAttributesView &AttrList, tok::ObjCKeywordKind MethodImplKind, bool isVariadic, bool MethodDefinition); diff --git a/clang/include/clang/Sema/SemaSwift.h b/clang/include/clang/Sema/SemaSwift.h index a5561d756affd..4a1ac3ffb1839 100644 --- a/clang/include/clang/Sema/SemaSwift.h +++ b/clang/include/clang/Sema/SemaSwift.h @@ -39,6 +39,7 @@ class SemaSwift : public SemaBase { void handleAsyncError(Decl *D, const ParsedAttr &AL); void handleName(Decl *D, const ParsedAttr &AL); void handleAsyncName(Decl *D, const ParsedAttr &AL); + void handleTransparentStepping(Decl *D, const ParsedAttr &AL); void handleNewType(Decl *D, const ParsedAttr &AL); /// Do a check to make sure \p Name looks like a legal argument for the diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h index 5dd0ba33f8a9c..2c1b17f4a70ae 100644 --- a/clang/include/clang/Serialization/ASTBitCodes.h +++ b/clang/include/clang/Serialization/ASTBitCodes.h @@ -44,7 +44,7 @@ namespace serialization { /// Version 4 of AST files also requires that the version control branch and /// revision match exactly, since there is no backward compatibility of /// AST files at this time. -const unsigned VERSION_MAJOR = 31; +const unsigned VERSION_MAJOR = 34; /// AST file minor version number supported by this version of /// Clang. @@ -54,7 +54,7 @@ const unsigned VERSION_MAJOR = 31; /// for the previous version could still support reading the new /// version by ignoring new kinds of subblocks), this number /// should be increased. -const unsigned VERSION_MINOR = 1; +const unsigned VERSION_MINOR = 0; /// An ID number that refers to an identifier in an AST file. /// @@ -350,9 +350,8 @@ enum ControlRecordTypes { /// and information about the compiler used to build this AST file. METADATA = 1, - /// Record code for the list of other AST files imported by - /// this AST file. - IMPORTS, + /// Record code for another AST file imported by this AST file. + IMPORT, /// Record code for the original file that was used to /// generate the AST file, including both its file ID and its @@ -376,6 +375,17 @@ enum ControlRecordTypes { /// Record code for the module build directory. MODULE_DIRECTORY, + + /// Record code for the (optional) \c ActionCache key for this module. + MODULE_CACHE_KEY, + + /// Record code for the (optional) CAS filesystem root ID for implicit modules + /// built with the dependency scanner. + CASFS_ROOT_ID, + + /// Record code for the (optional) include-tree ID for implicit modules + /// built with the dependency scanner. + CAS_INCLUDE_TREE_ID, }; /// Record types that occur within the options block inside @@ -392,9 +402,6 @@ enum OptionsRecordTypes { /// Record code for the target options table. TARGET_OPTIONS, - /// Record code for the filesystem options table. - FILE_SYSTEM_OPTIONS, - /// Record code for the headers search options table. HEADER_SEARCH_OPTIONS, @@ -416,6 +423,9 @@ enum UnhashedControlBlockRecordTypes { /// Record code for the headers search paths. HEADER_SEARCH_PATHS, + /// Record code for the filesystem options table. + FILE_SYSTEM_OPTIONS, + /// Record code for \#pragma diagnostic mappings. DIAG_PRAGMA_MAPPINGS, @@ -721,6 +731,13 @@ enum ASTRecordTypes { /// Record code for \#pragma clang unsafe_buffer_usage begin/end PP_UNSAFE_BUFFER_USAGE = 69, + + /// Record code for vtables to emit. + VTABLES_TO_EMIT = 70, + + /// Record code for Sema's vector of functions/blocks with effects to + /// be verified. + DECLS_WITH_EFFECTS_TO_VERIFY = 72, }; /// Record types used within a source manager block. @@ -1878,6 +1895,19 @@ enum StmtCode { // OpenCL EXPR_ASTYPE, // AsTypeExpr + /* TO_UPSTREAM(BoundsSafety) ON */ + // BoundsSafety + EXPR_ASSUMPTION, // AssumptionExpr + EXPR_BOUNDS_SAFETY_POINTER_PROMOTION, // BoundsSafetyPointerPromotionExpr + EXPR_FORGE_PTR, // ForgePtrExpr + EXPR_GET_BOUND, // GetBoundExpr + EXPR_PREDEFINED_BOUNDS_CHECK, // PredefinedBoundsCheckExpr + EXPR_BOUNDS_CHECK, // BoundsCheckExpr + EXPR_MATERIALIZE_SEQUENCE, // MaterializeSequenceExpr + EXPR_TERMINATED_BY_TO_INDEXABLE, // TerminatedByToIndexableExpr + EXPR_TERMINATED_BY_FROM_INDEXABLE, // TerminatedByFromIndexableExpr + /* TO_UPSTREAM(BoundsSafety) OFF */ + // Microsoft EXPR_CXX_PROPERTY_REF_EXPR, // MSPropertyRefExpr EXPR_CXX_PROPERTY_SUBSCRIPT_EXPR, // MSPropertySubscriptExpr diff --git a/clang/include/clang/Serialization/ASTReader.h b/clang/include/clang/Serialization/ASTReader.h index 76e51ac7ab979..1dcfa22c437b8 100644 --- a/clang/include/clang/Serialization/ASTReader.h +++ b/clang/include/clang/Serialization/ASTReader.h @@ -49,6 +49,7 @@ #include "llvm/ADT/iterator_range.h" #include "llvm/Bitstream/BitstreamReader.h" #include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/SaveAndRestore.h" #include "llvm/Support/Timer.h" #include "llvm/Support/VersionTuple.h" #include <cassert> @@ -240,6 +241,26 @@ class ASTReaderListener { /// AST file imported by this AST file. virtual void visitImport(StringRef ModuleName, StringRef Filename) {} + /// Called for each CAS filesystem root ID. + /// + /// \returns true to indicate \p RootID is invalid, or false otherwise. + virtual bool readCASFileSystemRootID(StringRef RootID, bool Complain) { + return false; + } + + /// Called for each CAS include-tree root ID. + /// + /// \returns true to indicate \p RootID is invalid, or false otherwise. + virtual bool readIncludeTreeID(StringRef ID, bool Complain) { return false; } + + /// Called for each module cache key. + /// + /// \returns true to indicate the key cannot be loaded. + virtual bool readModuleCacheKey(StringRef ModuleName, StringRef Filename, + StringRef CacheKey) { + return false; + } + /// Indicates that a particular module file extension has been read. virtual void readModuleFileExtension( const ModuleFileExtensionMetadata &Metadata) {} @@ -285,6 +306,10 @@ class ChainedASTReaderListener : public ASTReaderListener { serialization::ModuleKind Kind) override; bool visitInputFile(StringRef Filename, bool isSystem, bool isOverridden, bool isExplicitModule) override; + bool readCASFileSystemRootID(StringRef RootID, bool Complain) override; + bool readIncludeTreeID(StringRef ID, bool Complain) override; + bool readModuleCacheKey(StringRef ModuleName, StringRef Filename, + StringRef CacheKey) override; void readModuleFileExtension( const ModuleFileExtensionMetadata &Metadata) override; }; @@ -771,13 +796,6 @@ class ASTReader /// added to the global preprocessing entity ID to produce a local ID. GlobalPreprocessedEntityMapType GlobalPreprocessedEntityMap; - using GlobalSkippedRangeMapType = - ContinuousRangeMap<unsigned, ModuleFile *, 4>; - - /// Mapping from global skipped range base IDs to the module in which - /// the skipped ranges reside. - GlobalSkippedRangeMapType GlobalSkippedRangeMap; - /// \name CodeGen-relevant special data /// Fields containing data that is relevant to CodeGen. //@{ @@ -790,6 +808,11 @@ class ASTReader /// the consumer eagerly. SmallVector<GlobalDeclID, 16> EagerlyDeserializedDecls; + /// The IDs of all vtables to emit. The referenced declarations are passed + /// to the consumers' HandleVTable eagerly after passing + /// EagerlyDeserializedDecls. + SmallVector<GlobalDeclID, 16> VTablesToEmit; + /// The IDs of all tentative definitions stored in the chain. /// /// Sema keeps track of all tentative definitions in a TU because it has to @@ -948,6 +971,9 @@ class ASTReader /// Sema tracks these to emit deferred diags. llvm::SmallSetVector<GlobalDeclID, 4> DeclsToCheckForDeferredDiags; + /// The IDs of all decls with function effects to be checked. + SmallVector<GlobalDeclID> DeclsWithEffectsToVerify; + private: struct ImportedSubmodule { serialization::SubmoduleID ID; @@ -1301,9 +1327,48 @@ class ASTReader serialization::InputFile getInputFile(ModuleFile &F, unsigned ID, bool Complain = true); + /// The buffer used as the temporary backing storage for resolved paths. + SmallString<0> PathBuf; + + /// A wrapper around StringRef that temporarily borrows the underlying buffer. + class TemporarilyOwnedStringRef { + StringRef String; + llvm::SaveAndRestore<SmallString<0>> UnderlyingBuffer; + + public: + TemporarilyOwnedStringRef(StringRef S, SmallString<0> &UnderlyingBuffer) + : String(S), UnderlyingBuffer(UnderlyingBuffer, {}) {} + + /// Return the wrapped \c StringRef that must be outlived by \c this. + const StringRef *operator->() const & { return &String; } + const StringRef &operator*() const & { return String; } + + /// Make it harder to get a \c StringRef that outlives \c this. + const StringRef *operator->() && = delete; + const StringRef &operator*() && = delete; + }; + public: - void ResolveImportedPath(ModuleFile &M, std::string &Filename); - static void ResolveImportedPath(std::string &Filename, StringRef Prefix); + /// Get the buffer for resolving paths. + SmallString<0> &getPathBuf() { return PathBuf; } + + /// Resolve \c Path in the context of module file \c M. The return value + /// must go out of scope before the next call to \c ResolveImportedPath. + static TemporarilyOwnedStringRef + ResolveImportedPath(SmallString<0> &Buf, StringRef Path, ModuleFile &ModF); + /// Resolve \c Path in the context of the \c Prefix directory. The return + /// value must go out of scope before the next call to \c ResolveImportedPath. + static TemporarilyOwnedStringRef + ResolveImportedPath(SmallString<0> &Buf, StringRef Path, StringRef Prefix); + + /// Resolve \c Path in the context of module file \c M. + static std::string ResolveImportedPathAndAllocate(SmallString<0> &Buf, + StringRef Path, + ModuleFile &ModF); + /// Resolve \c Path in the context of the \c Prefix directory. + static std::string ResolveImportedPathAndAllocate(SmallString<0> &Buf, + StringRef Path, + StringRef Prefix); /// Returns the first key declaration for the given declaration. This /// is one that is formerly-canonical (or still canonical) and whose module @@ -1500,6 +1565,7 @@ class ASTReader bool isConsumerInterestedIn(Decl *D); void PassInterestingDeclsToConsumer(); void PassInterestingDeclToConsumer(Decl *D); + void PassVTableToConsumer(CXXRecordDecl *RD); void finishPendingActions(); void diagnoseOdrViolations(); @@ -1826,9 +1892,6 @@ class ASTReader std::optional<bool> isPreprocessedEntityInFileID(unsigned Index, FileID FID) override; - /// Read a preallocated skipped range from the external source. - SourceRange ReadSkippedRange(unsigned Index) override; - /// Read the header file information for the given file entry. HeaderFileInfo GetHeaderFileInfo(FileEntryRef FE) override; @@ -2284,6 +2347,8 @@ class ASTReader /// Translate a FileID from another module file's FileID space into ours. FileID TranslateFileID(ModuleFile &F, FileID FID) const { assert(FID.ID >= 0 && "Reading non-local FileID."); + if (FID.isInvalid()) + return FID; return FileID::get(F.SLocEntryBaseID + FID.ID - 1); } @@ -2296,11 +2361,8 @@ class ASTReader // Read a string static std::string ReadString(const RecordDataImpl &Record, unsigned &Idx); - - // Skip a string - static void SkipString(const RecordData &Record, unsigned &Idx) { - Idx += Record[Idx] + 1; - } + static StringRef ReadStringBlob(const RecordDataImpl &Record, unsigned &Idx, + StringRef &Blob); // Read a path std::string ReadPath(ModuleFile &F, const RecordData &Record, unsigned &Idx); @@ -2308,11 +2370,8 @@ class ASTReader // Read a path std::string ReadPath(StringRef BaseDirectory, const RecordData &Record, unsigned &Idx); - - // Skip a path - static void SkipPath(const RecordData &Record, unsigned &Idx) { - SkipString(Record, Idx); - } + std::string ReadPathBlob(StringRef BaseDirectory, const RecordData &Record, + unsigned &Idx, StringRef &Blob); /// Read a version tuple. static VersionTuple ReadVersionTuple(const RecordData &Record, unsigned &Idx); diff --git a/clang/include/clang/Serialization/ASTRecordReader.h b/clang/include/clang/Serialization/ASTRecordReader.h index 2561418b78ca7..b833b6dae357c 100644 --- a/clang/include/clang/Serialization/ASTRecordReader.h +++ b/clang/include/clang/Serialization/ASTRecordReader.h @@ -171,6 +171,12 @@ class ASTRecordReader return Qualifiers::fromOpaqueValue(readInt()); } + /* TO_UPSTREAM(BoundsSafety) ON */ + BoundsSafetyPointerAttributes readBoundsSafetyPointerAttributes() { + return BoundsSafetyPointerAttributes::fromOpaqueValue(readUInt32()); + } + /* TO_UPSTREAM(BoundsSafety) OFF */ + /// Read a type from the current position in the record. QualType readType() { return Reader->readType(*F, Record, Idx); diff --git a/clang/include/clang/Serialization/ASTRecordWriter.h b/clang/include/clang/Serialization/ASTRecordWriter.h index 0c8ac75fc40f4..43a46b0e16b7e 100644 --- a/clang/include/clang/Serialization/ASTRecordWriter.h +++ b/clang/include/clang/Serialization/ASTRecordWriter.h @@ -60,8 +60,9 @@ class ASTRecordWriter public: /// Construct a ASTRecordWriter that uses the default encoding scheme. - ASTRecordWriter(ASTWriter &W, ASTWriter::RecordDataImpl &Record) - : DataStreamBasicWriter(W.getASTContext()), Writer(&W), Record(&Record) {} + ASTRecordWriter(ASTContext &Context, ASTWriter &W, + ASTWriter::RecordDataImpl &Record) + : DataStreamBasicWriter(Context), Writer(&W), Record(&Record) {} /// Construct a ASTRecordWriter that uses the same encoding scheme as another /// ASTRecordWriter. @@ -128,6 +129,8 @@ class ASTRecordWriter AddStmt(const_cast<Stmt*>(S)); } + void writeAttr(const Attr *A) { AddAttr(A); } + /// Write an BTFTypeTagAttr object. void writeBTFTypeTagAttr(const BTFTypeTagAttr *A) { AddAttr(A); } @@ -146,6 +149,7 @@ class ASTRecordWriter void writeTypeCoupledDeclRefInfo(TypeCoupledDeclRefInfo Info) { writeDeclRef(Info.getDecl()); writeBool(Info.isDeref()); + writeBool(Info.isMember()); } /// Emit a source range. @@ -206,7 +210,7 @@ class ASTRecordWriter /// Emit a reference to a type. void AddTypeRef(QualType T) { - return Writer->AddTypeRef(T, *Record); + return Writer->AddTypeRef(getASTContext(), T, *Record); } void writeQualType(QualType T) { AddTypeRef(T); diff --git a/clang/include/clang/Serialization/ASTWriter.h b/clang/include/clang/Serialization/ASTWriter.h index a0e475ec9f862..d41da111ebe72 100644 --- a/clang/include/clang/Serialization/ASTWriter.h +++ b/clang/include/clang/Serialization/ASTWriter.h @@ -119,9 +119,6 @@ class ASTWriter : public ASTDeserializationListener, /// The PCM manager which manages memory buffers for pcm files. InMemoryModuleCache &ModuleCache; - /// The ASTContext we're writing. - ASTContext *Context = nullptr; - /// The preprocessor we're writing. Preprocessor *PP = nullptr; @@ -500,6 +497,10 @@ class ASTWriter : public ASTDeserializationListener, std::vector<SourceRange> NonAffectingRanges; std::vector<SourceLocation::UIntTy> NonAffectingOffsetAdjustments; + /// A list of classes in named modules which need to emit the VTable in + /// the corresponding object file. + llvm::SmallVector<CXXRecordDecl *> PendingEmittingVTables; + /// 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. @@ -533,14 +534,13 @@ class ASTWriter : public ASTDeserializationListener, unsigned getSubmoduleID(Module *Mod); /// Write the given subexpression to the bitstream. - void WriteSubStmt(Stmt *S); + void WriteSubStmt(ASTContext &Context, Stmt *S); void WriteBlockInfoBlock(); - void WriteControlBlock(Preprocessor &PP, ASTContext &Context, - StringRef isysroot); + void WriteControlBlock(Preprocessor &PP, StringRef isysroot); /// Write out the signature and diagnostic options, and return the signature. - void writeUnhashedControlBlock(Preprocessor &PP, ASTContext &Context); + void writeUnhashedControlBlock(Preprocessor &PP); ASTFileSignature backpatchSignature(); /// Calculate hash of the pcm content. @@ -548,40 +548,41 @@ class ASTWriter : public ASTDeserializationListener, ASTFileSignature createSignatureForNamedModule() const; void WriteInputFiles(SourceManager &SourceMgr, HeaderSearchOptions &HSOpts); - void WriteSourceManagerBlock(SourceManager &SourceMgr, - const Preprocessor &PP); + void WriteSourceManagerBlock(SourceManager &SourceMgr); void WritePreprocessor(const Preprocessor &PP, bool IsModule); void WriteHeaderSearch(const HeaderSearch &HS); void WritePreprocessorDetail(PreprocessingRecord &PPRec, uint64_t MacroOffsetsBase); - void WriteSubmodules(Module *WritingModule); + void WriteSubmodules(Module *WritingModule, ASTContext *Context); void WritePragmaDiagnosticMappings(const DiagnosticsEngine &Diag, bool isModule); unsigned TypeExtQualAbbrev = 0; void WriteTypeAbbrevs(); - void WriteType(QualType T); + void WriteType(ASTContext &Context, QualType T); bool isLookupResultExternal(StoredDeclsList &Result, DeclContext *DC); - void GenerateNameLookupTable(const DeclContext *DC, + void GenerateNameLookupTable(ASTContext &Context, const DeclContext *DC, llvm::SmallVectorImpl<char> &LookupTable); uint64_t WriteDeclContextLexicalBlock(ASTContext &Context, const DeclContext *DC); uint64_t WriteDeclContextVisibleBlock(ASTContext &Context, DeclContext *DC); void WriteTypeDeclOffsets(); void WriteFileDeclIDsMap(); - void WriteComments(); + void WriteComments(ASTContext &Context); void WriteSelectors(Sema &SemaRef); void WriteReferencedSelectorsPool(Sema &SemaRef); - void WriteIdentifierTable(Preprocessor &PP, IdentifierResolver &IdResolver, + void WriteIdentifierTable(Preprocessor &PP, IdentifierResolver *IdResolver, bool IsModule); void WriteDeclAndTypes(ASTContext &Context); void PrepareWritingSpecialDecls(Sema &SemaRef); void WriteSpecialDeclRecords(Sema &SemaRef); - void WriteDeclUpdatesBlocks(RecordDataImpl &OffsetsRecord); - void WriteDeclContextVisibleUpdate(const DeclContext *DC); + void WriteDeclUpdatesBlocks(ASTContext &Context, + RecordDataImpl &OffsetsRecord); + void WriteDeclContextVisibleUpdate(ASTContext &Context, + const DeclContext *DC); void WriteFPPragmaOptions(const FPOptionsOverride &Opts); void WriteOpenCLExtensions(Sema &SemaRef); void WriteCUDAPragmas(Sema &SemaRef); @@ -592,6 +593,7 @@ class ASTWriter : public ASTDeserializationListener, void WriteMSPointersToMembersPragmaOptions(Sema &SemaRef); void WritePackPragmaOptions(Sema &SemaRef); void WriteFloatControlPragmaOptions(Sema &SemaRef); + void WriteDeclsWithEffectsToVerify(Sema &SemaRef); void WriteModuleFileExtension(Sema &SemaRef, ModuleFileExtensionWriter &Writer); @@ -629,7 +631,7 @@ class ASTWriter : public ASTDeserializationListener, void WriteDeclAbbrevs(); void WriteDecl(ASTContext &Context, Decl *D); - ASTFileSignature WriteASTCore(Sema &SemaRef, StringRef isysroot, + ASTFileSignature WriteASTCore(Sema *SemaPtr, StringRef isysroot, Module *WritingModule); public: @@ -642,11 +644,6 @@ class ASTWriter : public ASTDeserializationListener, bool GeneratingReducedBMI = false); ~ASTWriter() override; - ASTContext &getASTContext() const { - assert(Context && "requested AST context when not writing AST"); - return *Context; - } - const LangOptions &getLangOpts() const; /// Get a timestamp for output into the AST file. The actual timestamp @@ -654,10 +651,13 @@ class ASTWriter : public ASTDeserializationListener, /// include timestamps in the output file. time_t getTimestampForOutput(const FileEntry *E) const; - /// Write a precompiled header for the given semantic analysis. + /// Write a precompiled header or a module with the AST produced by the + /// \c Sema object, or a dependency scanner module with the preprocessor state + /// produced by the \c Preprocessor object. /// - /// \param SemaRef a reference to the semantic analysis object that processed - /// the AST to be written into the precompiled header. + /// \param Subject The \c Sema object that processed the AST to be written, or + /// in the case of a dependency scanner module the \c Preprocessor that holds + /// the state. /// /// \param WritingModule The module that we are writing. If null, we are /// writing a precompiled header. @@ -668,8 +668,9 @@ class ASTWriter : public ASTDeserializationListener, /// /// \return the module signature, which eventually will be a hash of /// the module but currently is merely a random 32-bit number. - ASTFileSignature WriteAST(Sema &SemaRef, StringRef OutputFile, - Module *WritingModule, StringRef isysroot, + ASTFileSignature WriteAST(llvm::PointerUnion<Sema *, Preprocessor *> Subject, + StringRef OutputFile, Module *WritingModule, + StringRef isysroot, bool ShouldCacheASTInMemory = false); /// Emit a token. @@ -712,10 +713,10 @@ class ASTWriter : public ASTDeserializationListener, uint32_t getMacroDirectivesOffset(const IdentifierInfo *Name); /// Emit a reference to a type. - void AddTypeRef(QualType T, RecordDataImpl &Record); + void AddTypeRef(ASTContext &Context, QualType T, RecordDataImpl &Record); /// Force a type to be emitted and get its ID. - serialization::TypeID GetOrCreateTypeID(QualType T); + serialization::TypeID GetOrCreateTypeID(ASTContext &Context, QualType T); /// Find the first local declaration of a given local redeclarable /// decl. @@ -757,6 +758,8 @@ class ASTWriter : public ASTDeserializationListener, /// Add a string to the given record. void AddString(StringRef Str, RecordDataImpl &Record); + void AddStringBlob(StringRef Str, RecordDataImpl &Record, + SmallVectorImpl<char> &Blob); /// Convert a path from this build process into one that is appropriate /// for emission in the module file. @@ -764,6 +767,8 @@ class ASTWriter : public ASTDeserializationListener, /// Add a path to the given record. void AddPath(StringRef Path, RecordDataImpl &Record); + void AddPathBlob(StringRef Str, RecordDataImpl &Record, + SmallVectorImpl<char> &Blob); /// Emit the current record with the given path as a blob. void EmitRecordWithPath(unsigned Abbrev, RecordDataRef Record, @@ -857,6 +862,8 @@ class ASTWriter : public ASTDeserializationListener, return PredefinedDecls.count(D); } + void handleVTable(CXXRecordDecl *RD); + private: // ASTDeserializationListener implementation void ReaderInitialized(ASTReader *Reader) override; @@ -901,6 +908,10 @@ class ASTWriter : public ASTDeserializationListener, void RedefinedHiddenDefinition(const NamedDecl *D, Module *M) override; void AddedAttributeToRecord(const Attr *Attr, const RecordDecl *Record) override; + /* TO_UPSTREAM(BoundsSafety) ON*/ + void LazyAttributeToDecl(const Attr *Attr, + const Decl *D) override; + /* TO_UPSTREAM(BoundsSafety) OFF*/ void EnteringModulePurview() override; void AddedManglingNumber(const Decl *D, unsigned) override; void AddedStaticLocalNumbers(const Decl *D, unsigned) override; @@ -914,9 +925,9 @@ class PCHGenerator : public SemaConsumer { void anchor() override; Preprocessor &PP; + llvm::PointerUnion<Sema *, Preprocessor *> Subject; std::string OutputFile; std::string isysroot; - Sema *SemaPtr; std::shared_ptr<PCHBuffer> Buffer; llvm::BitstreamWriter Stream; ASTWriter Writer; @@ -931,9 +942,7 @@ class PCHGenerator : public SemaConsumer { bool isComplete() const { return Buffer->IsComplete; } PCHBuffer *getBufferPtr() { return Buffer.get(); } StringRef getOutputFile() const { return OutputFile; } - DiagnosticsEngine &getDiagnostics() const { - return SemaPtr->getDiagnostics(); - } + DiagnosticsEngine &getDiagnostics() const; Preprocessor &getPreprocessor() { return PP; } virtual Module *getEmittingModule(ASTContext &Ctx); @@ -949,8 +958,9 @@ class PCHGenerator : public SemaConsumer { bool GeneratingReducedBMI = false); ~PCHGenerator() override; - void InitializeSema(Sema &S) override { SemaPtr = &S; } + void InitializeSema(Sema &S) override; void HandleTranslationUnit(ASTContext &Ctx) override; + void HandleVTable(CXXRecordDecl *RD) override { Writer.handleVTable(RD); } ASTMutationListener *GetASTMutationListener() override; ASTDeserializationListener *GetASTDeserializationListener() override; bool hasEmittedPCH() const { return Buffer->IsComplete; } diff --git a/clang/include/clang/Serialization/ModuleFile.h b/clang/include/clang/Serialization/ModuleFile.h index 3e920c0f68360..161ec298d8604 100644 --- a/clang/include/clang/Serialization/ModuleFile.h +++ b/clang/include/clang/Serialization/ModuleFile.h @@ -15,6 +15,7 @@ #define LLVM_CLANG_SERIALIZATION_MODULEFILE_H #include "clang/Basic/FileManager.h" +#include "clang/Basic/LLVM.h" #include "clang/Basic/Module.h" #include "clang/Basic/SourceLocation.h" #include "clang/Serialization/ASTBitCodes.h" @@ -61,8 +62,9 @@ enum ModuleKind { /// The input file info that has been loaded from an AST file. struct InputFileInfo { - std::string FilenameAsRequested; - std::string Filename; + StringRef UnresolvedImportedFilenameAsRequested; + StringRef UnresolvedImportedFilename; + uint64_t ContentHash; off_t StoredSize; time_t StoredTime; @@ -70,6 +72,10 @@ struct InputFileInfo { bool Transient; bool TopLevel; bool ModuleMap; + + bool isValid() const { + return !UnresolvedImportedFilenameAsRequested.empty(); + } }; /// The input file that has been loaded from this AST file, along with @@ -138,14 +144,25 @@ class ModuleFile { /// The file name of the module file. std::string FileName; + /// The \c ActionCache key for this module, or empty. + std::string ModuleCacheKey; + + /// The CAS filesystem root ID for implicit modules built with the dependency + /// scanner, or empty. + std::string CASFileSystemRootID; + + /// The include-tree root ID for implicit modules built with the dependency + /// scanner, or empty. + std::string IncludeTreeID; + /// The name of the module. std::string ModuleName; /// The base directory of the module. std::string BaseDirectory; - std::string getTimestampFilename() const { - return FileName + ".timestamp"; + static std::string getTimestampFilename(StringRef FileName) { + return (FileName + ".timestamp").str(); } /// The original source file name that was used to build the @@ -372,12 +389,6 @@ class ModuleFile { const PPEntityOffset *PreprocessedEntityOffsets = nullptr; unsigned NumPreprocessedEntities = 0; - /// Base ID for preprocessed skipped ranges local to this module. - unsigned BasePreprocessedSkippedRangeID = 0; - - const PPSkippedRange *PreprocessedSkippedRangeOffsets = nullptr; - unsigned NumPreprocessedSkippedRanges = 0; - // === Header search information === /// The number of local HeaderFileInfo structures. diff --git a/clang/include/clang/Serialization/ObjectFilePCHContainerReader.h b/clang/include/clang/Serialization/ObjectFilePCHContainerReader.h new file mode 100644 index 0000000000000..06778d25d7e52 --- /dev/null +++ b/clang/include/clang/Serialization/ObjectFilePCHContainerReader.h @@ -0,0 +1,25 @@ +//===-- Serialization/ObjectFilePCHContainerReader.h ------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_SERIALIZATION_OBJECTFILEPCHCONTAINERREADER_H +#define LLVM_CLANG_SERIALIZATION_OBJECTFILEPCHCONTAINERREADER_H + +#include "clang/Serialization/PCHContainerOperations.h" + +namespace clang { +/// A PCHContainerReader implementation that uses LLVM to +/// wraps Clang modules inside a COFF, ELF, or Mach-O container. +class ObjectFilePCHContainerReader : public PCHContainerReader { + ArrayRef<StringRef> getFormats() const override; + + /// Returns the serialized AST inside the PCH container Buffer. + StringRef ExtractPCH(llvm::MemoryBufferRef Buffer) const override; +}; +} // namespace clang + +#endif diff --git a/clang/include/clang/Serialization/TypeBitCodes.def b/clang/include/clang/Serialization/TypeBitCodes.def index 82b053d4caca6..de16fe5b81259 100644 --- a/clang/include/clang/Serialization/TypeBitCodes.def +++ b/clang/include/clang/Serialization/TypeBitCodes.def @@ -66,6 +66,10 @@ TYPE_BIT_CODE(Using, USING, 54) TYPE_BIT_CODE(BTFTagAttributed, BTFTAG_ATTRIBUTED, 55) TYPE_BIT_CODE(PackIndexing, PACK_INDEXING, 56) TYPE_BIT_CODE(CountAttributed, COUNT_ATTRIBUTED, 57) -TYPE_BIT_CODE(ArrayParameter, ARRAY_PARAMETER, 58) +/* TO_UPSTREAM(BoundsSafety) ON */ +TYPE_BIT_CODE(DynamicRangePointer, DYNAMIC_END_POINTER, 58) +TYPE_BIT_CODE(ValueTerminated, VALUE_TERMINATED, 59) +/* TO_UPSTREAM(BoundsSafety) OFF */ +TYPE_BIT_CODE(ArrayParameter, ARRAY_PARAMETER, 60) #undef TYPE_BIT_CODE diff --git a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td index ec5dbd28a5272..be94b7cd0d458 100644 --- a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td +++ b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td @@ -1787,12 +1787,28 @@ def UncountedLambdaCapturesChecker : Checker<"UncountedLambdaCapturesChecker">, let ParentPackage = WebKitAlpha in { +def MemoryUnsafeCastChecker : Checker<"MemoryUnsafeCastChecker">, + HelpText<"Check for memory unsafe casts from base type to derived type.">, + Docu