merge: branch 'main' into users/krishna2803/nextfnbf16
This commit is contained in:
commit
6092968769
@ -49,8 +49,7 @@ DEPENDENTS_TO_TEST = {
|
||||
"flang",
|
||||
},
|
||||
"lld": {"bolt", "cross-project-tests"},
|
||||
# TODO(issues/132795): LLDB should be enabled on clang changes.
|
||||
"clang": {"clang-tools-extra", "cross-project-tests"},
|
||||
"clang": {"clang-tools-extra", "cross-project-tests", "lldb"},
|
||||
"mlir": {"flang"},
|
||||
# Test everything if ci scripts are changed.
|
||||
".ci": {
|
||||
|
@ -83,11 +83,11 @@ class TestComputeProjects(unittest.TestCase):
|
||||
)
|
||||
self.assertEqual(
|
||||
env_variables["projects_to_build"],
|
||||
"clang;clang-tools-extra;lld;llvm",
|
||||
"clang;clang-tools-extra;lld;lldb;llvm",
|
||||
)
|
||||
self.assertEqual(
|
||||
env_variables["project_check_targets"],
|
||||
"check-clang check-clang-tools",
|
||||
"check-clang check-clang-tools check-lldb",
|
||||
)
|
||||
self.assertEqual(
|
||||
env_variables["runtimes_to_build"], "compiler-rt;libcxx;libcxxabi;libunwind"
|
||||
@ -158,11 +158,11 @@ class TestComputeProjects(unittest.TestCase):
|
||||
)
|
||||
self.assertEqual(
|
||||
env_variables["projects_to_build"],
|
||||
"clang;clang-tools-extra;lld;llvm;mlir",
|
||||
"clang;clang-tools-extra;lld;lldb;llvm;mlir",
|
||||
)
|
||||
self.assertEqual(
|
||||
env_variables["project_check_targets"],
|
||||
"check-clang check-clang-cir check-clang-tools",
|
||||
"check-clang check-clang-cir check-clang-tools check-lldb",
|
||||
)
|
||||
self.assertEqual(
|
||||
env_variables["runtimes_to_build"], "compiler-rt;libcxx;libcxxabi;libunwind"
|
||||
|
@ -24,6 +24,7 @@ function at-exit {
|
||||
retcode=$?
|
||||
|
||||
mkdir -p artifacts
|
||||
sccache --show-stats
|
||||
sccache --show-stats >> artifacts/sccache_stats.txt
|
||||
cp "${BUILD_DIR}"/.ninja_log artifacts/.ninja_log
|
||||
cp "${MONOREPO_ROOT}"/*.log artifacts/ || :
|
||||
|
38
.github/workflows/libclang-python-tests.yml
vendored
38
.github/workflows/libclang-python-tests.yml
vendored
@ -4,7 +4,6 @@ permissions:
|
||||
contents: read
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- 'main'
|
||||
@ -13,29 +12,46 @@ on:
|
||||
- 'clang/tools/libclang/**'
|
||||
- 'clang/CMakeList.txt'
|
||||
- '.github/workflows/libclang-python-tests.yml'
|
||||
- '.github/workflows/llvm-project-tests.yml'
|
||||
pull_request:
|
||||
paths:
|
||||
- 'clang/bindings/python/**'
|
||||
- 'clang/tools/libclang/**'
|
||||
- 'clang/CMakeList.txt'
|
||||
- '.github/workflows/libclang-python-tests.yml'
|
||||
- '.github/workflows/llvm-project-tests.yml'
|
||||
|
||||
jobs:
|
||||
check-clang-python:
|
||||
# Build libclang and then run the libclang Python binding's unit tests.
|
||||
# There is an issue running on "windows-2019".
|
||||
# See https://github.com/llvm/llvm-project/issues/76601#issuecomment-1873049082.
|
||||
name: Build and run Python unit tests
|
||||
if: github.repository == 'llvm/llvm-project'
|
||||
runs-on: ubuntu-24.04
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
python-version: ["3.8", "3.13"]
|
||||
uses: ./.github/workflows/llvm-project-tests.yml
|
||||
with:
|
||||
build_target: check-clang-python
|
||||
projects: clang
|
||||
# There is an issue running on "windows-2019".
|
||||
# See https://github.com/llvm/llvm-project/issues/76601#issuecomment-1873049082.
|
||||
os_list: '["ubuntu-24.04"]'
|
||||
python_version: ${{ matrix.python-version }}
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
- name: Setup ccache
|
||||
uses: hendrikmuhs/ccache-action@a1209f81afb8c005c13b4296c32e363431bffea5 # v1.2.17
|
||||
with:
|
||||
max-size: 2G
|
||||
key: spirv-ubuntu-24.04
|
||||
variant: sccache
|
||||
- name: Build and Test
|
||||
run: |
|
||||
mkdir build
|
||||
cmake -GNinja \
|
||||
-S llvm \
|
||||
-B build \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DLLVM_ENABLE_ASSERTIONS=ON \
|
||||
-DCMAKE_C_COMPILER_LAUNCHER=sccache \
|
||||
-DCMAKE_CXX_COMPILER_LAUNCHER=sccache \
|
||||
-DLLVM_ENABLE_PROJECTS=clang
|
||||
ninja -C build check-clang-python
|
||||
|
149
.github/workflows/llvm-project-tests.yml
vendored
149
.github/workflows/llvm-project-tests.yml
vendored
@ -1,149 +0,0 @@
|
||||
name: LLVM Project Tests
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
build_target:
|
||||
required: false
|
||||
projects:
|
||||
required: false
|
||||
extra_cmake_args:
|
||||
required: false
|
||||
os_list:
|
||||
required: false
|
||||
default: '["ubuntu-24.04", "windows-2019", "macOS-13"]'
|
||||
python_version:
|
||||
required: false
|
||||
type: string
|
||||
default: '3.11'
|
||||
workflow_call:
|
||||
inputs:
|
||||
build_target:
|
||||
required: false
|
||||
type: string
|
||||
default: "all"
|
||||
|
||||
projects:
|
||||
required: true
|
||||
type: string
|
||||
|
||||
extra_cmake_args:
|
||||
required: false
|
||||
type: string
|
||||
|
||||
os_list:
|
||||
required: false
|
||||
type: string
|
||||
# Use windows-2019 due to:
|
||||
# https://developercommunity.visualstudio.com/t/Prev-Issue---with-__assume-isnan-/1597317
|
||||
default: '["ubuntu-24.04", "windows-2019", "macOS-13"]'
|
||||
|
||||
python_version:
|
||||
required: false
|
||||
type: string
|
||||
default: '3.11'
|
||||
|
||||
concurrency:
|
||||
# Skip intermediate builds: always.
|
||||
# Cancel intermediate builds: only if it is a pull request build.
|
||||
# If the group name here is the same as the group name in the workflow that includes
|
||||
# this one, then the action will try to wait on itself and get stuck.
|
||||
group: llvm-project-${{ github.workflow }}-${{ inputs.projects }}-${{ inputs.python_version }}${{ github.ref }}
|
||||
cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }}
|
||||
|
||||
jobs:
|
||||
lit-tests:
|
||||
name: Lit Tests
|
||||
runs-on: ${{ matrix.os }}
|
||||
container:
|
||||
image: ${{(startsWith(matrix.os, 'ubuntu') && 'ghcr.io/llvm/ci-ubuntu-24.04:latest') || null}}
|
||||
volumes:
|
||||
- /mnt/:/mnt/
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: ${{ fromJSON(inputs.os_list) }}
|
||||
steps:
|
||||
- name: Setup Windows
|
||||
if: startsWith(matrix.os, 'windows')
|
||||
uses: llvm/actions/setup-windows@main
|
||||
with:
|
||||
arch: amd64
|
||||
# On Windows, starting with win19/20220814.1, cmake choose the 32-bit
|
||||
# python3.10.6 libraries instead of the 64-bit libraries when building
|
||||
# lldb. Using this setup-python action to make 3.10 the default
|
||||
# python fixes this.
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0
|
||||
with:
|
||||
python-version: ${{ inputs.python_version }}
|
||||
- name: Install Ninja
|
||||
if: runner.os != 'Linux'
|
||||
uses: llvm/actions/install-ninja@main
|
||||
# actions/checkout deletes any existing files in the new git directory,
|
||||
# so this needs to either run before ccache-action or it has to use
|
||||
# clean: false.
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
fetch-depth: 250
|
||||
- name: Setup ccache
|
||||
uses: hendrikmuhs/ccache-action@a1209f81afb8c005c13b4296c32e363431bffea5 # v1.2.17
|
||||
with:
|
||||
# A full build of llvm, clang, lld, and lldb takes about 250MB
|
||||
# of ccache space. There's not much reason to have more than this,
|
||||
# because we usually won't need to save cache entries from older
|
||||
# builds. Also, there is an overall 10GB cache limit, and each
|
||||
# run creates a new cache entry so we want to ensure that we have
|
||||
# enough cache space for all the tests to run at once and still
|
||||
# fit under the 10 GB limit.
|
||||
# Default to 2G to workaround: https://github.com/hendrikmuhs/ccache-action/issues/174
|
||||
max-size: 2G
|
||||
key: ${{ matrix.os }}
|
||||
variant: sccache
|
||||
- name: Build and Test
|
||||
env:
|
||||
# Workaround for https://github.com/actions/virtual-environments/issues/5900.
|
||||
# This should be a no-op for non-mac OSes
|
||||
PKG_CONFIG_PATH: /usr/local/Homebrew/Library/Homebrew/os/mac/pkgconfig//12
|
||||
shell: bash
|
||||
id: build-llvm
|
||||
run: |
|
||||
if [ "${{ runner.os }}" == "Linux" ]; then
|
||||
builddir="/mnt/build/"
|
||||
sudo mkdir -p $builddir
|
||||
sudo chown gha $builddir
|
||||
extra_cmake_args="-DCMAKE_CXX_COMPILER=clang++ -DCMAKE_C_COMPILER=clang"
|
||||
else
|
||||
builddir="$(pwd)"/build
|
||||
fi
|
||||
if [ "${{ runner.os }}" == "macOS" ]; then
|
||||
# Workaround test failure on some lld tests on MacOS
|
||||
# https://github.com/llvm/llvm-project/issues/81967
|
||||
extra_cmake_args="-DLLVM_DISABLE_ASSEMBLY_FILES=ON"
|
||||
fi
|
||||
echo "llvm-builddir=$builddir" >> "$GITHUB_OUTPUT"
|
||||
cmake -G Ninja \
|
||||
-B "$builddir" \
|
||||
-S llvm \
|
||||
-DLLVM_ENABLE_PROJECTS="${{ inputs.projects }}" \
|
||||
-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 \
|
||||
${{ inputs.extra_cmake_args }}
|
||||
ninja -C "$builddir" '${{ inputs.build_target }}'
|
||||
|
||||
- name: Build and Test libclc
|
||||
if: "!startsWith(matrix.os, 'windows') && contains(inputs.projects, 'libclc')"
|
||||
env:
|
||||
LLVM_BUILDDIR: ${{ steps.build-llvm.outputs.llvm-builddir }}
|
||||
run: |
|
||||
# The libclc tests don't have a generated check target so all we can
|
||||
# do is build it.
|
||||
ninja -C "$LLVM_BUILDDIR"
|
@ -1,32 +0,0 @@
|
||||
# This workflow will test the llvm-project-tests workflow in PRs
|
||||
# targetting the main branch. Since this workflow doesn't normally
|
||||
# run on main PRs, we need some way to test it to ensure new updates
|
||||
# don't break it.
|
||||
|
||||
name: LLVM Workflow Test
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- 'main'
|
||||
paths:
|
||||
- '.github/workflows/llvm-project-tests.yml'
|
||||
- '.github/workflows/llvm-project-workflow-tests.yml'
|
||||
|
||||
concurrency:
|
||||
# Skip intermediate builds: always.
|
||||
# Cancel intermediate builds: only if it is a pull request build.
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }}
|
||||
|
||||
jobs:
|
||||
llvm-test:
|
||||
if: github.repository_owner == 'llvm'
|
||||
name: Build and Test
|
||||
uses: ./.github/workflows/llvm-project-tests.yml
|
||||
with:
|
||||
build_target: check-all
|
||||
projects: clang;lld;libclc;lldb
|
31
.github/workflows/mlir-spirv-tests.yml
vendored
31
.github/workflows/mlir-spirv-tests.yml
vendored
@ -24,9 +24,28 @@ jobs:
|
||||
check_spirv:
|
||||
if: github.repository_owner == 'llvm'
|
||||
name: Test MLIR SPIR-V
|
||||
uses: ./.github/workflows/llvm-project-tests.yml
|
||||
with:
|
||||
build_target: check-mlir
|
||||
projects: mlir
|
||||
extra_cmake_args: '-DLLVM_TARGETS_TO_BUILD="host" -DLLVM_INCLUDE_SPIRV_TOOLS_TESTS=ON'
|
||||
os_list: '["ubuntu-24.04"]'
|
||||
runs-on: ubuntu-24.04
|
||||
container:
|
||||
image: ghcr.io/llvm/ci-ubuntu-24.04:latest
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- name: Setup ccache
|
||||
uses: hendrikmuhs/ccache-action@a1209f81afb8c005c13b4296c32e363431bffea5 # v1.2.17
|
||||
with:
|
||||
max-size: 2G
|
||||
key: spirv-mlir-ubuntu-24.04
|
||||
variant: sccache
|
||||
- name: Build and Test
|
||||
run: |
|
||||
mkdir build
|
||||
cmake -GNinja \
|
||||
-S llvm \
|
||||
-B build \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DLLVM_ENABLE_ASSERTIONS=ON \
|
||||
-DCMAKE_C_COMPILER_LAUNCHER=sccache \
|
||||
-DCMAKE_CXX_COMPILER_LAUNCHER=sccache \
|
||||
-DLLVM_TARGETS_TO_BUILD="host" \
|
||||
-DLLVM_INCLUDE_SPIRV_TOOLS_TESTS=ON \
|
||||
-DLLVM_ENABLE_PROJECTS=mlir
|
||||
ninja -C build check-mlir
|
||||
|
2
.github/workflows/pr-code-format.yml
vendored
2
.github/workflows/pr-code-format.yml
vendored
@ -70,8 +70,6 @@ jobs:
|
||||
- name: Run code formatter
|
||||
env:
|
||||
GITHUB_PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
START_REV: ${{ github.event.pull_request.base.sha }}
|
||||
END_REV: ${{ github.event.pull_request.head.sha }}
|
||||
CHANGED_FILES: ${{ steps.changed-files.outputs.all_changed_files }}
|
||||
# Create an empty comments file so the pr-write job doesn't fail.
|
||||
run: |
|
||||
|
10
.github/workflows/premerge.yaml
vendored
10
.github/workflows/premerge.yaml
vendored
@ -69,6 +69,11 @@ jobs:
|
||||
|
||||
./.ci/monolithic-linux.sh "${projects_to_build}" "${project_check_targets}" "${runtimes_to_build}" "${runtimes_check_targets}" "${runtimes_check_targets_needs_reconfig}" "${enable_cir}"
|
||||
- name: Upload Artifacts
|
||||
# In some cases, Github will fail to upload the artifact. We want to
|
||||
# continue anyways as a failed artifact upload is an infra failure, not
|
||||
# a checks failure.
|
||||
# https://github.com/actions/upload-artifact/issues/569
|
||||
continue-on-error: true
|
||||
if: '!cancelled()'
|
||||
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
|
||||
with:
|
||||
@ -114,6 +119,11 @@ jobs:
|
||||
# these environment variables.
|
||||
bash -c "export SCCACHE_GCS_BUCKET=$CACHE_GCS_BUCKET; export SCCACHE_GCS_RW_MODE=READ_WRITE; export SCCACHE_IDLE_TIMEOUT=0; sccache --start-server; .ci/monolithic-windows.sh \"${{ steps.vars.outputs.windows-projects }}\" \"${{ steps.vars.outputs.windows-check-targets }}\""
|
||||
- name: Upload Artifacts
|
||||
# In some cases, Github will fail to upload the artifact. We want to
|
||||
# continue anyways as a failed artifact upload is an infra failure, not
|
||||
# a checks failure.
|
||||
# https://github.com/actions/upload-artifact/issues/569
|
||||
continue-on-error: true
|
||||
if: '!cancelled()'
|
||||
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
|
||||
with:
|
||||
|
31
.github/workflows/spirv-tests.yml
vendored
31
.github/workflows/spirv-tests.yml
vendored
@ -4,7 +4,6 @@ permissions:
|
||||
contents: read
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'llvm/lib/Target/SPIRV/**'
|
||||
@ -21,9 +20,27 @@ jobs:
|
||||
check_spirv:
|
||||
if: github.repository_owner == 'llvm'
|
||||
name: Test SPIR-V
|
||||
uses: ./.github/workflows/llvm-project-tests.yml
|
||||
with:
|
||||
build_target: check-llvm-codegen-spirv
|
||||
projects:
|
||||
extra_cmake_args: '-DLLVM_TARGETS_TO_BUILD="SPIRV" -DLLVM_INCLUDE_SPIRV_TOOLS_TESTS=ON'
|
||||
os_list: '["ubuntu-24.04"]'
|
||||
runs-on: ubuntu-24.04
|
||||
container:
|
||||
image: ghcr.io/llvm/ci-ubuntu-24.04:latest
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- name: Setup ccache
|
||||
uses: hendrikmuhs/ccache-action@a1209f81afb8c005c13b4296c32e363431bffea5 # v1.2.17
|
||||
with:
|
||||
max-size: 2G
|
||||
key: spirv-ubuntu-24.04
|
||||
variant: sccache
|
||||
- name: Build and Test
|
||||
run: |
|
||||
mkdir build
|
||||
cmake -GNinja \
|
||||
-S llvm \
|
||||
-B build \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DLLVM_ENABLE_ASSERTIONS=ON \
|
||||
-DCMAKE_C_COMPILER_LAUNCHER=sccache \
|
||||
-DCMAKE_CXX_COMPILER_LAUNCHER=sccache \
|
||||
-DLLVM_TARGETS_TO_BUILD="SPIRV" \
|
||||
-DLLVM_INCLUDE_SPIRV_TOOLS_TESTS=ON
|
||||
ninja -C build check-llvm-codegen-spirv
|
||||
|
@ -2517,7 +2517,7 @@ BinaryContext::calculateEmittedSize(BinaryFunction &BF, bool FixBranches) {
|
||||
// Clean-up the effect of the code emission.
|
||||
for (const MCSymbol &Symbol : Assembler.symbols()) {
|
||||
MCSymbol *MutableSymbol = const_cast<MCSymbol *>(&Symbol);
|
||||
MutableSymbol->setUndefined();
|
||||
MutableSymbol->setFragment(nullptr);
|
||||
MutableSymbol->setIsRegistered(false);
|
||||
}
|
||||
|
||||
|
@ -662,7 +662,7 @@ Error CleanMCState::runOnFunctions(BinaryContext &BC) {
|
||||
if (S->isDefined()) {
|
||||
LLVM_DEBUG(dbgs() << "BOLT-DEBUG: Symbol \"" << S->getName()
|
||||
<< "\" is already defined\n");
|
||||
const_cast<MCSymbol *>(S)->setUndefined();
|
||||
const_cast<MCSymbol *>(S)->setFragment(nullptr);
|
||||
}
|
||||
if (S->isRegistered()) {
|
||||
LLVM_DEBUG(dbgs() << "BOLT-DEBUG: Symbol \"" << S->getName()
|
||||
|
@ -424,6 +424,10 @@ ClangTidyASTConsumerFactory::createASTConsumer(
|
||||
FinderOptions.CheckProfiling.emplace(Profiling->Records);
|
||||
}
|
||||
|
||||
// Avoid processing system headers, unless the user explicitly requests it
|
||||
if (!Context.getOptions().SystemHeaders.value_or(false))
|
||||
FinderOptions.IgnoreSystemHeaders = true;
|
||||
|
||||
std::unique_ptr<ast_matchers::MatchFinder> Finder(
|
||||
new ast_matchers::MatchFinder(std::move(FinderOptions)));
|
||||
|
||||
@ -540,7 +544,7 @@ runClangTidy(clang::tidy::ClangTidyContext &Context,
|
||||
ArrayRef<std::string> InputFiles,
|
||||
llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> BaseFS,
|
||||
bool ApplyAnyFix, bool EnableCheckProfile,
|
||||
llvm::StringRef StoreCheckProfile) {
|
||||
llvm::StringRef StoreCheckProfile, bool Quiet) {
|
||||
ClangTool Tool(Compilations, InputFiles,
|
||||
std::make_shared<PCHContainerOperations>(), BaseFS);
|
||||
|
||||
@ -577,8 +581,9 @@ runClangTidy(clang::tidy::ClangTidyContext &Context,
|
||||
class ActionFactory : public FrontendActionFactory {
|
||||
public:
|
||||
ActionFactory(ClangTidyContext &Context,
|
||||
IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> BaseFS)
|
||||
: ConsumerFactory(Context, std::move(BaseFS)) {}
|
||||
IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> BaseFS,
|
||||
bool Quiet)
|
||||
: ConsumerFactory(Context, std::move(BaseFS)), Quiet(Quiet) {}
|
||||
std::unique_ptr<FrontendAction> create() override {
|
||||
return std::make_unique<Action>(&ConsumerFactory);
|
||||
}
|
||||
@ -589,6 +594,8 @@ runClangTidy(clang::tidy::ClangTidyContext &Context,
|
||||
DiagnosticConsumer *DiagConsumer) override {
|
||||
// Explicitly ask to define __clang_analyzer__ macro.
|
||||
Invocation->getPreprocessorOpts().SetUpStaticAnalyzer = true;
|
||||
if (Quiet)
|
||||
Invocation->getDiagnosticOpts().ShowCarets = false;
|
||||
return FrontendActionFactory::runInvocation(
|
||||
Invocation, Files, PCHContainerOps, DiagConsumer);
|
||||
}
|
||||
@ -607,9 +614,10 @@ runClangTidy(clang::tidy::ClangTidyContext &Context,
|
||||
};
|
||||
|
||||
ClangTidyASTConsumerFactory ConsumerFactory;
|
||||
bool Quiet;
|
||||
};
|
||||
|
||||
ActionFactory Factory(Context, std::move(BaseFS));
|
||||
ActionFactory Factory(Context, std::move(BaseFS), Quiet);
|
||||
Tool.run(&Factory);
|
||||
return DiagConsumer.take();
|
||||
}
|
||||
|
@ -94,7 +94,8 @@ runClangTidy(clang::tidy::ClangTidyContext &Context,
|
||||
ArrayRef<std::string> InputFiles,
|
||||
llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> BaseFS,
|
||||
bool ApplyAnyFix, bool EnableCheckProfile = false,
|
||||
llvm::StringRef StoreCheckProfile = StringRef());
|
||||
llvm::StringRef StoreCheckProfile = StringRef(),
|
||||
bool Quiet = false);
|
||||
|
||||
/// Controls what kind of fixes clang-tidy is allowed to apply.
|
||||
enum FixBehaviour {
|
||||
|
@ -89,13 +89,9 @@ def write_header(
|
||||
+ check_name_camel.upper()
|
||||
+ "_H"
|
||||
)
|
||||
f.write("//===--- ")
|
||||
f.write(os.path.basename(filename))
|
||||
f.write(" - clang-tidy ")
|
||||
f.write("-" * max(0, 42 - len(os.path.basename(filename))))
|
||||
f.write("*- C++ -*-===//")
|
||||
f.write(
|
||||
"""
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
@ -145,13 +141,9 @@ def write_implementation(
|
||||
filename = os.path.join(module_path, check_name_camel) + ".cpp"
|
||||
print("Creating %s..." % filename)
|
||||
with io.open(filename, "w", encoding="utf8", newline="\n") as f:
|
||||
f.write("//===--- ")
|
||||
f.write(os.path.basename(filename))
|
||||
f.write(" - clang-tidy ")
|
||||
f.write("-" * max(0, 51 - len(os.path.basename(filename))))
|
||||
f.write("-===//")
|
||||
f.write(
|
||||
"""
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
|
@ -188,7 +188,7 @@ static bool isKnownToHaveValue(const Expr &Cond, const ASTContext &Ctx,
|
||||
/// \return true iff all `CallExprs` visited have callees; false otherwise
|
||||
/// indicating there is an unresolved indirect call.
|
||||
static bool populateCallees(const Stmt *StmtNode,
|
||||
llvm::SmallSet<const Decl *, 16> &Callees) {
|
||||
llvm::SmallPtrSet<const Decl *, 16> &Callees) {
|
||||
if (const auto *Call = dyn_cast<CallExpr>(StmtNode)) {
|
||||
const Decl *Callee = Call->getDirectCallee();
|
||||
|
||||
@ -212,7 +212,7 @@ static bool populateCallees(const Stmt *StmtNode,
|
||||
/// returns true iff `SCC` contains `Func` and its' function set overlaps with
|
||||
/// `Callees`
|
||||
static bool overlap(ArrayRef<CallGraphNode *> SCC,
|
||||
const llvm::SmallSet<const Decl *, 16> &Callees,
|
||||
const llvm::SmallPtrSet<const Decl *, 16> &Callees,
|
||||
const Decl *Func) {
|
||||
bool ContainsFunc = false, Overlap = false;
|
||||
|
||||
@ -264,7 +264,7 @@ static bool hasRecursionOverStaticLoopCondVariables(const Expr *Cond,
|
||||
if (!hasStaticLocalVariable(Cond))
|
||||
return false;
|
||||
|
||||
llvm::SmallSet<const Decl *, 16> CalleesInLoop;
|
||||
llvm::SmallPtrSet<const Decl *, 16> CalleesInLoop;
|
||||
|
||||
if (!populateCallees(LoopStmt, CalleesInLoop)) {
|
||||
// If there are unresolved indirect calls, we assume there could
|
||||
|
@ -191,6 +191,9 @@ void PreferMemberInitializerCheck::check(
|
||||
if (!AssignmentToMember)
|
||||
continue;
|
||||
const FieldDecl *Field = AssignmentToMember->Field;
|
||||
// Skip if the field is inherited from a base class.
|
||||
if (Field->getParent() != Class)
|
||||
continue;
|
||||
const Expr *InitValue = AssignmentToMember->Init;
|
||||
updateAssignmentLevel(Field, InitValue, Ctor, AssignedFields);
|
||||
if (!canAdvanceAssignment(AssignedFields[Field]))
|
||||
|
@ -32,6 +32,7 @@ add_clang_library(clangTidyMiscModule STATIC
|
||||
NoRecursionCheck.cpp
|
||||
NonCopyableObjects.cpp
|
||||
NonPrivateMemberVariablesInClassesCheck.cpp
|
||||
OverrideWithDifferentVisibilityCheck.cpp
|
||||
RedundantExpressionCheck.cpp
|
||||
StaticAssertCheck.cpp
|
||||
ThrowByValueCatchByReferenceCheck.cpp
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "NoRecursionCheck.h"
|
||||
#include "NonCopyableObjects.h"
|
||||
#include "NonPrivateMemberVariablesInClassesCheck.h"
|
||||
#include "OverrideWithDifferentVisibilityCheck.h"
|
||||
#include "RedundantExpressionCheck.h"
|
||||
#include "StaticAssertCheck.h"
|
||||
#include "ThrowByValueCatchByReferenceCheck.h"
|
||||
@ -81,6 +82,8 @@ public:
|
||||
"misc-use-anonymous-namespace");
|
||||
CheckFactories.registerCheck<UseInternalLinkageCheck>(
|
||||
"misc-use-internal-linkage");
|
||||
CheckFactories.registerCheck<OverrideWithDifferentVisibilityCheck>(
|
||||
"misc-override-with-different-visibility");
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -0,0 +1,150 @@
|
||||
//===--- OverrideWithDifferentVisibilityCheck.cpp - clang-tidy ------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "OverrideWithDifferentVisibilityCheck.h"
|
||||
#include "../utils/Matchers.h"
|
||||
#include "../utils/OptionsUtils.h"
|
||||
#include "clang/ASTMatchers/ASTMatchFinder.h"
|
||||
|
||||
using namespace clang::ast_matchers;
|
||||
using namespace clang;
|
||||
|
||||
namespace {
|
||||
|
||||
AST_MATCHER(NamedDecl, isOperatorDecl) {
|
||||
DeclarationName::NameKind const NK = Node.getDeclName().getNameKind();
|
||||
return NK != DeclarationName::Identifier &&
|
||||
NK != DeclarationName::CXXConstructorName &&
|
||||
NK != DeclarationName::CXXDestructorName;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace clang::tidy {
|
||||
|
||||
template <>
|
||||
struct OptionEnumMapping<
|
||||
misc::OverrideWithDifferentVisibilityCheck::ChangeKind> {
|
||||
static llvm::ArrayRef<std::pair<
|
||||
misc::OverrideWithDifferentVisibilityCheck::ChangeKind, StringRef>>
|
||||
getEnumMapping() {
|
||||
static constexpr std::pair<
|
||||
misc::OverrideWithDifferentVisibilityCheck::ChangeKind, StringRef>
|
||||
Mapping[] = {
|
||||
{misc::OverrideWithDifferentVisibilityCheck::ChangeKind::Any,
|
||||
"any"},
|
||||
{misc::OverrideWithDifferentVisibilityCheck::ChangeKind::Widening,
|
||||
"widening"},
|
||||
{misc::OverrideWithDifferentVisibilityCheck::ChangeKind::Narrowing,
|
||||
"narrowing"},
|
||||
};
|
||||
return {Mapping};
|
||||
}
|
||||
};
|
||||
|
||||
namespace misc {
|
||||
|
||||
OverrideWithDifferentVisibilityCheck::OverrideWithDifferentVisibilityCheck(
|
||||
StringRef Name, ClangTidyContext *Context)
|
||||
: ClangTidyCheck(Name, Context),
|
||||
DetectVisibilityChange(
|
||||
Options.get("DisallowedVisibilityChange", ChangeKind::Any)),
|
||||
CheckDestructors(Options.get("CheckDestructors", false)),
|
||||
CheckOperators(Options.get("CheckOperators", false)),
|
||||
IgnoredFunctions(utils::options::parseStringList(
|
||||
Options.get("IgnoredFunctions", ""))) {}
|
||||
|
||||
void OverrideWithDifferentVisibilityCheck::storeOptions(
|
||||
ClangTidyOptions::OptionMap &Opts) {
|
||||
Options.store(Opts, "DisallowedVisibilityChange", DetectVisibilityChange);
|
||||
Options.store(Opts, "CheckDestructors", CheckDestructors);
|
||||
Options.store(Opts, "CheckOperators", CheckOperators);
|
||||
Options.store(Opts, "IgnoredFunctions",
|
||||
utils::options::serializeStringList(IgnoredFunctions));
|
||||
}
|
||||
|
||||
void OverrideWithDifferentVisibilityCheck::registerMatchers(
|
||||
MatchFinder *Finder) {
|
||||
const auto IgnoredDecl =
|
||||
namedDecl(matchers::matchesAnyListedName(IgnoredFunctions));
|
||||
const auto FilterDestructors =
|
||||
CheckDestructors ? decl() : decl(unless(cxxDestructorDecl()));
|
||||
const auto FilterOperators =
|
||||
CheckOperators ? namedDecl() : namedDecl(unless(isOperatorDecl()));
|
||||
Finder->addMatcher(
|
||||
cxxMethodDecl(
|
||||
isVirtual(), FilterDestructors, FilterOperators,
|
||||
ofClass(
|
||||
cxxRecordDecl(unless(isExpansionInSystemHeader())).bind("class")),
|
||||
forEachOverridden(cxxMethodDecl(ofClass(cxxRecordDecl().bind("base")),
|
||||
unless(IgnoredDecl))
|
||||
.bind("base_func")))
|
||||
.bind("func"),
|
||||
this);
|
||||
}
|
||||
|
||||
void OverrideWithDifferentVisibilityCheck::check(
|
||||
const MatchFinder::MatchResult &Result) {
|
||||
const auto *const MatchedFunction =
|
||||
Result.Nodes.getNodeAs<FunctionDecl>("func");
|
||||
if (!MatchedFunction->isCanonicalDecl())
|
||||
return;
|
||||
|
||||
const auto *const ParentClass =
|
||||
Result.Nodes.getNodeAs<CXXRecordDecl>("class");
|
||||
const auto *const BaseClass = Result.Nodes.getNodeAs<CXXRecordDecl>("base");
|
||||
CXXBasePaths Paths;
|
||||
if (!ParentClass->isDerivedFrom(BaseClass, Paths))
|
||||
return;
|
||||
|
||||
const auto *const OverriddenFunction =
|
||||
Result.Nodes.getNodeAs<FunctionDecl>("base_func");
|
||||
AccessSpecifier const ActualAccess = MatchedFunction->getAccess();
|
||||
AccessSpecifier OverriddenAccess = OverriddenFunction->getAccess();
|
||||
|
||||
const CXXBaseSpecifier *InheritanceWithStrictVisibility = nullptr;
|
||||
for (const CXXBasePath &Path : Paths) {
|
||||
for (const CXXBasePathElement &Elem : Path) {
|
||||
if (Elem.Base->getAccessSpecifier() > OverriddenAccess) {
|
||||
OverriddenAccess = Elem.Base->getAccessSpecifier();
|
||||
InheritanceWithStrictVisibility = Elem.Base;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ActualAccess != OverriddenAccess) {
|
||||
if (DetectVisibilityChange == ChangeKind::Widening &&
|
||||
ActualAccess > OverriddenAccess)
|
||||
return;
|
||||
if (DetectVisibilityChange == ChangeKind::Narrowing &&
|
||||
ActualAccess < OverriddenAccess)
|
||||
return;
|
||||
|
||||
if (InheritanceWithStrictVisibility) {
|
||||
diag(MatchedFunction->getLocation(),
|
||||
"visibility of function %0 is changed from %1 (through %1 "
|
||||
"inheritance of class %2) to %3")
|
||||
<< MatchedFunction << OverriddenAccess
|
||||
<< InheritanceWithStrictVisibility->getType() << ActualAccess;
|
||||
diag(InheritanceWithStrictVisibility->getBeginLoc(),
|
||||
"%0 is inherited as %1 here", DiagnosticIDs::Note)
|
||||
<< InheritanceWithStrictVisibility->getType() << OverriddenAccess;
|
||||
} else {
|
||||
diag(MatchedFunction->getLocation(),
|
||||
"visibility of function %0 is changed from %1 in class %2 to %3")
|
||||
<< MatchedFunction << OverriddenAccess << BaseClass << ActualAccess;
|
||||
}
|
||||
diag(OverriddenFunction->getLocation(), "function declared here as %0",
|
||||
DiagnosticIDs::Note)
|
||||
<< OverriddenFunction->getAccess();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace misc
|
||||
|
||||
} // namespace clang::tidy
|
@ -0,0 +1,43 @@
|
||||
//===--- OverrideWithDifferentVisibilityCheck.h - clang-tidy --*- C++ -*---===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_OVERRIDEWITHDIFFERENTVISIBILITYCHECK_H
|
||||
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_OVERRIDEWITHDIFFERENTVISIBILITYCHECK_H
|
||||
|
||||
#include "../ClangTidyCheck.h"
|
||||
|
||||
namespace clang::tidy::misc {
|
||||
|
||||
/// Finds virtual function overrides with different visibility than the function
|
||||
/// in the base class.
|
||||
///
|
||||
/// For the user-facing documentation see:
|
||||
/// http://clang.llvm.org/extra/clang-tidy/checks/misc/override-with-different-visibility.html
|
||||
class OverrideWithDifferentVisibilityCheck : public ClangTidyCheck {
|
||||
public:
|
||||
enum class ChangeKind { Any, Widening, Narrowing };
|
||||
|
||||
OverrideWithDifferentVisibilityCheck(StringRef Name,
|
||||
ClangTidyContext *Context);
|
||||
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
|
||||
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
|
||||
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
|
||||
bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
|
||||
return LangOpts.CPlusPlus;
|
||||
}
|
||||
|
||||
private:
|
||||
ChangeKind DetectVisibilityChange;
|
||||
bool CheckDestructors;
|
||||
bool CheckOperators;
|
||||
std::vector<llvm::StringRef> IgnoredFunctions;
|
||||
};
|
||||
|
||||
} // namespace clang::tidy::misc
|
||||
|
||||
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_OVERRIDEWITHDIFFERENTVISIBILITYCHECK_H
|
@ -717,7 +717,7 @@ int clangTidyMain(int argc, const char **argv) {
|
||||
EnableModuleHeadersParsing);
|
||||
std::vector<ClangTidyError> Errors =
|
||||
runClangTidy(Context, OptionsParser->getCompilations(), PathList, BaseFS,
|
||||
FixNotes, EnableCheckProfile, ProfilePrefix);
|
||||
FixNotes, EnableCheckProfile, ProfilePrefix, Quiet);
|
||||
bool FoundErrors = llvm::any_of(Errors, [](const ClangTidyError &E) {
|
||||
return E.DiagLevel == ClangTidyError::Error;
|
||||
});
|
||||
|
@ -28,6 +28,7 @@ import glob
|
||||
import json
|
||||
import multiprocessing
|
||||
import os
|
||||
import queue
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
@ -42,13 +43,6 @@ try:
|
||||
except ImportError:
|
||||
yaml = None
|
||||
|
||||
is_py2 = sys.version[0] == "2"
|
||||
|
||||
if is_py2:
|
||||
import Queue as queue
|
||||
else:
|
||||
import queue as queue
|
||||
|
||||
|
||||
def run_tidy(task_queue, lock, timeout, failed_files):
|
||||
watchdog = None
|
||||
|
@ -985,7 +985,7 @@ resolveForwardingParameters(const FunctionDecl *D, unsigned MaxDepth) {
|
||||
// Recurse on pack parameters
|
||||
size_t Depth = 0;
|
||||
const FunctionDecl *CurrentFunction = D;
|
||||
llvm::SmallSet<const FunctionTemplateDecl *, 4> SeenTemplates;
|
||||
llvm::SmallPtrSet<const FunctionTemplateDecl *, 4> SeenTemplates;
|
||||
if (const auto *Template = D->getPrimaryTemplate()) {
|
||||
SeenTemplates.insert(Template);
|
||||
}
|
||||
|
@ -833,6 +833,10 @@ bool OverlayCDB::setCompileCommand(PathRef File,
|
||||
std::unique_ptr<ProjectModules>
|
||||
OverlayCDB::getProjectModules(PathRef File) const {
|
||||
auto MDB = DelegatingCDB::getProjectModules(File);
|
||||
if (!MDB) {
|
||||
log("Failed to get compilation Database for {0}", File);
|
||||
return {};
|
||||
}
|
||||
MDB->setCommandMangler([&Mangler = Mangler](tooling::CompileCommand &Command,
|
||||
PathRef CommandPath) {
|
||||
Mangler(Command, CommandPath);
|
||||
|
@ -1876,7 +1876,7 @@ static void fillSubTypes(const SymbolID &ID,
|
||||
});
|
||||
}
|
||||
|
||||
using RecursionProtectionSet = llvm::SmallSet<const CXXRecordDecl *, 4>;
|
||||
using RecursionProtectionSet = llvm::SmallPtrSet<const CXXRecordDecl *, 4>;
|
||||
|
||||
// Extracts parents from AST and populates the type hierarchy item.
|
||||
static void fillSuperTypes(const CXXRecordDecl &CXXRD, llvm::StringRef TUPath,
|
||||
|
@ -181,7 +181,7 @@ struct ExtractionZone {
|
||||
bool requiresHoisting(const SourceManager &SM,
|
||||
const HeuristicResolver *Resolver) const {
|
||||
// First find all the declarations that happened inside extraction zone.
|
||||
llvm::SmallSet<const Decl *, 1> DeclsInExtZone;
|
||||
llvm::SmallPtrSet<const Decl *, 1> DeclsInExtZone;
|
||||
for (auto *RootStmt : RootStmts) {
|
||||
findExplicitReferences(
|
||||
RootStmt,
|
||||
|
66
clang-tools-extra/clangd/test/modules_no_cdb.test
Normal file
66
clang-tools-extra/clangd/test/modules_no_cdb.test
Normal file
@ -0,0 +1,66 @@
|
||||
# A smoke test to check that clangd works without compilation database
|
||||
#
|
||||
# Windows have different escaping modes.
|
||||
# FIXME: We should add one for windows.
|
||||
# UNSUPPORTED: system-windows
|
||||
#
|
||||
# RUN: rm -fr %t
|
||||
# RUN: mkdir -p %t
|
||||
# RUN: split-file %s %t
|
||||
#
|
||||
# RUN: sed -e "s|DIR|%/t|g" %t/definition.jsonrpc.tmpl > %t/definition.jsonrpc
|
||||
#
|
||||
# RUN: clangd -experimental-modules-support -lit-test < %t/definition.jsonrpc \
|
||||
# RUN: | FileCheck -strict-whitespace %t/definition.jsonrpc
|
||||
|
||||
#--- A.h
|
||||
void printA();
|
||||
|
||||
#--- Use.cpp
|
||||
#include "A.h"
|
||||
void foo() {
|
||||
print
|
||||
}
|
||||
|
||||
#--- definition.jsonrpc.tmpl
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 0,
|
||||
"method": "initialize",
|
||||
"params": {
|
||||
"processId": 123,
|
||||
"rootPath": "clangd",
|
||||
"capabilities": {
|
||||
"textDocument": {
|
||||
"completion": {
|
||||
"completionItem": {
|
||||
"snippetSupport": true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"trace": "off"
|
||||
}
|
||||
}
|
||||
---
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "textDocument/didOpen",
|
||||
"params": {
|
||||
"textDocument": {
|
||||
"uri": "file://DIR/Use.cpp",
|
||||
"languageId": "cpp",
|
||||
"version": 1,
|
||||
"text": "#include \"A.h\"\nvoid foo() {\n print\n}\n"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# CHECK: "message"{{.*}}printA{{.*}}(fix available)
|
||||
|
||||
---
|
||||
{"jsonrpc":"2.0","id":1,"method":"textDocument/completion","params":{"textDocument":{"uri":"file://DIR/Use.cpp"},"context":{"triggerKind":1},"position":{"line":2,"character":6}}}
|
||||
---
|
||||
{"jsonrpc":"2.0","id":2,"method":"shutdown"}
|
||||
---
|
||||
{"jsonrpc":"2.0","method":"exit"}
|
@ -104,6 +104,10 @@ Improvements to clang-query
|
||||
Improvements to clang-tidy
|
||||
--------------------------
|
||||
|
||||
- :program:`clang-tidy` no longer attemps to analyze code from system headers
|
||||
by default, greatly improving performance. This behavior is disabled if the
|
||||
`SystemHeaders` option is enabled.
|
||||
|
||||
- The :program:`run-clang-tidy.py` and :program:`clang-tidy-diff.py` scripts
|
||||
now run checks in parallel by default using all available hardware threads.
|
||||
Both scripts display the number of threads being used in their output.
|
||||
@ -115,6 +119,9 @@ Improvements to clang-tidy
|
||||
- Improved documentation of the `-line-filter` command-line flag of
|
||||
:program:`clang-tidy` and :program:`run-clang-tidy.py`.
|
||||
|
||||
- Improved :program:`clang-tidy` option `-quiet` by suppressing diagnostic
|
||||
count messages.
|
||||
|
||||
New checks
|
||||
^^^^^^^^^^
|
||||
|
||||
@ -130,6 +137,12 @@ New checks
|
||||
Checks for uses of MLIR's old/to be deprecated ``OpBuilder::create<T>`` form
|
||||
and suggests using ``T::create`` instead.
|
||||
|
||||
- New :doc:`misc-override-with-different-visibility
|
||||
<clang-tidy/checks/misc/override-with-different-visibility>` check.
|
||||
|
||||
Finds virtual function overrides with different visibility than the function
|
||||
in the base class.
|
||||
|
||||
New check aliases
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
@ -163,6 +176,10 @@ Changes in existing checks
|
||||
an additional matcher that generalizes the copy-and-swap idiom pattern
|
||||
detection.
|
||||
|
||||
- Improved :doc:`cppcoreguidelines-prefer-member-initializer
|
||||
<clang-tidy/checks/cppcoreguidelines/prefer-member-initializer>` check to
|
||||
avoid false positives on inherited members in class templates.
|
||||
|
||||
- Improved :doc:`misc-header-include-cycle
|
||||
<clang-tidy/checks/misc/header-include-cycle>` check performance.
|
||||
|
||||
|
314
clang-tools-extra/docs/clang-change-namespace.rst
Normal file
314
clang-tools-extra/docs/clang-change-namespace.rst
Normal file
@ -0,0 +1,314 @@
|
||||
======================
|
||||
Clang-Change-Namespace
|
||||
======================
|
||||
|
||||
.. contents::
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
:program:`clang-change-namespace` can be used to change the surrounding
|
||||
namespaces of class/function definitions.
|
||||
|
||||
Classes/functions in the moved namespace will have new namespaces while
|
||||
references to symbols (e.g. types, functions) which are not defined in the
|
||||
changed namespace will be correctly qualified by prepending namespace specifiers
|
||||
before them. This will try to add shortest namespace specifiers possible.
|
||||
|
||||
When a symbol reference needs to be fully-qualified, this adds a `::` prefix to
|
||||
the namespace specifiers unless the new namespace is the global namespace. For
|
||||
classes, only classes that are declared/defined in the given namespace in
|
||||
specified files will be moved: forward declarations will remain in the old
|
||||
namespace. The will be demonstrated in the next example.
|
||||
|
||||
Example usage
|
||||
-------------
|
||||
|
||||
For example, consider this `test.cc` example here with the forward declared
|
||||
class `FWD` and the defined class `A`, both in the namespace `a`.
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
namespace a {
|
||||
class FWD;
|
||||
class A {
|
||||
FWD *fwd;
|
||||
};
|
||||
} // namespace a
|
||||
|
||||
And now let's change the namespace `a` to `x`.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
clang-change-namespace \
|
||||
--old_namespace "a" \
|
||||
--new_namespace "x" \
|
||||
--file_pattern "test.cc" \
|
||||
--i \
|
||||
test.cc
|
||||
|
||||
Note that in the code below there's still the forward decalred class `FWD` that
|
||||
stayed in the namespace `a`. It wasn't moved to the new namespace because it
|
||||
wasn't defined/declared here in `a` but only forward declared.
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
namespace a {
|
||||
class FWD;
|
||||
} // namespace a
|
||||
namespace x {
|
||||
|
||||
class A {
|
||||
a::FWD *fwd;
|
||||
};
|
||||
} // namespace x
|
||||
|
||||
|
||||
Another example
|
||||
---------------
|
||||
|
||||
Consider this `test.cc` file:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
namespace na {
|
||||
class X {};
|
||||
namespace nb {
|
||||
class Y {
|
||||
X x;
|
||||
};
|
||||
} // namespace nb
|
||||
} // namespace na
|
||||
|
||||
To move the definition of class `Y` from namespace `na::nb` to `x::y`, run:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
clang-change-namespace \
|
||||
--old_namespace "na::nb" \
|
||||
--new_namespace "x::y" \
|
||||
--file_pattern "test.cc" \
|
||||
--i \
|
||||
test.cc
|
||||
|
||||
This will overwrite `test.cc` to look like this:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
namespace na {
|
||||
class X {};
|
||||
|
||||
} // namespace na
|
||||
namespace x {
|
||||
namespace y {
|
||||
class Y {
|
||||
na::X x;
|
||||
};
|
||||
} // namespace y
|
||||
} // namespace x
|
||||
|
||||
Note, that we've successfully moved the class `Y` from namespace `na::nb` to
|
||||
namespace `x::y`.
|
||||
|
||||
Caveats
|
||||
=======
|
||||
|
||||
Content already exists in new namespace
|
||||
---------------------------------------
|
||||
|
||||
Consider this `test.cc` example that defines two `class A` one inside the
|
||||
namespace `a` and one in namespace `b`:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
namespace a {
|
||||
class A {
|
||||
int classAFromWithinNamespace_a;
|
||||
};
|
||||
} // namespace a
|
||||
|
||||
namespace b {
|
||||
class A {
|
||||
int classAFromWithinNamespace_b;
|
||||
};
|
||||
} //namespace b
|
||||
|
||||
Let's move everything from namespace `a` to namespace `b`:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
clang-change-namespace \
|
||||
--old_namespace "a" \
|
||||
--new_namespace "b" \
|
||||
--file_pattern test.cc \
|
||||
test.cc
|
||||
|
||||
As expected we now have to definitions of `class A` inside the namespace `b`:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
namespace b {
|
||||
class A {
|
||||
int classAFromWithinNamespace_a;
|
||||
};
|
||||
} // namespace b
|
||||
|
||||
namespace b {
|
||||
class A {
|
||||
int classAFromWithinNamespace_b;
|
||||
};
|
||||
} //namespace b
|
||||
|
||||
The re-factoring looks correct but the code will not compile due to the name
|
||||
duplication. It is not up to the tool to ensure compilability in that sense.
|
||||
But one has to be aware of that.
|
||||
|
||||
Inline namespace doesn't work
|
||||
-----------------------------
|
||||
|
||||
Consider this usage of two versions of implementations for a `greet` function:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
namespace Greeter {
|
||||
inline namespace Version1 {
|
||||
const char* greet() { return "Hello from version 1!"; }
|
||||
} // namespace Version1
|
||||
namespace Version2 {
|
||||
const char* greet() { return "Hello from version 2!"; }
|
||||
} // namespace Version2
|
||||
} // namespace Greeter
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
printf("%s\n", Greeter::greet());
|
||||
return 0;
|
||||
}
|
||||
|
||||
Note, that currently `Greeter::greet()` will result in a call to
|
||||
`Greeter::Version1::greet()` because that's the inlined namespace.
|
||||
|
||||
Let's say you want to move one and make `Version2` the default now and remove
|
||||
the `inline` from the `Version1`. First let's try to turn `namespace Version2`
|
||||
into `inline namespace Version2`:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
clang-change-namespace \
|
||||
--old_namespace "Greeter::Version2" \
|
||||
--new_namespace "inline Version2" \
|
||||
--file_pattern main.cc main.cc
|
||||
|
||||
But this will put the `inline` keyword in the wrong place resulting in:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
namespace Greeter {
|
||||
inline namespace Version1 {
|
||||
const char* greet() { return "Hello from version 1!"; }
|
||||
} // namespace Version1
|
||||
|
||||
} // namespace Greeter
|
||||
namespace inline Greeter {
|
||||
namespace Version2 {
|
||||
const char *greet() { return "Hello from version 2!"; }
|
||||
} // namespace Version2
|
||||
} // namespace inline Greeter
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
printf("%s\n", Greeter::greet());
|
||||
return 0;
|
||||
}
|
||||
|
||||
One cannot use `:program:`clang-change-namespace` to inline a namespace.
|
||||
|
||||
Symbol references not updated
|
||||
-----------------------------
|
||||
|
||||
Consider this `test.cc` file:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
namespace old {
|
||||
struct foo {};
|
||||
} // namespace old
|
||||
|
||||
namespace b {
|
||||
old::foo g_foo;
|
||||
} // namespace b
|
||||
|
||||
Notice that namespace `b` defines a global variable of type `old::foo`. If we
|
||||
now change the name of the `old` namespace to `modern`, the reference will not
|
||||
be updated:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
clang-change-namespace \
|
||||
--old_namespace "old" \
|
||||
--new_namespace "modern" \
|
||||
--file_pattern test.cc \
|
||||
test.cc
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
namespace modern {
|
||||
struct foo {};
|
||||
} // namespace modern
|
||||
|
||||
namespace b {
|
||||
old::foo g_foo;
|
||||
} // namespace b
|
||||
|
||||
`g_foo` is still of the no longer existing type `old::foo` while instead it
|
||||
should use `modern::foo`.
|
||||
|
||||
Only symbol references in the moved namespace are updated, not outside of it.
|
||||
|
||||
|
||||
:program:`clang-change-namespace` Command Line Options
|
||||
======================================================
|
||||
|
||||
.. option:: --allowed_file=<string>
|
||||
|
||||
A file containing regexes of symbol names that are not expected to be updated
|
||||
when changing namespaces around them.
|
||||
|
||||
.. option:: --dump_result
|
||||
|
||||
Dump new file contents in YAML, if specified.
|
||||
|
||||
.. option:: --extra-arg=<string>
|
||||
|
||||
Additional argument to append to the compiler command line
|
||||
|
||||
.. option:: --extra-arg-before=<string>
|
||||
|
||||
Additional argument to prepend to the compiler command line
|
||||
|
||||
.. option:: --file_pattern=<string>
|
||||
|
||||
Only rename namespaces in files that match the given regular expression
|
||||
pattern.
|
||||
|
||||
.. option:: -i
|
||||
|
||||
Inplace edit <file>s, if specified.
|
||||
|
||||
.. option:: --new_namespace=<string>
|
||||
|
||||
New namespace. Use `""` when you target the global namespace.
|
||||
|
||||
.. option:: --old_namespace=<string>
|
||||
|
||||
Old namespace.
|
||||
|
||||
.. option:: -p <string>
|
||||
|
||||
Build path
|
||||
|
||||
.. option:: --style=<string>
|
||||
|
||||
The style name used for reformatting.
|
@ -271,6 +271,7 @@ Clang-Tidy Checks
|
||||
:doc:`misc-no-recursion <misc/no-recursion>`,
|
||||
:doc:`misc-non-copyable-objects <misc/non-copyable-objects>`,
|
||||
:doc:`misc-non-private-member-variables-in-classes <misc/non-private-member-variables-in-classes>`,
|
||||
:doc:`misc-override-with-different-visibility <misc/override-with-different-visibility>`,
|
||||
:doc:`misc-redundant-expression <misc/redundant-expression>`, "Yes"
|
||||
:doc:`misc-static-assert <misc/static-assert>`, "Yes"
|
||||
:doc:`misc-throw-by-value-catch-by-reference <misc/throw-by-value-catch-by-reference>`,
|
||||
|
@ -0,0 +1,87 @@
|
||||
.. title:: clang-tidy - misc-override-with-different-visibility
|
||||
|
||||
misc-override-with-different-visibility
|
||||
=======================================
|
||||
|
||||
Finds virtual function overrides with different visibility than the function
|
||||
in the base class. This includes for example if a virtual function declared as
|
||||
``private`` is overridden and declared as ``public`` in a subclass. The detected
|
||||
change is the modification of visibility resulting from keywords ``public``,
|
||||
``protected``, ``private`` at overridden virtual functions. The check applies to
|
||||
any normal virtual function and optionally to destructors or operators. Use of
|
||||
the ``using`` keyword is not considered as visibility change by this check.
|
||||
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
class A {
|
||||
public:
|
||||
virtual void f_pub();
|
||||
private:
|
||||
virtual void f_priv();
|
||||
};
|
||||
|
||||
class B: public A {
|
||||
public:
|
||||
void f_priv(); // warning: changed visibility from private to public
|
||||
private:
|
||||
void f_pub(); // warning: changed visibility from public to private
|
||||
};
|
||||
|
||||
class C: private A {
|
||||
// no warning: f_pub becomes private in this case but this is from the
|
||||
// private inheritance
|
||||
};
|
||||
|
||||
class D: private A {
|
||||
public:
|
||||
void f_pub(); // warning: changed visibility from private to public
|
||||
// 'f_pub' would have private access but is forced to be
|
||||
// public
|
||||
};
|
||||
|
||||
If the visibility is changed in this way, it can indicate bad design or
|
||||
programming error.
|
||||
|
||||
If a virtual function is private in a subclass but public in the base class, it
|
||||
can still be accessed from a pointer to the subclass if the pointer is converted
|
||||
to the base type. Probably private inheritance can be used instead.
|
||||
|
||||
A protected virtual function that is made public in a subclass may have valid
|
||||
use cases but similar (not exactly same) effect can be achieved with the
|
||||
``using`` keyword.
|
||||
|
||||
Options
|
||||
-------
|
||||
|
||||
.. option:: DisallowedVisibilityChange
|
||||
|
||||
Controls what kind of change to the visibility will be detected by the check.
|
||||
Possible values are `any`, `widening`, `narrowing`. For example the
|
||||
`widening` option will produce warning only if the visibility is changed
|
||||
from more restrictive (``private``) to less restrictive (``public``).
|
||||
Default value is `any`.
|
||||
|
||||
.. option:: CheckDestructors
|
||||
|
||||
If `true`, the check does apply to destructors too. Otherwise destructors
|
||||
are ignored by the check.
|
||||
Default value is `false`.
|
||||
|
||||
.. option:: CheckOperators
|
||||
|
||||
If `true`, the check does apply to overloaded C++ operators (as virtual
|
||||
member functions) too. This includes other special member functions (like
|
||||
conversions) too. This option is probably useful only in rare cases because
|
||||
operators and conversions are not often virtual functions.
|
||||
Default value is `false`.
|
||||
|
||||
.. option:: IgnoredFunctions
|
||||
|
||||
This option can be used to ignore the check at specific functions.
|
||||
To configure this option, a semicolon-separated list of function names
|
||||
should be provided. The list can contain regular expressions, in this way it
|
||||
is possible to select all functions of a specific class (like `MyClass::.*`)
|
||||
or a specific function of any class (like `my_function` or
|
||||
`::.*::my_function`). The function names are matched at the base class.
|
||||
Default value is empty string.
|
@ -111,6 +111,13 @@ Diagnostics which have a corresponding warning option, are named
|
||||
``-Wliteral-conversion`` will be reported with check name
|
||||
``clang-diagnostic-literal-conversion``.
|
||||
|
||||
Clang compiler errors (such as syntax errors, semantic errors, or other failures
|
||||
that prevent Clang from compiling the code) are reported with the check name
|
||||
``clang-diagnostic-error``. These represent fundamental compilation failures that
|
||||
must be fixed before :program:`clang-tidy` can perform its analysis. Unlike other
|
||||
diagnostics, ``clang-diagnostic-error`` cannot be disabled, as :program:`clang-tidy`
|
||||
requires valid code to function.
|
||||
|
||||
The ``-fix`` flag instructs :program:`clang-tidy` to fix found errors if
|
||||
supported by corresponding checks.
|
||||
|
||||
|
@ -17,6 +17,7 @@ Contents
|
||||
|
||||
clang-tidy/index
|
||||
clang-include-fixer
|
||||
clang-change-namespace
|
||||
modularize
|
||||
pp-trace
|
||||
clangd <https://clangd.llvm.org/>
|
||||
|
@ -650,3 +650,16 @@ struct InitFromBindingDecl {
|
||||
}
|
||||
};
|
||||
} // namespace GH82970
|
||||
|
||||
struct A {
|
||||
int m;
|
||||
};
|
||||
|
||||
struct B : A {
|
||||
B() { m = 0; }
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct C : A {
|
||||
C() { m = 0; }
|
||||
};
|
||||
|
@ -0,0 +1,14 @@
|
||||
#pragma clang system_header
|
||||
|
||||
namespace sys {
|
||||
|
||||
struct Base {
|
||||
virtual void publicF();
|
||||
};
|
||||
|
||||
struct Derived: public Base {
|
||||
private:
|
||||
void publicF() override;
|
||||
};
|
||||
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
// RUN: %check_clang_tidy %s misc-override-with-different-visibility %t -- \
|
||||
// RUN: -config="{CheckOptions: {misc-override-with-different-visibility.IgnoredFunctions: 'IgnoreAlways::.*;::a::IgnoreSelected::.*;IgnoreFunctions::f1;ignored_f'}}"
|
||||
|
||||
class IgnoreAlways {
|
||||
virtual void f();
|
||||
};
|
||||
|
||||
class IgnoreSelected {
|
||||
virtual void f();
|
||||
};
|
||||
|
||||
namespace a {
|
||||
class IgnoreAlways {
|
||||
virtual void f();
|
||||
};
|
||||
class IgnoreSelected {
|
||||
virtual void f();
|
||||
};
|
||||
}
|
||||
|
||||
namespace ignore_always {
|
||||
class Test1: public IgnoreAlways {
|
||||
public:
|
||||
void f();
|
||||
void ignored_f(int);
|
||||
};
|
||||
class Test2: public a::IgnoreAlways {
|
||||
public:
|
||||
void f();
|
||||
};
|
||||
}
|
||||
|
||||
namespace ignore_selected {
|
||||
class Test1: public IgnoreSelected {
|
||||
public:
|
||||
void f();
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'f'
|
||||
// CHECK-MESSAGES: :9:16: note: function declared here
|
||||
void ignored_f(int);
|
||||
};
|
||||
class Test2: public a::IgnoreSelected {
|
||||
public:
|
||||
void f();
|
||||
};
|
||||
}
|
||||
|
||||
class IgnoreFunctions {
|
||||
virtual void f1();
|
||||
virtual void f2();
|
||||
virtual void ignored_f();
|
||||
};
|
||||
|
||||
class IgnoreFunctionsTest: public IgnoreFunctions {
|
||||
public:
|
||||
void f1();
|
||||
void f2();
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'f2'
|
||||
// CHECK-MESSAGES: :[[@LINE-9]]:16: note: function declared here
|
||||
void ignored_f();
|
||||
};
|
@ -0,0 +1,75 @@
|
||||
// RUN: %check_clang_tidy -check-suffixes=DTORS,WIDENING,NARROWING %s misc-override-with-different-visibility %t -- \
|
||||
// RUN: -config="{CheckOptions: {misc-override-with-different-visibility.CheckDestructors: true}}"
|
||||
|
||||
// RUN: %check_clang_tidy -check-suffixes=OPS,WIDENING,NARROWING %s misc-override-with-different-visibility %t -- \
|
||||
// RUN: -config="{CheckOptions: {misc-override-with-different-visibility.CheckOperators: true}}"
|
||||
|
||||
// RUN: %check_clang_tidy -check-suffixes=WIDENING %s misc-override-with-different-visibility %t -- \
|
||||
// RUN: -config="{CheckOptions: {misc-override-with-different-visibility.DisallowedVisibilityChange: 'widening'}}"
|
||||
|
||||
// RUN: %check_clang_tidy -check-suffixes=NARROWING %s misc-override-with-different-visibility %t -- \
|
||||
// RUN: -config="{CheckOptions: {misc-override-with-different-visibility.DisallowedVisibilityChange: 'narrowing'}}"
|
||||
|
||||
namespace test_change {
|
||||
|
||||
class A {
|
||||
protected:
|
||||
virtual void f1();
|
||||
virtual void f2();
|
||||
};
|
||||
|
||||
class B: public A {
|
||||
public:
|
||||
void f1();
|
||||
// CHECK-MESSAGES-WIDENING: :[[@LINE-1]]:8: warning: visibility of function 'f1'
|
||||
// CHECK-MESSAGES-WIDENING: :[[@LINE-8]]:16: note: function declared here
|
||||
private:
|
||||
void f2();
|
||||
// CHECK-MESSAGES-NARROWING: :[[@LINE-1]]:8: warning: visibility of function 'f2'
|
||||
// CHECK-MESSAGES-NARROWING: :[[@LINE-11]]:16: note: function declared here
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace test_destructor {
|
||||
|
||||
class A {
|
||||
public:
|
||||
virtual ~A();
|
||||
};
|
||||
|
||||
class B: public A {
|
||||
protected:
|
||||
~B();
|
||||
// CHECK-MESSAGES-DTORS: :[[@LINE-1]]:3: warning: visibility of function '~B'
|
||||
// CHECK-MESSAGES-DTORS: :[[@LINE-7]]:11: note: function declared here
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace test_operator {
|
||||
|
||||
class A {
|
||||
virtual A& operator=(const A&);
|
||||
virtual A& operator++();
|
||||
virtual int operator()(int);
|
||||
virtual operator double() const;
|
||||
};
|
||||
|
||||
class B: public A {
|
||||
protected:
|
||||
A& operator=(const A&);
|
||||
// CHECK-MESSAGES-OPS: :[[@LINE-1]]:6: warning: visibility of function 'operator='
|
||||
// CHECK-MESSAGES-OPS: :[[@LINE-10]]:14: note: function declared here
|
||||
A& operator++();
|
||||
// CHECK-MESSAGES-OPS: :[[@LINE-1]]:6: warning: visibility of function 'operator++'
|
||||
// CHECK-MESSAGES-OPS: :[[@LINE-12]]:14: note: function declared here
|
||||
int operator()(int);
|
||||
// CHECK-MESSAGES-OPS: :[[@LINE-1]]:7: warning: visibility of function 'operator()'
|
||||
// CHECK-MESSAGES-OPS: :[[@LINE-14]]:15: note: function declared here
|
||||
operator double() const;
|
||||
// CHECK-MESSAGES-OPS: :[[@LINE-1]]:3: warning: visibility of function 'operator double'
|
||||
// CHECK-MESSAGES-OPS: :[[@LINE-16]]:11: note: function declared here
|
||||
};
|
||||
|
||||
}
|
@ -0,0 +1,289 @@
|
||||
// RUN: %check_clang_tidy %s misc-override-with-different-visibility %t -- -config="{CheckOptions: {misc-override-with-different-visibility.CheckDestructors: true,misc-override-with-different-visibility.CheckOperators: true}}" -- -I %S/Inputs/override-with-different-visibility
|
||||
#include <test-system-header.h>
|
||||
class A {
|
||||
public:
|
||||
virtual void pub_foo1() {}
|
||||
virtual void pub_foo2() {}
|
||||
virtual void pub_foo3() {}
|
||||
protected:
|
||||
virtual void prot_foo1();
|
||||
virtual void prot_foo2();
|
||||
virtual void prot_foo3();
|
||||
private:
|
||||
virtual void priv_foo1() {}
|
||||
virtual void priv_foo2() {}
|
||||
virtual void priv_foo3() {}
|
||||
};
|
||||
|
||||
void A::prot_foo1() {}
|
||||
void A::prot_foo2() {}
|
||||
void A::prot_foo3() {}
|
||||
|
||||
namespace test1 {
|
||||
|
||||
class B: public A {
|
||||
public:
|
||||
void pub_foo1() override {}
|
||||
void prot_foo1() override {}
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'prot_foo1' is changed from protected in class 'A' to public [misc-override-with-different-visibility]
|
||||
// CHECK-MESSAGES: :9:16: note: function declared here as protected
|
||||
void priv_foo1() override {}
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'priv_foo1' is changed from private in class 'A' to public [misc-override-with-different-visibility]
|
||||
// CHECK-MESSAGES: :13:16: note: function declared here as private
|
||||
protected:
|
||||
void pub_foo2() override {}
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'pub_foo2' is changed from public in class 'A' to protected [misc-override-with-different-visibility]
|
||||
// CHECK-MESSAGES: :6:16: note: function declared here as public
|
||||
void prot_foo2() override {}
|
||||
void priv_foo2() override {}
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'priv_foo2' is changed from private in class 'A' to protected [misc-override-with-different-visibility]
|
||||
// CHECK-MESSAGES: :14:16: note: function declared here as private
|
||||
private:
|
||||
void pub_foo3() override {}
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'pub_foo3' is changed from public in class 'A' to private [misc-override-with-different-visibility]
|
||||
// CHECK-MESSAGES: :7:16: note: function declared here as public
|
||||
void prot_foo3() override {}
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'prot_foo3' is changed from protected in class 'A' to private [misc-override-with-different-visibility]
|
||||
// CHECK-MESSAGES: :11:16: note: function declared here as protected
|
||||
void priv_foo3() override {}
|
||||
};
|
||||
|
||||
class C: public B {
|
||||
public:
|
||||
void pub_foo1() override;
|
||||
protected:
|
||||
void prot_foo1() override;
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'prot_foo1' is changed from public in class 'B' to protected [misc-override-with-different-visibility]
|
||||
// CHECK-MESSAGES: :27:8: note: function declared here as public
|
||||
private:
|
||||
void priv_foo1() override;
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'priv_foo1' is changed from public in class 'B' to private [misc-override-with-different-visibility]
|
||||
// CHECK-MESSAGES: :30:8: note: function declared here as public
|
||||
};
|
||||
|
||||
void C::prot_foo1() {}
|
||||
void C::priv_foo1() {}
|
||||
|
||||
}
|
||||
|
||||
namespace test2 {
|
||||
|
||||
class B: public A {
|
||||
public:
|
||||
void pub_foo1() override;
|
||||
protected:
|
||||
void prot_foo1() override;
|
||||
private:
|
||||
void priv_foo1() override;
|
||||
};
|
||||
|
||||
class C: public B {
|
||||
public:
|
||||
void pub_foo1() override;
|
||||
void prot_foo1() override;
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'prot_foo1' is changed from protected in class 'B' to public
|
||||
// CHECK-MESSAGES: :75:8: note: function declared here as protected
|
||||
void priv_foo1() override;
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'priv_foo1' is changed from private in class 'B' to public
|
||||
// CHECK-MESSAGES: :77:8: note: function declared here as private
|
||||
|
||||
void pub_foo2() override;
|
||||
void prot_foo2() override;
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'prot_foo2' is changed from protected in class 'A' to public
|
||||
// CHECK-MESSAGES: :10:16: note: function declared here as protected
|
||||
void priv_foo2() override;
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'priv_foo2' is changed from private in class 'A' to public
|
||||
// CHECK-MESSAGES: :14:16: note: function declared here as private
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace test3 {
|
||||
|
||||
class B: private A {
|
||||
public:
|
||||
void pub_foo1() override;
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'pub_foo1' is changed from private (through private inheritance of class 'A') to public
|
||||
// CHECK-MESSAGES: :103:10: note: 'A' is inherited as private here
|
||||
// CHECK-MESSAGES: :5:16: note: function declared here as public
|
||||
protected:
|
||||
void prot_foo1() override;
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'prot_foo1' is changed from private (through private inheritance of class 'A') to protected
|
||||
// CHECK-MESSAGES: :103:10: note: 'A' is inherited as private here
|
||||
// CHECK-MESSAGES: :9:16: note: function declared here as protected
|
||||
private:
|
||||
void priv_foo1() override;
|
||||
|
||||
public:
|
||||
void prot_foo2() override;
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'prot_foo2' is changed from private (through private inheritance of class 'A') to public
|
||||
// CHECK-MESSAGES: :103:10: note: 'A' is inherited as private here
|
||||
// CHECK-MESSAGES: :10:16: note: function declared here as protected
|
||||
void priv_foo2() override;
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'priv_foo2' is changed from private in class 'A' to public
|
||||
// CHECK-MESSAGES: :14:16: note: function declared here as private
|
||||
|
||||
private:
|
||||
void pub_foo3() override;
|
||||
void prot_foo3() override;
|
||||
};
|
||||
|
||||
class C: private A {
|
||||
};
|
||||
|
||||
class D: public C {
|
||||
public:
|
||||
void pub_foo1() override;
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'pub_foo1' is changed from private (through private inheritance of class 'A') to public
|
||||
// CHECK-MESSAGES: :131:10: note: 'A' is inherited as private here
|
||||
// CHECK-MESSAGES: :5:16: note: function declared here as public
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
namespace test4 {
|
||||
|
||||
struct Base1 {
|
||||
public:
|
||||
virtual void foo1();
|
||||
private:
|
||||
virtual void foo2();
|
||||
};
|
||||
|
||||
struct Base2 {
|
||||
public:
|
||||
virtual void foo2();
|
||||
private:
|
||||
virtual void foo1();
|
||||
};
|
||||
|
||||
struct A : public Base1, public Base2 {
|
||||
protected:
|
||||
void foo1() override;
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'foo1' is changed from private in class 'Base2' to protected
|
||||
// CHECK-MESSAGES: :158:16: note: function declared here as private
|
||||
// CHECK-MESSAGES: :[[@LINE-3]]:8: warning: visibility of function 'foo1' is changed from public in class 'Base1' to protected
|
||||
// CHECK-MESSAGES: :149:16: note: function declared here as public
|
||||
private:
|
||||
void foo2() override;
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'foo2' is changed from public in class 'Base2' to private
|
||||
// CHECK-MESSAGES: :156:16: note: function declared here as public
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace test5 {
|
||||
|
||||
struct B1: virtual public A {};
|
||||
struct B2: virtual private A {};
|
||||
struct B: public B1, public B2 {
|
||||
public:
|
||||
void pub_foo1() override;
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'pub_foo1' is changed from private (through private inheritance of class 'A') to public
|
||||
// CHECK-MESSAGES: :179:12: note: 'A' is inherited as private here
|
||||
// CHECK-MESSAGES: :5:16: note: function declared here as public
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace test_using {
|
||||
|
||||
class A {
|
||||
private:
|
||||
A(int);
|
||||
protected:
|
||||
virtual void f();
|
||||
};
|
||||
|
||||
class B: public A {
|
||||
public:
|
||||
using A::A;
|
||||
using A::f;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace test_template {
|
||||
|
||||
template <typename T>
|
||||
class A {
|
||||
protected:
|
||||
virtual T foo();
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class B: public A<T> {
|
||||
private:
|
||||
T foo() override;
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: visibility of function 'foo' is changed from protected in class 'A<int>' to private
|
||||
// CHECK-MESSAGES: :[[@LINE-8]]:13: note: function declared here as protected
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class C: private A<T> {
|
||||
public:
|
||||
T foo() override;
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: visibility of function 'foo' is changed from private (through private inheritance of class 'A<int>') to public
|
||||
// CHECK-MESSAGES: :[[@LINE-4]]:10: note: 'A<int>' is inherited as private here
|
||||
// CHECK-MESSAGES: :[[@LINE-17]]:13: note: function declared here as protected
|
||||
};
|
||||
|
||||
B<int> fB() {
|
||||
return B<int>{};
|
||||
}
|
||||
|
||||
C<int> fC() {
|
||||
return C<int>{};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace test_system_header {
|
||||
|
||||
struct SysDerived: public sys::Base {
|
||||
private:
|
||||
void publicF();
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'publicF' is changed from public in class 'Base' to private
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace test_destructor {
|
||||
|
||||
class A {
|
||||
public:
|
||||
virtual ~A();
|
||||
};
|
||||
|
||||
class B: public A {
|
||||
protected:
|
||||
~B();
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: visibility of function '~B'
|
||||
// CHECK-MESSAGES: :[[@LINE-7]]:11: note: function declared here
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace test_operator {
|
||||
|
||||
class A {
|
||||
virtual int operator()(int);
|
||||
virtual A& operator++();
|
||||
virtual operator double() const;
|
||||
};
|
||||
|
||||
class B: public A {
|
||||
protected:
|
||||
int operator()(int);
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: visibility of function 'operator()'
|
||||
// CHECK-MESSAGES: :[[@LINE-9]]:15: note: function declared here
|
||||
A& operator++();
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: visibility of function 'operator++'
|
||||
// CHECK-MESSAGES: :[[@LINE-11]]:14: note: function declared here
|
||||
operator double() const;
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: visibility of function 'operator double'
|
||||
// CHECK-MESSAGES: :[[@LINE-13]]:11: note: function declared here
|
||||
};
|
||||
|
||||
}
|
@ -10,7 +10,7 @@
|
||||
// RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='header_alias\.h' %s -- -I %t 2>&1 | FileCheck --check-prefix=CHECK_HEADER_ALIAS %s
|
||||
// RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='header_alias\.h' -quiet %s -- -I %t 2>&1 | FileCheck --check-prefix=CHECK_HEADER_ALIAS %s
|
||||
// RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='header\.h' %s -- -I %t 2>&1 | FileCheck --check-prefix=CHECK_HEADER %s
|
||||
// RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='header\.h' -quiet %s -- -I %t 2>&1 | FileCheck --check-prefix=CHECK_HEADER %s
|
||||
// RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='header\.h' -quiet %s -- -I %t 2>&1 | FileCheck --check-prefix=CHECK_HEADER --allow-empty %s
|
||||
|
||||
// Check that `-header-filter` operates on the same file paths as paths in
|
||||
// diagnostics printed by ClangTidy.
|
||||
|
@ -66,19 +66,14 @@ class A { A(int); };
|
||||
// CHECK4-NOT: warning:
|
||||
// CHECK4-QUIET-NOT: warning:
|
||||
|
||||
// CHECK: Suppressed 3 warnings (3 in non-user code)
|
||||
// CHECK: Use -header-filter=.* to display errors from all non-system headers.
|
||||
// CHECK-QUIET-NOT: Suppressed
|
||||
// CHECK2: Suppressed 1 warnings (1 in non-user code)
|
||||
// CHECK2: Use -header-filter=.* {{.*}}
|
||||
// CHECK2-QUIET-NOT: Suppressed
|
||||
// CHECK3: Suppressed 2 warnings (2 in non-user code)
|
||||
// CHECK3: Use -header-filter=.* {{.*}}
|
||||
// CHECK3-QUIET-NOT: Suppressed
|
||||
// CHECK4-NOT: Suppressed {{.*}} warnings
|
||||
// CHECK4-NOT: Use -header-filter=.* {{.*}}
|
||||
// CHECK4-QUIET-NOT: Suppressed
|
||||
// CHECK6: Suppressed 2 warnings (2 in non-user code)
|
||||
// CHECK6: Use -header-filter=.* {{.*}}
|
||||
|
||||
int x = 123;
|
||||
|
@ -0,0 +1,26 @@
|
||||
// This test ensures that the --quiet flag only suppresses the "X warnings generated"
|
||||
// message while keeping all diagnostic information including caret indicators (^).
|
||||
|
||||
// RUN: clang-tidy -checks=-*,readability-magic-numbers,clang-diagnostic-sign-compare %s -- \
|
||||
// RUN: -Wsign-compare 2>&1 | FileCheck %s --check-prefix=CHECK-NORMAL
|
||||
// RUN: clang-tidy -checks=-*,readability-magic-numbers,clang-diagnostic-sign-compare -quiet %s -- \
|
||||
// RUN: -Wsign-compare 2>&1 | FileCheck %s --check-prefix=CHECK-QUIET
|
||||
|
||||
// CHECK-NORMAL: 2 warnings generated
|
||||
// CHECK-NORMAL-DAG: warning: 42 is a magic number
|
||||
// CHECK-NORMAL-DAG: {{[ ]*\^}}
|
||||
// CHECK-NORMAL-DAG: warning: comparison of integers of different signs
|
||||
// CHECK-NORMAL-DAG: {{[ ]*~ \^ ~}}
|
||||
|
||||
// CHECK-QUIET-NOT: {{[0-9]+}} warning{{s?}} generated
|
||||
// CHECK-QUIET-DAG: warning: 42 is a magic number
|
||||
// CHECK-QUIET-DAG: {{[ ]*\^}}
|
||||
// CHECK-QUIET-DAG: warning: comparison of integers of different signs
|
||||
// CHECK-QUIET-DAG: {{[ ]*~ \^ ~}}
|
||||
|
||||
int main() {
|
||||
const int CONST_VAL = 10;
|
||||
int x = 42; // trigger 'readability-magic-numbers' with caret: ^
|
||||
unsigned int y = CONST_VAL;
|
||||
return x < y; // trigger 'clang-diagnostic-sign-compare' with caret: ^
|
||||
}
|
@ -11,9 +11,9 @@
|
||||
// RUN: clang-tidy -help | FileCheck -check-prefix=CHECK-OPT-PRESENT %s
|
||||
|
||||
// RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='.*' -system-headers=true %s -- -isystem %S/Inputs/system-headers 2>&1 | FileCheck -check-prefix=CHECK-SYSTEM-HEADERS %s
|
||||
// RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='.*' -system-headers=false %s -- -isystem %S/Inputs/system-headers 2>&1 | FileCheck -check-prefix=CHECK-NO-SYSTEM-HEADERS %s
|
||||
// RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='.*' -system-headers=false %s -- -isystem %S/Inputs/system-headers 2>&1 | FileCheck -check-prefix=CHECK-NO-SYSTEM-HEADERS --allow-empty %s
|
||||
// RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='.*' -config='SystemHeaders: true' %s -- -isystem %S/Inputs/system-headers 2>&1 | FileCheck -check-prefix=CHECK-SYSTEM-HEADERS %s
|
||||
// RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='.*' -config='SystemHeaders: false' %s -- -isystem %S/Inputs/system-headers 2>&1 | FileCheck -check-prefix=CHECK-NO-SYSTEM-HEADERS %s
|
||||
// RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='.*' -config='SystemHeaders: false' %s -- -isystem %S/Inputs/system-headers 2>&1 | FileCheck -check-prefix=CHECK-NO-SYSTEM-HEADERS --allow-empty %s
|
||||
|
||||
#include <system_header.h>
|
||||
// CHECK-SYSTEM-HEADERS: system_header.h:1:13: warning: single-argument constructors must be marked explicit
|
||||
|
@ -126,6 +126,9 @@ clang-format is turned off or back on.
|
||||
// clang-format on
|
||||
void formatted_code_again;
|
||||
|
||||
In addition, the ``OneLineFormatOffRegex`` option gives you a concise way to
|
||||
disable formatting for all of the lines that match the regular expression.
|
||||
|
||||
|
||||
Configuring Style in Code
|
||||
=========================
|
||||
@ -6483,13 +6486,51 @@ the configuration (without a prefix: ``Auto``).
|
||||
.. _SpaceInEmptyBlock:
|
||||
|
||||
**SpaceInEmptyBlock** (``Boolean``) :versionbadge:`clang-format 10` :ref:`¶ <SpaceInEmptyBlock>`
|
||||
If ``true``, spaces will be inserted into ``{}``.
|
||||
This option is **deprecated**. See ``Block`` of ``SpaceInEmptyBraces``.
|
||||
|
||||
.. _SpaceInEmptyBraces:
|
||||
|
||||
**SpaceInEmptyBraces** (``SpaceInEmptyBracesStyle``) :versionbadge:`clang-format 22` :ref:`¶ <SpaceInEmptyBraces>`
|
||||
Specifies when to insert a space in empty braces.
|
||||
|
||||
.. note::
|
||||
|
||||
This option doesn't apply to initializer braces if
|
||||
``Cpp11BracedListStyle`` is set to ``true``.
|
||||
|
||||
Possible values:
|
||||
|
||||
* ``SIEB_Always`` (in configuration: ``Always``)
|
||||
Always insert a space in empty braces.
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
void f() { }
|
||||
class Unit { };
|
||||
auto a = [] { };
|
||||
int x{ };
|
||||
|
||||
* ``SIEB_Block`` (in configuration: ``Block``)
|
||||
Only insert a space in empty blocks.
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
void f() { }
|
||||
class Unit { };
|
||||
auto a = [] { };
|
||||
int x{};
|
||||
|
||||
* ``SIEB_Never`` (in configuration: ``Never``)
|
||||
Never insert a space in empty braces.
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
void f() {}
|
||||
class Unit {};
|
||||
auto a = [] {};
|
||||
int x{};
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
true: false:
|
||||
void f() { } vs. void f() {}
|
||||
while (true) { } while (true) {}
|
||||
|
||||
.. _SpaceInEmptyParentheses:
|
||||
|
||||
|
@ -60,6 +60,10 @@ only for the linker wrapper will be forwarded to the wrapped linker job.
|
||||
--v Display the version number and exit
|
||||
-- The separator for the wrapped linker arguments
|
||||
|
||||
The linker wrapper will generate the appropriate runtime calls to register the
|
||||
generated device binary with the offloading runtime. To do this step manually we
|
||||
provide the ``llvm-offload-wrapper`` utility.
|
||||
|
||||
Relocatable Linking
|
||||
===================
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -137,6 +137,12 @@ Non-comprehensive list of changes in this release
|
||||
- ``__builtin_elementwise_max`` and ``__builtin_elementwise_min`` functions for integer types can
|
||||
now be used in constant expressions.
|
||||
|
||||
- Use of ``__has_feature`` to detect the ``ptrauth_qualifier`` and ``ptrauth_intrinsics``
|
||||
features has been deprecated, and is restricted to the arm64e target only. The
|
||||
correct method to check for these features is to test for the ``__PTRAUTH__``
|
||||
macro.
|
||||
|
||||
|
||||
New Compiler Flags
|
||||
------------------
|
||||
- New option ``-fno-sanitize-annotate-debug-info-traps`` added to disable emitting trap reasons into the debug info when compiling with trapping UBSan (e.g. ``-fsanitize-trap=undefined``).
|
||||
@ -193,6 +199,8 @@ Bug Fixes in This Version
|
||||
targets that treat ``_Float16``/``__fp16`` as native scalar types. Previously
|
||||
the warning was silently lost because the operands differed only by an implicit
|
||||
cast chain. (#GH149967).
|
||||
- Fixed a crash with incompatible pointer to integer conversions in designated
|
||||
initializers involving string literals. (#GH154046)
|
||||
|
||||
Bug Fixes to Compiler Builtins
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
@ -230,6 +238,7 @@ Bug Fixes to AST Handling
|
||||
- Fix incorrect name qualifiers applied to alias CTAD. (#GH136624)
|
||||
- Fixed ElaboratedTypes appearing within NestedNameSpecifier, which was not a
|
||||
legal representation. This is fixed because ElaboratedTypes don't exist anymore. (#GH43179) (#GH68670) (#GH92757)
|
||||
- Fix unrecognized html tag causing undesirable comment lexing (#GH152944)
|
||||
- Fix comment lexing of special command names (#GH152943)
|
||||
|
||||
Miscellaneous Bug Fixes
|
||||
@ -308,8 +317,12 @@ AST Matchers
|
||||
- Ensure ``hasBitWidth`` doesn't crash on bit widths that are dependent on template
|
||||
parameters.
|
||||
|
||||
- Add a boolean member ``IgnoreSystemHeaders`` to ``MatchFinderOptions``. This
|
||||
allows it to ignore nodes in system headers when traversing the AST.
|
||||
|
||||
clang-format
|
||||
------------
|
||||
- Add ``SpaceInEmptyBraces`` option and set it to ``Always`` for WebKit style.
|
||||
|
||||
libclang
|
||||
--------
|
||||
|
@ -66,17 +66,17 @@ supported. Uninstrumented code simply won't be accounted for in reports.
|
||||
|
||||
To compile code with Modified Condition/Decision Coverage (MC/DC) enabled,
|
||||
pass ``-fcoverage-mcdc`` in addition to the clang options specified above.
|
||||
MC/DC is an advanced form of code coverage most applicable in the embedded
|
||||
MC/DC is an advanced form of code coverage most applicable to the embedded
|
||||
space.
|
||||
|
||||
Running the instrumented program
|
||||
================================
|
||||
|
||||
The next step is to run the instrumented program. When the program exits it
|
||||
The next step is to run the instrumented program. When the program exits, it
|
||||
will write a **raw profile** to the path specified by the ``LLVM_PROFILE_FILE``
|
||||
environment variable. If that variable does not exist, the profile is written
|
||||
to ``default.profraw`` in the current directory of the program. If
|
||||
``LLVM_PROFILE_FILE`` contains a path to a non-existent directory, the missing
|
||||
``LLVM_PROFILE_FILE`` specifies a path to a non-existent directory, the missing
|
||||
directory structure will be created. Additionally, the following special
|
||||
**pattern strings** are rewritten:
|
||||
|
||||
@ -97,7 +97,7 @@ directory structure will be created. Additionally, the following special
|
||||
* "%b" expands out to the binary ID (build ID). It can be used with "%Nm" to
|
||||
avoid binary signature collisions. To use it, the program should be compiled
|
||||
with the build ID linker option (``--build-id`` for GNU ld or LLD,
|
||||
``/build-id`` for lld-link on Windows). Linux, Windows and AIX are supported.
|
||||
``/build-id`` for lld-link on Windows). Linux, Windows, and AIX are supported.
|
||||
|
||||
* "%c" expands out to nothing, but enables a mode in which profile counter
|
||||
updates are continuously synced to a file. This means that if the
|
||||
@ -128,7 +128,7 @@ and set bias to the offset between the original and the new counter location,
|
||||
at which point every subsequent counter access will be to the new location,
|
||||
which allows updating profile directly akin to the continuous mode.
|
||||
|
||||
The advantage of this approach is that doesn't require any special OS support.
|
||||
The advantage of this approach is that it doesn't require any special OS support.
|
||||
The disadvantage is the extra overhead due to additional instructions required
|
||||
for each counter access (overhead both in terms of binary size and performance)
|
||||
plus duplication of counters (i.e. one copy in the binary itself and another
|
||||
@ -137,7 +137,7 @@ other platforms by passing the ``-runtime-counter-relocation`` option to the
|
||||
backend during compilation.
|
||||
|
||||
For a program such as the `Lit <https://llvm.org/docs/CommandGuide/lit.html>`_
|
||||
testing tool which invokes other programs, it may be necessary to set
|
||||
testing tool, which invokes other programs, it may be necessary to set
|
||||
``LLVM_PROFILE_FILE`` for each invocation. The pattern strings "%p" or "%Nm"
|
||||
may help to avoid corruption due to concurrency. Note that "%p" is also a Lit
|
||||
token and needs to be escaped as "%%p".
|
||||
@ -149,7 +149,7 @@ token and needs to be escaped as "%%p".
|
||||
Creating coverage reports
|
||||
=========================
|
||||
|
||||
Raw profiles have to be **indexed** before they can be used to generate
|
||||
Raw profiles must be **indexed** before they can be used to generate
|
||||
coverage reports. This is done using the "merge" tool in ``llvm-profdata``
|
||||
(which can combine multiple raw profiles and index them at the same time):
|
||||
|
||||
@ -240,13 +240,13 @@ line-oriented report, try:
|
||||
TOTAL 13 0 100.00% 3 0 100.00% 13 0 100.00% 12 2 83.33%
|
||||
|
||||
The ``llvm-cov`` tool supports specifying a custom demangler, writing out
|
||||
reports in a directory structure, and generating html reports. For the full
|
||||
reports in a directory structure, and generating HTML reports. For the full
|
||||
list of options, please refer to the `command guide
|
||||
<https://llvm.org/docs/CommandGuide/llvm-cov.html>`_.
|
||||
|
||||
A few final notes:
|
||||
|
||||
* The ``-sparse`` flag is optional but can result in dramatically smaller
|
||||
* The ``-sparse`` flag is optional but can produce dramatically smaller
|
||||
indexed profiles. This option should not be used if the indexed profile will
|
||||
be reused for PGO.
|
||||
|
||||
@ -255,7 +255,7 @@ A few final notes:
|
||||
information directly into an existing raw profile on disk. The details are
|
||||
out of scope.
|
||||
|
||||
* The ``llvm-profdata`` tool can be used to merge together multiple raw or
|
||||
* The ``llvm-profdata`` tool can be used to merge multiple raw or
|
||||
indexed profiles. To combine profiling data from multiple runs of a program,
|
||||
try e.g:
|
||||
|
||||
@ -299,7 +299,7 @@ There are six statistics tracked in a coverage summary:
|
||||
source code that may each evaluate to either "true" or "false". These
|
||||
conditions may comprise larger boolean expressions linked by boolean logical
|
||||
operators. For example, "x = (y == 2) || (z < 10)" is a boolean expression
|
||||
that is comprised of two individual conditions, each of which evaluates to
|
||||
comprised of two individual conditions, each of which evaluates to
|
||||
either true or false, producing four total branch outcomes.
|
||||
|
||||
* Modified Condition/Decision Coverage (MC/DC) is the percentage of individual
|
||||
@ -316,7 +316,7 @@ There are six statistics tracked in a coverage summary:
|
||||
``-show-mcdc-summary`` option as long as code was also compiled using the
|
||||
clang option ``-fcoverage-mcdc``.
|
||||
|
||||
* Boolean expressions that are only comprised of one condition (and therefore
|
||||
* Boolean expressions comprised of only one condition (and therefore
|
||||
have no logical operators) are not included in MC/DC analysis and are
|
||||
trivially deducible using branch coverage.
|
||||
|
||||
@ -366,7 +366,7 @@ By default the compiler runtime uses a static initializer to determine the
|
||||
profile output path and to register a writer function. To collect profiles
|
||||
without using static initializers, do this manually:
|
||||
|
||||
* Export a ``int __llvm_profile_runtime`` symbol from each instrumented shared
|
||||
* Export an ``int __llvm_profile_runtime`` symbol from each instrumented shared
|
||||
library and executable. When the linker finds a definition of this symbol, it
|
||||
knows to skip loading the object which contains the profiling runtime's
|
||||
static initializer.
|
||||
@ -380,7 +380,7 @@ without using static initializers, do this manually:
|
||||
to ``__llvm_profile_write_file``.
|
||||
|
||||
* Forward-declare ``int __llvm_profile_write_file(void)`` and call it to write
|
||||
out a profile. This function returns 0 when it succeeds, and a non-zero value
|
||||
out a profile. This function returns 0 on success, and a non-zero value
|
||||
otherwise. Calling this function multiple times appends profile data to an
|
||||
existing on-disk raw profile.
|
||||
|
||||
@ -418,7 +418,7 @@ Collecting coverage reports for the llvm project
|
||||
================================================
|
||||
|
||||
To prepare a coverage report for llvm (and any of its sub-projects), add
|
||||
``-DLLVM_BUILD_INSTRUMENTED_COVERAGE=On`` to the cmake configuration. Raw
|
||||
``-DLLVM_BUILD_INSTRUMENTED_COVERAGE=On`` to the CMake configuration. Raw
|
||||
profiles will be written to ``$BUILD_DIR/profiles/``. To prepare an html
|
||||
report, run ``llvm/utils/prepare-code-coverage-artifact.py``.
|
||||
|
||||
@ -429,7 +429,7 @@ To specify an alternate directory for raw profiles, use
|
||||
Drawbacks and limitations
|
||||
=========================
|
||||
|
||||
* Prior to version 2.26, the GNU binutils BFD linker is not able link programs
|
||||
* Prior to version 2.26, the GNU binutils BFD linker cannot link programs
|
||||
compiled with ``-fcoverage-mapping`` in its ``--gc-sections`` mode. Possible
|
||||
workarounds include disabling ``--gc-sections``, upgrading to a newer version
|
||||
of BFD, or using the Gold linker.
|
||||
|
@ -359,7 +359,7 @@ class CXXFinalOverriderMap
|
||||
|
||||
/// A set of all the primary bases for a class.
|
||||
class CXXIndirectPrimaryBaseSet
|
||||
: public llvm::SmallSet<const CXXRecordDecl*, 32> {};
|
||||
: public llvm::SmallPtrSet<const CXXRecordDecl *, 32> {};
|
||||
|
||||
inline bool
|
||||
inheritanceModelHasVBPtrOffsetField(MSInheritanceModel Inheritance) {
|
||||
|
@ -51,6 +51,11 @@ def Col : Tag<"col"> { let EndTagForbidden = 1; }
|
||||
def Tr : Tag<"tr"> { let EndTagOptional = 1; }
|
||||
def Th : Tag<"th"> { let EndTagOptional = 1; }
|
||||
def Td : Tag<"td"> { let EndTagOptional = 1; }
|
||||
def Summary : Tag<"summary">;
|
||||
def Details : Tag<"details">;
|
||||
def Mark : Tag<"mark">;
|
||||
def Figure : Tag<"figure">;
|
||||
def FigCaption : Tag<"figcaption">;
|
||||
|
||||
// Define a list of attributes that are not safe to pass through to HTML
|
||||
// output if the input is untrusted.
|
||||
|
@ -3526,7 +3526,7 @@ protected:
|
||||
public:
|
||||
// Low-level accessor. If you just want the type defined by this node,
|
||||
// check out ASTContext::getTypeDeclType or one of
|
||||
// ASTContext::getTypedefType, ASTContext::getRecordType, etc. if you
|
||||
// ASTContext::getTypedefType, ASTContext::getTagType, etc. if you
|
||||
// already know the specific kind of node this is.
|
||||
const Type *getTypeForDecl() const {
|
||||
assert(!isa<TagDecl>(this));
|
||||
|
@ -1250,19 +1250,32 @@ public:
|
||||
SourceLocation EndLoc);
|
||||
};
|
||||
|
||||
// A structure to stand in for the recipe on a reduction. RecipeDecl is the
|
||||
// 'main' declaration used for initializaiton, which is fixed.
|
||||
struct OpenACCReductionRecipe {
|
||||
VarDecl *RecipeDecl;
|
||||
// TODO: OpenACC: this should eventually have the operations here too.
|
||||
};
|
||||
|
||||
class OpenACCReductionClause final
|
||||
: public OpenACCClauseWithVarList,
|
||||
private llvm::TrailingObjects<OpenACCReductionClause, Expr *> {
|
||||
private llvm::TrailingObjects<OpenACCReductionClause, Expr *,
|
||||
OpenACCReductionRecipe> {
|
||||
friend TrailingObjects;
|
||||
OpenACCReductionOperator Op;
|
||||
|
||||
OpenACCReductionClause(SourceLocation BeginLoc, SourceLocation LParenLoc,
|
||||
OpenACCReductionOperator Operator,
|
||||
ArrayRef<Expr *> VarList, SourceLocation EndLoc)
|
||||
ArrayRef<Expr *> VarList,
|
||||
ArrayRef<OpenACCReductionRecipe> Recipes,
|
||||
SourceLocation EndLoc)
|
||||
: OpenACCClauseWithVarList(OpenACCClauseKind::Reduction, BeginLoc,
|
||||
LParenLoc, EndLoc),
|
||||
Op(Operator) {
|
||||
setExprs(getTrailingObjects(VarList.size()), VarList);
|
||||
assert(VarList.size() == Recipes.size());
|
||||
setExprs(getTrailingObjects<Expr *>(VarList.size()), VarList);
|
||||
llvm::uninitialized_copy(Recipes, getTrailingObjects<
|
||||
OpenACCReductionRecipe > ());
|
||||
}
|
||||
|
||||
public:
|
||||
@ -1270,12 +1283,26 @@ public:
|
||||
return C->getClauseKind() == OpenACCClauseKind::Reduction;
|
||||
}
|
||||
|
||||
ArrayRef<OpenACCReductionRecipe> getRecipes() {
|
||||
return ArrayRef<OpenACCReductionRecipe>{
|
||||
getTrailingObjects<OpenACCReductionRecipe>(), getExprs().size()};
|
||||
}
|
||||
|
||||
ArrayRef<OpenACCReductionRecipe> getRecipes() const {
|
||||
return ArrayRef<OpenACCReductionRecipe>{
|
||||
getTrailingObjects<OpenACCReductionRecipe>(), getExprs().size()};
|
||||
}
|
||||
|
||||
static OpenACCReductionClause *
|
||||
Create(const ASTContext &C, SourceLocation BeginLoc, SourceLocation LParenLoc,
|
||||
OpenACCReductionOperator Operator, ArrayRef<Expr *> VarList,
|
||||
SourceLocation EndLoc);
|
||||
ArrayRef<OpenACCReductionRecipe> Recipes, SourceLocation EndLoc);
|
||||
|
||||
OpenACCReductionOperator getReductionOp() const { return Op; }
|
||||
|
||||
size_t numTrailingObjects(OverloadToken<Expr *>) const {
|
||||
return getExprs().size();
|
||||
}
|
||||
};
|
||||
|
||||
class OpenACCLinkClause final
|
||||
|
@ -6401,6 +6401,9 @@ protected:
|
||||
bool IsInjected, const Type *CanonicalType);
|
||||
|
||||
public:
|
||||
// FIXME: Temporarily renamed from `getDecl` in order to facilitate
|
||||
// rebasing, due to change in behaviour. This should be renamed back
|
||||
// to `getDecl` once the change is settled.
|
||||
TagDecl *getOriginalDecl() const { return decl; }
|
||||
|
||||
NestedNameSpecifier getQualifier() const;
|
||||
@ -6466,6 +6469,9 @@ class RecordType final : public TagType {
|
||||
using TagType::TagType;
|
||||
|
||||
public:
|
||||
// FIXME: Temporarily renamed from `getDecl` in order to facilitate
|
||||
// rebasing, due to change in behaviour. This should be renamed back
|
||||
// to `getDecl` once the change is settled.
|
||||
RecordDecl *getOriginalDecl() const {
|
||||
return reinterpret_cast<RecordDecl *>(TagType::getOriginalDecl());
|
||||
}
|
||||
@ -6483,6 +6489,9 @@ class EnumType final : public TagType {
|
||||
using TagType::TagType;
|
||||
|
||||
public:
|
||||
// FIXME: Temporarily renamed from `getDecl` in order to facilitate
|
||||
// rebasing, due to change in behaviour. This should be renamed back
|
||||
// to `getDecl` once the change is settled.
|
||||
EnumDecl *getOriginalDecl() const {
|
||||
return reinterpret_cast<EnumDecl *>(TagType::getOriginalDecl());
|
||||
}
|
||||
@ -6515,6 +6524,9 @@ class InjectedClassNameType final : public TagType {
|
||||
bool IsInjected, const Type *CanonicalType);
|
||||
|
||||
public:
|
||||
// FIXME: Temporarily renamed from `getDecl` in order to facilitate
|
||||
// rebasing, due to change in behaviour. This should be renamed back
|
||||
// to `getDecl` once the change is settled.
|
||||
CXXRecordDecl *getOriginalDecl() const {
|
||||
return reinterpret_cast<CXXRecordDecl *>(TagType::getOriginalDecl());
|
||||
}
|
||||
|
@ -135,10 +135,15 @@ public:
|
||||
llvm::StringMap<llvm::TimeRecord> &Records;
|
||||
};
|
||||
|
||||
MatchFinderOptions() {}
|
||||
|
||||
/// Enables per-check timers.
|
||||
///
|
||||
/// It prints a report after match.
|
||||
std::optional<Profiling> CheckProfiling;
|
||||
|
||||
/// Avoids matching declarations in system headers.
|
||||
bool IgnoreSystemHeaders{false};
|
||||
};
|
||||
|
||||
MatchFinder(MatchFinderOptions Options = MatchFinderOptions());
|
||||
|
@ -19,14 +19,35 @@
|
||||
#define LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_H
|
||||
#include "clang/Analysis/AnalysisDeclContext.h"
|
||||
#include "clang/Analysis/CFG.h"
|
||||
#include "clang/Basic/SourceLocation.h"
|
||||
#include "llvm/ADT/DenseMapInfo.h"
|
||||
#include "llvm/ADT/ImmutableMap.h"
|
||||
#include "llvm/ADT/ImmutableSet.h"
|
||||
#include "llvm/ADT/StringMap.h"
|
||||
#include <memory>
|
||||
|
||||
namespace clang::lifetimes {
|
||||
|
||||
/// Enum to track the confidence level of a potential error.
|
||||
enum class Confidence {
|
||||
None,
|
||||
Maybe, // Reported as a potential error (-Wlifetime-safety-strict)
|
||||
Definite // Reported as a definite error (-Wlifetime-safety-permissive)
|
||||
};
|
||||
|
||||
class LifetimeSafetyReporter {
|
||||
public:
|
||||
LifetimeSafetyReporter() = default;
|
||||
virtual ~LifetimeSafetyReporter() = default;
|
||||
|
||||
virtual void reportUseAfterFree(const Expr *IssueExpr, const Expr *UseExpr,
|
||||
SourceLocation FreeLoc,
|
||||
Confidence Confidence) {}
|
||||
};
|
||||
|
||||
/// The main entry point for the analysis.
|
||||
void runLifetimeSafetyAnalysis(AnalysisDeclContext &AC);
|
||||
void runLifetimeSafetyAnalysis(AnalysisDeclContext &AC,
|
||||
LifetimeSafetyReporter *Reporter);
|
||||
|
||||
namespace internal {
|
||||
// Forward declarations of internal types.
|
||||
@ -53,6 +74,7 @@ template <typename Tag> struct ID {
|
||||
IDBuilder.AddInteger(Value);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Tag>
|
||||
inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, ID<Tag> ID) {
|
||||
return OS << ID.Value;
|
||||
@ -78,7 +100,8 @@ using ProgramPoint = const Fact *;
|
||||
/// encapsulates the various dataflow analyses.
|
||||
class LifetimeSafetyAnalysis {
|
||||
public:
|
||||
LifetimeSafetyAnalysis(AnalysisDeclContext &AC);
|
||||
LifetimeSafetyAnalysis(AnalysisDeclContext &AC,
|
||||
LifetimeSafetyReporter *Reporter);
|
||||
~LifetimeSafetyAnalysis();
|
||||
|
||||
void run();
|
||||
@ -87,7 +110,7 @@ public:
|
||||
LoanSet getLoansAtPoint(OriginID OID, ProgramPoint PP) const;
|
||||
|
||||
/// Returns the set of loans that have expired at a specific program point.
|
||||
LoanSet getExpiredLoansAtPoint(ProgramPoint PP) const;
|
||||
std::vector<LoanID> getExpiredLoansAtPoint(ProgramPoint PP) const;
|
||||
|
||||
/// Finds the OriginID for a given declaration.
|
||||
/// Returns a null optional if not found.
|
||||
@ -110,6 +133,7 @@ public:
|
||||
|
||||
private:
|
||||
AnalysisDeclContext &AC;
|
||||
LifetimeSafetyReporter *Reporter;
|
||||
std::unique_ptr<LifetimeFactory> Factory;
|
||||
std::unique_ptr<FactManager> FactMgr;
|
||||
std::unique_ptr<LoanPropagationAnalysis> LoanPropagation;
|
||||
@ -118,4 +142,25 @@ private:
|
||||
} // namespace internal
|
||||
} // namespace clang::lifetimes
|
||||
|
||||
namespace llvm {
|
||||
template <typename Tag>
|
||||
struct DenseMapInfo<clang::lifetimes::internal::ID<Tag>> {
|
||||
using ID = clang::lifetimes::internal::ID<Tag>;
|
||||
|
||||
static inline ID getEmptyKey() {
|
||||
return {DenseMapInfo<uint32_t>::getEmptyKey()};
|
||||
}
|
||||
|
||||
static inline ID getTombstoneKey() {
|
||||
return {DenseMapInfo<uint32_t>::getTombstoneKey()};
|
||||
}
|
||||
|
||||
static unsigned getHashValue(const ID &Val) {
|
||||
return DenseMapInfo<uint32_t>::getHashValue(Val.Value);
|
||||
}
|
||||
|
||||
static bool isEqual(const ID &LHS, const ID &RHS) { return LHS == RHS; }
|
||||
};
|
||||
} // namespace llvm
|
||||
|
||||
#endif // LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_H
|
||||
|
@ -42,6 +42,18 @@ struct ContextSensitiveOptions {
|
||||
unsigned Depth = 2;
|
||||
};
|
||||
|
||||
/// A simple representation of essential elements of the logical context used in
|
||||
/// environments. Designed for import/export for applications requiring
|
||||
/// serialization support.
|
||||
struct SimpleLogicalContext {
|
||||
// Global invariant that applies for all definitions in the context.
|
||||
const Formula *Invariant;
|
||||
// Flow-condition tokens in the context.
|
||||
llvm::DenseMap<Atom, const Formula *> TokenDefs;
|
||||
// Dependencies between flow-condition definitions.
|
||||
llvm::DenseMap<Atom, llvm::DenseSet<Atom>> TokenDeps;
|
||||
};
|
||||
|
||||
/// Owns objects that encompass the state of a program and stores context that
|
||||
/// is used during dataflow analysis.
|
||||
class DataflowAnalysisContext {
|
||||
@ -140,6 +152,15 @@ public:
|
||||
/// Adds `Constraint` to the flow condition identified by `Token`.
|
||||
void addFlowConditionConstraint(Atom Token, const Formula &Constraint);
|
||||
|
||||
/// Adds `Deps` to the dependencies of the flow condition identified by
|
||||
/// `Token`. Intended for use in deserializing contexts. The formula alone
|
||||
/// doesn't have enough information to indicate its deps.
|
||||
void addFlowConditionDeps(Atom Token, const llvm::DenseSet<Atom> &Deps) {
|
||||
// Avoid creating an entry for `Token` with an empty set.
|
||||
if (!Deps.empty())
|
||||
FlowConditionDeps[Token].insert(Deps.begin(), Deps.end());
|
||||
}
|
||||
|
||||
/// Creates a new flow condition with the same constraints as the flow
|
||||
/// condition identified by `Token` and returns its token.
|
||||
Atom forkFlowCondition(Atom Token);
|
||||
@ -207,6 +228,14 @@ public:
|
||||
return {};
|
||||
}
|
||||
|
||||
/// Export the logical-context portions of `AC`, limited to the given target
|
||||
/// flow-condition tokens.
|
||||
SimpleLogicalContext
|
||||
exportLogicalContext(llvm::DenseSet<dataflow::Atom> TargetTokens) const;
|
||||
|
||||
/// Initializes this context's "logical" components with `LC`.
|
||||
void initLogicalContext(SimpleLogicalContext LC);
|
||||
|
||||
private:
|
||||
friend class Environment;
|
||||
|
||||
@ -228,6 +257,11 @@ private:
|
||||
DataflowAnalysisContext(Solver &S, std::unique_ptr<Solver> &&OwnedSolver,
|
||||
Options Opts);
|
||||
|
||||
/// Computes the transitive closure of dependencies of (flow-condition)
|
||||
/// `Tokens`. That is, the set of flow-condition tokens reachable from
|
||||
/// `Tokens` in the dependency graph.
|
||||
llvm::DenseSet<Atom> collectDependencies(llvm::DenseSet<Atom> Tokens) const;
|
||||
|
||||
// Extends the set of modeled field declarations.
|
||||
void addModeledFields(const FieldSet &Fields);
|
||||
|
||||
|
@ -157,10 +157,18 @@ public:
|
||||
};
|
||||
|
||||
/// Creates an environment that uses `DACtx` to store objects that encompass
|
||||
/// the state of a program.
|
||||
/// the state of a program. `FlowConditionToken` sets the flow condition
|
||||
/// associated with the environment. Generally, new environments should be
|
||||
/// initialized with a fresh token, by using one of the other
|
||||
/// constructors. This constructor is for specialized use, including
|
||||
/// deserialization and delegation from other constructors.
|
||||
Environment(DataflowAnalysisContext &DACtx, Atom FlowConditionToken)
|
||||
: DACtx(&DACtx), FlowConditionToken(FlowConditionToken) {}
|
||||
|
||||
/// Creates an environment that uses `DACtx` to store objects that encompass
|
||||
/// the state of a program. Populates a fresh atom as flow condition token.
|
||||
explicit Environment(DataflowAnalysisContext &DACtx)
|
||||
: DACtx(&DACtx),
|
||||
FlowConditionToken(DACtx.arena().makeFlowConditionToken()) {}
|
||||
: Environment(DACtx, DACtx.arena().makeFlowConditionToken()) {}
|
||||
|
||||
/// Creates an environment that uses `DACtx` to store objects that encompass
|
||||
/// the state of a program, with `S` as the statement to analyze.
|
||||
|
@ -85,21 +85,17 @@ public:
|
||||
}
|
||||
|
||||
using AtomNames = llvm::DenseMap<Atom, std::string>;
|
||||
// Produce a stable human-readable representation of this formula.
|
||||
// For example: (V3 | !(V1 & V2))
|
||||
// If AtomNames is provided, these override the default V0, V1... names.
|
||||
/// Produces a stable human-readable representation of this formula.
|
||||
/// For example: (V3 | !(V1 & V2))
|
||||
/// If AtomNames is provided, these override the default V0, V1... names.
|
||||
void print(llvm::raw_ostream &OS, const AtomNames * = nullptr) const;
|
||||
|
||||
// Allocate Formulas using Arena rather than calling this function directly.
|
||||
/// Allocates Formulas using Arena rather than calling this function directly.
|
||||
static const Formula &create(llvm::BumpPtrAllocator &Alloc, Kind K,
|
||||
ArrayRef<const Formula *> Operands,
|
||||
unsigned Value = 0);
|
||||
|
||||
private:
|
||||
Formula() = default;
|
||||
Formula(const Formula &) = delete;
|
||||
Formula &operator=(const Formula &) = delete;
|
||||
|
||||
/// Count of operands (sub-formulas) associated with Formulas of kind `K`.
|
||||
static unsigned numOperands(Kind K) {
|
||||
switch (K) {
|
||||
case AtomRef:
|
||||
@ -116,6 +112,11 @@ private:
|
||||
llvm_unreachable("Unhandled Formula::Kind enum");
|
||||
}
|
||||
|
||||
private:
|
||||
Formula() = default;
|
||||
Formula(const Formula &) = delete;
|
||||
Formula &operator=(const Formula &) = delete;
|
||||
|
||||
Kind FormulaKind;
|
||||
// Some kinds of formula have scalar values, e.g. AtomRef's atom number.
|
||||
unsigned Value;
|
||||
|
@ -0,0 +1,40 @@
|
||||
//=== FormulaSerialization.h - Formula De/Serialization support -*- 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_ANALYSIS_FLOWSENSITIVE_FORMULA_SERIALIZATION_H
|
||||
#define LLVM_CLANG_ANALYSIS_FLOWSENSITIVE_FORMULA_SERIALIZATION_H
|
||||
|
||||
#include "clang/Analysis/FlowSensitive/Arena.h"
|
||||
#include "clang/Analysis/FlowSensitive/Formula.h"
|
||||
#include "clang/Basic/LLVM.h"
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
#include "llvm/ADT/DenseMapInfo.h"
|
||||
#include "llvm/Support/Allocator.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include <cassert>
|
||||
#include <string>
|
||||
|
||||
namespace clang::dataflow {
|
||||
|
||||
/// Prints `F` to `OS` in a compact format, optimized for easy parsing
|
||||
/// (deserialization) rather than human use.
|
||||
void serializeFormula(const Formula &F, llvm::raw_ostream &OS);
|
||||
|
||||
/// Parses `Str` to build a serialized Formula.
|
||||
/// @returns error on parse failure or if parsing does not fully consume `Str`.
|
||||
/// @param A used to construct the formula components.
|
||||
/// @param AtomMap maps serialized Atom identifiers (unsigned ints) to Atoms.
|
||||
/// This map is provided by the caller to enable consistency across
|
||||
/// multiple formulas in a single file.
|
||||
llvm::Expected<const Formula *>
|
||||
parseFormula(llvm::StringRef Str, Arena &A,
|
||||
llvm::DenseMap<unsigned, Atom> &AtomMap);
|
||||
|
||||
} // namespace clang::dataflow
|
||||
#endif
|
@ -580,6 +580,8 @@ TARGET_BUILTIN(__builtin_ppc_bcdsub_p, "iiV16UcV16Uc", "",
|
||||
"isa-v207-instructions")
|
||||
|
||||
// P9 Binary-coded decimal (BCD) builtins.
|
||||
TARGET_BUILTIN(__builtin_ppc_bcdcopysign, "V16UcV16UcV16Uc", "", "power9-vector")
|
||||
TARGET_BUILTIN(__builtin_ppc_bcdsetsign, "V16UcV16UcUc", "t", "power9-vector")
|
||||
TARGET_BUILTIN(__builtin_ppc_national2packed, "V16UcV16UcUc", "t", "power9-vector")
|
||||
TARGET_BUILTIN(__builtin_ppc_packed2national, "V16UcV16Uc", "", "power9-vector")
|
||||
TARGET_BUILTIN(__builtin_ppc_packed2zoned, "V16UcV16UcUc", "t", "power9-vector")
|
||||
|
@ -268,7 +268,6 @@ let Header = "emmintrin.h", Attributes = [NoThrow, RequireDeclaration] in {
|
||||
}
|
||||
|
||||
let Features = "sse2", Attributes = [NoThrow, Const, RequiredVectorWidth<128>] in {
|
||||
def pmuludq128 : X86Builtin<"_Vector<2, long long int>(_Vector<4, int>, _Vector<4, int>)">;
|
||||
def psraw128 : X86Builtin<"_Vector<8, short>(_Vector<8, short>, _Vector<8, short>)">;
|
||||
def psrad128 : X86Builtin<"_Vector<4, int>(_Vector<4, int>, _Vector<4, int>)">;
|
||||
def psrlw128 : X86Builtin<"_Vector<8, short>(_Vector<8, short>, _Vector<8, short>)">;
|
||||
@ -290,6 +289,10 @@ let Features = "sse2", Attributes = [NoThrow, Const, RequiredVectorWidth<128>] i
|
||||
def psrldqi128_byteshift : X86Builtin<"_Vector<2, long long int>(_Vector<2, long long int>, _Constant int)">;
|
||||
}
|
||||
|
||||
let Features = "sse2", Attributes = [NoThrow, Const, Constexpr, RequiredVectorWidth<128>] in {
|
||||
def pmuludq128 : X86Builtin<"_Vector<2, long long int>(_Vector<4, int>, _Vector<4, int>)">;
|
||||
}
|
||||
|
||||
let Features = "sse3", Attributes = [NoThrow] in {
|
||||
def monitor : X86Builtin<"void(void const *, unsigned int, unsigned int)">;
|
||||
def mwait : X86Builtin<"void(unsigned int, unsigned int)">;
|
||||
@ -312,7 +315,6 @@ let Features = "sse4.1", Attributes = [NoThrow, Const, RequiredVectorWidth<128>]
|
||||
def blendvpd : X86Builtin<"_Vector<2, double>(_Vector<2, double>, _Vector<2, double>, _Vector<2, double>)">;
|
||||
def blendvps : X86Builtin<"_Vector<4, float>(_Vector<4, float>, _Vector<4, float>, _Vector<4, float>)">;
|
||||
def packusdw128 : X86Builtin<"_Vector<8, short>(_Vector<4, int>, _Vector<4, int>)">;
|
||||
def pmuldq128 : X86Builtin<"_Vector<2, long long int>(_Vector<4, int>, _Vector<4, int>)">;
|
||||
def roundps : X86Builtin<"_Vector<4, float>(_Vector<4, float>, _Constant int)">;
|
||||
def roundss : X86Builtin<"_Vector<4, float>(_Vector<4, float>, _Vector<4, float>, _Constant int)">;
|
||||
def roundsd : X86Builtin<"_Vector<2, double>(_Vector<2, double>, _Vector<2, double>, _Constant int)">;
|
||||
@ -329,6 +331,10 @@ let Features = "sse4.1", Attributes = [NoThrow, Const, RequiredVectorWidth<128>]
|
||||
def vec_set_v4si : X86Builtin<"_Vector<4, int>(_Vector<4, int>, int, _Constant int)">;
|
||||
}
|
||||
|
||||
let Features = "sse4.1", Attributes = [NoThrow, Const, Constexpr, RequiredVectorWidth<128>] in {
|
||||
def pmuldq128 : X86Builtin<"_Vector<2, long long int>(_Vector<4, int>, _Vector<4, int>)">;
|
||||
}
|
||||
|
||||
let Features = "sse4.2", Attributes = [NoThrow, Const, RequiredVectorWidth<128>] in {
|
||||
def pcmpistrm128 : X86Builtin<"_Vector<16, char>(_Vector<16, char>, _Vector<16, char>, _Constant char)">;
|
||||
def pcmpistri128 : X86Builtin<"int(_Vector<16, char>, _Vector<16, char>, _Constant char)">;
|
||||
@ -580,9 +586,7 @@ let Features = "avx2", Attributes = [NoThrow, Const, RequiredVectorWidth<256>] i
|
||||
def pmaddubsw256 : X86Builtin<"_Vector<16, short>(_Vector<32, char>, _Vector<32, char>)">;
|
||||
def pmaddwd256 : X86Builtin<"_Vector<8, int>(_Vector<16, short>, _Vector<16, short>)">;
|
||||
def pmovmskb256 : X86Builtin<"int(_Vector<32, char>)">;
|
||||
def pmuldq256 : X86Builtin<"_Vector<4, long long int>(_Vector<8, int>, _Vector<8, int>)">;
|
||||
def pmulhrsw256 : X86Builtin<"_Vector<16, short>(_Vector<16, short>, _Vector<16, short>)">;
|
||||
def pmuludq256 : X86Builtin<"_Vector<4, long long int>(_Vector<8, int>, _Vector<8, int>)">;
|
||||
def psadbw256 : X86Builtin<"_Vector<4, long long int>(_Vector<32, char>, _Vector<32, char>)">;
|
||||
def pshufb256 : X86Builtin<"_Vector<32, char>(_Vector<32, char>, _Vector<32, char>)">;
|
||||
def pshufd256 : X86Builtin<"_Vector<8, int>(_Vector<8, int>, _Constant int)">;
|
||||
@ -620,6 +624,11 @@ let Features = "avx2", Attributes = [NoThrow, Const, RequiredVectorWidth<256>] i
|
||||
def insert128i256 : X86Builtin<"_Vector<4, long long int>(_Vector<4, long long int>, _Vector<2, long long int>, _Constant int)">;
|
||||
}
|
||||
|
||||
let Features = "avx2", Attributes = [NoThrow, Const, Constexpr, RequiredVectorWidth<256>] in {
|
||||
def pmuldq256 : X86Builtin<"_Vector<4, long long int>(_Vector<8, int>, _Vector<8, int>)">;
|
||||
def pmuludq256 : X86Builtin<"_Vector<4, long long int>(_Vector<8, int>, _Vector<8, int>)">;
|
||||
}
|
||||
|
||||
let Features = "avx2", Attributes = [NoThrow, Const, Constexpr, RequiredVectorWidth<256>] in {
|
||||
def pmulhuw256 : X86Builtin<"_Vector<16, unsigned short>(_Vector<16, unsigned short>, _Vector<16, unsigned short>)">;
|
||||
def pmulhw256 : X86Builtin<"_Vector<16, short>(_Vector<16, short>, _Vector<16, short>)">;
|
||||
@ -1078,6 +1087,9 @@ let Features = "avx512f,evex512", Attributes = [NoThrow, Const, RequiredVectorWi
|
||||
def cvtpd2ps512_mask : X86Builtin<"_Vector<8, float>(_Vector<8, double>, _Vector<8, float>, unsigned char, _Constant int)">;
|
||||
def vcvtps2ph512_mask : X86Builtin<"_Vector<16, short>(_Vector<16, float>, _Constant int, _Vector<16, short>, unsigned short)">;
|
||||
def vcvtph2ps512_mask : X86Builtin<"_Vector<16, float>(_Vector<16, short>, _Vector<16, float>, unsigned short, _Constant int)">;
|
||||
}
|
||||
|
||||
let Features = "avx512f,evex512", Attributes = [NoThrow, Const, Constexpr, RequiredVectorWidth<512>] in {
|
||||
def pmuldq512 : X86Builtin<"_Vector<8, long long int>(_Vector<16, int>, _Vector<16, int>)">;
|
||||
def pmuludq512 : X86Builtin<"_Vector<8, long long int>(_Vector<16, int>, _Vector<16, int>)">;
|
||||
}
|
||||
@ -4118,99 +4130,99 @@ let Features = "avx512fp16,evex512", Attributes = [NoThrow, Const, RequiredVecto
|
||||
def vfcmulcph512_mask : X86Builtin<"_Vector<16, float>(_Vector<16, float>, _Vector<16, float>, _Vector<16, float>, unsigned short, _Constant int)">;
|
||||
}
|
||||
|
||||
let Features = "avx512bw,avx512vl", Attributes = [NoThrow, Const, RequiredVectorWidth<128>] in {
|
||||
let Features = "avx512bw,avx512vl", Attributes = [NoThrow, Const, Constexpr, RequiredVectorWidth<128>] in {
|
||||
def selectb_128 : X86Builtin<"_Vector<16, char>(unsigned short, _Vector<16, char>, _Vector<16, char>)">;
|
||||
}
|
||||
|
||||
let Features = "avx512bw,avx512vl", Attributes = [NoThrow, Const, RequiredVectorWidth<256>] in {
|
||||
let Features = "avx512bw,avx512vl", Attributes = [NoThrow, Const, Constexpr, RequiredVectorWidth<256>] in {
|
||||
def selectb_256 : X86Builtin<"_Vector<32, char>(unsigned int, _Vector<32, char>, _Vector<32, char>)">;
|
||||
}
|
||||
|
||||
let Features = "avx512bw,evex512", Attributes = [NoThrow, Const, RequiredVectorWidth<512>] in {
|
||||
let Features = "avx512bw,evex512", Attributes = [NoThrow, Const, Constexpr, RequiredVectorWidth<512>] in {
|
||||
def selectb_512 : X86Builtin<"_Vector<64, char>(unsigned long long int, _Vector<64, char>, _Vector<64, char>)">;
|
||||
}
|
||||
|
||||
let Features = "avx512bw,avx512vl", Attributes = [NoThrow, Const, RequiredVectorWidth<128>] in {
|
||||
let Features = "avx512bw,avx512vl", Attributes = [NoThrow, Const, Constexpr, RequiredVectorWidth<128>] in {
|
||||
def selectw_128 : X86Builtin<"_Vector<8, short>(unsigned char, _Vector<8, short>, _Vector<8, short>)">;
|
||||
}
|
||||
|
||||
let Features = "avx512bw,avx512vl", Attributes = [NoThrow, Const, RequiredVectorWidth<256>] in {
|
||||
let Features = "avx512bw,avx512vl", Attributes = [NoThrow, Const, Constexpr, RequiredVectorWidth<256>] in {
|
||||
def selectw_256 : X86Builtin<"_Vector<16, short>(unsigned short, _Vector<16, short>, _Vector<16, short>)">;
|
||||
}
|
||||
|
||||
let Features = "avx512bw,evex512", Attributes = [NoThrow, Const, RequiredVectorWidth<512>] in {
|
||||
let Features = "avx512bw,evex512", Attributes = [NoThrow, Const, Constexpr, RequiredVectorWidth<512>] in {
|
||||
def selectw_512 : X86Builtin<"_Vector<32, short>(unsigned int, _Vector<32, short>, _Vector<32, short>)">;
|
||||
}
|
||||
|
||||
let Features = "avx512vl", Attributes = [NoThrow, Const, RequiredVectorWidth<128>] in {
|
||||
let Features = "avx512vl", Attributes = [NoThrow, Const, Constexpr, RequiredVectorWidth<128>] in {
|
||||
def selectd_128 : X86Builtin<"_Vector<4, int>(unsigned char, _Vector<4, int>, _Vector<4, int>)">;
|
||||
}
|
||||
|
||||
let Features = "avx512vl", Attributes = [NoThrow, Const, RequiredVectorWidth<256>] in {
|
||||
let Features = "avx512vl", Attributes = [NoThrow, Const, Constexpr, RequiredVectorWidth<256>] in {
|
||||
def selectd_256 : X86Builtin<"_Vector<8, int>(unsigned char, _Vector<8, int>, _Vector<8, int>)">;
|
||||
}
|
||||
|
||||
let Features = "avx512f,evex512", Attributes = [NoThrow, Const, RequiredVectorWidth<512>] in {
|
||||
let Features = "avx512f,evex512", Attributes = [NoThrow, Const, Constexpr, RequiredVectorWidth<512>] in {
|
||||
def selectd_512 : X86Builtin<"_Vector<16, int>(unsigned short, _Vector<16, int>, _Vector<16, int>)">;
|
||||
}
|
||||
|
||||
let Features = "avx512fp16,avx512vl", Attributes = [NoThrow, Const, RequiredVectorWidth<128>] in {
|
||||
let Features = "avx512fp16,avx512vl", Attributes = [NoThrow, Const, Constexpr, RequiredVectorWidth<128>] in {
|
||||
def selectph_128 : X86Builtin<"_Vector<8, _Float16>(unsigned char, _Vector<8, _Float16>, _Vector<8, _Float16>)">;
|
||||
}
|
||||
|
||||
let Features = "avx512fp16,avx512vl", Attributes = [NoThrow, Const, RequiredVectorWidth<256>] in {
|
||||
let Features = "avx512fp16,avx512vl", Attributes = [NoThrow, Const, Constexpr, RequiredVectorWidth<256>] in {
|
||||
def selectph_256 : X86Builtin<"_Vector<16, _Float16>(unsigned short, _Vector<16, _Float16>, _Vector<16, _Float16>)">;
|
||||
}
|
||||
|
||||
let Features = "avx512fp16,evex512", Attributes = [NoThrow, Const, RequiredVectorWidth<512>] in {
|
||||
let Features = "avx512fp16,evex512", Attributes = [NoThrow, Const, Constexpr, RequiredVectorWidth<512>] in {
|
||||
def selectph_512 : X86Builtin<"_Vector<32, _Float16>(unsigned int, _Vector<32, _Float16>, _Vector<32, _Float16>)">;
|
||||
}
|
||||
|
||||
let Features = "avx512bf16,avx512vl", Attributes = [NoThrow, Const, RequiredVectorWidth<128>] in {
|
||||
let Features = "avx512bf16,avx512vl", Attributes = [NoThrow, Const, Constexpr, RequiredVectorWidth<128>] in {
|
||||
def selectpbf_128 : X86Builtin<"_Vector<8, __bf16>(unsigned char, _Vector<8, __bf16>, _Vector<8, __bf16>)">;
|
||||
}
|
||||
|
||||
let Features = "avx512bf16,avx512vl", Attributes = [NoThrow, Const, RequiredVectorWidth<256>] in {
|
||||
let Features = "avx512bf16,avx512vl", Attributes = [NoThrow, Const, Constexpr, RequiredVectorWidth<256>] in {
|
||||
def selectpbf_256 : X86Builtin<"_Vector<16, __bf16>(unsigned short, _Vector<16, __bf16>, _Vector<16, __bf16>)">;
|
||||
}
|
||||
|
||||
let Features = "avx512bf16,evex512", Attributes = [NoThrow, Const, RequiredVectorWidth<512>] in {
|
||||
let Features = "avx512bf16,evex512", Attributes = [NoThrow, Const, Constexpr, RequiredVectorWidth<512>] in {
|
||||
def selectpbf_512 : X86Builtin<"_Vector<32, __bf16>(unsigned int, _Vector<32, __bf16>, _Vector<32, __bf16>)">;
|
||||
}
|
||||
|
||||
let Features = "avx512vl", Attributes = [NoThrow, Const, RequiredVectorWidth<128>] in {
|
||||
let Features = "avx512vl", Attributes = [NoThrow, Const, Constexpr, RequiredVectorWidth<128>] in {
|
||||
def selectq_128 : X86Builtin<"_Vector<2, long long int>(unsigned char, _Vector<2, long long int>, _Vector<2, long long int>)">;
|
||||
}
|
||||
|
||||
let Features = "avx512vl", Attributes = [NoThrow, Const, RequiredVectorWidth<256>] in {
|
||||
let Features = "avx512vl", Attributes = [NoThrow, Const, Constexpr, RequiredVectorWidth<256>] in {
|
||||
def selectq_256 : X86Builtin<"_Vector<4, long long int>(unsigned char, _Vector<4, long long int>, _Vector<4, long long int>)">;
|
||||
}
|
||||
|
||||
let Features = "avx512f,evex512", Attributes = [NoThrow, Const, RequiredVectorWidth<512>] in {
|
||||
let Features = "avx512f,evex512", Attributes = [NoThrow, Const, Constexpr, RequiredVectorWidth<512>] in {
|
||||
def selectq_512 : X86Builtin<"_Vector<8, long long int>(unsigned char, _Vector<8, long long int>, _Vector<8, long long int>)">;
|
||||
}
|
||||
|
||||
let Features = "avx512vl", Attributes = [NoThrow, Const, RequiredVectorWidth<128>] in {
|
||||
let Features = "avx512vl", Attributes = [NoThrow, Const, Constexpr, RequiredVectorWidth<128>] in {
|
||||
def selectps_128 : X86Builtin<"_Vector<4, float>(unsigned char, _Vector<4, float>, _Vector<4, float>)">;
|
||||
}
|
||||
|
||||
let Features = "avx512vl", Attributes = [NoThrow, Const, RequiredVectorWidth<256>] in {
|
||||
let Features = "avx512vl", Attributes = [NoThrow, Const, Constexpr, RequiredVectorWidth<256>] in {
|
||||
def selectps_256 : X86Builtin<"_Vector<8, float>(unsigned char, _Vector<8, float>, _Vector<8, float>)">;
|
||||
}
|
||||
|
||||
let Features = "avx512f,evex512", Attributes = [NoThrow, Const, RequiredVectorWidth<512>] in {
|
||||
let Features = "avx512f,evex512", Attributes = [NoThrow, Const, Constexpr, RequiredVectorWidth<512>] in {
|
||||
def selectps_512 : X86Builtin<"_Vector<16, float>(unsigned short, _Vector<16, float>, _Vector<16, float>)">;
|
||||
}
|
||||
|
||||
let Features = "avx512vl", Attributes = [NoThrow, Const, RequiredVectorWidth<128>] in {
|
||||
let Features = "avx512vl", Attributes = [NoThrow, Const, Constexpr, RequiredVectorWidth<128>] in {
|
||||
def selectpd_128 : X86Builtin<"_Vector<2, double>(unsigned char, _Vector<2, double>, _Vector<2, double>)">;
|
||||
}
|
||||
|
||||
let Features = "avx512vl", Attributes = [NoThrow, Const, RequiredVectorWidth<256>] in {
|
||||
let Features = "avx512vl", Attributes = [NoThrow, Const, Constexpr, RequiredVectorWidth<256>] in {
|
||||
def selectpd_256 : X86Builtin<"_Vector<4, double>(unsigned char, _Vector<4, double>, _Vector<4, double>)">;
|
||||
}
|
||||
|
||||
let Features = "avx512f,evex512", Attributes = [NoThrow, Const, RequiredVectorWidth<512>] in {
|
||||
let Features = "avx512f,evex512", Attributes = [NoThrow, Const, Constexpr, RequiredVectorWidth<512>] in {
|
||||
def selectpd_512 : X86Builtin<"_Vector<8, double>(unsigned char, _Vector<8, double>, _Vector<8, double>)">;
|
||||
}
|
||||
|
||||
|
@ -581,6 +581,13 @@ def err_drv_reduced_module_output_overrided : Warning<
|
||||
"please consider use '-fmodule-output=' to specify the output file for reduced BMI explicitly">,
|
||||
InGroup<DiagGroup<"reduced-bmi-output-overrided">>;
|
||||
|
||||
def remark_found_cxx20_module_usage : Remark<
|
||||
"found C++20 module usage in file '%0'">,
|
||||
InGroup<ModulesDriver>;
|
||||
def remark_performing_driver_managed_module_build : Remark<
|
||||
"performing driver managed module build">,
|
||||
InGroup<ModulesDriver>;
|
||||
|
||||
def warn_drv_delayed_template_parsing_after_cxx20 : Warning<
|
||||
"-fdelayed-template-parsing is deprecated after C++20">,
|
||||
InGroup<DiagGroup<"delayed-template-parsing-in-cxx20">>;
|
||||
|
@ -533,7 +533,14 @@ def Dangling : DiagGroup<"dangling", [DanglingAssignment,
|
||||
DanglingGsl,
|
||||
ReturnStackAddress]>;
|
||||
|
||||
def LifetimeSafety : DiagGroup<"experimental-lifetime-safety">;
|
||||
def LifetimeSafetyPermissive : DiagGroup<"experimental-lifetime-safety-permissive">;
|
||||
def LifetimeSafetyStrict : DiagGroup<"experimental-lifetime-safety-strict">;
|
||||
def LifetimeSafety : DiagGroup<"experimental-lifetime-safety",
|
||||
[LifetimeSafetyPermissive, LifetimeSafetyStrict]> {
|
||||
code Documentation = [{
|
||||
Experimental warnings to detect use-after-free and related temporal safety bugs based on lifetime safety analysis.
|
||||
}];
|
||||
}
|
||||
|
||||
def DistributedObjectModifiers : DiagGroup<"distributed-object-modifiers">;
|
||||
def DllexportExplicitInstantiationDecl : DiagGroup<"dllexport-explicit-instantiation-decl">;
|
||||
@ -628,6 +635,7 @@ def ModuleConflict : DiagGroup<"module-conflict">;
|
||||
def ModuleFileExtension : DiagGroup<"module-file-extension">;
|
||||
def ModuleIncludeDirectiveTranslation : DiagGroup<"module-include-translation">;
|
||||
def ModuleMap : DiagGroup<"module-map">;
|
||||
def ModulesDriver : DiagGroup<"modules-driver">;
|
||||
def RoundTripCC1Args : DiagGroup<"round-trip-cc1-args">;
|
||||
def NewlineEOF : DiagGroup<"newline-eof">;
|
||||
def Nullability : DiagGroup<"nullability">;
|
||||
|
@ -10671,9 +10671,15 @@ def warn_dangling_reference_captured_by_unknown : Warning<
|
||||
"object whose reference is captured will be destroyed at the end of "
|
||||
"the full-expression">, InGroup<DanglingCapture>;
|
||||
|
||||
def warn_experimental_lifetime_safety_dummy_warning : Warning<
|
||||
"todo: remove this warning after we have atleast one warning based on the lifetime analysis">,
|
||||
InGroup<LifetimeSafety>, DefaultIgnore;
|
||||
// Diagnostics based on the Lifetime safety analysis.
|
||||
def warn_lifetime_safety_loan_expires_permissive : Warning<
|
||||
"object whose reference is captured does not live long enough">,
|
||||
InGroup<LifetimeSafetyPermissive>, DefaultIgnore;
|
||||
def warn_lifetime_safety_loan_expires_strict : Warning<
|
||||
"object whose reference is captured may not live long enough">,
|
||||
InGroup<LifetimeSafetyStrict>, DefaultIgnore;
|
||||
def note_lifetime_safety_used_here : Note<"later used here">;
|
||||
def note_lifetime_safety_destroyed_here : Note<"destroyed here">;
|
||||
|
||||
// For non-floating point, expressions of the form x == x or x != x
|
||||
// should result in a warning, since these always evaluate to a constant.
|
||||
|
@ -147,8 +147,10 @@ FEATURE(type_sanitizer, LangOpts.Sanitize.has(SanitizerKind::Type))
|
||||
FEATURE(thread_sanitizer, LangOpts.Sanitize.has(SanitizerKind::Thread))
|
||||
FEATURE(dataflow_sanitizer, LangOpts.Sanitize.has(SanitizerKind::DataFlow))
|
||||
FEATURE(scudo, LangOpts.Sanitize.hasOneOf(SanitizerKind::Scudo))
|
||||
FEATURE(ptrauth_intrinsics, LangOpts.PointerAuthIntrinsics)
|
||||
EXTENSION(ptrauth_qualifier, LangOpts.PointerAuthIntrinsics)
|
||||
FEATURE(ptrauth_intrinsics, LangOpts.PointerAuthIntrinsics &&
|
||||
PP.getTargetInfo().getTriple().isOSDarwin())
|
||||
FEATURE(ptrauth_qualifier, LangOpts.PointerAuthIntrinsics &&
|
||||
PP.getTargetInfo().getTriple().isOSDarwin())
|
||||
FEATURE(ptrauth_calls, LangOpts.PointerAuthCalls)
|
||||
FEATURE(ptrauth_returns, LangOpts.PointerAuthReturns)
|
||||
FEATURE(ptrauth_vtable_pointer_address_discrimination, LangOpts.PointerAuthVTPtrAddressDiscrimination)
|
||||
|
@ -233,8 +233,9 @@ protected:
|
||||
bool TLSSupported;
|
||||
bool VLASupported;
|
||||
bool NoAsmVariants; // True if {|} are normal characters.
|
||||
bool HasLegalHalfType; // True if the backend supports operations on the half
|
||||
// LLVM IR type.
|
||||
bool HasFastHalfType; // True if the backend has native half float support,
|
||||
// and performing calculations in float instead does
|
||||
// not have a performance advantage.
|
||||
bool HalfArgsAndReturns; // OpenCL 6.1.1.1, NEON (IEEE 754-2008 half) type.
|
||||
bool HasFloat128;
|
||||
bool HasFloat16;
|
||||
@ -700,8 +701,9 @@ public:
|
||||
return 128;
|
||||
}
|
||||
|
||||
/// Determine whether _Float16 is supported on this target.
|
||||
virtual bool hasLegalHalfType() const { return HasLegalHalfType; }
|
||||
/// Determine whether the target has fast native support for operations
|
||||
/// on half types.
|
||||
virtual bool hasFastHalfType() const { return HasFastHalfType; }
|
||||
|
||||
/// Whether half args and returns are supported.
|
||||
virtual bool allowHalfArgsAndReturns() const { return HalfArgsAndReturns; }
|
||||
|
@ -157,6 +157,20 @@ public:
|
||||
return create<cir::ComplexImagOp>(loc, operandTy.getElementType(), operand);
|
||||
}
|
||||
|
||||
cir::LoadOp createLoad(mlir::Location loc, mlir::Value ptr,
|
||||
uint64_t alignment = 0) {
|
||||
mlir::IntegerAttr alignmentAttr = getAlignmentAttr(alignment);
|
||||
assert(!cir::MissingFeatures::opLoadStoreVolatile());
|
||||
assert(!cir::MissingFeatures::opLoadStoreMemOrder());
|
||||
return cir::LoadOp::create(*this, loc, ptr, /*isDeref=*/false,
|
||||
alignmentAttr);
|
||||
}
|
||||
|
||||
mlir::Value createAlignedLoad(mlir::Location loc, mlir::Value ptr,
|
||||
uint64_t alignment) {
|
||||
return createLoad(loc, ptr, alignment);
|
||||
}
|
||||
|
||||
mlir::Value createNot(mlir::Value value) {
|
||||
return create<cir::UnaryOp>(value.getLoc(), value.getType(),
|
||||
cir::UnaryOpKind::Not, value);
|
||||
@ -504,8 +518,7 @@ public:
|
||||
static OpBuilder::InsertPoint getBestAllocaInsertPoint(mlir::Block *block) {
|
||||
auto last =
|
||||
std::find_if(block->rbegin(), block->rend(), [](mlir::Operation &op) {
|
||||
// TODO: Add LabelOp missing feature here
|
||||
return mlir::isa<cir::AllocaOp>(&op);
|
||||
return mlir::isa<cir::AllocaOp, cir::LabelOp>(&op);
|
||||
});
|
||||
|
||||
if (last != block->rend())
|
||||
|
@ -341,6 +341,44 @@ def CIR_ConstVectorAttr : CIR_Attr<"ConstVector", "const_vector", [
|
||||
let genVerifyDecl = 1;
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// ConstRecordAttr
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
def CIR_ConstRecordAttr : CIR_Attr<"ConstRecord", "const_record", [
|
||||
TypedAttrInterface
|
||||
]> {
|
||||
let summary = "Represents a constant record";
|
||||
let description = [{
|
||||
Effectively supports "struct-like" constants. It's must be built from
|
||||
an `mlir::ArrayAttr` instance where each element is a typed attribute
|
||||
(`mlir::TypedAttribute`).
|
||||
|
||||
Example:
|
||||
```
|
||||
cir.global external @rgb2 = #cir.const_record<{0 : i8,
|
||||
5 : i64, #cir.null : !cir.ptr<i8>
|
||||
}> : !cir.record<"", i8, i64, !cir.ptr<i8>>
|
||||
```
|
||||
}];
|
||||
|
||||
let parameters = (ins AttributeSelfTypeParameter<"">:$type,
|
||||
"mlir::ArrayAttr":$members);
|
||||
|
||||
let builders = [
|
||||
AttrBuilderWithInferredContext<(ins "cir::RecordType":$type,
|
||||
"mlir::ArrayAttr":$members), [{
|
||||
return $_get(type.getContext(), type, members);
|
||||
}]>
|
||||
];
|
||||
|
||||
let assemblyFormat = [{
|
||||
`<` custom<RecordMembers>($members) `>`
|
||||
}];
|
||||
|
||||
let genVerifyDecl = 1;
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// ConstPtrAttr
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
@ -1060,6 +1060,62 @@ def CIR_BrOp : CIR_Op<"br",[
|
||||
}];
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// GotoOp
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
def CIR_GotoOp : CIR_Op<"goto", [Terminator]> {
|
||||
let description = [{
|
||||
|
||||
Transfers control to the specified `label`. This requires a corresponding
|
||||
`cir.label` to exist and is used by to represent source level `goto`s
|
||||
that jump across region boundaries. Alternatively, `cir.br` is used to
|
||||
construct goto's that don't violate such boundaries.
|
||||
|
||||
`cir.goto` is completely symbolic (i.e. it "jumps" on a label that isn't
|
||||
yet materialized) and should be taken into account by passes and analysis
|
||||
when deciding if it's safe to make some assumptions about a given region
|
||||
or basic block.
|
||||
|
||||
Example:
|
||||
```C++
|
||||
int test(int x) {
|
||||
if (x)
|
||||
goto label;
|
||||
{
|
||||
x = 10;
|
||||
label:
|
||||
return x;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```mlir
|
||||
cir.scope { // REGION #1
|
||||
%2 = cir.load %0 : !cir.ptr<!s32i>, !s32i
|
||||
%3 = cir.cast(int_to_bool, %2 : !s32i), !cir.bool
|
||||
cir.if %3 {
|
||||
cir.goto "label"
|
||||
}
|
||||
}
|
||||
cir.scope { // REGION #2
|
||||
%2 = cir.const #cir.int<10> : !s32i
|
||||
cir.store %2, %0 : !s32i, !cir.ptr<!s32i>
|
||||
cir.br ^bb1
|
||||
^bb1: // pred: ^bb0
|
||||
cir.label "label"
|
||||
%3 = cir.load %0 : !cir.ptr<!s32i>, !s32i
|
||||
cir.store %3, %1 : !s32i, !cir.ptr<!s32i>
|
||||
%4 = cir.load %1 : !cir.ptr<!s32i>, !s32i
|
||||
cir.return %4 : !s32i
|
||||
}
|
||||
cir.unreachable
|
||||
```
|
||||
}];
|
||||
let arguments = (ins StrAttr:$label);
|
||||
let assemblyFormat = [{ $label attr-dict }];
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// LabelOp
|
||||
//===----------------------------------------------------------------------===//
|
||||
@ -1782,6 +1838,54 @@ def CIR_VTableGetVPtrOp : CIR_Op<"vtable.get_vptr", [Pure]> {
|
||||
}];
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// VTableGetVirtualFnAddrOp
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
def CIR_VTableGetVirtualFnAddrOp : CIR_Op<"vtable.get_virtual_fn_addr", [
|
||||
Pure
|
||||
]> {
|
||||
let summary = "Get a the address of a virtual function pointer";
|
||||
let description = [{
|
||||
The `vtable.get_virtual_fn_addr` operation retrieves the address of a
|
||||
virtual function pointer from an object's vtable (__vptr).
|
||||
This is an abstraction to perform the basic pointer arithmetic to get
|
||||
the address of the virtual function pointer, which can then be loaded and
|
||||
called.
|
||||
|
||||
The `vptr` operand must be a `!cir.ptr<!cir.vptr>` value, which would
|
||||
have been returned by a previous call to `cir.vatble.get_vptr`. The
|
||||
`index` operand is an index of the virtual function in the vtable.
|
||||
|
||||
The return type is a pointer-to-pointer to the function type.
|
||||
|
||||
Example:
|
||||
```mlir
|
||||
%2 = cir.load %0 : !cir.ptr<!cir.ptr<!rec_C>>, !cir.ptr<!rec_C>
|
||||
%3 = cir.vtable.get_vptr %2 : !cir.ptr<!rec_C> -> !cir.ptr<!cir.vptr>
|
||||
%4 = cir.load %3 : !cir.ptr<!cir.vptr>, !cir.vptr
|
||||
%5 = cir.vtable.get_virtual_fn_addr %4[2] : !cir.vptr
|
||||
-> !cir.ptr<!cir.ptr<!cir.func<(!cir.ptr<!rec_C>) -> !s32i>>>
|
||||
%6 = cir.load align(8) %5 : !cir.ptr<!cir.ptr<!cir.func<(!cir.ptr<!rec_C>)
|
||||
-> !s32i>>>,
|
||||
!cir.ptr<!cir.func<(!cir.ptr<!rec_C>) -> !s32i>>
|
||||
%7 = cir.call %6(%2) : (!cir.ptr<!cir.func<(!cir.ptr<!rec_C>) -> !s32i>>,
|
||||
!cir.ptr<!rec_C>) -> !s32i
|
||||
```
|
||||
}];
|
||||
|
||||
let arguments = (ins
|
||||
Arg<CIR_VPtrType, "vptr", [MemRead]>:$vptr,
|
||||
I64Attr:$index);
|
||||
|
||||
let results = (outs CIR_PointerType:$result);
|
||||
|
||||
let assemblyFormat = [{
|
||||
$vptr `[` $index `]` attr-dict
|
||||
`:` qualified(type($vptr)) `->` qualified(type($result))
|
||||
}];
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// SetBitfieldOp
|
||||
//===----------------------------------------------------------------------===//
|
||||
@ -3510,4 +3614,86 @@ def CIR_FAbsOp : CIR_UnaryFPToFPBuiltinOp<"fabs", "FAbsOp"> {
|
||||
}];
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Variadic Operations
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
def CIR_VAStartOp : CIR_Op<"va_start"> {
|
||||
let summary = "Starts a variable argument list";
|
||||
let description = [{
|
||||
The cir.va_start operation models the C/C++ va_start macro by
|
||||
initializing a variable argument list at the given va_list storage
|
||||
location.
|
||||
|
||||
The first operand must be a pointer to the target's `va_list`
|
||||
representation. This operation has no results and produces its effect by
|
||||
mutating the storage referenced by the pointer operand. The second operand
|
||||
must be an integer value that contains the expected number of arguments in
|
||||
that list.
|
||||
|
||||
Each `cir.va_start` must be paired with a corresponding `cir.va_end`
|
||||
on the same logical `va_list` object along all control-flow paths. After
|
||||
`cir.va_end`, the `va_list` must not be accessed unless reinitialized
|
||||
with another `cir.va_start`.
|
||||
|
||||
Lowering maps this to the LLVM intrinsic `llvm.va_start`, passing the
|
||||
appropriately decayed pointer to the underlying `va_list` storage.
|
||||
|
||||
Example:
|
||||
|
||||
```mlir
|
||||
// %args : !cir.ptr<!cir.array<!rec___va_list_tag x 1>>
|
||||
%p = cir.cast(array_to_ptrdecay, %args
|
||||
: !cir.ptr<!cir.array<!rec___va_list_tag x 1>>),
|
||||
!cir.ptr<!rec___va_list_tag>
|
||||
%count = cir.load %0 : !cir.ptr<!s32i>, !s32i
|
||||
cir.va_start %p %count : !cir.ptr<!rec___va_list_tag>, !s32i
|
||||
```
|
||||
}];
|
||||
let arguments = (ins
|
||||
CIR_PointerType:$arg_list,
|
||||
CIR_AnyFundamentalIntType:$count
|
||||
);
|
||||
|
||||
let assemblyFormat = [{
|
||||
$arg_list $count attr-dict `:` type(operands)
|
||||
}];
|
||||
}
|
||||
|
||||
def CIR_VAEndOp : CIR_Op<"va_end"> {
|
||||
let summary = "Ends a variable argument list";
|
||||
let description = [{
|
||||
The `cir.va_end` operation models the C/C++ va_end macro by finalizing
|
||||
and cleaning up a variable argument list previously initialized with
|
||||
`cir.va_start`.
|
||||
|
||||
The operand must be a pointer to the target's `va_list` representation.
|
||||
This operation has no results and produces its effect by mutating the
|
||||
storage referenced by the pointer operand.
|
||||
|
||||
`cir.va_end` must only be called after a matching `cir.va_start` on the
|
||||
same `va_list` along all control-flow paths. After `cir.va_end`, the
|
||||
`va_list` is invalid and must not be accessed unless reinitialized.
|
||||
|
||||
Lowering typically maps this to the LLVM intrinsic `llvm.va_end`,
|
||||
passing the appropriately decayed pointer to the underlying `va_list`
|
||||
storage.
|
||||
|
||||
Example:
|
||||
```mlir
|
||||
// %args : !cir.ptr<!cir.array<!rec___va_list_tag x 1>>
|
||||
%p = cir.cast(array_to_ptrdecay, %args
|
||||
: !cir.ptr<!cir.array<!rec___va_list_tag x 1>>),
|
||||
!cir.ptr<!rec___va_list_tag>
|
||||
cir.va_end %p : !cir.ptr<!rec___va_list_tag>
|
||||
```
|
||||
}];
|
||||
|
||||
let arguments = (ins CIR_PointerType:$arg_list);
|
||||
|
||||
let assemblyFormat = [{
|
||||
$arg_list attr-dict `:` type(operands)
|
||||
}];
|
||||
}
|
||||
|
||||
#endif // CLANG_CIR_DIALECT_IR_CIROPS_TD
|
||||
|
@ -95,7 +95,6 @@ struct MissingFeatures {
|
||||
static bool opCallArgEvaluationOrder() { return false; }
|
||||
static bool opCallCallConv() { return false; }
|
||||
static bool opCallMustTail() { return false; }
|
||||
static bool opCallVirtual() { return false; }
|
||||
static bool opCallInAlloca() { return false; }
|
||||
static bool opCallAttrs() { return false; }
|
||||
static bool opCallSurroundingTry() { return false; }
|
||||
@ -176,7 +175,12 @@ struct MissingFeatures {
|
||||
static bool aggValueSlotVolatile() { return false; }
|
||||
static bool alignCXXRecordDecl() { return false; }
|
||||
static bool armComputeVolatileBitfields() { return false; }
|
||||
static bool asmGoto() { return false; }
|
||||
static bool asmInputOperands() { return false; }
|
||||
static bool asmLabelAttr() { return false; }
|
||||
static bool asmMemoryEffects() { return false; }
|
||||
static bool asmOutputOperands() { return false; }
|
||||
static bool asmUnwindClobber() { return false; }
|
||||
static bool assignMemcpyizer() { return false; }
|
||||
static bool astVarDeclInterface() { return false; }
|
||||
static bool attributeBuiltin() { return false; }
|
||||
@ -204,6 +208,7 @@ struct MissingFeatures {
|
||||
static bool dataLayoutTypeAllocSize() { return false; }
|
||||
static bool dataLayoutTypeStoreSize() { return false; }
|
||||
static bool deferredCXXGlobalInit() { return false; }
|
||||
static bool devirtualizeMemberFunction() { return false; }
|
||||
static bool ehCleanupFlags() { return false; }
|
||||
static bool ehCleanupScope() { return false; }
|
||||
static bool ehCleanupScopeRequiresEHCleanup() { return false; }
|
||||
@ -215,6 +220,7 @@ struct MissingFeatures {
|
||||
static bool emitLValueAlignmentAssumption() { return false; }
|
||||
static bool emitNullabilityCheck() { return false; }
|
||||
static bool emitTypeCheck() { return false; }
|
||||
static bool emitTypeMetadataCodeForVCall() { return false; }
|
||||
static bool fastMathFlags() { return false; }
|
||||
static bool fpConstraints() { return false; }
|
||||
static bool generateDebugInfo() { return false; }
|
||||
|
@ -512,6 +512,9 @@ public:
|
||||
|
||||
/// BuildActions - Construct the list of actions to perform for the
|
||||
/// given arguments, which are only done for a single architecture.
|
||||
/// If the compilation is an explicit module build, delegates to
|
||||
/// BuildDriverManagedModuleBuildActions. Otherwise, BuildDefaultActions is
|
||||
/// used.
|
||||
///
|
||||
/// \param C - The compilation that is being built.
|
||||
/// \param Args - The input arguments.
|
||||
@ -796,6 +799,35 @@ private:
|
||||
/// compilation based on which -f(no-)?lto(=.*)? option occurs last.
|
||||
void setLTOMode(const llvm::opt::ArgList &Args);
|
||||
|
||||
/// BuildDefaultActions - Constructs the list of actions to perform
|
||||
/// for the provided arguments, which are only done for a single architecture.
|
||||
///
|
||||
/// \param C - The compilation that is being built.
|
||||
/// \param Args - The input arguments.
|
||||
/// \param Actions - The list to store the resulting actions onto.
|
||||
void BuildDefaultActions(Compilation &C, llvm::opt::DerivedArgList &Args,
|
||||
const InputList &Inputs, ActionList &Actions) const;
|
||||
|
||||
/// BuildDriverManagedModuleBuildActions - Performs a dependency
|
||||
/// scan and constructs the list of actions to perform for dependency order
|
||||
/// and the provided arguments. This is only done for a single a architecture.
|
||||
///
|
||||
/// \param C - The compilation that is being built.
|
||||
/// \param Args - The input arguments.
|
||||
/// \param Actions - The list to store the resulting actions onto.
|
||||
void BuildDriverManagedModuleBuildActions(Compilation &C,
|
||||
llvm::opt::DerivedArgList &Args,
|
||||
const InputList &Inputs,
|
||||
ActionList &Actions) const;
|
||||
|
||||
/// Scans the leading lines of the C++ source inputs to detect C++20 module
|
||||
/// usage.
|
||||
///
|
||||
/// \returns True if module usage is detected, false otherwise, or an error on
|
||||
/// read failure.
|
||||
llvm::ErrorOr<bool>
|
||||
ScanInputsForCXX20ModulesUsage(const InputList &Inputs) const;
|
||||
|
||||
/// Retrieves a ToolChain for a particular \p Target triple.
|
||||
///
|
||||
/// Will cache ToolChains for the life of the driver object, and create them
|
||||
|
@ -3296,6 +3296,13 @@ defm modules_reduced_bmi : BoolOption<"f", "modules-reduced-bmi",
|
||||
PosFlag<SetTrue, [], [ClangOption, CC1Option],
|
||||
"Generate the reduced BMI">>;
|
||||
|
||||
def fmodules_driver : Flag<["-"], "fmodules-driver">,
|
||||
Group<f_Group>, Visibility<[ClangOption]>,
|
||||
HelpText<"Enable support for driver managed module builds (experimental)">;
|
||||
def fno_modules_driver : Flag<["-"], "fno-modules-driver">,
|
||||
Group<f_Group>, Visibility<[ClangOption]>,
|
||||
HelpText<"Disable support for driver managed module builds (experimental)">;
|
||||
|
||||
def experimental_modules_reduced_bmi : Flag<["-"], "fexperimental-modules-reduced-bmi">,
|
||||
Group<f_Group>, Visibility<[ClangOption, CC1Option]>, Alias<fmodules_reduced_bmi>;
|
||||
|
||||
|
@ -4813,14 +4813,45 @@ struct FormatStyle {
|
||||
/// \version 7
|
||||
bool SpaceBeforeRangeBasedForLoopColon;
|
||||
|
||||
/// If ``true``, spaces will be inserted into ``{}``.
|
||||
/// \code
|
||||
/// true: false:
|
||||
/// void f() { } vs. void f() {}
|
||||
/// while (true) { } while (true) {}
|
||||
/// \endcode
|
||||
/// This option is **deprecated**. See ``Block`` of ``SpaceInEmptyBraces``.
|
||||
/// \version 10
|
||||
bool SpaceInEmptyBlock;
|
||||
// bool SpaceInEmptyBlock;
|
||||
|
||||
/// Style of when to insert a space in empty braces.
|
||||
enum SpaceInEmptyBracesStyle : int8_t {
|
||||
/// Always insert a space in empty braces.
|
||||
/// \code
|
||||
/// void f() { }
|
||||
/// class Unit { };
|
||||
/// auto a = [] { };
|
||||
/// int x{ };
|
||||
/// \endcode
|
||||
SIEB_Always,
|
||||
/// Only insert a space in empty blocks.
|
||||
/// \code
|
||||
/// void f() { }
|
||||
/// class Unit { };
|
||||
/// auto a = [] { };
|
||||
/// int x{};
|
||||
/// \endcode
|
||||
SIEB_Block,
|
||||
/// Never insert a space in empty braces.
|
||||
/// \code
|
||||
/// void f() {}
|
||||
/// class Unit {};
|
||||
/// auto a = [] {};
|
||||
/// int x{};
|
||||
/// \endcode
|
||||
SIEB_Never
|
||||
};
|
||||
|
||||
/// Specifies when to insert a space in empty braces.
|
||||
/// \note
|
||||
/// This option doesn't apply to initializer braces if
|
||||
/// ``Cpp11BracedListStyle`` is set to ``true``.
|
||||
/// \endnote
|
||||
/// \version 22
|
||||
SpaceInEmptyBracesStyle SpaceInEmptyBraces;
|
||||
|
||||
/// If ``true``, spaces may be inserted into ``()``.
|
||||
/// This option is **deprecated**. See ``InEmptyParentheses`` of
|
||||
@ -5494,7 +5525,7 @@ struct FormatStyle {
|
||||
SpaceBeforeRangeBasedForLoopColon ==
|
||||
R.SpaceBeforeRangeBasedForLoopColon &&
|
||||
SpaceBeforeSquareBrackets == R.SpaceBeforeSquareBrackets &&
|
||||
SpaceInEmptyBlock == R.SpaceInEmptyBlock &&
|
||||
SpaceInEmptyBraces == R.SpaceInEmptyBraces &&
|
||||
SpacesBeforeTrailingComments == R.SpacesBeforeTrailingComments &&
|
||||
SpacesInAngles == R.SpacesInAngles &&
|
||||
SpacesInContainerLiterals == R.SpacesInContainerLiterals &&
|
||||
|
@ -135,6 +135,13 @@ void printDependencyDirectivesAsSource(
|
||||
ArrayRef<dependency_directives_scan::Directive> Directives,
|
||||
llvm::raw_ostream &OS);
|
||||
|
||||
/// Scan an input source buffer for C++20 named module usage.
|
||||
///
|
||||
/// \param Source The input source buffer.
|
||||
///
|
||||
/// \returns true if any C++20 named modules related directive was found.
|
||||
bool scanInputForCXX20ModulesUsage(StringRef Source);
|
||||
|
||||
/// Functor that returns the dependency directives for a given file.
|
||||
class DependencyDirectivesGetter {
|
||||
public:
|
||||
|
@ -143,9 +143,6 @@ class Lexer : public PreprocessorLexer {
|
||||
/// True if this is the first time we're lexing the input file.
|
||||
bool IsFirstTimeLexingFile;
|
||||
|
||||
/// True if current lexing token is the first pp-token.
|
||||
bool IsFirstPPToken;
|
||||
|
||||
// NewLinePtr - A pointer to new line character '\n' being lexed. For '\r\n',
|
||||
// it also points to '\n.'
|
||||
const char *NewLinePtr;
|
||||
|
310
clang/include/clang/Lex/NoTrivialPPDirectiveTracer.h
Normal file
310
clang/include/clang/Lex/NoTrivialPPDirectiveTracer.h
Normal file
@ -0,0 +1,310 @@
|
||||
//===--- NoTrivialPPDirectiveTracer.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 defines the NoTrivialPPDirectiveTracer interface.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_CLANG_LEX_NO_TRIVIAL_PPDIRECTIVE_TRACER_H
|
||||
#define LLVM_CLANG_LEX_NO_TRIVIAL_PPDIRECTIVE_TRACER_H
|
||||
|
||||
#include "clang/Lex/PPCallbacks.h"
|
||||
|
||||
namespace clang {
|
||||
class Preprocessor;
|
||||
|
||||
/// Consider the following code:
|
||||
///
|
||||
/// # 1 __FILE__ 1 3
|
||||
/// export module a;
|
||||
///
|
||||
/// According to the wording in
|
||||
/// [P1857R3](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p1857r3.html):
|
||||
///
|
||||
/// A module directive may only appear as the first preprocessing tokens in a
|
||||
/// file (excluding the global module fragment.)
|
||||
///
|
||||
/// and the wording in
|
||||
/// [[cpp.pre]](https://eel.is/c++draft/cpp.pre#nt:module-file):
|
||||
/// module-file:
|
||||
/// pp-global-module-fragment[opt] pp-module group[opt]
|
||||
/// pp-private-module-fragment[opt]
|
||||
///
|
||||
/// `#` is the first pp-token in the translation unit, and it was rejected by
|
||||
/// clang, but they really should be exempted from this rule. The goal is to not
|
||||
/// allow any preprocessor conditionals or most state changes, but these don't
|
||||
/// fit that.
|
||||
///
|
||||
/// State change would mean most semantically observable preprocessor state,
|
||||
/// particularly anything that is order dependent. Global flags like being a
|
||||
/// system header/module shouldn't matter.
|
||||
///
|
||||
/// We should exempt a brunch of directives, even though it violates the current
|
||||
/// standard wording.
|
||||
///
|
||||
/// This class used to trace 'no-trivial' pp-directives in main file, which may
|
||||
/// change the preprocessing state.
|
||||
///
|
||||
/// FIXME: Once the wording of the standard is revised, we need to follow the
|
||||
/// wording of the standard. Currently this is just a workaround
|
||||
class NoTrivialPPDirectiveTracer : public PPCallbacks {
|
||||
Preprocessor &PP;
|
||||
|
||||
/// Whether preprocessing main file. We only focus on the main file.
|
||||
bool InMainFile = true;
|
||||
|
||||
/// Whether one or more conditional, include or other 'no-trivial'
|
||||
/// pp-directives has seen before.
|
||||
bool SeenNoTrivialPPDirective = false;
|
||||
|
||||
void setSeenNoTrivialPPDirective();
|
||||
|
||||
public:
|
||||
NoTrivialPPDirectiveTracer(Preprocessor &P) : PP(P) {}
|
||||
|
||||
bool hasSeenNoTrivialPPDirective() const;
|
||||
|
||||
/// Callback invoked whenever the \p Lexer moves to a different file for
|
||||
/// lexing. Unlike \p FileChanged line number directives and other related
|
||||
/// pragmas do not trigger callbacks to \p LexedFileChanged.
|
||||
///
|
||||
/// \param FID The \p FileID that the \p Lexer moved to.
|
||||
///
|
||||
/// \param Reason Whether the \p Lexer entered a new file or exited one.
|
||||
///
|
||||
/// \param FileType The \p CharacteristicKind of the file the \p Lexer moved
|
||||
/// to.
|
||||
///
|
||||
/// \param PrevFID The \p FileID the \p Lexer was using before the change.
|
||||
///
|
||||
/// \param Loc The location where the \p Lexer entered a new file from or the
|
||||
/// location that the \p Lexer moved into after exiting a file.
|
||||
void LexedFileChanged(FileID FID, LexedFileChangeReason Reason,
|
||||
SrcMgr::CharacteristicKind FileType, FileID PrevFID,
|
||||
SourceLocation Loc) override;
|
||||
|
||||
/// Callback invoked whenever an embed directive has been processed,
|
||||
/// regardless of whether the embed will actually find a file.
|
||||
///
|
||||
/// \param HashLoc The location of the '#' that starts the embed directive.
|
||||
///
|
||||
/// \param FileName The name of the file being included, as written in the
|
||||
/// source code.
|
||||
///
|
||||
/// \param IsAngled Whether the file name was enclosed in angle brackets;
|
||||
/// otherwise, it was enclosed in quotes.
|
||||
///
|
||||
/// \param File The actual file that may be included by this embed directive.
|
||||
///
|
||||
/// \param Params The parameters used by the directive.
|
||||
void EmbedDirective(SourceLocation HashLoc, StringRef FileName, bool IsAngled,
|
||||
OptionalFileEntryRef File,
|
||||
const LexEmbedParametersResult &Params) override {
|
||||
setSeenNoTrivialPPDirective();
|
||||
}
|
||||
|
||||
/// Callback invoked whenever an inclusion directive of
|
||||
/// any kind (\c \#include, \c \#import, etc.) has been processed, regardless
|
||||
/// of whether the inclusion will actually result in an inclusion.
|
||||
///
|
||||
/// \param HashLoc The location of the '#' that starts the inclusion
|
||||
/// directive.
|
||||
///
|
||||
/// \param IncludeTok The token that indicates the kind of inclusion
|
||||
/// directive, e.g., 'include' or 'import'.
|
||||
///
|
||||
/// \param FileName The name of the file being included, as written in the
|
||||
/// source code.
|
||||
///
|
||||
/// \param IsAngled Whether the file name was enclosed in angle brackets;
|
||||
/// otherwise, it was enclosed in quotes.
|
||||
///
|
||||
/// \param FilenameRange The character range of the quotes or angle brackets
|
||||
/// for the written file name.
|
||||
///
|
||||
/// \param File The actual file that may be included by this inclusion
|
||||
/// directive.
|
||||
///
|
||||
/// \param SearchPath Contains the search path which was used to find the file
|
||||
/// in the file system. If the file was found via an absolute include path,
|
||||
/// SearchPath will be empty. For framework includes, the SearchPath and
|
||||
/// RelativePath will be split up. For example, if an include of "Some/Some.h"
|
||||
/// is found via the framework path
|
||||
/// "path/to/Frameworks/Some.framework/Headers/Some.h", SearchPath will be
|
||||
/// "path/to/Frameworks/Some.framework/Headers" and RelativePath will be
|
||||
/// "Some.h".
|
||||
///
|
||||
/// \param RelativePath The path relative to SearchPath, at which the include
|
||||
/// file was found. This is equal to FileName except for framework includes.
|
||||
///
|
||||
/// \param SuggestedModule The module suggested for this header, if any.
|
||||
///
|
||||
/// \param ModuleImported Whether this include was translated into import of
|
||||
/// \p SuggestedModule.
|
||||
///
|
||||
/// \param FileType The characteristic kind, indicates whether a file or
|
||||
/// directory holds normal user code, system code, or system code which is
|
||||
/// implicitly 'extern "C"' in C++ mode.
|
||||
///
|
||||
void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok,
|
||||
StringRef FileName, bool IsAngled,
|
||||
CharSourceRange FilenameRange,
|
||||
OptionalFileEntryRef File, StringRef SearchPath,
|
||||
StringRef RelativePath, const Module *SuggestedModule,
|
||||
bool ModuleImported,
|
||||
SrcMgr::CharacteristicKind FileType) override {
|
||||
setSeenNoTrivialPPDirective();
|
||||
}
|
||||
|
||||
/// Callback invoked whenever there was an explicit module-import
|
||||
/// syntax.
|
||||
///
|
||||
/// \param ImportLoc The location of import directive token.
|
||||
///
|
||||
/// \param Path The identifiers (and their locations) of the module
|
||||
/// "path", e.g., "std.vector" would be split into "std" and "vector".
|
||||
///
|
||||
/// \param Imported The imported module; can be null if importing failed.
|
||||
///
|
||||
void moduleImport(SourceLocation ImportLoc, ModuleIdPath Path,
|
||||
const Module *Imported) override {
|
||||
setSeenNoTrivialPPDirective();
|
||||
}
|
||||
|
||||
/// Callback invoked when the end of the main file is reached.
|
||||
///
|
||||
/// No subsequent callbacks will be made.
|
||||
void EndOfMainFile() override { setSeenNoTrivialPPDirective(); }
|
||||
|
||||
/// Callback invoked when start reading any pragma directive.
|
||||
void PragmaDirective(SourceLocation Loc,
|
||||
PragmaIntroducerKind Introducer) override {}
|
||||
|
||||
/// Called by Preprocessor::HandleMacroExpandedIdentifier when a
|
||||
/// macro invocation is found.
|
||||
void MacroExpands(const Token &MacroNameTok, const MacroDefinition &MD,
|
||||
SourceRange Range, const MacroArgs *Args) override;
|
||||
|
||||
/// Hook called whenever a macro definition is seen.
|
||||
void MacroDefined(const Token &MacroNameTok,
|
||||
const MacroDirective *MD) override {
|
||||
setSeenNoTrivialPPDirective();
|
||||
}
|
||||
|
||||
/// Hook called whenever a macro \#undef is seen.
|
||||
/// \param MacroNameTok The active Token
|
||||
/// \param MD A MacroDefinition for the named macro.
|
||||
/// \param Undef New MacroDirective if the macro was defined, null otherwise.
|
||||
///
|
||||
/// MD is released immediately following this callback.
|
||||
void MacroUndefined(const Token &MacroNameTok, const MacroDefinition &MD,
|
||||
const MacroDirective *Undef) override {
|
||||
setSeenNoTrivialPPDirective();
|
||||
}
|
||||
|
||||
/// Hook called whenever the 'defined' operator is seen.
|
||||
/// \param MD The MacroDirective if the name was a macro, null otherwise.
|
||||
void Defined(const Token &MacroNameTok, const MacroDefinition &MD,
|
||||
SourceRange Range) override {
|
||||
setSeenNoTrivialPPDirective();
|
||||
}
|
||||
|
||||
/// Hook called whenever an \#if is seen.
|
||||
/// \param Loc the source location of the directive.
|
||||
/// \param ConditionRange The SourceRange of the expression being tested.
|
||||
/// \param ConditionValue The evaluated value of the condition.
|
||||
///
|
||||
// FIXME: better to pass in a list (or tree!) of Tokens.
|
||||
void If(SourceLocation Loc, SourceRange ConditionRange,
|
||||
ConditionValueKind ConditionValue) override {
|
||||
setSeenNoTrivialPPDirective();
|
||||
}
|
||||
|
||||
/// Hook called whenever an \#elif is seen.
|
||||
/// \param Loc the source location of the directive.
|
||||
/// \param ConditionRange The SourceRange of the expression being tested.
|
||||
/// \param ConditionValue The evaluated value of the condition.
|
||||
/// \param IfLoc the source location of the \#if/\#ifdef/\#ifndef directive.
|
||||
// FIXME: better to pass in a list (or tree!) of Tokens.
|
||||
void Elif(SourceLocation Loc, SourceRange ConditionRange,
|
||||
ConditionValueKind ConditionValue, SourceLocation IfLoc) override {
|
||||
setSeenNoTrivialPPDirective();
|
||||
}
|
||||
|
||||
/// Hook called whenever an \#ifdef is seen.
|
||||
/// \param Loc the source location of the directive.
|
||||
/// \param MacroNameTok Information on the token being tested.
|
||||
/// \param MD The MacroDefinition if the name was a macro, null otherwise.
|
||||
void Ifdef(SourceLocation Loc, const Token &MacroNameTok,
|
||||
const MacroDefinition &MD) override {
|
||||
setSeenNoTrivialPPDirective();
|
||||
}
|
||||
|
||||
/// Hook called whenever an \#elifdef branch is taken.
|
||||
/// \param Loc the source location of the directive.
|
||||
/// \param MacroNameTok Information on the token being tested.
|
||||
/// \param MD The MacroDefinition if the name was a macro, null otherwise.
|
||||
void Elifdef(SourceLocation Loc, const Token &MacroNameTok,
|
||||
const MacroDefinition &MD) override {
|
||||
setSeenNoTrivialPPDirective();
|
||||
}
|
||||
/// Hook called whenever an \#elifdef is skipped.
|
||||
/// \param Loc the source location of the directive.
|
||||
/// \param ConditionRange The SourceRange of the expression being tested.
|
||||
/// \param IfLoc the source location of the \#if/\#ifdef/\#ifndef directive.
|
||||
// FIXME: better to pass in a list (or tree!) of Tokens.
|
||||
void Elifdef(SourceLocation Loc, SourceRange ConditionRange,
|
||||
SourceLocation IfLoc) override {
|
||||
setSeenNoTrivialPPDirective();
|
||||
}
|
||||
|
||||
/// Hook called whenever an \#ifndef is seen.
|
||||
/// \param Loc the source location of the directive.
|
||||
/// \param MacroNameTok Information on the token being tested.
|
||||
/// \param MD The MacroDefiniton if the name was a macro, null otherwise.
|
||||
void Ifndef(SourceLocation Loc, const Token &MacroNameTok,
|
||||
const MacroDefinition &MD) override {
|
||||
setSeenNoTrivialPPDirective();
|
||||
}
|
||||
|
||||
/// Hook called whenever an \#elifndef branch is taken.
|
||||
/// \param Loc the source location of the directive.
|
||||
/// \param MacroNameTok Information on the token being tested.
|
||||
/// \param MD The MacroDefinition if the name was a macro, null otherwise.
|
||||
void Elifndef(SourceLocation Loc, const Token &MacroNameTok,
|
||||
const MacroDefinition &MD) override {
|
||||
setSeenNoTrivialPPDirective();
|
||||
}
|
||||
/// Hook called whenever an \#elifndef is skipped.
|
||||
/// \param Loc the source location of the directive.
|
||||
/// \param ConditionRange The SourceRange of the expression being tested.
|
||||
/// \param IfLoc the source location of the \#if/\#ifdef/\#ifndef directive.
|
||||
// FIXME: better to pass in a list (or tree!) of Tokens.
|
||||
void Elifndef(SourceLocation Loc, SourceRange ConditionRange,
|
||||
SourceLocation IfLoc) override {
|
||||
setSeenNoTrivialPPDirective();
|
||||
}
|
||||
|
||||
/// Hook called whenever an \#else is seen.
|
||||
/// \param Loc the source location of the directive.
|
||||
/// \param IfLoc the source location of the \#if/\#ifdef/\#ifndef directive.
|
||||
void Else(SourceLocation Loc, SourceLocation IfLoc) override {
|
||||
setSeenNoTrivialPPDirective();
|
||||
}
|
||||
|
||||
/// Hook called whenever an \#endif is seen.
|
||||
/// \param Loc the source location of the directive.
|
||||
/// \param IfLoc the source location of the \#if/\#ifdef/\#ifndef directive.
|
||||
void Endif(SourceLocation Loc, SourceLocation IfLoc) override {
|
||||
setSeenNoTrivialPPDirective();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace clang
|
||||
|
||||
#endif // LLVM_CLANG_LEX_NO_TRIVIAL_PPDIRECTIVE_TRACER_H
|
@ -82,6 +82,7 @@ class PreprocessorLexer;
|
||||
class PreprocessorOptions;
|
||||
class ScratchBuffer;
|
||||
class TargetInfo;
|
||||
class NoTrivialPPDirectiveTracer;
|
||||
|
||||
namespace Builtin {
|
||||
class Context;
|
||||
@ -353,6 +354,11 @@ private:
|
||||
/// First pp-token source location in current translation unit.
|
||||
SourceLocation FirstPPTokenLoc;
|
||||
|
||||
/// A preprocessor directive tracer to trace whether the preprocessing
|
||||
/// state changed. These changes would mean most semantically observable
|
||||
/// preprocessor state, particularly anything that is order dependent.
|
||||
NoTrivialPPDirectiveTracer *DirTracer = nullptr;
|
||||
|
||||
/// A position within a C++20 import-seq.
|
||||
class StdCXXImportSeq {
|
||||
public:
|
||||
@ -609,6 +615,8 @@ private:
|
||||
return State == NamedModuleImplementation && !getName().contains(':');
|
||||
}
|
||||
|
||||
bool isNotAModuleDecl() const { return State == NotAModuleDecl; }
|
||||
|
||||
StringRef getName() const {
|
||||
assert(isNamedModule() && "Can't get name from a non named module");
|
||||
return Name;
|
||||
@ -3091,6 +3099,10 @@ public:
|
||||
bool setDeserializedSafeBufferOptOutMap(
|
||||
const SmallVectorImpl<SourceLocation> &SrcLocSeqs);
|
||||
|
||||
/// Whether we've seen pp-directives which may have changed the preprocessing
|
||||
/// state.
|
||||
bool hasSeenNoTrivialPPDirective() const;
|
||||
|
||||
private:
|
||||
/// Helper functions to forward lexing to the actual lexer. They all share the
|
||||
/// same signature.
|
||||
|
@ -86,12 +86,12 @@ public:
|
||||
// macro stringizing or charizing operator.
|
||||
CommaAfterElided = 0x200, // The comma following this token was elided (MS).
|
||||
IsEditorPlaceholder = 0x400, // This identifier is a placeholder.
|
||||
|
||||
IsReinjected = 0x800, // A phase 4 token that was produced before and
|
||||
// re-added, e.g. via EnterTokenStream. Annotation
|
||||
// tokens are *not* reinjected.
|
||||
FirstPPToken = 0x1000, // This token is the first pp token in the
|
||||
// translation unit.
|
||||
IsReinjected = 0x800, // A phase 4 token that was produced before and
|
||||
// re-added, e.g. via EnterTokenStream. Annotation
|
||||
// tokens are *not* reinjected.
|
||||
HasSeenNoTrivialPPDirective =
|
||||
0x1000, // Whether we've seen any 'no-trivial' pp-directives before
|
||||
// current position.
|
||||
};
|
||||
|
||||
tok::TokenKind getKind() const { return Kind; }
|
||||
@ -321,8 +321,9 @@ public:
|
||||
/// lexer uses identifier tokens to represent placeholders.
|
||||
bool isEditorPlaceholder() const { return getFlag(IsEditorPlaceholder); }
|
||||
|
||||
/// Returns true if this token is the first pp-token.
|
||||
bool isFirstPPToken() const { return getFlag(FirstPPToken); }
|
||||
bool hasSeenNoTrivialPPDirective() const {
|
||||
return getFlag(HasSeenNoTrivialPPDirective);
|
||||
}
|
||||
};
|
||||
|
||||
/// Information about the conditional stack (\#if directives)
|
||||
|
@ -933,7 +933,7 @@ public:
|
||||
/// to local variables that are usable as constant expressions and
|
||||
/// do not involve an odr-use (they may still need to be captured
|
||||
/// if the enclosing full-expression is instantiation dependent).
|
||||
llvm::SmallSet<Expr *, 8> NonODRUsedCapturingExprs;
|
||||
llvm::SmallPtrSet<Expr *, 8> NonODRUsedCapturingExprs;
|
||||
|
||||
/// A map of explicit capture indices to their introducer source ranges.
|
||||
llvm::DenseMap<unsigned, SourceRange> ExplicitCaptureRanges;
|
||||
|
@ -9837,7 +9837,7 @@ public:
|
||||
SourceLocation ModuleLoc, ModuleDeclKind MDK,
|
||||
ModuleIdPath Path, ModuleIdPath Partition,
|
||||
ModuleImportState &ImportState,
|
||||
bool IntroducerIsFirstPPToken);
|
||||
bool SeenNoTrivialPPDirective);
|
||||
|
||||
/// The parser has processed a global-module-fragment declaration that begins
|
||||
/// the definition of the global module fragment of the current module unit.
|
||||
|
@ -229,10 +229,17 @@ private:
|
||||
|
||||
void diagnoseAvailabilityViolations(TranslationUnitDecl *TU);
|
||||
|
||||
bool initGlobalResourceDecl(VarDecl *VD);
|
||||
uint32_t getNextImplicitBindingOrderID() {
|
||||
return ImplicitBindingNextOrderID++;
|
||||
}
|
||||
|
||||
bool initGlobalResourceDecl(VarDecl *VD);
|
||||
bool initGlobalResourceArrayDecl(VarDecl *VD);
|
||||
void createResourceRecordCtorArgs(const Type *ResourceTy, StringRef VarName,
|
||||
HLSLResourceBindingAttr *RBA,
|
||||
HLSLVkBindingAttr *VkBinding,
|
||||
uint32_t ArrayIndex,
|
||||
llvm::SmallVectorImpl<Expr *> &Args);
|
||||
};
|
||||
|
||||
} // namespace clang
|
||||
|
@ -947,12 +947,12 @@ public:
|
||||
ArrayRef<Expr *> IntExprs, SourceLocation EndLoc);
|
||||
// Does the checking for a 'reduction ' clause that needs to be done in
|
||||
// dependent and not dependent cases.
|
||||
OpenACCClause *
|
||||
CheckReductionClause(ArrayRef<const OpenACCClause *> ExistingClauses,
|
||||
OpenACCDirectiveKind DirectiveKind,
|
||||
SourceLocation BeginLoc, SourceLocation LParenLoc,
|
||||
OpenACCReductionOperator ReductionOp,
|
||||
ArrayRef<Expr *> Vars, SourceLocation EndLoc);
|
||||
OpenACCClause *CheckReductionClause(
|
||||
ArrayRef<const OpenACCClause *> ExistingClauses,
|
||||
OpenACCDirectiveKind DirectiveKind, SourceLocation BeginLoc,
|
||||
SourceLocation LParenLoc, OpenACCReductionOperator ReductionOp,
|
||||
ArrayRef<Expr *> Vars, ArrayRef<OpenACCReductionRecipe> Recipes,
|
||||
SourceLocation EndLoc);
|
||||
|
||||
ExprResult BuildOpenACCAsteriskSizeExpr(SourceLocation AsteriskLoc);
|
||||
ExprResult ActOnOpenACCAsteriskSizeExpr(SourceLocation AsteriskLoc);
|
||||
|
@ -320,7 +320,7 @@ protected:
|
||||
|
||||
/// A set of location contexts that correspoind to call sites which should be
|
||||
/// considered "interesting".
|
||||
llvm::SmallSet<const LocationContext *, 2> InterestingLocationContexts;
|
||||
llvm::SmallPtrSet<const LocationContext *, 2> InterestingLocationContexts;
|
||||
|
||||
/// A set of custom visitors which generate "event" diagnostics at
|
||||
/// interesting points in the path.
|
||||
@ -348,7 +348,7 @@ protected:
|
||||
llvm::SmallSet<InvalidationRecord, 4> Invalidations;
|
||||
|
||||
/// Conditions we're already tracking.
|
||||
llvm::SmallSet<const ExplodedNode *, 4> TrackedConditions;
|
||||
llvm::SmallPtrSet<const ExplodedNode *, 4> TrackedConditions;
|
||||
|
||||
/// Reports with different uniqueing locations are considered to be different
|
||||
/// for the purposes of deduplication.
|
||||
|
@ -60,16 +60,18 @@ template <class Emitter> class OptionScope final {
|
||||
public:
|
||||
/// Root constructor, compiling or discarding primitives.
|
||||
OptionScope(Compiler<Emitter> *Ctx, bool NewDiscardResult,
|
||||
bool NewInitializing)
|
||||
bool NewInitializing, bool NewToLValue)
|
||||
: Ctx(Ctx), OldDiscardResult(Ctx->DiscardResult),
|
||||
OldInitializing(Ctx->Initializing) {
|
||||
OldInitializing(Ctx->Initializing), OldToLValue(NewToLValue) {
|
||||
Ctx->DiscardResult = NewDiscardResult;
|
||||
Ctx->Initializing = NewInitializing;
|
||||
Ctx->ToLValue = NewToLValue;
|
||||
}
|
||||
|
||||
~OptionScope() {
|
||||
Ctx->DiscardResult = OldDiscardResult;
|
||||
Ctx->Initializing = OldInitializing;
|
||||
Ctx->ToLValue = OldToLValue;
|
||||
}
|
||||
|
||||
private:
|
||||
@ -78,6 +80,7 @@ private:
|
||||
/// Old discard flag to restore.
|
||||
bool OldDiscardResult;
|
||||
bool OldInitializing;
|
||||
bool OldToLValue;
|
||||
};
|
||||
|
||||
template <class Emitter>
|
||||
@ -222,6 +225,9 @@ bool Compiler<Emitter>::VisitCastExpr(const CastExpr *CE) {
|
||||
|
||||
switch (CE->getCastKind()) {
|
||||
case CK_LValueToRValue: {
|
||||
if (ToLValue && CE->getType()->isPointerType())
|
||||
return this->delegate(SubExpr);
|
||||
|
||||
if (SubExpr->getType().isVolatileQualified())
|
||||
return this->emitInvalidCast(CastKind::Volatile, /*Fatal=*/true, CE);
|
||||
|
||||
@ -250,7 +256,7 @@ bool Compiler<Emitter>::VisitCastExpr(const CastExpr *CE) {
|
||||
|
||||
// Prepare storage for the result.
|
||||
if (!Initializing && !SubExprT) {
|
||||
std::optional<unsigned> LocalIndex = allocateLocal(SubExpr);
|
||||
UnsignedOrNone LocalIndex = allocateLocal(SubExpr);
|
||||
if (!LocalIndex)
|
||||
return false;
|
||||
if (!this->emitGetPtrLocal(*LocalIndex, CE))
|
||||
@ -603,7 +609,7 @@ bool Compiler<Emitter>::VisitCastExpr(const CastExpr *CE) {
|
||||
// We're creating a complex value here, so we need to
|
||||
// allocate storage for it.
|
||||
if (!Initializing) {
|
||||
std::optional<unsigned> LocalIndex = allocateTemporary(CE);
|
||||
UnsignedOrNone LocalIndex = allocateTemporary(CE);
|
||||
if (!LocalIndex)
|
||||
return false;
|
||||
if (!this->emitGetPtrLocal(*LocalIndex, CE))
|
||||
@ -627,7 +633,7 @@ bool Compiler<Emitter>::VisitCastExpr(const CastExpr *CE) {
|
||||
assert(CE->getType()->isAnyComplexType());
|
||||
assert(SubExpr->getType()->isAnyComplexType());
|
||||
if (!Initializing) {
|
||||
std::optional<unsigned> LocalIndex = allocateLocal(CE);
|
||||
UnsignedOrNone LocalIndex = allocateLocal(CE);
|
||||
if (!LocalIndex)
|
||||
return false;
|
||||
if (!this->emitGetPtrLocal(*LocalIndex, CE))
|
||||
@ -672,7 +678,7 @@ bool Compiler<Emitter>::VisitCastExpr(const CastExpr *CE) {
|
||||
assert(CE->getType()->isVectorType());
|
||||
|
||||
if (!Initializing) {
|
||||
std::optional<unsigned> LocalIndex = allocateLocal(CE);
|
||||
UnsignedOrNone LocalIndex = allocateLocal(CE);
|
||||
if (!LocalIndex)
|
||||
return false;
|
||||
if (!this->emitGetPtrLocal(*LocalIndex, CE))
|
||||
@ -716,7 +722,7 @@ bool Compiler<Emitter>::VisitCastExpr(const CastExpr *CE) {
|
||||
assert(CE->getType()->isVectorType());
|
||||
|
||||
if (!Initializing) {
|
||||
std::optional<unsigned> LocalIndex = allocateTemporary(CE);
|
||||
UnsignedOrNone LocalIndex = allocateTemporary(CE);
|
||||
if (!LocalIndex)
|
||||
return false;
|
||||
if (!this->emitGetPtrLocal(*LocalIndex, CE))
|
||||
@ -804,7 +810,7 @@ bool Compiler<Emitter>::VisitImaginaryLiteral(const ImaginaryLiteral *E) {
|
||||
return true;
|
||||
|
||||
if (!Initializing) {
|
||||
std::optional<unsigned> LocalIndex = allocateTemporary(E);
|
||||
UnsignedOrNone LocalIndex = allocateTemporary(E);
|
||||
if (!LocalIndex)
|
||||
return false;
|
||||
if (!this->emitGetPtrLocal(*LocalIndex, E))
|
||||
@ -905,7 +911,7 @@ bool Compiler<Emitter>::VisitBinaryOperator(const BinaryOperator *BO) {
|
||||
|
||||
// We need a temporary variable holding our return value.
|
||||
if (!Initializing) {
|
||||
std::optional<unsigned> ResultIndex = this->allocateLocal(BO);
|
||||
UnsignedOrNone ResultIndex = this->allocateLocal(BO);
|
||||
if (!this->emitGetPtrLocal(*ResultIndex, BO))
|
||||
return false;
|
||||
}
|
||||
@ -1145,7 +1151,7 @@ template <class Emitter>
|
||||
bool Compiler<Emitter>::VisitComplexBinOp(const BinaryOperator *E) {
|
||||
// Prepare storage for result.
|
||||
if (!Initializing) {
|
||||
std::optional<unsigned> LocalIndex = allocateTemporary(E);
|
||||
UnsignedOrNone LocalIndex = allocateTemporary(E);
|
||||
if (!LocalIndex)
|
||||
return false;
|
||||
if (!this->emitGetPtrLocal(*LocalIndex, E))
|
||||
@ -1204,7 +1210,7 @@ bool Compiler<Emitter>::VisitComplexBinOp(const BinaryOperator *E) {
|
||||
|
||||
if (!LHSIsComplex) {
|
||||
// This is using the RHS type for the fake-complex LHS.
|
||||
std::optional<unsigned> LocalIndex = allocateTemporary(RHS);
|
||||
UnsignedOrNone LocalIndex = allocateTemporary(RHS);
|
||||
if (!LocalIndex)
|
||||
return false;
|
||||
LHSOffset = *LocalIndex;
|
||||
@ -1379,7 +1385,7 @@ bool Compiler<Emitter>::VisitVectorBinOp(const BinaryOperator *E) {
|
||||
|
||||
// Prepare storage for result.
|
||||
if (!Initializing && !E->isCompoundAssignmentOp()) {
|
||||
std::optional<unsigned> LocalIndex = allocateTemporary(E);
|
||||
UnsignedOrNone LocalIndex = allocateTemporary(E);
|
||||
if (!LocalIndex)
|
||||
return false;
|
||||
if (!this->emitGetPtrLocal(*LocalIndex, E))
|
||||
@ -1933,8 +1939,17 @@ bool Compiler<Emitter>::visitInitList(ArrayRef<const Expr *> Inits,
|
||||
PrimType TargetT = classifyPrim(Init->getType());
|
||||
|
||||
auto Eval = [&](const IntegerLiteral *IL, unsigned ElemIndex) {
|
||||
if (!this->emitConst(IL->getValue(), Init))
|
||||
return false;
|
||||
if (TargetT == PT_Float) {
|
||||
if (!this->emitConst(IL->getValue(), classifyPrim(IL), Init))
|
||||
return false;
|
||||
const auto *Sem = &Ctx.getFloatSemantics(CAT->getElementType());
|
||||
if (!this->emitCastIntegralFloating(classifyPrim(IL), Sem,
|
||||
getFPOptions(E), E))
|
||||
return false;
|
||||
} else {
|
||||
if (!this->emitConst(IL->getValue(), TargetT, Init))
|
||||
return false;
|
||||
}
|
||||
return this->emitInitElem(TargetT, ElemIndex, IL);
|
||||
};
|
||||
if (!EmbedS->doForEachDataElement(Eval, ElementIndex))
|
||||
@ -2084,7 +2099,7 @@ bool Compiler<Emitter>::visitCallArgs(ArrayRef<const Expr *> Args,
|
||||
ExplicitMemberFn);
|
||||
}
|
||||
|
||||
std::optional<unsigned> LocalIndex =
|
||||
UnsignedOrNone LocalIndex =
|
||||
allocateLocal(std::move(Source), Arg->getType(),
|
||||
/*ExtendingDecl=*/nullptr, ScopeKind::Call);
|
||||
if (!LocalIndex)
|
||||
@ -2927,7 +2942,7 @@ bool Compiler<Emitter>::VisitMaterializeTemporaryExpr(
|
||||
return false;
|
||||
|
||||
const Expr *Inner = E->getSubExpr()->skipRValueSubobjectAdjustments();
|
||||
if (std::optional<unsigned> LocalIndex =
|
||||
if (UnsignedOrNone LocalIndex =
|
||||
allocateLocal(E, Inner->getType(), E->getExtendingDecl())) {
|
||||
InitLinkScope<Emitter> ILS(this, InitLink::Temp(*LocalIndex));
|
||||
if (!this->emitGetPtrLocal(*LocalIndex, E))
|
||||
@ -2974,20 +2989,25 @@ bool Compiler<Emitter>::VisitCompoundLiteralExpr(const CompoundLiteralExpr *E) {
|
||||
if (T && !E->isLValue())
|
||||
return this->delegate(Init);
|
||||
|
||||
if (std::optional<unsigned> GlobalIndex = P.createGlobal(E)) {
|
||||
if (!this->emitGetPtrGlobal(*GlobalIndex, E))
|
||||
std::optional<unsigned> GlobalIndex = P.createGlobal(E);
|
||||
if (!GlobalIndex)
|
||||
return false;
|
||||
|
||||
if (!this->emitGetPtrGlobal(*GlobalIndex, E))
|
||||
return false;
|
||||
|
||||
// Since this is a global variable, we might've already seen,
|
||||
// don't do it again.
|
||||
if (P.isGlobalInitialized(*GlobalIndex))
|
||||
return true;
|
||||
|
||||
if (T) {
|
||||
if (!this->visit(Init))
|
||||
return false;
|
||||
|
||||
if (T) {
|
||||
if (!this->visit(Init))
|
||||
return false;
|
||||
return this->emitInitGlobal(*T, *GlobalIndex, E);
|
||||
}
|
||||
|
||||
return this->visitInitializer(Init) && this->emitFinishInit(E);
|
||||
return this->emitInitGlobal(*T, *GlobalIndex, E);
|
||||
}
|
||||
|
||||
return false;
|
||||
return this->visitInitializer(Init) && this->emitFinishInit(E);
|
||||
}
|
||||
|
||||
// Otherwise, use a local variable.
|
||||
@ -2999,7 +3019,7 @@ bool Compiler<Emitter>::VisitCompoundLiteralExpr(const CompoundLiteralExpr *E) {
|
||||
unsigned LocalIndex;
|
||||
if (T)
|
||||
LocalIndex = this->allocateLocalPrimitive(Init, *T, /*IsConst=*/false);
|
||||
else if (std::optional<unsigned> MaybeIndex = this->allocateLocal(Init))
|
||||
else if (UnsignedOrNone MaybeIndex = this->allocateLocal(Init))
|
||||
LocalIndex = *MaybeIndex;
|
||||
else
|
||||
return false;
|
||||
@ -3170,20 +3190,13 @@ bool Compiler<Emitter>::VisitCXXConstructExpr(const CXXConstructExpr *E) {
|
||||
if (T->isRecordType()) {
|
||||
const CXXConstructorDecl *Ctor = E->getConstructor();
|
||||
|
||||
// Trivial copy/move constructor. Avoid copy.
|
||||
if (Ctor->isDefaulted() && Ctor->isCopyOrMoveConstructor() &&
|
||||
Ctor->isTrivial() &&
|
||||
E->getArg(0)->isTemporaryObject(Ctx.getASTContext(),
|
||||
T->getAsCXXRecordDecl()))
|
||||
return this->visitInitializer(E->getArg(0));
|
||||
|
||||
// If we're discarding a construct expression, we still need
|
||||
// to allocate a variable and call the constructor and destructor.
|
||||
if (DiscardResult) {
|
||||
if (Ctor->isTrivial())
|
||||
return true;
|
||||
assert(!Initializing);
|
||||
std::optional<unsigned> LocalIndex = allocateLocal(E);
|
||||
UnsignedOrNone LocalIndex = allocateLocal(E);
|
||||
|
||||
if (!LocalIndex)
|
||||
return false;
|
||||
@ -3192,6 +3205,13 @@ bool Compiler<Emitter>::VisitCXXConstructExpr(const CXXConstructExpr *E) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Trivial copy/move constructor. Avoid copy.
|
||||
if (Ctor->isDefaulted() && Ctor->isCopyOrMoveConstructor() &&
|
||||
Ctor->isTrivial() &&
|
||||
E->getArg(0)->isTemporaryObject(Ctx.getASTContext(),
|
||||
T->getAsCXXRecordDecl()))
|
||||
return this->visitInitializer(E->getArg(0));
|
||||
|
||||
// Zero initialization.
|
||||
if (E->requiresZeroInitialization()) {
|
||||
const Record *R = getRecord(E->getType());
|
||||
@ -3386,7 +3406,7 @@ bool Compiler<Emitter>::VisitCXXScalarValueInitExpr(
|
||||
|
||||
if (const auto *CT = Ty->getAs<ComplexType>()) {
|
||||
if (!Initializing) {
|
||||
std::optional<unsigned> LocalIndex = allocateLocal(E);
|
||||
UnsignedOrNone LocalIndex = allocateLocal(E);
|
||||
if (!LocalIndex)
|
||||
return false;
|
||||
if (!this->emitGetPtrLocal(*LocalIndex, E))
|
||||
@ -3409,7 +3429,7 @@ bool Compiler<Emitter>::VisitCXXScalarValueInitExpr(
|
||||
if (const auto *VT = Ty->getAs<VectorType>()) {
|
||||
// FIXME: Code duplication with the _Complex case above.
|
||||
if (!Initializing) {
|
||||
std::optional<unsigned> LocalIndex = allocateLocal(E);
|
||||
UnsignedOrNone LocalIndex = allocateLocal(E);
|
||||
if (!LocalIndex)
|
||||
return false;
|
||||
if (!this->emitGetPtrLocal(*LocalIndex, E))
|
||||
@ -4035,8 +4055,7 @@ bool Compiler<Emitter>::VisitExtVectorElementExpr(
|
||||
|
||||
// Now the vector variable for the return value.
|
||||
if (!Initializing) {
|
||||
std::optional<unsigned> ResultIndex;
|
||||
ResultIndex = allocateLocal(E);
|
||||
UnsignedOrNone ResultIndex = allocateLocal(E);
|
||||
if (!ResultIndex)
|
||||
return false;
|
||||
if (!this->emitGetPtrLocal(*ResultIndex, E))
|
||||
@ -4097,8 +4116,7 @@ bool Compiler<Emitter>::VisitCXXStdInitializerListExpr(
|
||||
|
||||
PrimType SecondFieldT = classifyPrim(R->getField(1u)->Decl->getType());
|
||||
if (isIntegralType(SecondFieldT)) {
|
||||
if (!this->emitConst(static_cast<APSInt>(ArrayType->getSize()),
|
||||
SecondFieldT, E))
|
||||
if (!this->emitConst(ArrayType->getSize(), SecondFieldT, E))
|
||||
return false;
|
||||
return this->emitInitField(SecondFieldT, R->getField(1u)->Offset, E);
|
||||
}
|
||||
@ -4108,7 +4126,7 @@ bool Compiler<Emitter>::VisitCXXStdInitializerListExpr(
|
||||
return false;
|
||||
if (!this->emitExpandPtr(E))
|
||||
return false;
|
||||
if (!this->emitConst(static_cast<APSInt>(ArrayType->getSize()), PT_Uint64, E))
|
||||
if (!this->emitConst(ArrayType->getSize(), PT_Uint64, E))
|
||||
return false;
|
||||
if (!this->emitArrayElemPtrPop(PT_Uint64, E))
|
||||
return false;
|
||||
@ -4140,13 +4158,13 @@ bool Compiler<Emitter>::VisitStmtExpr(const StmtExpr *E) {
|
||||
|
||||
template <class Emitter> bool Compiler<Emitter>::discard(const Expr *E) {
|
||||
OptionScope<Emitter> Scope(this, /*NewDiscardResult=*/true,
|
||||
/*NewInitializing=*/false);
|
||||
/*NewInitializing=*/false, /*ToLValue=*/false);
|
||||
return this->Visit(E);
|
||||
}
|
||||
|
||||
template <class Emitter> bool Compiler<Emitter>::delegate(const Expr *E) {
|
||||
// We're basically doing:
|
||||
// OptionScope<Emitter> Scope(this, DicardResult, Initializing);
|
||||
// OptionScope<Emitter> Scope(this, DicardResult, Initializing, ToLValue);
|
||||
// but that's unnecessary of course.
|
||||
return this->Visit(E);
|
||||
}
|
||||
@ -4161,7 +4179,7 @@ template <class Emitter> bool Compiler<Emitter>::visit(const Expr *E) {
|
||||
// Create local variable to hold the return value.
|
||||
if (!E->isGLValue() && !E->getType()->isAnyComplexType() &&
|
||||
!canClassify(E->getType())) {
|
||||
std::optional<unsigned> LocalIndex = allocateLocal(E);
|
||||
UnsignedOrNone LocalIndex = allocateLocal(E);
|
||||
if (!LocalIndex)
|
||||
return false;
|
||||
|
||||
@ -4174,7 +4192,7 @@ template <class Emitter> bool Compiler<Emitter>::visit(const Expr *E) {
|
||||
// Otherwise,we have a primitive return value, produce the value directly
|
||||
// and push it on the stack.
|
||||
OptionScope<Emitter> Scope(this, /*NewDiscardResult=*/false,
|
||||
/*NewInitializing=*/false);
|
||||
/*NewInitializing=*/false, /*ToLValue=*/ToLValue);
|
||||
return this->Visit(E);
|
||||
}
|
||||
|
||||
@ -4183,7 +4201,13 @@ bool Compiler<Emitter>::visitInitializer(const Expr *E) {
|
||||
assert(!canClassify(E->getType()));
|
||||
|
||||
OptionScope<Emitter> Scope(this, /*NewDiscardResult=*/false,
|
||||
/*NewInitializing=*/true);
|
||||
/*NewInitializing=*/true, /*ToLValue=*/false);
|
||||
return this->Visit(E);
|
||||
}
|
||||
|
||||
template <class Emitter> bool Compiler<Emitter>::visitAsLValue(const Expr *E) {
|
||||
OptionScope<Emitter> Scope(this, /*NewDiscardResult=*/false,
|
||||
/*NewInitializing=*/false, /*ToLValue=*/true);
|
||||
return this->Visit(E);
|
||||
}
|
||||
|
||||
@ -4480,12 +4504,18 @@ bool Compiler<Emitter>::emitConst(T Value, const Expr *E) {
|
||||
template <class Emitter>
|
||||
bool Compiler<Emitter>::emitConst(const APSInt &Value, PrimType Ty,
|
||||
const Expr *E) {
|
||||
return this->emitConst(static_cast<const APInt &>(Value), Ty, E);
|
||||
}
|
||||
|
||||
template <class Emitter>
|
||||
bool Compiler<Emitter>::emitConst(const APInt &Value, PrimType Ty,
|
||||
const Expr *E) {
|
||||
if (Ty == PT_IntAPS)
|
||||
return this->emitConstIntAPS(Value, E);
|
||||
if (Ty == PT_IntAP)
|
||||
return this->emitConstIntAP(Value, E);
|
||||
|
||||
if (Value.isSigned())
|
||||
if (isSignedType(Ty))
|
||||
return this->emitConst(Value.getSExtValue(), Ty, E);
|
||||
return this->emitConst(Value.getZExtValue(), Ty, E);
|
||||
}
|
||||
@ -4516,10 +4546,10 @@ unsigned Compiler<Emitter>::allocateLocalPrimitive(
|
||||
}
|
||||
|
||||
template <class Emitter>
|
||||
std::optional<unsigned>
|
||||
Compiler<Emitter>::allocateLocal(DeclTy &&Src, QualType Ty,
|
||||
const ValueDecl *ExtendingDecl, ScopeKind SC,
|
||||
bool IsConstexprUnknown) {
|
||||
UnsignedOrNone Compiler<Emitter>::allocateLocal(DeclTy &&Src, QualType Ty,
|
||||
const ValueDecl *ExtendingDecl,
|
||||
ScopeKind SC,
|
||||
bool IsConstexprUnknown) {
|
||||
const ValueDecl *Key = nullptr;
|
||||
const Expr *Init = nullptr;
|
||||
bool IsTemporary = false;
|
||||
@ -4553,7 +4583,7 @@ Compiler<Emitter>::allocateLocal(DeclTy &&Src, QualType Ty,
|
||||
}
|
||||
|
||||
template <class Emitter>
|
||||
std::optional<unsigned> Compiler<Emitter>::allocateTemporary(const Expr *E) {
|
||||
UnsignedOrNone Compiler<Emitter>::allocateTemporary(const Expr *E) {
|
||||
QualType Ty = E->getType();
|
||||
assert(!Ty->isRecordType());
|
||||
|
||||
@ -4632,7 +4662,7 @@ bool Compiler<Emitter>::visitExpr(const Expr *E, bool DestroyToplevelScope) {
|
||||
// Expressions with a composite return type.
|
||||
// For us, that means everything we don't
|
||||
// have a PrimType for.
|
||||
if (std::optional<unsigned> LocalOffset = this->allocateLocal(E)) {
|
||||
if (UnsignedOrNone LocalOffset = this->allocateLocal(E)) {
|
||||
InitLinkScope<Emitter> ILS(this, InitLink::Temp(*LocalOffset));
|
||||
if (!this->emitGetPtrLocal(*LocalOffset, E))
|
||||
return false;
|
||||
@ -4829,7 +4859,7 @@ VarCreationState Compiler<Emitter>::visitVarDecl(const VarDecl *VD,
|
||||
return this->emitSetLocal(*VarT, Offset, VD);
|
||||
}
|
||||
} else {
|
||||
if (std::optional<unsigned> Offset = this->allocateLocal(
|
||||
if (UnsignedOrNone Offset = this->allocateLocal(
|
||||
VD, VD->getType(), nullptr, ScopeKind::Block, IsConstexprUnknown)) {
|
||||
if (!Init)
|
||||
return true;
|
||||
@ -4944,7 +4974,6 @@ bool Compiler<Emitter>::visitAPValueInitializer(const APValue &Val,
|
||||
template <class Emitter>
|
||||
bool Compiler<Emitter>::VisitBuiltinCallExpr(const CallExpr *E,
|
||||
unsigned BuiltinID) {
|
||||
|
||||
if (BuiltinID == Builtin::BI__builtin_constant_p) {
|
||||
// Void argument is always invalid and harder to handle later.
|
||||
if (E->getArg(0)->getType()->isVoidType()) {
|
||||
@ -4982,18 +5011,38 @@ bool Compiler<Emitter>::VisitBuiltinCallExpr(const CallExpr *E,
|
||||
|
||||
// Non-primitive return type. Prepare storage.
|
||||
if (!Initializing && !ReturnT && !ReturnType->isVoidType()) {
|
||||
std::optional<unsigned> LocalIndex = allocateLocal(E);
|
||||
UnsignedOrNone LocalIndex = allocateLocal(E);
|
||||
if (!LocalIndex)
|
||||
return false;
|
||||
if (!this->emitGetPtrLocal(*LocalIndex, E))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Context::isUnevaluatedBuiltin(BuiltinID)) {
|
||||
// Put arguments on the stack.
|
||||
for (const auto *Arg : E->arguments()) {
|
||||
if (!this->visit(Arg))
|
||||
// Prepare function arguments including special cases.
|
||||
switch (BuiltinID) {
|
||||
case Builtin::BI__builtin_object_size:
|
||||
case Builtin::BI__builtin_dynamic_object_size: {
|
||||
assert(E->getNumArgs() == 2);
|
||||
const Expr *Arg0 = E->getArg(0);
|
||||
if (Arg0->isGLValue()) {
|
||||
if (!this->visit(Arg0))
|
||||
return false;
|
||||
|
||||
} else {
|
||||
if (!this->visitAsLValue(Arg0))
|
||||
return false;
|
||||
}
|
||||
if (!this->visit(E->getArg(1)))
|
||||
return false;
|
||||
|
||||
} break;
|
||||
default:
|
||||
if (!Context::isUnevaluatedBuiltin(BuiltinID)) {
|
||||
// Put arguments on the stack.
|
||||
for (const auto *Arg : E->arguments()) {
|
||||
if (!this->visit(Arg))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -5058,7 +5107,7 @@ bool Compiler<Emitter>::VisitCallExpr(const CallExpr *E) {
|
||||
// If we need to discard the return value but the function returns its
|
||||
// value via an RVO pointer, we need to create one such pointer just
|
||||
// for this call.
|
||||
if (std::optional<unsigned> LocalIndex = allocateLocal(E)) {
|
||||
if (UnsignedOrNone LocalIndex = allocateLocal(E)) {
|
||||
if (!this->emitGetPtrLocal(*LocalIndex, E))
|
||||
return false;
|
||||
}
|
||||
@ -5066,7 +5115,7 @@ bool Compiler<Emitter>::VisitCallExpr(const CallExpr *E) {
|
||||
// We need the result. Prepare a pointer to return or
|
||||
// dup the current one.
|
||||
if (!Initializing) {
|
||||
if (std::optional<unsigned> LocalIndex = allocateLocal(E)) {
|
||||
if (UnsignedOrNone LocalIndex = allocateLocal(E)) {
|
||||
if (!this->emitGetPtrLocal(*LocalIndex, E))
|
||||
return false;
|
||||
}
|
||||
@ -5102,7 +5151,7 @@ bool Compiler<Emitter>::VisitCallExpr(const CallExpr *E) {
|
||||
}
|
||||
|
||||
bool Devirtualized = false;
|
||||
std::optional<unsigned> CalleeOffset;
|
||||
UnsignedOrNone CalleeOffset = std::nullopt;
|
||||
// Add the (optional, implicit) This pointer.
|
||||
if (const auto *MC = dyn_cast<CXXMemberCallExpr>(E)) {
|
||||
if (!FuncDecl && classifyPrim(E->getCallee()) == PT_MemberPtr) {
|
||||
@ -5146,7 +5195,8 @@ bool Compiler<Emitter>::VisitCallExpr(const CallExpr *E) {
|
||||
if (!this->emitCheckPseudoDtor(E))
|
||||
return false;
|
||||
const Expr *Base = PD->getBase();
|
||||
if (!Base->isGLValue())
|
||||
// E.g. `using T = int; 0.~T();`.
|
||||
if (OptPrimType BaseT = classify(Base); !BaseT || BaseT != PT_Ptr)
|
||||
return this->discard(Base);
|
||||
if (!this->visit(Base))
|
||||
return false;
|
||||
@ -6509,7 +6559,7 @@ bool Compiler<Emitter>::VisitComplexUnaryOperator(const UnaryOperator *E) {
|
||||
OptPrimType ResT = classify(E);
|
||||
auto prepareResult = [=]() -> bool {
|
||||
if (!ResT && !Initializing) {
|
||||
std::optional<unsigned> LocalIndex = allocateLocal(SubExpr);
|
||||
UnsignedOrNone LocalIndex = allocateLocal(SubExpr);
|
||||
if (!LocalIndex)
|
||||
return false;
|
||||
return this->emitGetPtrLocal(*LocalIndex, E);
|
||||
@ -6627,7 +6677,7 @@ bool Compiler<Emitter>::VisitVectorUnaryOperator(const UnaryOperator *E) {
|
||||
return this->delegate(SubExpr);
|
||||
|
||||
if (!Initializing) {
|
||||
std::optional<unsigned> LocalIndex = allocateLocal(SubExpr);
|
||||
UnsignedOrNone LocalIndex = allocateLocal(SubExpr);
|
||||
if (!LocalIndex)
|
||||
return false;
|
||||
if (!this->emitGetPtrLocal(*LocalIndex, E))
|
||||
@ -7231,7 +7281,7 @@ bool Compiler<Emitter>::emitBuiltinBitCast(const CastExpr *E) {
|
||||
|
||||
// Prepare storage for the result in case we discard.
|
||||
if (DiscardResult && !Initializing && !ToT) {
|
||||
std::optional<unsigned> LocalIndex = allocateLocal(E);
|
||||
UnsignedOrNone LocalIndex = allocateLocal(E);
|
||||
if (!LocalIndex)
|
||||
return false;
|
||||
if (!this->emitGetPtrLocal(*LocalIndex, E))
|
||||
|
@ -282,6 +282,7 @@ protected:
|
||||
/// been created. visitInitializer() then relies on a pointer to this
|
||||
/// variable being on top of the stack.
|
||||
bool visitInitializer(const Expr *E);
|
||||
bool visitAsLValue(const Expr *E);
|
||||
/// Evaluates an expression for side effects and discards the result.
|
||||
bool discard(const Expr *E);
|
||||
/// Just pass evaluation on to \p E. This leaves all the parsing flags
|
||||
@ -315,11 +316,11 @@ protected:
|
||||
bool IsConstexprUnknown = false);
|
||||
|
||||
/// Allocates a space storing a local given its type.
|
||||
std::optional<unsigned>
|
||||
allocateLocal(DeclTy &&Decl, QualType Ty = QualType(),
|
||||
const ValueDecl *ExtendingDecl = nullptr,
|
||||
ScopeKind = ScopeKind::Block, bool IsConstexprUnknown = false);
|
||||
std::optional<unsigned> allocateTemporary(const Expr *E);
|
||||
UnsignedOrNone allocateLocal(DeclTy &&Decl, QualType Ty = QualType(),
|
||||
const ValueDecl *ExtendingDecl = nullptr,
|
||||
ScopeKind = ScopeKind::Block,
|
||||
bool IsConstexprUnknown = false);
|
||||
UnsignedOrNone allocateTemporary(const Expr *E);
|
||||
|
||||
private:
|
||||
friend class VariableScope<Emitter>;
|
||||
@ -346,9 +347,10 @@ private:
|
||||
|
||||
/// Emits an APSInt constant.
|
||||
bool emitConst(const llvm::APSInt &Value, PrimType Ty, const Expr *E);
|
||||
bool emitConst(const llvm::APInt &Value, PrimType Ty, const Expr *E);
|
||||
bool emitConst(const llvm::APSInt &Value, const Expr *E);
|
||||
bool emitConst(const llvm::APInt &Value, const Expr *E) {
|
||||
return emitConst(static_cast<llvm::APSInt>(Value), E);
|
||||
return emitConst(Value, classifyPrim(E), E);
|
||||
}
|
||||
|
||||
/// Emits an integer constant.
|
||||
@ -426,6 +428,7 @@ protected:
|
||||
bool DiscardResult = false;
|
||||
|
||||
bool InStmtExpr = false;
|
||||
bool ToLValue = false;
|
||||
|
||||
/// Flag inidicating if we're initializing an already created
|
||||
/// variable. This is set in visitInitializer().
|
||||
@ -565,7 +568,7 @@ public:
|
||||
|
||||
void addLocal(const Scope::Local &Local) override {
|
||||
if (!Idx) {
|
||||
Idx = this->Ctx->Descriptors.size();
|
||||
Idx = static_cast<unsigned>(this->Ctx->Descriptors.size());
|
||||
this->Ctx->Descriptors.emplace_back();
|
||||
this->Ctx->emitInitScope(*Idx, {});
|
||||
}
|
||||
@ -613,7 +616,7 @@ public:
|
||||
}
|
||||
|
||||
/// Index of the scope in the chain.
|
||||
std::optional<unsigned> Idx;
|
||||
UnsignedOrNone Idx = std::nullopt;
|
||||
};
|
||||
|
||||
/// Scope for storage declared in a compound statement.
|
||||
|
@ -398,17 +398,11 @@ const llvm::fltSemantics &Context::getFloatSemantics(QualType T) const {
|
||||
}
|
||||
|
||||
bool Context::Run(State &Parent, const Function *Func) {
|
||||
|
||||
{
|
||||
InterpState State(Parent, *P, Stk, *this, Func);
|
||||
if (Interpret(State)) {
|
||||
assert(Stk.empty());
|
||||
return true;
|
||||
}
|
||||
// State gets destroyed here, so the Stk.clear() below doesn't accidentally
|
||||
// remove values the State's destructor might access.
|
||||
InterpState State(Parent, *P, Stk, *this, Func);
|
||||
if (Interpret(State)) {
|
||||
assert(Stk.empty());
|
||||
return true;
|
||||
}
|
||||
|
||||
Stk.clear();
|
||||
return false;
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ namespace interp {
|
||||
class Function;
|
||||
class Program;
|
||||
class State;
|
||||
enum PrimType : unsigned;
|
||||
enum PrimType : uint8_t;
|
||||
|
||||
struct ParamOffset {
|
||||
unsigned Offset;
|
||||
|
@ -24,7 +24,7 @@ class Record;
|
||||
class SourceInfo;
|
||||
struct InitMap;
|
||||
struct Descriptor;
|
||||
enum PrimType : unsigned;
|
||||
enum PrimType : uint8_t;
|
||||
|
||||
using DeclTy = llvm::PointerUnion<const Decl *, const Expr *>;
|
||||
using InitMapPtr = std::optional<std::pair<bool, std::shared_ptr<InitMap>>>;
|
||||
|
@ -545,7 +545,7 @@ LLVM_DUMP_METHOD void Block::dump(llvm::raw_ostream &OS) const {
|
||||
OS << " Initialized: " << IsInitialized << "\n";
|
||||
OS << " Weak: " << isWeak() << "\n";
|
||||
OS << " Dummy: " << isDummy() << '\n';
|
||||
OS << " Dynamic: " << IsDynamic << "\n";
|
||||
OS << " Dynamic: " << isDynamic() << "\n";
|
||||
}
|
||||
|
||||
LLVM_DUMP_METHOD void EvaluationResult::dump() const {
|
||||
|
@ -101,13 +101,17 @@ Block *DynamicAllocator::allocate(const Descriptor *D, unsigned EvalID,
|
||||
ID->LifeState =
|
||||
AllocForm == Form::Operator ? Lifetime::Ended : Lifetime::Started;
|
||||
|
||||
B->IsDynamic = true;
|
||||
|
||||
if (auto It = AllocationSites.find(D->asExpr()); It != AllocationSites.end())
|
||||
if (auto It = AllocationSites.find(D->asExpr());
|
||||
It != AllocationSites.end()) {
|
||||
It->second.Allocations.emplace_back(std::move(Memory));
|
||||
else
|
||||
B->setDynAllocId(It->second.NumAllocs);
|
||||
++It->second.NumAllocs;
|
||||
} else {
|
||||
AllocationSites.insert(
|
||||
{D->asExpr(), AllocationSite(std::move(Memory), AllocForm)});
|
||||
B->setDynAllocId(0);
|
||||
}
|
||||
assert(B->isDynamic());
|
||||
return B;
|
||||
}
|
||||
|
||||
|
@ -48,11 +48,13 @@ private:
|
||||
|
||||
struct AllocationSite {
|
||||
llvm::SmallVector<Allocation> Allocations;
|
||||
unsigned NumAllocs = 0;
|
||||
Form AllocForm;
|
||||
|
||||
AllocationSite(std::unique_ptr<std::byte[]> Memory, Form AllocForm)
|
||||
: AllocForm(AllocForm) {
|
||||
Allocations.push_back({std::move(Memory)});
|
||||
++NumAllocs;
|
||||
}
|
||||
|
||||
size_t size() const { return Allocations.size(); }
|
||||
|
@ -28,7 +28,7 @@ namespace interp {
|
||||
class Program;
|
||||
class ByteCodeEmitter;
|
||||
class Pointer;
|
||||
enum PrimType : uint32_t;
|
||||
enum PrimType : uint8_t;
|
||||
|
||||
/// Describes a scope block.
|
||||
///
|
||||
|
@ -1852,6 +1852,11 @@ bool EndLifetime(InterpState &S, CodePtr OpPC) {
|
||||
const auto &Ptr = S.Stk.peek<Pointer>();
|
||||
if (Ptr.isBlockPointer() && !CheckDummy(S, OpPC, Ptr.block(), AK_Destroy))
|
||||
return false;
|
||||
|
||||
// FIXME: We need per-element lifetime information for primitive arrays.
|
||||
if (Ptr.isArrayElement())
|
||||
return true;
|
||||
|
||||
endLifetimeRecurse(Ptr.narrow());
|
||||
return true;
|
||||
}
|
||||
@ -1861,6 +1866,11 @@ bool EndLifetimePop(InterpState &S, CodePtr OpPC) {
|
||||
const auto &Ptr = S.Stk.pop<Pointer>();
|
||||
if (Ptr.isBlockPointer() && !CheckDummy(S, OpPC, Ptr.block(), AK_Destroy))
|
||||
return false;
|
||||
|
||||
// FIXME: We need per-element lifetime information for primitive arrays.
|
||||
if (Ptr.isArrayElement())
|
||||
return true;
|
||||
|
||||
endLifetimeRecurse(Ptr.narrow());
|
||||
return true;
|
||||
}
|
||||
|
@ -18,10 +18,6 @@ using namespace clang::interp;
|
||||
|
||||
void Block::addPointer(Pointer *P) {
|
||||
assert(P);
|
||||
if (IsStatic) {
|
||||
assert(!Pointers);
|
||||
return;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
assert(!hasPointer(P));
|
||||
@ -39,10 +35,6 @@ void Block::addPointer(Pointer *P) {
|
||||
void Block::removePointer(Pointer *P) {
|
||||
assert(P->isBlockPointer());
|
||||
assert(P);
|
||||
if (IsStatic) {
|
||||
assert(!Pointers);
|
||||
return;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
assert(hasPointer(P));
|
||||
@ -64,7 +56,7 @@ void Block::removePointer(Pointer *P) {
|
||||
}
|
||||
|
||||
void Block::cleanup() {
|
||||
if (Pointers == nullptr && !IsDynamic && isDead())
|
||||
if (Pointers == nullptr && !isDynamic() && isDead())
|
||||
(reinterpret_cast<DeadBlock *>(this + 1) - 1)->free();
|
||||
}
|
||||
|
||||
@ -74,10 +66,6 @@ void Block::replacePointer(Pointer *Old, Pointer *New) {
|
||||
assert(New);
|
||||
assert(New->isBlockPointer());
|
||||
assert(Old != New);
|
||||
if (IsStatic) {
|
||||
assert(!Pointers);
|
||||
return;
|
||||
}
|
||||
#ifndef NDEBUG
|
||||
assert(hasPointer(Old));
|
||||
#endif
|
||||
@ -123,7 +111,7 @@ DeadBlock::DeadBlock(DeadBlock *&Root, Block *Blk)
|
||||
Prev = nullptr;
|
||||
Root = this;
|
||||
|
||||
B.IsDynamic = Blk->IsDynamic;
|
||||
B.DynAllocId = Blk->DynAllocId;
|
||||
|
||||
// Transfer pointers.
|
||||
B.Pointers = Blk->Pointers;
|
||||
|
@ -22,7 +22,7 @@ class Block;
|
||||
class DeadBlock;
|
||||
class InterpState;
|
||||
class Pointer;
|
||||
enum PrimType : unsigned;
|
||||
enum PrimType : uint8_t;
|
||||
|
||||
/// A memory block, either on the stack or in the heap.
|
||||
///
|
||||
@ -50,9 +50,9 @@ private:
|
||||
|
||||
public:
|
||||
/// Creates a new block.
|
||||
Block(unsigned EvalID, const std::optional<unsigned> &DeclID,
|
||||
const Descriptor *Desc, bool IsStatic = false, bool IsExtern = false,
|
||||
bool IsWeak = false, bool IsDummy = false)
|
||||
Block(unsigned EvalID, UnsignedOrNone DeclID, const Descriptor *Desc,
|
||||
bool IsStatic = false, bool IsExtern = false, bool IsWeak = false,
|
||||
bool IsDummy = false)
|
||||
: Desc(Desc), DeclID(DeclID), EvalID(EvalID), IsStatic(IsStatic) {
|
||||
assert(Desc);
|
||||
AccessFlags |= (ExternFlag * IsExtern);
|
||||
@ -62,8 +62,7 @@ public:
|
||||
|
||||
Block(unsigned EvalID, const Descriptor *Desc, bool IsStatic = false,
|
||||
bool IsExtern = false, bool IsWeak = false, bool IsDummy = false)
|
||||
: Desc(Desc), DeclID((unsigned)-1), EvalID(EvalID), IsStatic(IsStatic),
|
||||
IsDynamic(false) {
|
||||
: Desc(Desc), EvalID(EvalID), IsStatic(IsStatic) {
|
||||
assert(Desc);
|
||||
AccessFlags |= (ExternFlag * IsExtern);
|
||||
AccessFlags |= (WeakFlag * IsWeak);
|
||||
@ -81,13 +80,13 @@ public:
|
||||
/// Checks if the block is temporary.
|
||||
bool isTemporary() const { return Desc->IsTemporary; }
|
||||
bool isWeak() const { return AccessFlags & WeakFlag; }
|
||||
bool isDynamic() const { return IsDynamic; }
|
||||
bool isDynamic() const { return (DynAllocId != std::nullopt); }
|
||||
bool isDummy() const { return AccessFlags & DummyFlag; }
|
||||
bool isDead() const { return AccessFlags & DeadFlag; }
|
||||
/// Returns the size of the block.
|
||||
unsigned getSize() const { return Desc->getAllocSize(); }
|
||||
/// Returns the declaration ID.
|
||||
std::optional<unsigned> getDeclID() const { return DeclID; }
|
||||
UnsignedOrNone getDeclID() const { return DeclID; }
|
||||
/// Returns whether the data of this block has been initialized via
|
||||
/// invoking the Ctor func.
|
||||
bool isInitialized() const { return IsInitialized; }
|
||||
@ -150,6 +149,7 @@ private:
|
||||
friend class DeadBlock;
|
||||
friend class InterpState;
|
||||
friend class DynamicAllocator;
|
||||
friend class Program;
|
||||
|
||||
Block(unsigned EvalID, const Descriptor *Desc, bool IsExtern, bool IsStatic,
|
||||
bool IsWeak, bool IsDummy, bool IsDead)
|
||||
@ -161,6 +161,9 @@ private:
|
||||
AccessFlags |= (DummyFlag * IsDummy);
|
||||
}
|
||||
|
||||
/// To be called by DynamicAllocator.
|
||||
void setDynAllocId(unsigned ID) { DynAllocId = ID; }
|
||||
|
||||
/// Deletes a dead block at the end of its lifetime.
|
||||
void cleanup();
|
||||
|
||||
@ -177,16 +180,15 @@ private:
|
||||
/// Start of the chain of pointers.
|
||||
Pointer *Pointers = nullptr;
|
||||
/// Unique identifier of the declaration.
|
||||
std::optional<unsigned> DeclID;
|
||||
UnsignedOrNone DeclID = std::nullopt;
|
||||
const unsigned EvalID = ~0u;
|
||||
/// Flag indicating if the block has static storage duration.
|
||||
bool IsStatic = false;
|
||||
/// Flag indicating if the block contents have been initialized
|
||||
/// via invokeCtor.
|
||||
bool IsInitialized = false;
|
||||
/// Flag indicating if this block has been allocated via dynamic
|
||||
/// memory allocation (e.g. malloc).
|
||||
bool IsDynamic = false;
|
||||
/// Allocation ID for this dynamic allocation, if it is one.
|
||||
UnsignedOrNone DynAllocId = std::nullopt;
|
||||
/// AccessFlags containing IsExtern, IsDead, IsWeak, and IsDummy bits.
|
||||
uint8_t AccessFlags = 0;
|
||||
};
|
||||
|
@ -2170,29 +2170,32 @@ static bool interp__builtin_memchr(InterpState &S, CodePtr OpPC,
|
||||
return true;
|
||||
}
|
||||
|
||||
static unsigned computeFullDescSize(const ASTContext &ASTCtx,
|
||||
const Descriptor *Desc) {
|
||||
|
||||
static std::optional<unsigned> computeFullDescSize(const ASTContext &ASTCtx,
|
||||
const Descriptor *Desc) {
|
||||
if (Desc->isPrimitive())
|
||||
return ASTCtx.getTypeSizeInChars(Desc->getType()).getQuantity();
|
||||
|
||||
if (Desc->isArray())
|
||||
return ASTCtx.getTypeSizeInChars(Desc->getElemQualType()).getQuantity() *
|
||||
Desc->getNumElems();
|
||||
if (Desc->isRecord()) {
|
||||
// Can't use Descriptor::getType() as that may return a pointer type. Look
|
||||
// at the decl directly.
|
||||
return ASTCtx
|
||||
.getTypeSizeInChars(
|
||||
ASTCtx.getCanonicalTagType(Desc->ElemRecord->getDecl()))
|
||||
.getQuantity();
|
||||
}
|
||||
|
||||
if (Desc->isRecord())
|
||||
return ASTCtx.getTypeSizeInChars(Desc->getType()).getQuantity();
|
||||
|
||||
llvm_unreachable("Unhandled descriptor type");
|
||||
return 0;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/// Compute the byte offset of \p Ptr in the full declaration.
|
||||
static unsigned computePointerOffset(const ASTContext &ASTCtx,
|
||||
const Pointer &Ptr) {
|
||||
unsigned Result = 0;
|
||||
|
||||
Pointer P = Ptr;
|
||||
while (P.isArrayElement() || P.isField()) {
|
||||
while (P.isField() || P.isArrayElement()) {
|
||||
P = P.expand();
|
||||
const Descriptor *D = P.getFieldDesc();
|
||||
|
||||
@ -2205,7 +2208,6 @@ static unsigned computePointerOffset(const ASTContext &ASTCtx,
|
||||
Result += ElemSize * P.getIndex();
|
||||
P = P.expand().getArray();
|
||||
} else if (P.isBaseClass()) {
|
||||
|
||||
const auto *RD = cast<CXXRecordDecl>(D->asDecl());
|
||||
bool IsVirtual = Ptr.isVirtualBaseClass();
|
||||
P = P.getBase();
|
||||
@ -2234,30 +2236,136 @@ static unsigned computePointerOffset(const ASTContext &ASTCtx,
|
||||
return Result;
|
||||
}
|
||||
|
||||
/// Does Ptr point to the last subobject?
|
||||
static bool pointsToLastObject(const Pointer &Ptr) {
|
||||
Pointer P = Ptr;
|
||||
while (!P.isRoot()) {
|
||||
|
||||
if (P.isArrayElement()) {
|
||||
P = P.expand().getArray();
|
||||
continue;
|
||||
}
|
||||
if (P.isBaseClass()) {
|
||||
if (P.getRecord()->getNumFields() > 0)
|
||||
return false;
|
||||
P = P.getBase();
|
||||
continue;
|
||||
}
|
||||
|
||||
Pointer Base = P.getBase();
|
||||
if (const Record *R = Base.getRecord()) {
|
||||
assert(P.getField());
|
||||
if (P.getField()->getFieldIndex() != R->getNumFields() - 1)
|
||||
return false;
|
||||
}
|
||||
P = Base;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Does Ptr point to the last object AND to a flexible array member?
|
||||
static bool isUserWritingOffTheEnd(const ASTContext &Ctx, const Pointer &Ptr) {
|
||||
auto isFlexibleArrayMember = [&](const Descriptor *FieldDesc) {
|
||||
using FAMKind = LangOptions::StrictFlexArraysLevelKind;
|
||||
FAMKind StrictFlexArraysLevel =
|
||||
Ctx.getLangOpts().getStrictFlexArraysLevel();
|
||||
|
||||
if (StrictFlexArraysLevel == FAMKind::Default)
|
||||
return true;
|
||||
|
||||
unsigned NumElems = FieldDesc->getNumElems();
|
||||
if (NumElems == 0 && StrictFlexArraysLevel != FAMKind::IncompleteOnly)
|
||||
return true;
|
||||
|
||||
if (NumElems == 1 && StrictFlexArraysLevel == FAMKind::OneZeroOrIncomplete)
|
||||
return true;
|
||||
return false;
|
||||
};
|
||||
|
||||
const Descriptor *FieldDesc = Ptr.getFieldDesc();
|
||||
if (!FieldDesc->isArray())
|
||||
return false;
|
||||
|
||||
return Ptr.isDummy() && pointsToLastObject(Ptr) &&
|
||||
isFlexibleArrayMember(FieldDesc);
|
||||
}
|
||||
|
||||
static bool interp__builtin_object_size(InterpState &S, CodePtr OpPC,
|
||||
const InterpFrame *Frame,
|
||||
const CallExpr *Call) {
|
||||
const ASTContext &ASTCtx = S.getASTContext();
|
||||
PrimType KindT = *S.getContext().classify(Call->getArg(1));
|
||||
[[maybe_unused]] unsigned Kind = popToAPSInt(S.Stk, KindT).getZExtValue();
|
||||
|
||||
// From the GCC docs:
|
||||
// Kind is an integer constant from 0 to 3. If the least significant bit is
|
||||
// clear, objects are whole variables. If it is set, a closest surrounding
|
||||
// subobject is considered the object a pointer points to. The second bit
|
||||
// determines if maximum or minimum of remaining bytes is computed.
|
||||
unsigned Kind = popToAPSInt(S.Stk, KindT).getZExtValue();
|
||||
assert(Kind <= 3 && "unexpected kind");
|
||||
|
||||
bool UseFieldDesc = (Kind & 1u);
|
||||
bool ReportMinimum = (Kind & 2u);
|
||||
const Pointer &Ptr = S.Stk.pop<Pointer>();
|
||||
|
||||
if (Ptr.isZero())
|
||||
if (Call->getArg(0)->HasSideEffects(ASTCtx)) {
|
||||
// "If there are any side effects in them, it returns (size_t) -1
|
||||
// for type 0 or 1 and (size_t) 0 for type 2 or 3."
|
||||
pushInteger(S, Kind <= 1 ? -1 : 0, Call->getType());
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Ptr.isZero() || !Ptr.isBlockPointer())
|
||||
return false;
|
||||
|
||||
// We can't load through pointers.
|
||||
if (Ptr.isDummy() && Ptr.getType()->isPointerType())
|
||||
return false;
|
||||
|
||||
bool DetermineForCompleteObject = Ptr.getFieldDesc() == Ptr.getDeclDesc();
|
||||
const Descriptor *DeclDesc = Ptr.getDeclDesc();
|
||||
if (!DeclDesc)
|
||||
assert(DeclDesc);
|
||||
|
||||
if (!UseFieldDesc || DetermineForCompleteObject) {
|
||||
// Lower bound, so we can't fall back to this.
|
||||
if (ReportMinimum && !DetermineForCompleteObject)
|
||||
return false;
|
||||
|
||||
// Can't read beyond the pointer decl desc.
|
||||
if (!UseFieldDesc && !ReportMinimum && DeclDesc->getType()->isPointerType())
|
||||
return false;
|
||||
} else {
|
||||
if (isUserWritingOffTheEnd(ASTCtx, Ptr.expand())) {
|
||||
// If we cannot determine the size of the initial allocation, then we
|
||||
// can't given an accurate upper-bound. However, we are still able to give
|
||||
// conservative lower-bounds for Type=3.
|
||||
if (Kind == 1)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const Descriptor *Desc = UseFieldDesc ? Ptr.getFieldDesc() : DeclDesc;
|
||||
assert(Desc);
|
||||
|
||||
std::optional<unsigned> FullSize = computeFullDescSize(ASTCtx, Desc);
|
||||
if (!FullSize)
|
||||
return false;
|
||||
|
||||
const ASTContext &ASTCtx = S.getASTContext();
|
||||
unsigned ByteOffset;
|
||||
if (UseFieldDesc) {
|
||||
if (Ptr.isBaseClass())
|
||||
ByteOffset = computePointerOffset(ASTCtx, Ptr.getBase()) -
|
||||
computePointerOffset(ASTCtx, Ptr);
|
||||
else
|
||||
ByteOffset =
|
||||
computePointerOffset(ASTCtx, Ptr) -
|
||||
computePointerOffset(ASTCtx, Ptr.expand().atIndex(0).narrow());
|
||||
} else
|
||||
ByteOffset = computePointerOffset(ASTCtx, Ptr);
|
||||
|
||||
unsigned ByteOffset = computePointerOffset(ASTCtx, Ptr);
|
||||
unsigned FullSize = computeFullDescSize(ASTCtx, DeclDesc);
|
||||
|
||||
pushInteger(S, FullSize - ByteOffset, Call->getType());
|
||||
assert(ByteOffset <= *FullSize);
|
||||
unsigned Result = *FullSize - ByteOffset;
|
||||
|
||||
pushInteger(S, Result, Call->getType());
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -2366,15 +2474,30 @@ static bool interp__builtin_elementwise_sat(InterpState &S, CodePtr OpPC,
|
||||
});
|
||||
|
||||
APSInt Result;
|
||||
if (BuiltinID == Builtin::BI__builtin_elementwise_add_sat) {
|
||||
switch (BuiltinID) {
|
||||
case Builtin::BI__builtin_elementwise_add_sat:
|
||||
Result = APSInt(Elem1.isSigned() ? Elem1.sadd_sat(Elem2)
|
||||
: Elem1.uadd_sat(Elem2),
|
||||
Call->getType()->isUnsignedIntegerOrEnumerationType());
|
||||
} else if (BuiltinID == Builtin::BI__builtin_elementwise_sub_sat) {
|
||||
break;
|
||||
case Builtin::BI__builtin_elementwise_sub_sat:
|
||||
Result = APSInt(Elem1.isSigned() ? Elem1.ssub_sat(Elem2)
|
||||
: Elem1.usub_sat(Elem2),
|
||||
Call->getType()->isUnsignedIntegerOrEnumerationType());
|
||||
} else {
|
||||
break;
|
||||
case clang::X86::BI__builtin_ia32_pmulhuw128:
|
||||
case clang::X86::BI__builtin_ia32_pmulhuw256:
|
||||
case clang::X86::BI__builtin_ia32_pmulhuw512:
|
||||
Result = APSInt(llvm::APIntOps::mulhu(Elem1, Elem2),
|
||||
/*isUnsigned=*/true);
|
||||
break;
|
||||
case clang::X86::BI__builtin_ia32_pmulhw128:
|
||||
case clang::X86::BI__builtin_ia32_pmulhw256:
|
||||
case clang::X86::BI__builtin_ia32_pmulhw512:
|
||||
Result = APSInt(llvm::APIntOps::mulhs(Elem1, Elem2),
|
||||
/*isUnsigned=*/false);
|
||||
break;
|
||||
default:
|
||||
llvm_unreachable("Wrong builtin ID");
|
||||
}
|
||||
|
||||
@ -2460,6 +2583,50 @@ static bool interp__builtin_elementwise_maxmin(InterpState &S, CodePtr OpPC,
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool interp__builtin_ia32_pmul(InterpState &S, CodePtr OpPC,
|
||||
const CallExpr *Call,
|
||||
unsigned BuiltinID) {
|
||||
assert(Call->getArg(0)->getType()->isVectorType() &&
|
||||
Call->getArg(1)->getType()->isVectorType());
|
||||
const Pointer &RHS = S.Stk.pop<Pointer>();
|
||||
const Pointer &LHS = S.Stk.pop<Pointer>();
|
||||
const Pointer &Dst = S.Stk.peek<Pointer>();
|
||||
|
||||
const auto *VT = Call->getArg(0)->getType()->castAs<VectorType>();
|
||||
PrimType ElemT = *S.getContext().classify(VT->getElementType());
|
||||
unsigned SourceLen = VT->getNumElements();
|
||||
SmallVector<APValue, 4> ResultElements;
|
||||
ResultElements.reserve(SourceLen / 2);
|
||||
|
||||
for (unsigned I = 0; I != SourceLen; I += 2) {
|
||||
APSInt Elem1;
|
||||
APSInt Elem2;
|
||||
INT_TYPE_SWITCH_NO_BOOL(ElemT, {
|
||||
Elem1 = LHS.elem<T>(I).toAPSInt();
|
||||
Elem2 = RHS.elem<T>(I).toAPSInt();
|
||||
});
|
||||
|
||||
APSInt Result;
|
||||
switch (BuiltinID) {
|
||||
case clang::X86::BI__builtin_ia32_pmuludq128:
|
||||
case clang::X86::BI__builtin_ia32_pmuludq256:
|
||||
case clang::X86::BI__builtin_ia32_pmuludq512:
|
||||
Result = APSInt(llvm::APIntOps::muluExtended(Elem1, Elem2), true);
|
||||
break;
|
||||
case clang::X86::BI__builtin_ia32_pmuldq128:
|
||||
case clang::X86::BI__builtin_ia32_pmuldq256:
|
||||
case clang::X86::BI__builtin_ia32_pmuldq512:
|
||||
Result = APSInt(llvm::APIntOps::mulsExtended(Elem1, Elem2), false);
|
||||
break;
|
||||
}
|
||||
INT_TYPE_SWITCH_NO_BOOL(ElemT,
|
||||
{ Dst.elem<T>(I) = static_cast<T>(Result); });
|
||||
}
|
||||
|
||||
Dst.initializeAllElements();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const CallExpr *Call,
|
||||
uint32_t BuiltinID) {
|
||||
if (!S.getASTContext().BuiltinInfo.isConstantEvaluated(BuiltinID))
|
||||
@ -2868,12 +3035,25 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const CallExpr *Call,
|
||||
|
||||
case Builtin::BI__builtin_elementwise_add_sat:
|
||||
case Builtin::BI__builtin_elementwise_sub_sat:
|
||||
case clang::X86::BI__builtin_ia32_pmulhuw128:
|
||||
case clang::X86::BI__builtin_ia32_pmulhuw256:
|
||||
case clang::X86::BI__builtin_ia32_pmulhuw512:
|
||||
case clang::X86::BI__builtin_ia32_pmulhw128:
|
||||
case clang::X86::BI__builtin_ia32_pmulhw256:
|
||||
case clang::X86::BI__builtin_ia32_pmulhw512:
|
||||
return interp__builtin_elementwise_sat(S, OpPC, Call, BuiltinID);
|
||||
|
||||
case Builtin::BI__builtin_elementwise_max:
|
||||
case Builtin::BI__builtin_elementwise_min:
|
||||
return interp__builtin_elementwise_maxmin(S, OpPC, Call, BuiltinID);
|
||||
|
||||
case clang::X86::BI__builtin_ia32_pmuldq128:
|
||||
case clang::X86::BI__builtin_ia32_pmuldq256:
|
||||
case clang::X86::BI__builtin_ia32_pmuldq512:
|
||||
case clang::X86::BI__builtin_ia32_pmuludq128:
|
||||
case clang::X86::BI__builtin_ia32_pmuludq256:
|
||||
return interp__builtin_ia32_pmul(S, OpPC, Call, BuiltinID);
|
||||
|
||||
default:
|
||||
S.FFDiag(S.Current->getLocation(OpPC),
|
||||
diag::note_invalid_subexpr_in_const_expr)
|
||||
|
@ -26,33 +26,33 @@ InterpStack::~InterpStack() {
|
||||
std::free(Chunk);
|
||||
Chunk = nullptr;
|
||||
StackSize = 0;
|
||||
#ifndef NDEBUG
|
||||
ItemTypes.clear();
|
||||
#endif
|
||||
}
|
||||
|
||||
// We keep the last chunk around to reuse.
|
||||
void InterpStack::clear() {
|
||||
if (!Chunk)
|
||||
return;
|
||||
|
||||
if (Chunk->Next)
|
||||
std::free(Chunk->Next);
|
||||
|
||||
assert(Chunk);
|
||||
StackSize = 0;
|
||||
#ifndef NDEBUG
|
||||
ItemTypes.clear();
|
||||
#endif
|
||||
for (PrimType Item : llvm::reverse(ItemTypes)) {
|
||||
TYPE_SWITCH(Item, { this->discard<T>(); });
|
||||
}
|
||||
assert(ItemTypes.empty());
|
||||
assert(empty());
|
||||
}
|
||||
|
||||
void InterpStack::clearTo(size_t NewSize) {
|
||||
assert(NewSize <= size());
|
||||
size_t ToShrink = size() - NewSize;
|
||||
if (ToShrink == 0)
|
||||
if (NewSize == 0)
|
||||
return clear();
|
||||
if (NewSize == size())
|
||||
return;
|
||||
|
||||
shrink(ToShrink);
|
||||
assert(NewSize <= size());
|
||||
for (PrimType Item : llvm::reverse(ItemTypes)) {
|
||||
TYPE_SWITCH(Item, { this->discard<T>(); });
|
||||
|
||||
if (size() == NewSize)
|
||||
break;
|
||||
}
|
||||
|
||||
// Note: discard() above already removed the types from ItemTypes.
|
||||
assert(size() == NewSize);
|
||||
}
|
||||
|
||||
@ -105,25 +105,9 @@ void InterpStack::shrink(size_t Size) {
|
||||
|
||||
Chunk->End -= Size;
|
||||
StackSize -= Size;
|
||||
|
||||
#ifndef NDEBUG
|
||||
size_t TypesSize = 0;
|
||||
for (PrimType T : ItemTypes)
|
||||
TYPE_SWITCH(T, { TypesSize += aligned_size<T>(); });
|
||||
|
||||
size_t StackSize = size();
|
||||
while (TypesSize > StackSize) {
|
||||
TYPE_SWITCH(ItemTypes.back(), {
|
||||
TypesSize -= aligned_size<T>();
|
||||
ItemTypes.pop_back();
|
||||
});
|
||||
}
|
||||
assert(TypesSize == StackSize);
|
||||
#endif
|
||||
}
|
||||
|
||||
void InterpStack::dump() const {
|
||||
#ifndef NDEBUG
|
||||
llvm::errs() << "Items: " << ItemTypes.size() << ". Size: " << size() << '\n';
|
||||
if (ItemTypes.empty())
|
||||
return;
|
||||
@ -133,11 +117,11 @@ void InterpStack::dump() const {
|
||||
|
||||
// The type of the item on the top of the stack is inserted to the back
|
||||
// of the vector, so the iteration has to happen backwards.
|
||||
for (auto TyIt = ItemTypes.rbegin(); TyIt != ItemTypes.rend(); ++TyIt) {
|
||||
Offset += align(primSize(*TyIt));
|
||||
for (PrimType Item : llvm::reverse(ItemTypes)) {
|
||||
Offset += align(primSize(Item));
|
||||
|
||||
llvm::errs() << Index << '/' << Offset << ": ";
|
||||
TYPE_SWITCH(*TyIt, {
|
||||
TYPE_SWITCH(Item, {
|
||||
const T &V = peek<T>(Offset);
|
||||
llvm::errs() << V;
|
||||
});
|
||||
@ -145,5 +129,4 @@ void InterpStack::dump() const {
|
||||
|
||||
++Index;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
@ -17,7 +17,6 @@
|
||||
#include "IntegralAP.h"
|
||||
#include "MemberPointer.h"
|
||||
#include "PrimType.h"
|
||||
#include <vector>
|
||||
|
||||
namespace clang {
|
||||
namespace interp {
|
||||
@ -33,18 +32,14 @@ public:
|
||||
/// Constructs a value in place on the top of the stack.
|
||||
template <typename T, typename... Tys> void push(Tys &&...Args) {
|
||||
new (grow(aligned_size<T>())) T(std::forward<Tys>(Args)...);
|
||||
#ifndef NDEBUG
|
||||
ItemTypes.push_back(toPrimType<T>());
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Returns the value from the top of the stack and removes it.
|
||||
template <typename T> T pop() {
|
||||
#ifndef NDEBUG
|
||||
assert(!ItemTypes.empty());
|
||||
assert(ItemTypes.back() == toPrimType<T>());
|
||||
ItemTypes.pop_back();
|
||||
#endif
|
||||
T *Ptr = &peekInternal<T>();
|
||||
T Value = std::move(*Ptr);
|
||||
shrink(aligned_size<T>());
|
||||
@ -53,22 +48,20 @@ public:
|
||||
|
||||
/// Discards the top value from the stack.
|
||||
template <typename T> void discard() {
|
||||
#ifndef NDEBUG
|
||||
assert(!ItemTypes.empty());
|
||||
assert(ItemTypes.back() == toPrimType<T>());
|
||||
ItemTypes.pop_back();
|
||||
#endif
|
||||
T *Ptr = &peekInternal<T>();
|
||||
Ptr->~T();
|
||||
if constexpr (!std::is_trivially_destructible_v<T>) {
|
||||
Ptr->~T();
|
||||
}
|
||||
shrink(aligned_size<T>());
|
||||
}
|
||||
|
||||
/// Returns a reference to the value on the top of the stack.
|
||||
template <typename T> T &peek() const {
|
||||
#ifndef NDEBUG
|
||||
assert(!ItemTypes.empty());
|
||||
assert(ItemTypes.back() == toPrimType<T>());
|
||||
#endif
|
||||
return peekInternal<T>();
|
||||
}
|
||||
|
||||
@ -83,7 +76,7 @@ public:
|
||||
/// Returns the size of the stack in bytes.
|
||||
size_t size() const { return StackSize; }
|
||||
|
||||
/// Clears the stack without calling any destructors.
|
||||
/// Clears the stack.
|
||||
void clear();
|
||||
void clearTo(size_t NewSize);
|
||||
|
||||
@ -146,9 +139,11 @@ private:
|
||||
/// Total size of the stack.
|
||||
size_t StackSize = 0;
|
||||
|
||||
#ifndef NDEBUG
|
||||
/// vector recording the type of data we pushed into the stack.
|
||||
std::vector<PrimType> ItemTypes;
|
||||
/// SmallVector recording the type of data we pushed into the stack.
|
||||
/// We don't usually need this during normal code interpretation but
|
||||
/// when aborting, we need type information to call the destructors
|
||||
/// for what's left on the stack.
|
||||
llvm::SmallVector<PrimType> ItemTypes;
|
||||
|
||||
template <typename T> static constexpr PrimType toPrimType() {
|
||||
if constexpr (std::is_same_v<T, Pointer>)
|
||||
@ -192,7 +187,6 @@ private:
|
||||
|
||||
llvm_unreachable("unknown type push()'ed into InterpStack");
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace interp
|
||||
|
@ -45,6 +45,12 @@ InterpState::~InterpState() {
|
||||
|
||||
while (DeadBlocks) {
|
||||
DeadBlock *Next = DeadBlocks->Next;
|
||||
|
||||
// There might be a pointer in a global structure pointing to the dead
|
||||
// block.
|
||||
for (Pointer *P = DeadBlocks->B.Pointers; P; P = P->asBlockPointer().Next)
|
||||
DeadBlocks->B.removePointer(P);
|
||||
|
||||
std::free(DeadBlocks);
|
||||
DeadBlocks = Next;
|
||||
}
|
||||
@ -53,12 +59,6 @@ InterpState::~InterpState() {
|
||||
void InterpState::cleanup() {
|
||||
// As a last resort, make sure all pointers still pointing to a dead block
|
||||
// don't point to it anymore.
|
||||
for (DeadBlock *DB = DeadBlocks; DB; DB = DB->Next) {
|
||||
for (Pointer *P = DB->B.Pointers; P; P = P->asBlockPointer().Next) {
|
||||
P->PointeeStorage.BS.Pointee = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
Alloc.cleanup();
|
||||
}
|
||||
|
||||
|
@ -179,10 +179,7 @@ APValue Pointer::toAPValue(const ASTContext &ASTCtx) const {
|
||||
else if (const auto *E = Desc->asExpr()) {
|
||||
if (block()->isDynamic()) {
|
||||
QualType AllocatedType = getDeclPtr().getFieldDesc()->getDataType(ASTCtx);
|
||||
// FIXME: Suboptimal counting of dynamic allocations. Move this to Context
|
||||
// or InterpState?
|
||||
static int ReportedDynamicAllocs = 0;
|
||||
DynamicAllocLValue DA(ReportedDynamicAllocs++);
|
||||
DynamicAllocLValue DA(*block()->DynAllocId);
|
||||
Base = APValue::LValueBase::getDynamicAlloc(DA, AllocatedType);
|
||||
} else {
|
||||
Base = E;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user