diff --git a/.ci/generate-buildkite-pipeline-premerge b/.ci/generate-buildkite-pipeline-premerge index 98a8b8fff3687..7676ff716c418 100755 --- a/.ci/generate-buildkite-pipeline-premerge +++ b/.ci/generate-buildkite-pipeline-premerge @@ -191,6 +191,9 @@ function keep-modified-projects() { } function check-targets() { + # Do not use "check-all" here because if there is "check-all" plus a + # project specific target like "check-clang", that project's tests + # will be run twice. projects=${@} for project in ${projects}; do case ${project} in @@ -198,7 +201,7 @@ function check-targets() { echo "check-clang-tools" ;; compiler-rt) - echo "check-all" + echo "check-compiler-rt" ;; cross-project-tests) echo "check-cross-project" @@ -216,10 +219,10 @@ function check-targets() { echo "check-lldb" ;; pstl) - echo "check-all" + # Currently we do not run pstl tests in CI. ;; libclc) - echo "check-all" + # Currently there is no testing for libclc. ;; *) echo "check-${project}" diff --git a/.github/new-prs-labeler.yml b/.github/new-prs-labeler.yml index 8cc6c36fa945b..cef4782331510 100644 --- a/.github/new-prs-labeler.yml +++ b/.github/new-prs-labeler.yml @@ -69,7 +69,7 @@ PGO: - llvm/**/llvm-profdata/**/* - llvm/**/llvm-profgen/**/* -vectorization: +vectorizers: - llvm/lib/Transforms/Vectorize/**/* - llvm/include/llvm/Transforms/Vectorize/**/* diff --git a/.github/workflows/ericwf-contracts-nightly.yml b/.github/workflows/ericwf-contracts-nightly.yml new file mode 100644 index 0000000000000..59da7c904a046 --- /dev/null +++ b/.github/workflows/ericwf-contracts-nightly.yml @@ -0,0 +1,65 @@ +name: EricWF Contracts Nightly Release flow + +permissions: + contents: write + actions: read + +on: + workflow_dispatch: + push: + branches: + - 'eric-contracts' + - "eric-test-contracts" + + +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: + check_clang: + name: "Do it" + if: github.repository_owner == 'EricWF' + runs-on: aah + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ github.ref }} + fetch-depth: 0 + - name: "Setup" + run: | + mkdir build + mkdir install + + - name: "Configure" + run: > + cmake -B build -S ./llvm/ + -G Ninja + -DCMAKE_BUILD_TYPE=RELWITHDEBINFO + -DLLVM_ENABLE_PROJECTS="clang" + "-DLLVM_ENABLE_RUNTIMES=libcxx;libcxxabi;libunwind" + -DLLVM_ENABLE_ASSERTIONS=ON + -DLLVM_USE_LINKER=mold + -DCMAKE_INSTALL_PREFIX=./install + -DCMAKE_C_COMPILER=clang + -DCMAKE_CXX_COMPILER=clang++ + '-DLLVM_USE_SANITIZER=Address;Undefined' + - name: Build Clang & Libc++ + run: | + cd build + ninja clang + ninja cxx + - name: Test Clang + run: | + cd build + ninja check-clang + - name: Check Libc++ + run: | + cd build + ninja check-cxx + - name: Install + run: | + cd build + ninja install \ No newline at end of file diff --git a/.github/workflows/release-binaries.yml b/.github/workflows/release-binaries.yml index f24e25879b96b..1cde628d3f66c 100644 --- a/.github/workflows/release-binaries.yml +++ b/.github/workflows/release-binaries.yml @@ -328,7 +328,7 @@ jobs: run: | # Build some of the mlir tools that take a long time to link if [ "${{ needs.prepare.outputs.build-flang }}" = "true" ]; then - ninja -C ${{ steps.setup-stage.outputs.build-prefix }}/build/tools/clang/stage2-bins/ -j2 flang-new bbc + ninja -C ${{ steps.setup-stage.outputs.build-prefix }}/build/tools/clang/stage2-bins/ -j2 flang bbc fi ninja -C ${{ steps.setup-stage.outputs.build-prefix }}/build/tools/clang/stage2-bins/ \ mlir-bytecode-parser-fuzzer \ diff --git a/CONTRACTS_TODO.txt b/CONTRACTS_TODO.txt new file mode 100644 index 0000000000000..4a97987400b8d --- /dev/null +++ b/CONTRACTS_TODO.txt @@ -0,0 +1,15 @@ +* Parse result name in post contracts + * auto declared functions. + * templates. + * Late parsing in general +* Diagnose +* Test parsing failures +* What happens when pre/post throws from a noexcept function? Can we even callee-emit contracts? +* Start constification +* Check 'this' access in pre/post +* Codegen: + * Make it work + +* Required headers? + + diff --git a/bolt/include/bolt/Passes/ContinuityStats.h b/bolt/include/bolt/Passes/ContinuityStats.h new file mode 100644 index 0000000000000..bd4d491ad4a55 --- /dev/null +++ b/bolt/include/bolt/Passes/ContinuityStats.h @@ -0,0 +1,61 @@ +//===- bolt/Passes/ContinuityStats.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 pass checks how well the BOLT input profile satisfies the following +// "CFG continuity" property of a perfect profile: +// +// Each positive-execution-count block in the function’s CFG +// should be *reachable* from a positive-execution-count function +// entry block through a positive-execution-count path. +// +// More specifically, for each of the hottest 1000 functions, the pass +// calculates the function’s fraction of basic block execution counts +// that is *unreachable*. It then reports the 95th percentile of the +// distribution of the 1000 unreachable fractions in a single BOLT-INFO line. +// The smaller the reported value is, the better the BOLT profile +// satisfies the CFG continuity property. + +// The default value of 1000 above can be changed via the hidden BOLT option +// `-num-functions-for-continuity-check=[N]`. +// If more detailed stats are needed, `-v=1` can be used: the hottest N +// functions will be grouped into 5 equally-sized buckets, from the hottest +// to the coldest; for each bucket, various summary statistics of the +// distribution of the unreachable fractions and the raw unreachable execution +// counts will be reported. +// +//===----------------------------------------------------------------------===// + +#ifndef BOLT_PASSES_CONTINUITYSTATS_H +#define BOLT_PASSES_CONTINUITYSTATS_H + +#include "bolt/Passes/BinaryPasses.h" +#include + +namespace llvm { + +class raw_ostream; + +namespace bolt { +class BinaryContext; + +/// Compute and report to the user the function CFG continuity quality +class PrintContinuityStats : public BinaryFunctionPass { +public: + explicit PrintContinuityStats(const cl::opt &PrintPass) + : BinaryFunctionPass(PrintPass) {} + + bool shouldOptimize(const BinaryFunction &BF) const override; + const char *getName() const override { return "continuity-stats"; } + bool shouldPrint(const BinaryFunction &) const override { return false; } + Error runOnFunctions(BinaryContext &BC) override; +}; + +} // namespace bolt +} // namespace llvm + +#endif // BOLT_PASSES_CONTINUITYSTATS_H diff --git a/bolt/lib/Passes/ADRRelaxationPass.cpp b/bolt/lib/Passes/ADRRelaxationPass.cpp index 256034a841c70..52811edcb8273 100644 --- a/bolt/lib/Passes/ADRRelaxationPass.cpp +++ b/bolt/lib/Passes/ADRRelaxationPass.cpp @@ -56,13 +56,14 @@ void ADRRelaxationPass::runOnFunction(BinaryFunction &BF) { continue; } - // Don't relax adr if it points to the same function and it is not split - // and BF initial size is < 1MB. + // Don't relax ADR if it points to the same function and is in the main + // fragment and BF initial size is < 1MB. const unsigned OneMB = 0x100000; if (BF.getSize() < OneMB) { BinaryFunction *TargetBF = BC.getFunctionForSymbol(Symbol); - if (TargetBF == &BF && !BF.isSplit()) + if (TargetBF == &BF && !BB.isSplit()) continue; + // No relaxation needed if ADR references a basic block in the same // fragment. if (BinaryBasicBlock *TargetBB = BF.getBasicBlockForLabel(Symbol)) diff --git a/bolt/lib/Passes/CMakeLists.txt b/bolt/lib/Passes/CMakeLists.txt index 407d8b03f7397..1c1273b3d2420 100644 --- a/bolt/lib/Passes/CMakeLists.txt +++ b/bolt/lib/Passes/CMakeLists.txt @@ -26,6 +26,7 @@ add_llvm_library(LLVMBOLTPasses PatchEntries.cpp PettisAndHansen.cpp PLTCall.cpp + ContinuityStats.cpp RegAnalysis.cpp RegReAssign.cpp ReorderAlgorithm.cpp diff --git a/bolt/lib/Passes/ContinuityStats.cpp b/bolt/lib/Passes/ContinuityStats.cpp new file mode 100644 index 0000000000000..b32365b59065d --- /dev/null +++ b/bolt/lib/Passes/ContinuityStats.cpp @@ -0,0 +1,250 @@ +//===- bolt/Passes/ContinuityStats.cpp --------------------------*- 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 implements the continuity stats calculation pass. +// +//===----------------------------------------------------------------------===// + +#include "bolt/Passes/ContinuityStats.h" +#include "bolt/Core/BinaryBasicBlock.h" +#include "bolt/Core/BinaryFunction.h" +#include "bolt/Utils/CommandLineOpts.h" +#include "llvm/Support/CommandLine.h" +#include +#include +#include + +#define DEBUG_TYPE "bolt-opts" + +using namespace llvm; +using namespace bolt; + +namespace opts { +extern cl::opt Verbosity; +cl::opt NumFunctionsForContinuityCheck( + "num-functions-for-continuity-check", + cl::desc("number of hottest functions to print aggregated " + "CFG discontinuity stats of."), + cl::init(1000), cl::ZeroOrMore, cl::Hidden, cl::cat(BoltOptCategory)); +} // namespace opts + +namespace { +using FunctionListType = std::vector; +using function_iterator = FunctionListType::iterator; + +template +void printDistribution(raw_ostream &OS, std::vector &values, + bool Fraction = false) { + if (values.empty()) + return; + // Sort values from largest to smallest and print the MAX, TOP 1%, 5%, 10%, + // 20%, 50%, 80%, MIN. If Fraction is true, then values are printed as + // fractions instead of integers. + std::sort(values.begin(), values.end()); + + auto printLine = [&](std::string Text, double Percent) { + int Rank = int(values.size() * (1.0 - Percent / 100)); + if (Percent == 0) + Rank = values.size() - 1; + if (Fraction) + OS << " " << Text << std::string(9 - Text.length(), ' ') << ": " + << format("%.2lf%%", values[Rank] * 100) << "\n"; + else + OS << " " << Text << std::string(9 - Text.length(), ' ') << ": " + << values[Rank] << "\n"; + }; + + printLine("MAX", 0); + const int percentages[] = {1, 5, 10, 20, 50, 80}; + for (size_t i = 0; i < sizeof(percentages) / sizeof(percentages[0]); ++i) { + printLine("TOP " + std::to_string(percentages[i]) + "%", percentages[i]); + } + printLine("MIN", 100); +} + +void printCFGContinuityStats(raw_ostream &OS, + iterator_range &Functions) { + // Given a perfect profile, every positive-execution-count BB should be + // connected to an entry of the function through a positive-execution-count + // directed path in the control flow graph. + std::vector NumUnreachables; + std::vector SumECUnreachables; + std::vector FractionECUnreachables; + + for (auto it = Functions.begin(); it != Functions.end(); ++it) { + const BinaryFunction *Function = *it; + if (Function->size() <= 1) + continue; + + // Compute the sum of all BB execution counts (ECs). + size_t NumPosECBBs = 0; + size_t SumAllBBEC = 0; + for (const BinaryBasicBlock &BB : *Function) { + const size_t BBEC = BB.getKnownExecutionCount(); + NumPosECBBs += BBEC > 0 ? 1 : 0; + SumAllBBEC += BBEC; + } + + // Perform BFS on subgraph of CFG induced by positive weight edges. + // Compute the number of BBs reachable from the entry(s) of the function and + // the sum of their execution counts (ECs). + std::unordered_map IndexToBB; + std::unordered_set Visited; + std::queue Queue; + for (const BinaryBasicBlock &BB : *Function) { + // Make sure BB.getIndex() is not already in IndexToBB. + assert(IndexToBB.find(BB.getIndex()) == IndexToBB.end()); + IndexToBB[BB.getIndex()] = &BB; + if (BB.isEntryPoint() && BB.getKnownExecutionCount() > 0) { + Queue.push(BB.getIndex()); + Visited.insert(BB.getIndex()); + } + } + while (!Queue.empty()) { + const unsigned BBIndex = Queue.front(); + const BinaryBasicBlock *BB = IndexToBB[BBIndex]; + Queue.pop(); + auto SuccBIIter = BB->branch_info_begin(); + for (const BinaryBasicBlock *Succ : BB->successors()) { + const uint64_t Count = SuccBIIter->Count; + if (Count == BinaryBasicBlock::COUNT_NO_PROFILE || Count == 0) { + ++SuccBIIter; + continue; + } + if (!Visited.insert(Succ->getIndex()).second) { + ++SuccBIIter; + continue; + } + Queue.push(Succ->getIndex()); + ++SuccBIIter; + } + } + + const size_t NumReachableBBs = Visited.size(); + + // Loop through Visited, and sum the corresponding BBs' execution counts + // (ECs). + size_t SumReachableBBEC = 0; + for (const unsigned BBIndex : Visited) { + const BinaryBasicBlock *BB = IndexToBB[BBIndex]; + SumReachableBBEC += BB->getKnownExecutionCount(); + } + + const size_t NumPosECBBsUnreachableFromEntry = + NumPosECBBs - NumReachableBBs; + const size_t SumUnreachableBBEC = SumAllBBEC - SumReachableBBEC; + const double FractionECUnreachable = + (double)SumUnreachableBBEC / SumAllBBEC; + + if (opts::Verbosity >= 2 && FractionECUnreachable >= 0.05) { + OS << "Non-trivial CFG discontinuity observed in function " + << Function->getPrintName() << "\n"; + LLVM_DEBUG(Function->dump()); + } + + NumUnreachables.push_back(NumPosECBBsUnreachableFromEntry); + SumECUnreachables.push_back(SumUnreachableBBEC); + FractionECUnreachables.push_back(FractionECUnreachable); + } + + if (FractionECUnreachables.empty()) + return; + + std::sort(FractionECUnreachables.begin(), FractionECUnreachables.end()); + const int Rank = int(FractionECUnreachables.size() * 0.95); + OS << format("top 5%% function CFG discontinuity is %.2lf%%\n", + FractionECUnreachables[Rank] * 100); + + if (opts::Verbosity >= 1) { + OS << "abbreviations: EC = execution count, POS BBs = positive EC BBs\n" + << "distribution of NUM(unreachable POS BBs) among all focal " + "functions\n"; + printDistribution(OS, NumUnreachables); + + OS << "distribution of SUM_EC(unreachable POS BBs) among all focal " + "functions\n"; + printDistribution(OS, SumECUnreachables); + + OS << "distribution of [(SUM_EC(unreachable POS BBs) / SUM_EC(all " + "POS BBs))] among all focal functions\n"; + printDistribution(OS, FractionECUnreachables, /*Fraction=*/true); + } +} + +void printAll(BinaryContext &BC, FunctionListType &ValidFunctions, + size_t NumTopFunctions) { + // Sort the list of functions by execution counts (reverse). + llvm::sort(ValidFunctions, + [&](const BinaryFunction *A, const BinaryFunction *B) { + return A->getKnownExecutionCount() > B->getKnownExecutionCount(); + }); + + const size_t RealNumTopFunctions = + std::min(NumTopFunctions, ValidFunctions.size()); + + iterator_range Functions( + ValidFunctions.begin(), ValidFunctions.begin() + RealNumTopFunctions); + + BC.outs() << format("BOLT-INFO: among the hottest %zu functions ", + RealNumTopFunctions); + printCFGContinuityStats(BC.outs(), Functions); + + // Print more detailed bucketed stats if requested. + if (opts::Verbosity >= 1 && RealNumTopFunctions >= 5) { + const size_t PerBucketSize = RealNumTopFunctions / 5; + BC.outs() << format( + "Detailed stats for 5 buckets, each with %zu functions:\n", + PerBucketSize); + + // For each bucket, print the CFG continuity stats of the functions in the + // bucket. + for (size_t BucketIndex = 0; BucketIndex < 5; ++BucketIndex) { + const size_t StartIndex = BucketIndex * PerBucketSize; + const size_t EndIndex = StartIndex + PerBucketSize; + iterator_range Functions( + ValidFunctions.begin() + StartIndex, + ValidFunctions.begin() + EndIndex); + const size_t MaxFunctionExecutionCount = + ValidFunctions[StartIndex]->getKnownExecutionCount(); + const size_t MinFunctionExecutionCount = + ValidFunctions[EndIndex - 1]->getKnownExecutionCount(); + BC.outs() << format("----------------\n| Bucket %zu: " + "|\n----------------\n", + BucketIndex + 1) + << format( + "execution counts of the %zu functions in the bucket: " + "%zu-%zu\n", + EndIndex - StartIndex, MinFunctionExecutionCount, + MaxFunctionExecutionCount); + printCFGContinuityStats(BC.outs(), Functions); + } + } +} +} // namespace + +bool PrintContinuityStats::shouldOptimize(const BinaryFunction &BF) const { + if (BF.empty() || !BF.hasValidProfile()) + return false; + + return BinaryFunctionPass::shouldOptimize(BF); +} + +Error PrintContinuityStats::runOnFunctions(BinaryContext &BC) { + // Create a list of functions with valid profiles. + FunctionListType ValidFunctions; + for (const auto &BFI : BC.getBinaryFunctions()) { + const BinaryFunction *Function = &BFI.second; + if (PrintContinuityStats::shouldOptimize(*Function)) + ValidFunctions.push_back(Function); + } + if (ValidFunctions.empty() || opts::NumFunctionsForContinuityCheck == 0) + return Error::success(); + + printAll(BC, ValidFunctions, opts::NumFunctionsForContinuityCheck); + return Error::success(); +} diff --git a/bolt/lib/Profile/YAMLProfileReader.cpp b/bolt/lib/Profile/YAMLProfileReader.cpp index 67ed32017667d..fe0fcfdcd42f9 100644 --- a/bolt/lib/Profile/YAMLProfileReader.cpp +++ b/bolt/lib/Profile/YAMLProfileReader.cpp @@ -643,11 +643,7 @@ size_t YAMLProfileReader::matchWithNameSimilarity(BinaryContext &BC) { // equal number of blocks. if (NamespaceToProfiledBFSizesIt->second.count(BF->size()) == 0) continue; - auto NamespaceToBFsIt = NamespaceToBFs.find(Namespace); - if (NamespaceToBFsIt == NamespaceToBFs.end()) - NamespaceToBFs[Namespace] = {BF}; - else - NamespaceToBFsIt->second.push_back(BF); + NamespaceToBFs[Namespace].push_back(BF); } // Iterates through all profiled functions and binary functions belonging to diff --git a/bolt/lib/Rewrite/BinaryPassManager.cpp b/bolt/lib/Rewrite/BinaryPassManager.cpp index 5dfef0b71cc79..b090604183348 100644 --- a/bolt/lib/Rewrite/BinaryPassManager.cpp +++ b/bolt/lib/Rewrite/BinaryPassManager.cpp @@ -12,6 +12,7 @@ #include "bolt/Passes/AllocCombiner.h" #include "bolt/Passes/AsmDump.h" #include "bolt/Passes/CMOVConversion.h" +#include "bolt/Passes/ContinuityStats.h" #include "bolt/Passes/FixRISCVCallsPass.h" #include "bolt/Passes/FixRelaxationPass.h" #include "bolt/Passes/FrameOptimizer.h" @@ -373,6 +374,8 @@ Error BinaryFunctionPassManager::runAllPasses(BinaryContext &BC) { if (opts::PrintProfileStats) Manager.registerPass(std::make_unique(NeverPrint)); + Manager.registerPass(std::make_unique(NeverPrint)); + Manager.registerPass(std::make_unique(NeverPrint)); Manager.registerPass(std::make_unique(NeverPrint)); diff --git a/bolt/test/AArch64/adr-relaxation.s b/bolt/test/AArch64/adr-relaxation.s new file mode 100644 index 0000000000000..0aa3c71f29aaa --- /dev/null +++ b/bolt/test/AArch64/adr-relaxation.s @@ -0,0 +1,49 @@ +## Check that llvm-bolt will not unnecessarily relax ADR instruction. + +# RUN: llvm-mc -filetype=obj -triple aarch64-unknown-unknown %s -o %t.o +# RUN: %clang %cflags %t.o -o %t.exe -Wl,-q -static +# RUN: llvm-bolt %t.exe -o %t.bolt --split-functions --split-strategy=random2 +# RUN: llvm-objdump -d --disassemble-symbols=_start %t.bolt | FileCheck %s +# RUN: llvm-objdump -d --disassemble-symbols=foo.cold.0 %t.bolt \ +# RUN: | FileCheck --check-prefix=CHECK-FOO %s + +## ADR below references its containing function that is split. But ADR is always +## in the main fragment, thus there is no need to relax it. + .text + .globl _start + .type _start, %function +_start: +# CHECK: <_start>: + .cfi_startproc + adr x1, _start +# CHECK-NOT: adrp +# CHECK: adr + cmp x1, x11 + b.hi .L1 + mov x0, #0x0 +.L1: + ret x30 + .cfi_endproc +.size _start, .-_start + + +## In foo, ADR is in the split fragment but references the main one. Thus, it +## needs to be relaxed into ADRP + ADD. + .globl foo + .type foo, %function +foo: + .cfi_startproc + cmp x1, x11 + b.hi .L2 + mov x0, #0x0 +.L2: +# CHECK-FOO: : + adr x1, foo +# CHECK-FOO: adrp +# CHECK-FOO-NEXT: add + ret x30 + .cfi_endproc +.size foo, .-foo + +## Force relocation mode. + .reloc 0, R_AARCH64_NONE diff --git a/bolt/test/X86/cfg-discontinuity-reporting.test b/bolt/test/X86/cfg-discontinuity-reporting.test new file mode 100644 index 0000000000000..4d7d3305cdb75 --- /dev/null +++ b/bolt/test/X86/cfg-discontinuity-reporting.test @@ -0,0 +1,4 @@ +## Check profile discontinuity reporting +RUN: yaml2obj %p/Inputs/blarge_new.yaml &> %t.exe +RUN: llvm-bolt %t.exe -o %t.out --pa -p %p/Inputs/blarge_new.preagg.txt | FileCheck %s +CHECK: among the hottest 5 functions top 5% function CFG discontinuity is 100.00% diff --git a/change_default_route_ip.py b/change_default_route_ip.py new file mode 100644 index 0000000000000..ca110e95e283f --- /dev/null +++ b/change_default_route_ip.py @@ -0,0 +1,51 @@ +import subprocess +from pathlib import Path +from rich import print + +def run_command(command): + result = subprocess.run(command, capture_output=True, text=True, shell=True) + return result.stdout.strip() + +# Find the device used for the default route +default_route_device = run_command("ip route show default | awk '{print $5}'") + +if not default_route_device: + print("[bold red]Error:[/bold red] No default route device found.") + exit(1) + +print(f"[bold green]Default route device:[/bold green] {default_route_device}") + +# Get the IP address returned by hostname -i +new_ip = run_command("hostname -i") + +if not new_ip: + print("[bold red]Error:[/bold red] Unable to get IP address from hostname -i.") + exit(1) + +print(f"[bold green]New IP address:[/bold green] {new_ip}") + +# Change the IP address of the identified device +current_ip = run_command(f"ip addr show dev {default_route_device} | awk '/inet / {{print $2}}'") + +if not current_ip: + print(f"[bold red]Error:[/bold red] Unable to get current IP address for {default_route_device}") + exit(1) + +print(f"[bold green]Current IP address:[/bold green] {current_ip}") + +# Remove the current IP address +remove_ip_cmd = f"sudo ip addr del {current_ip} dev {default_route_device}" +run_command(remove_ip_cmd) + +# Add the new IP address +add_ip_cmd = f"sudo ip addr add {new_ip} dev {default_route_device}" +result = run_command(add_ip_cmd) + +if result: + print(f"[bold red]Error changing IP address:[/bold red] {result}") +else: + print(f"[bold green]Successfully changed IP address of {default_route_device} to {new_ip}[/bold green]") + +# Verify the change +new_current_ip = run_command(f"ip addr show dev {default_route_device} | awk '/inet / {{print $2}}'") +print(f"[bold green]New current IP address:[/bold green] {new_current_ip}") \ No newline at end of file diff --git a/change_network_config.yml b/change_network_config.yml new file mode 100644 index 0000000000000..5bc2ffe38a21b --- /dev/null +++ b/change_network_config.yml @@ -0,0 +1,32 @@ +--- +- name: Change network configuration to static IP + hosts: localhost + become: yes + gather_facts: yes + + tasks: + - name: Get default network interface + set_fact: + default_interface: "{{ ansible_default_ipv4.interface }}" + + - name: Get IP address from hostname -i + shell: hostname -i + register: hostname_ip + changed_when: false + + - name: Update network configuration + template: + src: interfaces.j2 + dest: /etc/network/interfaces + vars: + interface: "{{ default_interface }}" + ip_address: "{{ hostname_ip.stdout }}" + + - name: Restart networking service + service: + name: networking + state: restarted + + - name: Display network configuration change + debug: + msg: "Network configuration updated. Interface {{ default_interface }} now has static IP {{ hostname_ip.stdout }}" \ No newline at end of file diff --git a/clang-tools-extra/clang-apply-replacements/CMakeLists.txt b/clang-tools-extra/clang-apply-replacements/CMakeLists.txt index 93198ccbfc406..551ded903e88a 100644 --- a/clang-tools-extra/clang-apply-replacements/CMakeLists.txt +++ b/clang-tools-extra/clang-apply-replacements/CMakeLists.txt @@ -2,7 +2,7 @@ set(LLVM_LINK_COMPONENTS Support ) -add_clang_library(clangApplyReplacements +add_clang_library(clangApplyReplacements STATIC lib/Tooling/ApplyReplacements.cpp DEPENDS diff --git a/clang-tools-extra/clang-apply-replacements/lib/Tooling/ApplyReplacements.cpp b/clang-tools-extra/clang-apply-replacements/lib/Tooling/ApplyReplacements.cpp index 9e0da82dfd380..b895075e4f31c 100644 --- a/clang-tools-extra/clang-apply-replacements/lib/Tooling/ApplyReplacements.cpp +++ b/clang-tools-extra/clang-apply-replacements/lib/Tooling/ApplyReplacements.cpp @@ -148,11 +148,8 @@ groupReplacements(const TUReplacements &TUs, const TUDiagnostics &TUDs, if (auto Entry = SM.getFileManager().getOptionalFileRef(Path)) { if (SourceTU) { - auto &Replaces = DiagReplacements[*Entry]; - auto It = Replaces.find(R); - if (It == Replaces.end()) - Replaces.emplace(R, SourceTU); - else if (It->second != SourceTU) + auto [It, Inserted] = DiagReplacements[*Entry].try_emplace(R, SourceTU); + if (!Inserted && It->second != SourceTU) // This replacement is a duplicate of one suggested by another TU. return; } diff --git a/clang-tools-extra/clang-change-namespace/CMakeLists.txt b/clang-tools-extra/clang-change-namespace/CMakeLists.txt index ded91edb8e34f..62289ad031cfd 100644 --- a/clang-tools-extra/clang-change-namespace/CMakeLists.txt +++ b/clang-tools-extra/clang-change-namespace/CMakeLists.txt @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS Support ) -add_clang_library(clangChangeNamespace +add_clang_library(clangChangeNamespace STATIC ChangeNamespace.cpp DEPENDS diff --git a/clang-tools-extra/clang-change-namespace/ChangeNamespace.cpp b/clang-tools-extra/clang-change-namespace/ChangeNamespace.cpp index 879c0d26d472a..850df7daf5c03 100644 --- a/clang-tools-extra/clang-change-namespace/ChangeNamespace.cpp +++ b/clang-tools-extra/clang-change-namespace/ChangeNamespace.cpp @@ -606,9 +606,8 @@ void ChangeNamespaceTool::run( Result.Nodes.getNodeAs("func_ref")) { // If this reference has been processed as a function call, we do not // process it again. - if (ProcessedFuncRefs.count(FuncRef)) + if (!ProcessedFuncRefs.insert(FuncRef).second) return; - ProcessedFuncRefs.insert(FuncRef); const auto *Func = Result.Nodes.getNodeAs("func_decl"); assert(Func); const auto *Context = Result.Nodes.getNodeAs("dc"); diff --git a/clang-tools-extra/clang-doc/CMakeLists.txt b/clang-tools-extra/clang-doc/CMakeLists.txt index 975ad8e242e49..520fe58cbe68e 100644 --- a/clang-tools-extra/clang-doc/CMakeLists.txt +++ b/clang-tools-extra/clang-doc/CMakeLists.txt @@ -4,7 +4,7 @@ set(LLVM_LINK_COMPONENTS FrontendOpenMP ) -add_clang_library(clangDoc +add_clang_library(clangDoc STATIC BitcodeReader.cpp BitcodeWriter.cpp ClangDoc.cpp diff --git a/clang-tools-extra/clang-include-fixer/CMakeLists.txt b/clang-tools-extra/clang-include-fixer/CMakeLists.txt index 94afdcc3c67b6..00f2f6976152c 100644 --- a/clang-tools-extra/clang-include-fixer/CMakeLists.txt +++ b/clang-tools-extra/clang-include-fixer/CMakeLists.txt @@ -2,7 +2,7 @@ set(LLVM_LINK_COMPONENTS support ) -add_clang_library(clangIncludeFixer +add_clang_library(clangIncludeFixer STATIC IncludeFixer.cpp IncludeFixerContext.cpp InMemorySymbolIndex.cpp diff --git a/clang-tools-extra/clang-include-fixer/find-all-symbols/CMakeLists.txt b/clang-tools-extra/clang-include-fixer/find-all-symbols/CMakeLists.txt index 0ed46428b0d63..ad4eccfb377ec 100644 --- a/clang-tools-extra/clang-include-fixer/find-all-symbols/CMakeLists.txt +++ b/clang-tools-extra/clang-include-fixer/find-all-symbols/CMakeLists.txt @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS FrontendOpenMP ) -add_clang_library(findAllSymbols +add_clang_library(findAllSymbols STATIC FindAllSymbols.cpp FindAllSymbolsAction.cpp FindAllMacros.cpp diff --git a/clang-tools-extra/clang-include-fixer/plugin/CMakeLists.txt b/clang-tools-extra/clang-include-fixer/plugin/CMakeLists.txt index db5d5a8324389..b193a30ad4e11 100644 --- a/clang-tools-extra/clang-include-fixer/plugin/CMakeLists.txt +++ b/clang-tools-extra/clang-include-fixer/plugin/CMakeLists.txt @@ -1,4 +1,4 @@ -add_clang_library(clangIncludeFixerPlugin +add_clang_library(clangIncludeFixerPlugin STATIC IncludeFixerPlugin.cpp LINK_LIBS diff --git a/clang-tools-extra/clang-move/CMakeLists.txt b/clang-tools-extra/clang-move/CMakeLists.txt index e8aeaedd61d4d..1380a111c5c4c 100644 --- a/clang-tools-extra/clang-move/CMakeLists.txt +++ b/clang-tools-extra/clang-move/CMakeLists.txt @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS FrontendOpenMP ) -add_clang_library(clangMove +add_clang_library(clangMove STATIC Move.cpp HelperDeclRefGraph.cpp diff --git a/clang-tools-extra/clang-query/CMakeLists.txt b/clang-tools-extra/clang-query/CMakeLists.txt index 34f018c4a03f3..b168a3a858156 100644 --- a/clang-tools-extra/clang-query/CMakeLists.txt +++ b/clang-tools-extra/clang-query/CMakeLists.txt @@ -4,7 +4,7 @@ set(LLVM_LINK_COMPONENTS FrontendOpenMP ) -add_clang_library(clangQuery +add_clang_library(clangQuery STATIC Query.cpp QueryParser.cpp diff --git a/clang-tools-extra/clang-reorder-fields/CMakeLists.txt b/clang-tools-extra/clang-reorder-fields/CMakeLists.txt index b82146a711585..2fdeb65d89767 100644 --- a/clang-tools-extra/clang-reorder-fields/CMakeLists.txt +++ b/clang-tools-extra/clang-reorder-fields/CMakeLists.txt @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS support ) -add_clang_library(clangReorderFields +add_clang_library(clangReorderFields STATIC ReorderFieldsAction.cpp DEPENDS diff --git a/clang-tools-extra/clang-tidy/CMakeLists.txt b/clang-tools-extra/clang-tidy/CMakeLists.txt index 430ea4cdbb38e..83a3236131dc9 100644 --- a/clang-tools-extra/clang-tidy/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/CMakeLists.txt @@ -8,7 +8,7 @@ configure_file( ${CMAKE_CURRENT_BINARY_DIR}/clang-tidy-config.h) include_directories(BEFORE ${CMAKE_CURRENT_BINARY_DIR}) -add_clang_library(clangTidy +add_clang_library(clangTidy STATIC ClangTidy.cpp ClangTidyCheck.cpp ClangTidyModule.cpp diff --git a/clang-tools-extra/clang-tidy/ClangTidy.cpp b/clang-tools-extra/clang-tidy/ClangTidy.cpp index 1cd7cdd10bc25..62f9d19b2a362 100644 --- a/clang-tools-extra/clang-tidy/ClangTidy.cpp +++ b/clang-tools-extra/clang-tidy/ClangTidy.cpp @@ -672,6 +672,18 @@ getAllChecksAndOptions(bool AllowEnablingAnalyzerAlphaCheckers) { Buffer.append(AnalyzerCheck); Result.Names.insert(Buffer); } + for (std::string OptionName : { +#define GET_CHECKER_OPTIONS +#define CHECKER_OPTION(TYPE, CHECKER, OPTION_NAME, DESCRIPTION, DEFAULT, \ + RELEASE, HIDDEN) \ + Twine(AnalyzerCheckNamePrefix).concat(CHECKER ":" OPTION_NAME).str(), + +#include "clang/StaticAnalyzer/Checkers/Checkers.inc" +#undef CHECKER_OPTION +#undef GET_CHECKER_OPTIONS + }) { + Result.Options.insert(OptionName); + } #endif // CLANG_TIDY_ENABLE_STATIC_ANALYZER Context.setOptionsCollector(&Result.Options); diff --git a/clang-tools-extra/clang-tidy/abseil/CMakeLists.txt b/clang-tools-extra/clang-tidy/abseil/CMakeLists.txt index 489d732abaa8d..ca7cc6782f1e6 100644 --- a/clang-tools-extra/clang-tidy/abseil/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/abseil/CMakeLists.txt @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS FrontendOpenMP ) -add_clang_library(clangTidyAbseilModule +add_clang_library(clangTidyAbseilModule STATIC AbseilTidyModule.cpp CleanupCtadCheck.cpp DurationAdditionCheck.cpp diff --git a/clang-tools-extra/clang-tidy/altera/CMakeLists.txt b/clang-tools-extra/clang-tidy/altera/CMakeLists.txt index f885993c3c9e7..5d9e7b3a023bb 100644 --- a/clang-tools-extra/clang-tidy/altera/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/altera/CMakeLists.txt @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS support ) -add_clang_library(clangTidyAlteraModule +add_clang_library(clangTidyAlteraModule STATIC AlteraTidyModule.cpp IdDependentBackwardBranchCheck.cpp KernelNameRestrictionCheck.cpp diff --git a/clang-tools-extra/clang-tidy/android/CMakeLists.txt b/clang-tools-extra/clang-tidy/android/CMakeLists.txt index c33d0daf4e25c..b461730ef1418 100644 --- a/clang-tools-extra/clang-tidy/android/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/android/CMakeLists.txt @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS FrontendOpenMP ) -add_clang_library(clangTidyAndroidModule +add_clang_library(clangTidyAndroidModule STATIC AndroidTidyModule.cpp CloexecAccept4Check.cpp CloexecAcceptCheck.cpp diff --git a/clang-tools-extra/clang-tidy/boost/CMakeLists.txt b/clang-tools-extra/clang-tidy/boost/CMakeLists.txt index fed3c3ba01c16..9032771ec2fdc 100644 --- a/clang-tools-extra/clang-tidy/boost/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/boost/CMakeLists.txt @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS FrontendOpenMP ) -add_clang_library(clangTidyBoostModule +add_clang_library(clangTidyBoostModule STATIC BoostTidyModule.cpp UseRangesCheck.cpp UseToStringCheck.cpp diff --git a/clang-tools-extra/clang-tidy/boost/UseRangesCheck.cpp b/clang-tools-extra/clang-tidy/boost/UseRangesCheck.cpp index 4022ea0cdaf5e..e45687fde6d9f 100644 --- a/clang-tools-extra/clang-tidy/boost/UseRangesCheck.cpp +++ b/clang-tools-extra/clang-tidy/boost/UseRangesCheck.cpp @@ -204,7 +204,7 @@ utils::UseRangesCheck::ReplacerMap UseRangesCheck::getReplacerMap() const { ReplacerMap Results; static const Signature SingleSig = {{0}}; static const Signature TwoSig = {{0}, {2}}; - static const auto AddFrom = + const auto AddFrom = [&Results](llvm::IntrusiveRefCntPtr Replacer, std::initializer_list Names, StringRef Prefix) { llvm::SmallString<64> Buffer; @@ -214,17 +214,17 @@ utils::UseRangesCheck::ReplacerMap UseRangesCheck::getReplacerMap() const { } }; - static const auto AddFromStd = - [](llvm::IntrusiveRefCntPtr Replacer, - std::initializer_list Names) { + const auto AddFromStd = + [&](llvm::IntrusiveRefCntPtr Replacer, + std::initializer_list Names) { AddFrom(Replacer, Names, "std"); }; - static const auto AddFromBoost = - [](llvm::IntrusiveRefCntPtr Replacer, - std::initializer_list< - std::pair>> - NamespaceAndNames) { + const auto AddFromBoost = + [&](llvm::IntrusiveRefCntPtr Replacer, + std::initializer_list< + std::pair>> + NamespaceAndNames) { for (auto [Namespace, Names] : NamespaceAndNames) AddFrom(Replacer, Names, SmallString<64>{"boost", (Namespace.empty() ? "" : "::"), diff --git a/clang-tools-extra/clang-tidy/bugprone/BitwisePointerCastCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/BitwisePointerCastCheck.cpp new file mode 100644 index 0000000000000..0992e49b7f372 --- /dev/null +++ b/clang-tools-extra/clang-tidy/bugprone/BitwisePointerCastCheck.cpp @@ -0,0 +1,46 @@ +//===--- BitwisePointerCastCheck.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 "BitwisePointerCastCheck.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +using namespace clang::ast_matchers; + +namespace clang::tidy::bugprone { + +void BitwisePointerCastCheck::registerMatchers(MatchFinder *Finder) { + if (getLangOpts().CPlusPlus20) { + auto IsPointerType = refersToType(qualType(isAnyPointer())); + Finder->addMatcher(callExpr(hasDeclaration(functionDecl(allOf( + hasName("::std::bit_cast"), + hasTemplateArgument(0, IsPointerType), + hasTemplateArgument(1, IsPointerType))))) + .bind("bit_cast"), + this); + } + + auto IsDoublePointerType = + hasType(qualType(pointsTo(qualType(isAnyPointer())))); + Finder->addMatcher(callExpr(hasArgument(0, IsDoublePointerType), + hasArgument(1, IsDoublePointerType), + hasDeclaration(functionDecl(hasName("::memcpy")))) + .bind("memcpy"), + this); +} + +void BitwisePointerCastCheck::check(const MatchFinder::MatchResult &Result) { + if (const auto *Call = Result.Nodes.getNodeAs("bit_cast")) + diag(Call->getBeginLoc(), + "do not use 'std::bit_cast' to cast between pointers") + << Call->getSourceRange(); + else if (const auto *Call = Result.Nodes.getNodeAs("memcpy")) + diag(Call->getBeginLoc(), "do not use 'memcpy' to cast between pointers") + << Call->getSourceRange(); +} + +} // namespace clang::tidy::bugprone diff --git a/clang-tools-extra/clang-tidy/bugprone/BitwisePointerCastCheck.h b/clang-tools-extra/clang-tidy/bugprone/BitwisePointerCastCheck.h new file mode 100644 index 0000000000000..1515519b3c9fd --- /dev/null +++ b/clang-tools-extra/clang-tidy/bugprone/BitwisePointerCastCheck.h @@ -0,0 +1,34 @@ +//===--- BitwisePointerCastCheck.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_BUGPRONE_BITWISEPOINTERCASTCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_BITWISEPOINTERCASTCHECK_H + +#include "../ClangTidyCheck.h" + +namespace clang::tidy::bugprone { + +/// Warns about code that tries to cast between pointers by means of +/// ``std::bit_cast`` or ``memcpy``. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone/bitwise-pointer-cast.html +class BitwisePointerCastCheck : public ClangTidyCheck { +public: + BitwisePointerCastCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + 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; + } +}; + +} // namespace clang::tidy::bugprone + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_BITWISEPOINTERCASTCHECK_H diff --git a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp index 642f025359b1d..9120c4b6c0d9a 100644 --- a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp @@ -14,6 +14,7 @@ #include "AssertSideEffectCheck.h" #include "AssignmentInIfConditionCheck.h" #include "BadSignalToKillThreadCheck.h" +#include "BitwisePointerCastCheck.h" #include "BoolPointerImplicitConversionCheck.h" #include "BranchCloneCheck.h" #include "CastingThroughVoidCheck.h" @@ -109,6 +110,8 @@ class BugproneModule : public ClangTidyModule { "bugprone-assignment-in-if-condition"); CheckFactories.registerCheck( "bugprone-bad-signal-to-kill-thread"); + CheckFactories.registerCheck( + "bugprone-bitwise-pointer-cast"); CheckFactories.registerCheck( "bugprone-bool-pointer-implicit-conversion"); CheckFactories.registerCheck("bugprone-branch-clone"); diff --git a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt index 9f7ecb9623c53..f0667bbfdd87f 100644 --- a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt @@ -3,11 +3,12 @@ set(LLVM_LINK_COMPONENTS FrontendOpenMP ) -add_clang_library(clangTidyBugproneModule +add_clang_library(clangTidyBugproneModule STATIC ArgumentCommentCheck.cpp AssertSideEffectCheck.cpp AssignmentInIfConditionCheck.cpp BadSignalToKillThreadCheck.cpp + BitwisePointerCastCheck.cpp BoolPointerImplicitConversionCheck.cpp BranchCloneCheck.cpp BugproneTidyModule.cpp diff --git a/clang-tools-extra/clang-tidy/bugprone/ForwardDeclarationNamespaceCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/ForwardDeclarationNamespaceCheck.cpp index d77df50f8fea2..080454287f28b 100644 --- a/clang-tools-extra/clang-tidy/bugprone/ForwardDeclarationNamespaceCheck.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/ForwardDeclarationNamespaceCheck.cpp @@ -146,12 +146,13 @@ void ForwardDeclarationNamespaceCheck::onEndOfTranslationUnit() { } // Check if a definition in another namespace exists. const auto DeclName = CurDecl->getName(); - if (!DeclNameToDefinitions.contains(DeclName)) { + auto It = DeclNameToDefinitions.find(DeclName); + if (It == DeclNameToDefinitions.end()) { continue; // No definition in this translation unit, we can skip it. } // Make a warning for each definition with the same name (in other // namespaces). - const auto &Definitions = DeclNameToDefinitions[DeclName]; + const auto &Definitions = It->second; for (const auto *Def : Definitions) { diag(CurDecl->getLocation(), "no definition found for %0, but a definition with " diff --git a/clang-tools-extra/clang-tidy/cert/CMakeLists.txt b/clang-tools-extra/clang-tidy/cert/CMakeLists.txt index 882735c9d1e0d..e3187b28399c7 100644 --- a/clang-tools-extra/clang-tidy/cert/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/cert/CMakeLists.txt @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS FrontendOpenMP ) -add_clang_library(clangTidyCERTModule +add_clang_library(clangTidyCERTModule STATIC CERTTidyModule.cpp CommandProcessorCheck.cpp DefaultOperatorNewAlignmentCheck.cpp diff --git a/clang-tools-extra/clang-tidy/concurrency/CMakeLists.txt b/clang-tools-extra/clang-tidy/concurrency/CMakeLists.txt index 3dab6aaf8aea2..91c6cedabcc80 100644 --- a/clang-tools-extra/clang-tidy/concurrency/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/concurrency/CMakeLists.txt @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS Support ) -add_clang_library(clangTidyConcurrencyModule +add_clang_library(clangTidyConcurrencyModule STATIC ConcurrencyTidyModule.cpp MtUnsafeCheck.cpp ThreadCanceltypeAsynchronousCheck.cpp diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt b/clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt index eb35bbc6a538f..07bb89ec7937a 100644 --- a/clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS Support ) -add_clang_library(clangTidyCppCoreGuidelinesModule +add_clang_library(clangTidyCppCoreGuidelinesModule STATIC AvoidCapturingLambdaCoroutinesCheck.cpp AvoidConstOrRefDataMembersCheck.cpp AvoidDoWhileCheck.cpp diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/ProTypeMemberInitCheck.cpp b/clang-tools-extra/clang-tidy/cppcoreguidelines/ProTypeMemberInitCheck.cpp index 9c3c7cc70c187..225e867c9b24f 100644 --- a/clang-tools-extra/clang-tidy/cppcoreguidelines/ProTypeMemberInitCheck.cpp +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/ProTypeMemberInitCheck.cpp @@ -474,10 +474,8 @@ void ProTypeMemberInitCheck::checkMissingMemberInitializer( // It only includes fields that have not been fixed SmallPtrSet AllFieldsToInit; forEachField(ClassDecl, FieldsToInit, [&](const FieldDecl *F) { - if (!HasRecordClassMemberSet.contains(F)) { + if (HasRecordClassMemberSet.insert(F).second) AllFieldsToInit.insert(F); - HasRecordClassMemberSet.insert(F); - } }); if (FieldsToInit.empty()) return; diff --git a/clang-tools-extra/clang-tidy/darwin/CMakeLists.txt b/clang-tools-extra/clang-tidy/darwin/CMakeLists.txt index 6f6b3607b3ec6..d19e8144bcfa3 100644 --- a/clang-tools-extra/clang-tidy/darwin/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/darwin/CMakeLists.txt @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS Support ) -add_clang_library(clangTidyDarwinModule +add_clang_library(clangTidyDarwinModule STATIC AvoidSpinlockCheck.cpp DarwinTidyModule.cpp DispatchOnceNonstaticCheck.cpp diff --git a/clang-tools-extra/clang-tidy/fuchsia/CMakeLists.txt b/clang-tools-extra/clang-tidy/fuchsia/CMakeLists.txt index d0e68bfec47fe..c12c281bc5321 100644 --- a/clang-tools-extra/clang-tidy/fuchsia/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/fuchsia/CMakeLists.txt @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS Support ) -add_clang_library(clangTidyFuchsiaModule +add_clang_library(clangTidyFuchsiaModule STATIC DefaultArgumentsCallsCheck.cpp DefaultArgumentsDeclarationsCheck.cpp FuchsiaTidyModule.cpp diff --git a/clang-tools-extra/clang-tidy/google/CMakeLists.txt b/clang-tools-extra/clang-tidy/google/CMakeLists.txt index fcba2b1b214ad..2470c089ef7ca 100644 --- a/clang-tools-extra/clang-tidy/google/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/google/CMakeLists.txt @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS Support ) -add_clang_library(clangTidyGoogleModule +add_clang_library(clangTidyGoogleModule STATIC AvoidCStyleCastsCheck.cpp AvoidNSObjectNewCheck.cpp AvoidThrowingObjCExceptionCheck.cpp diff --git a/clang-tools-extra/clang-tidy/hicpp/CMakeLists.txt b/clang-tools-extra/clang-tidy/hicpp/CMakeLists.txt index 132fbaccccf8a..2f31d168e65c0 100644 --- a/clang-tools-extra/clang-tidy/hicpp/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/hicpp/CMakeLists.txt @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS Support ) -add_clang_library(clangTidyHICPPModule +add_clang_library(clangTidyHICPPModule STATIC ExceptionBaseclassCheck.cpp HICPPTidyModule.cpp IgnoredRemoveResultCheck.cpp diff --git a/clang-tools-extra/clang-tidy/linuxkernel/CMakeLists.txt b/clang-tools-extra/clang-tidy/linuxkernel/CMakeLists.txt index 403589d947590..e7e2dcf9a82fe 100644 --- a/clang-tools-extra/clang-tidy/linuxkernel/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/linuxkernel/CMakeLists.txt @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS Support ) -add_clang_library(clangTidyLinuxKernelModule +add_clang_library(clangTidyLinuxKernelModule STATIC LinuxKernelTidyModule.cpp MustCheckErrsCheck.cpp diff --git a/clang-tools-extra/clang-tidy/llvm/CMakeLists.txt b/clang-tools-extra/clang-tidy/llvm/CMakeLists.txt index b56498bdc8c4b..79c58a19aedac 100644 --- a/clang-tools-extra/clang-tidy/llvm/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/llvm/CMakeLists.txt @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS Support ) -add_clang_library(clangTidyLLVMModule +add_clang_library(clangTidyLLVMModule STATIC HeaderGuardCheck.cpp IncludeOrderCheck.cpp LLVMTidyModule.cpp diff --git a/clang-tools-extra/clang-tidy/llvmlibc/CMakeLists.txt b/clang-tools-extra/clang-tidy/llvmlibc/CMakeLists.txt index b071cfd67dcf4..eaeddf4f62857 100644 --- a/clang-tools-extra/clang-tidy/llvmlibc/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/llvmlibc/CMakeLists.txt @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS Support ) -add_clang_library(clangTidyLLVMLibcModule +add_clang_library(clangTidyLLVMLibcModule STATIC CalleeNamespaceCheck.cpp ImplementationInNamespaceCheck.cpp InlineFunctionDeclCheck.cpp diff --git a/clang-tools-extra/clang-tidy/misc/CMakeLists.txt b/clang-tools-extra/clang-tidy/misc/CMakeLists.txt index 1c1d3b836ea1b..fd7affd22a463 100644 --- a/clang-tools-extra/clang-tidy/misc/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/misc/CMakeLists.txt @@ -17,7 +17,7 @@ add_custom_command( add_custom_target(genconfusable DEPENDS Confusables.inc) set_target_properties(genconfusable PROPERTIES FOLDER "Clang Tools Extra/Sourcegenning") -add_clang_library(clangTidyMiscModule +add_clang_library(clangTidyMiscModule STATIC ConstCorrectnessCheck.cpp CoroutineHostileRAIICheck.cpp DefinitionsInHeadersCheck.cpp diff --git a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt index 4f68c487cac9d..c919d49b42873 100644 --- a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS Support ) -add_clang_library(clangTidyModernizeModule +add_clang_library(clangTidyModernizeModule STATIC AvoidBindCheck.cpp AvoidCArraysCheck.cpp ConcatNestedNamespacesCheck.cpp diff --git a/clang-tools-extra/clang-tidy/modernize/LoopConvertUtils.cpp b/clang-tools-extra/clang-tidy/modernize/LoopConvertUtils.cpp index c0bf4903ec391..90c16bfddd84f 100644 --- a/clang-tools-extra/clang-tidy/modernize/LoopConvertUtils.cpp +++ b/clang-tools-extra/clang-tidy/modernize/LoopConvertUtils.cpp @@ -777,7 +777,7 @@ bool ForLoopIndexUseVisitor::TraverseLambdaCapture(LambdaExpr *LE, const LambdaCapture *C, Expr *Init) { if (C->capturesVariable()) { - const ValueDecl *VDecl = C->getCapturedVar(); + ValueDecl *VDecl = C->getCapturedVar(); if (areSameVariable(IndexVar, VDecl)) { // FIXME: if the index is captured, it will count as an usage and the // alias (if any) won't work, because it is only used in case of having @@ -787,6 +787,8 @@ bool ForLoopIndexUseVisitor::TraverseLambdaCapture(LambdaExpr *LE, : Usage::UK_CaptureByRef, C->getLocation())); } + if (VDecl->isInitCapture()) + TraverseStmtImpl(cast(VDecl)->getInit()); } return VisitorBase::TraverseLambdaCapture(LE, C, Init); } @@ -816,6 +818,17 @@ bool ForLoopIndexUseVisitor::VisitDeclStmt(DeclStmt *S) { return true; } +bool ForLoopIndexUseVisitor::TraverseStmtImpl(Stmt *S) { + // All this pointer swapping is a mechanism for tracking immediate parentage + // of Stmts. + const Stmt *OldNextParent = NextStmtParent; + CurrStmtParent = NextStmtParent; + NextStmtParent = S; + bool Result = VisitorBase::TraverseStmt(S); + NextStmtParent = OldNextParent; + return Result; +} + bool ForLoopIndexUseVisitor::TraverseStmt(Stmt *S) { // If this is an initialization expression for a lambda capture, prune the // traversal so that we don't end up diagnosing the contained DeclRefExpr as @@ -828,15 +841,7 @@ bool ForLoopIndexUseVisitor::TraverseStmt(Stmt *S) { return true; } } - - // All this pointer swapping is a mechanism for tracking immediate parentage - // of Stmts. - const Stmt *OldNextParent = NextStmtParent; - CurrStmtParent = NextStmtParent; - NextStmtParent = S; - bool Result = VisitorBase::TraverseStmt(S); - NextStmtParent = OldNextParent; - return Result; + return TraverseStmtImpl(S); } std::string VariableNamer::createIndexName() { diff --git a/clang-tools-extra/clang-tidy/modernize/LoopConvertUtils.h b/clang-tools-extra/clang-tidy/modernize/LoopConvertUtils.h index cfceb3b586842..ca9c1855038b5 100644 --- a/clang-tools-extra/clang-tidy/modernize/LoopConvertUtils.h +++ b/clang-tools-extra/clang-tidy/modernize/LoopConvertUtils.h @@ -354,6 +354,8 @@ class ForLoopIndexUseVisitor bool VisitDeclStmt(DeclStmt *S); bool TraverseStmt(Stmt *S); + bool TraverseStmtImpl(Stmt *S); + /// Add an expression to the list of expressions on which the container /// expression depends. void addComponent(const Expr *E); diff --git a/clang-tools-extra/clang-tidy/modernize/UseStdFormatCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStdFormatCheck.cpp index cdb34aef1b0e6..f97b844c56496 100644 --- a/clang-tools-extra/clang-tidy/modernize/UseStdFormatCheck.cpp +++ b/clang-tools-extra/clang-tidy/modernize/UseStdFormatCheck.cpp @@ -44,6 +44,7 @@ void UseStdFormatCheck::registerPPCallbacks(const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) { IncludeInserter.registerPreprocessor(PP); + this->PP = PP; } void UseStdFormatCheck::registerMatchers(MatchFinder *Finder) { @@ -75,9 +76,9 @@ void UseStdFormatCheck::check(const MatchFinder::MatchResult &Result) { utils::FormatStringConverter::Configuration ConverterConfig; ConverterConfig.StrictMode = StrictMode; - utils::FormatStringConverter Converter(Result.Context, StrFormat, - FormatArgOffset, ConverterConfig, - getLangOpts()); + utils::FormatStringConverter Converter( + Result.Context, StrFormat, FormatArgOffset, ConverterConfig, + getLangOpts(), *Result.SourceManager, *PP); const Expr *StrFormatCall = StrFormat->getCallee(); if (!Converter.canApply()) { diag(StrFormat->getBeginLoc(), diff --git a/clang-tools-extra/clang-tidy/modernize/UseStdFormatCheck.h b/clang-tools-extra/clang-tidy/modernize/UseStdFormatCheck.h index b59a4708c6e4b..9ac2240212ebf 100644 --- a/clang-tools-extra/clang-tidy/modernize/UseStdFormatCheck.h +++ b/clang-tools-extra/clang-tidy/modernize/UseStdFormatCheck.h @@ -44,6 +44,7 @@ class UseStdFormatCheck : public ClangTidyCheck { StringRef ReplacementFormatFunction; utils::IncludeInserter IncludeInserter; std::optional MaybeHeaderToInclude; + Preprocessor *PP = nullptr; }; } // namespace clang::tidy::modernize diff --git a/clang-tools-extra/clang-tidy/modernize/UseStdPrintCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStdPrintCheck.cpp index 16f2f4b3e7d1a..9161c0e702a28 100644 --- a/clang-tools-extra/clang-tidy/modernize/UseStdPrintCheck.cpp +++ b/clang-tools-extra/clang-tidy/modernize/UseStdPrintCheck.cpp @@ -68,6 +68,7 @@ void UseStdPrintCheck::registerPPCallbacks(const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) { IncludeInserter.registerPreprocessor(PP); + this->PP = PP; } static clang::ast_matchers::StatementMatcher @@ -131,7 +132,8 @@ void UseStdPrintCheck::check(const MatchFinder::MatchResult &Result) { ConverterConfig.StrictMode = StrictMode; ConverterConfig.AllowTrailingNewlineRemoval = true; utils::FormatStringConverter Converter( - Result.Context, Printf, FormatArgOffset, ConverterConfig, getLangOpts()); + Result.Context, Printf, FormatArgOffset, ConverterConfig, getLangOpts(), + *Result.SourceManager, *PP); const Expr *PrintfCall = Printf->getCallee(); const StringRef ReplacementFunction = Converter.usePrintNewlineFunction() ? ReplacementPrintlnFunction diff --git a/clang-tools-extra/clang-tidy/modernize/UseStdPrintCheck.h b/clang-tools-extra/clang-tidy/modernize/UseStdPrintCheck.h index 7a06cf38b4264..995c740389e73 100644 --- a/clang-tools-extra/clang-tidy/modernize/UseStdPrintCheck.h +++ b/clang-tools-extra/clang-tidy/modernize/UseStdPrintCheck.h @@ -36,6 +36,7 @@ class UseStdPrintCheck : public ClangTidyCheck { } private: + Preprocessor *PP; bool StrictMode; std::vector PrintfLikeFunctions; std::vector FprintfLikeFunctions; diff --git a/clang-tools-extra/clang-tidy/mpi/CMakeLists.txt b/clang-tools-extra/clang-tidy/mpi/CMakeLists.txt index 717683042f524..1232a28c7cf7b 100644 --- a/clang-tools-extra/clang-tidy/mpi/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/mpi/CMakeLists.txt @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS Support ) -add_clang_library(clangTidyMPIModule +add_clang_library(clangTidyMPIModule STATIC BufferDerefCheck.cpp MPITidyModule.cpp TypeMismatchCheck.cpp diff --git a/clang-tools-extra/clang-tidy/objc/CMakeLists.txt b/clang-tools-extra/clang-tidy/objc/CMakeLists.txt index aa428fce56a59..e28d25deee84c 100644 --- a/clang-tools-extra/clang-tidy/objc/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/objc/CMakeLists.txt @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS Support ) -add_clang_library(clangTidyObjCModule +add_clang_library(clangTidyObjCModule STATIC AssertEquals.cpp AvoidNSErrorInitCheck.cpp DeallocInCategoryCheck.cpp diff --git a/clang-tools-extra/clang-tidy/openmp/CMakeLists.txt b/clang-tools-extra/clang-tidy/openmp/CMakeLists.txt index 4ef61f88c0509..acee08a48aeff 100644 --- a/clang-tools-extra/clang-tidy/openmp/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/openmp/CMakeLists.txt @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS Support ) -add_clang_library(clangTidyOpenMPModule +add_clang_library(clangTidyOpenMPModule STATIC ExceptionEscapeCheck.cpp OpenMPTidyModule.cpp UseDefaultNoneCheck.cpp diff --git a/clang-tools-extra/clang-tidy/performance/CMakeLists.txt b/clang-tools-extra/clang-tidy/performance/CMakeLists.txt index 81128ff086021..c6e547c5089fb 100644 --- a/clang-tools-extra/clang-tidy/performance/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/performance/CMakeLists.txt @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS Support ) -add_clang_library(clangTidyPerformanceModule +add_clang_library(clangTidyPerformanceModule STATIC AvoidEndlCheck.cpp EnumSizeCheck.cpp FasterStringFindCheck.cpp diff --git a/clang-tools-extra/clang-tidy/performance/MoveConstArgCheck.cpp b/clang-tools-extra/clang-tidy/performance/MoveConstArgCheck.cpp index d29b9e91f2e35..421ce003975bc 100644 --- a/clang-tools-extra/clang-tidy/performance/MoveConstArgCheck.cpp +++ b/clang-tools-extra/clang-tidy/performance/MoveConstArgCheck.cpp @@ -209,8 +209,9 @@ void MoveConstArgCheck::check(const MatchFinder::MatchResult &Result) { } if (const CXXRecordDecl *RecordDecl = ArgType->getAsCXXRecordDecl(); - RecordDecl && !(RecordDecl->hasMoveConstructor() && - RecordDecl->hasMoveAssignment())) { + RecordDecl && RecordDecl->hasDefinition() && + !(RecordDecl->hasMoveConstructor() && + RecordDecl->hasMoveAssignment())) { const bool MissingMoveAssignment = !RecordDecl->hasMoveAssignment(); const bool MissingMoveConstructor = !RecordDecl->hasMoveConstructor(); const bool MissingBoth = MissingMoveAssignment && MissingMoveConstructor; diff --git a/clang-tools-extra/clang-tidy/plugin/CMakeLists.txt b/clang-tools-extra/clang-tidy/plugin/CMakeLists.txt index 673da472a7468..aff2018b693b1 100644 --- a/clang-tools-extra/clang-tidy/plugin/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/plugin/CMakeLists.txt @@ -1,4 +1,4 @@ -add_clang_library(clangTidyPlugin +add_clang_library(clangTidyPlugin STATIC ClangTidyPlugin.cpp LINK_LIBS diff --git a/clang-tools-extra/clang-tidy/portability/CMakeLists.txt b/clang-tools-extra/clang-tidy/portability/CMakeLists.txt index 01a86d686daa7..5a38722a61481 100644 --- a/clang-tools-extra/clang-tidy/portability/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/portability/CMakeLists.txt @@ -4,11 +4,12 @@ set(LLVM_LINK_COMPONENTS TargetParser ) -add_clang_library(clangTidyPortabilityModule +add_clang_library(clangTidyPortabilityModule STATIC PortabilityTidyModule.cpp RestrictSystemIncludesCheck.cpp SIMDIntrinsicsCheck.cpp StdAllocatorConstCheck.cpp + TemplateVirtualMemberFunctionCheck.cpp LINK_LIBS clangTidy diff --git a/clang-tools-extra/clang-tidy/portability/PortabilityTidyModule.cpp b/clang-tools-extra/clang-tidy/portability/PortabilityTidyModule.cpp index b3759a754587d..316b98b46cf3f 100644 --- a/clang-tools-extra/clang-tidy/portability/PortabilityTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/portability/PortabilityTidyModule.cpp @@ -12,6 +12,7 @@ #include "RestrictSystemIncludesCheck.h" #include "SIMDIntrinsicsCheck.h" #include "StdAllocatorConstCheck.h" +#include "TemplateVirtualMemberFunctionCheck.h" namespace clang::tidy { namespace portability { @@ -25,6 +26,8 @@ class PortabilityModule : public ClangTidyModule { "portability-simd-intrinsics"); CheckFactories.registerCheck( "portability-std-allocator-const"); + CheckFactories.registerCheck( + "portability-template-virtual-member-function"); } }; diff --git a/clang-tools-extra/clang-tidy/portability/TemplateVirtualMemberFunctionCheck.cpp b/clang-tools-extra/clang-tidy/portability/TemplateVirtualMemberFunctionCheck.cpp new file mode 100644 index 0000000000000..9c2f27f01f184 --- /dev/null +++ b/clang-tools-extra/clang-tidy/portability/TemplateVirtualMemberFunctionCheck.cpp @@ -0,0 +1,44 @@ +//===--- TemplateVirtualMemberFunctionCheck.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 "TemplateVirtualMemberFunctionCheck.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +using namespace clang::ast_matchers; + +namespace clang::tidy::portability { +namespace { +AST_MATCHER(CXXMethodDecl, isUsed) { return Node.isUsed(); } +} // namespace + +void TemplateVirtualMemberFunctionCheck::registerMatchers(MatchFinder *Finder) { + Finder->addMatcher( + cxxMethodDecl(ofClass(classTemplateSpecializationDecl( + unless(isExplicitTemplateSpecialization())) + .bind("specialization")), + isVirtual(), unless(isUsed()), + unless(cxxDestructorDecl(isDefaulted()))) + .bind("method"), + this); +} + +void TemplateVirtualMemberFunctionCheck::check( + const MatchFinder::MatchResult &Result) { + const auto *ImplicitSpecialization = + Result.Nodes.getNodeAs("specialization"); + const auto *MethodDecl = Result.Nodes.getNodeAs("method"); + + diag(MethodDecl->getLocation(), + "unspecified virtual member function instantiation; the virtual " + "member function is not instantiated but it might be with a " + "different compiler"); + diag(ImplicitSpecialization->getPointOfInstantiation(), + "template instantiated here", DiagnosticIDs::Note); +} + +} // namespace clang::tidy::portability diff --git a/clang-tools-extra/clang-tidy/portability/TemplateVirtualMemberFunctionCheck.h b/clang-tools-extra/clang-tidy/portability/TemplateVirtualMemberFunctionCheck.h new file mode 100644 index 0000000000000..41f92adadd6e8 --- /dev/null +++ b/clang-tools-extra/clang-tidy/portability/TemplateVirtualMemberFunctionCheck.h @@ -0,0 +1,38 @@ +//===--- TemplateVirtualMemberFunctionCheck.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_PORTABILITY_TEMPLATEVIRTUALMEMBERFUNCTIONCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PORTABILITY_TEMPLATEVIRTUALMEMBERFUNCTIONCHECK_H + +#include "../ClangTidyCheck.h" + +namespace clang::tidy::portability { + +/// Upon instantiating a template class, non-virtual member functions don't have +/// to be instantiated unless they are used. Virtual member function +/// instantiation on the other hand is unspecified and depends on the +/// implementation of the compiler. This check intends to find cases when a +/// virtual member function is not instantiated but it might be with a different +/// compiler. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/portability/template-virtual-member-function.html +class TemplateVirtualMemberFunctionCheck : public ClangTidyCheck { +public: + TemplateVirtualMemberFunctionCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + 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; + } +}; + +} // namespace clang::tidy::portability + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PORTABILITY_TEMPLATEVIRTUALMEMBERFUNCTIONCHECK_H diff --git a/clang-tools-extra/clang-tidy/readability/CMakeLists.txt b/clang-tools-extra/clang-tidy/readability/CMakeLists.txt index 41065fc8e8785..8f303c51e1b0d 100644 --- a/clang-tools-extra/clang-tidy/readability/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/readability/CMakeLists.txt @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS Support ) -add_clang_library(clangTidyReadabilityModule +add_clang_library(clangTidyReadabilityModule STATIC AvoidConstParamsInDecls.cpp AvoidNestedConditionalOperatorCheck.cpp AvoidReturnWithVoidValueCheck.cpp diff --git a/clang-tools-extra/clang-tidy/rename_check.py b/clang-tools-extra/clang-tidy/rename_check.py index bf9c886699cb2..5f3295b23ba72 100755 --- a/clang-tools-extra/clang-tidy/rename_check.py +++ b/clang-tools-extra/clang-tidy/rename_check.py @@ -8,16 +8,16 @@ # # ===-----------------------------------------------------------------------===# -from __future__ import unicode_literals - import argparse import glob import io import os import re +import sys +from typing import List -def replaceInFileRegex(fileName, sFrom, sTo): +def replaceInFileRegex(fileName: str, sFrom: str, sTo: str) -> None: if sFrom == sTo: return @@ -35,7 +35,7 @@ def replaceInFileRegex(fileName, sFrom, sTo): f.write(txt) -def replaceInFile(fileName, sFrom, sTo): +def replaceInFile(fileName: str, sFrom: str, sTo: str) -> None: if sFrom == sTo: return txt = None @@ -51,7 +51,7 @@ def replaceInFile(fileName, sFrom, sTo): f.write(txt) -def generateCommentLineHeader(filename): +def generateCommentLineHeader(filename: str) -> str: return "".join( [ "//===--- ", @@ -63,7 +63,7 @@ def generateCommentLineHeader(filename): ) -def generateCommentLineSource(filename): +def generateCommentLineSource(filename: str) -> str: return "".join( [ "//===--- ", @@ -75,7 +75,7 @@ def generateCommentLineSource(filename): ) -def fileRename(fileName, sFrom, sTo): +def fileRename(fileName: str, sFrom: str, sTo: str) -> str: if sFrom not in fileName or sFrom == sTo: return fileName newFileName = fileName.replace(sFrom, sTo) @@ -84,7 +84,7 @@ def fileRename(fileName, sFrom, sTo): return newFileName -def deleteMatchingLines(fileName, pattern): +def deleteMatchingLines(fileName: str, pattern: str) -> bool: lines = None with io.open(fileName, "r", encoding="utf8") as f: lines = f.readlines() @@ -101,7 +101,7 @@ def deleteMatchingLines(fileName, pattern): return True -def getListOfFiles(clang_tidy_path): +def getListOfFiles(clang_tidy_path: str) -> List[str]: files = glob.glob(os.path.join(clang_tidy_path, "**"), recursive=True) files += [ os.path.normpath(os.path.join(clang_tidy_path, "../docs/ReleaseNotes.rst")) @@ -124,7 +124,7 @@ def getListOfFiles(clang_tidy_path): # Adapts the module's CMakelist file. Returns 'True' if it could add a new # entry and 'False' if the entry already existed. -def adapt_cmake(module_path, check_name_camel): +def adapt_cmake(module_path: str, check_name_camel: str) -> bool: filename = os.path.join(module_path, "CMakeLists.txt") with io.open(filename, "r", encoding="utf8") as f: lines = f.readlines() @@ -153,7 +153,9 @@ def adapt_cmake(module_path, check_name_camel): # Modifies the module to include the new check. -def adapt_module(module_path, module, check_name, check_name_camel): +def adapt_module( + module_path: str, module: str, check_name: str, check_name_camel: str +) -> None: modulecpp = next( iter( filter( @@ -204,7 +206,9 @@ def adapt_module(module_path, module, check_name, check_name_camel): # Adds a release notes entry. -def add_release_notes(clang_tidy_path, old_check_name, new_check_name): +def add_release_notes( + clang_tidy_path: str, old_check_name: str, new_check_name: str +) -> None: filename = os.path.normpath( os.path.join(clang_tidy_path, "../docs/ReleaseNotes.rst") ) @@ -262,7 +266,7 @@ def add_release_notes(clang_tidy_path, old_check_name, new_check_name): f.write(line) -def main(): +def main() -> None: parser = argparse.ArgumentParser(description="Rename clang-tidy check.") parser.add_argument("old_check_name", type=str, help="Old check name.") parser.add_argument("new_check_name", type=str, help="New check name.") @@ -311,7 +315,7 @@ def main(): "Check name '%s' not found in %s. Exiting." % (check_name_camel, cmake_lists) ) - return 1 + sys.exit(1) modulecpp = next( iter( diff --git a/clang-tools-extra/clang-tidy/tool/CMakeLists.txt b/clang-tools-extra/clang-tidy/tool/CMakeLists.txt index b220cbea80f1b..81fba3bbf12fe 100644 --- a/clang-tools-extra/clang-tidy/tool/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/tool/CMakeLists.txt @@ -9,7 +9,7 @@ set(LLVM_LINK_COMPONENTS # Needed by LLVM's CMake checks because this file defines multiple targets. set(LLVM_OPTIONAL_SOURCES ClangTidyMain.cpp ClangTidyToolMain.cpp) -add_clang_library(clangTidyMain +add_clang_library(clangTidyMain STATIC ClangTidyMain.cpp LINK_LIBS diff --git a/clang-tools-extra/clang-tidy/utils/CMakeLists.txt b/clang-tools-extra/clang-tidy/utils/CMakeLists.txt index 504c6e928bdad..b83a1e9a77182 100644 --- a/clang-tools-extra/clang-tidy/utils/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/utils/CMakeLists.txt @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS Support ) -add_clang_library(clangTidyUtils +add_clang_library(clangTidyUtils STATIC Aliasing.cpp ASTUtils.cpp BracesAroundStatement.cpp diff --git a/clang-tools-extra/clang-tidy/utils/FormatStringConverter.cpp b/clang-tools-extra/clang-tidy/utils/FormatStringConverter.cpp index 33f3ea47df1e3..7f4ccca84faa5 100644 --- a/clang-tools-extra/clang-tidy/utils/FormatStringConverter.cpp +++ b/clang-tools-extra/clang-tidy/utils/FormatStringConverter.cpp @@ -18,6 +18,7 @@ #include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/Basic/LangOptions.h" #include "clang/Lex/Lexer.h" +#include "clang/Lex/Preprocessor.h" #include "clang/Tooling/FixIt.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/Debug.h" @@ -195,11 +196,10 @@ static bool castMismatchedIntegerTypes(const CallExpr *Call, bool StrictMode) { return false; } -FormatStringConverter::FormatStringConverter(ASTContext *ContextIn, - const CallExpr *Call, - unsigned FormatArgOffset, - const Configuration ConfigIn, - const LangOptions &LO) +FormatStringConverter::FormatStringConverter( + ASTContext *ContextIn, const CallExpr *Call, unsigned FormatArgOffset, + const Configuration ConfigIn, const LangOptions &LO, SourceManager &SM, + Preprocessor &PP) : Context(ContextIn), Config(ConfigIn), CastMismatchedIntegerTypes( castMismatchedIntegerTypes(Call, ConfigIn.StrictMode)), @@ -208,11 +208,22 @@ FormatStringConverter::FormatStringConverter(ASTContext *ContextIn, assert(ArgsOffset <= NumArgs); FormatExpr = llvm::dyn_cast( Args[FormatArgOffset]->IgnoreImplicitAsWritten()); + if (!FormatExpr || !FormatExpr->isOrdinary()) { // Function must have a narrow string literal as its first argument. conversionNotPossible("first argument is not a narrow string literal"); return; } + + if (const std::optional MaybeMacroName = + formatStringContainsUnreplaceableMacro(Call, FormatExpr, SM, PP); + MaybeMacroName) { + conversionNotPossible( + ("format string contains unreplaceable macro '" + *MaybeMacroName + "'") + .str()); + return; + } + PrintfFormatString = FormatExpr->getString(); // Assume that the output will be approximately the same size as the input, @@ -230,6 +241,50 @@ FormatStringConverter::FormatStringConverter(ASTContext *ContextIn, finalizeFormatText(); } +std::optional +FormatStringConverter::formatStringContainsUnreplaceableMacro( + const CallExpr *Call, const StringLiteral *FormatExpr, SourceManager &SM, + Preprocessor &PP) { + // If a macro invocation surrounds the entire call then we don't want that to + // inhibit conversion. The whole format string will appear to come from that + // macro, as will the function call. + std::optional MaybeSurroundingMacroName; + if (SourceLocation BeginCallLoc = Call->getBeginLoc(); + BeginCallLoc.isMacroID()) + MaybeSurroundingMacroName = + Lexer::getImmediateMacroName(BeginCallLoc, SM, PP.getLangOpts()); + + for (auto I = FormatExpr->tokloc_begin(), E = FormatExpr->tokloc_end(); + I != E; ++I) { + const SourceLocation &TokenLoc = *I; + if (TokenLoc.isMacroID()) { + const StringRef MacroName = + Lexer::getImmediateMacroName(TokenLoc, SM, PP.getLangOpts()); + + if (MaybeSurroundingMacroName != MacroName) { + // glibc uses __PRI64_PREFIX and __PRIPTR_PREFIX to define the prefixes + // for types that change size so we must look for multiple prefixes. + if (!MacroName.starts_with("PRI") && !MacroName.starts_with("__PRI")) + return MacroName; + + const SourceLocation TokenSpellingLoc = SM.getSpellingLoc(TokenLoc); + const OptionalFileEntryRef MaybeFileEntry = + SM.getFileEntryRefForID(SM.getFileID(TokenSpellingLoc)); + if (!MaybeFileEntry) + return MacroName; + + HeaderSearch &HS = PP.getHeaderSearchInfo(); + // Check if the file is a system header + if (!isSystem(HS.getFileDirFlavor(*MaybeFileEntry)) || + llvm::sys::path::filename(MaybeFileEntry->getName()) != + "inttypes.h") + return MacroName; + } + } + } + return std::nullopt; +} + void FormatStringConverter::emitAlignment(const PrintfSpecifier &FS, std::string &FormatSpec) { ConversionSpecifier::Kind ArgKind = FS.getConversionSpecifier().getKind(); diff --git a/clang-tools-extra/clang-tidy/utils/FormatStringConverter.h b/clang-tools-extra/clang-tidy/utils/FormatStringConverter.h index 1109a0b602262..15d1f597fe440 100644 --- a/clang-tools-extra/clang-tidy/utils/FormatStringConverter.h +++ b/clang-tools-extra/clang-tidy/utils/FormatStringConverter.h @@ -40,7 +40,8 @@ class FormatStringConverter FormatStringConverter(ASTContext *Context, const CallExpr *Call, unsigned FormatArgOffset, Configuration Config, - const LangOptions &LO); + const LangOptions &LO, SourceManager &SM, + Preprocessor &PP); bool canApply() const { return ConversionNotPossibleReason.empty(); } const std::string &conversionNotPossibleReason() const { @@ -110,6 +111,10 @@ class FormatStringConverter void appendFormatText(StringRef Text); void finalizeFormatText(); + static std::optional + formatStringContainsUnreplaceableMacro(const CallExpr *CallExpr, + const StringLiteral *FormatExpr, + SourceManager &SM, Preprocessor &PP); bool conversionNotPossible(std::string Reason) { ConversionNotPossibleReason = std::move(Reason); return false; diff --git a/clang-tools-extra/clang-tidy/zircon/CMakeLists.txt b/clang-tools-extra/clang-tidy/zircon/CMakeLists.txt index cd605d6d6c108..e08fe80e730ac 100644 --- a/clang-tools-extra/clang-tidy/zircon/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/zircon/CMakeLists.txt @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS Support ) -add_clang_library(clangTidyZirconModule +add_clang_library(clangTidyZirconModule STATIC TemporaryObjectsCheck.cpp ZirconTidyModule.cpp diff --git a/clang-tools-extra/clangd/CMakeLists.txt b/clang-tools-extra/clangd/CMakeLists.txt index 8dcbf5f47e056..d797ddce8c44d 100644 --- a/clang-tools-extra/clangd/CMakeLists.txt +++ b/clang-tools-extra/clangd/CMakeLists.txt @@ -61,7 +61,7 @@ endif() include_directories(BEFORE "${CMAKE_CURRENT_BINARY_DIR}/../clang-tidy") include_directories(BEFORE "${CMAKE_CURRENT_SOURCE_DIR}/../include-cleaner/include") -add_clang_library(clangDaemon +add_clang_library(clangDaemon STATIC AST.cpp ASTSignals.cpp ClangdLSPServer.cpp diff --git a/clang-tools-extra/clangd/ClangdServer.cpp b/clang-tools-extra/clangd/ClangdServer.cpp index e910a80ba0bae..9b38be04e7ddd 100644 --- a/clang-tools-extra/clangd/ClangdServer.cpp +++ b/clang-tools-extra/clangd/ClangdServer.cpp @@ -451,6 +451,7 @@ void ClangdServer::codeComplete(PathRef File, Position Pos, CodeCompleteOpts.MainFileSignals = IP->Signals; CodeCompleteOpts.AllScopes = Config::current().Completion.AllScopes; + CodeCompleteOpts.ArgumentLists = Config::current().Completion.ArgumentLists; // FIXME(ibiryukov): even if Preamble is non-null, we may want to check // both the old and the new version in case only one of them matches. CodeCompleteResult Result = clangd::codeComplete( diff --git a/clang-tools-extra/clangd/CodeComplete.cpp b/clang-tools-extra/clangd/CodeComplete.cpp index 6711eb7dc10f8..2c2d5f0b5ac92 100644 --- a/clang-tools-extra/clangd/CodeComplete.cpp +++ b/clang-tools-extra/clangd/CodeComplete.cpp @@ -21,6 +21,7 @@ #include "AST.h" #include "CodeCompletionStrings.h" #include "Compiler.h" +#include "Config.h" #include "ExpectedTypes.h" #include "Feature.h" #include "FileDistance.h" @@ -350,8 +351,7 @@ struct CodeCompletionBuilder { CodeCompletionContext::Kind ContextKind, const CodeCompleteOptions &Opts, bool IsUsingDeclaration, tok::TokenKind NextTokenKind) - : ASTCtx(ASTCtx), - EnableFunctionArgSnippets(Opts.EnableFunctionArgSnippets), + : ASTCtx(ASTCtx), ArgumentLists(Opts.ArgumentLists), IsUsingDeclaration(IsUsingDeclaration), NextTokenKind(NextTokenKind) { Completion.Deprecated = true; // cleared by any non-deprecated overload. add(C, SemaCCS, ContextKind); @@ -561,6 +561,15 @@ struct CodeCompletionBuilder { } std::string summarizeSnippet() const { + /// localize ArgumentLists tests for better readability + const bool None = ArgumentLists == Config::ArgumentListsPolicy::None; + const bool Open = + ArgumentLists == Config::ArgumentListsPolicy::OpenDelimiter; + const bool Delim = ArgumentLists == Config::ArgumentListsPolicy::Delimiters; + const bool Full = + ArgumentLists == Config::ArgumentListsPolicy::FullPlaceholders || + (!None && !Open && !Delim); // <-- failsafe: Full is default + if (IsUsingDeclaration) return ""; auto *Snippet = onlyValue<&BundledEntry::SnippetSuffix>(); @@ -568,7 +577,7 @@ struct CodeCompletionBuilder { // All bundles are function calls. // FIXME(ibiryukov): sometimes add template arguments to a snippet, e.g. // we need to complete 'forward<$1>($0)'. - return "($0)"; + return None ? "" : (Open ? "(" : "($0)"); if (Snippet->empty()) return ""; @@ -607,7 +616,7 @@ struct CodeCompletionBuilder { return ""; } } - if (EnableFunctionArgSnippets) + if (Full) return *Snippet; // Replace argument snippets with a simplified pattern. @@ -622,9 +631,9 @@ struct CodeCompletionBuilder { bool EmptyArgs = llvm::StringRef(*Snippet).ends_with("()"); if (Snippet->front() == '<') - return EmptyArgs ? "<$1>()$0" : "<$1>($0)"; + return None ? "" : (Open ? "<" : (EmptyArgs ? "<$1>()$0" : "<$1>($0)")); if (Snippet->front() == '(') - return EmptyArgs ? "()" : "($0)"; + return None ? "" : (Open ? "(" : (EmptyArgs ? "()" : "($0)")); return *Snippet; // Not an arg snippet? } // 'CompletionItemKind::Interface' matches template type aliases. @@ -638,7 +647,7 @@ struct CodeCompletionBuilder { // e.g. Foo<${1:class}>. if (llvm::StringRef(*Snippet).ends_with("<>")) return "<>"; // can happen with defaulted template arguments. - return "<$0>"; + return None ? "" : (Open ? "<" : "<$0>"); } return *Snippet; } @@ -654,7 +663,8 @@ struct CodeCompletionBuilder { ASTContext *ASTCtx; CodeCompletion Completion; llvm::SmallVector Bundled; - bool EnableFunctionArgSnippets; + /// the way argument lists are handled. + Config::ArgumentListsPolicy ArgumentLists; // No snippets will be generated for using declarations and when the function // arguments are already present. bool IsUsingDeclaration; diff --git a/clang-tools-extra/clangd/CodeComplete.h b/clang-tools-extra/clangd/CodeComplete.h index 9bcdeb0227cd4..bb2ebd9478645 100644 --- a/clang-tools-extra/clangd/CodeComplete.h +++ b/clang-tools-extra/clangd/CodeComplete.h @@ -17,6 +17,7 @@ #include "ASTSignals.h" #include "Compiler.h" +#include "Config.h" #include "Protocol.h" #include "Quality.h" #include "index/Index.h" @@ -101,10 +102,6 @@ struct CodeCompleteOptions { /// '->' on member access etc. bool IncludeFixIts = false; - /// Whether to generate snippets for function arguments on code-completion. - /// Needs snippets to be enabled as well. - bool EnableFunctionArgSnippets = true; - /// Whether to include index symbols that are not defined in the scopes /// visible from the code completion point. This applies in contexts without /// explicit scope qualifiers. @@ -112,6 +109,10 @@ struct CodeCompleteOptions { /// Such completions can insert scope qualifiers. bool AllScopes = false; + /// The way argument list on calls '()' and generics '<>' are handled. + Config::ArgumentListsPolicy ArgumentLists = + Config::ArgumentListsPolicy::FullPlaceholders; + /// Whether to use the clang parser, or fallback to text-based completion /// (using identifiers in the current file and symbol indexes). enum CodeCompletionParse { diff --git a/clang-tools-extra/clangd/Config.h b/clang-tools-extra/clangd/Config.h index 41143b9ebc8d2..8fcbc5c33469f 100644 --- a/clang-tools-extra/clangd/Config.h +++ b/clang-tools-extra/clangd/Config.h @@ -126,11 +126,25 @@ struct Config { std::vector FullyQualifiedNamespaces; } Style; + /// controls the completion options for argument lists. + enum class ArgumentListsPolicy { + /// nothing, no argument list and also NO Delimiters "()" or "<>". + None, + /// open, only opening delimiter "(" or "<". + OpenDelimiter, + /// empty pair of delimiters "()" or "<>". + Delimiters, + /// full name of both type and variable. + FullPlaceholders, + }; + /// Configures code completion feature. struct { /// Whether code completion includes results that are not visible in current /// scopes. bool AllScopes = true; + /// controls the completion options for argument lists. + ArgumentListsPolicy ArgumentLists = ArgumentListsPolicy::FullPlaceholders; } Completion; /// Configures hover feature. diff --git a/clang-tools-extra/clangd/ConfigCompile.cpp b/clang-tools-extra/clangd/ConfigCompile.cpp index f32f674443ffe..58610a5b87922 100644 --- a/clang-tools-extra/clangd/ConfigCompile.cpp +++ b/clang-tools-extra/clangd/ConfigCompile.cpp @@ -622,6 +622,21 @@ struct FragmentCompiler { C.Completion.AllScopes = AllScopes; }); } + if (F.ArgumentLists) { + if (auto Val = + compileEnum("ArgumentLists", + *F.ArgumentLists) + .map("None", Config::ArgumentListsPolicy::None) + .map("OpenDelimiter", + Config::ArgumentListsPolicy::OpenDelimiter) + .map("Delimiters", Config::ArgumentListsPolicy::Delimiters) + .map("FullPlaceholders", + Config::ArgumentListsPolicy::FullPlaceholders) + .value()) + Out.Apply.push_back([Val](const Params &, Config &C) { + C.Completion.ArgumentLists = *Val; + }); + } } void compile(Fragment::HoverBlock &&F) { diff --git a/clang-tools-extra/clangd/ConfigFragment.h b/clang-tools-extra/clangd/ConfigFragment.h index f3e51a9b6dbc4..fc1b45f5d4c3e 100644 --- a/clang-tools-extra/clangd/ConfigFragment.h +++ b/clang-tools-extra/clangd/ConfigFragment.h @@ -32,6 +32,7 @@ #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_CONFIGFRAGMENT_H #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_CONFIGFRAGMENT_H +#include "Config.h" #include "ConfigProvider.h" #include "llvm/Support/SMLoc.h" #include "llvm/Support/SourceMgr.h" @@ -308,6 +309,13 @@ struct Fragment { /// Whether code completion should include suggestions from scopes that are /// not visible. The required scope prefix will be inserted. std::optional> AllScopes; + /// How to present the argument list between '()' and '<>': + /// valid values are enum Config::ArgumentListsPolicy values: + /// None: Nothing at all + /// OpenDelimiter: only opening delimiter "(" or "<" + /// Delimiters: empty pair of delimiters "()" or "<>" + /// FullPlaceholders: full name of both type and parameter + std::optional> ArgumentLists; }; CompletionBlock Completion; diff --git a/clang-tools-extra/clangd/ConfigYAML.cpp b/clang-tools-extra/clangd/ConfigYAML.cpp index 3e9b6a07d3b32..bcdda99eeed67 100644 --- a/clang-tools-extra/clangd/ConfigYAML.cpp +++ b/clang-tools-extra/clangd/ConfigYAML.cpp @@ -230,6 +230,10 @@ class Parser { if (auto AllScopes = boolValue(N, "AllScopes")) F.AllScopes = *AllScopes; }); + Dict.handle("ArgumentLists", [&](Node &N) { + if (auto ArgumentLists = scalarValue(N, "ArgumentLists")) + F.ArgumentLists = *ArgumentLists; + }); Dict.parse(N); } diff --git a/clang-tools-extra/clangd/FindSymbols.cpp b/clang-tools-extra/clangd/FindSymbols.cpp index cf2f8b62a2841..84bcbc1f2ddd3 100644 --- a/clang-tools-extra/clangd/FindSymbols.cpp +++ b/clang-tools-extra/clangd/FindSymbols.cpp @@ -111,7 +111,7 @@ getWorkspaceSymbols(llvm::StringRef Query, int Limit, *Req.Limit *= 5; } TopN Top( - Req.Limit ? *Req.Limit : std::numeric_limits::max()); + Req.Limit.value_or(std::numeric_limits::max())); FuzzyMatcher Filter(Req.Query); Index->fuzzyFind(Req, [HintPath, &Top, &Filter, AnyScope = Req.AnyScope, diff --git a/clang-tools-extra/clangd/Headers.cpp b/clang-tools-extra/clangd/Headers.cpp index 75f8668e7bef0..b537417bd1056 100644 --- a/clang-tools-extra/clangd/Headers.cpp +++ b/clang-tools-extra/clangd/Headers.cpp @@ -75,8 +75,8 @@ class IncludeStructure::RecordHeaders : public PPCallbacks { IDs.push_back(HID); } } - Out->MainFileIncludesBySpelling.try_emplace(Inc.Written) - .first->second.push_back(Out->MainFileIncludes.size() - 1); + Out->MainFileIncludesBySpelling[Inc.Written].push_back( + Out->MainFileIncludes.size() - 1); } // Record include graph (not just for main-file includes) diff --git a/clang-tools-extra/clangd/XRefs.cpp b/clang-tools-extra/clangd/XRefs.cpp index f94cadeffaa29..b34aba603b530 100644 --- a/clang-tools-extra/clangd/XRefs.cpp +++ b/clang-tools-extra/clangd/XRefs.cpp @@ -2282,8 +2282,7 @@ incomingCalls(const CallHierarchyItem &Item, const SymbolIndex *Index) { elog("incomingCalls failed to convert location: {0}", Loc.takeError()); return; } - auto It = CallsIn.try_emplace(R.Container, std::vector{}).first; - It->second.push_back(Loc->range); + CallsIn[R.Container].push_back(Loc->range); ContainerLookup.IDs.insert(R.Container); }); diff --git a/clang-tools-extra/clangd/index/MemIndex.cpp b/clang-tools-extra/clangd/index/MemIndex.cpp index fe0ee873018b3..2665d46b97d83 100644 --- a/clang-tools-extra/clangd/index/MemIndex.cpp +++ b/clang-tools-extra/clangd/index/MemIndex.cpp @@ -31,7 +31,7 @@ bool MemIndex::fuzzyFind( trace::Span Tracer("MemIndex fuzzyFind"); TopN> Top( - Req.Limit ? *Req.Limit : std::numeric_limits::max()); + Req.Limit.value_or(std::numeric_limits::max())); FuzzyMatcher Filter(Req.Query); bool More = false; for (const auto &Pair : Index) { diff --git a/clang-tools-extra/clangd/index/dex/Dex.cpp b/clang-tools-extra/clangd/index/dex/Dex.cpp index 19dc3080f9f89..b7d3063e19b49 100644 --- a/clang-tools-extra/clangd/index/dex/Dex.cpp +++ b/clang-tools-extra/clangd/index/dex/Dex.cpp @@ -264,7 +264,7 @@ bool Dex::fuzzyFind(const FuzzyFindRequest &Req, return LHS.second > RHS.second; }; TopN Top( - Req.Limit ? *Req.Limit : std::numeric_limits::max(), Compare); + Req.Limit.value_or(std::numeric_limits::max()), Compare); for (const auto &IDAndScore : IDAndScores) { const DocID SymbolDocID = IDAndScore.first; const auto *Sym = Symbols[SymbolDocID]; diff --git a/clang-tools-extra/clangd/index/remote/CMakeLists.txt b/clang-tools-extra/clangd/index/remote/CMakeLists.txt index 106bbeff84ccf..28df71855a142 100644 --- a/clang-tools-extra/clangd/index/remote/CMakeLists.txt +++ b/clang-tools-extra/clangd/index/remote/CMakeLists.txt @@ -19,7 +19,7 @@ if (CLANGD_ENABLE_REMOTE) # target-local? add_definitions(-DGOOGLE_PROTOBUF_NO_RTTI=1) - add_clang_library(clangdRemoteIndex + add_clang_library(clangdRemoteIndex STATIC Client.cpp LINK_LIBS diff --git a/clang-tools-extra/clangd/index/remote/marshalling/CMakeLists.txt b/clang-tools-extra/clangd/index/remote/marshalling/CMakeLists.txt index 071802a962647..d7e37003e5f98 100644 --- a/clang-tools-extra/clangd/index/remote/marshalling/CMakeLists.txt +++ b/clang-tools-extra/clangd/index/remote/marshalling/CMakeLists.txt @@ -1,4 +1,4 @@ -add_clang_library(clangdRemoteMarshalling +add_clang_library(clangdRemoteMarshalling STATIC Marshalling.cpp LINK_LIBS diff --git a/clang-tools-extra/clangd/index/remote/unimplemented/CMakeLists.txt b/clang-tools-extra/clangd/index/remote/unimplemented/CMakeLists.txt index 86d13c77ce874..5c4907f0d7a83 100644 --- a/clang-tools-extra/clangd/index/remote/unimplemented/CMakeLists.txt +++ b/clang-tools-extra/clangd/index/remote/unimplemented/CMakeLists.txt @@ -2,7 +2,7 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../../../) # When compiled without Remote Index support, the real implementation index # client is not present. Users will get a notification about this when trying # to connect to remote index server instance. -add_clang_library(clangdRemoteIndex +add_clang_library(clangdRemoteIndex STATIC UnimplementedClient.cpp LINK_LIBS diff --git a/clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt b/clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt index 2e948c23569f6..59475b0dfd3d2 100644 --- a/clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt +++ b/clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt @@ -29,6 +29,7 @@ add_clang_library(clangDaemonTweaks OBJECT RemoveUsingNamespace.cpp ScopifyEnum.cpp SpecialMembers.cpp + SwapBinaryOperands.cpp SwapIfBranches.cpp LINK_LIBS diff --git a/clang-tools-extra/clangd/refactor/tweaks/SwapBinaryOperands.cpp b/clang-tools-extra/clangd/refactor/tweaks/SwapBinaryOperands.cpp new file mode 100644 index 0000000000000..9ad0089a5d035 --- /dev/null +++ b/clang-tools-extra/clangd/refactor/tweaks/SwapBinaryOperands.cpp @@ -0,0 +1,217 @@ +//===--- SwapBinaryOperands.cpp ----------------------------------*- C++-*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +#include "ParsedAST.h" +#include "Protocol.h" +#include "Selection.h" +#include "SourceCode.h" +#include "refactor/Tweak.h" +#include "support/Logger.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Expr.h" +#include "clang/AST/OperationKinds.h" +#include "clang/AST/Stmt.h" +#include "clang/Basic/LLVM.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Tooling/Core/Replacement.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/FormatVariadic.h" +#include +#include + +namespace clang { +namespace clangd { +namespace { +/// Check whether it makes logical sense to swap operands to an operator. +/// Assignment or member access operators are rarely swappable +/// while keeping the meaning intact, whereas comparison operators, mathematical +/// operators, etc. are often desired to be swappable for readability, avoiding +/// bugs by assigning to nullptr when comparison was desired, etc. +bool isOpSwappable(const BinaryOperatorKind Opcode) { + switch (Opcode) { + case BinaryOperatorKind::BO_Mul: + case BinaryOperatorKind::BO_Add: + case BinaryOperatorKind::BO_LT: + case BinaryOperatorKind::BO_GT: + case BinaryOperatorKind::BO_LE: + case BinaryOperatorKind::BO_GE: + case BinaryOperatorKind::BO_EQ: + case BinaryOperatorKind::BO_NE: + case BinaryOperatorKind::BO_And: + case BinaryOperatorKind::BO_Xor: + case BinaryOperatorKind::BO_Or: + case BinaryOperatorKind::BO_LAnd: + case BinaryOperatorKind::BO_LOr: + case BinaryOperatorKind::BO_Comma: + return true; + // Noncommutative operators: + case BinaryOperatorKind::BO_Div: + case BinaryOperatorKind::BO_Sub: + case BinaryOperatorKind::BO_Shl: + case BinaryOperatorKind::BO_Shr: + case BinaryOperatorKind::BO_Rem: + // <=> is noncommutative + case BinaryOperatorKind::BO_Cmp: + // Member access: + case BinaryOperatorKind::BO_PtrMemD: + case BinaryOperatorKind::BO_PtrMemI: + // Assignment: + case BinaryOperatorKind::BO_Assign: + case BinaryOperatorKind::BO_MulAssign: + case BinaryOperatorKind::BO_DivAssign: + case BinaryOperatorKind::BO_RemAssign: + case BinaryOperatorKind::BO_AddAssign: + case BinaryOperatorKind::BO_SubAssign: + case BinaryOperatorKind::BO_ShlAssign: + case BinaryOperatorKind::BO_ShrAssign: + case BinaryOperatorKind::BO_AndAssign: + case BinaryOperatorKind::BO_XorAssign: + case BinaryOperatorKind::BO_OrAssign: + return false; + } + return false; +} + +/// Some operators are asymmetric and need to be flipped when swapping their +/// operands +/// @param[out] Opcode the opcode to potentially swap +/// If the opcode does not need to be swapped or is not swappable, does nothing +BinaryOperatorKind swapOperator(const BinaryOperatorKind Opcode) { + switch (Opcode) { + case BinaryOperatorKind::BO_LT: + return BinaryOperatorKind::BO_GT; + + case BinaryOperatorKind::BO_GT: + return BinaryOperatorKind::BO_LT; + + case BinaryOperatorKind::BO_LE: + return BinaryOperatorKind::BO_GE; + + case BinaryOperatorKind::BO_GE: + return BinaryOperatorKind::BO_LE; + + case BinaryOperatorKind::BO_Mul: + case BinaryOperatorKind::BO_Add: + case BinaryOperatorKind::BO_Cmp: + case BinaryOperatorKind::BO_EQ: + case BinaryOperatorKind::BO_NE: + case BinaryOperatorKind::BO_And: + case BinaryOperatorKind::BO_Xor: + case BinaryOperatorKind::BO_Or: + case BinaryOperatorKind::BO_LAnd: + case BinaryOperatorKind::BO_LOr: + case BinaryOperatorKind::BO_Comma: + case BinaryOperatorKind::BO_Div: + case BinaryOperatorKind::BO_Sub: + case BinaryOperatorKind::BO_Shl: + case BinaryOperatorKind::BO_Shr: + case BinaryOperatorKind::BO_Rem: + case BinaryOperatorKind::BO_PtrMemD: + case BinaryOperatorKind::BO_PtrMemI: + case BinaryOperatorKind::BO_Assign: + case BinaryOperatorKind::BO_MulAssign: + case BinaryOperatorKind::BO_DivAssign: + case BinaryOperatorKind::BO_RemAssign: + case BinaryOperatorKind::BO_AddAssign: + case BinaryOperatorKind::BO_SubAssign: + case BinaryOperatorKind::BO_ShlAssign: + case BinaryOperatorKind::BO_ShrAssign: + case BinaryOperatorKind::BO_AndAssign: + case BinaryOperatorKind::BO_XorAssign: + case BinaryOperatorKind::BO_OrAssign: + return Opcode; + } + llvm_unreachable("Unknown BinaryOperatorKind enum"); +} + +/// Swaps the operands to a binary operator +/// Before: +/// x != nullptr +/// ^ ^^^^^^^ +/// After: +/// nullptr != x +class SwapBinaryOperands : public Tweak { +public: + const char *id() const final; + + bool prepare(const Selection &Inputs) override; + Expected apply(const Selection &Inputs) override; + std::string title() const override { + return llvm::formatv("Swap operands to {0}", + Op ? Op->getOpcodeStr() : "binary operator"); + } + llvm::StringLiteral kind() const override { + return CodeAction::REFACTOR_KIND; + } + bool hidden() const override { return false; } + +private: + const BinaryOperator *Op; +}; + +REGISTER_TWEAK(SwapBinaryOperands) + +bool SwapBinaryOperands::prepare(const Selection &Inputs) { + for (const SelectionTree::Node *N = Inputs.ASTSelection.commonAncestor(); + N && !Op; N = N->Parent) { + // Stop once we hit a block, e.g. a lambda in one of the operands. + // This makes sure that the selection point is in the 'scope' of the binary + // operator, not from somewhere inside a lambda for example + // (5 < [](){ ^return 1; }) + if (llvm::isa_and_nonnull(N->ASTNode.get())) + return false; + Op = dyn_cast_or_null(N->ASTNode.get()); + // If we hit upon a nonswappable binary operator, ignore and keep going + if (Op && !isOpSwappable(Op->getOpcode())) { + Op = nullptr; + } + } + return Op != nullptr; +} + +Expected SwapBinaryOperands::apply(const Selection &Inputs) { + const auto &Ctx = Inputs.AST->getASTContext(); + const auto &SrcMgr = Inputs.AST->getSourceManager(); + + const auto LHSRng = toHalfOpenFileRange(SrcMgr, Ctx.getLangOpts(), + Op->getLHS()->getSourceRange()); + if (!LHSRng) + return error( + "Could not obtain range of the 'lhs' of the operator. Macros?"); + const auto RHSRng = toHalfOpenFileRange(SrcMgr, Ctx.getLangOpts(), + Op->getRHS()->getSourceRange()); + if (!RHSRng) + return error( + "Could not obtain range of the 'rhs' of the operator. Macros?"); + const auto OpRng = + toHalfOpenFileRange(SrcMgr, Ctx.getLangOpts(), Op->getOperatorLoc()); + if (!OpRng) + return error("Could not obtain range of the operator itself. Macros?"); + + const auto LHSCode = toSourceCode(SrcMgr, *LHSRng); + const auto RHSCode = toSourceCode(SrcMgr, *RHSRng); + const auto OperatorCode = toSourceCode(SrcMgr, *OpRng); + + tooling::Replacements Result; + if (auto Err = Result.add(tooling::Replacement( + Ctx.getSourceManager(), LHSRng->getBegin(), LHSCode.size(), RHSCode))) + return std::move(Err); + if (auto Err = Result.add(tooling::Replacement( + Ctx.getSourceManager(), RHSRng->getBegin(), RHSCode.size(), LHSCode))) + return std::move(Err); + const auto SwappedOperator = swapOperator(Op->getOpcode()); + if (auto Err = Result.add(tooling::Replacement( + Ctx.getSourceManager(), OpRng->getBegin(), OperatorCode.size(), + Op->getOpcodeStr(SwappedOperator)))) + return std::move(Err); + return Effect::mainFileEdit(SrcMgr, std::move(Result)); +} + +} // namespace +} // namespace clangd +} // namespace clang diff --git a/clang-tools-extra/clangd/support/CMakeLists.txt b/clang-tools-extra/clangd/support/CMakeLists.txt index 506a3f2c8551d..c0be846ebbbf4 100644 --- a/clang-tools-extra/clangd/support/CMakeLists.txt +++ b/clang-tools-extra/clangd/support/CMakeLists.txt @@ -15,7 +15,7 @@ if(NOT HAVE_CXX_ATOMICS_WITHOUT_LIB OR NOT HAVE_CXX_ATOMICS64_WITHOUT_LIB) list(APPEND CLANGD_ATOMIC_LIB "atomic") endif() -add_clang_library(clangdSupport +add_clang_library(clangdSupport STATIC Bracket.cpp Cancellation.cpp Context.cpp diff --git a/clang-tools-extra/clangd/tool/CMakeLists.txt b/clang-tools-extra/clangd/tool/CMakeLists.txt index 4012b6401c008..850a82833e444 100644 --- a/clang-tools-extra/clangd/tool/CMakeLists.txt +++ b/clang-tools-extra/clangd/tool/CMakeLists.txt @@ -1,7 +1,7 @@ # Needed by LLVM's CMake checks because this file defines multiple targets. set(LLVM_OPTIONAL_SOURCES ClangdToolMain.cpp) -add_clang_library(clangdMain +add_clang_library(clangdMain STATIC ClangdMain.cpp Check.cpp ) diff --git a/clang-tools-extra/clangd/tool/ClangdMain.cpp b/clang-tools-extra/clangd/tool/ClangdMain.cpp index 1b669c50fa31a..cc061e2d93231 100644 --- a/clang-tools-extra/clangd/tool/ClangdMain.cpp +++ b/clang-tools-extra/clangd/tool/ClangdMain.cpp @@ -242,13 +242,13 @@ opt FallbackStyle{ init(clang::format::DefaultFallbackStyle), }; -opt EnableFunctionArgSnippets{ +opt EnableFunctionArgSnippets{ "function-arg-placeholders", cat(Features), - desc("When disabled, completions contain only parentheses for " - "function calls. When enabled, completions also contain " + desc("When disabled (0), completions contain only parentheses for " + "function calls. When enabled (1), completions also contain " "placeholders for method parameters"), - init(CodeCompleteOptions().EnableFunctionArgSnippets), + init(-1), }; opt HeaderInsertion{ @@ -650,6 +650,7 @@ class FlagsConfigProvider : public config::Provider { std::optional CDBSearch; std::optional IndexSpec; std::optional BGPolicy; + std::optional ArgumentLists; // If --compile-commands-dir arg was invoked, check value and override // default path. @@ -694,6 +695,12 @@ class FlagsConfigProvider : public config::Provider { BGPolicy = Config::BackgroundPolicy::Skip; } + if (EnableFunctionArgSnippets >= 0) { + ArgumentLists = EnableFunctionArgSnippets + ? Config::ArgumentListsPolicy::FullPlaceholders + : Config::ArgumentListsPolicy::Delimiters; + } + Frag = [=](const config::Params &, Config &C) { if (CDBSearch) C.CompileFlags.CDBSearch = *CDBSearch; @@ -701,6 +708,8 @@ class FlagsConfigProvider : public config::Provider { C.Index.External = *IndexSpec; if (BGPolicy) C.Index.Background = *BGPolicy; + if (ArgumentLists) + C.Completion.ArgumentLists = *ArgumentLists; if (AllScopesCompletion.getNumOccurrences()) C.Completion.AllScopes = AllScopesCompletion; @@ -916,7 +925,6 @@ clangd accepts flags on the commandline, and in the CLANGD_FLAGS environment var Opts.CodeComplete.IncludeIndicator.Insert.clear(); Opts.CodeComplete.IncludeIndicator.NoInsert.clear(); } - Opts.CodeComplete.EnableFunctionArgSnippets = EnableFunctionArgSnippets; Opts.CodeComplete.RunParser = CodeCompletionParse; Opts.CodeComplete.RankingModel = RankingModel; // FIXME: If we're using C++20 modules, force the lookup process to load diff --git a/clang-tools-extra/clangd/unittests/CMakeLists.txt b/clang-tools-extra/clangd/unittests/CMakeLists.txt index 4fa9f18407ae9..dffdcd5d014ca 100644 --- a/clang-tools-extra/clangd/unittests/CMakeLists.txt +++ b/clang-tools-extra/clangd/unittests/CMakeLists.txt @@ -137,6 +137,7 @@ add_unittest(ClangdUnitTests ClangdTests tweaks/ScopifyEnumTests.cpp tweaks/ShowSelectionTreeTests.cpp tweaks/SpecialMembersTests.cpp + tweaks/SwapBinaryOperandsTests.cpp tweaks/SwapIfBranchesTests.cpp tweaks/TweakTesting.cpp tweaks/TweakTests.cpp diff --git a/clang-tools-extra/clangd/unittests/ClangdLSPServerTests.cpp b/clang-tools-extra/clangd/unittests/ClangdLSPServerTests.cpp index 75a140767035b..49a94045ea487 100644 --- a/clang-tools-extra/clangd/unittests/ClangdLSPServerTests.cpp +++ b/clang-tools-extra/clangd/unittests/ClangdLSPServerTests.cpp @@ -262,6 +262,22 @@ TEST_F(LSPTest, ClangTidyRename) { EXPECT_EQ(Params, std::vector{llvm::json::Value(std::move(ExpectedEdit))}); } +TEST_F(LSPTest, ClangTidyCrash_Issue109367) { + // This test requires clang-tidy checks to be linked in. + if (!CLANGD_TIDY_CHECKS) + return; + Opts.ClangTidyProvider = [](tidy::ClangTidyOptions &ClangTidyOpts, + llvm::StringRef) { + ClangTidyOpts.Checks = {"-*,boost-use-ranges"}; + }; + // Check that registering the boost-use-ranges checker's matchers + // on two different threads does not cause a crash. + auto &Client = start(); + Client.didOpen("a.cpp", ""); + Client.didOpen("b.cpp", ""); + Client.sync(); +} + TEST_F(LSPTest, IncomingCalls) { Annotations Code(R"cpp( void calle^e(int); diff --git a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp index 96d1ee1f0add7..a89f499736226 100644 --- a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp +++ b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp @@ -11,6 +11,7 @@ #include "ClangdServer.h" #include "CodeComplete.h" #include "Compiler.h" +#include "Config.h" #include "Feature.h" #include "Matchers.h" #include "Protocol.h" @@ -2595,10 +2596,10 @@ TEST(SignatureHelpTest, DynamicIndexDocumentation) { ElementsAre(AllOf(sig("foo() -> int"), sigDoc("Member doc")))); } -TEST(CompletionTest, CompletionFunctionArgsDisabled) { +TEST(CompletionTest, ArgumentListsPolicy) { CodeCompleteOptions Opts; Opts.EnableSnippets = true; - Opts.EnableFunctionArgSnippets = false; + Opts.ArgumentLists = Config::ArgumentListsPolicy::Delimiters; { auto Results = completions( @@ -2670,6 +2671,26 @@ TEST(CompletionTest, CompletionFunctionArgsDisabled) { EXPECT_THAT(Results.Completions, UnorderedElementsAre(AllOf( named("FOO"), snippetSuffix("($0)")))); } + { + Opts.ArgumentLists = Config::ArgumentListsPolicy::None; + auto Results = completions( + R"cpp( + void xfoo(int x, int y); + void f() { xfo^ })cpp", + {}, Opts); + EXPECT_THAT(Results.Completions, + UnorderedElementsAre(AllOf(named("xfoo"), snippetSuffix("")))); + } + { + Opts.ArgumentLists = Config::ArgumentListsPolicy::OpenDelimiter; + auto Results = completions( + R"cpp( + void xfoo(int x, int y); + void f() { xfo^ })cpp", + {}, Opts); + EXPECT_THAT(Results.Completions, + UnorderedElementsAre(AllOf(named("xfoo"), snippetSuffix("(")))); + } } TEST(CompletionTest, SuggestOverrides) { diff --git a/clang-tools-extra/clangd/unittests/tweaks/SwapBinaryOperandsTests.cpp b/clang-tools-extra/clangd/unittests/tweaks/SwapBinaryOperandsTests.cpp new file mode 100644 index 0000000000000..e157bbefbdaaf --- /dev/null +++ b/clang-tools-extra/clangd/unittests/tweaks/SwapBinaryOperandsTests.cpp @@ -0,0 +1,92 @@ +//===-- SwapBinaryOperandsTests.cpp -----------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "TweakTesting.h" +#include "gmock/gmock-matchers.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +namespace clang { +namespace clangd { +namespace { + +TWEAK_TEST(SwapBinaryOperands); + +TEST_F(SwapBinaryOperandsTest, Test) { + Context = Function; + EXPECT_EQ(apply("int *p = nullptr; bool c = ^p == nullptr;"), + "int *p = nullptr; bool c = nullptr == p;"); + EXPECT_EQ(apply("int *p = nullptr; bool c = p ^== nullptr;"), + "int *p = nullptr; bool c = nullptr == p;"); + EXPECT_EQ(apply("int x = 3; bool c = ^x >= 5;"), + "int x = 3; bool c = 5 <= x;"); + EXPECT_EQ(apply("int x = 3; bool c = x >^= 5;"), + "int x = 3; bool c = 5 <= x;"); + EXPECT_EQ(apply("int x = 3; bool c = x >=^ 5;"), + "int x = 3; bool c = 5 <= x;"); + EXPECT_EQ(apply("int x = 3; bool c = x >=^ 5;"), + "int x = 3; bool c = 5 <= x;"); + EXPECT_EQ(apply("int f(); int x = 3; bool c = x >=^ f();"), + "int f(); int x = 3; bool c = f() <= x;"); + EXPECT_EQ(apply(R"cpp( + int f(); + #define F f + int x = 3; bool c = x >=^ F(); + )cpp"), + R"cpp( + int f(); + #define F f + int x = 3; bool c = F() <= x; + )cpp"); + EXPECT_EQ(apply(R"cpp( + int f(); + #define F f() + int x = 3; bool c = x >=^ F; + )cpp"), + R"cpp( + int f(); + #define F f() + int x = 3; bool c = F <= x; + )cpp"); + EXPECT_EQ(apply(R"cpp( + int f(bool); + #define F(v) f(v) + int x = 0; + bool c = F(x^ < 5); + )cpp"), + R"cpp( + int f(bool); + #define F(v) f(v) + int x = 0; + bool c = F(5 > x); + )cpp"); + ExtraArgs = {"-std=c++20"}; + Context = CodeContext::File; + EXPECT_UNAVAILABLE(R"cpp( + namespace std { + struct strong_ordering { + int val; + static const strong_ordering less; + static const strong_ordering equivalent; + static const strong_ordering equal; + static const strong_ordering greater; + }; + inline constexpr strong_ordering strong_ordering::less {-1}; + inline constexpr strong_ordering strong_ordering::equivalent {0}; + inline constexpr strong_ordering strong_ordering::equal {0}; + inline constexpr strong_ordering strong_ordering::greater {1}; + }; + #define F(v) v + int x = 0; + auto c = F(5^ <=> x); + )cpp"); +} + +} // namespace +} // namespace clangd +} // namespace clang diff --git a/clang-tools-extra/clangd/xpc/CMakeLists.txt b/clang-tools-extra/clangd/xpc/CMakeLists.txt index b35c509a3f53d..f05d949646731 100644 --- a/clang-tools-extra/clangd/xpc/CMakeLists.txt +++ b/clang-tools-extra/clangd/xpc/CMakeLists.txt @@ -14,12 +14,12 @@ set(LLVM_LINK_COMPONENTS # Needed by LLVM's CMake checks because this file defines multiple targets. set(LLVM_OPTIONAL_SOURCES Conversion.cpp XPCTransport.cpp) -add_clang_library(clangdXpcJsonConversions +add_clang_library(clangdXpcJsonConversions STATIC Conversion.cpp LINK_LIBS clangDaemon clangdSupport ) -add_clang_library(clangdXpcTransport +add_clang_library(clangdXpcTransport STATIC XPCTransport.cpp LINK_LIBS clangDaemon clangdSupport clangdXpcJsonConversions DEPENDS ClangDriverOptions diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index 702a8d2a3576b..3f7bcde1eb301 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -74,6 +74,8 @@ Code completion Code actions ^^^^^^^^^^^^ +- Added `Swap operands` tweak for certain binary operators. + Signature help ^^^^^^^^^^^^^^ @@ -97,18 +99,34 @@ The improvements are... Improvements to clang-tidy -------------------------- +- Improved :program:`clang-tidy`'s `--verify-config` flag by adding support for + the configuration options of the `Clang Static Analyzer Checks + `_. + - Improved :program:`run-clang-tidy.py` script. Fixed minor shutdown noise happening on certain platforms when interrupting the script. New checks ^^^^^^^^^^ +- New :doc:`bugprone-bitwise-pointer-cast + ` check. + + Warns about code that tries to cast between pointers by means of + ``std::bit_cast`` or ``memcpy``. + - New :doc:`bugprone-tagged-union-member-count ` check. Gives warnings for tagged unions, where the number of tags is different from the number of data members inside the union. +- New :doc:`portability-template-virtual-member-function + ` check. + + Finds cases when an uninstantiated virtual member function in a template class + causes cross-compiler incompatibility. + New check aliases ^^^^^^^^^^^^^^^^^ @@ -171,6 +189,10 @@ Changes in existing checks as a replacement for parameters of incomplete C array type in C++20 and ``std::array`` or ``std::vector`` before C++20. +- Improved :doc:`modernize-loop-convert + ` check to fix false positive when + using loop variable in initializer of lambda capture. + - Improved :doc:`modernize-min-max-use-initializer-list ` check by fixing a false positive when only an implicit conversion happened inside an @@ -182,16 +204,22 @@ Changes in existing checks - Improved :doc:`modernize-use-std-format ` check to support replacing - member function calls too. + member function calls too and to only expand macros starting with ``PRI`` + and ``__PRI`` from ```` in the format string. - Improved :doc:`modernize-use-std-print ` check to support replacing - member function calls too. + member function calls too and to only expand macros starting with ``PRI`` + and ``__PRI`` from ```` in the format string. - Improved :doc:`performance-avoid-endl ` check to use ``std::endl`` as placeholder when lexer cannot get source text. +- Improved :doc:`performance-move-const-arg + ` check to fix a crash when + an argument type is declared but not defined. + - Improved :doc:`readability-container-contains ` check to let it work on any class that has a ``contains`` method. diff --git a/clang-tools-extra/docs/clang-tidy/checks/bugprone/bitwise-pointer-cast.rst b/clang-tools-extra/docs/clang-tidy/checks/bugprone/bitwise-pointer-cast.rst new file mode 100644 index 0000000000000..ac58654421a0a --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/bugprone/bitwise-pointer-cast.rst @@ -0,0 +1,52 @@ +.. title:: clang-tidy - bugprone-bitwise-pointer-cast + +bugprone-bitwise-pointer-cast +============================= + +Warns about code that tries to cast between pointers by means of +``std::bit_cast`` or ``memcpy``. + +The motivation is that ``std::bit_cast`` is advertised as the safe alternative +to type punning via ``reinterpret_cast`` in modern C++. However, one should not +blindly replace ``reinterpret_cast`` with ``std::bit_cast``, as follows: + +.. code-block:: c++ + + int x{}; + -float y = *reinterpret_cast(&x); + +float y = *std::bit_cast(&x); + +The drop-in replacement behaves exactly the same as ``reinterpret_cast``, and +Undefined Behavior is still invoked. ``std::bit_cast`` is copying the bytes of +the input pointer, not the pointee, into an output pointer of a different type, +which may violate the strict aliasing rules. However, simply looking at the +code, it looks "safe", because it uses ``std::bit_cast`` which is advertised as +safe. + +The solution to safe type punning is to apply ``std::bit_cast`` on value types, +not on pointer types: + +.. code-block:: c++ + + int x{}; + float y = std::bit_cast(x); + +This way, the bytes of the input object are copied into the output object, which +is much safer. Do note that Undefined Behavior can still occur, if there is no +value of type ``To`` corresponding to the value representation produced. +Compilers may be able to optimize this copy and generate identical assembly to +the original ``reinterpret_cast`` version. + +Code before C++20 may backport ``std::bit_cast`` by means of ``memcpy``, or +simply call ``memcpy`` directly, which is equally problematic. This is also +detected by this check: + +.. code-block:: c++ + + int* x{}; + float* y{}; + std::memcpy(&y, &x, sizeof(x)); + +Alternatively, if a cast between pointers is truly wanted, ``reinterpret_cast`` +should be used, to clearly convey the intent and enable warnings from compilers +and linters, which should be addressed accordingly. diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst index e3dfabba8fad1..0082234f5ed31 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/list.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -81,6 +81,7 @@ Clang-Tidy Checks :doc:`bugprone-assert-side-effect `, :doc:`bugprone-assignment-in-if-condition `, :doc:`bugprone-bad-signal-to-kill-thread `, + :doc:`bugprone-bitwise-pointer-cast `, :doc:`bugprone-bool-pointer-implicit-conversion `, "Yes" :doc:`bugprone-branch-clone `, :doc:`bugprone-casting-through-void `, @@ -347,6 +348,7 @@ Clang-Tidy Checks :doc:`portability-restrict-system-includes `, "Yes" :doc:`portability-simd-intrinsics `, :doc:`portability-std-allocator-const `, + :doc:`portability-template-virtual-member-function `, :doc:`readability-avoid-const-params-in-decls `, "Yes" :doc:`readability-avoid-nested-conditional-operator `, :doc:`readability-avoid-return-with-void-value `, "Yes" diff --git a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-std-format.rst b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-std-format.rst index b88fde5162e28..cfa11d3cac8bf 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-std-format.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-std-format.rst @@ -23,7 +23,8 @@ into: The check uses the same format-string-conversion algorithm as `modernize-use-std-print <../modernize/use-std-print.html>`_ and its -shortcomings are described in the documentation for that check. +shortcomings and behaviour in combination with macros are described in the +documentation for that check. Options ------- diff --git a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-std-print.rst b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-std-print.rst index e70402ad8b334..0cf51e3961a05 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-std-print.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-std-print.rst @@ -24,8 +24,15 @@ into: std::println(stderr, "The {} is {:3}", description, value); If the `ReplacementPrintFunction` or `ReplacementPrintlnFunction` options -are left, or assigned to their default values then this check is only -enabled with `-std=c++23` or later. +are left at or set to their default values then this check is only enabled +with `-std=c++23` or later. + +Macros starting with ``PRI`` and ``__PRI`` from `` are +expanded, escaping is handled and adjacent strings are concatenated to form +a single ``StringLiteral`` before the format string is converted. Use of +any other macros in the format string will cause a warning message to be +emitted and no conversion will be performed. The converted format string +will always be a single string literal. The check doesn't do a bad job, but it's not perfect. In particular: @@ -34,13 +41,10 @@ The check doesn't do a bad job, but it's not perfect. In particular: possible. - At the point that the check runs, the AST contains a single - ``StringLiteral`` for the format string and any macro expansion, token - pasting, adjacent string literal concatenation and escaping has been - handled. Although it's possible for the check to automatically put the - escapes back, they may not be exactly as they were written (e.g. - ``"\x0a"`` will become ``"\n"`` and ``"ab" "cd"`` will become - ``"abcd"``.) This is helpful since it means that the ``PRIx`` macros from - ```` are removed correctly. + ``StringLiteral`` for the format string where escapes have been expanded. + The check tries to reconstruct escape sequences, they may not be the same + as they were written (e.g. ``"\x41\x0a"`` will become ``"A\n"`` and + ``"ab" "cd"`` will become ``"abcd"``.) - It supports field widths, precision, positional arguments, leading zeros, leading ``+``, alignment and alternative forms. diff --git a/clang-tools-extra/docs/clang-tidy/checks/portability/template-virtual-member-function.rst b/clang-tools-extra/docs/clang-tidy/checks/portability/template-virtual-member-function.rst new file mode 100644 index 0000000000000..aa3ed6653b475 --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/portability/template-virtual-member-function.rst @@ -0,0 +1,37 @@ +.. title:: clang-tidy - portability-template-virtual-member-function + +portability-template-virtual-member-function +============================================ + +Finds cases when an uninstantiated virtual member function in a template class causes +cross-compiler incompatibility. + +Upon instantiating a template class, non-virtual member functions don't have to be +instantiated unless they are used. Virtual member function instantiation on the other hand +is unspecified and depends on the implementation of the compiler. + +In the following snippets the virtual member function is not instantiated by GCC and Clang, +but it is instantiated by MSVC, so while the snippet is accepted by the former compilers, +it is rejected by the latter. + +.. code:: c++ + + template + struct CrossPlatformError { + virtual ~CrossPlatformError() = default; + + static void used() {} + + virtual void unused() { + T MSVCError = this; + }; + }; + + int main() { + CrossPlatformError::used(); + return 0; + } + +Cross-platform projects that need to support MSVC on Windows might see compiler errors +because certain virtual member functions are instantiated, which are not instantiated +by other compilers on other platforms. This check highlights such virtual member functions. diff --git a/clang-tools-extra/docs/clang-tidy/index.rst b/clang-tools-extra/docs/clang-tidy/index.rst index e38141bdb8be1..a4233d5d8e269 100644 --- a/clang-tools-extra/docs/clang-tidy/index.rst +++ b/clang-tools-extra/docs/clang-tidy/index.rst @@ -287,7 +287,7 @@ An overview of all the command-line options: FormatStyle - Same as '--format-style'. HeaderFileExtensions - File extensions to consider to determine if a given diagnostic is located in a header file. - HeaderFilterRegex - Same as '--header-filter-regex'. + HeaderFilterRegex - Same as '--header-filter'. ImplementationFileExtensions - File extensions to consider to determine if a given diagnostic is located in an implementation file. diff --git a/clang-tools-extra/include-cleaner/lib/CMakeLists.txt b/clang-tools-extra/include-cleaner/lib/CMakeLists.txt index 208791a1a7f1e..7ad5325f6026d 100644 --- a/clang-tools-extra/include-cleaner/lib/CMakeLists.txt +++ b/clang-tools-extra/include-cleaner/lib/CMakeLists.txt @@ -1,6 +1,6 @@ set(LLVM_LINK_COMPONENTS Support) -add_clang_library(clangIncludeCleaner +add_clang_library(clangIncludeCleaner STATIC Analysis.cpp IncludeSpeller.cpp FindHeaders.cpp diff --git a/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/inttypes.h b/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/inttypes.h index 9dc7ae39b3a3f..74437f405931b 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/inttypes.h +++ b/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/inttypes.h @@ -21,40 +21,46 @@ typedef __UINT32_TYPE__ uint32_t; typedef __UINT16_TYPE__ uint16_t; typedef __UINT8_TYPE__ uint8_t; -#define PRIdMAX "lld" -#define PRId64 "lld" +#if __WORDSIZE == 64 +# define __PRI64_PREFIX "l" +#else +# define __PRI64_PREFIX "ll" +#endif + +#define PRIdMAX __PRI64_PREFIX "d" +#define PRId64 __PRI64_PREFIX "d" #define PRId32 "d" #define PRId16 "hd" #define PRId8 "hhd" -#define PRIiMAX "lli" -#define PRIi64 "lli" +#define PRIiMAX __PRI64_PREFIX "i" +#define PRIi64 __PRI64_PREFIX "i" #define PRIi32 "i" #define PRIi16 "hi" #define PRIi8 "hhi" -#define PRIiFAST64 "lli" +#define PRIiFAST64 __PRI64_PREFIX "i" #define PRIiFAST32 "i" #define PRIiFAST16 "hi" #define PRIiFAST8 "hhi" -#define PRIiLEAST64 "lli" +#define PRIiLEAST64 __PRI64_PREFIX "i" #define PRIiLEAST32 "i" #define PRIiLEAST16 "hi" #define PRIiLEAST8 "hhi" -#define PRIuMAX "llu" -#define PRIu64 "llu" +#define PRIuMAX __PRI64_PREFIX "u" +#define PRIu64 __PRI64_PREFIX "u" #define PRIu32 "u" #define PRIu16 "hu" #define PRIu8 "hhu" -#define PRIuFAST64 "llu" +#define PRIuFAST64 __PRI64_PREFIX "u" #define PRIuFAST32 "u" #define PRIuFAST16 "hu" #define PRIuFAST8 "hhu" -#define PRIuLEAST64 "llu" +#define PRIuLEAST64 __PRI64_PREFIX "u" #define PRIuLEAST32 "u" #define PRIuLEAST16 "hu" #define PRIuLEAST8 "hhu" diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/bitwise-pointer-cast-cxx20.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/bitwise-pointer-cast-cxx20.cpp new file mode 100644 index 0000000000000..6f7b827a980ae --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/bitwise-pointer-cast-cxx20.cpp @@ -0,0 +1,63 @@ +// RUN: %check_clang_tidy -std=c++20 %s bugprone-bitwise-pointer-cast %t + +void memcpy(void* to, void* dst, unsigned long long size) +{ + // Dummy implementation for the purpose of the test +} + +namespace std +{ +template +To bit_cast(From from) +{ + // Dummy implementation for the purpose of the test + To to{}; + return to; +} + +using ::memcpy; +} + +void pointer2pointer() +{ + int x{}; + float bad = *std::bit_cast(&x); // UB, but looks safe due to std::bit_cast + // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: do not use 'std::bit_cast' to cast between pointers [bugprone-bitwise-pointer-cast] + float good = std::bit_cast(x); // Well-defined + + using IntPtr = int*; + using FloatPtr = float*; + IntPtr x2{}; + float bad2 = *std::bit_cast(x2); + // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: do not use 'std::bit_cast' to cast between pointers [bugprone-bitwise-pointer-cast] +} + +void pointer2pointer_memcpy() +{ + int x{}; + int* px{}; + float y{}; + float* py{}; + + memcpy(&py, &px, sizeof(px)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: do not use 'memcpy' to cast between pointers [bugprone-bitwise-pointer-cast] + std::memcpy(&py, &px, sizeof(px)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: do not use 'memcpy' to cast between pointers [bugprone-bitwise-pointer-cast] + + std::memcpy(&y, &x, sizeof(x)); +} + +// Pointer-integer conversions are allowed by this check +void int2pointer() +{ + unsigned long long addr{}; + float* p = std::bit_cast(addr); + std::memcpy(&p, &addr, sizeof(addr)); +} + +void pointer2int() +{ + float* p{}; + auto addr = std::bit_cast(p); + std::memcpy(&addr, &p, sizeof(p)); +} diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/bitwise-pointer-cast.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/bitwise-pointer-cast.cpp new file mode 100644 index 0000000000000..85434fe509b98 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/bitwise-pointer-cast.cpp @@ -0,0 +1,41 @@ +// RUN: %check_clang_tidy %s bugprone-bitwise-pointer-cast %t + +void memcpy(void* to, void* dst, unsigned long long size) +{ + // Dummy implementation for the purpose of the test +} + +namespace std +{ +using ::memcpy; +} + +void pointer2pointer() +{ + int x{}; + int* px{}; + float y{}; + float* py{}; + + memcpy(&py, &px, sizeof(px)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: do not use 'memcpy' to cast between pointers [bugprone-bitwise-pointer-cast] + std::memcpy(&py, &px, sizeof(px)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: do not use 'memcpy' to cast between pointers [bugprone-bitwise-pointer-cast] + + std::memcpy(&y, &x, sizeof(x)); +} + +// Pointer-integer conversions are allowed by this check +void int2pointer() +{ + unsigned long long addr{}; + float* p{}; + std::memcpy(&p, &addr, sizeof(addr)); +} + +void pointer2int() +{ + unsigned long long addr{}; + float* p{}; + std::memcpy(&addr, &p, sizeof(p)); +} diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/loop-convert-basic.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/loop-convert-basic.cpp index 695925a5d877c..df2a2c1af1f54 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/modernize/loop-convert-basic.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/loop-convert-basic.cpp @@ -980,3 +980,30 @@ namespace PR78381 { } } } + +namespace GH109083 { +void test() { + const int N = 6; + int Arr[N] = {1, 2, 3, 4, 5, 6}; + + for (int I = 0; I < N; ++I) { + auto V = [T = Arr[I]]() {}; + } + // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead [modernize-loop-convert] + // CHECK-FIXES: for (int I : Arr) + // CHECK-FIXES-NEXT: auto V = [T = I]() {}; + for (int I = 0; I < N; ++I) { + auto V = [T = 10 + Arr[I]]() {}; + } + // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead [modernize-loop-convert] + // CHECK-FIXES: for (int I : Arr) + // CHECK-FIXES-NEXT: auto V = [T = 10 + I]() {}; + + for (int I = 0; I < N; ++I) { + auto V = [T = I]() {}; + } + for (int I = 0; I < N; ++I) { + auto V = [T = I + 10]() {}; + } +} +} // namespace GH109083 diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-format.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-format.cpp index 800a95062e8f1..42fb3382e4a93 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-format.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-format.cpp @@ -1,13 +1,18 @@ // RUN: %check_clang_tidy \ // RUN: -std=c++20 %s modernize-use-std-format %t -- \ // RUN: -config="{CheckOptions: {StrictMode: true}}" \ -// RUN: -- -isystem %clang_tidy_headers +// RUN: -- -isystem %clang_tidy_headers \ +// RUN: -DPRI_CMDLINE_MACRO="\"s\"" \ +// RUN: -D__PRI_CMDLINE_MACRO="\"s\"" // RUN: %check_clang_tidy \ // RUN: -std=c++20 %s modernize-use-std-format %t -- \ // RUN: -config="{CheckOptions: {StrictMode: false}}" \ -// RUN: -- -isystem %clang_tidy_headers +// RUN: -- -isystem %clang_tidy_headers \ +// RUN: -DPRI_CMDLINE_MACRO="\"s\"" \ +// RUN: -D__PRI_CMDLINE_MACRO="\"s\"" #include // CHECK-FIXES: #include +#include namespace absl { @@ -102,13 +107,6 @@ std::string StrFormat_macros() { // CHECK-MESSAGES: [[@LINE-1]]:13: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format] // CHECK-FIXES: std::format("Hello {}", 42); - // The format string is replaced even though it comes from a macro, this - // behaviour is required so that that macros are replaced. -#define FORMAT_STRING "Hello %s" - auto s2 = absl::StrFormat(FORMAT_STRING, 42); - // CHECK-MESSAGES: [[@LINE-1]]:13: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format] - // CHECK-FIXES: std::format("Hello {}", 42); - // Arguments that are macros aren't replaced with their value, even if they are rearranged. #define VALUE 3.14159265358979323846 #define WIDTH 10 @@ -116,4 +114,67 @@ std::string StrFormat_macros() { auto s3 = absl::StrFormat("Hello %*.*f", WIDTH, PRECISION, VALUE); // CHECK-MESSAGES: [[@LINE-1]]:13: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format] // CHECK-FIXES: std::format("Hello {:{}.{}f}", VALUE, WIDTH, PRECISION); + + const uint64_t u64 = 42; + const uint32_t u32 = 32; + std::string s; + + auto s4 = absl::StrFormat("Replaceable macro at end %" PRIu64, u64); + // CHECK-MESSAGES: [[@LINE-1]]:13: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format] + // CHECK-FIXES: std::format("Replaceable macro at end {}", u64); + + auto s5 = absl::StrFormat("Replaceable macros in middle %" PRIu64 " %" PRIu32 "\n", u64, u32); + // CHECK-MESSAGES: [[@LINE-1]]:13: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format] + // CHECK-FIXES: std::format("Replaceable macros in middle {} {}\n", u64, u32); + +// These need PRI and __PRI prefixes so that the check get as far as looking for +// where the macro comes from. +#define PRI_FMT_MACRO "s" +#define __PRI_FMT_MACRO "s" + + auto s6 = absl::StrFormat("Unreplaceable macro at end %" PRI_FMT_MACRO, s.c_str()); + // CHECK-MESSAGES: [[@LINE-1]]:13: warning: unable to use 'std::format' instead of 'StrFormat' because format string contains unreplaceable macro 'PRI_FMT_MACRO' [modernize-use-std-format] + + auto s7 = absl::StrFormat(__PRI_FMT_MACRO " Unreplaceable macro at beginning %s", s); + // CHECK-MESSAGES: [[@LINE-1]]:13: warning: unable to use 'std::format' instead of 'StrFormat' because format string contains unreplaceable macro '__PRI_FMT_MACRO' [modernize-use-std-format] + + auto s8 = absl::StrFormat("Unreplacemable macro %" PRI_FMT_MACRO " in the middle", s); + // CHECK-MESSAGES: [[@LINE-1]]:13: warning: unable to use 'std::format' instead of 'StrFormat' because format string contains unreplaceable macro 'PRI_FMT_MACRO' [modernize-use-std-format] + + auto s9 = absl::StrFormat("First macro is replaceable %" PRIu64 " but second one is not %" __PRI_FMT_MACRO, u64, s); + // CHECK-MESSAGES: [[@LINE-1]]:13: warning: unable to use 'std::format' instead of 'StrFormat' because format string contains unreplaceable macro '__PRI_FMT_MACRO' [modernize-use-std-format] + + // Needs a PRI prefix so that we get as far as looking for where the macro comes from + auto s10 = absl::StrFormat(" macro from command line %" PRI_CMDLINE_MACRO, s); + // CHECK-MESSAGES: [[@LINE-1]]:14: warning: unable to use 'std::format' instead of 'StrFormat' because format string contains unreplaceable macro 'PRI_CMDLINE_MACRO' [modernize-use-std-format] + + // Needs a __PRI prefix so that we get as far as looking for where the macro comes from + auto s11 = absl::StrFormat(" macro from command line %" __PRI_CMDLINE_MACRO, s); + // CHECK-MESSAGES: [[@LINE-1]]:14: warning: unable to use 'std::format' instead of 'StrFormat' because format string contains unreplaceable macro '__PRI_CMDLINE_MACRO' [modernize-use-std-format] + + // We ought to be able to fix this since the macro surrounds the whole call + // and therefore can't change the format string independently. This is + // required to be able to fix calls inside Catch2 macros for example. +#define SURROUND_ALL(x) x + auto s12 = SURROUND_ALL(absl::StrFormat("Macro surrounding entire invocation %" PRIu64, u64)); + // CHECK-MESSAGES: [[@LINE-1]]:27: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format] + // CHECK-FIXES: auto s12 = SURROUND_ALL(std::format("Macro surrounding entire invocation {}", u64)); + + // But having that surrounding macro shouldn't stop us ignoring an + // unreplaceable macro elsewhere. + auto s13 = SURROUND_ALL(absl::StrFormat("Macro surrounding entire invocation with unreplaceable macro %" PRI_FMT_MACRO, s)); + // CHECK-MESSAGES: [[@LINE-1]]:27: warning: unable to use 'std::format' instead of 'StrFormat' because format string contains unreplaceable macro 'PRI_FMT_MACRO' [modernize-use-std-format] + + // At the moment at least the check will replace occurrences where the + // function name is the result of expanding a macro. +#define SURROUND_FUNCTION_NAME(x) absl:: x + auto s14 = SURROUND_FUNCTION_NAME(StrFormat)("Hello %d", 4442); + // CHECK-MESSAGES: [[@LINE-1]]:14: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format] + // CHECK-FIXES: auto s14 = std::format("Hello {}", 4442); + + // We can't safely fix occurrences where the macro may affect the format + // string differently in different builds. +#define SURROUND_FORMAT(x) "!" x + auto s15 = absl::StrFormat(SURROUND_FORMAT("Hello %d"), 4443); + // CHECK-MESSAGES: [[@LINE-1]]:14: warning: unable to use 'std::format' instead of 'StrFormat' because format string contains unreplaceable macro 'SURROUND_FORMAT' [modernize-use-std-format] } diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-print.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-print.cpp index da1a18782c9be..f11fc408fcb9c 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-print.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-print.cpp @@ -1,11 +1,15 @@ // RUN: %check_clang_tidy -check-suffixes=,STRICT \ // RUN: -std=c++23 %s modernize-use-std-print %t -- \ // RUN: -config="{CheckOptions: {StrictMode: true}}" \ -// RUN: -- -isystem %clang_tidy_headers -fexceptions +// RUN: -- -isystem %clang_tidy_headers -fexceptions \ +// RUN: -DPRI_CMDLINE_MACRO="\"s\"" \ +// RUN: -D__PRI_CMDLINE_MACRO="\"s\"" // RUN: %check_clang_tidy -check-suffixes=,NOTSTRICT \ // RUN: -std=c++23 %s modernize-use-std-print %t -- \ // RUN: -config="{CheckOptions: {StrictMode: false}}" \ -// RUN: -- -isystem %clang_tidy_headers -fexceptions +// RUN: -- -isystem %clang_tidy_headers -fexceptions \ +// RUN: -DPRI_CMDLINE_MACRO="\"s\"" \ +// RUN: -D__PRI_CMDLINE_MACRO="\"s\"" #include #include #include @@ -1571,3 +1575,68 @@ void p(S s1, S *s2) // CHECK-MESSAGES: [[@LINE-1]]:3: warning: use 'std::print' instead of 'printf' [modernize-use-std-print] // CHECK-FIXES: std::print("Not std::string {} {}", s1.data(), s2->data()); } + +// These need PRI and __PRI prefixes so that the check gets as far as looking +// for where the macro comes from. +#define PRI_FMT_MACRO "s" +#define __PRI_FMT_MACRO "s" + +void macro_expansion(const char *s) +{ + const uint64_t u64 = 42; + const uint32_t u32 = 32; + + printf("Replaceable macro at end %" PRIu64, u64); + // CHECK-MESSAGES: [[@LINE-1]]:3: warning: use 'std::print' instead of 'printf' [modernize-use-std-print] + // CHECK-FIXES: std::print("Replaceable macro at end {}", u64); + + printf("Replaceable macros in middle %" PRIu64 " %" PRIu32 "\n", u64, u32); + // CHECK-MESSAGES: [[@LINE-1]]:3: warning: use 'std::println' instead of 'printf' [modernize-use-std-print] + // CHECK-FIXES: std::println("Replaceable macros in middle {} {}", u64, u32); + + printf("Unreplaceable macro at end %" PRI_FMT_MACRO, s); + // CHECK-MESSAGES: [[@LINE-1]]:3: warning: unable to use 'std::print' instead of 'printf' because format string contains unreplaceable macro 'PRI_FMT_MACRO' [modernize-use-std-print] + + printf(PRI_FMT_MACRO " Unreplaceable macro at beginning %s", s); + // CHECK-MESSAGES: [[@LINE-1]]:3: warning: unable to use 'std::print' instead of 'printf' because format string contains unreplaceable macro 'PRI_FMT_MACRO' [modernize-use-std-print] + + printf("Unreplacemable macro %" __PRI_FMT_MACRO " in the middle", s); + // CHECK-MESSAGES: [[@LINE-1]]:3: warning: unable to use 'std::print' instead of 'printf' because format string contains unreplaceable macro '__PRI_FMT_MACRO' [modernize-use-std-print] + + printf("First macro is replaceable %" PRIu64 " but second one is not %" PRI_FMT_MACRO, u64, s); + // CHECK-MESSAGES: [[@LINE-1]]:3: warning: unable to use 'std::print' instead of 'printf' because format string contains unreplaceable macro 'PRI_FMT_MACRO' [modernize-use-std-print] + + // Needs a PRI prefix so that we get as far as looking for where the macro comes from + printf(" macro from command line %" PRI_CMDLINE_MACRO, s); + // CHECK-MESSAGES: [[@LINE-1]]:3: warning: unable to use 'std::print' instead of 'printf' because format string contains unreplaceable macro 'PRI_CMDLINE_MACRO' [modernize-use-std-print] + + // Needs a __PRI prefix so that we get as far as looking for where the macro comes from + printf(" macro from command line %" __PRI_CMDLINE_MACRO, s); + // CHECK-MESSAGES: [[@LINE-1]]:3: warning: unable to use 'std::print' instead of 'printf' because format string contains unreplaceable macro '__PRI_CMDLINE_MACRO' [modernize-use-std-print] + + // We ought to be able to fix this since the macro surrounds the whole call + // and therefore can't change the format string independently. This is + // required to be able to fix calls inside Catch2 macros for example. +#define SURROUND_ALL(x) x + SURROUND_ALL(printf("Macro surrounding entire invocation %" PRIu64, u64)); + // CHECK-MESSAGES: [[@LINE-1]]:16: warning: use 'std::print' instead of 'printf' [modernize-use-std-print] + // CHECK-FIXES: SURROUND_ALL(std::print("Macro surrounding entire invocation {}", u64)); + + // But having that surrounding macro shouldn't stop us ignoring an + // unreplaceable macro elsewhere. + SURROUND_ALL(printf("Macro surrounding entire invocation with unreplaceable macro %" PRI_FMT_MACRO, s)); + // CHECK-MESSAGES: [[@LINE-1]]:16: warning: unable to use 'std::print' instead of 'printf' because format string contains unreplaceable macro 'PRI_FMT_MACRO' [modernize-use-std-print] + + // At the moment at least the check will replace occurrences where the + // function name is the result of expanding a macro. +#define SURROUND_FUNCTION_NAME(x) x + SURROUND_FUNCTION_NAME(printf)("Hello %d", 4442); + // CHECK-MESSAGES: [[@LINE-1]]:26: warning: use 'std::print' instead of 'printf' [modernize-use-std-print] + // CHECK-FIXES: std::print("Hello {}", 4442); + + // We can't safely fix occurrences where the macro may affect the format + // string differently in different builds. +#define SURROUND_FORMAT(x) "!" x + printf(SURROUND_FORMAT("Hello %d"), 4443); + // CHECK-MESSAGES: [[@LINE-1]]:3: warning: unable to use 'std::print' instead of 'printf' because format string contains unreplaceable macro 'SURROUND_FORMAT' [modernize-use-std-print] +} diff --git a/clang-tools-extra/test/clang-tidy/checkers/performance/move-const-arg.cpp b/clang-tools-extra/test/clang-tidy/checkers/performance/move-const-arg.cpp index 4505eef6df24b..8e325b0ae6ca3 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/performance/move-const-arg.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/performance/move-const-arg.cpp @@ -546,3 +546,17 @@ void testAlsoNonMoveable() { } } // namespace issue_62550 + +namespace GH111450 { +struct Status; + +struct Error { + Error(const Status& S); +}; + +struct Result { + Error E; + Result(Status&& S) : E(std::move(S)) {} + // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: passing result of std::move() as a const reference argument; no move will actually happen [performance-move-const-arg] +}; +} // namespace GH111450 diff --git a/clang-tools-extra/test/clang-tidy/checkers/portability/template-virtual-member-function.cpp b/clang-tools-extra/test/clang-tidy/checkers/portability/template-virtual-member-function.cpp new file mode 100644 index 0000000000000..94786ae93dd3f --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/portability/template-virtual-member-function.cpp @@ -0,0 +1,173 @@ +// RUN: %check_clang_tidy %s portability-template-virtual-member-function %t +namespace UninstantiatedVirtualMember { +template +struct CrossPlatformError { + virtual ~CrossPlatformError() = default; + + static void used() {} + + // CHECK-MESSAGES: [[#@LINE+1]]:18: warning: unspecified virtual member function instantiation + virtual void unused() { + T MSVCError = this; + }; +}; + +int main() { + // CHECK-MESSAGES: [[#@LINE+1]]:5: note: template instantiated here + CrossPlatformError::used(); + return 0; +} +} // namespace UninstantiatedVirtualMember + +namespace UninstantiatedVirtualMembers { +template +struct CrossPlatformError { + virtual ~CrossPlatformError() = default; + + static void used() {} + + // CHECK-MESSAGES: [[#@LINE+2]]:18: warning: unspecified virtual member function instantiation + // CHECK-MESSAGES: [[#@LINE+13]]:5: note: template instantiated here + virtual void unused() { + T MSVCError = this; + }; + + // CHECK-MESSAGES: [[#@LINE+2]]:18: warning: unspecified virtual member function instantiation + // CHECK-MESSAGES: [[#@LINE+7]]:5: note: template instantiated here + virtual void unused2() { + T MSVCError = this; + }; +}; + +int main() { + CrossPlatformError::used(); + return 0; +} +} // namespace UninstantiatedVirtualMembers + +namespace UninstantiatedVirtualDestructor { +template +struct CrossPlatformError { + // CHECK-MESSAGES: [[#@LINE+2]]:13: warning: unspecified virtual member function instantiation + // CHECK-MESSAGES: [[#@LINE+9]]:5: note: template instantiated here + virtual ~CrossPlatformError() { + T MSVCError = this; + }; + + static void used() {} +}; + +int main() { + CrossPlatformError::used(); + return 0; +} +} // namespace UninstantiatedVirtualDestructor + +namespace MultipleImplicitInstantiations { +template +struct CrossPlatformError { + virtual ~CrossPlatformError() = default; + + static void used() {} + + // CHECK-MESSAGES: [[#@LINE+2]]:18: warning: unspecified virtual member function instantiation + // CHECK-MESSAGES: [[#@LINE+7]]:5: note: template instantiated here + virtual void unused() { + T MSVCError = this; + }; +}; + +int main() { + CrossPlatformError::used(); + CrossPlatformError::used(); + CrossPlatformError::used(); + return 0; +} +} // namespace MultipleImplicitInstantiations + +namespace SomeImplicitInstantiationError { +template struct CrossPlatformError { + virtual ~CrossPlatformError() = default; + + static void used() {} + + // CHECK-MESSAGES: [[#@LINE+2]]:18: warning: unspecified virtual member function instantiation + // CHECK-MESSAGES: [[#@LINE+5]]:5: note: template instantiated here + virtual void unused(){}; +}; + +int main() { + CrossPlatformError::used(); + CrossPlatformError NoError; + return 0; +} +} // namespace SomeImplicitInstantiationError + +namespace InstantiatedVirtualMemberFunctions { +template +struct NoError { + virtual ~NoError() {}; + virtual void unused() {}; + virtual void unused2() {}; + virtual void unused3() {}; +}; + +int main() { + NoError Ne; + return 0; +} +} // namespace InstantiatedVirtualMemberFunctions + +namespace UninstantiatedNonVirtualMemberFunctions { +template +struct NoError { + static void used() {}; + void unused() {}; + void unused2() {}; + void unused3() {}; +}; + +int main() { + NoError::used(); + return 0; +} +} // namespace UninstantiatedNonVirtualMemberFunctions + +namespace PartialSpecializationError { +template +struct CrossPlatformError {}; + +template +struct CrossPlatformError{ + virtual ~CrossPlatformError() = default; + + static void used() {} + + // CHECK-MESSAGES: [[#@LINE+2]]:18: warning: unspecified virtual member function instantiation + // CHECK-MESSAGES: [[#@LINE+7]]:5: note: template instantiated here + virtual void unused() { + U MSVCError = this; + }; +}; + +int main() { + CrossPlatformError::used(); + return 0; +} +} // namespace PartialSpecializationError + +namespace PartialSpecializationNoInstantiation { +template +struct NoInstantiation {}; + +template +struct NoInstantiation{ + virtual ~NoInstantiation() = default; + + static void used() {} + + virtual void unused() { + U MSVCError = this; + }; +}; +} // namespace PartialSpecializationNoInstantiation diff --git a/clang-tools-extra/test/clang-tidy/infrastructure/verify-config.cpp b/clang-tools-extra/test/clang-tidy/infrastructure/verify-config.cpp index 3659285986482..93f6f9fc156a4 100644 --- a/clang-tools-extra/test/clang-tidy/infrastructure/verify-config.cpp +++ b/clang-tools-extra/test/clang-tidy/infrastructure/verify-config.cpp @@ -30,3 +30,11 @@ // CHECK-VERIFY-BLOCK-BAD: command-line option '-config': warning: check glob 'bugprone-arguments-*' doesn't match any known check [-verify-config] // CHECK-VERIFY-BLOCK-BAD: command-line option '-config': warning: unknown check 'bugprone-assert-side-effects'; did you mean 'bugprone-assert-side-effect' [-verify-config] +// RUN: echo -e 'Checks: "-*,clang-analyzer-optin.cplusplus.UninitializedObject"\nCheckOptions:\n clang-analyzer-optin.cplusplus.UninitializedObject:Pedantic: true' > %T/MyClangTidyConfigCSA +// RUN: clang-tidy --verify-config --config-file=%T/MyClangTidyConfigCSA 2>&1 | FileCheck %s -check-prefix=CHECK-VERIFY-CSA-OK -implicit-check-not='{{warnings|error}}' +// CHECK-VERIFY-CSA-OK: No config errors detected. + +// RUN: echo -e 'Checks: "-*,clang-analyzer-optin.cplusplus.UninitializedObject"\nCheckOptions:\n clang-analyzer-optin.cplusplus.UninitializedObject.Pedantic: true' > %T/MyClangTidyConfigCSABad +// RUN: not clang-tidy --verify-config --config-file=%T/MyClangTidyConfigCSABad 2>&1 | FileCheck %s -check-prefix=CHECK-VERIFY-CSA-BAD -implicit-check-not='{{warnings|error}}' +// CHECK-VERIFY-CSA-BAD: command-line option '-config': warning: unknown check option 'clang-analyzer-optin.cplusplus.UninitializedObject.Pedantic'; did you mean 'clang-analyzer-optin.cplusplus.UninitializedObject:Pedantic' [-verify-config] + diff --git a/clang/CodeOwners.rst b/clang/Maintainers.rst similarity index 87% rename from clang/CodeOwners.rst rename to clang/Maintainers.rst index f067b7183ae73..ee5334b02f700 100644 --- a/clang/CodeOwners.rst +++ b/clang/Maintainers.rst @@ -1,36 +1,36 @@ ================= -Clang Code Owners +Clang Maintainers ================= This file is a list of the -`code owners `_ for +`maintainers `_ for Clang. .. contents:: :depth: 2 :local: -Current Code Owners -=================== -The following people are the active code owners for the project. Please reach +Active Maintainers +================== +The following people are the active maintainers for the project. Please reach out to them for code reviews, questions about their area of expertise, or other assistance. -All parts of Clang not covered by someone else ----------------------------------------------- +Lead Maintainer +--------------- | Aaron Ballman | aaron\@aaronballman.com (email), aaron.ballman (Phabricator), AaronBallman (GitHub), AaronBallman (Discourse), aaronballman (Discord), AaronBallman (IRC) Contained Components -------------------- -These code owners are responsible for particular high-level components within +These maintainers are responsible for particular high-level components within Clang that are typically contained to one area of the compiler. AST matchers ~~~~~~~~~~~~ -| Manuel Klimek -| klimek\@google.com (email), klimek (Phabricator), r4nt (GitHub) +| Aaron Ballman +| aaron\@aaronballman.com (email), aaron.ballman (Phabricator), AaronBallman (GitHub), AaronBallman (Discourse), aaronballman (Discord), AaronBallman (IRC) Clang LLVM IR generation @@ -60,7 +60,7 @@ Analysis & CFG Experimental new constant interpreter ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | Timm Bäder -| tbaeder\@redhat.com (email), tbaeder (Phabricator), tbaederr (GitHub), tbaeder (Discourse), tbaeder (Discord) +| tbaeder\@redhat.com (em), tbaeder (Phabricator), tbaederr (GitHub), tbaeder (Discourse), tbaeder (Discord) Modules & serialization @@ -125,14 +125,9 @@ Driver parts not covered by someone else Tools ----- -These code owners are responsible for user-facing tools under the Clang +These maintainers are responsible for user-facing tools under the Clang umbrella or components used to support such tools. -Tooling library -~~~~~~~~~~~~~~~ -| Manuel Klimek -| klimek\@google.com (email), klimek (Phabricator), r4nt (GitHub) - clang-format ~~~~~~~~~~~~ @@ -255,19 +250,20 @@ SYCL conformance | alexey.bader\@intel.com (email), bader (Phabricator), bader (GitHub) -Former Code Owners -================== -The following people have graciously spent time performing code ownership +Inactive Maintainers +==================== +The following people have graciously spent time performing maintainership responsibilities but are no longer active in that role. Thank you for all your help with the success of the project! -Emeritus owners ---------------- +Emeritus Lead Maintainers +------------------------- | Doug Gregor (dgregor\@apple.com) | Richard Smith (richard\@metafoo.co.uk) -Former component owners ------------------------ +Inactive component maintainers +------------------------------ | Chandler Carruth (chandlerc\@gmail.com, chandlerc\@google.com) -- CMake, library layering | Devin Coughlin (dcoughlin\@apple.com) -- Clang static analyzer +| Manuel Klimek (klimek\@google.com (email), klimek (Phabricator), r4nt (GitHub)) -- Tooling, AST matchers diff --git a/clang/cmake/caches/Android.cmake b/clang/cmake/caches/Android.cmake index 9e15fff033761..d5ca6b50d4ada 100644 --- a/clang/cmake/caches/Android.cmake +++ b/clang/cmake/caches/Android.cmake @@ -9,7 +9,6 @@ set(CLANG_VENDOR Android CACHE STRING "") set(CMAKE_BUILD_TYPE RELEASE CACHE STRING "") -set(HAVE_LIBCXXABI ON CACHE BOOL "") set(LLVM_BUILD_TOOLS OFF CACHE BOOL "") set(LLVM_ENABLE_ASSERTIONS ON CACHE BOOL "") set(LLVM_ENABLE_THREADS OFF CACHE BOOL "") diff --git a/clang/cmake/caches/Fuchsia-stage2.cmake b/clang/cmake/caches/Fuchsia-stage2.cmake index 26ae30c71b4df..5af98c7b3b3fb 100644 --- a/clang/cmake/caches/Fuchsia-stage2.cmake +++ b/clang/cmake/caches/Fuchsia-stage2.cmake @@ -345,6 +345,7 @@ foreach(target armv6m-none-eabi;armv7m-none-eabi;armv8m.main-none-eabi) set(RUNTIMES_${target}_LIBCXX_CXX_ABI none CACHE STRING "") set(RUNTIMES_${target}_LIBCXX_ENABLE_SHARED OFF CACHE BOOL "") set(RUNTIMES_${target}_LIBCXX_ENABLE_STATIC ON CACHE BOOL "") + set(RUNTIMES_${target}_LIBCXX_SHARED_OUTPUT_NAME "c++-shared" CACHE STRING "") set(RUNTIMES_${target}_LIBCXX_LIBC "llvm-libc" CACHE STRING "") set(RUNTIMES_${target}_LIBCXX_ENABLE_FILESYSTEM OFF CACHE BOOL "") set(RUNTIMES_${target}_LIBCXX_ENABLE_RANDOM_DEVICE OFF CACHE BOOL "") @@ -396,6 +397,7 @@ foreach(target riscv32-unknown-elf) set(RUNTIMES_${target}_LIBCXX_CXX_ABI none CACHE STRING "") set(RUNTIMES_${target}_LIBCXX_ENABLE_SHARED OFF CACHE BOOL "") set(RUNTIMES_${target}_LIBCXX_ENABLE_STATIC ON CACHE BOOL "") + set(RUNTIMES_${target}_LIBCXX_SHARED_OUTPUT_NAME "c++-shared" CACHE STRING "") set(RUNTIMES_${target}_LIBCXX_LIBC "llvm-libc" CACHE STRING "") set(RUNTIMES_${target}_LIBCXX_ENABLE_FILESYSTEM OFF CACHE BOOL "") set(RUNTIMES_${target}_LIBCXX_ENABLE_RANDOM_DEVICE OFF CACHE BOOL "") diff --git a/clang/cmake/caches/hexagon-unknown-linux-musl-clang-cross.cmake b/clang/cmake/caches/hexagon-unknown-linux-musl-clang-cross.cmake new file mode 100644 index 0000000000000..91bbe26b62105 --- /dev/null +++ b/clang/cmake/caches/hexagon-unknown-linux-musl-clang-cross.cmake @@ -0,0 +1,15 @@ +# This file is for the llvm+clang options that are specific to building +# a cross-toolchain targeting hexagon linux. +set(DEFAULT_SYSROOT "../target/hexagon-unknown-linux-musl/" CACHE STRING "") +set(CLANG_LINKS_TO_CREATE + hexagon-linux-musl-clang++ + hexagon-linux-musl-clang + hexagon-unknown-linux-musl-clang++ + hexagon-unknown-linux-musl-clang + hexagon-none-elf-clang++ + hexagon-none-elf-clang + hexagon-unknown-none-elf-clang++ + hexagon-unknown-none-elf-clang + CACHE STRING "") + +set(LLVM_INSTALL_TOOLCHAIN_ONLY ON CACHE BOOL "") diff --git a/clang/cmake/caches/hexagon-unknown-linux-musl-clang.cmake b/clang/cmake/caches/hexagon-unknown-linux-musl-clang.cmake new file mode 100644 index 0000000000000..9f3eb4678fd17 --- /dev/null +++ b/clang/cmake/caches/hexagon-unknown-linux-musl-clang.cmake @@ -0,0 +1,15 @@ + +set(LLVM_TARGETS_TO_BUILD "Hexagon" CACHE STRING "") +set(LLVM_DEFAULT_TARGET_TRIPLE "hexagon-unknown-linux-musl" CACHE STRING "") +set(CLANG_DEFAULT_CXX_STDLIB "libc++" CACHE STRING "") +set(CLANG_DEFAULT_OBJCOPY "llvm-objcopy" CACHE STRING "") +set(CLANG_DEFAULT_RTLIB "compiler-rt" CACHE STRING "") +set(CLANG_DEFAULT_UNWINDLIB "libunwind" CACHE STRING "") +set(CLANG_DEFAULT_LINKER "lld" CACHE STRING "") +set(LLVM_ENABLE_PROJECTS "clang;lld" CACHE STRING "") + +set(LLVM_INCLUDE_TESTS OFF CACHE BOOL "") +set(LLVM_INCLUDE_DOCS OFF CACHE BOOL "") +# Enabling toolchain-only causes problems when doing some of the +# subsequent builds, will need to investigate: +set(LLVM_INSTALL_TOOLCHAIN_ONLY OFF CACHE BOOL "") diff --git a/clang/docs/AddressSanitizer.rst b/clang/docs/AddressSanitizer.rst index e1997153f2037..76fdf55995059 100644 --- a/clang/docs/AddressSanitizer.rst +++ b/clang/docs/AddressSanitizer.rst @@ -313,6 +313,14 @@ Limitations usually expected. * Static linking of executables is not supported. +Security Considerations +======================= + +AddressSanitizer is a bug detection tool and its runtime is not meant to be +linked against production executables. While it may be useful for testing, +AddressSanitizer's runtime was not developed with security-sensitive +constraints in mind and may compromise the security of the resulting executable. + Supported Platforms =================== diff --git a/clang/docs/CMakeLists.txt b/clang/docs/CMakeLists.txt index 51e9db29f887f..4fecc007f5995 100644 --- a/clang/docs/CMakeLists.txt +++ b/clang/docs/CMakeLists.txt @@ -117,7 +117,7 @@ if (LLVM_ENABLE_SPHINX) "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_BINARY_DIR}" COMMAND "${CMAKE_COMMAND}" -E copy_if_different - "${CMAKE_CURRENT_SOURCE_DIR}/../CodeOwners.rst" + "${CMAKE_CURRENT_SOURCE_DIR}/../Maintainers.rst" "${CMAKE_CURRENT_BINARY_DIR}" ) diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst index a427d7cd40fcd..a9bfb4c4a0fcb 100644 --- a/clang/docs/ClangFormatStyleOptions.rst +++ b/clang/docs/ClangFormatStyleOptions.rst @@ -249,7 +249,7 @@ the configuration (without a prefix: ``Auto``). .. _AlignArrayOfStructures: **AlignArrayOfStructures** (``ArrayInitializerAlignmentStyle``) :versionbadge:`clang-format 13` :ref:`¶ ` - if not ``None``, when using initialization for an array of structs + If not ``None``, when using initialization for an array of structs aligns the fields into columns. @@ -307,11 +307,12 @@ the configuration (without a prefix: ``Auto``). Alignment options. They can also be read as a whole for compatibility. The choices are: - - None - - Consecutive - - AcrossEmptyLines - - AcrossComments - - AcrossEmptyLinesAndComments + + * ``None`` + * ``Consecutive`` + * ``AcrossEmptyLines`` + * ``AcrossComments`` + * ``AcrossEmptyLinesAndComments`` For example, to align across empty lines and not across comments, either of these work. @@ -392,6 +393,21 @@ the configuration (without a prefix: ``Auto``). a &= 2; bbb = 2; + * ``bool AlignFunctionDeclarations`` Only for ``AlignConsecutiveDeclarations``. Whether function declarations + are aligned. + + .. code-block:: c++ + + true: + unsigned int f1(void); + void f2(void); + size_t f3(void); + + false: + unsigned int f1(void); + void f2(void); + size_t f3(void); + * ``bool AlignFunctionPointers`` Only for ``AlignConsecutiveDeclarations``. Whether function pointers are aligned. @@ -449,11 +465,12 @@ the configuration (without a prefix: ``Auto``). Alignment options. They can also be read as a whole for compatibility. The choices are: - - None - - Consecutive - - AcrossEmptyLines - - AcrossComments - - AcrossEmptyLinesAndComments + + * ``None`` + * ``Consecutive`` + * ``AcrossEmptyLines`` + * ``AcrossComments`` + * ``AcrossEmptyLinesAndComments`` For example, to align across empty lines and not across comments, either of these work. @@ -534,6 +551,21 @@ the configuration (without a prefix: ``Auto``). a &= 2; bbb = 2; + * ``bool AlignFunctionDeclarations`` Only for ``AlignConsecutiveDeclarations``. Whether function declarations + are aligned. + + .. code-block:: c++ + + true: + unsigned int f1(void); + void f2(void); + size_t f3(void); + + false: + unsigned int f1(void); + void f2(void); + size_t f3(void); + * ``bool AlignFunctionPointers`` Only for ``AlignConsecutiveDeclarations``. Whether function pointers are aligned. @@ -591,11 +623,12 @@ the configuration (without a prefix: ``Auto``). Alignment options. They can also be read as a whole for compatibility. The choices are: - - None - - Consecutive - - AcrossEmptyLines - - AcrossComments - - AcrossEmptyLinesAndComments + + * ``None`` + * ``Consecutive`` + * ``AcrossEmptyLines`` + * ``AcrossComments`` + * ``AcrossEmptyLinesAndComments`` For example, to align across empty lines and not across comments, either of these work. @@ -676,6 +709,21 @@ the configuration (without a prefix: ``Auto``). a &= 2; bbb = 2; + * ``bool AlignFunctionDeclarations`` Only for ``AlignConsecutiveDeclarations``. Whether function declarations + are aligned. + + .. code-block:: c++ + + true: + unsigned int f1(void); + void f2(void); + size_t f3(void); + + false: + unsigned int f1(void); + void f2(void); + size_t f3(void); + * ``bool AlignFunctionPointers`` Only for ``AlignConsecutiveDeclarations``. Whether function pointers are aligned. @@ -734,11 +782,12 @@ the configuration (without a prefix: ``Auto``). Alignment options. They can also be read as a whole for compatibility. The choices are: - - None - - Consecutive - - AcrossEmptyLines - - AcrossComments - - AcrossEmptyLinesAndComments + + * ``None`` + * ``Consecutive`` + * ``AcrossEmptyLines`` + * ``AcrossComments`` + * ``AcrossEmptyLinesAndComments`` For example, to align across empty lines and not across comments, either of these work. @@ -819,6 +868,21 @@ the configuration (without a prefix: ``Auto``). a &= 2; bbb = 2; + * ``bool AlignFunctionDeclarations`` Only for ``AlignConsecutiveDeclarations``. Whether function declarations + are aligned. + + .. code-block:: c++ + + true: + unsigned int f1(void); + void f2(void); + size_t f3(void); + + false: + unsigned int f1(void); + void f2(void); + size_t f3(void); + * ``bool AlignFunctionPointers`` Only for ``AlignConsecutiveDeclarations``. Whether function pointers are aligned. @@ -996,11 +1060,12 @@ the configuration (without a prefix: ``Auto``). Alignment options. They can also be read as a whole for compatibility. The choices are: - - None - - Consecutive - - AcrossEmptyLines - - AcrossComments - - AcrossEmptyLinesAndComments + + * ``None`` + * ``Consecutive`` + * ``AcrossEmptyLines`` + * ``AcrossComments`` + * ``AcrossEmptyLinesAndComments`` For example, to align across empty lines and not across comments, either of these work. @@ -1081,6 +1146,21 @@ the configuration (without a prefix: ``Auto``). a &= 2; bbb = 2; + * ``bool AlignFunctionDeclarations`` Only for ``AlignConsecutiveDeclarations``. Whether function declarations + are aligned. + + .. code-block:: c++ + + true: + unsigned int f1(void); + void f2(void); + size_t f3(void); + + false: + unsigned int f1(void); + void f2(void); + size_t f3(void); + * ``bool AlignFunctionPointers`` Only for ``AlignConsecutiveDeclarations``. Whether function pointers are aligned. @@ -1136,11 +1216,12 @@ the configuration (without a prefix: ``Auto``). Alignment options. They can also be read as a whole for compatibility. The choices are: - - None - - Consecutive - - AcrossEmptyLines - - AcrossComments - - AcrossEmptyLinesAndComments + + * ``None`` + * ``Consecutive`` + * ``AcrossEmptyLines`` + * ``AcrossComments`` + * ``AcrossEmptyLinesAndComments`` For example, to align across empty lines and not across comments, either of these work. @@ -1221,6 +1302,21 @@ the configuration (without a prefix: ``Auto``). a &= 2; bbb = 2; + * ``bool AlignFunctionDeclarations`` Only for ``AlignConsecutiveDeclarations``. Whether function declarations + are aligned. + + .. code-block:: c++ + + true: + unsigned int f1(void); + void f2(void); + size_t f3(void); + + false: + unsigned int f1(void); + void f2(void); + size_t f3(void); + * ``bool AlignFunctionPointers`` Only for ``AlignConsecutiveDeclarations``. Whether function pointers are aligned. @@ -1276,11 +1372,12 @@ the configuration (without a prefix: ``Auto``). Alignment options. They can also be read as a whole for compatibility. The choices are: - - None - - Consecutive - - AcrossEmptyLines - - AcrossComments - - AcrossEmptyLinesAndComments + + * ``None`` + * ``Consecutive`` + * ``AcrossEmptyLines`` + * ``AcrossComments`` + * ``AcrossEmptyLinesAndComments`` For example, to align across empty lines and not across comments, either of these work. @@ -1361,6 +1458,21 @@ the configuration (without a prefix: ``Auto``). a &= 2; bbb = 2; + * ``bool AlignFunctionDeclarations`` Only for ``AlignConsecutiveDeclarations``. Whether function declarations + are aligned. + + .. code-block:: c++ + + true: + unsigned int f1(void); + void f2(void); + size_t f3(void); + + false: + unsigned int f1(void); + void f2(void); + size_t f3(void); + * ``bool AlignFunctionPointers`` Only for ``AlignConsecutiveDeclarations``. Whether function pointers are aligned. @@ -6554,6 +6666,15 @@ the configuration (without a prefix: ``Auto``). let DAGArgOtherID = (other i32:$other1, i32:$other2); let DAGArgBang = (!cast("Some") i32:$src1, i32:$src2) +.. _TemplateNames: + +**TemplateNames** (``List of Strings``) :versionbadge:`clang-format 20` :ref:`¶ ` + A vector of non-keyword identifiers that should be interpreted as + template names. + + A ``<`` after a template name is annotated as a template opener instead of + a binary operator. + .. _TypeNames: **TypeNames** (``List of Strings``) :versionbadge:`clang-format 17` :ref:`¶ ` diff --git a/clang/docs/CodeOwners.rst b/clang/docs/CodeOwners.rst deleted file mode 100644 index 48128fbc5d909..0000000000000 --- a/clang/docs/CodeOwners.rst +++ /dev/null @@ -1 +0,0 @@ -.. include:: ../CodeOwners.rst diff --git a/clang/docs/HardwareAssistedAddressSanitizerDesign.rst b/clang/docs/HardwareAssistedAddressSanitizerDesign.rst index f89ca117427ad..20db41c032c56 100644 --- a/clang/docs/HardwareAssistedAddressSanitizerDesign.rst +++ b/clang/docs/HardwareAssistedAddressSanitizerDesign.rst @@ -262,6 +262,14 @@ than that of AddressSanitizer: `1/TG` extra memory for the shadow and some overhead due to `TG`-aligning all objects. +Security Considerations +======================= + +HWASAN is a bug detection tool and its runtime is not meant to be +linked against production executables. While it may be useful for testing, +HWASAN's runtime was not developed with security-sensitive +constraints in mind and may compromise the security of the resulting executable. + Supported architectures ======================= HWASAN relies on `Address Tagging`_ which is only available on AArch64. diff --git a/clang/docs/LeakSanitizer.rst b/clang/docs/LeakSanitizer.rst index 6858f32957ebe..adcb6421c6a1f 100644 --- a/clang/docs/LeakSanitizer.rst +++ b/clang/docs/LeakSanitizer.rst @@ -43,6 +43,14 @@ To use LeakSanitizer in stand-alone mode, link your program with link step, so that it would link in proper LeakSanitizer run-time library into the final executable. +Security Considerations +======================= + +LeakSanitizer is a bug detection tool and its runtime is not meant to be +linked against production executables. While it may be useful for testing, +LeakSanitizer's runtime was not developed with security-sensitive +constraints in mind and may compromise the security of the resulting executable. + Supported Platforms =================== diff --git a/clang/docs/Maintainers.rst b/clang/docs/Maintainers.rst new file mode 100644 index 0000000000000..7e69c1165d2f5 --- /dev/null +++ b/clang/docs/Maintainers.rst @@ -0,0 +1 @@ +.. include:: ../Maintainers.rst diff --git a/clang/docs/MemorySanitizer.rst b/clang/docs/MemorySanitizer.rst index 05e43a32b9b87..9f0d3f13a9d62 100644 --- a/clang/docs/MemorySanitizer.rst +++ b/clang/docs/MemorySanitizer.rst @@ -198,6 +198,14 @@ uninstrumented libc. For example, the authors were able to bootstrap MemorySanitizer-instrumented Clang compiler by linking it with self-built instrumented libc++ (as a replacement for libstdc++). +Security Considerations +======================= + +MemorySanitizer is a bug detection tool and its runtime is not meant to be +linked against production executables. While it may be useful for testing, +MemorySanitizer's runtime was not developed with security-sensitive +constraints in mind and may compromise the security of the resulting executable. + Supported Platforms =================== diff --git a/clang/docs/Multilib.rst b/clang/docs/Multilib.rst index 6d77fda3623b2..7637d0db9565b 100644 --- a/clang/docs/Multilib.rst +++ b/clang/docs/Multilib.rst @@ -202,8 +202,8 @@ For a more comprehensive example see # If there is no multilib available for a particular set of flags, and the # other multilibs are not adequate fallbacks, then you can define a variant - # record with a FatalError key in place of the Dir key. - - FatalError: this multilib collection has no hard-float ABI support + # record with an Error key in place of the Dir key. + - Error: this multilib collection has no hard-float ABI support Flags: [--target=thumbv7m-none-eabi, -mfloat-abi=hard] diff --git a/clang/docs/RealtimeSanitizer.rst b/clang/docs/RealtimeSanitizer.rst index 3f96267603aef..103842e055db7 100644 --- a/clang/docs/RealtimeSanitizer.rst +++ b/clang/docs/RealtimeSanitizer.rst @@ -9,7 +9,7 @@ Introduction ============ RealtimeSanitizer (a.k.a. RTSan) is a real-time safety testing tool for C and C++ projects. RTSan can be used to detect real-time violations, i.e. calls to methods -that are not safe for use in functions with deterministic runtime requirements. +that are not safe for use in functions with deterministic run time requirements. RTSan considers any function marked with the ``[[clang::nonblocking]]`` attribute to be a real-time function. If RTSan detects a call to ``malloc``, ``free``, ``pthread_mutex_lock``, or anything else that could have a non-deterministic @@ -58,7 +58,7 @@ code. return 0; } # Compile and link - % clang++ -fsanitize=realtime -g example_realtime_violation.cpp + % clang++ -fsanitize=realtime example_realtime_violation.cpp If a real-time safety violation is detected in a ``[[clang::nonblocking]]`` context, or any function invoked by that function, the program will exit with a @@ -66,23 +66,63 @@ non-zero exit code. .. code-block:: console - % clang++ -fsanitize=realtime -g example_realtime_violation.cpp + % clang++ -fsanitize=realtime example_realtime_violation.cpp % ./a.out - Real-time violation: intercepted call to real-time unsafe function `malloc` in real-time context! Stack trace: - #0 0x000102893034 in __rtsan::PrintStackTrace() rtsan_stack.cpp:45 - #1 0x000102892e64 in __rtsan::Context::ExpectNotRealtime(char const*) rtsan_context.cpp:78 - #2 0x00010289397c in malloc rtsan_interceptors.cpp:286 - #3 0x000195bd7bd0 in operator new(unsigned long)+0x1c (libc++abi.dylib:arm64+0x16bd0) - #4 0x5c7f00010230f07c () - #5 0x00010230f058 in std::__1::__libcpp_allocate[abi:ue170006](unsigned long, unsigned long) new:324 - #6 0x00010230effc in std::__1::allocator::allocate[abi:ue170006](unsigned long) allocator.h:114 - ... snip ... - #10 0x00010230e4bc in std::__1::vector>::__append(unsigned long) vector:1162 - #11 0x00010230dcdc in std::__1::vector>::resize(unsigned long) vector:1981 - #12 0x00010230dc28 in violation() main.cpp:5 - #13 0x00010230dd64 in main main.cpp:9 - #14 0x0001958960dc () - #15 0x2f557ffffffffffc () + ==76290==ERROR: RealtimeSanitizer: unsafe-library-call + Intercepted call to real-time unsafe function `malloc` in real-time context! + #0 0x000102a7b884 in malloc rtsan_interceptors.cpp:426 + #1 0x00019c326bd0 in operator new(unsigned long)+0x1c (libc++abi.dylib:arm64+0x16bd0) + #2 0xa30d0001024f79a8 () + #3 0x0001024f794c in std::__1::__libcpp_allocate[abi:ne200000](unsigned long, unsigned long)+0x44 + #4 0x0001024f78c4 in std::__1::allocator::allocate[abi:ne200000](unsigned long)+0x44 + ... snip ... + #9 0x0001024f6868 in std::__1::vector>::resize(unsigned long)+0x48 + #10 0x0001024f67b4 in violation()+0x24 + #11 0x0001024f68f0 in main+0x18 (a.out:arm64+0x1000028f0) + #12 0x00019bfe3150 () + #13 0xed5efffffffffffc () + + +Blocking functions +------------------ + +Calls to system library functions such as ``malloc`` are automatically caught by +RealtimeSanitizer. Real-time programmers may also write their own blocking +(real-time unsafe) functions that they wish RealtimeSanitizer to be aware of. +RealtimeSanitizer will raise an error at run time if any function attributed +with ``[[clang::blocking]]`` is called in a ``[[clang::nonblocking]]`` context. + +.. code-block:: console + + $ cat example_blocking_violation.cpp + #include + #include + + std::atomic has_permission{false}; + + int wait_for_permission() [[clang::blocking]] { + while (has_permission.load() == false) + std::this_thread::yield(); + return 0; + } + + int real_time_function() [[clang::nonblocking]] { + return wait_for_permission(); + } + + int main() { + return real_time_function(); + } + + $ clang++ -fsanitize=realtime example_blocking_violation.cpp && ./a.out + ==76131==ERROR: RealtimeSanitizer: blocking-call + Call to blocking function `wait_for_permission()` in real-time context! + #0 0x0001000c3db0 in wait_for_permission()+0x10 (a.out:arm64+0x100003db0) + #1 0x0001000c3e3c in real_time_function()+0x10 (a.out:arm64+0x100003e3c) + #2 0x0001000c3e68 in main+0x10 (a.out:arm64+0x100003e68) + #3 0x00019bfe3150 () + #4 0x5a27fffffffffffc () + Run-time flags -------------- @@ -159,7 +199,7 @@ Disabling In some circumstances, you may want to suppress error reporting in a specific scope. -In C++, this is achieved via ``__rtsan::ScopedDisabler``. Within the scope where the ``ScopedDisabler`` object is instantiated, all sanitizer error reports are suppressed. This suppression applies to the current scope as well as all invoked functions, including any functions called transitively. +In C++, this is achieved via ``__rtsan::ScopedDisabler``. Within the scope where the ``ScopedDisabler`` object is instantiated, all sanitizer error reports are suppressed. This suppression applies to the current scope as well as all invoked functions, including any functions called transitively. .. code-block:: c++ @@ -174,7 +214,7 @@ In C++, this is achieved via ``__rtsan::ScopedDisabler``. Within the scope wher If RealtimeSanitizer is not enabled at compile time (i.e., the code is not compiled with the ``-fsanitize=realtime`` flag), the ``ScopedDisabler`` is compiled as a no-op. -In C, you can use the ``__rtsan_disable()`` and ``rtsan_enable()`` functions to manually disable and re-enable RealtimeSanitizer checks. +In C, you can use the ``__rtsan_disable()`` and ``rtsan_enable()`` functions to manually disable and re-enable RealtimeSanitizer checks. .. code-block:: c++ diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index b7d287fdf4cc6..e74dd1a5fb32d 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -99,6 +99,20 @@ C++ Specific Potentially Breaking Changes // Was error, now evaluates to false. constexpr bool b = f() == g(); +- The warning ``-Wdeprecated-literal-operator`` is now on by default, as this is + something that WG21 has shown interest in removing from the language. The + result is that anyone who is compiling with ``-Werror`` should see this + diagnostic. To fix this diagnostic, simply removing the space character from + between the ``operator""`` and the user defined literal name will make the + source no longer deprecated. This is consistent with `CWG2521 _`. + + .. code-block:: c++ + + // Now diagnoses by default. + unsigned operator"" _udl_name(unsigned long long); + // Fixed version: + unsigned operator""_udl_name(unsigned long long); + ABI Changes in This Version --------------------------- @@ -171,13 +185,12 @@ C++23 Feature Support ^^^^^^^^^^^^^^^^^^^^^ - Removed the restriction to literal types in constexpr functions in C++23 mode. +- Extend lifetime of temporaries in mem-default-init for P2718R0. Clang now fully + supports `P2718R0 Lifetime extension in range-based for loops `_. + C++20 Feature Support ^^^^^^^^^^^^^^^^^^^^^ -C++17 Feature Support -^^^^^^^^^^^^^^^^^^^^^ -- The implementation of the relaxed template template argument matching rules is - more complete and reliable, and should provide more accurate diagnostics. Resolutions to C++ Defect Reports ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -216,6 +229,10 @@ Resolutions to C++ Defect Reports - Clang now allows trailing requires clause on explicit deduction guides. (`CWG2707: Deduction guides cannot have a trailing requires-clause `_). +- Clang now diagnoses a space in the first production of a ``literal-operator-id`` + by default. + (`CWG2521: User-defined literals and reserved identifiers `_). + C Language Changes ------------------ @@ -335,10 +352,6 @@ Improvements to Clang's diagnostics - Clang now diagnoses when the result of a [[nodiscard]] function is discarded after being cast in C. Fixes #GH104391. -- Clang now properly explains the reason a template template argument failed to - match a template template parameter, in terms of the C++17 relaxed matching rules - instead of the old ones. - - Don't emit duplicated dangling diagnostics. (#GH93386). - Improved diagnostic when trying to befriend a concept. (#GH45182). @@ -378,6 +391,16 @@ Improvements to Clang's diagnostics - Clang now emits a diagnostic note at the class declaration when the method definition does not match any declaration (#GH110638). +- Clang now omits warnings for extra parentheses in fold expressions with single expansion (#GH101863). + +- The warning for an unsupported type for a named register variable is now phrased ``unsupported type for named register variable``, + instead of ``bad type for named register variable``. This makes it clear that the type is not supported at all, rather than being + suboptimal in some way the error fails to mention (#GH111550). + +- Clang now emits a ``-Wdepredcated-literal-operator`` diagnostic, even if the + name was a reserved name, which we improperly allowed to suppress the + diagnostic. + Improvements to Clang's time-trace ---------------------------------- @@ -393,6 +416,8 @@ Bug Fixes in This Version - Fixed a crash when trying to transform a dependent address space type. Fixes #GH101685. - Fixed a crash when diagnosing format strings and encountering an empty delimited escape sequence (e.g., ``"\o{}"``). #GH102218 +- The warning emitted for an unsupported register variable type now points to + the unsupported type instead of the ``register`` keyword (#GH109776). Bug Fixes to Compiler Builtins ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -404,6 +429,8 @@ Bug Fixes to Compiler Builtins - ``__noop`` can now be used in a constant expression. (#GH102064) +- Fix ``__has_builtin`` incorrectly returning ``false`` for some C++ type traits. (#GH111477) + Bug Fixes to Attribute Support ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -438,8 +465,6 @@ Bug Fixes to C++ Support - Correctly check constraints of explicit instantiations of member functions. (#GH46029) - When performing partial ordering of function templates, clang now checks that the deduction was consistent. Fixes (#GH18291). -- Fixes to several issues in partial ordering of template template parameters, which - were documented in the test suite. - Fixed an assertion failure about a constraint of a friend function template references to a value with greater template depth than the friend function template. (#GH98258) - Clang now rebuilds the template parameters of out-of-line declarations and specializations in the context @@ -471,8 +496,20 @@ Bug Fixes to C++ Support - Fixed an issue deducing non-type template arguments of reference type. (#GH73460) - Fixed an issue in constraint evaluation, where type constraints on the lambda expression containing outer unexpanded parameters were not correctly expanded. (#GH101754) +- Fixes crashes with function template member specializations, and increases + conformance of explicit instantiation behaviour with MSVC. (#GH111266) - Fixed a bug in constraint expression comparison where the ``sizeof...`` expression was not handled properly in certain friend declarations. (#GH93099) +- Clang now instantiates the correct lambda call operator when a lambda's class type is + merged across modules. (#GH110401) +- Clang now uses the correct set of template argument lists when comparing the constraints of + out-of-line definitions and member templates explicitly specialized for a given implicit instantiation of + a class template. (#GH102320) +- Fix a crash when parsing a pseudo destructor involving an invalid type. (#GH111460) +- Fixed an assertion failure when invoking recovery call expressions with explicit attributes + and undeclared templates. (#GH107047, #GH49093) +- Clang no longer crashes when a lambda contains an invalid block declaration that contains an unexpanded + parameter pack. (#GH109148) Bug Fixes to AST Handling ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -510,6 +547,10 @@ OpenACC Specific Changes Target Specific Changes ----------------------- +- Clang now implements the Solaris-specific mangling of ``std::tm`` as + ``tm``, same for ``std::div_t``, ``std::ldiv_t``, and + ``std::lconv``, for Solaris ABI compatibility. (#GH33114) + AMDGPU Support ^^^^^^^^^^^^^^ @@ -611,10 +652,15 @@ AST Matchers - Fixed a crash when traverse lambda expr with invalid captures. (#GH106444) +- Ensure ``hasName`` matches template specializations across inline namespaces, + making `matchesNodeFullSlow` and `matchesNodeFullFast` consistent. + clang-format ------------ - Adds ``BreakBinaryOperations`` option. +- Adds ``TemplateNames`` option. +- Adds ``AlignFunctionDeclarations`` option to ``AlignConsecutiveDeclarations``. libclang -------- @@ -627,11 +673,18 @@ Static Analyzer New features ^^^^^^^^^^^^ +- Now CSA models `__builtin_*_overflow` functions. (#GH102602) + - MallocChecker now checks for ``ownership_returns(class, idx)`` and ``ownership_takes(class, idx)`` attributes with class names different from "malloc". Clang static analyzer now reports an error if class of allocation and deallocation function mismatches. `Documentation `__. +- Function effects, e.g. the ``nonblocking`` and ``nonallocating`` "performance constraint" + attributes, are now verified. For example, for functions declared with the ``nonblocking`` + attribute, the compiler can generate warnings about the use of any language features, or calls to + other functions, which may block. + Crash and bug fixes ^^^^^^^^^^^^^^^^^^^ diff --git a/clang/docs/ThreadSanitizer.rst b/clang/docs/ThreadSanitizer.rst index 98d5307d824f9..5dc78fa5a7a56 100644 --- a/clang/docs/ThreadSanitizer.rst +++ b/clang/docs/ThreadSanitizer.rst @@ -135,6 +135,14 @@ Limitations flag had been supplied if compiling without ``-fPIC``, and as though the ``-pie`` flag had been supplied if linking an executable. +Security Considerations +----------------------- + +ThreadSanitizer is a bug detection tool and its runtime is not meant to be +linked against production executables. While it may be useful for testing, +ThreadSanitizer's runtime was not developed with security-sensitive +constraints in mind and may compromise the security of the resulting executable. + Current Status -------------- diff --git a/clang/docs/UndefinedBehaviorSanitizer.rst b/clang/docs/UndefinedBehaviorSanitizer.rst index 0d1010b7dcb33..671db7f9f3671 100644 --- a/clang/docs/UndefinedBehaviorSanitizer.rst +++ b/clang/docs/UndefinedBehaviorSanitizer.rst @@ -256,6 +256,8 @@ Volatile The ``null``, ``alignment``, ``object-size``, ``local-bounds``, and ``vptr`` checks do not apply to pointers to types with the ``volatile`` qualifier. +.. _minimal-runtime: + Minimal Runtime =============== @@ -416,6 +418,15 @@ There are several limitations: * Check groups (like ``undefined``) can't be used in suppressions file, only fine-grained checks are supported. +Security Considerations +======================= + +UndefinedBehaviorSanitizer's runtime is meant for testing purposes and its usage +in production environment should be carefully considered from security +perspective as it may compromise the security of the resulting executable. +For security-sensitive applications consider using :ref:`Minimal Runtime +` or trap mode for all checks. + Supported Platforms =================== diff --git a/clang/docs/index.rst b/clang/docs/index.rst index 4a497f4d9bcc3..f4fdc93290a0d 100644 --- a/clang/docs/index.rst +++ b/clang/docs/index.rst @@ -104,7 +104,7 @@ Design Documents .. toctree:: :maxdepth: 1 - CodeOwners + Maintainers InternalsManual DriverInternals Multilib diff --git a/clang/include/clang/AST/APValue.h b/clang/include/clang/AST/APValue.h index c4206b73b1156..7869ee386689d 100644 --- a/clang/include/clang/AST/APValue.h +++ b/clang/include/clang/AST/APValue.h @@ -314,51 +314,95 @@ class APValue { DataType Data; public: + /// Creates an empty APValue of type None. APValue() : Kind(None) {} + /// Creates an integer APValue holding the given value. explicit APValue(APSInt I) : Kind(None) { MakeInt(); setInt(std::move(I)); } + /// Creates a float APValue holding the given value. explicit APValue(APFloat F) : Kind(None) { MakeFloat(); setFloat(std::move(F)); } + /// Creates a fixed-point APValue holding the given value. explicit APValue(APFixedPoint FX) : Kind(None) { MakeFixedPoint(std::move(FX)); } + /// Creates a vector APValue with \p N elements. The elements + /// are read from \p E. explicit APValue(const APValue *E, unsigned N) : Kind(None) { MakeVector(); setVector(E, N); } + /// Creates an integer complex APValue with the given real and imaginary + /// values. APValue(APSInt R, APSInt I) : Kind(None) { MakeComplexInt(); setComplexInt(std::move(R), std::move(I)); } + /// Creates a float complex APValue with the given real and imaginary values. APValue(APFloat R, APFloat I) : Kind(None) { MakeComplexFloat(); setComplexFloat(std::move(R), std::move(I)); } APValue(const APValue &RHS); APValue(APValue &&RHS); - APValue(LValueBase B, const CharUnits &O, NoLValuePath N, + /// Creates an lvalue APValue without an lvalue path. + /// \param Base The base of the lvalue. + /// \param Offset The offset of the lvalue. + /// \param IsNullPtr Whether this lvalue is a null pointer. + APValue(LValueBase Base, const CharUnits &Offset, NoLValuePath, bool IsNullPtr = false) : Kind(None) { - MakeLValue(); setLValue(B, O, N, IsNullPtr); - } - APValue(LValueBase B, const CharUnits &O, ArrayRef Path, - bool OnePastTheEnd, bool IsNullPtr = false) + MakeLValue(); + setLValue(Base, Offset, NoLValuePath{}, IsNullPtr); + } + /// Creates an lvalue APValue with an lvalue path. + /// \param Base The base of the lvalue. + /// \param Offset The offset of the lvalue. + /// \param Path The lvalue path. + /// \param OnePastTheEnd Whether this lvalue is one-past-the-end of the + /// subobject it points to. + /// \param IsNullPtr Whether this lvalue is a null pointer. + APValue(LValueBase Base, const CharUnits &Offset, + ArrayRef Path, bool OnePastTheEnd, + bool IsNullPtr = false) : Kind(None) { - MakeLValue(); setLValue(B, O, Path, OnePastTheEnd, IsNullPtr); - } + MakeLValue(); + setLValue(Base, Offset, Path, OnePastTheEnd, IsNullPtr); + } + /// Creates a new array APValue. + /// \param UninitArray Marker. Pass an empty UninitArray. + /// \param InitElts Number of elements you're going to initialize in the + /// array. + /// \param Size Full size of the array. APValue(UninitArray, unsigned InitElts, unsigned Size) : Kind(None) { MakeArray(InitElts, Size); } - APValue(UninitStruct, unsigned B, unsigned M) : Kind(None) { - MakeStruct(B, M); - } - explicit APValue(const FieldDecl *D, const APValue &V = APValue()) + /// Creates a new struct APValue. + /// \param UninitStruct Marker. Pass an empty UninitStruct. + /// \param NumBases Number of bases. + /// \param NumMembers Number of members. + APValue(UninitStruct, unsigned NumBases, unsigned NumMembers) : Kind(None) { + MakeStruct(NumBases, NumMembers); + } + /// Creates a new union APValue. + /// \param ActiveDecl The FieldDecl of the active union member. + /// \param ActiveValue The value of the active union member. + explicit APValue(const FieldDecl *ActiveDecl, + const APValue &ActiveValue = APValue()) : Kind(None) { - MakeUnion(); setUnion(D, V); + MakeUnion(); + setUnion(ActiveDecl, ActiveValue); } + /// Creates a new member pointer APValue. + /// \param Member Declaration of the member + /// \param IsDerivedMember Whether member is a derived one. + /// \param Path The path of the member. APValue(const ValueDecl *Member, bool IsDerivedMember, ArrayRef Path) : Kind(None) { MakeMemberPointer(Member, IsDerivedMember, Path); } + /// Creates a new address label diff APValue. + /// \param LHSExpr The left-hand side of the difference. + /// \param RHSExpr The right-hand side of the difference. APValue(const AddrLabelExpr* LHSExpr, const AddrLabelExpr* RHSExpr) : Kind(None) { MakeAddrLabelDiff(); setAddrLabelDiff(LHSExpr, RHSExpr); diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index a4d36f2eacd5d..1158209d7e5e0 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -1219,6 +1219,10 @@ class ASTContext : public RefCountedBase { // The decl is built when constructing 'BuiltinVaListDecl'. mutable Decl *VaListTagDecl = nullptr; + // Decl used to define the datastructure for the contract violation object + // used for C++ contracts + mutable Decl *BuiltinContractViolationRecordDecl = nullptr; + // Implicitly-declared type 'struct _GUID'. mutable TagDecl *MSGuidTagDecl = nullptr; @@ -2259,6 +2263,21 @@ class ASTContext : public RefCountedBase { return getTagDeclType(MSGuidTagDecl); } + QualType getBuiltinContractViolationRecordType() const { + return getRecordType( + cast(getBuiltinContractViolationRecordDecl())); + } + + Decl *getBuiltinContractViolationRecordDecl() const; + UnnamedGlobalConstantDecl * + BuildViolationObject(const ContractStmt *CS, + const FunctionDecl *CurDecl = nullptr); + + /// Calculate the evaluation semantic for a specific contract. + /// + /// This takes into account the default evaluation semantic for all contracts, + /// as well as any attributes on the specific contract itself. + /// Return whether a declaration to a builtin is allowed to be /// overloaded/redeclared. bool canBuiltinBeRedeclared(const FunctionDecl *) const; diff --git a/clang/include/clang/AST/ASTNodeTraverser.h b/clang/include/clang/AST/ASTNodeTraverser.h index a443a88bab1f2..648f2b97592a9 100644 --- a/clang/include/clang/AST/ASTNodeTraverser.h +++ b/clang/include/clang/AST/ASTNodeTraverser.h @@ -159,7 +159,7 @@ class ASTNodeTraverser // Some statements have custom mechanisms for dumping their children. if (isa(S) || isa(S) || - isa(S)) + isa(S) || isa(S)) return; if (Traversal == TK_IgnoreUnlessSpelledInSource && @@ -534,6 +534,9 @@ class ASTNodeTraverser if (const Expr *TRC = D->getTrailingRequiresClause()) Visit(TRC); + if (const ContractSpecifierDecl *CSD = D->getContracts()) + Visit(CSD); + if (Traversal == TK_IgnoreUnlessSpelledInSource && D->isDefaulted()) return; @@ -727,6 +730,17 @@ class ASTNodeTraverser Visit(D->getConstraintExpr()); } + void VisitContractSpecifierDecl(const ContractSpecifierDecl *CSD) { + for (auto *CS : CSD->contracts()) + Visit(CS); + } + + void VisitContractStmt(const ContractStmt *S) { + if (S->hasResultName()) + Visit(S->getResultName()); + Visit(S->getCond()); + } + void VisitImplicitConceptSpecializationDecl( const ImplicitConceptSpecializationDecl *CSD) { for (const TemplateArgument &Arg : CSD->getTemplateArguments()) diff --git a/clang/include/clang/AST/Attr.h b/clang/include/clang/AST/Attr.h index ac44e9fdd7c4e..725498e132fc2 100644 --- a/clang/include/clang/AST/Attr.h +++ b/clang/include/clang/AST/Attr.h @@ -197,6 +197,23 @@ class InheritableParamAttr : public InheritableAttr { } }; +class InheritableParamOrStmtAttr : public InheritableParamAttr { +protected: + InheritableParamOrStmtAttr(ASTContext &Context, + const AttributeCommonInfo &CommonInfo, + attr::Kind AK, bool IsLateParsed, + bool InheritEvenIfAlreadyPresent) + : InheritableParamAttr(Context, CommonInfo, AK, IsLateParsed, + InheritEvenIfAlreadyPresent) {} + +public: + // Implement isa/cast/dyncast/etc. + static bool classof(const Attr *A) { + return A->getKind() >= attr::FirstInheritableParamOrStmtAttr && + A->getKind() <= attr::LastInheritableParamOrStmtAttr; + } +}; + class HLSLAnnotationAttr : public InheritableAttr { protected: HLSLAnnotationAttr(ASTContext &Context, const AttributeCommonInfo &CommonInfo, diff --git a/clang/include/clang/AST/ComputeDependence.h b/clang/include/clang/AST/ComputeDependence.h index 6d3a51c379f9d..e96275e5f2e07 100644 --- a/clang/include/clang/AST/ComputeDependence.h +++ b/clang/include/clang/AST/ComputeDependence.h @@ -107,6 +107,7 @@ class ObjCSubscriptRefExpr; class ObjCIsaExpr; class ObjCIndirectCopyRestoreExpr; class ObjCMessageExpr; +class OpenACCAsteriskSizeExpr; // The following functions are called from constructors of `Expr`, so they // should not access anything beyond basic @@ -132,7 +133,8 @@ ExprDependence computeDependence(ArrayInitLoopExpr *E); ExprDependence computeDependence(ImplicitValueInitExpr *E); ExprDependence computeDependence(InitListExpr *E); ExprDependence computeDependence(ExtVectorElementExpr *E); -ExprDependence computeDependence(BlockExpr *E); +ExprDependence computeDependence(BlockExpr *E, + bool ContainsUnexpandedParameterPack); ExprDependence computeDependence(AsTypeExpr *E); ExprDependence computeDependence(DeclRefExpr *E, const ASTContext &Ctx); ExprDependence computeDependence(RecoveryExpr *E); @@ -203,6 +205,7 @@ ExprDependence computeDependence(ObjCSubscriptRefExpr *E); ExprDependence computeDependence(ObjCIsaExpr *E); ExprDependence computeDependence(ObjCIndirectCopyRestoreExpr *E); ExprDependence computeDependence(ObjCMessageExpr *E); +ExprDependence computeDependence(OpenACCAsteriskSizeExpr *E); } // namespace clang #endif diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index 7ff35d73df599..06b1f8788778e 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -55,6 +55,8 @@ namespace clang { class ASTContext; struct ASTTemplateArgumentListInfo; class CompoundStmt; +class ContractStmt; +class ContractSpecifierDecl; class DependentFunctionTemplateSpecializationInfo; class EnumDecl; class Expr; @@ -67,6 +69,7 @@ class Module; class NamespaceDecl; class ParmVarDecl; class RecordDecl; +class ResultNameDecl; class Stmt; class StringLiteral; class TagDecl; @@ -77,6 +80,7 @@ class TypeAliasTemplateDecl; class UnresolvedSetImpl; class VarTemplateDecl; enum class ImplicitParamKind; +enum class ContractKind; /// The top declaration context. class TranslationUnitDecl : public Decl, @@ -734,8 +738,8 @@ struct QualifierInfo { /// Contains type source information through TypeSourceInfo. class DeclaratorDecl : public ValueDecl { // A struct representing a TInfo, a trailing requires-clause and a syntactic - // qualifier, to be used for the (uncommon) case of out-of-line declarations - // and constrained function decls. + // qualifier, to be used for the (uncommon) case of out-of-line declarations, + // constrained function decls or functions with contracts. struct ExtInfo : public QualifierInfo { TypeSourceInfo *TInfo; Expr *TrailingRequiresClause = nullptr; @@ -1359,7 +1363,8 @@ class VarDecl : public DeclaratorDecl, public Redeclarable { private: APValue *evaluateValueImpl(SmallVectorImpl &Notes, - bool IsConstantInitialization) const; + bool IsConstantInitialization, + bool EnableContracts) const; public: /// Return the already-evaluated value of this variable's @@ -1391,8 +1396,13 @@ class VarDecl : public DeclaratorDecl, public Redeclarable { /// Evaluate the initializer of this variable to determine whether it's a /// constant initializer. Should only be called once, after completing the /// definition of the variable. - bool checkForConstantInitialization( - SmallVectorImpl &Notes) const; + bool + checkForConstantInitialization(SmallVectorImpl &Notes, + bool EnableContracts = true) const; + + bool + recheckForConstantInitialization(SmallVectorImpl &Notes, + bool EnableContracts = true) const; void setInitStyle(InitializationStyle Style) { VarDeclBits.InitStyle = Style; @@ -1995,6 +2005,10 @@ class FunctionDecl : public DeclaratorDecl, /// no formals. ParmVarDecl **ParamInfo = nullptr; + /// The contract sequence specified on this function declaration if there is + /// any, otherwise nullptr + ContractSpecifierDecl *Contracts = nullptr; + /// The active member of this union is determined by /// FunctionDeclBits.HasDefaultedOrDeletedInfo. union { @@ -2002,6 +2016,7 @@ class FunctionDecl : public DeclaratorDecl, LazyDeclStmtPtr Body; /// Information about a future defaulted function definition. DefaultedOrDeletedFunctionInfo *DefaultedOrDeletedInfo; + /// }; unsigned ODRHash; @@ -2091,7 +2106,8 @@ class FunctionDecl : public DeclaratorDecl, const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo, StorageClass S, bool UsesFPIntrin, bool isInlineSpecified, ConstexprSpecKind ConstexprKind, - Expr *TrailingRequiresClause = nullptr); + Expr *TrailingRequiresClause = nullptr, + ContractSpecifierDecl *Contracts = nullptr); using redeclarable_base = Redeclarable; @@ -2127,12 +2143,14 @@ class FunctionDecl : public DeclaratorDecl, TypeSourceInfo *TInfo, StorageClass SC, bool UsesFPIntrin = false, bool isInlineSpecified = false, bool hasWrittenPrototype = true, ConstexprSpecKind ConstexprKind = ConstexprSpecKind::Unspecified, - Expr *TrailingRequiresClause = nullptr) { + Expr *TrailingRequiresClause = nullptr, + ContractSpecifierDecl *Contracts = nullptr) { + DeclarationNameInfo NameInfo(N, NLoc); return FunctionDecl::Create(C, DC, StartLoc, NameInfo, T, TInfo, SC, UsesFPIntrin, isInlineSpecified, hasWrittenPrototype, ConstexprKind, - TrailingRequiresClause); + TrailingRequiresClause, Contracts); } static FunctionDecl * @@ -2140,7 +2158,7 @@ class FunctionDecl : public DeclaratorDecl, const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo, StorageClass SC, bool UsesFPIntrin, bool isInlineSpecified, bool hasWrittenPrototype, ConstexprSpecKind ConstexprKind, - Expr *TrailingRequiresClause); + Expr *TrailingRequiresClause, ContractSpecifierDecl *Contracts); static FunctionDecl *CreateDeserialized(ASTContext &C, GlobalDeclID ID); @@ -2644,6 +2662,10 @@ class FunctionDecl : public DeclaratorDecl, return const_cast(this)->getCanonicalDecl(); } + + FunctionDecl *getDeclForContracts(); + const FunctionDecl *getDeclForContracts() const; + unsigned getBuiltinID(bool ConsiderWrapperFunctions = false) const; // ArrayRef interface to parameters. @@ -3017,6 +3039,22 @@ class FunctionDecl : public DeclaratorDecl, return {}; } + /// Set the function level contracts for this function. Update the specifier + /// decl and it's children to have this declaration as a declaration context. + void setContracts(ContractSpecifierDecl *CSD); + + bool hasContracts() const { return Contracts != nullptr; } + + ContractSpecifierDecl *getContracts() const { return Contracts; } + + using ContractRange = llvm::iterator_range::iterator, bool (*)(const ContractStmt *)>>; + + // Convenience functions to get the preconditions and postconditions. + ArrayRef contracts() const; + ContractRange preconditions() const; + ContractRange postconditions() const; + // Implement isa/cast/dyncast/etc. static bool classof(const Decl *D) { return classofKind(D->getKind()); } static bool classofKind(Kind K) { diff --git a/clang/include/clang/AST/DeclBase.h b/clang/include/clang/AST/DeclBase.h index ee662ed73d7e0..a3447d1990975 100644 --- a/clang/include/clang/AST/DeclBase.h +++ b/clang/include/clang/AST/DeclBase.h @@ -687,6 +687,9 @@ class alignas(8) Decl { /// Whether this declaration comes from a named module. bool isInNamedModule() const; + /// Whether this declaration comes from a header unit. + bool isFromHeaderUnit() const; + /// Return true if this declaration has an attribute which acts as /// definition of the entity, such as 'alias' or 'ifunc'. bool hasDefiningAttr() const; diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h index 2693cc0e95b4b..ce0ddda3f09c7 100644 --- a/clang/include/clang/AST/DeclCXX.h +++ b/clang/include/clang/AST/DeclCXX.h @@ -72,6 +72,7 @@ class BaseUsingDecl; class TemplateDecl; class TemplateParameterList; class UsingDecl; +class ContractSpecifierDecl; /// Represents an access specifier followed by colon ':'. /// @@ -2077,9 +2078,11 @@ class CXXMethodDecl : public FunctionDecl { QualType T, TypeSourceInfo *TInfo, StorageClass SC, bool UsesFPIntrin, bool isInline, ConstexprSpecKind ConstexprKind, SourceLocation EndLocation, - Expr *TrailingRequiresClause = nullptr) + Expr *TrailingRequiresClause = nullptr, + ContractSpecifierDecl *Contracts = nullptr) : FunctionDecl(DK, C, RD, StartLoc, NameInfo, T, TInfo, SC, UsesFPIntrin, - isInline, ConstexprKind, TrailingRequiresClause) { + isInline, ConstexprKind, TrailingRequiresClause, + Contracts) { if (EndLocation.isValid()) setRangeEnd(EndLocation); } @@ -2090,7 +2093,8 @@ class CXXMethodDecl : public FunctionDecl { const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo, StorageClass SC, bool UsesFPIntrin, bool isInline, ConstexprSpecKind ConstexprKind, SourceLocation EndLocation, - Expr *TrailingRequiresClause = nullptr); + Expr *TrailingRequiresClause = nullptr, + ContractSpecifierDecl *Contracts = nullptr); static CXXMethodDecl *CreateDeserialized(ASTContext &C, GlobalDeclID ID); @@ -2558,7 +2562,8 @@ class CXXConstructorDecl final bool UsesFPIntrin, bool isInline, bool isImplicitlyDeclared, ConstexprSpecKind ConstexprKind, InheritedConstructor Inherited, - Expr *TrailingRequiresClause); + Expr *TrailingRequiresClause, + ContractSpecifierDecl *Contracts); void anchor() override; @@ -2601,7 +2606,8 @@ class CXXConstructorDecl final ExplicitSpecifier ES, bool UsesFPIntrin, bool isInline, bool isImplicitlyDeclared, ConstexprSpecKind ConstexprKind, InheritedConstructor Inherited = InheritedConstructor(), - Expr *TrailingRequiresClause = nullptr); + Expr *TrailingRequiresClause = nullptr, + ContractSpecifierDecl *Contracts = nullptr); void setExplicitSpecifier(ExplicitSpecifier ES) { assert((!ES.getExpr() || @@ -2820,10 +2826,11 @@ class CXXDestructorDecl : public CXXMethodDecl { const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo, bool UsesFPIntrin, bool isInline, bool isImplicitlyDeclared, ConstexprSpecKind ConstexprKind, - Expr *TrailingRequiresClause = nullptr) + Expr *TrailingRequiresClause = nullptr, + ContractSpecifierDecl *Contracts = nullptr) : CXXMethodDecl(CXXDestructor, C, RD, StartLoc, NameInfo, T, TInfo, SC_None, UsesFPIntrin, isInline, ConstexprKind, - SourceLocation(), TrailingRequiresClause) { + SourceLocation(), TrailingRequiresClause, Contracts) { setImplicit(isImplicitlyDeclared); } @@ -2835,7 +2842,8 @@ class CXXDestructorDecl : public CXXMethodDecl { const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo, bool UsesFPIntrin, bool isInline, bool isImplicitlyDeclared, ConstexprSpecKind ConstexprKind, - Expr *TrailingRequiresClause = nullptr); + Expr *TrailingRequiresClause = nullptr, + ContractSpecifierDecl *Contracts = nullptr); static CXXDestructorDecl *CreateDeserialized(ASTContext &C, GlobalDeclID ID); void setOperatorDelete(FunctionDecl *OD, Expr *ThisArg); @@ -2876,10 +2884,11 @@ class CXXConversionDecl : public CXXMethodDecl { TypeSourceInfo *TInfo, bool UsesFPIntrin, bool isInline, ExplicitSpecifier ES, ConstexprSpecKind ConstexprKind, SourceLocation EndLocation, - Expr *TrailingRequiresClause = nullptr) + Expr *TrailingRequiresClause = nullptr, + ContractSpecifierDecl *Contracts = nullptr) : CXXMethodDecl(CXXConversion, C, RD, StartLoc, NameInfo, T, TInfo, SC_None, UsesFPIntrin, isInline, ConstexprKind, - EndLocation, TrailingRequiresClause), + EndLocation, TrailingRequiresClause, Contracts), ExplicitSpec(ES) {} void anchor() override; @@ -2894,7 +2903,8 @@ class CXXConversionDecl : public CXXMethodDecl { const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo, bool UsesFPIntrin, bool isInline, ExplicitSpecifier ES, ConstexprSpecKind ConstexprKind, SourceLocation EndLocation, - Expr *TrailingRequiresClause = nullptr); + Expr *TrailingRequiresClause = nullptr, + ContractSpecifierDecl *Contracts = nullptr); static CXXConversionDecl *CreateDeserialized(ASTContext &C, GlobalDeclID ID); ExplicitSpecifier getExplicitSpecifier() { @@ -4350,8 +4360,7 @@ class MSGuidDecl : public ValueDecl, /// uniquified by value within a translation unit. /// /// These is currently only used to back the LValue returned by -/// __builtin_source_location, but could potentially be used for other similar -/// situations in the future. +/// __builtin_source_location, as well as for emitting parts of contracts violations. class UnnamedGlobalConstantDecl : public ValueDecl, public Mergeable, public llvm::FoldingSetNode { @@ -4400,6 +4409,182 @@ class UnnamedGlobalConstantDecl : public ValueDecl, const StreamingDiagnostic &operator<<(const StreamingDiagnostic &DB, AccessSpecifier AS); +/// A result name introduces in a post condition. For instance, given: +/// +/// int foo() post(r : r > 0); +/// +/// Where `r` refers to the value returned by the function +class ResultNameDecl : public ValueDecl { + friend class ContractSpecifierDecl; + /// The canonical declaration of a result name is the result name declared in + /// the first post condition (with a result name) on a particular function. + /// + /// The canonical decl is used as the key for the value of the return value + /// during codegen and constant evaluation. This is necessary because the + /// changes to the return value in the post conditions must be visible to + /// subsequent post conditions. + /// + // FIXME(EricWF): Remove this? I think we can dig the canonical result name + // out of the decl context? But I think that will be rather bug prone. Maybe + // we could make the ContractSpecifierDecl a DeclContext and dig it up from + // there? + ResultNameDecl *CanonicalResultName = nullptr; + + /// Whether this result name is using a dummy placeholder type to represent + /// the deduced return type of a non-template function until the actual return + /// type is known. + /// + /// Result names are only allowed on non-template functions with deduced + /// return types if that function declaration is also a definition. + bool HasInventedPlaceholderType = false; + + ResultNameDecl(DeclContext *DC, SourceLocation IdLoc, IdentifierInfo *Id, + QualType T, ResultNameDecl *CanonicalDecl = nullptr, + bool HasInventedPlaceholderType = false) + : ValueDecl(Decl::ResultName, DC, IdLoc, Id, T), + HasInventedPlaceholderType(HasInventedPlaceholderType) {} + + void setCanonicalResultName(ResultNameDecl *CRND) { + assert(CRND != this && + "setCanonicalResultName called on the canonical result"); + this->CanonicalResultName = CRND; + } + + void anchor() override; + +public: + friend class ASTDeclReader; + + static ResultNameDecl *Create(ASTContext &C, DeclContext *DC, + SourceLocation IdLoc, IdentifierInfo *Id, + QualType T, ResultNameDecl *CRND = nullptr, + bool HasInventedPlaceholderType = false); + static ResultNameDecl *CreateDeserialized(ASTContext &C, GlobalDeclID ID); + + using ValueDecl::getDeclName; + using ValueDecl::setType; + + /// Returns true if this declaration is the canonical result name declaration + /// (This is true if it doesn't reference another result name). + bool isCanonicalResultName() const { + return getCanonicalResultName() == this; + } + + ResultNameDecl *getCanonicalResultName() { + return CanonicalResultName ? CanonicalResultName : this; + } + + const ResultNameDecl *getCanonicalResultName() const { + return CanonicalResultName ? CanonicalResultName : this; + } + + bool hasInventedPlaceholderType() const { return HasInventedPlaceholderType; } + + static bool classof(const Decl *D) { return classofKind(D->getKind()); } + static bool classofKind(Kind K) { return K == Decl::ResultName; } +}; + +enum class ContractInstantiation { + Uninstantiated, + Instantiated, +}; + +/// Represents a series of contracts on a function declaration. +/// For instance: +/// +/// int foo(const int x) pre(x) post(r : x < r); +/// +/// This declaration also stores whether any of the contracts are invalid. +class ContractSpecifierDecl final + : public Decl, + private llvm::TrailingObjects { + friend class TrailingObjects; + friend class ASTDeclReader; + friend class ASTDeclWriter; + + bool IsUninstantiated = false; + + /// The number of contracts in this sequence. + unsigned NumContracts; + + static bool IsPreconditionPred(const ContractStmt *); + static bool IsPostconditionPred(const ContractStmt *); + static ResultNameDecl *ExtractResultName(const ContractStmt *); + + ContractSpecifierDecl(DeclContext *DC, SourceLocation Loc, + unsigned NumContracts) + : Decl(Decl::ContractSpecifier, DC, Loc), IsUninstantiated(DC->isDependentContext()), + NumContracts(NumContracts) { + std::uninitialized_fill_n(getTrailingObjects(), + NumContracts, nullptr); + } + + ContractSpecifierDecl(DeclContext *DC, SourceLocation Loc, + ArrayRef Contracts, bool IsInvalid); + + void setContracts(ArrayRef Contracts); + + using FilterRangeT = llvm::iterator_range::iterator, bool (*)(const ContractStmt *)>>; + + virtual void anchor(); + +public: + static ContractSpecifierDecl *Create(ASTContext &C, DeclContext *DC, + SourceLocation Loc, + ArrayRef Contracts, + bool IsInvalid); + static ContractSpecifierDecl * + CreateDeserialized(ASTContext &C, GlobalDeclID ID, unsigned NumContracts); + +public: + ArrayRef contracts() const { + return llvm::ArrayRef(getTrailingObjects(), NumContracts); + } + + /// Returns a range representing the preconditions in this contract sequence + /// (in order of declaration) + auto preconditions() const { + return llvm::make_filter_range(contracts(), IsPreconditionPred); + } + + /// Returns a range representing the postconditions in this contract sequence + /// (in order of declaration). + auto postconditions() const { + return llvm::make_filter_range(contracts(), IsPostconditionPred); + } + + /// Returns a range representing the result names in this contract sequence + /// (in order of declaration). + auto result_names() const { + return llvm::make_filter_range( + llvm::map_range(postconditions(), ExtractResultName), + [](ResultNameDecl *R) { return R != nullptr; }); + } + + /// Returns true if this function contract sequence contains result names & + /// those result names use an invented placeholder type to allow us to delay + /// the deduction of the return type. + bool hasInventedPlaceholdersTypes() const; + + unsigned getNumContracts() const { return NumContracts; } + + /// True if and only if there is a postcondition with a result name in this + /// contract sequence. + bool hasCanonicalResultName() const; + + /// Returns the canonical result name for this contract sequence. + const ResultNameDecl *getCanonicalResultName() const; + + /// Update the declaration context of this contract sequence and of any result name declarations contained within it. + void setOwningFunction(DeclContext *FD); + + SourceRange getSourceRange() const override LLVM_READONLY; + + static bool classof(const Decl *D) { return classofKind(D->getKind()); } + static bool classofKind(Kind K) { return K == Decl::ContractSpecifier; } +}; + } // namespace clang #endif // LLVM_CLANG_AST_DECLCXX_H diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h index 687715a22e9fd..141f58c4600af 100644 --- a/clang/include/clang/AST/DeclTemplate.h +++ b/clang/include/clang/AST/DeclTemplate.h @@ -781,15 +781,11 @@ class RedeclarableTemplateDecl : public TemplateDecl, EntryType *Entry, void *InsertPos); struct CommonBase { - CommonBase() : InstantiatedFromMember(nullptr, false) {} + CommonBase() {} /// The template from which this was most /// directly instantiated (or null). - /// - /// The boolean value indicates whether this template - /// was explicitly specialized. - llvm::PointerIntPair - InstantiatedFromMember; + RedeclarableTemplateDecl *InstantiatedFromMember = nullptr; /// If non-null, points to an array of specializations (including /// partial specializations) known only by their external declaration IDs. @@ -809,14 +805,19 @@ class RedeclarableTemplateDecl : public TemplateDecl, }; /// Pointer to the common data shared by all declarations of this - /// template. - mutable CommonBase *Common = nullptr; + /// template, and a flag indicating if the template is a member + /// specialization. + mutable llvm::PointerIntPair Common; + + CommonBase *getCommonPtrInternal() const { return Common.getPointer(); } /// Retrieves the "common" pointer shared by all (re-)declarations of /// the same template. Calling this routine may implicitly allocate memory /// for the common pointer. CommonBase *getCommonPtr() const; + void setCommonPtr(CommonBase *C) const { Common.setPointer(C); } + virtual CommonBase *newCommon(ASTContext &C) const = 0; // Construct a template decl with name, parameters, and templated element. @@ -857,15 +858,22 @@ class RedeclarableTemplateDecl : public TemplateDecl, /// template<> template /// struct X::Inner { /* ... */ }; /// \endcode - bool isMemberSpecialization() const { - return getCommonPtr()->InstantiatedFromMember.getInt(); + bool isMemberSpecialization() const { return Common.getInt(); } + + /// Determines whether any redeclaration of this template was + /// a specialization of a member template. + bool hasMemberSpecialization() const { + for (const auto *D : redecls()) { + if (D->isMemberSpecialization()) + return true; + } + return false; } /// Note that this member template is a specialization. void setMemberSpecialization() { - assert(getCommonPtr()->InstantiatedFromMember.getPointer() && - "Only member templates can be member template specializations"); - getCommonPtr()->InstantiatedFromMember.setInt(true); + assert(!isMemberSpecialization() && "already a member specialization"); + Common.setInt(true); } /// Retrieve the member template from which this template was @@ -905,12 +913,12 @@ class RedeclarableTemplateDecl : public TemplateDecl, /// void X::f(T, U); /// \endcode RedeclarableTemplateDecl *getInstantiatedFromMemberTemplate() const { - return getCommonPtr()->InstantiatedFromMember.getPointer(); + return getCommonPtr()->InstantiatedFromMember; } void setInstantiatedFromMemberTemplate(RedeclarableTemplateDecl *TD) { - assert(!getCommonPtr()->InstantiatedFromMember.getPointer()); - getCommonPtr()->InstantiatedFromMember.setPointer(TD); + assert(!getCommonPtr()->InstantiatedFromMember); + getCommonPtr()->InstantiatedFromMember = TD; } /// Retrieve the "injected" template arguments that correspond to the @@ -1989,6 +1997,8 @@ class ClassTemplateSpecializationDecl : public CXXRecordDecl, /// template arguments have been deduced. void setInstantiationOf(ClassTemplatePartialSpecializationDecl *PartialSpec, const TemplateArgumentList *TemplateArgs) { + assert(!isa(this) && + "A partial specialization cannot be instantiated from a template"); assert(!SpecializedTemplate.is() && "Already set to a class template partial specialization!"); auto *PS = new (getASTContext()) SpecializedPartialSpecialization(); @@ -2000,6 +2010,8 @@ class ClassTemplateSpecializationDecl : public CXXRecordDecl, /// Note that this class template specialization is an instantiation /// of the given class template. void setInstantiationOf(ClassTemplateDecl *TemplDecl) { + assert(!isa(this) && + "A partial specialization cannot be instantiated from a template"); assert(!SpecializedTemplate.is() && "Previously set to a class template partial specialization!"); SpecializedTemplate = TemplDecl; @@ -2187,19 +2199,23 @@ class ClassTemplatePartialSpecializationDecl /// struct X::Inner { /* ... */ }; /// \endcode bool isMemberSpecialization() const { - const auto *First = - cast(getFirstDecl()); - return First->InstantiatedFromMember.getInt(); + return InstantiatedFromMember.getInt(); } - /// Note that this member template is a specialization. - void setMemberSpecialization() { - auto *First = cast(getFirstDecl()); - assert(First->InstantiatedFromMember.getPointer() && - "Only member templates can be member template specializations"); - return First->InstantiatedFromMember.setInt(true); + /// Determines whether any redeclaration of this this class template partial + /// specialization was a specialization of a member partial specialization. + bool hasMemberSpecialization() const { + for (const auto *D : redecls()) { + if (cast(D) + ->isMemberSpecialization()) + return true; + } + return false; } + /// Note that this member template is a specialization. + void setMemberSpecialization() { return InstantiatedFromMember.setInt(true); } + /// Retrieves the injected specialization type for this partial /// specialization. This is not the same as the type-decl-type for /// this partial specialization, which is an InjectedClassNameType. @@ -2268,10 +2284,6 @@ class ClassTemplateDecl : public RedeclarableTemplateDecl { return static_cast(RedeclarableTemplateDecl::getCommonPtr()); } - void setCommonPtr(Common *C) { - RedeclarableTemplateDecl::Common = C; - } - public: friend class ASTDeclReader; @@ -2754,6 +2766,8 @@ class VarTemplateSpecializationDecl : public VarDecl, /// template arguments have been deduced. void setInstantiationOf(VarTemplatePartialSpecializationDecl *PartialSpec, const TemplateArgumentList *TemplateArgs) { + assert(!isa(this) && + "A partial specialization cannot be instantiated from a template"); assert(!SpecializedTemplate.is() && "Already set to a variable template partial specialization!"); auto *PS = new (getASTContext()) SpecializedPartialSpecialization(); @@ -2765,6 +2779,8 @@ class VarTemplateSpecializationDecl : public VarDecl, /// Note that this variable template specialization is an instantiation /// of the given variable template. void setInstantiationOf(VarTemplateDecl *TemplDecl) { + assert(!isa(this) && + "A partial specialization cannot be instantiated from a template"); assert(!SpecializedTemplate.is() && "Previously set to a variable template partial specialization!"); SpecializedTemplate = TemplDecl; @@ -2949,19 +2965,24 @@ class VarTemplatePartialSpecializationDecl /// U* X::Inner = (T*)(0) + 1; /// \endcode bool isMemberSpecialization() const { - const auto *First = - cast(getFirstDecl()); - return First->InstantiatedFromMember.getInt(); + return InstantiatedFromMember.getInt(); } - /// Note that this member template is a specialization. - void setMemberSpecialization() { - auto *First = cast(getFirstDecl()); - assert(First->InstantiatedFromMember.getPointer() && - "Only member templates can be member template specializations"); - return First->InstantiatedFromMember.setInt(true); + /// Determines whether any redeclaration of this this variable template + /// partial specialization was a specialization of a member partial + /// specialization. + bool hasMemberSpecialization() const { + for (const auto *D : redecls()) { + if (cast(D) + ->isMemberSpecialization()) + return true; + } + return false; } + /// Note that this member template is a specialization. + void setMemberSpecialization() { return InstantiatedFromMember.setInt(true); } + SourceRange getSourceRange() const override LLVM_READONLY; void Profile(llvm::FoldingSetNodeID &ID) const { diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h index 66c746cc25040..e095192532cba 100644 --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -734,7 +734,8 @@ class Expr : public ValueStmt { bool EvaluateAsInitializer(APValue &Result, const ASTContext &Ctx, const VarDecl *VD, SmallVectorImpl &Notes, - bool IsConstantInitializer) const; + bool IsConstantInitializer, + bool EvaluateContracts = true) const; /// EvaluateWithSubstitution - Evaluate an expression as if from the context /// of a call to the given function with the given arguments, inside an @@ -1235,6 +1236,17 @@ class OpaqueValueExpr : public Expr { } }; +// This enum is silly, but avoids creating overload sets where many adjacent +// arguments are all convertible to/from bool or int. +enum class ContractConstification { + CC_None, + CC_ApplyConst, +}; + +constexpr ContractConstification CC_None = ContractConstification::CC_None; +constexpr ContractConstification CC_ApplyConst = + ContractConstification::CC_ApplyConst; + /// A reference to a declared variable, function, enum, etc. /// [C99 6.5.1p2] /// @@ -1258,6 +1270,9 @@ class OpaqueValueExpr : public Expr { /// DeclRefExprBits.RefersToEnclosingVariableOrCapture /// Specifies when this declaration reference expression (validly) /// refers to an enclosed local or a captured variable. +/// DeclRefExprBits.IsInConstificationContext +/// Specifies when this declaration reference expression is in a context +/// where constification is applied. class DeclRefExpr final : public Expr, private llvm::TrailingObjectsgetStmtClass() == DeclRefExprClass; } @@ -2072,6 +2097,41 @@ class PredefinedExpr final } }; +/// This expression type represents an asterisk in an OpenACC Size-Expr, used in +/// the 'tile' and 'gang' clauses. It is of 'int' type, but should not be +/// evaluated. +class OpenACCAsteriskSizeExpr final : public Expr { + friend class ASTStmtReader; + SourceLocation AsteriskLoc; + + OpenACCAsteriskSizeExpr(SourceLocation AsteriskLoc, QualType IntTy) + : Expr(OpenACCAsteriskSizeExprClass, IntTy, VK_PRValue, OK_Ordinary), + AsteriskLoc(AsteriskLoc) {} + + void setAsteriskLocation(SourceLocation Loc) { AsteriskLoc = Loc; } + +public: + static OpenACCAsteriskSizeExpr *Create(const ASTContext &C, + SourceLocation Loc); + static OpenACCAsteriskSizeExpr *CreateEmpty(const ASTContext &C); + + SourceLocation getBeginLoc() const { return AsteriskLoc; } + SourceLocation getEndLoc() const { return AsteriskLoc; } + SourceLocation getLocation() const { return AsteriskLoc; } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == OpenACCAsteriskSizeExprClass; + } + // Iterators + child_range children() { + return child_range(child_iterator(), child_iterator()); + } + + const_child_range children() const { + return const_child_range(const_child_iterator(), const_child_iterator()); + } +}; + // This represents a use of the __builtin_sycl_unique_stable_name, which takes a // type-id, and at CodeGen time emits a unique string representation of the // type in a way that permits us to properly encode information about the SYCL @@ -2135,11 +2195,13 @@ class SYCLUniqueStableNameExpr final : public Expr { class ParenExpr : public Expr { SourceLocation L, R; Stmt *Val; + public: ParenExpr(SourceLocation l, SourceLocation r, Expr *val) : Expr(ParenExprClass, val->getType(), val->getValueKind(), val->getObjectKind()), L(l), R(r), Val(val) { + ParenExprBits.ProducedByFoldExpansion = false; setDependence(computeDependence(this)); } @@ -2171,6 +2233,13 @@ class ParenExpr : public Expr { const_child_range children() const { return const_child_range(&Val, &Val + 1); } + + bool isProducedByFoldExpansion() const { + return ParenExprBits.ProducedByFoldExpansion != 0; + } + void setIsProducedByFoldExpansion(bool ProducedByFoldExpansion = true) { + ParenExprBits.ProducedByFoldExpansion = ProducedByFoldExpansion; + } }; /// UnaryOperator - This represents the unary-expression's (except sizeof and @@ -4760,7 +4829,7 @@ enum class SourceLocIdentKind { /// Represents a function call to one of __builtin_LINE(), __builtin_COLUMN(), /// __builtin_FUNCTION(), __builtin_FUNCSIG(), __builtin_FILE(), -/// __builtin_FILE_NAME() or __builtin_source_location(). +/// __builtin_FILE_NAME(), __builtin_source_location(), or __builtin_source_location()2. class SourceLocExpr final : public Expr { SourceLocation BuiltinLoc, RParenLoc; DeclContext *ParentContext; @@ -6369,9 +6438,9 @@ class BlockExpr : public Expr { protected: BlockDecl *TheBlock; public: - BlockExpr(BlockDecl *BD, QualType ty) + BlockExpr(BlockDecl *BD, QualType ty, bool ContainsUnexpandedParameterPack) : Expr(BlockExprClass, ty, VK_PRValue, OK_Ordinary), TheBlock(BD) { - setDependence(computeDependence(this)); + setDependence(computeDependence(this, ContainsUnexpandedParameterPack)); } /// Build an empty block expression. diff --git a/clang/include/clang/AST/JSONNodeDumper.h b/clang/include/clang/AST/JSONNodeDumper.h index 55bd583e304e8..9422c8fceccfb 100644 --- a/clang/include/clang/AST/JSONNodeDumper.h +++ b/clang/include/clang/AST/JSONNodeDumper.h @@ -283,6 +283,7 @@ class JSONNodeDumper void VisitDeclRefExpr(const DeclRefExpr *DRE); void VisitSYCLUniqueStableNameExpr(const SYCLUniqueStableNameExpr *E); + void VisitOpenACCAsteriskSizeExpr(const OpenACCAsteriskSizeExpr *E); void VisitPredefinedExpr(const PredefinedExpr *PE); void VisitUnaryOperator(const UnaryOperator *UO); void VisitBinaryOperator(const BinaryOperator *BO); diff --git a/clang/include/clang/AST/OpenACCClause.h b/clang/include/clang/AST/OpenACCClause.h index 90f5b7fc9ab6f..f3a09eb651458 100644 --- a/clang/include/clang/AST/OpenACCClause.h +++ b/clang/include/clang/AST/OpenACCClause.h @@ -119,32 +119,6 @@ class OpenACCSeqClause : public OpenACCClause { } }; -// Not yet implemented, but the type name is necessary for 'seq' diagnostics, so -// this provides a basic, do-nothing implementation. We still need to add this -// type to the visitors/etc, as well as get it to take its proper arguments. -class OpenACCGangClause : public OpenACCClause { -protected: - OpenACCGangClause(SourceLocation BeginLoc, SourceLocation EndLoc) - : OpenACCClause(OpenACCClauseKind::Gang, BeginLoc, EndLoc) { - llvm_unreachable("Not yet implemented"); - } - -public: - static bool classof(const OpenACCClause *C) { - return C->getClauseKind() == OpenACCClauseKind::Gang; - } - - static OpenACCGangClause * - Create(const ASTContext &Ctx, SourceLocation BeginLoc, SourceLocation EndLoc); - - child_range children() { - return child_range(child_iterator(), child_iterator()); - } - const_child_range children() const { - return const_child_range(const_child_iterator(), const_child_iterator()); - } -}; - // Not yet implemented, but the type name is necessary for 'seq' diagnostics, so // this provides a basic, do-nothing implementation. We still need to add this // type to the visitors/etc, as well as get it to take its proper arguments. @@ -157,7 +131,7 @@ class OpenACCVectorClause : public OpenACCClause { public: static bool classof(const OpenACCClause *C) { - return C->getClauseKind() == OpenACCClauseKind::Gang; + return C->getClauseKind() == OpenACCClauseKind::Vector; } static OpenACCVectorClause * @@ -177,13 +151,13 @@ class OpenACCVectorClause : public OpenACCClause { class OpenACCWorkerClause : public OpenACCClause { protected: OpenACCWorkerClause(SourceLocation BeginLoc, SourceLocation EndLoc) - : OpenACCClause(OpenACCClauseKind::Gang, BeginLoc, EndLoc) { + : OpenACCClause(OpenACCClauseKind::Worker, BeginLoc, EndLoc) { llvm_unreachable("Not yet implemented"); } public: static bool classof(const OpenACCClause *C) { - return C->getClauseKind() == OpenACCClauseKind::Gang; + return C->getClauseKind() == OpenACCClauseKind::Worker; } static OpenACCWorkerClause * @@ -481,6 +455,35 @@ class OpenACCNumGangsClause final } }; +class OpenACCTileClause final + : public OpenACCClauseWithExprs, + public llvm::TrailingObjects { + OpenACCTileClause(SourceLocation BeginLoc, SourceLocation LParenLoc, + ArrayRef SizeExprs, SourceLocation EndLoc) + : OpenACCClauseWithExprs(OpenACCClauseKind::Tile, BeginLoc, LParenLoc, + EndLoc) { + std::uninitialized_copy(SizeExprs.begin(), SizeExprs.end(), + getTrailingObjects()); + setExprs(MutableArrayRef(getTrailingObjects(), SizeExprs.size())); + } + +public: + static bool classof(const OpenACCClause *C) { + return C->getClauseKind() == OpenACCClauseKind::Tile; + } + static OpenACCTileClause *Create(const ASTContext &C, SourceLocation BeginLoc, + SourceLocation LParenLoc, + ArrayRef SizeExprs, + SourceLocation EndLoc); + llvm::ArrayRef getSizeExprs() { + return OpenACCClauseWithExprs::getExprs(); + } + + llvm::ArrayRef getSizeExprs() const { + return OpenACCClauseWithExprs::getExprs(); + } +}; + /// Represents one of a handful of clauses that have a single integer /// expression. class OpenACCClauseWithSingleIntExpr : public OpenACCClauseWithExprs { @@ -506,6 +509,38 @@ class OpenACCClauseWithSingleIntExpr : public OpenACCClauseWithExprs { Expr *getIntExpr() { return hasIntExpr() ? getExprs()[0] : nullptr; }; }; +class OpenACCGangClause final + : public OpenACCClauseWithExprs, + public llvm::TrailingObjects { +protected: + OpenACCGangClause(SourceLocation BeginLoc, SourceLocation LParenLoc, + ArrayRef GangKinds, + ArrayRef IntExprs, SourceLocation EndLoc); + + OpenACCGangKind getGangKind(unsigned I) const { + return getTrailingObjects()[I]; + } + +public: + static bool classof(const OpenACCClause *C) { + return C->getClauseKind() == OpenACCClauseKind::Gang; + } + + size_t numTrailingObjects(OverloadToken) const { + return getNumExprs(); + } + + unsigned getNumExprs() const { return getExprs().size(); } + std::pair getExpr(unsigned I) const { + return {getGangKind(I), getExprs()[I]}; + } + + static OpenACCGangClause * + Create(const ASTContext &Ctx, SourceLocation BeginLoc, + SourceLocation LParenLoc, ArrayRef GangKinds, + ArrayRef IntExprs, SourceLocation EndLoc); +}; + class OpenACCNumWorkersClause : public OpenACCClauseWithSingleIntExpr { OpenACCNumWorkersClause(SourceLocation BeginLoc, SourceLocation LParenLoc, Expr *IntExpr, SourceLocation EndLoc); diff --git a/clang/include/clang/AST/OpenMPClause.h b/clang/include/clang/AST/OpenMPClause.h index 3a1d6852d2a70..2e48c1c3c72c8 100644 --- a/clang/include/clang/AST/OpenMPClause.h +++ b/clang/include/clang/AST/OpenMPClause.h @@ -930,6 +930,105 @@ class OMPSizesClause final } }; +/// This class represents the 'permutation' clause in the +/// '#pragma omp interchange' directive. +/// +/// \code{.c} +/// #pragma omp interchange permutation(2,1) +/// for (int i = 0; i < 64; ++i) +/// for (int j = 0; j < 64; ++j) +/// \endcode +class OMPPermutationClause final + : public OMPClause, + private llvm::TrailingObjects { + friend class OMPClauseReader; + friend class llvm::TrailingObjects; + + /// Location of '('. + SourceLocation LParenLoc; + + /// Number of arguments in the clause, and hence also the number of loops to + /// be permuted. + unsigned NumLoops; + + /// Sets the permutation index expressions. + void setArgRefs(ArrayRef VL) { + assert(VL.size() == NumLoops && "Expecting one expression per loop"); + llvm::copy(VL, static_cast(this) + ->template getTrailingObjects()); + } + + /// Build an empty clause. + explicit OMPPermutationClause(int NumLoops) + : OMPClause(llvm::omp::OMPC_permutation, SourceLocation(), + SourceLocation()), + NumLoops(NumLoops) {} + +public: + /// Build a 'permutation' clause AST node. + /// + /// \param C Context of the AST. + /// \param StartLoc Location of the 'permutation' identifier. + /// \param LParenLoc Location of '('. + /// \param EndLoc Location of ')'. + /// \param Args Content of the clause. + static OMPPermutationClause * + Create(const ASTContext &C, SourceLocation StartLoc, SourceLocation LParenLoc, + SourceLocation EndLoc, ArrayRef Args); + + /// Build an empty 'permutation' AST node for deserialization. + /// + /// \param C Context of the AST. + /// \param NumLoops Number of arguments in the clause. + static OMPPermutationClause *CreateEmpty(const ASTContext &C, + unsigned NumLoops); + + /// Sets the location of '('. + void setLParenLoc(SourceLocation Loc) { LParenLoc = Loc; } + + /// Returns the location of '('. + SourceLocation getLParenLoc() const { return LParenLoc; } + + /// Returns the number of list items. + unsigned getNumLoops() const { return NumLoops; } + + /// Returns the permutation index expressions. + ///@{ + MutableArrayRef getArgsRefs() { + return MutableArrayRef(static_cast(this) + ->template getTrailingObjects(), + NumLoops); + } + ArrayRef getArgsRefs() const { + return ArrayRef(static_cast(this) + ->template getTrailingObjects(), + NumLoops); + } + ///@} + + child_range children() { + MutableArrayRef Args = getArgsRefs(); + return child_range(reinterpret_cast(Args.begin()), + reinterpret_cast(Args.end())); + } + const_child_range children() const { + ArrayRef Args = getArgsRefs(); + return const_child_range(reinterpret_cast(Args.begin()), + reinterpret_cast(Args.end())); + } + + child_range used_children() { + return child_range(child_iterator(), child_iterator()); + } + const_child_range used_children() const { + return const_child_range(const_child_iterator(), const_child_iterator()); + } + + static bool classof(const OMPClause *T) { + return T->getClauseKind() == llvm::omp::OMPC_permutation; + } +}; + /// Representation of the 'full' clause of the '#pragma omp unroll' directive. /// /// \code diff --git a/clang/include/clang/AST/PrettyPrinter.h b/clang/include/clang/AST/PrettyPrinter.h index 332ac3c6a004a..91818776b770c 100644 --- a/clang/include/clang/AST/PrettyPrinter.h +++ b/clang/include/clang/AST/PrettyPrinter.h @@ -55,15 +55,17 @@ class PrintingCallbacks { /// This type is intended to be small and suitable for passing by value. /// It is very frequently copied. struct PrintingPolicy { + enum SuppressInlineNamespaceMode : uint8_t { None, Redundant, All }; + /// Create a default printing policy for the specified language. PrintingPolicy(const LangOptions &LO) : Indentation(2), SuppressSpecifiers(false), SuppressTagKeyword(LO.CPlusPlus), IncludeTagDefinition(false), SuppressScope(false), SuppressUnwrittenScope(false), - SuppressInlineNamespace(true), SuppressElaboration(false), - SuppressInitializers(false), ConstantArraySizeAsWritten(false), - AnonymousTagLocations(true), SuppressStrongLifetime(false), - SuppressLifetimeQualifiers(false), + SuppressInlineNamespace(SuppressInlineNamespaceMode::Redundant), + SuppressElaboration(false), SuppressInitializers(false), + ConstantArraySizeAsWritten(false), AnonymousTagLocations(true), + SuppressStrongLifetime(false), SuppressLifetimeQualifiers(false), SuppressTemplateArgsInCXXConstructors(false), SuppressDefaultTemplateArgs(true), Bool(LO.Bool), Nullptr(LO.CPlusPlus11 || LO.C23), NullptrTypeInNamespace(LO.CPlusPlus), @@ -141,10 +143,12 @@ struct PrintingPolicy { unsigned SuppressUnwrittenScope : 1; /// Suppress printing parts of scope specifiers that correspond - /// to inline namespaces, where the name is unambiguous with the specifier + /// to inline namespaces. + /// If Redudant, where the name is unambiguous with the specifier removed. + /// If All, even if the name is ambiguous with the specifier /// removed. - LLVM_PREFERRED_TYPE(bool) - unsigned SuppressInlineNamespace : 1; + LLVM_PREFERRED_TYPE(SuppressInlineNamespaceMode) + unsigned SuppressInlineNamespace : 2; /// Ignore qualifiers and tag keywords as specified by elaborated type sugar, /// instead letting the underlying type print as normal. diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h index cd9947f7ab980..31d59d83865b7 100644 --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -1614,6 +1614,12 @@ DEF_TRAVERSE_DECL(FriendDecl, { } }) +DEF_TRAVERSE_DECL(ContractSpecifierDecl, { + for (auto *C : D->contracts()) { + TRY_TO(TraverseStmt(C)); + } +}) + DEF_TRAVERSE_DECL(FriendTemplateDecl, { if (D->getFriendType()) TRY_TO(TraverseTypeLoc(D->getFriendType()->getTypeLoc())); @@ -2155,6 +2161,8 @@ DEF_TRAVERSE_DECL(BindingDecl, { TRY_TO(TraverseStmt(D->getBinding())); }) +DEF_TRAVERSE_DECL(ResultNameDecl, {}) + DEF_TRAVERSE_DECL(MSPropertyDecl, { TRY_TO(TraverseDeclaratorHelper(D)); }) DEF_TRAVERSE_DECL(MSGuidDecl, {}) @@ -2237,6 +2245,13 @@ bool RecursiveASTVisitor::TraverseFunctionHelper(FunctionDecl *D) { TRY_TO(TraverseStmt(TrailingRequiresClause)); } + // Visit any contracts attached to the function declaration.. +#if 0 // TODO(EricWF): Enable this. + if (auto *Contracts = D->getContracts()) { + TRY_TO(TraverseDecl(Contracts)); + } +#endif + if (CXXConstructorDecl *Ctor = dyn_cast(D)) { // Constructor initializers. for (auto *I : Ctor->inits()) { @@ -2451,6 +2466,15 @@ DEF_TRAVERSE_STMT(ObjCAtThrowStmt, {}) DEF_TRAVERSE_STMT(ObjCAtTryStmt, {}) DEF_TRAVERSE_STMT(ObjCForCollectionStmt, {}) DEF_TRAVERSE_STMT(ObjCAutoreleasePoolStmt, {}) +// FIXME(EricWF): This may have a declaration with a body eventually. +// Will that need a different implementation. +DEF_TRAVERSE_STMT(ContractStmt, { + if (S->hasResultName()) { + TRY_TO(TraverseDecl(S->getResultName())); + } + TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getCond()); + ShouldVisitChildren = false; +}) DEF_TRAVERSE_STMT(CXXForRangeStmt, { if (!getDerived().shouldVisitImplicitCode()) { @@ -2867,6 +2891,7 @@ DEF_TRAVERSE_STMT(ParenListExpr, {}) DEF_TRAVERSE_STMT(SYCLUniqueStableNameExpr, { TRY_TO(TraverseTypeLoc(S->getTypeSourceInfo()->getTypeLoc())); }) +DEF_TRAVERSE_STMT(OpenACCAsteriskSizeExpr, {}) DEF_TRAVERSE_STMT(PredefinedExpr, {}) DEF_TRAVERSE_STMT(ShuffleVectorExpr, {}) DEF_TRAVERSE_STMT(ConvertVectorExpr, {}) @@ -3347,6 +3372,14 @@ bool RecursiveASTVisitor::VisitOMPSizesClause(OMPSizesClause *C) { return true; } +template +bool RecursiveASTVisitor::VisitOMPPermutationClause( + OMPPermutationClause *C) { + for (Expr *E : C->getArgsRefs()) + TRY_TO(TraverseStmt(E)); + return true; +} + template bool RecursiveASTVisitor::VisitOMPFullClause(OMPFullClause *C) { return true; diff --git a/clang/include/clang/AST/Stmt.h b/clang/include/clang/AST/Stmt.h index 7aed83e9c68bb..fda2e0b73d60e 100644 --- a/clang/include/clang/AST/Stmt.h +++ b/clang/include/clang/AST/Stmt.h @@ -74,6 +74,7 @@ enum class CXXNewInitializationStyle; enum class PredefinedIdentKind; enum class SourceLocIdentKind; enum class StringLiteralKind; +enum class ContractKind; //===----------------------------------------------------------------------===// // AST classes for statements. @@ -447,6 +448,13 @@ class alignas(void *) Stmt { unsigned NonOdrUseReason : 2; LLVM_PREFERRED_TYPE(bool) unsigned IsImmediateEscalating : 1; + // True if the expression had constification applied due to a contract + // scope (and it wouldn't have otherwise been const). + LLVM_PREFERRED_TYPE(bool) + unsigned IsConstified : 1; + + LLVM_PREFERRED_TYPE(bool) + unsigned IsInContractContext : 1; /// The location of the declaration name itself. SourceLocation Loc; @@ -719,6 +727,18 @@ class alignas(void *) Stmt { unsigned Kind : 3; }; + class ParenExprBitfields { + friend class ASTStmtReader; + friend class ASTStmtWriter; + friend class ParenExpr; + + LLVM_PREFERRED_TYPE(ExprBitfields) + unsigned : NumExprBits; + + LLVM_PREFERRED_TYPE(bool) + unsigned ProducedByFoldExpansion : 1; + }; + class StmtExprBitfields { friend class ASTStmtReader; friend class StmtExpr; @@ -857,6 +877,26 @@ class alignas(void *) Stmt { SourceLocation RParenLoc; }; + class ContractAssertBitfields { + friend class ASTStmtReader; + friend class ASTStmtWriter; + friend class ContractStmt; + + LLVM_PREFERRED_TYPE(StmtBitfields) + unsigned : NumStmtBits; + + LLVM_PREFERRED_TYPE(bool) + unsigned HasResultName : 1; + + LLVM_PREFERRED_TYPE(ContractKind) + unsigned ContractKind : 2; + + enum { NumContractAssertBits = 3 }; + + LLVM_PREFERRED_TYPE(unsigned) + unsigned NumAttrs : 32 - NumStmtBits - NumContractAssertBits; + }; + class CXXNewExprBitfields { friend class ASTStmtReader; friend class ASTStmtWriter; @@ -1241,6 +1281,7 @@ class alignas(void *) Stmt { GenericSelectionExprBitfields GenericSelectionExprBits; PseudoObjectExprBitfields PseudoObjectExprBits; SourceLocExprBitfields SourceLocExprBits; + ParenExprBitfields ParenExprBits; // GNU Extensions. StmtExprBitfields StmtExprBits; @@ -1274,6 +1315,9 @@ class alignas(void *) Stmt { // C++ Coroutines expressions CoawaitExprBitfields CoawaitBits; + // C++ contracts + ContractAssertBitfields ContractAssertBits; + // Obj-C Expressions ObjCIndirectCopyRestoreExprBitfields ObjCIndirectCopyRestoreExprBits; diff --git a/clang/include/clang/AST/StmtCXX.h b/clang/include/clang/AST/StmtCXX.h index 8b4ef24ed376a..df517e05167fd 100644 --- a/clang/include/clang/AST/StmtCXX.h +++ b/clang/include/clang/AST/StmtCXX.h @@ -22,6 +22,8 @@ namespace clang { class VarDecl; +class ResultNameDecl; +class UnnamedGlobalConstantDecl; /// CXXCatchStmt - This represents a C++ catch block. /// @@ -524,6 +526,192 @@ class CoreturnStmt : public Stmt { } }; +class SemaContractHelper; + +class ContractStmt final + : public Stmt, + private llvm::TrailingObjects { + + // A Note on attributes: + // Contracts allow attribute to apppear in two places: + // [[#1]] pre [[#2]] ( ... ) + // + // Unlike most statements, contracts will have meaning ful attributes. For + // example, one which overrides the message in diagnostics. + // + // Because these attributes apply directly to the contract and it's semantics + // we store them directly in the contract statement. This is different from + // other statements, which use AttributedStmt to wrap the statement. + // However, the wrapping behavior makes it non-trivial for a contract to + // access its attributes. + // + // NOTE: We should consider only storing attributes of the form [[#2]] here. + + friend class ASTStmtReader; + friend TrailingObjects; + friend class SemaContractHelper; + + unsigned numTrailingObjects(OverloadToken) const { + return ContractAssertBits.HasResultName + 1; + } + + Stmt **getStmtPtr() { return getTrailingObjects(); } + + unsigned numTrailingObjects(OverloadToken) const { + return ContractAssertBits.NumAttrs; + } + + const Attr **getAttrPtr() { return getTrailingObjects(); } + + SourceLocation KeywordLoc; + + ArrayRef getSubStmts() const { + return llvm::ArrayRef(getTrailingObjects(), + numTrailingObjects(OverloadToken{})); + } + + MutableArrayRef getSubStmts() { + return llvm::MutableArrayRef(getTrailingObjects(), + numTrailingObjects(OverloadToken{})); + } + + void setResultName(DeclStmt *D) { + assert(hasResultName() && "no result name decl"); + getSubStmts().front() = D; + } + + void setCondition(Expr *E) { + assert(E && "no condition"); + getSubStmts().back() = E; + } + + void copyAttrs(ArrayRef Attrs) { + assert(Attrs.size() == ContractAssertBits.NumAttrs && + "wrong number of attributes"); + std::copy(Attrs.begin(), Attrs.end(), getAttrPtr()); + } + + ContractStmt(ContractKind CK, SourceLocation KeywordLoc, Expr *Condition, + DeclStmt *RN, ArrayRef Attrs = {}) + : Stmt(ContractStmtClass), KeywordLoc(KeywordLoc) { + ContractAssertBits.ContractKind = static_cast(CK); + ContractAssertBits.HasResultName = RN != nullptr; + ContractAssertBits.NumAttrs = Attrs.size(); + if (RN) + setResultName(RN); + setCondition(Condition); + if (!Attrs.empty()) + copyAttrs(Attrs); + } + + ContractStmt(EmptyShell Empty, ContractKind Kind, bool HasResultName, + unsigned NumAttrs = 0) + : Stmt(ContractStmtClass, Empty) { + ContractAssertBits.ContractKind = static_cast(Kind); + ContractAssertBits.HasResultName = HasResultName; + ContractAssertBits.NumAttrs = NumAttrs; + if (NumAttrs != 0) + std::fill_n(getAttrPtr(), NumAttrs, nullptr); + } + +public: + static ContractStmt *Create(const ASTContext &C, ContractKind Kind, + SourceLocation KeywordLoc, Expr *Condition, + DeclStmt *ResultNameDecl, + ArrayRef Attrs = {}); + + static ContractStmt *CreateEmpty(const ASTContext &C, ContractKind Kind, + bool HasResultName, unsigned NumAttrs); + + bool hasResultName() const { return ContractAssertBits.HasResultName; } + + DeclStmt *getResultNameDeclStmt() const { + return hasResultName() ? static_cast(getSubStmts().front()) + : nullptr; + } + + ResultNameDecl *getResultName() const; + + Expr *getCond() { return static_cast(getSubStmts().back()); } + const Expr *getCond() const { + return const_cast(this)->getCond(); + } + +public: + // Convert the contract semantic to a string for use in diagnostics. + static StringRef SemanticAsString(ContractEvaluationSemantic Semantic); + static ContractEvaluationSemantic *StringAsSemantic(StringRef Str); + + // Convert the contract kind (pre, post, or contract_assert) to a string + static StringRef ContractKindAsString(ContractKind Kind); + + /// Return the source text associated with this contract. + std::string getSourceText(const ASTContext &Ctx) const; + + /// Return the message for use in building the contract violation + /// diagnostic. It may be different than the source text. + std::string getMessage(const ASTContext &Ctx) const; + + /// Return the type of the contract. + /// It's one of 'pre', 'post', 'contract_assert'. + ContractKind getContractKind() const { + return static_cast(ContractAssertBits.ContractKind); + } + + StringRef getContractKindString() const { + return ContractStmt::ContractKindAsString(getContractKind()); + } + + /// Calculate the evaluation semantic for a specific contract. + /// + /// This takes into account the default evaluation semantic for all contracts, + /// as well as any attributes on the specific contract itself. + ContractEvaluationSemantic getSemantic(const ASTContext &Ctx) const; + + StringRef getSemanticString(const ASTContext &Ctx) const { + return ContractStmt::SemanticAsString(getSemantic(Ctx)); + } + + ArrayRef getAttrs() const { + return llvm::ArrayRef(getTrailingObjects(), + ContractAssertBits.NumAttrs); + } + // Access attributes of a particular type. + template const T *getAttrAs() const; + + SourceLocation getKeywordLoc() const { return KeywordLoc; } + SourceLocation getBeginLoc() const LLVM_READONLY { return KeywordLoc; } + SourceLocation getExpressionLoc() const LLVM_READONLY { + return getSubStmts().back()->getBeginLoc(); + } + SourceLocation getEndLoc() const LLVM_READONLY { + assert(!KeywordLoc.isInvalid() && + "Trying to get end location on an invalid node"); + return getSubStmts().back()->getEndLoc(); + } + + child_range children() { return getSubStmts(); } + const_child_range children() const { return getSubStmts(); } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == ContractStmtClass; + } +}; + +template const T *ContractStmt::getAttrAs() const { + const T *Result = nullptr; + for (auto *A : getAttrs()) { + if (const T *TA = dyn_cast(A)) { + assert(Result == nullptr); + Result = TA; +#ifdef NDEBUG + break; +#endif + } + } + return Result; +} + } // end namespace clang #endif diff --git a/clang/include/clang/AST/TextNodeDumper.h b/clang/include/clang/AST/TextNodeDumper.h index 57100e7ede171..b5733900d1cdf 100644 --- a/clang/include/clang/AST/TextNodeDumper.h +++ b/clang/include/clang/AST/TextNodeDumper.h @@ -410,7 +410,10 @@ class TextNodeDumper void VisitHLSLOutArgExpr(const HLSLOutArgExpr *E); void VisitOpenACCConstructStmt(const OpenACCConstructStmt *S); void VisitOpenACCLoopConstruct(const OpenACCLoopConstruct *S); + void VisitOpenACCAsteriskSizeExpr(const OpenACCAsteriskSizeExpr *S); void VisitEmbedExpr(const EmbedExpr *S); + void VisitResultNameDecl(const ResultNameDecl *D); + void VisitContractStmt(const ContractStmt *S); void VisitAtomicExpr(const AtomicExpr *AE); }; diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h index 7126940058bae..8ff04cf89a6b9 100644 --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -49,6 +49,7 @@ #include "llvm/Support/PointerLikeTypeTraits.h" #include "llvm/Support/TrailingObjects.h" #include "llvm/Support/type_traits.h" +#include #include #include #include @@ -119,6 +120,8 @@ class EnumDecl; class Expr; class ExtQualsTypeCommonBase; class FunctionDecl; +class FunctionEffectsRef; +class FunctionEffectKindSet; class FunctionEffectSet; class IdentifierInfo; class NamedDecl; @@ -4712,12 +4715,13 @@ class FunctionEffect { public: /// Identifies the particular effect. enum class Kind : uint8_t { - None = 0, - NonBlocking = 1, - NonAllocating = 2, - Blocking = 3, - Allocating = 4 + NonBlocking, + NonAllocating, + Blocking, + Allocating, + Last = Allocating }; + constexpr static size_t KindCount = static_cast(Kind::Last) + 1; /// Flags describing some behaviors of the effect. using Flags = unsigned; @@ -4743,8 +4747,6 @@ class FunctionEffect { // be considered for uniqueness. public: - FunctionEffect() : FKind(Kind::None) {} - explicit FunctionEffect(Kind K) : FKind(K) {} /// The kind of the effect. @@ -4773,8 +4775,6 @@ class FunctionEffect { case Kind::Blocking: case Kind::Allocating: return 0; - case Kind::None: - break; } llvm_unreachable("unknown effect kind"); } @@ -4782,26 +4782,36 @@ class FunctionEffect { /// The description printed in diagnostics, e.g. 'nonblocking'. StringRef name() const; - /// Return true if the effect is allowed to be inferred on the callee, - /// which is either a FunctionDecl or BlockDecl. + friend raw_ostream &operator<<(raw_ostream &OS, + const FunctionEffect &Effect) { + OS << Effect.name(); + return OS; + } + + /// Determine whether the effect is allowed to be inferred on the callee, + /// which is either a FunctionDecl or BlockDecl. If the returned optional + /// is empty, inference is permitted; otherwise it holds the effect which + /// blocked inference. /// Example: This allows nonblocking(false) to prevent inference for the /// function. - bool canInferOnFunction(const Decl &Callee) const; + std::optional + effectProhibitingInference(const Decl &Callee, + FunctionEffectKindSet CalleeFX) const; // Return false for success. When true is returned for a direct call, then the // FE_InferrableOnCallees flag may trigger inference rather than an immediate // diagnostic. Caller should be assumed to have the effect (it may not have it // explicitly when inferring). bool shouldDiagnoseFunctionCall(bool Direct, - ArrayRef CalleeFX) const; + FunctionEffectKindSet CalleeFX) const; - friend bool operator==(const FunctionEffect &LHS, const FunctionEffect &RHS) { + friend bool operator==(FunctionEffect LHS, FunctionEffect RHS) { return LHS.FKind == RHS.FKind; } - friend bool operator!=(const FunctionEffect &LHS, const FunctionEffect &RHS) { + friend bool operator!=(FunctionEffect LHS, FunctionEffect RHS) { return !(LHS == RHS); } - friend bool operator<(const FunctionEffect &LHS, const FunctionEffect &RHS) { + friend bool operator<(FunctionEffect LHS, FunctionEffect RHS) { return LHS.FKind < RHS.FKind; } }; @@ -4829,13 +4839,14 @@ struct FunctionEffectWithCondition { FunctionEffect Effect; EffectConditionExpr Cond; - FunctionEffectWithCondition() = default; - FunctionEffectWithCondition(const FunctionEffect &E, - const EffectConditionExpr &C) + FunctionEffectWithCondition(FunctionEffect E, const EffectConditionExpr &C) : Effect(E), Cond(C) {} /// Return a textual description of the effect, and its condition, if any. std::string description() const; + + friend raw_ostream &operator<<(raw_ostream &OS, + const FunctionEffectWithCondition &CFE); }; /// Support iteration in parallel through a pair of FunctionEffect and @@ -4940,6 +4951,85 @@ class FunctionEffectsRef { void dump(llvm::raw_ostream &OS) const; }; +/// A mutable set of FunctionEffect::Kind. +class FunctionEffectKindSet { + // For now this only needs to be a bitmap. + constexpr static size_t EndBitPos = FunctionEffect::KindCount; + using KindBitsT = std::bitset; + + KindBitsT KindBits{}; + + explicit FunctionEffectKindSet(KindBitsT KB) : KindBits(KB) {} + + // Functions to translate between an effect kind, starting at 1, and a + // position in the bitset. + + constexpr static size_t kindToPos(FunctionEffect::Kind K) { + return static_cast(K); + } + + constexpr static FunctionEffect::Kind posToKind(size_t Pos) { + return static_cast(Pos); + } + + // Iterates through the bits which are set. + class iterator { + const FunctionEffectKindSet *Outer = nullptr; + size_t Idx = 0; + + // If Idx does not reference a set bit, advance it until it does, + // or until it reaches EndBitPos. + void advanceToNextSetBit() { + while (Idx < EndBitPos && !Outer->KindBits.test(Idx)) + ++Idx; + } + + public: + iterator(); + iterator(const FunctionEffectKindSet &O, size_t I) : Outer(&O), Idx(I) { + advanceToNextSetBit(); + } + bool operator==(const iterator &Other) const { return Idx == Other.Idx; } + bool operator!=(const iterator &Other) const { return Idx != Other.Idx; } + + iterator operator++() { + ++Idx; + advanceToNextSetBit(); + return *this; + } + + FunctionEffect operator*() const { + assert(Idx < EndBitPos && "Dereference of end iterator"); + return FunctionEffect(posToKind(Idx)); + } + }; + +public: + FunctionEffectKindSet() = default; + explicit FunctionEffectKindSet(FunctionEffectsRef FX) { insert(FX); } + + iterator begin() const { return iterator(*this, 0); } + iterator end() const { return iterator(*this, EndBitPos); } + + void insert(FunctionEffect Effect) { KindBits.set(kindToPos(Effect.kind())); } + void insert(FunctionEffectsRef FX) { + for (FunctionEffect Item : FX.effects()) + insert(Item); + } + void insert(FunctionEffectKindSet Set) { KindBits |= Set.KindBits; } + + bool empty() const { return KindBits.none(); } + bool contains(const FunctionEffect::Kind EK) const { + return KindBits.test(kindToPos(EK)); + } + void dump(llvm::raw_ostream &OS) const; + + static FunctionEffectKindSet difference(FunctionEffectKindSet LHS, + FunctionEffectKindSet RHS) { + return FunctionEffectKindSet(LHS.KindBits & ~RHS.KindBits); + } +}; + /// A mutable set of FunctionEffects and possibly conditions attached to them. /// Used to compare and merge effects on declarations. /// diff --git a/clang/include/clang/ASTMatchers/ASTMatchers.h b/clang/include/clang/ASTMatchers/ASTMatchers.h index f1c72efc23878..7674a65735487 100644 --- a/clang/include/clang/ASTMatchers/ASTMatchers.h +++ b/clang/include/clang/ASTMatchers/ASTMatchers.h @@ -2772,6 +2772,153 @@ extern const internal::VariadicDynCastAllOfMatcher extern const internal::VariadicDynCastAllOfMatcher designatedInitExpr; + + +/// Matches C++ contract statements. +/// +/// Example matches pre(x), post(x), and contract_assert(x) +/// \code +/// void f(int x) pre(x) post(x) { contract_assert(x); } +/// \endcode +extern const internal::VariadicDynCastAllOfMatcher + contractStmt; + +/// Matches C++ contract statements. +/// +/// Example matches the node containing both pre(x) and post(x), but not +/// contract_assert(x) +/// \code +/// void f(int x) pre(x) post(x) { contract_assert(x); } +/// \endcode +extern const internal::VariadicDynCastAllOfMatcher + contractSpecifierDecl; + +/// Matches the contract specifier sequence on a function declaration. +/// +/// For example, given the code- +/// \code +/// void f(int x) pre(x) post(x) { contract_assert(x); } +/// \endcode +/// functionDecl(hasContractSpecifier())) +/// matches the function declaration of 'f'. +AST_MATCHER_P(FunctionDecl, hasContractSpecifier, + internal::Matcher, InnerMatcher) { + if (const auto *RetValue = Node.getContracts()) + return InnerMatcher.matches(*RetValue, Finder, Builder); + return false; +} + +/// Matches a contract that's part of a function contract specifier +/// +/// Given +/// \code +/// int foo(int x) post(r : r) { return x; } +/// \endcode +/// functionDecl(hasContractSpecifier(hasAnyContract(hasResultName(hasName("b"))))) +/// matches \code post(r : r) \endcode +AST_MATCHER_P(ContractSpecifierDecl, hasAnyContract, + internal::Matcher, InnerMatcher) { + return matchesFirstInPointerRange(InnerMatcher, Node.contracts().begin(), + Node.contracts().end(), Finder, + Builder) != Node.contracts().end(); +} + +/// Matches any using shadow declaration. +/// +/// Given +/// \code +/// int foo(int x) post(r : r) { return x; } +/// \endcode +/// functionDecl(hasContractSpecifier(hasAnyContract(hasResultName(hasName("b"))))) +/// matches \code post(r : r) \endcode +AST_MATCHER_P(ContractStmt, hasResultName, internal::Matcher, + InnerMatcher) { + if (const auto *RetValue = Node.getResultName()) + return InnerMatcher.matches(*RetValue, Finder, Builder); + return false; +} + +/// Matches each method overridden by the given method. This matcher may +/// produce multiple matches. +/// +/// Given +/// \code +/// int f() pre(x) post(x) post(r: r) { contract_assert(x); return x; } +/// \endcode +/// functionDecl(hasContractSpecifier(forEachContract(isPostcondition().bind("post")))) +/// matches 'post(x)' and 'post(r: r)' +/// +AST_MATCHER_P(ContractSpecifierDecl, forEachContract, + internal::Matcher, InnerMatcher) { + BoundNodesTreeBuilder Result; + bool Matched = false; + for (const auto *Contract : Node.contracts()) { + BoundNodesTreeBuilder ContractBuilder(*Builder); + const bool ContractMatched = + InnerMatcher.matches(*Contract, Finder, &ContractBuilder); + if (ContractMatched) { + Matched = true; + Result.addMatch(ContractBuilder); + } + } + *Builder = std::move(Result); + return Matched; +} + +/// Matches a contract precondition. +/// +/// Example matches pre(x) but not post(x) or contract_assert(x) (matcher = +/// contractStmt(isPrecondition())) +/// \code +/// auto f = [x=3] () pre(x) post(x) { contract_assert(x); return x; }; +/// \endcode +AST_MATCHER(ContractStmt, isPrecondition) { + return Node.getContractKind() == ContractKind::Pre; +} + +/// Matches a contract postcondition. +/// +/// Example matches post(x) but not pre(x) or contract_assert(x) (matcher = contractStmt(isPostcondition())) +/// \code +/// auto f = [x=3]() { return x; }; +/// \endcode +AST_MATCHER(ContractStmt, isPostcondition) { + return Node.getContractKind() == ContractKind::Post; +} + +/// Matches a contract postcondition. +/// +/// Example matches contract_assert(x) but not pre(x) or post(x) (matcher = contractStmt(isContractAssert())) +/// \code +/// int f(int x) pre(x) post(x) { contract_assert(x); } +/// \endcode +AST_MATCHER(ContractStmt, isContractAssert) { + return Node.getContractKind() == ContractKind::Assert; +} + +/// Matches a function declaration that contains either pre or post conditions. +/// It does not match functions whos body contain `contract_assert` statements. +/// +/// Example matches 'f' but not 'g' (matcher = functionDecl(hasFunctionContracts())) +/// +/// \code +/// void f(int x) pre(x) post(x) { } +/// void g(int x) { contract_assert(x); } +/// \endcode +AST_MATCHER(FunctionDecl, hasFunctionContracts) { + return Node.getContracts() != nullptr; +} + +/// Matches C++ result name declaration inside a post condition. +/// +/// Example matches \c r in \c post(r : x) +/// \code +/// int f(int x) post(r : x); +/// \endcode +extern const internal::VariadicDynCastAllOfMatcher + resultNameDecl; + + /// Matches designated initializer expressions that contain /// a specific number of designators. /// @@ -5596,17 +5743,17 @@ AST_POLYMORPHIC_MATCHER_P(hasInitStatement, } /// Matches the condition expression of an if statement, for loop, -/// switch statement or conditional operator. +/// switch statement, conditional operator, or contract. /// /// Example matches true (matcher = hasCondition(cxxBoolLiteral(equals(true)))) /// \code /// if (true) {} /// \endcode -AST_POLYMORPHIC_MATCHER_P( - hasCondition, - AST_POLYMORPHIC_SUPPORTED_TYPES(IfStmt, ForStmt, WhileStmt, DoStmt, - SwitchStmt, AbstractConditionalOperator), - internal::Matcher, InnerMatcher) { +AST_POLYMORPHIC_MATCHER_P(hasCondition, + AST_POLYMORPHIC_SUPPORTED_TYPES( + IfStmt, ForStmt, WhileStmt, DoStmt, SwitchStmt, + AbstractConditionalOperator, ContractStmt), + internal::Matcher, InnerMatcher) { const Expr *const Condition = Node.getCond(); return (Condition != nullptr && InnerMatcher.matches(*Condition, Finder, Builder)); diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index fbcbf0ed41641..c0a42e95b7d55 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -759,6 +759,11 @@ class TargetSpecificAttr { /// redeclarations, even when it's written on a parameter. class InheritableParamAttr : InheritableAttr; +/// A attribute that is either a declaration attribute or a statement attribute, +/// and if used as a declaration attribute, is inherited by later +/// redeclarations, even when it's written on a parameter. +class InheritableParamOrStmtAttr : InheritableParamAttr; + /// An attribute which changes the ABI rules for a specific parameter. class ParameterABIAttr : InheritableParamAttr { let Subjects = SubjectList<[ParmVar]>; @@ -928,7 +933,7 @@ def AnalyzerNoReturn : InheritableAttr { let Documentation = [Undocumented]; } -def Annotate : InheritableParamAttr { +def Annotate : InheritableParamOrStmtAttr { let Spellings = [Clang<"annotate">]; let Args = [StringArgument<"Annotation">, VariadicExprArgument<"Args">]; // Ensure that the annotate attribute can be used with @@ -3359,6 +3364,47 @@ def Unavailable : InheritableAttr { let MeaningfulToClassTemplateDefinition = 1; } +def ContractGroup : StmtAttr { + let Spellings = [CXX11<"clang", "contract_group">]; + let Args = [ StringArgument<"Group"> ]; + let Subjects = SubjectList<[ContractStmt], ErrorDiag, "contract_assert">; + let Documentation = [Undocumented]; +} + +def ContractSemantic : StmtAttr { + let Spellings = [CXX11<"clang", "contract_semantic">]; + let Args = [ + EnumArgument<"Semantic", "ContractEvaluationSemantic", /*is_string=*/true, + ["ignore", "observe", "enforce", "quick_enforce"], + ["Ignore", "Observe", "Enforce", "QuickEnforce"], + /*opt=*/0, /*fake=*/0, /*isExternalType=*/1, /*isCovered=*/1>, + DefaultBoolArgument<"Override", 0, /*fake*/ 0> + ]; + let Subjects = SubjectList<[ContractStmt], ErrorDiag, "contract_assert">; + let Documentation = [Undocumented]; +} + + +def ContractEmission : StmtAttr { + let Spellings = [CXX11<"clang", "contract_emission">]; + let Args = [ + EnumArgument<"Emission", "ContractEmissionStyle", /*is_string=*/true, + ["in_caller", "in_definition"], + ["InCaller", "InDefinition"], + /*opt=*/0, /*fake=*/0, /*isExternalType=*/1, /*isCovered=*/1> + ]; + let Subjects = SubjectList<[ContractStmt], ErrorDiag, "contract_assert">; + let Documentation = [Undocumented]; +} + + +def ContractMessage : StmtAttr { + let Spellings = [CXX11<"clang", "contract_message">]; + let Args = [ StringArgument<"Message">, DefaultBoolArgument<"IncludeSourceText", 1, /*fake*/ 0> ]; + let Subjects = SubjectList<[ContractStmt], ErrorDiag, "contract_assert">; + let Documentation = [Undocumented]; +} + def DiagnoseIf : InheritableAttr { // Does not have a [[]] spelling because this attribute requires the ability // to parse function arguments but the attribute is not written in the type diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index 53d88482698f0..b1512e22ee2dd 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -8482,9 +8482,9 @@ compiler warnings: - A redeclaration of a ``nonblocking`` or ``nonallocating`` function must also be declared with the same attribute (or a stronger one). A redeclaration may add an attribute. -The warnings are controlled by ``-Wfunction-effects``, which is enabled by default. +The warnings are controlled by ``-Wfunction-effects``, which is disabled by default. -In a future commit, the compiler will diagnose function calls from ``nonblocking`` and ``nonallocating`` +The compiler also diagnoses function calls from ``nonblocking`` and ``nonallocating`` functions to other functions which lack the appropriate attribute. }]; } diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td index 5b504666365b3..e13d9b16915d4 100644 --- a/clang/include/clang/Basic/Builtins.td +++ b/clang/include/clang/Basic/Builtins.td @@ -2667,6 +2667,7 @@ def SetJmpEx : MSLibBuiltin<"setjmpex.h"> { let Prototype = "int(jmp_buf)"; } + // C99 library functions // C99 stdarg.h def VaStart : LibBuiltin<"stdarg.h"> { @@ -4739,6 +4740,18 @@ def HLSLClamp : LangBuiltin<"HLSL_LANG"> { let Prototype = "void(...)"; } +def HLSLCross: LangBuiltin<"HLSL_LANG"> { + let Spellings = ["__builtin_hlsl_cross"]; + let Attributes = [NoThrow, Const]; + let Prototype = "void(...)"; +} + +def HLSLDegrees : LangBuiltin<"HLSL_LANG"> { + let Spellings = ["__builtin_hlsl_elementwise_degrees"]; + let Attributes = [NoThrow, Const]; + let Prototype = "void(...)"; +} + def HLSLDotProduct : LangBuiltin<"HLSL_LANG"> { let Spellings = ["__builtin_hlsl_dot"]; let Attributes = [NoThrow, Const]; @@ -4818,6 +4831,12 @@ def HLSLStep: LangBuiltin<"HLSL_LANG"> { let Prototype = "void(...)"; } +def HLSLRadians : LangBuiltin<"HLSL_LANG"> { + let Spellings = ["__builtin_hlsl_elementwise_radians"]; + let Attributes = [NoThrow, Const]; + let Prototype = "void(...)"; +} + // Builtins for XRay. def XRayCustomEvent : Builtin { let Spellings = ["__xray_customevent"]; diff --git a/clang/include/clang/Basic/ContractOptions.h b/clang/include/clang/Basic/ContractOptions.h new file mode 100644 index 0000000000000..f812313d9b570 --- /dev/null +++ b/clang/include/clang/Basic/ContractOptions.h @@ -0,0 +1,165 @@ +//===- ContractsOptions.h - C++ Contract Options ----------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +/// \file +/// Defines common enums and types used by contracts and contract attributes. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_BASIC_CONTRACT_OPTIONS_H +#define LLVM_CLANG_BASIC_CONTRACT_OPTIONS_H + +#include "clang/Basic/LLVM.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" +#include +#include +#include + +namespace clang { +using llvm::StringRef; +class ASTContext; + +enum class ContractTag { No = 0, Yes = 1 }; + +constexpr ContractTag NotAContract = ContractTag::No; +constexpr ContractTag IsAContract = ContractTag::Yes; +constexpr ContractTag InContract = ContractTag::Yes; +constexpr ContractTag NotInContract = ContractTag::No; + +enum class ContractGroupDiagnostic { + // The remaining values map to %select values in diagnostics in both + // DiagnosticSemaKinds.td and DiagnosticDriverKinds.td. + InvalidFirstChar, + InvalidChar, + InvalidLastChar, + EmptySubGroup, + Empty, + InvalidSemantic, +}; + +/// The kind of contract. +/// +/// NOTE: These enumerations _do not_ match the values in +/// std::contracts::assertion_kind because they start at 1. +enum class ContractKind { + /// A function precondition + Pre, + + /// A function post condition + Post, + + /// contract_assert + Assert +}; + +// std::contracts::assertion_kind +enum class StdContractAssertionKind { + Pre = 1, + + Post = 2, + + Assert = 3, +}; + +/// Contract evaluation mode. Determines whether to check contracts, and +// whether contract failures cause compile errors. +// +// These values match up with std::contracts::evaluation_semantic. However, only +// `enforce` and `observe` actually appear in STL enum. +enum class ContractEvaluationSemantic { + // Contracts are parsed, syntax checked and type checked, but never evaluated. + // FIXME(EricWF): This doesn't yet map to an actual enumerator in + // std::contracts::evaluation_semantic + Ignore = 0, + + // Contracts are run, failures are reported, and when a contract fails the + // program is terminated. The compiler can assume after contracts statements + // that the contracts hold. + Enforce = 1, + + // Contracts are run, and failures are reported, but contract failures do not + // logically stop execution of the program, nor can the compiler assume + // contracts are true for optimizing. + Observe = 2, + + // Contracts are run, failures cause an immediate trap + // FIXME(EricWF): This doesn't yet map to an actual enumerator in + // std::contracts::evaluation_semantic + QuickEnforce = 3, +}; + +/// The result of checking a contract. The second two values match +/// std::contracts::detection_mode. +enum class ContractViolationDetection { + NoViolation = 0, + PredicateFailed = 1, + ExceptionRaised = 2 +}; + +/// The code generation style for the contract. Inline +enum class ContractEmissionStyle { + /// Emit the contract at every call site, in the caller. + InCaller, + + /// Emit the contract with the function definition. + InDefinition, +}; + +/// Represents the set of contract groups that have been enabled or disabled +/// on the command line using '-fclang-contract-groups='. +/// +/// A contract group is a string consisting of identifiers join by '.'. For +/// example, "a.b.c" is a contract group with three subgroups. +/// +/// When determining if a particular contract check is enabled, we check if +/// the user has enabled/disabled the group/subgroup that the check belongs to, +/// in order of specificity. For example, passing +/// '-fclang-contract-groups=-std,+std.hardening,-std.hardening.foo' +/// will enable 'std.hardening.baz', but disable 'std.hardening.foo.bar' and +/// 'std.baz'. +/// +/// TODO(EricWF): Should we match in the same manner as clang-tidy checks? +/// Specifically, allow the use of '*' and drop all notion of groups? +class ContractOptions { +public: + ContractOptions() = default; + +public: + using DiagnoseGroupFunc = + std::function; + + static bool validateContractGroup(llvm::StringRef GroupAndValue, + const DiagnoseGroupFunc &Diagnoser); + + std::vector serializeContractGroupArgs() const; + + void parseContractGroups(const std::vector &Groups, + const DiagnoseGroupFunc &Diagnoser); + + void addUnparsedContractGroup(StringRef GroupAndValue, + const DiagnoseGroupFunc &Diagnoser); + + ContractEvaluationSemantic getSemanticForGroup(llvm::StringRef Group) const; + void setGroupSemantic(llvm::StringRef Group, + ContractEvaluationSemantic Semantic); + +public: + /// The default semantics for contracts. + ContractEvaluationSemantic DefaultSemantic = + ContractEvaluationSemantic::Enforce; + + /// The semantics for each contract group, if specified. + llvm::StringMap SemanticsByGroup; + + ContractEmissionStyle DefaultEmission = ContractEmissionStyle::InDefinition; +}; + +} // namespace clang + +#endif // LLVM_CLANG_BASIC_CONTRACT_OPTIONS_H diff --git a/clang/include/clang/Basic/DeclNodes.td b/clang/include/clang/Basic/DeclNodes.td index 48396e85c5ada..c4e9b9314f024 100644 --- a/clang/include/clang/Basic/DeclNodes.td +++ b/clang/include/clang/Basic/DeclNodes.td @@ -38,6 +38,7 @@ def Named : DeclNode; def UnresolvedUsingValue : DeclNode; def IndirectField : DeclNode; def Binding : DeclNode; + def ResultName : DeclNode; def OMPDeclareReduction : DeclNode, DeclContext; def OMPDeclareMapper : DeclNode, DeclContext; def MSGuid : DeclNode; @@ -110,3 +111,4 @@ def Empty : DeclNode; def RequiresExprBody : DeclNode, DeclContext; def LifetimeExtendedTemporary : DeclNode; def HLSLBuffer : DeclNode, DeclContext; +def ContractSpecifier : DeclNode; diff --git a/clang/include/clang/Basic/DiagnosticDriverKinds.td b/clang/include/clang/Basic/DiagnosticDriverKinds.td index 97573fcf20c1f..abdbac9fbd2dc 100644 --- a/clang/include/clang/Basic/DiagnosticDriverKinds.td +++ b/clang/include/clang/Basic/DiagnosticDriverKinds.td @@ -147,6 +147,9 @@ def warn_drv_unsupported_option_for_processor : Warning< def warn_drv_unsupported_openmp_library : Warning< "the library '%0=%1' is not supported, OpenMP will not be enabled">, InGroup; +def warn_openmp_experimental : Warning< + "OpenMP support in flang is still experimental">, + InGroup; def err_drv_invalid_thread_model_for_target : Error< "invalid thread model '%0' in '%1' for this target">; @@ -828,4 +831,15 @@ def err_drv_triple_version_invalid : Error< def warn_missing_include_dirs : Warning< "no such include directory: '%0'">, InGroup, DefaultIgnore; + + +def err_drv_contract_group_name_invalid : Error< + "contract group name %select{" + "\"%1\" cannot start with '%2'" + "|\"%1\" cannot contain '%2'" + "|\"%1\" cannot end with '%2'" + "|\"%1\" cannot contain empty subgroup \"%2\"" + "|cannot be empty" + "|\"%2\" is an invalid value" + "}0">; } diff --git a/clang/include/clang/Basic/DiagnosticFrontendKinds.td b/clang/include/clang/Basic/DiagnosticFrontendKinds.td index a6b17ccb6799d..1ed379c76c8ea 100644 --- a/clang/include/clang/Basic/DiagnosticFrontendKinds.td +++ b/clang/include/clang/Basic/DiagnosticFrontendKinds.td @@ -335,7 +335,7 @@ def warn_atomic_op_misaligned : Warning< def warn_atomic_op_oversized : Warning< "large atomic operation may incur " "significant performance penalty" - "; the access size (%0 bytes) exceeds the max lock-free size (%1 bytes)">, + "; the access size (%0 bytes) exceeds the max lock-free size (%1 bytes)">, InGroup; def warn_sync_op_misaligned : Warning< diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td index 7d81bdf827ea0..573939508be46 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -1125,6 +1125,11 @@ def ThreadSafety : DiagGroup<"thread-safety", def ThreadSafetyVerbose : DiagGroup<"thread-safety-verbose">; def ThreadSafetyBeta : DiagGroup<"thread-safety-beta">; +// Warnings and notes related to the function effects system which underlies +// the nonblocking and nonallocating attributes. +def FunctionEffects : DiagGroup<"function-effects">; +def PerfConstraintImpliesNoexcept : DiagGroup<"perf-constraint-implies-noexcept">; + // Uniqueness Analysis warnings def Consumed : DiagGroup<"consumed">; @@ -1133,7 +1138,7 @@ def Consumed : DiagGroup<"consumed">; // DefaultIgnore in addition to putting it here. def All : DiagGroup<"all", [Most, Parentheses, Switch, SwitchBool, MisleadingIndentation, PackedNonPod, - VLACxxExtension]>; + VLACxxExtension, PerfConstraintImpliesNoexcept]>; // Warnings that should be in clang-cl /w4. def : DiagGroup<"CL4", [All, Extra]>; @@ -1566,10 +1571,6 @@ def UnsafeBufferUsageInContainer : DiagGroup<"unsafe-buffer-usage-in-container"> def UnsafeBufferUsageInLibcCall : DiagGroup<"unsafe-buffer-usage-in-libc-call">; def UnsafeBufferUsage : DiagGroup<"unsafe-buffer-usage", [UnsafeBufferUsageInContainer, UnsafeBufferUsageInLibcCall]>; -// Warnings and notes related to the function effects system underlying -// the nonblocking and nonallocating attributes. -def FunctionEffects : DiagGroup<"function-effects">; - // Warnings and notes InstallAPI verification. def InstallAPIViolation : DiagGroup<"installapi-violation">; @@ -1582,3 +1583,14 @@ def ExtractAPIMisuse : DiagGroup<"extractapi-misuse">; // Warnings about using the non-standard extension having an explicit specialization // with a storage class specifier. def ExplicitSpecializationStorageClass : DiagGroup<"explicit-specialization-storage-class">; + +// A warning for options that enable a feature that is not yet complete +def ExperimentalOption : DiagGroup<"experimental-option">; + + +// Warnings produced by failing "observe" contract violations +def ContractViolation : DiagGroup<"contract-violation">; + +def ContractWarning : DiagGroup<"contracts", [ContractViolation]>; + +def EricWFFixme : DiagGroup<"ericwf-fixme">; \ No newline at end of file diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index dae357eb2d370..28b125550c428 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -439,7 +439,7 @@ def warn_reserved_extern_symbol: Warning< InGroup, DefaultIgnore; def warn_deprecated_literal_operator_id: Warning< "identifier %0 preceded by whitespace in a literal operator declaration " - "is deprecated">, InGroup, DefaultIgnore; + "is deprecated">, InGroup; def warn_reserved_module_name : Warning< "%0 is a reserved name for a module">, InGroup; def warn_import_implementation_partition_unit_in_interface_unit : Warning< @@ -3093,6 +3093,7 @@ def err_unsupported_placeholder_constraint : Error< "constrained placeholder types other than simple 'auto' on non-type template " "parameters not supported yet">; + def err_template_different_requires_clause : Error< "requires clause differs in template redeclaration">; def err_template_different_type_constraint : Error< @@ -5262,13 +5263,6 @@ def note_template_arg_refers_here_func : Note< def err_template_arg_template_params_mismatch : Error< "template template argument has different template parameters than its " "corresponding template template parameter">; -def note_template_arg_template_params_mismatch : Note< - "template template argument has different template parameters than its " - "corresponding template template parameter">; -def err_non_deduced_mismatch : Error< - "could not match %diff{$ against $|types}0,1">; -def err_inconsistent_deduction : Error< - "conflicting deduction %diff{$ against $|types}0,1 for parameter">; def err_template_arg_not_integral_or_enumeral : Error< "non-type template argument of type %0 must have an integral or enumeration" " type">; @@ -7521,6 +7515,9 @@ def note_typecheck_assign_const : Note< "member function %q1 is declared const here|" "%select{|nested }1data member %2 declared const here}0">; +def err_typecheck_assign_constified : Error< + "cannot assign to %select{variable %1|non-static data member}0 because it is considered 'const' inside of a contract">; + def warn_unsigned_always_true_comparison : Warning< "result of comparison of %select{%3|unsigned expression}0 %2 " "%select{unsigned expression|%3}0 is always %4">, @@ -9386,7 +9383,8 @@ let CategoryName = "Inline Assembly Issue" in { "global register variables on this target">; def err_asm_register_size_mismatch : Error<"size of register '%0' does not " "match variable size">; - def err_asm_bad_register_type : Error<"bad type for named register variable">; + def err_asm_unsupported_register_type : Error< + "unsupported type for named register variable">; def err_asm_invalid_input_size : Error< "invalid input size for constraint '%0'">; def err_asm_invalid_output_size : Error< @@ -10510,8 +10508,8 @@ def err_first_argument_to_cwsc_pdtor_call : Error< def err_second_argument_to_cwsc_not_pointer : Error< "second argument to __builtin_call_with_static_chain must be of pointer type">; -def err_vector_incorrect_num_initializers : Error< - "%select{too many|too few}0 elements in vector initialization (expected %1 elements, have %2)">; +def err_vector_incorrect_num_elements : Error< + "%select{too many|too few}0 elements in vector %select{initialization|operand}3 (expected %1 elements, have %2)">; def err_altivec_empty_initializer : Error<"expected initializer">; def err_invalid_neon_type_code : Error< @@ -10625,7 +10623,10 @@ def warn_duplicate_attribute : Warning< "attribute %0 is already applied with different arguments">, InGroup; def err_disallowed_duplicate_attribute : Error< - "attribute %0 cannot appear more than once on a declaration">; + "attribute %0 cannot appear more than once on a %select{declaration|contract specifier}1">; + +def err_disallow_incompatible_attribute : Error< + "attribute %1 cannot appear on a %select{declaration|contract specifier}0 with attribute %2">; def warn_sync_fetch_and_nand_semantics_change : Warning< "the semantics of this intrinsic changed with GCC " @@ -10972,19 +10973,68 @@ def warn_imp_cast_drops_unaligned : Warning< InGroup>; // Function effects +def warn_func_effect_violation : Warning< + "%select{function|constructor|destructor|lambda|block|member initializer of constructor}0 " + "with '%1' attribute " + "must not %select{allocate or deallocate memory|throw or catch exceptions|" + "have static local variables|use thread-local variables|access ObjC methods or properties}2">, + InGroup, DefaultIgnore; +def note_func_effect_violation : Note< + "%select{function|constructor|destructor|lambda|block|member initializer}0 " + "cannot be inferred '%1' because it " + "%select{allocates or deallocates memory|throws or catches exceptions|" + "has a static local variable|uses a thread-local variable|" + "accesses an ObjC method or property}2">; +def warn_func_effect_calls_func_without_effect : Warning< + "%select{function|constructor|destructor|lambda|block|member initializer of constructor}0 " + "with '%1' attribute " + "must not call non-'%1' " + "%select{function|constructor|destructor|lambda|block}2 " + "'%3'">, + InGroup, DefaultIgnore; +def note_func_effect_calls_func_without_effect : Note< + "%select{function|constructor|destructor|lambda|block|member initializer}0 " + "cannot be inferred '%1' because it calls non-'%1' " + "%select{function|constructor|destructor|lambda|block}2 " + "'%3'">; +def warn_func_effect_calls_expr_without_effect : Warning< + "%select{function|constructor|destructor|lambda|block|member initializer of constructor}0 " + "with '%1' attribute " + "must not call non-'%1' expression">, + InGroup, DefaultIgnore; +def note_func_effect_call_extern : Note< + "declaration cannot be inferred '%0' because it has no definition in this translation unit">; +def note_func_effect_call_disallows_inference : Note< + "%select{function|constructor|destructor|lambda|block}0 " + "does not permit inference of '%1' because it is declared '%2'">; +def note_func_effect_call_indirect : Note< + "%select{virtual method|function pointer}0 cannot be inferred '%1'">; +def warn_perf_constraint_implies_noexcept : Warning< + "%select{function|constructor|destructor|lambda|block}0 " + "with '%1' attribute should be declared noexcept">, + InGroup, DefaultIgnore; + +// FIXME: It would be nice if we could provide fuller template expansion notes. +def note_func_effect_from_template : Note< + "in template expansion here">; +def note_func_effect_in_constructor : Note< + "in%select{| implicit}0 constructor here">; +def note_in_evaluating_default_argument : Note< + "in evaluating default argument here">; + // spoofing nonblocking/nonallocating def warn_invalid_add_func_effects : Warning< "attribute '%0' should not be added via type conversion">, - InGroup; + InGroup, DefaultIgnore; def warn_mismatched_func_effect_override : Warning< "attribute '%0' on overriding function does not match base declaration">, - InGroup; + InGroup, DefaultIgnore; def warn_mismatched_func_effect_redeclaration : Warning< "attribute '%0' on function does not match previous declaration">, - InGroup; + InGroup, DefaultIgnore; def warn_conflicting_func_effects : Warning< "effects conflict when merging declarations; kept '%0', discarded '%1'">, - InGroup; + InGroup, DefaultIgnore; def err_func_with_effects_no_prototype : Error< "'%0' function must have a prototype">; @@ -11659,6 +11709,10 @@ def err_omp_dispatch_statement_call " to a target function or an assignment to one">; def err_omp_unroll_full_variable_trip_count : Error< "loop to be fully unrolled must have a constant trip count">; +def err_omp_interchange_permutation_value_range : Error< + "permutation index must be at least 1 and at most %0">; +def err_omp_interchange_permutation_value_repeated : Error< + "index %0 must appear exactly once in the permutation clause">; def note_omp_directive_here : Note<"'%0' directive found here">; def err_omp_instantiation_not_supported : Error<"instantiation of '%0' not supported yet">; @@ -11939,6 +11993,122 @@ def err_coroutine_return_type : Error< >; } // end of coroutines issue category +def select_contract_kind : TextSubstitution< + "%select{pre|post|contract_assert}0">; + +let CategoryName = "Contract Issue" in { +def err_ericwf_unimplemented : Error< + "contract feature %0 is not yet implemented (complain to eric@efcs.ca)" +>; +def warn_ericwf_unimplemented : Warning< + "contract feature %0 is not yet _correctly_ implemented (Danm it Eric!)" +>, InGroup; + +def err_ericwf_fixme : Error< + "woops... FIXME(EricWF): %0" +>; +def warn_ericwf_fixme : Warning< + "woops... FIXME(EricWF): %0" +>, InGroup; + +def err_result_name_shadows_param : Error< + "declaration of result name %0 shadows parameter">; +def err_void_result_name : Error< + "result name %0 cannot be used with a void return type">; +def err_deduced_auto_result_name_without_body : Error< + "result name on function with deduced return type requires a body">; +def err_result_name_not_allowed : Error< + "result name %0 not allowed outside of post condition specifier">; + +// Diagnose clang::contract_group attribute strings +def err_contract_group_attribute_invalid_group : Error< + "clang::contract_group group %select{" + "\"%1\" cannot start with '%2'" + "|\"%1\" cannot contain '%2'" + "|\"%1\" cannot end with '%2'" + "|\"%1\" cannot contain empty subgroup \"%2\"" + "|cannot be empty" + "|" + "}0">; + + + +def err_contract_violation : Error<"contract %select{precondition|postcondition|assertion}0 failed%select{: %2|}1">; +def warn_contract_violation : Warning<"contract %select{precondition|postcondition|assertion}0 failed%select{: %2|}1">, + InGroup; + +def err_contract_requirement_failed : Error< + "%select{precondition|postcondition|contract assertion}0 failed due to requirement '%1'%select{: %3|}2">; +def warn_contract_requirement_failed : Warning< + "%select{precondition|postcondition|contract assertion}0 failed due to requirement '%1'%select{: %3|}2">, + InGroup; + +def err_constexpr_contract_failure : Error< + "contract failed during execution of constexpr function">; +def warn_constexpr_contract_failure : Warning< + "contract failed during execution of constexpr function">, + InGroup; + +def err_initialization_of_constant_initialized_variable_failed : Error< + "initialization of constant-initialized variable failed">; +def note_initialization_changed_contract_semantic : Note< + "initializer is constant when evaluated with 'ignore' contract evaluation semantic">; + +// Redeclaration of contract attributes +def err_function_different_contract_seq : Error< + "%select{function|method}0 %select{redeclaration|out-of-line definition}0 differs in contract specifier sequence">; +def note_contract_spec_seq_arity_mismatch : Note< + "previously declared %select{with additional|with fewer|without}0 contracts here%select{ (%1 vs %2)| (%1 vs %2)|}0">; +def note_previous_contract_spec_seq : Note< + "previously declared %select{without|with %1}0 contracts here">; + +def note_mismatched_contract : Note< + "in contract specified here">; +def note_previous_contracts : Note< + "contract previously specified %select{" + "" + "|" + "|as '%sub{select_contract_kind}1'" + "|%select{without|with}2 result name" + "|with a non-equivalent condition}0">; + +def err_contract_attr_bool_arg_not_constant : Error< + "argument to %0 attribute must be a boolean constant expression">; + +def warn_contract_always_evaluates_to : Warning< + "contract condition always evaluates to %select{false|true}0">, + InGroup; + +def err_contract_postcondition_parameter_type_invalid : Error< + "parameter %0 referenced in contract postcondition %select{cannot have an array type|cannot have a function type|must be declared const}1">; +def note_parameter_with_name : Note< + "parameter %0 is declared here">; +def note_parameter_with_name_and_type : Note< + "parameter %0 declared with type %1 here">; + +def err_lambda_implicit_capture_in_contracts_only : Error< + "implicit capture of local entity %0 is not allowed when used exclusively in contract assertions">; + +def note_lambda_implicit_capture_in_contracts_only : Note< + "use explicit capture list to capture %0">; + +def err_auto_result_name_on_non_def_decl : Error< + "postcondition with result name %0 cannot appear on a function declaration with deduced return type unless it is a definition">; + // "result name '%0' cannot appear in postcondition for a non-template function declaration with a deduced return type">; +def note_function_return_type : Note< + "function return type declared here">; + +def note_contract_context : Note< + "within contract context introduced here">; +def note_cxx_this_const_in_contract_introduced_here : Note< + "'this' is 'const' within contract introduced here">; +def err_keyword_not_allowed_in_contract : Error< + "'%0' cannot be used inside a contract">; + + +def err_lambda_decl_ref_not_modifiable_lvalue_contract : Error< + "cannot assign to a variable captured by reference which was captured as const because it is inside a contract">; +} // end of contracts issues let CategoryName = "Documentation Issue" in { def warn_not_a_doxygen_trailing_member_comment : Warning< "not a Doxygen trailing comment">, InGroup, DefaultIgnore; @@ -12529,6 +12699,7 @@ def err_acc_duplicate_clause_disallowed : Error<"OpenACC '%1' clause cannot appear more than once on a '%0' " "directive">; def note_acc_previous_clause_here : Note<"previous clause is here">; +def note_acc_previous_expr_here : Note<"previous expression is here">; def err_acc_branch_in_out_compute_construct : Error<"invalid %select{branch|return|throw}0 %select{out of|into}1 " "OpenACC Compute Construct">; @@ -12617,17 +12788,42 @@ def err_acc_loop_spec_conflict def err_acc_collapse_loop_count : Error<"OpenACC 'collapse' clause loop count must be a %select{constant " "expression|positive integer value, evaluated to %1}0">; -def err_acc_invalid_in_collapse_loop - : Error<"%select{OpenACC '%1' construct|while loop|do loop}0 cannot appear " - "in intervening code of a 'loop' with a 'collapse' clause">; -def note_acc_collapse_clause_here - : Note<"active 'collapse' clause defined here">; -def err_acc_collapse_multiple_loops +def err_acc_size_expr_value + : Error< + "OpenACC 'tile' clause size expression must be %select{an asterisk " + "or a constant expression|positive integer value, evaluated to %1}0">; +def err_acc_invalid_in_loop + : Error<"%select{OpenACC '%2' construct|while loop|do loop}0 cannot appear " + "in intervening code of a 'loop' with a '%1' clause">; +def note_acc_active_clause_here + : Note<"active '%0' clause defined here">; +def err_acc_clause_multiple_loops : Error<"more than one for-loop in a loop associated with OpenACC 'loop' " - "construct with a 'collapse' clause">; -def err_acc_collapse_insufficient_loops - : Error<"'collapse' clause specifies a loop count greater than the number " + "construct with a '%select{collapse|tile}0' clause">; +def err_acc_insufficient_loops + : Error<"'%0' clause specifies a loop count greater than the number " "of available loops">; +def err_acc_intervening_code + : Error<"inner loops must be tightly nested inside a '%0' clause on " + "a 'loop' construct">; +def err_acc_gang_multiple_elt + : Error<"OpenACC 'gang' clause may have at most one %select{unnamed or " + "'num'|'dim'|'static'}0 argument">; +def err_acc_gang_arg_invalid + : Error<"'%0' argument on 'gang' clause is not permitted on a%select{n " + "orphaned|||}1 'loop' construct %select{|associated with a " + "'parallel' compute construct|associated with a 'kernels' compute " + "construct|associated with a 'serial' compute construct}1">; +def err_acc_gang_dim_value + : Error<"argument to 'gang' clause dimension must be %select{a constant " + "expression|1, 2, or 3: evaluated to %1}0">; +def err_acc_gang_num_gangs_conflict + : Error<"'num' argument to 'gang' clause not allowed on a 'loop' construct " + "associated with a 'kernels' construct that has a 'num_gangs' " + "clause">; +def err_acc_gang_inside_gang + : Error<"loop with a 'gang' clause may not exist in the region of a 'gang' " + "clause on a 'kernels' compute construct">; // AMDGCN builtins diagnostics def err_amdgcn_global_load_lds_size_invalid_value : Error<"invalid size value">; diff --git a/clang/include/clang/Basic/EricWFDebug.h b/clang/include/clang/Basic/EricWFDebug.h new file mode 100644 index 0000000000000..f4578aeb9fe3e --- /dev/null +++ b/clang/include/clang/Basic/EricWFDebug.h @@ -0,0 +1,72 @@ +//===- EricWFDebug.h - A File that needs to die -----------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +/// \file +/// Defines debugging macros that should only be present when EricWF is +/// debugging the code. It's a nice way to keep track of places EricWF has +/// modified and needs to remove. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_BASIC_ERICWFDEBUG_H +#define LLVM_CLANG_BASIC_ERICWFDEBUG_H + +#include "llvm/Support/Debug.h" +#include "llvm/Support/Signals.h" +#include "llvm/Support/raw_ostream.h" + +namespace clang { + +#ifdef ERICWF_DEBUG_ENABLED +constexpr bool EricWFDebugEnabled = true; +#else +constexpr bool EricWFDebugEnabled = false; +#endif + +inline void printSourceLocation(const char *file, const char *func, + unsigned line) { + if (not EricWFDebugEnabled) + return; + llvm::errs() << file << ":" << line; + if (func != nullptr) + llvm::errs() << ": in " << func; + llvm::errs() << "\n"; +} + +#define ERICWF_PRINT_SOURCE_LOC() \ + ::clang::printSourceLocation(__FILE__, __func__, __LINE__) + +#define ERICWF_STACK_TRACE(N) \ + do { \ + if (::clang::EricWFDebugEnabled) { \ + llvm::PrintStackTrace(llvm::errs(), N) \ + } \ + } while (0) + +} // namespace clang + +#define ERICWF_PRINT(x) \ + do { \ + ERICWF_PRINT_SOURCE_LOC(); \ + if constexpr (::clang::EricWFDebugEnabled) \ + llvm::errs() << #x << " = " << x; \ + } while (0) + +#define ERICWF_DEBUG_BLOCK \ + ERICWF_PRINT_SOURCE_LOC(); \ + if constexpr (::clang::EricWFDebugEnabled) + +#define ERICWF_DEBUG_BLOCK_QUIET if constexpr (::clang::EricWFDebugEnabled) + +#ifdef ERICWF_DEBUG_ENABLED +#define ERICWF_ASSERT(...) assert(__VA_ARGS__) +#else +#define ERICWF_ASSERT(...) ((void)0) +#endif + +#endif // LLVM_CLANG_BASIC_ERICWFDEBUG_H diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def index 68db400c22e6c..e67ed2612b0dd 100644 --- a/clang/include/clang/Basic/LangOptions.def +++ b/clang/include/clang/Basic/LangOptions.def @@ -158,6 +158,8 @@ LANGOPT(NoBuiltin , 1, 0, "disable builtin functions") LANGOPT(NoMathBuiltin , 1, 0, "disable math builtin functions") LANGOPT(GNUAsm , 1, 1, "GNU-style inline assembly") LANGOPT(Coroutines , 1, 0, "C++20 coroutines") +LANGOPT(Contracts , 1, 1, "C++2c contracts") +LANGOPT(ContractExceptions, 1, 1, "Contracts report a violation on exception") LANGOPT(CoroAlignedAllocation, 1, 0, "prefer Aligned Allocation according to P2014 Option 2") LANGOPT(DllExportInlines , 1, 1, "dllexported classes dllexport inline methods") LANGOPT(RelaxedTemplateTemplateArgs, 1, 1, "C++17 relaxed matching of template template arguments") diff --git a/clang/include/clang/Basic/LangOptions.h b/clang/include/clang/Basic/LangOptions.h index e2d4206c72cc9..7192fb36b5109 100644 --- a/clang/include/clang/Basic/LangOptions.h +++ b/clang/include/clang/Basic/LangOptions.h @@ -16,6 +16,7 @@ #include "clang/Basic/CFProtectionOptions.h" #include "clang/Basic/CommentOptions.h" +#include "clang/Basic/ContractOptions.h" #include "clang/Basic/LLVM.h" #include "clang/Basic/LangStandard.h" #include "clang/Basic/ObjCRuntime.h" @@ -469,6 +470,8 @@ class LangOptionsBase { CX_None }; + using ContractEvaluationSemantic = clang::ContractEvaluationSemantic; + /// Controls which variables have static destructors registered. enum class RegisterStaticDestructorsKind { /// Register static destructors for all variables. @@ -479,6 +482,7 @@ class LangOptionsBase { None, }; + // Define simple language options (with no accessors). #define LANGOPT(Name, Bits, Default, Description) unsigned Name : Bits; #define ENUM_LANGOPT(Name, Type, Bits, Default, Description) @@ -593,11 +597,19 @@ class LangOptions : public LangOptionsBase { /// The default stream kind used for HIP kernel launching. GPUDefaultStreamKind GPUDefaultStream; + /// C++ contracts evaluation mode + ContractEvaluationSemantic ContractEvalSemantic; + + /// A list of options pretaining to c++ contracts and clang attributes about + /// them. + ContractOptions ContractOpts; + /// Which overflow patterns should be excluded from sanitizer instrumentation unsigned OverflowPatternExclusionMask = 0; std::vector OverflowPatternExclusionValues; + /// The seed used by the randomize structure layout feature. std::string RandstructSeed; diff --git a/clang/include/clang/Basic/OpenACCClauses.def b/clang/include/clang/Basic/OpenACCClauses.def index 19cdfe7672133..2a098de31eb61 100644 --- a/clang/include/clang/Basic/OpenACCClauses.def +++ b/clang/include/clang/Basic/OpenACCClauses.def @@ -42,6 +42,7 @@ VISIT_CLAUSE(DevicePtr) VISIT_CLAUSE(DeviceType) CLAUSE_ALIAS(DType, DeviceType, false) VISIT_CLAUSE(FirstPrivate) +VISIT_CLAUSE(Gang) VISIT_CLAUSE(If) VISIT_CLAUSE(Independent) VISIT_CLAUSE(NoCreate) @@ -52,6 +53,7 @@ VISIT_CLAUSE(Private) VISIT_CLAUSE(Reduction) VISIT_CLAUSE(Self) VISIT_CLAUSE(Seq) +VISIT_CLAUSE(Tile) VISIT_CLAUSE(VectorLength) VISIT_CLAUSE(Wait) diff --git a/clang/include/clang/Basic/OpenACCKinds.h b/clang/include/clang/Basic/OpenACCKinds.h index 7b9d619a8aec6..3f48ebca708a4 100644 --- a/clang/include/clang/Basic/OpenACCKinds.h +++ b/clang/include/clang/Basic/OpenACCKinds.h @@ -22,7 +22,7 @@ namespace clang { // Represents the Construct/Directive kind of a pragma directive. Note the // OpenACC standard is inconsistent between calling these Construct vs // Directive, but we're calling it a Directive to be consistent with OpenMP. -enum class OpenACCDirectiveKind { +enum class OpenACCDirectiveKind : uint8_t { // Compute Constructs. Parallel, Serial, @@ -152,7 +152,7 @@ inline bool isOpenACCComputeDirectiveKind(OpenACCDirectiveKind K) { K == OpenACCDirectiveKind::Kernels; } -enum class OpenACCAtomicKind { +enum class OpenACCAtomicKind : uint8_t { Read, Write, Update, @@ -161,7 +161,7 @@ enum class OpenACCAtomicKind { }; /// Represents the kind of an OpenACC clause. -enum class OpenACCClauseKind { +enum class OpenACCClauseKind : uint8_t { /// 'finalize' clause, allowed on 'exit data' directive. Finalize, /// 'if_present' clause, allowed on 'host_data' and 'update' directives. @@ -459,7 +459,7 @@ inline llvm::raw_ostream &operator<<(llvm::raw_ostream &Out, return printOpenACCClauseKind(Out, K); } -enum class OpenACCDefaultClauseKind { +enum class OpenACCDefaultClauseKind : uint8_t { /// 'none' option. None, /// 'present' option. @@ -492,7 +492,7 @@ inline llvm::raw_ostream &operator<<(llvm::raw_ostream &Out, return printOpenACCDefaultClauseKind(Out, K); } -enum class OpenACCReductionOperator { +enum class OpenACCReductionOperator : uint8_t { /// '+'. Addition, /// '*'. @@ -550,6 +550,35 @@ inline llvm::raw_ostream &operator<<(llvm::raw_ostream &Out, OpenACCReductionOperator Op) { return printOpenACCReductionOperator(Out, Op); } + +enum class OpenACCGangKind : uint8_t { + /// num: + Num, + /// dim: + Dim, + /// static: + Static +}; + +template +inline StreamTy &printOpenACCGangKind(StreamTy &Out, OpenACCGangKind GK) { + switch (GK) { + case OpenACCGangKind::Num: + return Out << "num"; + case OpenACCGangKind::Dim: + return Out << "dim"; + case OpenACCGangKind::Static: + return Out << "static"; + } +} +inline const StreamingDiagnostic &operator<<(const StreamingDiagnostic &Out, + OpenACCGangKind Op) { + return printOpenACCGangKind(Out, Op); +} +inline llvm::raw_ostream &operator<<(llvm::raw_ostream &Out, + OpenACCGangKind Op) { + return printOpenACCGangKind(Out, Op); +} } // namespace clang #endif // LLVM_CLANG_BASIC_OPENACCKINDS_H diff --git a/clang/include/clang/Basic/PlistSupport.h b/clang/include/clang/Basic/PlistSupport.h index d52d196019cf8..1814130f1d2cb 100644 --- a/clang/include/clang/Basic/PlistSupport.h +++ b/clang/include/clang/Basic/PlistSupport.h @@ -26,13 +26,10 @@ using FIDMap = llvm::DenseMap; inline unsigned AddFID(FIDMap &FIDs, SmallVectorImpl &V, FileID FID) { - FIDMap::iterator I = FIDs.find(FID); - if (I != FIDs.end()) - return I->second; - unsigned NewValue = V.size(); - FIDs[FID] = NewValue; - V.push_back(FID); - return NewValue; + auto [I, Inserted] = FIDs.try_emplace(FID, V.size()); + if (Inserted) + V.push_back(FID); + return I->second; } inline unsigned AddFID(FIDMap &FIDs, SmallVectorImpl &V, diff --git a/clang/include/clang/Basic/StmtNodes.td b/clang/include/clang/Basic/StmtNodes.td index 30f2c8f1dbfde..dd4933dff04c9 100644 --- a/clang/include/clang/Basic/StmtNodes.td +++ b/clang/include/clang/Basic/StmtNodes.td @@ -54,6 +54,9 @@ def CXXForRangeStmt : StmtNode; def CoroutineBodyStmt : StmtNode; def CoreturnStmt : StmtNode; +// C++ contract statements +def ContractStmt : StmtNode; + // Expressions def Expr : StmtNode; def PredefinedExpr : StmtNode; @@ -308,5 +311,8 @@ def OpenACCAssociatedStmtConstruct def OpenACCComputeConstruct : StmtNode; def OpenACCLoopConstruct : StmtNode; +// OpenACC Additional Expressions. +def OpenACCAsteriskSizeExpr : StmtNode; + // HLSL Constructs. def HLSLOutArgExpr : StmtNode; diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def index c5c3838407cf4..6673f7358814c 100644 --- a/clang/include/clang/Basic/TokenKinds.def +++ b/clang/include/clang/Basic/TokenKinds.def @@ -40,6 +40,9 @@ #ifndef MODULES_KEYWORD #define MODULES_KEYWORD(X) KEYWORD(X,KEYMODULES) #endif +#ifndef CONTRACTS_KEYWORD +#define CONTRACTS_KEYWORD(X) KEYWORD(X,KEYCONTRACTS) +#endif #ifndef TYPE_TRAIT #define TYPE_TRAIT(N,I,K) KEYWORD(I,K) #endif @@ -64,6 +67,10 @@ #ifndef EXPRESSION_TRAIT #define EXPRESSION_TRAIT(I,E,K) KEYWORD(I,K) #endif +#ifndef TRANSFORM_TYPE_TRAIT_DEF +#define TRANSFORM_TYPE_TRAIT_DEF(K, Trait) KEYWORD(__##Trait, KEYCXX) +#endif + #ifndef ALIAS #define ALIAS(X,Y,Z) #endif @@ -283,6 +290,7 @@ PUNCTUATOR(greatergreatergreater, ">>>") // which are heavily based on AltiVec // KEYBORLAND - This is a keyword if Borland extensions are enabled // KEYCOROUTINES - This is a keyword if support for C++ coroutines is enabled +// KEYCONTRACTS - This is a keyword if support for C++ contracts is enabled // BOOLSUPPORT - This is a keyword if 'bool' is a built-in type // HALFSUPPORT - This is a keyword if 'half' is a built-in type // WCHARSUPPORT - This is a keyword if 'wchar_t' is a built-in type @@ -424,6 +432,10 @@ CXX20_KEYWORD(constinit , 0) CXX20_KEYWORD(concept , 0) CXX20_KEYWORD(requires , 0) +// C++ contracts keywords +CONTRACTS_KEYWORD(contract_assert) +ALIAS("__contract_assert" , contract_assert , KEYCXX11) + // Not a CXX20_KEYWORD because it is disabled by -fno-char8_t. KEYWORD(char8_t , CHAR8SUPPORT) @@ -458,6 +470,7 @@ KEYWORD(__builtin_LINE , KEYALL) KEYWORD(__builtin_COLUMN , KEYALL) KEYWORD(__builtin_source_location , KEYCXX) + // __builtin_types_compatible_p is a GNU C extension that we handle like a C++ // type trait. TYPE_TRAIT_2(__builtin_types_compatible_p, TypeCompatible, KEYNOCXX) @@ -534,7 +547,6 @@ TYPE_TRAIT_1(__has_unique_object_representations, TYPE_TRAIT_2(__is_layout_compatible, IsLayoutCompatible, KEYCXX) TYPE_TRAIT_2(__is_pointer_interconvertible_base_of, IsPointerInterconvertibleBaseOf, KEYCXX) -#define TRANSFORM_TYPE_TRAIT_DEF(_, Trait) KEYWORD(__##Trait, KEYCXX) #include "clang/Basic/TransformTypeTraits.def" // Clang-only C++ Type Traits diff --git a/clang/include/clang/Basic/arm_sme.td b/clang/include/clang/Basic/arm_sme.td index 45673cb915c5e..0f689e82bdb74 100644 --- a/clang/include/clang/Basic/arm_sme.td +++ b/clang/include/clang/Basic/arm_sme.td @@ -818,7 +818,7 @@ multiclass ZAReadzArray{ defm SVREADZ_VG2 : ZAReadzArray<"2">; defm SVREADZ_VG4 : ZAReadzArray<"4">; -let SMETargetGuard = "sme2,sme-lutv2" in { +let SMETargetGuard = "sme-lutv2" in { def SVWRITE_LANE_ZT : SInst<"svwrite_lane_zt[_{d}]", "vidi", "cUcsUsiUilUlfhdb", MergeNone, "aarch64_sme_write_lane_zt", [IsStreaming, IsInOutZT0], [ImmCheck<0, ImmCheck0_0>, ImmCheck<2, ImmCheck1_3>]>; def SVWRITE_ZT : SInst<"svwrite_zt[_{d}]", "vid", "cUcsUsiUilUlfhdb", MergeNone, "aarch64_sme_write_zt", [IsStreaming, IsOutZT0], [ImmCheck<0, ImmCheck0_0>]>; def SVLUTI4_ZT_X4 : SInst<"svluti4_zt_{d}_x4", "4i2.u", "cUc", MergeNone, "aarch64_sme_luti4_zt_x4", [IsStreaming, IsInZT0], [ImmCheck<0, ImmCheck0_0>]>; diff --git a/clang/include/clang/Basic/arm_sve.td b/clang/include/clang/Basic/arm_sve.td index da496e30fbb52..90b1ec242e6ba 100644 --- a/clang/include/clang/Basic/arm_sve.td +++ b/clang/include/clang/Basic/arm_sve.td @@ -943,11 +943,6 @@ defm SVFCVTZS_S64_F16 : SInstCvtMXZ<"svcvt_s64[_f16]", "ddPO", "dPO", "l", "aar defm SVFCVTZS_S32_F32 : SInstCvtMXZ<"svcvt_s32[_f32]", "ddPM", "dPM", "i", "aarch64_sve_fcvtzs", [IsOverloadCvt]>; defm SVFCVTZS_S64_F32 : SInstCvtMXZ<"svcvt_s64[_f32]", "ddPM", "dPM", "l", "aarch64_sve_fcvtzs_i64f32">; -let SVETargetGuard = "sve,bf16", SMETargetGuard = "sme,bf16" in { - defm SVCVT_BF16_F32 : SInstCvtMXZ<"svcvt_bf16[_f32]", "ddPM", "dPM", "b", "aarch64_sve_fcvt_bf16f32">; - def SVCVTNT_BF16_F32 : SInst<"svcvtnt_bf16[_f32]", "ddPM", "b", MergeOp1, "aarch64_sve_fcvtnt_bf16f32", [IsOverloadNone, VerifyRuntimeMode]>; -} - // svcvt_s##_f64 defm SVFCVTZS_S32_F64 : SInstCvtMXZ<"svcvt_s32[_f64]", "ttPd", "tPd", "d", "aarch64_sve_fcvtzs_i32f64">; defm SVFCVTZS_S64_F64 : SInstCvtMXZ<"svcvt_s64[_f64]", "ddPN", "dPN", "l", "aarch64_sve_fcvtzs", [IsOverloadCvt]>; @@ -1003,19 +998,26 @@ defm SVFCVT_F32_F64 : SInstCvtMXZ<"svcvt_f32[_f64]", "MMPd", "MPd", "d", "aarc defm SVFCVT_F64_F16 : SInstCvtMXZ<"svcvt_f64[_f16]", "ddPO", "dPO", "d", "aarch64_sve_fcvt_f64f16">; defm SVFCVT_F64_F32 : SInstCvtMXZ<"svcvt_f64[_f32]", "ddPM", "dPM", "d", "aarch64_sve_fcvt_f64f32">; -let SVETargetGuard = "sve2", SMETargetGuard = "sme" in { -defm SVCVTLT_F32 : SInstCvtMX<"svcvtlt_f32[_f16]", "ddPh", "dPh", "f", "aarch64_sve_fcvtlt_f32f16">; -defm SVCVTLT_F64 : SInstCvtMX<"svcvtlt_f64[_f32]", "ddPh", "dPh", "d", "aarch64_sve_fcvtlt_f64f32">; +let SVETargetGuard = "sve,bf16", SMETargetGuard = "sme,bf16" in { +defm SVCVT_BF16_F32 : SInstCvtMXZ<"svcvt_bf16[_f32]", "$$Pd", "$Pd", "f", "aarch64_sve_fcvt_bf16f32_v2">; + +def SVCVTNT_BF16_F32 : SInst<"svcvtnt_bf16[_f32]", "$$Pd", "f", MergeOp1, "aarch64_sve_fcvtnt_bf16f32_v2", [IsOverloadNone, VerifyRuntimeMode]>; +// SVCVTNT_X_BF16_F32 : Implemented as macro by SveEmitter.cpp +} -defm SVCVTX_F32 : SInstCvtMXZ<"svcvtx_f32[_f64]", "MMPd", "MPd", "d", "aarch64_sve_fcvtx_f32f64">; +let SVETargetGuard = "sve2", SMETargetGuard = "sme" in { +defm SVCVTLT_F32_F16 : SInstCvtMX<"svcvtlt_f32[_f16]", "ddPh", "dPh", "f", "aarch64_sve_fcvtlt_f32f16">; +defm SVCVTLT_F64_F32 : SInstCvtMX<"svcvtlt_f64[_f32]", "ddPh", "dPh", "d", "aarch64_sve_fcvtlt_f64f32">; -def SVCVTNT_F32 : SInst<"svcvtnt_f16[_f32]", "hhPd", "f", MergeOp1, "aarch64_sve_fcvtnt_f16f32", [IsOverloadNone, VerifyRuntimeMode]>; -def SVCVTNT_F64 : SInst<"svcvtnt_f32[_f64]", "hhPd", "d", MergeOp1, "aarch64_sve_fcvtnt_f32f64", [IsOverloadNone, VerifyRuntimeMode]>; -// SVCVTNT_X : Implemented as macro by SveEmitter.cpp +defm SVCVTX_F32_F64 : SInstCvtMXZ<"svcvtx_f32[_f64]", "MMPd", "MPd", "d", "aarch64_sve_fcvtx_f32f64">; -def SVCVTXNT_F32 : SInst<"svcvtxnt_f32[_f64]", "MMPd", "d", MergeOp1, "aarch64_sve_fcvtxnt_f32f64", [IsOverloadNone, VerifyRuntimeMode]>; -// SVCVTXNT_X_F32 : Implemented as macro by SveEmitter.cpp +def SVCVTNT_F16_F32 : SInst<"svcvtnt_f16[_f32]", "hhPd", "f", MergeOp1, "aarch64_sve_fcvtnt_f16f32", [IsOverloadNone, VerifyRuntimeMode]>; +def SVCVTNT_F32_F64 : SInst<"svcvtnt_f32[_f64]", "hhPd", "d", MergeOp1, "aarch64_sve_fcvtnt_f32f64", [IsOverloadNone, VerifyRuntimeMode]>; +// SVCVTNT_X_F16_F32 : Implemented as macro by SveEmitter.cpp +// SVCVTNT_X_F32_F64 : Implemented as macro by SveEmitter.cpp +def SVCVTXNT_F32_F64 : SInst<"svcvtxnt_f32[_f64]", "MMPd", "d", MergeOp1, "aarch64_sve_fcvtxnt_f32f64", [IsOverloadNone, VerifyRuntimeMode]>; +// SVCVTXNT_X_F32_F64 : Implemented as macro by SveEmitter.cpp } //////////////////////////////////////////////////////////////////////////////// diff --git a/clang/include/clang/Basic/riscv_vector.td b/clang/include/clang/Basic/riscv_vector.td index 6e57e51793a71..1387494c4aeb0 100644 --- a/clang/include/clang/Basic/riscv_vector.td +++ b/clang/include/clang/Basic/riscv_vector.td @@ -2564,7 +2564,7 @@ let HasMasked = false, HasVL = false, IRName = "" in { return Builder.CreateIntrinsic(Intrinsic::riscv_tuple_extract, {ResultType, Ops[0]->getType()}, - {Ops[0], Builder.CreateZExt(Ops[1], + {Ops[0], Builder.CreateTrunc(Ops[1], Builder.getInt32Ty())}); } }] in { @@ -2606,7 +2606,7 @@ let HasMasked = false, HasVL = false, IRName = "" in { return Builder.CreateIntrinsic(Intrinsic::riscv_tuple_insert, {ResultType, Ops[2]->getType()}, {Ops[0], Ops[2], - Builder.CreateZExt(Ops[1],Builder.getInt32Ty())}); + Builder.CreateTrunc(Ops[1],Builder.getInt32Ty())}); } }] in { foreach dst_lmul = ["(LFixedLog2LMUL:1)", "(LFixedLog2LMUL:2)", "(LFixedLog2LMUL:3)"] in { diff --git a/clang/include/clang/CIR/.clang-tidy b/clang/include/clang/CIR/.clang-tidy new file mode 100644 index 0000000000000..aaba4585494d6 --- /dev/null +++ b/clang/include/clang/CIR/.clang-tidy @@ -0,0 +1,62 @@ +InheritParentConfig: true +Checks: > + -misc-const-correctness, + -llvm-header-guard, + bugprone-argument-comment, + bugprone-assert-side-effect, + bugprone-branch-clone, + bugprone-copy-constructor-init, + bugprone-dangling-handle, + bugprone-dynamic-static-initializers, + bugprone-macro-parentheses, + bugprone-macro-repeated-side-effects, + bugprone-misplaced-widening-cast, + bugprone-move-forwarding-reference, + bugprone-multiple-statement-macro, + bugprone-suspicious-semicolon, + bugprone-swapped-arguments, + bugprone-terminating-continue, + bugprone-unused-raii, + bugprone-unused-return-value, + misc-redundant-expression, + misc-static-assert, + misc-unused-using-decls, + modernize-use-bool-literals, + modernize-loop-convert, + modernize-make-unique, + modernize-raw-string-literal, + modernize-use-equals-default, + modernize-use-default-member-init, + modernize-use-emplace, + modernize-use-nullptr, + modernize-use-override, + modernize-use-using, + performance-for-range-copy, + performance-implicit-conversion-in-loop, + performance-inefficient-algorithm, + performance-inefficient-vector-operation, + performance-move-const-arg, + performance-no-automatic-move, + performance-trivially-destructible, + performance-unnecessary-copy-initialization, + performance-unnecessary-value-param, + readability-avoid-const-params-in-decls, + readability-const-return-type, + readability-container-size-empty, + readability-identifier-naming, + readability-inconsistent-declaration-parameter-name, + readability-misleading-indentation, + readability-redundant-control-flow, + readability-redundant-smartptr-get, + readability-simplify-boolean-expr, + readability-simplify-subscript-expr, + readability-use-anyofallof + + +CheckOptions: + - key: readability-identifier-naming.MemberCase + value: camelBack + - key: readability-identifier-naming.ParameterCase + value: camelBack + - key: readability-identifier-naming.VariableCase + value: camelBack diff --git a/clang/include/clang/CIR/CIRGenerator.h b/clang/include/clang/CIR/CIRGenerator.h new file mode 100644 index 0000000000000..9a8930ac46ea9 --- /dev/null +++ b/clang/include/clang/CIR/CIRGenerator.h @@ -0,0 +1,60 @@ +//===- CIRGenerator.h - CIR Generation from Clang AST ---------------------===// +// +// 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 declares a simple interface to perform CIR generation from Clang +// AST +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_CIR_CIRGENERATOR_H +#define LLVM_CLANG_CIR_CIRGENERATOR_H + +#include "clang/AST/ASTConsumer.h" +#include "clang/Basic/CodeGenOptions.h" + +#include "llvm/ADT/IntrusiveRefCntPtr.h" +#include "llvm/Support/VirtualFileSystem.h" + +#include + +namespace clang { +class DeclGroupRef; +class DiagnosticsEngine; +} // namespace clang + +namespace mlir { +class MLIRContext; +} // namespace mlir +namespace cir { +class CIRGenModule; + +class CIRGenerator : public clang::ASTConsumer { + virtual void anchor(); + clang::DiagnosticsEngine &diags; + clang::ASTContext *astCtx; + // Only used for debug info. + llvm::IntrusiveRefCntPtr fs; + + const clang::CodeGenOptions &codeGenOpts; + +protected: + std::unique_ptr mlirCtx; + std::unique_ptr cgm; + +public: + CIRGenerator(clang::DiagnosticsEngine &diags, + llvm::IntrusiveRefCntPtr fs, + const clang::CodeGenOptions &cgo); + ~CIRGenerator() override; + void Initialize(clang::ASTContext &astCtx) override; + bool HandleTopLevelDecl(clang::DeclGroupRef group) override; +}; + +} // namespace cir + +#endif // LLVM_CLANG_CIR_CIRGENERATOR_H diff --git a/clang/include/clang/CIR/FrontendAction/CIRGenAction.h b/clang/include/clang/CIR/FrontendAction/CIRGenAction.h new file mode 100644 index 0000000000000..2ab612613b73d --- /dev/null +++ b/clang/include/clang/CIR/FrontendAction/CIRGenAction.h @@ -0,0 +1,60 @@ +//===---- CIRGenAction.h - CIR Code Generation Frontend Action -*- 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_CIR_CIRGENACTION_H +#define LLVM_CLANG_CIR_CIRGENACTION_H + +#include "clang/Frontend/FrontendAction.h" + +#include "mlir/IR/BuiltinOps.h" +#include "mlir/IR/OwningOpRef.h" + +namespace mlir { +class MLIRContext; +class ModuleOp; +} // namespace mlir + +namespace cir { +class CIRGenConsumer; + +class CIRGenAction : public clang::ASTFrontendAction { +public: + enum class OutputType { + EmitCIR, + }; + +private: + friend class CIRGenConsumer; + + mlir::OwningOpRef MLIRMod; + + mlir::MLIRContext *MLIRCtx; + +protected: + CIRGenAction(OutputType Action, mlir::MLIRContext *MLIRCtx = nullptr); + + std::unique_ptr + CreateASTConsumer(clang::CompilerInstance &CI, + llvm::StringRef InFile) override; + +public: + ~CIRGenAction() override; + + OutputType Action; +}; + +class EmitCIRAction : public CIRGenAction { + virtual void anchor(); + +public: + EmitCIRAction(mlir::MLIRContext *MLIRCtx = nullptr); +}; + +} // namespace cir + +#endif diff --git a/clang/include/clang/CIRFrontendAction/.clang-tidy b/clang/include/clang/CIRFrontendAction/.clang-tidy new file mode 100644 index 0000000000000..ef88dbcec488c --- /dev/null +++ b/clang/include/clang/CIRFrontendAction/.clang-tidy @@ -0,0 +1,53 @@ +InheritParentConfig: true +Checks: > + -misc-const-correctness, + -llvm-header-guard, + bugprone-argument-comment, + bugprone-assert-side-effect, + bugprone-branch-clone, + bugprone-copy-constructor-init, + bugprone-dangling-handle, + bugprone-dynamic-static-initializers, + bugprone-macro-parentheses, + bugprone-macro-repeated-side-effects, + bugprone-misplaced-widening-cast, + bugprone-move-forwarding-reference, + bugprone-multiple-statement-macro, + bugprone-suspicious-semicolon, + bugprone-swapped-arguments, + bugprone-terminating-continue, + bugprone-unused-raii, + bugprone-unused-return-value, + misc-redundant-expression, + misc-static-assert, + misc-unused-using-decls, + modernize-use-bool-literals, + modernize-loop-convert, + modernize-make-unique, + modernize-raw-string-literal, + modernize-use-equals-default, + modernize-use-default-member-init, + modernize-use-emplace, + modernize-use-nullptr, + modernize-use-override, + modernize-use-using, + performance-for-range-copy, + performance-implicit-conversion-in-loop, + performance-inefficient-algorithm, + performance-inefficient-vector-operation, + performance-move-const-arg, + performance-no-automatic-move, + performance-trivially-destructible, + performance-unnecessary-copy-initialization, + performance-unnecessary-value-param, + readability-avoid-const-params-in-decls, + readability-const-return-type, + readability-container-size-empty, + readability-identifier-naming, + readability-inconsistent-declaration-parameter-name, + readability-misleading-indentation, + readability-redundant-control-flow, + readability-redundant-smartptr-get, + readability-simplify-boolean-expr, + readability-simplify-subscript-expr, + readability-use-anyofallof diff --git a/clang/include/clang/Driver/Multilib.h b/clang/include/clang/Driver/Multilib.h index 1a79417111eec..dbed70f4f9008 100644 --- a/clang/include/clang/Driver/Multilib.h +++ b/clang/include/clang/Driver/Multilib.h @@ -54,7 +54,7 @@ class Multilib { // Some Multilib objects don't actually represent library directories you can // select. Instead, they represent failures of multilib selection, of the // form 'Sorry, we don't have any library compatible with these constraints'. - std::optional FatalError; + std::optional Error; public: /// GCCSuffix, OSSuffix & IncludeSuffix will be appended directly to the @@ -63,7 +63,7 @@ class Multilib { Multilib(StringRef GCCSuffix = {}, StringRef OSSuffix = {}, StringRef IncludeSuffix = {}, const flags_list &Flags = flags_list(), StringRef ExclusiveGroup = {}, - std::optional FatalError = std::nullopt); + std::optional Error = std::nullopt); /// Get the detected GCC installation path suffix for the multi-arch /// target variant. Always starts with a '/', unless empty @@ -94,9 +94,9 @@ class Multilib { bool operator==(const Multilib &Other) const; - bool isFatalError() const { return FatalError.has_value(); } + bool isError() const { return Error.has_value(); } - const std::string &getFatalError() const { return FatalError.value(); } + const std::string &getErrorMessage() const { return Error.value(); } }; raw_ostream &operator<<(raw_ostream &OS, const Multilib &M); diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index 9d183ff2d69b3..83722633956c1 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -1195,10 +1195,11 @@ def cxx_isystem : JoinedOrSeparate<["-"], "cxx-isystem">, Group, def c : Flag<["-"], "c">, Flags<[NoXarchOption]>, Visibility<[ClangOption, FlangOption]>, Group, HelpText<"Only run preprocess, compile, and assemble steps">; -defm convergent_functions : BoolFOption<"convergent-functions", - LangOpts<"ConvergentFunctions">, DefaultFalse, - NegFlag, - PosFlag>; +def fconvergent_functions : Flag<["-"], "fconvergent-functions">, + Visibility<[ClangOption, CC1Option]>, + HelpText< "Assume all functions may be convergent.">; +def fno_convergent_functions : Flag<["-"], "fno-convergent-functions">, + Visibility<[ClangOption, CC1Option]>; // Common offloading options let Group = offload_Group in { @@ -1681,6 +1682,33 @@ defm auto_import : BoolFOption<"auto-import", def offload_EQ : CommaJoined<["--"], "offload=">, Flags<[NoXarchOption]>, HelpText<"Specify comma-separated list of offloading target triples (CUDA and HIP only)">; +// C++ Contracts +defm contracts : BoolFOption<"contracts", + LangOpts<"Contracts">, DefaultFalse, + PosFlag, + NegFlag>; + + +def fcontract_evaluation_semantic_EQ : Joined<["-"], "fcontract-evaluation-semantic=">, + HelpText<"Specify contract evaluation semantic. The default value is 'enforce'.">, + Values<"ignore,enforce,observe,quick_enforce">, + Visibility<[ClangOption, CC1Option]>, + NormalizedValuesScope<"LangOptions::ContractEvaluationSemantic">, + NormalizedValues<["Ignore", "Enforce", "Observe", "QuickEnforce"]>, + MarshallingInfoEnum, "Enforce">; + +def fcontract_group_evaluation_semantic_EQ : CommaJoined<["-"], "fcontract-group-evaluation-semantic=">, + Visibility<[ClangOption, CC1Option]>, + HelpText<"Clang extension. Enable or disable contracts by group. The argument is a comma-separated " + "sequence of one or more group names, each prefixed by '+' or '-'.">; + +defm contract_exceptions : BoolFOption<"contract-exceptions", + LangOpts<"ContractExceptions">, DefaultTrue, + PosFlag, + NegFlag>; // C++ Coroutines defm coroutines : BoolFOption<"coroutines", LangOpts<"Coroutines">, Default, @@ -2995,7 +3023,7 @@ defm clangir : BoolFOption<"clangir", PosFlag, NegFlag LLVM pipeline to compile">, BothFlags<[], [ClangOption, CC1Option], "">>; -def emit_cir : Flag<["-"], "emit-cir">, Visibility<[CC1Option]>, +def emit_cir : Flag<["-"], "emit-cir">, Visibility<[ClangOption, CC1Option]>, Group, HelpText<"Build ASTs and then lower to ClangIR">; /// ClangIR-specific options - END @@ -5557,6 +5585,9 @@ def mthumb : Flag<["-"], "mthumb">, Group; def mtune_EQ : Joined<["-"], "mtune=">, Group, Visibility<[ClangOption, FlangOption]>, HelpText<"Only supported on AArch64, PowerPC, RISC-V, SPARC, SystemZ, and X86">; +def multi_lib_config : Joined<["-", "--"], "multi-lib-config=">, + HelpText<"Path to the YAML configuration file to be used for multilib selection">, + MetaVarName<"">; def multi__module : Flag<["-"], "multi_module">; def multiply__defined__unused : Separate<["-"], "multiply_defined_unused">; def multiply__defined : Separate<["-"], "multiply_defined">; @@ -6073,7 +6104,7 @@ def _sysroot_EQ : Joined<["--"], "sysroot=">, Visibility<[ClangOption, FlangOpti def _sysroot : Separate<["--"], "sysroot">, Alias<_sysroot_EQ>; //===----------------------------------------------------------------------===// -// pie/pic options (clang + flang-new) +// pie/pic options (clang + flang) //===----------------------------------------------------------------------===// let Visibility = [ClangOption, FlangOption] in { @@ -6089,7 +6120,7 @@ def fno_pie : Flag<["-"], "fno-pie">, Group; } // let Vis = [Default, FlangOption] //===----------------------------------------------------------------------===// -// Target Options (clang + flang-new) +// Target Options (clang + flang) //===----------------------------------------------------------------------===// let Flags = [TargetSpecific] in { let Visibility = [ClangOption, FlangOption] in { diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h index d8b62c7652a0f..3d3e4330902a3 100644 --- a/clang/include/clang/Format/Format.h +++ b/clang/include/clang/Format/Format.h @@ -131,7 +131,7 @@ struct FormatStyle { /// Don't align array initializer columns. AIAS_None }; - /// if not ``None``, when using initialization for an array of structs + /// If not ``None``, when using initialization for an array of structs /// aligns the fields into columns. /// /// \note @@ -145,11 +145,12 @@ struct FormatStyle { /// Alignment options. /// /// They can also be read as a whole for compatibility. The choices are: - /// - None - /// - Consecutive - /// - AcrossEmptyLines - /// - AcrossComments - /// - AcrossEmptyLinesAndComments + /// + /// * ``None`` + /// * ``Consecutive`` + /// * ``AcrossEmptyLines`` + /// * ``AcrossComments`` + /// * ``AcrossEmptyLinesAndComments`` /// /// For example, to align across empty lines and not across comments, either /// of these work. @@ -225,6 +226,20 @@ struct FormatStyle { /// bbb = 2; /// \endcode bool AlignCompound; + /// Only for ``AlignConsecutiveDeclarations``. Whether function declarations + /// are aligned. + /// \code + /// true: + /// unsigned int f1(void); + /// void f2(void); + /// size_t f3(void); + /// + /// false: + /// unsigned int f1(void); + /// void f2(void); + /// size_t f3(void); + /// \endcode + bool AlignFunctionDeclarations; /// Only for ``AlignConsecutiveDeclarations``. Whether function pointers are /// aligned. /// \code @@ -264,6 +279,7 @@ struct FormatStyle { return Enabled == R.Enabled && AcrossEmptyLines == R.AcrossEmptyLines && AcrossComments == R.AcrossComments && AlignCompound == R.AlignCompound && + AlignFunctionDeclarations == R.AlignFunctionDeclarations && AlignFunctionPointers == R.AlignFunctionPointers && PadOperators == R.PadOperators; } @@ -4974,6 +4990,15 @@ struct FormatStyle { /// \version 3.7 unsigned TabWidth; + /// A vector of non-keyword identifiers that should be interpreted as + /// template names. + /// + /// A ``<`` after a template name is annotated as a template opener instead of + /// a binary operator. + /// + /// \version 20 + std::vector TemplateNames; + /// A vector of non-keyword identifiers that should be interpreted as type /// names. /// @@ -5230,6 +5255,7 @@ struct FormatStyle { TableGenBreakingDAGArgOperators == R.TableGenBreakingDAGArgOperators && TableGenBreakInsideDAGArg == R.TableGenBreakInsideDAGArg && + TabWidth == R.TabWidth && TemplateNames == R.TemplateNames && TabWidth == R.TabWidth && TypeNames == R.TypeNames && TypenameMacros == R.TypenameMacros && UseTab == R.UseTab && VerilogBreakBetweenInstancePorts == diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index eb8a851da7e04..04907792e67c8 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -169,6 +169,12 @@ class Parser : public CodeCompletionHandler { mutable IdentifierInfo *Ident_import; mutable IdentifierInfo *Ident_module; + // C++2c(?) contextual keywords. + mutable IdentifierInfo *Ident_pre; + mutable IdentifierInfo *Ident___pre; + mutable IdentifierInfo *Ident_post; + mutable IdentifierInfo *Ident___post; + // C++ type trait keywords that can be reverted to identifiers and still be // used as type traits. llvm::SmallDenseMap RevertibleTypeTraits; @@ -1484,6 +1490,9 @@ class Parser : public CodeCompletionHandler { /// The set of tokens that make up an exception-specification that /// has not yet been parsed. CachedTokens *ExceptionSpecTokens; + + // The set of tokens that make up the contracts on the method. + CachedTokens ContractTokens; }; /// LateParsedMemberInitializer - An initializer for a non-static class data @@ -2112,6 +2121,50 @@ class Parser : public CodeCompletionHandler { ExprResult ParseRequiresExpression(); void ParseTrailingRequiresClause(Declarator &D); + //===--------------------------------------------------------------------===// + // C++ Contracts +public: + enum ContractEnterScopeKind { + CES_None = 0, + CES_Prototype = 0x1, + CES_Parameters = 0x2, + CES_CXXThis = 0x4, + CES_Function = 0x8, + CES_AllScopes = CES_Prototype | CES_Parameters | CES_CXXThis | CES_Function, + LLVM_MARK_AS_BITMASK_ENUM(CES_AllScopes) + }; + std::optional getContractKeyword(const Token &Token) const; + std::optional getContractKeyword() const { + return getContractKeyword(Tok); + } + bool isFunctionContractKeyword() const { + return isFunctionContractKeyword(Tok); + } + bool isFunctionContractKeyword(const Token &Token) const { + return getContractKeyword(Token).value_or(ContractKind::Assert) != + ContractKind::Assert; + } + + bool isAnyContractKeyword(const Token &Token) const { + return getContractKeyword(Token).has_value(); + } + +private: + StmtResult ParseContractAssertStatement(); + + void ParseContractSpecifierSequence(Declarator &DeclarationInfo, + bool EnterScope, + QualType TrailingReturnType = QualType()); + + StmtResult ParseFunctionContractSpecifierImpl( + llvm::function_ref ReturnTypeResolver, bool &IsInvalid); + + void LateParseFunctionContractSpecifierSeq(CachedTokens &ContractToks); + bool LateParseFunctionContractSpecifier(CachedTokens &ContractToks); + + bool ParseLexedFunctionContracts(CachedTokens &Toks, Decl *FD, + ContractEnterScopeKind EnterScopeKinds); + //===--------------------------------------------------------------------===// // C99 6.7.8: Initialization. @@ -3234,9 +3287,12 @@ class Parser : public CodeCompletionHandler { void ParseFunctionDeclarator(Declarator &D, ParsedAttributes &FirstArgAttrs, BalancedDelimiterTracker &Tracker, bool IsAmbiguous, bool RequiresArg = false); +public: void InitCXXThisScopeForDeclaratorIfRelevant( const Declarator &D, const DeclSpec &DS, - std::optional &ThisScope); + std::optional &ThisScope, bool AddConst = false); + +private: bool ParseRefQualifier(bool &RefQualifierIsLValueRef, SourceLocation &RefQualifierLoc); bool isFunctionDeclaratorIdentifierList(); @@ -3595,6 +3651,9 @@ class Parser : public CodeCompletionHandler { /// Parses the 'sizes' clause of a '#pragma omp tile' directive. OMPClause *ParseOpenMPSizesClause(); + /// Parses the 'permutation' clause of a '#pragma omp interchange' directive. + OMPClause *ParseOpenMPPermutationClause(); + /// Parses clause without any additional arguments. /// /// \param Kind Kind of current clause. @@ -3786,14 +3845,23 @@ class Parser : public CodeCompletionHandler { OpenACCIntExprParseResult ParseOpenACCAsyncArgument(OpenACCDirectiveKind DK, OpenACCClauseKind CK, SourceLocation Loc); + /// Parses the 'size-expr', which is an integral value, or an asterisk. - bool ParseOpenACCSizeExpr(); + /// Asterisk is represented by a OpenACCAsteriskSizeExpr + ExprResult ParseOpenACCSizeExpr(OpenACCClauseKind CK); /// Parses a comma delimited list of 'size-expr's. - bool ParseOpenACCSizeExprList(); + bool ParseOpenACCSizeExprList(OpenACCClauseKind CK, + llvm::SmallVectorImpl &SizeExprs); /// Parses a 'gang-arg-list', used for the 'gang' clause. - bool ParseOpenACCGangArgList(SourceLocation GangLoc); - /// Parses a 'gang-arg', used for the 'gang' clause. - bool ParseOpenACCGangArg(SourceLocation GangLoc); + bool ParseOpenACCGangArgList(SourceLocation GangLoc, + llvm::SmallVectorImpl &GKs, + llvm::SmallVectorImpl &IntExprs); + + using OpenACCGangArgRes = std::pair; + /// Parses a 'gang-arg', used for the 'gang' clause. Returns a pair of the + /// ExprResult (which contains the validity of the expression), plus the gang + /// kind for the current argument. + OpenACCGangArgRes ParseOpenACCGangArg(SourceLocation GangLoc); /// Parses a 'condition' expr, ensuring it results in a ExprResult ParseOpenACCConditionExpr(); @@ -3943,6 +4011,8 @@ class Parser : public CodeCompletionHandler { bool isGNUAsmQualifier(const Token &TokAfterAsm) const; GNUAsmQualifiers::AQ getGNUAsmQualifier(const Token &Tok) const; bool parseGNUAsmQualifierListOpt(GNUAsmQualifiers &AQ); + ContractSpecifierDecl *ParseLexedFunctionContractsInScope(CachedTokens &Toks, + QualType RetType); }; } // end namespace clang diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h index 06243f2624876..c16b26463f2ca 100644 --- a/clang/include/clang/Sema/DeclSpec.h +++ b/clang/include/clang/Sema/DeclSpec.h @@ -49,6 +49,7 @@ namespace clang { class ObjCDeclSpec; class Sema; class Declarator; + class ContractStmt; struct TemplateIdAnnotation; /// Represents a C++ nested-name-specifier or a global scope specifier. @@ -1851,33 +1852,34 @@ enum class FunctionDefinitionKind { }; enum class DeclaratorContext { - File, // File scope declaration. - Prototype, // Within a function prototype. - ObjCResult, // An ObjC method result type. - ObjCParameter, // An ObjC method parameter type. - KNRTypeList, // K&R type definition list for formals. - TypeName, // Abstract declarator for types. - FunctionalCast, // Type in a C++ functional cast expression. - Member, // Struct/Union field. - Block, // Declaration within a block in a function. - ForInit, // Declaration within first part of a for loop. - SelectionInit, // Declaration within optional init stmt of if/switch. - Condition, // Condition declaration in a C++ if/switch/while/for. - TemplateParam, // Within a template parameter list. - CXXNew, // C++ new-expression. - CXXCatch, // C++ catch exception-declaration - ObjCCatch, // Objective-C catch exception-declaration - BlockLiteral, // Block literal declarator. - LambdaExpr, // Lambda-expression declarator. - LambdaExprParameter, // Lambda-expression parameter declarator. - ConversionId, // C++ conversion-type-id. - TrailingReturn, // C++11 trailing-type-specifier. - TrailingReturnVar, // C++11 trailing-type-specifier for variable. - TemplateArg, // Any template argument (in template argument list). - TemplateTypeArg, // Template type argument (in default argument). - AliasDecl, // C++11 alias-declaration. - AliasTemplate, // C++11 alias-declaration template. - RequiresExpr, // C++2a requires-expression. + File, // File scope declaration. + Prototype, // Within a function prototype. + ObjCResult, // An ObjC method result type. + ObjCParameter, // An ObjC method parameter type. + KNRTypeList, // K&R type definition list for formals. + TypeName, // Abstract declarator for types. + FunctionalCast, // Type in a C++ functional cast expression. + Member, // Struct/Union field. + Block, // Declaration within a block in a function. + ForInit, // Declaration within first part of a for loop. + SelectionInit, // Declaration within optional init stmt of if/switch. + Condition, // Condition declaration in a C++ if/switch/while/for. + TemplateParam, // Within a template parameter list. + CXXNew, // C++ new-expression. + CXXCatch, // C++ catch exception-declaration + ObjCCatch, // Objective-C catch exception-declaration + BlockLiteral, // Block literal declarator. + LambdaExpr, // Lambda-expression declarator. + LambdaExprParameter, // Lambda-expression parameter declarator. + ConversionId, // C++ conversion-type-id. + TrailingReturn, // C++11 trailing-type-specifier. + TrailingReturnVar, // C++11 trailing-type-specifier for variable. + TemplateArg, // Any template argument (in template argument list). + TemplateTypeArg, // Template type argument (in default argument). + AliasDecl, // C++11 alias-declaration. + AliasTemplate, // C++11 alias-declaration template. + RequiresExpr, // C++2a requires-expression. + //ContractPostcondition, // C++2a requires-type. FIXME(EricWF) Association // C11 _Generic selection expression association. }; @@ -1973,6 +1975,13 @@ class Declarator { /// requires-clause, or null if no such clause was specified. Expr *TrailingRequiresClause; +public: + /// \brief All pre and post contracts specified by the function declaration + ContractSpecifierDecl *Contracts = nullptr; + + CachedTokens LateParsedContracts; + +private: /// If this declarator declares a template, its template parameter lists. ArrayRef TemplateParameterLists; @@ -2128,6 +2137,9 @@ class Declarator { CommaLoc = SourceLocation(); EllipsisLoc = SourceLocation(); PackIndexingExpr = nullptr; + Contracts = nullptr; + assert(LateParsedContracts.empty() && "Late-parsed contracts unhandled"); + LateParsedContracts.clear(); } /// mayOmitIdentifier - Return true if the identifier is either optional or @@ -2631,7 +2643,7 @@ class Declarator { SetRangeEnd(TRC->getEndLoc()); } - + /// \brief Sets a trailing requires clause for this declarator. Expr *getTrailingRequiresClause() { return TrailingRequiresClause; @@ -2643,6 +2655,18 @@ class Declarator { return TrailingRequiresClause != nullptr; } + /// \brief Add a pre contract for this declarator + /// \brief Get all pre contracts for this declarator + ContractSpecifierDecl *getContracts() const { return Contracts; } + + void addLateParsedContract(CachedTokens &Toks) { + LateParsedContracts.append(Toks); + } + + const CachedTokens &getLateParsedContracts() const { + return LateParsedContracts; + } + /// Sets the template parameter lists that preceded the declarator. void setTemplateParameterLists(ArrayRef TPLs) { TemplateParameterLists = TPLs; diff --git a/clang/include/clang/Sema/Scope.h b/clang/include/clang/Sema/Scope.h index 084db73034219..16a4b51014339 100644 --- a/clang/include/clang/Sema/Scope.h +++ b/clang/include/clang/Sema/Scope.h @@ -14,6 +14,7 @@ #define LLVM_CLANG_SEMA_SCOPE_H #include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" #include "clang/Basic/Diagnostic.h" #include "llvm/ADT/PointerIntPair.h" #include "llvm/ADT/SmallPtrSet.h" @@ -162,7 +163,17 @@ class Scope { /// This is a scope of friend declaration. FriendScope = 0x40000000, + + /// The scope introduced by a pre, post, or contract_assert. + // + // FIXME(Ericwf): This scope in unlike others, where it doesn't applied + // to the entire scope, but only to the statement that introduced it. + // This is a bit of a hack, but it's the simplest way to get the + // functionality we need. + ContractAssertScope = 0x80000000, }; + using UT = std::underlying_type_t; + static_assert(std::is_unsigned_v, "ScopeFlags must be an unsigned type"); private: /// The parent scope for this scope. This is null for the translation-unit @@ -293,13 +304,20 @@ class Scope { // is disallowed despite being a continue scope. void setIsConditionVarScope(bool InConditionVarScope) { Flags = (Flags & ~ConditionVarScope) | - (InConditionVarScope ? ConditionVarScope : 0); + (InConditionVarScope ? ConditionVarScope : ScopeFlags(0)); } bool isConditionVarScope() const { return Flags & ConditionVarScope; } + void setIsContractScope(bool InContractScope) { + Flags = (Flags & ~ContractAssertScope) | + (InContractScope ? ContractAssertScope : ScopeFlags(0)); + } + + bool isContractScope() const { return Flags & ContractAssertScope; } + /// getBreakParent - Return the closest scope that a break statement /// would be affected by. Scope *getBreakParent() { @@ -563,6 +581,10 @@ class Scope { return getFlags() & ScopeFlags::ContinueScope; } + bool isContractAssertScope() const { + return getFlags() & ScopeFlags::ContractAssertScope; + } + /// Determine whether this scope is a C++ 'try' block. bool isTryScope() const { return getFlags() & Scope::TryScope; } diff --git a/clang/include/clang/Sema/ScopeInfo.h b/clang/include/clang/Sema/ScopeInfo.h index 700e361ef83f1..f01e42d859d81 100644 --- a/clang/include/clang/Sema/ScopeInfo.h +++ b/clang/include/clang/Sema/ScopeInfo.h @@ -178,6 +178,9 @@ class FunctionScopeInfo { /// Whether we found an immediate-escalating expression. bool FoundImmediateEscalatingExpression : 1; + /// Wether we're currently in a contract statement in this function scope. + bool InContract : 1; + /// First coroutine statement in the current function. /// (ex co_return, co_await, co_yield) SourceLocation FirstCoroutineStmtLoc; @@ -398,7 +401,7 @@ class FunctionScopeInfo { ObjCIsDesignatedInit(false), ObjCWarnForNoDesignatedInitChain(false), ObjCIsSecondaryInit(false), ObjCWarnForNoInitDelegation(false), NeedsCoroutineSuspends(true), FoundImmediateEscalatingExpression(false), - ErrorTrap(Diag) {} + InContract(false), ErrorTrap(Diag) {} virtual ~FunctionScopeInfo(); @@ -724,10 +727,16 @@ class CapturingScopeInfo : public FunctionScopeInfo { /// is deduced (e.g. a lambda or block with omitted return type). bool HasImplicitReturnType = false; + /// Whether this contains an unexpanded parameter pack. + bool ContainsUnexpandedParameterPack = false; + /// ReturnType - The target type of return statements in this context, /// or null if unknown. QualType ReturnType; + /// Packs introduced by this, if any. + SmallVector LocalPacks; + void addCapture(ValueDecl *Var, bool isBlock, bool isByref, bool isNested, SourceLocation Loc, SourceLocation EllipsisLoc, QualType CaptureType, bool Invalid) { @@ -895,12 +904,6 @@ class LambdaScopeInfo final : /// Whether any of the capture expressions requires cleanups. CleanupInfo Cleanup; - /// Whether the lambda contains an unexpanded parameter pack. - bool ContainsUnexpandedParameterPack = false; - - /// Packs introduced by this lambda, if any. - SmallVector LocalPacks; - /// Source range covering the explicit template parameter list (if it exists). SourceRange ExplicitTemplateParamsRange; diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 3d9f12d45d646..7b576f1ad916f 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -457,55 +457,6 @@ enum class FunctionEffectMode : uint8_t { Dependent // effect(expr) where expr is dependent. }; -struct FunctionEffectDiff { - enum class Kind { Added, Removed, ConditionMismatch }; - - FunctionEffect::Kind EffectKind; - Kind DiffKind; - FunctionEffectWithCondition Old; // invalid when Added. - FunctionEffectWithCondition New; // invalid when Removed. - - StringRef effectName() const { - if (Old.Effect.kind() != FunctionEffect::Kind::None) - return Old.Effect.name(); - return New.Effect.name(); - } - - /// Describes the result of effects differing between a base class's virtual - /// method and an overriding method in a subclass. - enum class OverrideResult { - NoAction, - Warn, - Merge // Merge missing effect from base to derived. - }; - - /// Return true if adding or removing the effect as part of a type conversion - /// should generate a diagnostic. - bool shouldDiagnoseConversion(QualType SrcType, - const FunctionEffectsRef &SrcFX, - QualType DstType, - const FunctionEffectsRef &DstFX) const; - - /// Return true if adding or removing the effect in a redeclaration should - /// generate a diagnostic. - bool shouldDiagnoseRedeclaration(const FunctionDecl &OldFunction, - const FunctionEffectsRef &OldFX, - const FunctionDecl &NewFunction, - const FunctionEffectsRef &NewFX) const; - - /// Return true if adding or removing the effect in a C++ virtual method - /// override should generate a diagnostic. - OverrideResult shouldDiagnoseMethodOverride( - const CXXMethodDecl &OldMethod, const FunctionEffectsRef &OldFX, - const CXXMethodDecl &NewMethod, const FunctionEffectsRef &NewFX) const; -}; - -struct FunctionEffectDifferences : public SmallVector { - /// Caller should short-circuit by checking for equality first. - FunctionEffectDifferences(const FunctionEffectsRef &Old, - const FunctionEffectsRef &New); -}; - /// Sema - This implements semantic analysis and AST building for C. /// \nosubgrouping class Sema final : public SemaBase { @@ -546,6 +497,7 @@ class Sema final : public SemaBase { // 32. Constraints and Concepts (SemaConcept.cpp) // 33. Types (SemaType.cpp) // 34. FixIt Helpers (SemaFixItUtils.cpp) + // 35. Function Effects (SemaFunctionEffects.cpp) /// \name Semantic Analysis /// Implementations are in Sema.cpp @@ -750,10 +702,10 @@ class Sema final : public SemaBase { /// Retrieve the current block, if any. sema::BlockScopeInfo *getCurBlock(); - /// Get the innermost lambda enclosing the current location, if any. This - /// looks through intervening non-lambda scopes such as local functions and - /// blocks. - sema::LambdaScopeInfo *getEnclosingLambda() const; + /// Get the innermost lambda or block enclosing the current location, if any. + /// This looks through intervening non-lambda, non-block scopes such as local + /// functions. + sema::CapturingScopeInfo *getEnclosingLambdaOrBlock() const; /// Retrieve the current lambda scope info, if any. /// \param IgnoreNonLambdaCapturingScope true if should find the top-most @@ -851,30 +803,10 @@ class Sema final : public SemaBase { /// Warn when implicitly casting 0 to nullptr. void diagnoseZeroToNullptrConversion(CastKind Kind, const Expr *E); - // ----- function effects --- - /// Warn when implicitly changing function effects. void diagnoseFunctionEffectConversion(QualType DstType, QualType SrcType, SourceLocation Loc); - /// Warn and return true if adding an effect to a set would create a conflict. - bool diagnoseConflictingFunctionEffect(const FunctionEffectsRef &FX, - const FunctionEffectWithCondition &EC, - SourceLocation NewAttrLoc); - - // Report a failure to merge function effects between declarations due to a - // conflict. - void - diagnoseFunctionEffectMergeConflicts(const FunctionEffectSet::Conflicts &Errs, - SourceLocation NewLoc, - SourceLocation OldLoc); - - /// Try to parse the conditional expression attached to an effect attribute - /// (e.g. 'nonblocking'). (c.f. Sema::ActOnNoexceptSpec). Return an empty - /// optional on error. - std::optional - ActOnEffectExpression(Expr *CondExpr, StringRef AttributeName); - /// makeUnavailableInSystemHeader - There is an error in the current /// context. If we're still in a system header, and we can plausibly /// make the relevant declaration unavailable instead of erroring, do @@ -2760,6 +2692,76 @@ class Sema final : public SemaBase { // // + /// \name C++ Contracts + /// Implementations are in SemaContract.cpp + ///@{ + +public: + StmtResult ActOnContractAssert(ContractKind CK, SourceLocation KeywordLoc, + Expr *Cond, ResultNameDecl *ResultNameDecl, + ParsedAttributes &Attrs); + + ResultNameDecl *ActOnResultNameDeclarator(ContractKind CK, Scope *S, + QualType T, SourceLocation IDLoc, + IdentifierInfo *II); + + ExprResult ActOnContractAssertCondition(Expr *Cond); + + StmtResult BuildContractStmt(ContractKind CK, SourceLocation KeywordLoc, + Expr *Cond, DeclStmt *ResultName, + ArrayRef Attrs); + + ContractSpecifierDecl * + BuildContractSpecifierDecl(ArrayRef Contracts, + DeclContext *DC, SourceLocation Loc, + bool IsInvalid); + + // Check two function declarations for equivalent contract sequences. + // Return true if a diagnostic was issued, false otherwise. + bool CheckEquivalentContractSequence(FunctionDecl *OrigDecl, + FunctionDecl *NewDecl); + bool CheckContractsOnRedeclaration(FunctionDecl *OrigDecl, + FunctionDecl *NewDecl); + + + /// Perform semantic analysis for a contract specifier on the specified function. + /// For function templates, these checks should be performed with the instantiation of + /// the body, and not the declaration. + void CheckFunctionContracts(FunctionDecl *FD, bool IsDefinition, + bool IsInstantiation); + + // FIXME(EricWF): Remove me. These are just convinence hooks while I move + // things around in the implementation. + ContractSpecifierDecl * + ActOnFinishContractSpecifierSequence(ArrayRef ContractStmts, + SourceLocation Loc, bool IsInvalid); + + void ActOnContractsOnFinishFunctionDecl(FunctionDecl *FD, bool IsDefinition); + void ActOnContractsOnFinishFunctionBody(FunctionDecl *FD); + + /// Rebuild the contract specifier against another declaration of the function + /// (using the new functions parameters) + ContractSpecifierDecl * + RebuildContractSpecifierForDecl(FunctionDecl *FirstDecl, + FunctionDecl *Definition); + + /// + DeclResult + RebuildContractsWithPlaceholderReturnType(FunctionDecl *Definition); + + void InstantiateContractSpecifier( + SourceLocation PointOfInstantiation, FunctionDecl *Instantiation, + const FunctionDecl *Pattern, + const MultiLevelTemplateArgumentList &TemplateArgs); + + ///@} + + // + // + // ------------------------------------------------------------------------- + // + // + /// \name C++ Scope Specifiers /// Implementations are in SemaCXXScopeSpec.cpp ///@{ @@ -4521,9 +4523,10 @@ class Sema final : public SemaBase { SourceLocation *ArgLocation = nullptr); /// Determine if type T is a valid subject for a nonnull and similar - /// attributes. By default, we look through references (the behavior used by - /// nonnull), but if the second parameter is true, then we treat a reference - /// type as valid. + /// attributes. Dependent types are considered valid so they can be checked + /// during instantiation time. By default, we look through references (the + /// behavior used by nonnull), but if the second parameter is true, then we + /// treat a reference type as valid. bool isValidPointerAttrType(QualType T, bool RefOkay = false); /// AddAssumeAlignedAttr - Adds an assume_aligned attribute to a particular @@ -4595,9 +4598,10 @@ class Sema final : public SemaBase { /// declaration. void AddAlignValueAttr(Decl *D, const AttributeCommonInfo &CI, Expr *E); - /// AddAnnotationAttr - Adds an annotation Annot with Args arguments to D. - void AddAnnotationAttr(Decl *D, const AttributeCommonInfo &CI, - StringRef Annot, MutableArrayRef Args); + /// CreateAnnotationAttr - Creates an annotation Annot with Args arguments. + Attr *CreateAnnotationAttr(const AttributeCommonInfo &CI, StringRef Annot, + MutableArrayRef Args); + Attr *CreateAnnotationAttr(const ParsedAttr &AL); bool checkMSInheritanceAttrOnDefinition(CXXRecordDecl *RD, SourceRange Range, bool BestCase, @@ -6392,7 +6396,6 @@ class Sema final : public SemaBase { bool InDiscardedStatement; bool InImmediateFunctionContext; bool InImmediateEscalatingFunctionContext; - bool IsCurrentlyCheckingDefaultArgumentOrInitializer = false; // We are in a constant context, but we also allow @@ -6405,6 +6408,9 @@ class Sema final : public SemaBase { /// example, in a for-range initializer). bool InLifetimeExtendingContext = false; + /// Whether we're currently evaluating the predicate of a contract assertion + bool InContractAssertion = false; + /// Whether we should rebuild CXXDefaultArgExpr and CXXDefaultInitExpr. bool RebuildDefaultArgOrDefaultInit = false; @@ -6475,6 +6481,12 @@ class Sema final : public SemaBase { ExpressionEvaluationContext::ImmediateFunctionContext && InDiscardedStatement); } + + bool isContractAssertionContext() const { return InContractAssertion; } + + // True iff we're in a context that requires applying constification + // adjustments to some declarations. + bool isConstificationContext() const { return InContractAssertion; } }; const ExpressionEvaluationContextRecord ¤tEvaluationContext() const { @@ -6504,6 +6516,77 @@ class Sema final : public SemaBase { ExpressionEvaluationContextRecord::ExpressionKind::EK_AttrArgument; } + bool isContractAssertionContext() const { + return ExprEvalContexts.back().isContractAssertionContext(); + } + + bool isConstificationContext() const { + return ExprEvalContexts.back().isConstificationContext(); + } + + struct ContractScopeRecord { + SourceLocation KeywordLoc; + DeclContext *ContextAtPush; + QualType PreviousCXXThisType; + + /// The size of the FunctionScopes stack at the time of entering the contract + /// This is important for determining if constification applies to a given declaration + /// captured within a lambda expression. + unsigned FunctionIndexAtPush; + bool AddedConstToCXXThis = false; + bool WasInContractContext = false; + ContractScopeRecord *Previous = nullptr; + + public: + ContractScopeRecord(SourceLocation KeywordLoc, DeclContext *PushContext, + QualType PreviousCXXThisType, + unsigned FunctionIndexAtPush, bool AddedConstToCXXThis, + bool WasInContractContext, + ContractScopeRecord *Previous) + : KeywordLoc(KeywordLoc), ContextAtPush(PushContext), + PreviousCXXThisType(PreviousCXXThisType), + FunctionIndexAtPush(FunctionIndexAtPush), + AddedConstToCXXThis(AddedConstToCXXThis), + WasInContractContext(WasInContractContext), + Previous(Previous) {} + + ContractScopeRecord(ContractScopeRecord const &) = delete; + ContractScopeRecord &operator=(ContractScopeRecord const &) = delete; + }; + + struct ContractScopeRAII { + ContractScopeRAII(Sema &S, SourceLocation ContractLoc, + bool ReplaceThis = false); + ~ContractScopeRAII(); + + private: + ContractScopeRAII(ContractScopeRAII const &) = delete; + ContractScopeRAII &operator=(ContractScopeRAII const &) = delete; + + Sema &S; + ContractScopeRecord Record; + }; + + // The contract whos condition is the current expression evaluation context. + // or null if we're not inside a contract. + ContractScopeRecord *CurrentContractEntry = nullptr; + + SourceLocation getCurrentContractKeywordLoc() { + assert(CurrentContractEntry && "No current contract?"); + return CurrentContractEntry->KeywordLoc; + } + + // Return whether to constify the specified variable in the current context. + ContractConstification getContractConstification(const ValueDecl *VD); + + /// Return true if the usage of this variable in the current context would "cross" a contract boundary. + /// Meaning the variable is declared above the contract scope and used below it. + bool isUsageAcrossContract(const ValueDecl *VD); + + /// Return the correctly constified 'this' type, accounting for any constification contexts + /// that may be in effect. + QualType adjustCXXThisTypeForContracts(QualType QT); + /// Increment when we find a reference; decrement when we find an ignored /// assignment. Ultimately the value is 0 if every reference is an ignored /// assignment. @@ -8076,6 +8159,11 @@ class Sema final : public SemaBase { /// this provides the type used for 'this'. QualType CXXThisTypeOverride; + /// Whether the current CXXThisTypeOverride has `const` added to it by a + /// contract constification context when the current 'this' type would have + /// otherwise been non-const. Used when issuing diagnostics. + bool ContractAddedConstToCXXThis = false; + /// RAII object used to temporarily allow the C++ 'this' expression /// to be used, with the given qualifiers on the current class type. class CXXThisScopeRAII { @@ -11393,9 +11481,9 @@ class Sema final : public SemaBase { CXXScopeSpec &SS, IdentifierInfo *Name, SourceLocation NameLoc, const ParsedAttributesView &Attr, TemplateParameterList *TemplateParams, AccessSpecifier AS, SourceLocation ModulePrivateLoc, - SourceLocation FriendLoc, unsigned NumOuterTemplateParamLists, - TemplateParameterList **OuterTemplateParamLists, - SkipBodyInfo *SkipBody = nullptr); + SourceLocation FriendLoc, + ArrayRef OuterTemplateParamLists, + bool IsMemberSpecialization, SkipBodyInfo *SkipBody = nullptr); /// Translates template arguments as provided by the parser /// into template arguments used by semantic analysis. @@ -11434,7 +11522,8 @@ class Sema final : public SemaBase { DeclResult ActOnVarTemplateSpecialization( Scope *S, Declarator &D, TypeSourceInfo *DI, LookupResult &Previous, SourceLocation TemplateKWLoc, TemplateParameterList *TemplateParams, - StorageClass SC, bool IsPartialSpecialization); + StorageClass SC, bool IsPartialSpecialization, + bool IsMemberSpecialization); /// Get the specialization of the given variable template corresponding to /// the specified argument list, or a null-but-valid result if the arguments @@ -12484,9 +12573,8 @@ class Sema final : public SemaBase { sema::TemplateDeductionInfo &Info); bool isTemplateTemplateParameterAtLeastAsSpecializedAs( - TemplateParameterList *PParam, TemplateDecl *PArg, TemplateDecl *AArg, - const DefaultArguments &DefaultArgs, SourceLocation ArgLoc, - bool IsDeduced); + TemplateParameterList *PParam, TemplateDecl *AArg, + const DefaultArguments &DefaultArgs, SourceLocation Loc, bool IsDeduced); /// Mark which template parameters are used in a given expression. /// @@ -12795,9 +12883,6 @@ class Sema final : public SemaBase { /// We are instantiating a type alias template declaration. TypeAliasTemplateInstantiation, - - /// We are performing partial ordering for template template parameters. - PartialOrderingTTP, } Kind; /// Was the enclosing context a non-instantiation SFINAE context? @@ -13019,12 +13104,6 @@ class Sema final : public SemaBase { TemplateDecl *Entity, BuildingDeductionGuidesTag, SourceRange InstantiationRange = SourceRange()); - struct PartialOrderingTTP {}; - /// \brief Note that we are partial ordering template template parameters. - InstantiatingTemplate(Sema &SemaRef, SourceLocation ArgLoc, - PartialOrderingTTP, TemplateDecl *PArg, - SourceRange InstantiationRange = SourceRange()); - /// Note that we have finished instantiating this template. void Clear(); @@ -13085,28 +13164,14 @@ class Sema final : public SemaBase { /// dealing with a specialization. This is only relevant for function /// template specializations. /// - /// \param Pattern If non-NULL, indicates the pattern from which we will be - /// instantiating the definition of the given declaration, \p ND. This is - /// used to determine the proper set of template instantiation arguments for - /// friend function template specializations. - /// /// \param ForConstraintInstantiation when collecting arguments, /// ForConstraintInstantiation indicates we should continue looking when /// encountering a lambda generic call operator, and continue looking for /// arguments on an enclosing class template. - /// - /// \param SkipForSpecialization when specified, any template specializations - /// in a traversal would be ignored. - /// \param ForDefaultArgumentSubstitution indicates we should continue looking - /// when encountering a specialized member function template, rather than - /// returning immediately. MultiLevelTemplateArgumentList getTemplateInstantiationArgs( const NamedDecl *D, const DeclContext *DC = nullptr, bool Final = false, std::optional> Innermost = std::nullopt, - bool RelativeToPrimary = false, const FunctionDecl *Pattern = nullptr, - bool ForConstraintInstantiation = false, - bool SkipForSpecialization = false, - bool ForDefaultArgumentSubstitution = false); + bool RelativeToPrimary = false, bool ForConstraintInstantiation = false); /// RAII object to handle the state changes required to synthesize /// a function body. @@ -15062,6 +15127,12 @@ class Sema final : public SemaBase { return hasAcceptableDefinition(D, &Hidden, Kind); } + /// Try to parse the conditional expression attached to an effect attribute + /// (e.g. 'nonblocking'). (c.f. Sema::ActOnNoexceptSpec). Return an empty + /// optional on error. + std::optional + ActOnEffectExpression(Expr *CondExpr, StringRef AttributeName); + private: /// The implementation of RequireCompleteType bool RequireCompleteTypeImpl(SourceLocation Loc, QualType T, @@ -15092,6 +15163,108 @@ class Sema final : public SemaBase { std::string getFixItZeroLiteralForType(QualType T, SourceLocation Loc) const; ///@} + + // + // + // ------------------------------------------------------------------------- + // + // + + /// \name Function Effects + /// Implementations are in SemaFunctionEffects.cpp + ///@{ +public: + struct FunctionEffectDiff { + enum class Kind { Added, Removed, ConditionMismatch }; + + FunctionEffect::Kind EffectKind; + Kind DiffKind; + std::optional + Old; // Invalid when 'Kind' is 'Added'. + std::optional + New; // Invalid when 'Kind' is 'Removed'. + + StringRef effectName() const { + if (Old) + return Old.value().Effect.name(); + return New.value().Effect.name(); + } + + /// Describes the result of effects differing between a base class's virtual + /// method and an overriding method in a subclass. + enum class OverrideResult { + NoAction, + Warn, + Merge // Merge missing effect from base to derived. + }; + + /// Return true if adding or removing the effect as part of a type + /// conversion should generate a diagnostic. + bool shouldDiagnoseConversion(QualType SrcType, + const FunctionEffectsRef &SrcFX, + QualType DstType, + const FunctionEffectsRef &DstFX) const; + + /// Return true if adding or removing the effect in a redeclaration should + /// generate a diagnostic. + bool shouldDiagnoseRedeclaration(const FunctionDecl &OldFunction, + const FunctionEffectsRef &OldFX, + const FunctionDecl &NewFunction, + const FunctionEffectsRef &NewFX) const; + + /// Return true if adding or removing the effect in a C++ virtual method + /// override should generate a diagnostic. + OverrideResult shouldDiagnoseMethodOverride( + const CXXMethodDecl &OldMethod, const FunctionEffectsRef &OldFX, + const CXXMethodDecl &NewMethod, const FunctionEffectsRef &NewFX) const; + }; + + struct FunctionEffectDiffVector : public SmallVector { + /// Caller should short-circuit by checking for equality first. + FunctionEffectDiffVector(const FunctionEffectsRef &Old, + const FunctionEffectsRef &New); + }; + + /// All functions/lambdas/blocks which have bodies and which have a non-empty + /// FunctionEffectsRef to be verified. + SmallVector DeclsWithEffectsToVerify; + + /// The union of all effects present on DeclsWithEffectsToVerify. Conditions + /// are all null. + FunctionEffectKindSet AllEffectsToVerify; + +public: + /// Warn and return true if adding a function effect to a set would create a + /// conflict. + bool diagnoseConflictingFunctionEffect(const FunctionEffectsRef &FX, + const FunctionEffectWithCondition &EC, + SourceLocation NewAttrLoc); + + // Report a failure to merge function effects between declarations due to a + // conflict. + void + diagnoseFunctionEffectMergeConflicts(const FunctionEffectSet::Conflicts &Errs, + SourceLocation NewLoc, + SourceLocation OldLoc); + + /// Inline checks from the start of maybeAddDeclWithEffects, to + /// minimize performance impact on code not using effects. + template + void maybeAddDeclWithEffects(FuncOrBlockDecl *D) { + if (Context.hasAnyFunctionEffects()) + if (FunctionEffectsRef FX = D->getFunctionEffects(); !FX.empty()) + maybeAddDeclWithEffects(D, FX); + } + + /// Potentially add a FunctionDecl or BlockDecl to DeclsWithEffectsToVerify. + void maybeAddDeclWithEffects(const Decl *D, const FunctionEffectsRef &FX); + + /// Unconditionally add a Decl to DeclsWithEfffectsToVerify. + void addDeclWithEffects(const Decl *D, const FunctionEffectsRef &FX); + + void performFunctionEffectAnalysis(TranslationUnitDecl *TU); + + ///@} }; DeductionFailureInfo diff --git a/clang/include/clang/Sema/SemaOpenACC.h b/clang/include/clang/Sema/SemaOpenACC.h index 26564835fa1af..59a9648d5f938 100644 --- a/clang/include/clang/Sema/SemaOpenACC.h +++ b/clang/include/clang/Sema/SemaOpenACC.h @@ -38,9 +38,37 @@ class SemaOpenACC : public SemaBase { /// haven't had their 'parent' compute construct set yet. Entires will only be /// made to this list in the case where we know the loop isn't an orphan. llvm::SmallVector ParentlessLoopConstructs; - /// Whether we are inside of a compute construct, and should add loops to the - /// above collection. - bool InsideComputeConstruct = false; + + struct ComputeConstructInfo { + /// Which type of compute construct we are inside of, which we can use to + /// determine whether we should add loops to the above collection. We can + /// also use it to diagnose loop construct clauses. + OpenACCDirectiveKind Kind = OpenACCDirectiveKind::Invalid; + // If we have an active compute construct, stores the list of clauses we've + // prepared for it, so that we can diagnose limitations on child constructs. + ArrayRef Clauses; + } ActiveComputeConstructInfo; + + bool isInComputeConstruct() const { + return ActiveComputeConstructInfo.Kind != OpenACCDirectiveKind::Invalid; + } + + /// Certain clauses care about the same things that aren't specific to the + /// individual clause, but can be shared by a few, so store them here. All + /// require a 'no intervening constructs' rule, so we know they are all from + /// the same 'place'. + struct LoopCheckingInfo { + /// Records whether we've seen the top level 'for'. We already diagnose + /// later that the 'top level' is a for loop, so we use this to suppress the + /// 'collapse inner loop not a 'for' loop' diagnostic. + LLVM_PREFERRED_TYPE(bool) + unsigned TopLevelLoopSeen : 1; + + /// Records whether this 'tier' of the loop has already seen a 'for' loop, + /// used to diagnose if there are multiple 'for' loops at any one level. + LLVM_PREFERRED_TYPE(bool) + unsigned CurLevelHasLoopAlready : 1; + } LoopInfo{/*TopLevelLoopSeen=*/false, /*CurLevelHasLoopAlready=*/false}; /// The 'collapse' clause requires quite a bit of checking while /// parsing/instantiating its body, so this structure/object keeps all of the @@ -59,27 +87,38 @@ class SemaOpenACC : public SemaBase { /// else it should be 'N' minus the current depth traversed. std::optional CurCollapseCount; - /// Records whether we've seen the top level 'for'. We already diagnose - /// later that the 'top level' is a for loop, so we use this to suppress the - /// 'collapse inner loop not a 'for' loop' diagnostic. - LLVM_PREFERRED_TYPE(bool) - unsigned TopLevelLoopSeen : 1; - - /// Records whether this 'tier' of the loop has already seen a 'for' loop, - /// used to diagnose if there are multiple 'for' loops at any one level. - LLVM_PREFERRED_TYPE(bool) - unsigned CurLevelHasLoopAlready : 1; - /// Records whether we've hit a CurCollapseCount of '0' on the way down, /// which allows us to diagnose if the value of 'N' is too large for the /// current number of 'for' loops. - LLVM_PREFERRED_TYPE(bool) - unsigned CollapseDepthSatisfied : 1; - } CollapseInfo{nullptr, std::nullopt, /*TopLevelLoopSeen=*/false, - /*CurLevelHasLoopAlready=*/false, - /*CollapseDepthSatisfied=*/true}; + bool CollapseDepthSatisfied = true; + } CollapseInfo; + + /// The 'tile' clause requires a bit of additional checking as well, so like + /// the `CollapseCheckingInfo`, ensure we maintain information here too. + struct TileCheckingInfo { + OpenACCTileClause *ActiveTile = nullptr; + + /// This is the number of expressions on a 'tile' clause. This doesn't have + /// to be an APSInt because it isn't the result of a constexpr, just by our + /// own counting of elements. + std::optional CurTileCount; + + /// Records whether we've hit a 'CurTileCount' of '0' on the wya down, + /// which allows us to diagnose if the number of arguments is too large for + /// the current number of 'for' loops. + bool TileDepthSatisfied = true; + } TileInfo; public: + ComputeConstructInfo &getActiveComputeConstructInfo() { + return ActiveComputeConstructInfo; + } + + /// If there is a current 'active' loop construct with a 'gang' clause on a + /// 'kernel' construct, this will have the source location for it. This + /// permits us to implement the restriction of no further 'gang' clauses. + SourceLocation LoopGangClauseOnKernelLoc; + // Redeclaration of the version in OpenACCClause.h. using DeviceTypeArgument = std::pair; @@ -130,9 +169,14 @@ class SemaOpenACC : public SemaBase { Expr *LoopCount; }; + struct GangDetails { + SmallVector GangKinds; + SmallVector IntExprs; + }; + std::variant + ReductionDetails, CollapseDetails, GangDetails> Details = std::monostate{}; public: @@ -179,6 +223,7 @@ class SemaOpenACC : public SemaBase { assert((ClauseKind == OpenACCClauseKind::NumGangs || ClauseKind == OpenACCClauseKind::NumWorkers || ClauseKind == OpenACCClauseKind::Async || + ClauseKind == OpenACCClauseKind::Tile || ClauseKind == OpenACCClauseKind::VectorLength) && "Parsed clause kind does not have a int exprs"); @@ -224,9 +269,19 @@ class SemaOpenACC : public SemaBase { assert((ClauseKind == OpenACCClauseKind::NumGangs || ClauseKind == OpenACCClauseKind::NumWorkers || ClauseKind == OpenACCClauseKind::Async || + ClauseKind == OpenACCClauseKind::Tile || + ClauseKind == OpenACCClauseKind::Gang || ClauseKind == OpenACCClauseKind::VectorLength) && "Parsed clause kind does not have a int exprs"); + if (ClauseKind == OpenACCClauseKind::Gang) { + // There might not be any gang int exprs, as this is an optional + // argument. + if (std::holds_alternative(Details)) + return {}; + return std::get(Details).IntExprs; + } + return std::get(Details).IntExprs; } @@ -238,6 +293,16 @@ class SemaOpenACC : public SemaBase { return std::get(Details).Op; } + ArrayRef getGangKinds() const { + assert(ClauseKind == OpenACCClauseKind::Gang && + "Parsed clause kind does not have gang kind"); + // The args on gang are optional, so this might not actually hold + // anything. + if (std::holds_alternative(Details)) + return {}; + return std::get(Details).GangKinds; + } + ArrayRef getVarList() { assert((ClauseKind == OpenACCClauseKind::Private || ClauseKind == OpenACCClauseKind::NoCreate || @@ -335,6 +400,7 @@ class SemaOpenACC : public SemaBase { assert((ClauseKind == OpenACCClauseKind::NumGangs || ClauseKind == OpenACCClauseKind::NumWorkers || ClauseKind == OpenACCClauseKind::Async || + ClauseKind == OpenACCClauseKind::Tile || ClauseKind == OpenACCClauseKind::VectorLength) && "Parsed clause kind does not have a int exprs"); Details = IntExprDetails{{IntExprs.begin(), IntExprs.end()}}; @@ -343,11 +409,31 @@ class SemaOpenACC : public SemaBase { assert((ClauseKind == OpenACCClauseKind::NumGangs || ClauseKind == OpenACCClauseKind::NumWorkers || ClauseKind == OpenACCClauseKind::Async || + ClauseKind == OpenACCClauseKind::Tile || ClauseKind == OpenACCClauseKind::VectorLength) && "Parsed clause kind does not have a int exprs"); Details = IntExprDetails{std::move(IntExprs)}; } + void setGangDetails(ArrayRef GKs, + ArrayRef IntExprs) { + assert(ClauseKind == OpenACCClauseKind::Gang && + "Parsed Clause kind does not have gang details"); + assert(GKs.size() == IntExprs.size() && "Mismatched kind/size?"); + + Details = GangDetails{{GKs.begin(), GKs.end()}, + {IntExprs.begin(), IntExprs.end()}}; + } + + void setGangDetails(llvm::SmallVector &&GKs, + llvm::SmallVector &&IntExprs) { + assert(ClauseKind == OpenACCClauseKind::Gang && + "Parsed Clause kind does not have gang details"); + assert(GKs.size() == IntExprs.size() && "Mismatched kind/size?"); + + Details = GangDetails{std::move(GKs), std::move(IntExprs)}; + } + void setVarListDetails(ArrayRef VarList, bool IsReadOnly, bool IsZero) { assert((ClauseKind == OpenACCClauseKind::Private || @@ -522,17 +608,28 @@ class SemaOpenACC : public SemaBase { SourceLocation RBLoc); /// Checks the loop depth value for a collapse clause. ExprResult CheckCollapseLoopCount(Expr *LoopCount); + /// Checks a single size expr for a tile clause. + ExprResult CheckTileSizeExpr(Expr *SizeExpr); + + // Check a single expression on a gang clause. + ExprResult CheckGangExpr(OpenACCGangKind GK, Expr *E); + + ExprResult BuildOpenACCAsteriskSizeExpr(SourceLocation AsteriskLoc); + ExprResult ActOnOpenACCAsteriskSizeExpr(SourceLocation AsteriskLoc); /// Helper type to restore the state of various 'loop' constructs when we run /// into a loop (for, etc) inside the construct. class LoopInConstructRAII { SemaOpenACC &SemaRef; + LoopCheckingInfo OldLoopInfo; CollapseCheckingInfo OldCollapseInfo; + TileCheckingInfo OldTileInfo; bool PreserveDepth; public: LoopInConstructRAII(SemaOpenACC &SemaRef, bool PreserveDepth = true) - : SemaRef(SemaRef), OldCollapseInfo(SemaRef.CollapseInfo), + : SemaRef(SemaRef), OldLoopInfo(SemaRef.LoopInfo), + OldCollapseInfo(SemaRef.CollapseInfo), OldTileInfo(SemaRef.TileInfo), PreserveDepth(PreserveDepth) {} ~LoopInConstructRAII() { // The associated-statement level of this should NOT preserve this, as it @@ -541,12 +638,20 @@ class SemaOpenACC : public SemaBase { bool CollapseDepthSatisified = PreserveDepth ? SemaRef.CollapseInfo.CollapseDepthSatisfied : OldCollapseInfo.CollapseDepthSatisfied; + bool TileDepthSatisfied = PreserveDepth + ? SemaRef.TileInfo.TileDepthSatisfied + : OldTileInfo.TileDepthSatisfied; bool CurLevelHasLoopAlready = - PreserveDepth ? SemaRef.CollapseInfo.CurLevelHasLoopAlready - : OldCollapseInfo.CurLevelHasLoopAlready; + PreserveDepth ? SemaRef.LoopInfo.CurLevelHasLoopAlready + : OldLoopInfo.CurLevelHasLoopAlready; + + SemaRef.LoopInfo = OldLoopInfo; SemaRef.CollapseInfo = OldCollapseInfo; + SemaRef.TileInfo = OldTileInfo; + SemaRef.CollapseInfo.CollapseDepthSatisfied = CollapseDepthSatisified; - SemaRef.CollapseInfo.CurLevelHasLoopAlready = CurLevelHasLoopAlready; + SemaRef.TileInfo.TileDepthSatisfied = TileDepthSatisfied; + SemaRef.LoopInfo.CurLevelHasLoopAlready = CurLevelHasLoopAlready; } }; @@ -555,8 +660,9 @@ class SemaOpenACC : public SemaBase { /// Loop needing its parent construct. class AssociatedStmtRAII { SemaOpenACC &SemaRef; - bool WasInsideComputeConstruct; + ComputeConstructInfo OldActiveComputeConstructInfo; OpenACCDirectiveKind DirKind; + SourceLocation OldLoopGangClauseOnKernelLoc; llvm::SmallVector ParentlessLoopConstructs; LoopInConstructRAII LoopRAII; @@ -567,6 +673,9 @@ class SemaOpenACC : public SemaBase { void SetCollapseInfoBeforeAssociatedStmt( ArrayRef UnInstClauses, ArrayRef Clauses); + void SetTileInfoBeforeAssociatedStmt( + ArrayRef UnInstClauses, + ArrayRef Clauses); ~AssociatedStmtRAII(); }; }; diff --git a/clang/include/clang/Sema/SemaOpenMP.h b/clang/include/clang/Sema/SemaOpenMP.h index 53191e7bb4272..80ad30b0f99ef 100644 --- a/clang/include/clang/Sema/SemaOpenMP.h +++ b/clang/include/clang/Sema/SemaOpenMP.h @@ -891,6 +891,11 @@ class SemaOpenMP : public SemaBase { SourceLocation StartLoc, SourceLocation LParenLoc, SourceLocation EndLoc); + /// Called on well-form 'permutation' clause after parsing its arguments. + OMPClause *ActOnOpenMPPermutationClause(ArrayRef PermExprs, + SourceLocation StartLoc, + SourceLocation LParenLoc, + SourceLocation EndLoc); /// Called on well-form 'full' clauses. OMPClause *ActOnOpenMPFullClause(SourceLocation StartLoc, SourceLocation EndLoc); diff --git a/clang/include/clang/Sema/Template.h b/clang/include/clang/Sema/Template.h index fe27290efdbfc..e6d53eb086dd4 100644 --- a/clang/include/clang/Sema/Template.h +++ b/clang/include/clang/Sema/Template.h @@ -411,10 +411,10 @@ enum class TemplateSubstitutionKind : char { /// lookup will search our outer scope. bool CombineWithOuterScope; - /// Whether this scope is being used to instantiate a lambda expression, - /// in which case it should be reused for instantiating the lambda's - /// FunctionProtoType. - bool InstantiatingLambda = false; + /// Whether this scope is being used to instantiate a lambda or block + /// expression, in which case it should be reused for instantiating the + /// lambda's FunctionProtoType. + bool InstantiatingLambdaOrBlock = false; /// If non-NULL, the template parameter pack that has been /// partially substituted per C++0x [temp.arg.explicit]p9. @@ -431,10 +431,10 @@ enum class TemplateSubstitutionKind : char { public: LocalInstantiationScope(Sema &SemaRef, bool CombineWithOuterScope = false, - bool InstantiatingLambda = false) + bool InstantiatingLambdaOrBlock = false) : SemaRef(SemaRef), Outer(SemaRef.CurrentInstantiationScope), CombineWithOuterScope(CombineWithOuterScope), - InstantiatingLambda(InstantiatingLambda) { + InstantiatingLambdaOrBlock(InstantiatingLambdaOrBlock) { SemaRef.CurrentInstantiationScope = this; } @@ -561,8 +561,8 @@ enum class TemplateSubstitutionKind : char { /// Determine whether D is a pack expansion created in this scope. bool isLocalPackExpansion(const Decl *D); - /// Determine whether this scope is for instantiating a lambda. - bool isLambda() const { return InstantiatingLambda; } + /// Determine whether this scope is for instantiating a lambda or block. + bool isLambdaOrBlock() const { return InstantiatingLambdaOrBlock; } }; class TemplateDeclInstantiator @@ -627,10 +627,11 @@ enum class TemplateSubstitutionKind : char { #define EMPTY(DERIVED, BASE) #define LIFETIMEEXTENDEDTEMPORARY(DERIVED, BASE) - // Decls which use special-case instantiation code. + // Decls which use special-case instantiation code.q #define BLOCK(DERIVED, BASE) #define CAPTURED(DERIVED, BASE) #define IMPLICITPARAM(DERIVED, BASE) +#define CONTRACTSPECIFIER(DERIVED, BASE) #include "clang/AST/DeclNodes.inc" diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h index bde19a09d6ae0..a14379308549d 100644 --- a/clang/include/clang/Serialization/ASTBitCodes.h +++ b/clang/include/clang/Serialization/ASTBitCodes.h @@ -730,6 +730,10 @@ enum ASTRecordTypes { /// canonical declaration for the lambda class from the same module as /// enclosing function. FUNCTION_DECL_TO_LAMBDAS_MAP = 71, + + /// Record code for Sema's vector of functions/blocks with effects to + /// be verified. + DECLS_WITH_EFFECTS_TO_VERIFY = 72, }; /// Record types used within a source manager block. @@ -1499,7 +1503,13 @@ enum DeclCode { /// An ImplicitConceptSpecializationDecl record. DECL_IMPLICIT_CONCEPT_SPECIALIZATION, - DECL_LAST = DECL_IMPLICIT_CONCEPT_SPECIALIZATION + /// A ResultNameDecl record + DECL_RESULT_NAME, + + /// A contract specifier sequence on a function + DECL_CONTRACT_SPECIFIER, + + DECL_LAST = DECL_CONTRACT_SPECIFIER, }; /// Record codes for each kind of statement or expression. @@ -1992,18 +2002,23 @@ enum StmtCode { EXPR_COYIELD, EXPR_DEPENDENT_COAWAIT, + // contracts + STMT_CXX_CONTRACT, + // FixedPointLiteral EXPR_FIXEDPOINT_LITERAL, // SYCLUniqueStableNameExpr EXPR_SYCL_UNIQUE_STABLE_NAME, - // OpenACC Constructs + // OpenACC Constructs/Exprs STMT_OPENACC_COMPUTE_CONSTRUCT, STMT_OPENACC_LOOP_CONSTRUCT, + EXPR_OPENACC_ASTERISK_SIZE, // HLSL Constructs EXPR_HLSL_OUT_ARG, + }; /// The kinds of designators that can occur in a diff --git a/clang/include/clang/Serialization/ASTReader.h b/clang/include/clang/Serialization/ASTReader.h index c1843218a4b8b..ee4e897b24888 100644 --- a/clang/include/clang/Serialization/ASTReader.h +++ b/clang/include/clang/Serialization/ASTReader.h @@ -984,6 +984,9 @@ class ASTReader /// Sema tracks these to emit deferred diags. llvm::SmallSetVector DeclsToCheckForDeferredDiags; + /// The IDs of all decls with function effects to be checked. + SmallVector DeclsWithEffectsToVerify; + private: struct ImportedSubmodule { serialization::SubmoduleID ID; @@ -2524,7 +2527,7 @@ class BitsUnpacker { inline bool shouldSkipCheckingODR(const Decl *D) { return D->getASTContext().getLangOpts().SkipODRCheckInGMF && - D->isFromGlobalModule(); + (D->isFromGlobalModule() || D->isFromHeaderUnit()); } } // namespace clang diff --git a/clang/include/clang/Serialization/ASTWriter.h b/clang/include/clang/Serialization/ASTWriter.h index e21d41c867314..198dd01b8d07a 100644 --- a/clang/include/clang/Serialization/ASTWriter.h +++ b/clang/include/clang/Serialization/ASTWriter.h @@ -604,6 +604,7 @@ class ASTWriter : public ASTDeserializationListener, void WriteMSPointersToMembersPragmaOptions(Sema &SemaRef); void WritePackPragmaOptions(Sema &SemaRef); void WriteFloatControlPragmaOptions(Sema &SemaRef); + void WriteDeclsWithEffectsToVerify(Sema &SemaRef); void WriteModuleFileExtension(Sema &SemaRef, ModuleFileExtensionWriter &Writer); diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 735def67f7840..bf3e0becd55ec 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -66,6 +66,7 @@ #include "clang/Basic/TargetCXXABI.h" #include "clang/Basic/TargetInfo.h" #include "clang/Basic/XRayLists.h" +#include "clang/Lex/Preprocessor.h" #include "llvm/ADT/APFixedPoint.h" #include "llvm/ADT/APInt.h" #include "llvm/ADT/APSInt.h" @@ -90,6 +91,7 @@ #include "llvm/Support/raw_ostream.h" #include "llvm/TargetParser/AArch64TargetParser.h" #include "llvm/TargetParser/Triple.h" + #include #include #include @@ -9627,6 +9629,253 @@ CreateSystemZBuiltinVaListDecl(const ASTContext *Context) { return Context->buildImplicitTypedef(VaListTagArrayType, "__builtin_va_list"); } +enum ContractViolationDescriptorType { + DT_CStr, + DT_VoidPtr, + DT_Unsigned, + DT_Int, + DT_DescriptorTable +}; + +const char * +GetContractViolationDescriptorTypeString(ContractViolationDescriptorType DT) { + switch (DT) { + case ContractViolationDescriptorType::DT_CStr: + return "const char*"; + case ContractViolationDescriptorType::DT_VoidPtr: + return "void*"; + case ContractViolationDescriptorType::DT_Unsigned: + return "unsigned"; + case ContractViolationDescriptorType::DT_Int: + return "int"; + case ContractViolationDescriptorType::DT_DescriptorTable: + return "descriptor_table*"; + } +} + +QualType GetContractViolationDescriptorType(ContractViolationDescriptorType DT, + const ASTContext *Context) { + switch (DT) { + case ContractViolationDescriptorType::DT_CStr: + return Context->getPointerType(Context->getConstType(Context->CharTy)); + case ContractViolationDescriptorType::DT_VoidPtr: + return Context->VoidPtrTy; + case ContractViolationDescriptorType::DT_Unsigned: + return Context->UnsignedIntTy; + case ContractViolationDescriptorType::DT_Int: + return Context->IntTy; + case ContractViolationDescriptorType::DT_DescriptorTable: + return Context->VoidPtrTy; + } + llvm_unreachable("unhandled case"); +} + +struct ContractViolationDescriptorEntry { + const char *Name; + ContractViolationDescriptorType Type; +}; + +[[maybe_unused]] constexpr ContractViolationDescriptorEntry + ContractViolationDescriptorTableV1[] = {{"__file_", DT_CStr}, + {"__function_", DT_CStr}, + {"__line_", DT_Unsigned}, + {"__column_", DT_Unsigned}, + {"__message_", DT_CStr}, + {"__contract_kind_", DT_Unsigned} + +}; + +struct ContractViolationDescriptorTable { + unsigned version; + + std::vector Entries; +}; + +[[maybe_unused]] static RecordDecl * +CreateBuiltinContractViolationDescriptorEntryDecl(const ASTContext *Context) { + RecordDecl *ViolationInfoT = Context->buildImplicitRecord( + "__builtin_contract_violation_descriptor_entry_t"); + ViolationInfoT->startDefinition(); + + QualType ConstStrLiteralTy = + Context->getPointerType(Context->getConstType(Context->CharTy)); + using Pt = std::pair; + std::array, 3> FieldInfo = { + Pt{ConstStrLiteralTy, "__name_"}, Pt{ConstStrLiteralTy, "__type_"}, + Pt{ConstStrLiteralTy, "__additional_info_"}}; + const auto NumFields = FieldInfo.size(); + + // Create fields + for (unsigned i = 0; i < NumFields; ++i) { + FieldDecl *Field = FieldDecl::Create( + const_cast(*Context), ViolationInfoT, SourceLocation(), + SourceLocation(), &Context->Idents.get(FieldInfo[i].second), + FieldInfo[i].first, + /*TInfo=*/nullptr, + /*BitWidth=*/nullptr, + /*Mutable=*/false, ICIS_NoInit); + Field->setAccess(AS_public); + ViolationInfoT->addDecl(Field); + } + ViolationInfoT->completeDefinition(); + + return ViolationInfoT; +} + +[[maybe_unused]] static RecordDecl * +CreateBuiltinContractDestriptorTable(const ASTContext *Context) { + RecordDecl *ViolationInfoT = Context->buildImplicitRecord( + "__builtin_contract_violation_descriptor_entry_t"); + ViolationInfoT->startDefinition(); + + QualType ConstStrLiteralTy = + Context->getPointerType(Context->getConstType(Context->CharTy)); + using Pt = std::pair; + std::array, 3> FieldInfo = { + Pt{ConstStrLiteralTy, "__name_"}, + Pt{ConstStrLiteralTy, "__type_"}, + Pt{ConstStrLiteralTy, "__size_"}, + }; + const auto NumFields = FieldInfo.size(); + + // Create fields + for (unsigned i = 0; i < NumFields; ++i) { + FieldDecl *Field = FieldDecl::Create( + const_cast(*Context), ViolationInfoT, SourceLocation(), + SourceLocation(), &Context->Idents.get(FieldInfo[i].second), + FieldInfo[i].first, + /*TInfo=*/nullptr, + /*BitWidth=*/nullptr, + /*Mutable=*/false, ICIS_NoInit); + Field->setAccess(AS_public); + ViolationInfoT->addDecl(Field); + } + ViolationInfoT->completeDefinition(); + + return ViolationInfoT; +} + +static RecordDecl * +CreateBuiltinContractViolationRecordDecl(const ASTContext *Context) { + RecordDecl *ViolationInfoT = + Context->buildImplicitRecord("__builtin_contract_violation_info_t"); + ViolationInfoT->startDefinition(); + + QualType ConstStrLiteralTy = + Context->getPointerType(Context->getConstType(Context->CharTy)); + using Pt = std::pair; + std::array, 7> FieldInfo = { + Pt{Context->UnsignedIntTy, "__version_"}, + + // This prefix of {file, function, line, colunm} is important, as it + // matches the layout of the source_location impl struct. This means we + // can use it to produce a valid source location object. + // + Pt{ConstStrLiteralTy, "__file_"}, Pt{ConstStrLiteralTy, "__function_"}, + Pt{Context->UnsignedIntTy, "__line_"}, + Pt{Context->UnsignedIntTy, "__column_"}, + + Pt{ConstStrLiteralTy, "__message_"}, + Pt{Context->UnsignedIntTy, "__contract_kind_"}}; + const auto NumFields = FieldInfo.size(); + + // Create fields + for (unsigned i = 0; i < NumFields; ++i) { + FieldDecl *Field = FieldDecl::Create( + const_cast(*Context), ViolationInfoT, SourceLocation(), + SourceLocation(), &Context->Idents.get(FieldInfo[i].second), + FieldInfo[i].first, + /*TInfo=*/nullptr, + /*BitWidth=*/nullptr, + /*Mutable=*/false, ICIS_NoInit); + Field->setAccess(AS_public); + ViolationInfoT->addDecl(Field); + } + ViolationInfoT->completeDefinition(); + + return ViolationInfoT; +} + +UnnamedGlobalConstantDecl * +ASTContext::BuildViolationObject(const ContractStmt *CS, + const FunctionDecl *CurDecl) { + assert(CS); + SourceLocation Loc = CS->getBeginLoc(); + + auto &Ctx = *this; + + PresumedLoc PLoc = Ctx.getSourceManager().getPresumedLoc( + Ctx.getSourceManager().getExpansionRange(Loc).getEnd()); + + auto MakeStringLiteral = [&](StringRef Tmp) { + using LValuePathEntry = APValue::LValuePathEntry; + StringLiteral *Res = Ctx.getPredefinedStringLiteralFromCache(Tmp); + // Decay the string to a pointer to the first character. + LValuePathEntry Path[1] = {LValuePathEntry::ArrayIndex(0)}; + return APValue(Res, CharUnits::Zero(), Path, /*OnePastTheEnd=*/false); + }; + + const RecordDecl *ImplDecl = + dyn_cast_or_null(getBuiltinContractViolationRecordDecl()); + assert(ImplDecl); + + APValue Value(APValue::UninitStruct(), 0, 8); + for (const FieldDecl *F : ImplDecl->fields()) { + StringRef Name = F->getName(); + if (Name == "__version_") { + llvm::APSInt IntVal = Ctx.MakeIntValue(3, F->getType()); + Value.getStructField(F->getFieldIndex()) = APValue(IntVal); + } else if (Name == "__message_") { + Value.getStructField(F->getFieldIndex()) = + MakeStringLiteral(CS->getMessage(Ctx)); + } else if (Name == "__file_") { + SmallString<256> Path(PLoc.getFilename()); + clang::Preprocessor::processPathForFileMacro(Path, Ctx.getLangOpts(), + Ctx.getTargetInfo()); + Value.getStructField(F->getFieldIndex()) = MakeStringLiteral(Path); + } else if (Name == "__function_") { + // Note: this emits the PrettyFunction name -- different than what + // __builtin_FUNCTION() above returns! + Value.getStructField(F->getFieldIndex()) = MakeStringLiteral( + CurDecl && !isa(CurDecl) + ? StringRef(PredefinedExpr::ComputeName( + PredefinedIdentKind::PrettyFunction, CurDecl)) + : ""); + } else if (Name == "__line_") { + llvm::APSInt IntVal = Ctx.MakeIntValue(PLoc.getLine(), F->getType()); + Value.getStructField(F->getFieldIndex()) = APValue(IntVal); + } else if (Name == "__column_") { + llvm::APSInt IntVal = Ctx.MakeIntValue(PLoc.getColumn(), F->getType()); + Value.getStructField(F->getFieldIndex()) = APValue(IntVal); + + } else if (Name == "__contract_kind_") { + unsigned ContractKindValue = [&]() { + switch (CS->getContractKind()) { + case ContractKind::Pre: + return 1; + case ContractKind::Post: + return 2; + case ContractKind::Assert: + return 3; + } + llvm_unreachable("unhandled ContractKind"); + }(); + + llvm::APSInt IntVal = Ctx.MakeIntValue(ContractKindValue, F->getType()); + Value.getStructField(F->getFieldIndex()) = APValue(IntVal); + } else { + assert(false && + "unexpected field in __builtin_contract_violation_info_t"); + llvm_unreachable("can't touch this"); + } + } + QualType QT = + Ctx.getBuiltinContractViolationRecordType().getUnqualifiedType(); + UnnamedGlobalConstantDecl *GV = Ctx.getUnnamedGlobalConstantDecl(QT, Value); + + return GV; +} + static TypedefDecl *CreateHexagonBuiltinVaListDecl(const ASTContext *Context) { // typedef struct __va_list_tag { RecordDecl *VaListTagDecl; @@ -9713,6 +9962,16 @@ TypedefDecl *ASTContext::getBuiltinVaListDecl() const { return BuiltinVaListDecl; } +Decl *ASTContext::getBuiltinContractViolationRecordDecl() const { + if (!BuiltinContractViolationRecordDecl) { + BuiltinContractViolationRecordDecl = + CreateBuiltinContractViolationRecordDecl(this); + assert(BuiltinContractViolationRecordDecl->isImplicit()); + } + + return BuiltinContractViolationRecordDecl; +} + Decl *ASTContext::getVaListTagDecl() const { // Force the creation of VaListTagDecl by building the __builtin_va_list // declaration. @@ -9722,6 +9981,7 @@ Decl *ASTContext::getVaListTagDecl() const { return VaListTagDecl; } + TypedefDecl *ASTContext::getBuiltinMSVaListDecl() const { if (!BuiltinMSVaListDecl) BuiltinMSVaListDecl = CreateMSVaListDecl(this); @@ -14325,9 +14585,17 @@ void ASTContext::getFunctionFeatureMap(llvm::StringMap &FeatureMap, Target->initFeatureMap(FeatureMap, getDiagnostics(), TargetCPU, Features); } } else if (const auto *TV = FD->getAttr()) { - llvm::SmallVector Feats; - TV->getFeatures(Feats); - std::vector Features = getFMVBackendFeaturesFor(Feats); + std::vector Features; + if (Target->getTriple().isRISCV()) { + ParsedTargetAttr ParsedAttr = Target->parseTargetAttr(TV->getName()); + Features.insert(Features.begin(), ParsedAttr.Features.begin(), + ParsedAttr.Features.end()); + } else { + assert(Target->getTriple().isAArch64()); + llvm::SmallVector Feats; + TV->getFeatures(Feats); + Features = getFMVBackendFeaturesFor(Feats); + } Features.insert(Features.begin(), Target->getTargetOpts().FeaturesAsWritten.begin(), Target->getTargetOpts().FeaturesAsWritten.end()); @@ -14439,8 +14707,6 @@ bool ASTContext::useAbbreviatedThunkName(GlobalDecl VirtualMethodDecl, Mangler->mangleThunk(Method, Thunk, /* elideOverrideInfo */ false, mangledNameStream); - if (Thunks.find(ElidedName) == Thunks.end()) - Thunks[ElidedName] = {}; Thunks[ElidedName].push_back(std::string(MangledName)); } } diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index 020a2f396b5aa..cac90da0128fa 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -3923,6 +3923,7 @@ ExpectedDecl ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) { auto ToQualifierLoc = importChecked(Err, D->getQualifierLoc()); auto TrailingRequiresClause = importChecked(Err, D->getTrailingRequiresClause()); + auto Contracts = D->getContracts(); if (Err) return std::move(Err); @@ -3954,7 +3955,7 @@ ExpectedDecl ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) { ToFunction, D, Importer.getToContext(), cast(DC), ToInnerLocStart, NameInfo, T, TInfo, ESpec, D->UsesFPIntrin(), D->isInlineSpecified(), D->isImplicit(), D->getConstexprKind(), - ToInheritedConstructor, TrailingRequiresClause)) + ToInheritedConstructor, TrailingRequiresClause, Contracts)) return ToFunction; } else if (CXXDestructorDecl *FromDtor = dyn_cast(D)) { @@ -3969,7 +3970,7 @@ ExpectedDecl ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) { ToFunction, D, Importer.getToContext(), cast(DC), ToInnerLocStart, NameInfo, T, TInfo, D->UsesFPIntrin(), D->isInlineSpecified(), D->isImplicit(), D->getConstexprKind(), - TrailingRequiresClause)) + TrailingRequiresClause, Contracts)) return ToFunction; CXXDestructorDecl *ToDtor = cast(ToFunction); @@ -3985,14 +3986,15 @@ ExpectedDecl ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) { ToFunction, D, Importer.getToContext(), cast(DC), ToInnerLocStart, NameInfo, T, TInfo, D->UsesFPIntrin(), D->isInlineSpecified(), ESpec, D->getConstexprKind(), - SourceLocation(), TrailingRequiresClause)) + SourceLocation(), TrailingRequiresClause, Contracts)) return ToFunction; } else if (auto *Method = dyn_cast(D)) { if (GetImportedOrCreateDecl( ToFunction, D, Importer.getToContext(), cast(DC), ToInnerLocStart, NameInfo, T, TInfo, Method->getStorageClass(), Method->UsesFPIntrin(), Method->isInlineSpecified(), - D->getConstexprKind(), SourceLocation(), TrailingRequiresClause)) + D->getConstexprKind(), SourceLocation(), TrailingRequiresClause, + Contracts)) return ToFunction; } else if (auto *Guide = dyn_cast(D)) { ExplicitSpecifier ESpec = @@ -4012,7 +4014,7 @@ ExpectedDecl ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) { ToFunction, D, Importer.getToContext(), DC, ToInnerLocStart, NameInfo, T, TInfo, D->getStorageClass(), D->UsesFPIntrin(), D->isInlineSpecified(), D->hasWrittenPrototype(), - D->getConstexprKind(), TrailingRequiresClause)) + D->getConstexprKind(), TrailingRequiresClause, Contracts)) return ToFunction; } diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp index 680be736aa647..b2663714340b9 100644 --- a/clang/lib/AST/ByteCode/Compiler.cpp +++ b/clang/lib/AST/ByteCode/Compiler.cpp @@ -2128,6 +2128,13 @@ bool Compiler::VisitUnaryExprOrTypeTraitExpr( return this->emitConst(1, E); } + if (Kind == UETT_OpenMPRequiredSimdAlign) { + assert(E->isArgumentType()); + unsigned Bits = ASTCtx.getOpenMPDefaultSimdAlign(E->getArgumentType()); + + return this->emitConst(ASTCtx.toCharUnitsFromBits(Bits).getQuantity(), E); + } + return false; } @@ -2721,7 +2728,7 @@ bool Compiler::VisitMaterializeTemporaryExpr( const Expr *Inner = E->getSubExpr()->skipRValueSubobjectAdjustments(); if (std::optional LocalIndex = - allocateLocal(Inner, E->getExtendingDecl())) { + allocateLocal(E, Inner->getType(), E->getExtendingDecl())) { InitLinkScope ILS(this, InitLink::Temp(*LocalIndex)); if (!this->emitGetPtrLocal(*LocalIndex, E)) return false; @@ -2862,6 +2869,11 @@ bool Compiler::VisitPredefinedExpr(const PredefinedExpr *E) { if (DiscardResult) return true; + if (!Initializing) { + unsigned StringIndex = P.createGlobalString(E->getFunctionName(), E); + return this->emitGetPtrGlobal(StringIndex, E); + } + return this->delegate(E->getFunctionName()); } @@ -3399,7 +3411,7 @@ bool Compiler::VisitCXXDeleteExpr(const CXXDeleteExpr *E) { if (!this->visit(Arg)) return false; - return this->emitFree(E->isArrayForm(), E); + return this->emitFree(E->isArrayForm(), E->isGlobalDelete(), E); } template @@ -3579,8 +3591,9 @@ bool Compiler::VisitShuffleVectorExpr(const ShuffleVectorExpr *E) { } for (unsigned I = 0; I != NumOutputElems; ++I) { APSInt ShuffleIndex = E->getShuffleMaskIdx(Ctx.getASTContext(), I); + assert(ShuffleIndex >= -1); if (ShuffleIndex == -1) - return this->emitInvalid(E); // FIXME: Better diagnostic. + return this->emitInvalidShuffleVectorIndex(I, E); assert(ShuffleIndex < (NumInputElems * 2)); if (!this->emitGetLocal(PT_Ptr, @@ -4021,7 +4034,8 @@ unsigned Compiler::allocateLocalPrimitive(DeclTy &&Src, PrimType Ty, template std::optional -Compiler::allocateLocal(DeclTy &&Src, const ValueDecl *ExtendingDecl) { +Compiler::allocateLocal(DeclTy &&Src, QualType Ty, + const ValueDecl *ExtendingDecl) { // Make sure we don't accidentally register the same decl twice. if ([[maybe_unused]] const auto *VD = dyn_cast_if_present(Src.dyn_cast())) { @@ -4029,7 +4043,6 @@ Compiler::allocateLocal(DeclTy &&Src, const ValueDecl *ExtendingDecl) { assert(!Locals.contains(VD)); } - QualType Ty; const ValueDecl *Key = nullptr; const Expr *Init = nullptr; bool IsTemporary = false; @@ -4042,7 +4055,8 @@ Compiler::allocateLocal(DeclTy &&Src, const ValueDecl *ExtendingDecl) { } if (auto *E = Src.dyn_cast()) { IsTemporary = true; - Ty = E->getType(); + if (Ty.isNull()) + Ty = E->getType(); } Descriptor *D = P.createDescriptor( @@ -5997,6 +6011,9 @@ bool Compiler::visitDeclRef(const ValueDecl *D, const Expr *E) { return this->emitGetPtrParam(It->second.Offset, E); } + + if (D->getType()->isReferenceType()) + return false; // FIXME: Do we need to emit InvalidDeclRef? } // In case we need to re-visit a declaration. @@ -6033,9 +6050,7 @@ bool Compiler::visitDeclRef(const ValueDecl *D, const Expr *E) { const auto typeShouldBeVisited = [&](QualType T) -> bool { if (T.isConstant(Ctx.getASTContext())) return true; - if (const auto *RT = T->getAs()) - return RT->getPointeeType().isConstQualified(); - return false; + return T->isReferenceType(); }; // DecompositionDecls are just proxies for us. @@ -6051,9 +6066,12 @@ bool Compiler::visitDeclRef(const ValueDecl *D, const Expr *E) { // other words, we're evaluating the initializer, just to know if we can // evaluate the initializer. if (VD->isLocalVarDecl() && typeShouldBeVisited(VD->getType()) && - VD->getInit() && !VD->getInit()->isValueDependent() && - VD->evaluateValue()) - return revisit(VD); + VD->getInit() && !VD->getInit()->isValueDependent()) { + + if (VD->evaluateValue()) + return revisit(VD); + return this->emitInvalidDeclRef(cast(E), E); + } } } else { if (const auto *VD = dyn_cast(D); diff --git a/clang/lib/AST/ByteCode/Compiler.h b/clang/lib/AST/ByteCode/Compiler.h index 22e078f3fe546..4253e7b3248c9 100644 --- a/clang/lib/AST/ByteCode/Compiler.h +++ b/clang/lib/AST/ByteCode/Compiler.h @@ -302,7 +302,8 @@ class Compiler : public ConstStmtVisitor, bool>, /// Allocates a space storing a local given its type. std::optional - allocateLocal(DeclTy &&Decl, const ValueDecl *ExtendingDecl = nullptr); + allocateLocal(DeclTy &&Decl, QualType Ty = QualType(), + const ValueDecl *ExtendingDecl = nullptr); unsigned allocateTemporary(const Expr *E); private: diff --git a/clang/lib/AST/ByteCode/Descriptor.cpp b/clang/lib/AST/ByteCode/Descriptor.cpp index 65ac7a3129aba..5a8a2b64d5582 100644 --- a/clang/lib/AST/ByteCode/Descriptor.cpp +++ b/clang/lib/AST/ByteCode/Descriptor.cpp @@ -103,6 +103,7 @@ static void ctorArrayDesc(Block *B, std::byte *Ptr, bool IsConst, Desc->IsConst = IsConst || D->IsConst; Desc->IsFieldMutable = IsMutable || D->IsMutable; Desc->InUnion = InUnion; + Desc->IsArrayElement = true; if (auto Fn = D->ElemDesc->CtorFn) Fn(B, ElemLoc, Desc->IsConst, Desc->IsFieldMutable, IsActive, @@ -408,6 +409,8 @@ QualType Descriptor::getType() const { QualType Descriptor::getElemQualType() const { assert(isArray()); QualType T = getType(); + if (T->isPointerOrReferenceType()) + return T->getPointeeType(); if (const auto *AT = T->getAsArrayTypeUnsafe()) return AT->getElementType(); if (const auto *CT = T->getAs()) diff --git a/clang/lib/AST/ByteCode/Descriptor.h b/clang/lib/AST/ByteCode/Descriptor.h index 5460199e0e991..bcb635e157f00 100644 --- a/clang/lib/AST/ByteCode/Descriptor.h +++ b/clang/lib/AST/ByteCode/Descriptor.h @@ -96,12 +96,16 @@ struct InlineDescriptor { /// Flag indicating if the field is mutable (if in a record). LLVM_PREFERRED_TYPE(bool) unsigned IsFieldMutable : 1; + /// Flag indicating if the field is an element of a composite array. + LLVM_PREFERRED_TYPE(bool) + unsigned IsArrayElement : 1; const Descriptor *Desc; InlineDescriptor(const Descriptor *D) : Offset(sizeof(InlineDescriptor)), IsConst(false), IsInitialized(false), - IsBase(false), IsActive(false), IsFieldMutable(false), Desc(D) {} + IsBase(false), IsActive(false), IsFieldMutable(false), + IsArrayElement(false), Desc(D) {} void dump() const { dump(llvm::errs()); } void dump(llvm::raw_ostream &OS) const; diff --git a/clang/lib/AST/ByteCode/EvaluationResult.cpp b/clang/lib/AST/ByteCode/EvaluationResult.cpp index 627d4b2f65be9..c0d116cdf26c4 100644 --- a/clang/lib/AST/ByteCode/EvaluationResult.cpp +++ b/clang/lib/AST/ByteCode/EvaluationResult.cpp @@ -130,8 +130,9 @@ static bool CheckFieldsInitialized(InterpState &S, SourceLocation Loc, const Descriptor *Desc = BasePtr.getDeclDesc(); if (const auto *CD = dyn_cast_if_present(R->getDecl())) { const auto &BS = *std::next(CD->bases_begin(), I); - S.FFDiag(BS.getBaseTypeLoc(), diag::note_constexpr_uninitialized_base) - << B.Desc->getType() << BS.getSourceRange(); + SourceLocation TypeBeginLoc = BS.getBaseTypeLoc(); + S.FFDiag(TypeBeginLoc, diag::note_constexpr_uninitialized_base) + << B.Desc->getType() << SourceRange(TypeBeginLoc, BS.getEndLoc()); } else { S.FFDiag(Desc->getLocation(), diag::note_constexpr_uninitialized_base) << B.Desc->getType(); diff --git a/clang/lib/AST/ByteCode/Function.h b/clang/lib/AST/ByteCode/Function.h index 640bfa65644f0..7fe9aeb110120 100644 --- a/clang/lib/AST/ByteCode/Function.h +++ b/clang/lib/AST/ByteCode/Function.h @@ -222,6 +222,10 @@ class Function final { return ParamOffsets[ParamIndex]; } + PrimType getParamType(unsigned ParamIndex) const { + return ParamTypes[ParamIndex]; + } + private: /// Construct a function representing an actual function. Function(Program &P, FunctionDeclTy Source, unsigned ArgSize, diff --git a/clang/lib/AST/ByteCode/Interp.cpp b/clang/lib/AST/ByteCode/Interp.cpp index fd9a256843a0e..95715655cc9bb 100644 --- a/clang/lib/AST/ByteCode/Interp.cpp +++ b/clang/lib/AST/ByteCode/Interp.cpp @@ -21,6 +21,8 @@ #include "clang/AST/DeclObjC.h" #include "clang/AST/Expr.h" #include "clang/AST/ExprCXX.h" +#include "clang/Basic/DiagnosticSema.h" +#include "clang/Basic/TargetInfo.h" #include "llvm/ADT/APSInt.h" #include "llvm/ADT/StringExtras.h" #include @@ -962,7 +964,7 @@ static bool runRecordDestructor(InterpState &S, CodePtr OpPC, return true; } -bool RunDestructors(InterpState &S, CodePtr OpPC, const Block *B) { +static bool RunDestructors(InterpState &S, CodePtr OpPC, const Block *B) { assert(B); const Descriptor *Desc = B->getDescriptor(); @@ -987,6 +989,89 @@ bool RunDestructors(InterpState &S, CodePtr OpPC, const Block *B) { return runRecordDestructor(S, OpPC, Pointer(const_cast(B)), Desc); } +bool Free(InterpState &S, CodePtr OpPC, bool DeleteIsArrayForm, + bool IsGlobalDelete) { + if (!CheckDynamicMemoryAllocation(S, OpPC)) + return false; + + const Expr *Source = nullptr; + const Block *BlockToDelete = nullptr; + { + // Extra scope for this so the block doesn't have this pointer + // pointing to it when we destroy it. + Pointer Ptr = S.Stk.pop(); + + // Deleteing nullptr is always fine. + if (Ptr.isZero()) + return true; + + // Remove base casts. + while (Ptr.isBaseClass()) + Ptr = Ptr.getBase(); + + if (!Ptr.isRoot() || Ptr.isOnePastEnd() || Ptr.isArrayElement()) { + const SourceInfo &Loc = S.Current->getSource(OpPC); + S.FFDiag(Loc, diag::note_constexpr_delete_subobject) + << Ptr.toDiagnosticString(S.getASTContext()) << Ptr.isOnePastEnd(); + return false; + } + + Source = Ptr.getDeclDesc()->asExpr(); + BlockToDelete = Ptr.block(); + + if (!CheckDeleteSource(S, OpPC, Source, Ptr)) + return false; + + // For a class type with a virtual destructor, the selected operator delete + // is the one looked up when building the destructor. + QualType AllocType = Ptr.getType(); + if (!DeleteIsArrayForm && !IsGlobalDelete) { + auto getVirtualOperatorDelete = [](QualType T) -> const FunctionDecl * { + if (const CXXRecordDecl *RD = T->getAsCXXRecordDecl()) + if (const CXXDestructorDecl *DD = RD->getDestructor()) + return DD->isVirtual() ? DD->getOperatorDelete() : nullptr; + return nullptr; + }; + + AllocType->dump(); + if (const FunctionDecl *VirtualDelete = + getVirtualOperatorDelete(AllocType); + VirtualDelete && + !VirtualDelete->isReplaceableGlobalAllocationFunction()) { + S.FFDiag(S.Current->getSource(OpPC), + diag::note_constexpr_new_non_replaceable) + << isa(VirtualDelete) << VirtualDelete; + return false; + } + } + } + assert(Source); + assert(BlockToDelete); + + // Invoke destructors before deallocating the memory. + if (!RunDestructors(S, OpPC, BlockToDelete)) + return false; + + DynamicAllocator &Allocator = S.getAllocator(); + const Descriptor *BlockDesc = BlockToDelete->getDescriptor(); + std::optional AllocForm = + Allocator.getAllocationForm(Source); + + if (!Allocator.deallocate(Source, BlockToDelete, S)) { + // Nothing has been deallocated, this must be a double-delete. + const SourceInfo &Loc = S.Current->getSource(OpPC); + S.FFDiag(Loc, diag::note_constexpr_double_delete); + return false; + } + + assert(AllocForm); + DynamicAllocator::Form DeleteForm = DeleteIsArrayForm + ? DynamicAllocator::Form::Array + : DynamicAllocator::Form::NonArray; + return CheckNewDeleteForms(S, OpPC, *AllocForm, DeleteForm, BlockDesc, + Source); +} + void diagnoseEnumValue(InterpState &S, CodePtr OpPC, const EnumDecl *ED, const APSInt &Value) { llvm::APInt Min; @@ -1406,6 +1491,58 @@ bool handleFixedPointOverflow(InterpState &S, CodePtr OpPC, return S.noteUndefinedBehavior(); } +bool InvalidShuffleVectorIndex(InterpState &S, CodePtr OpPC, uint32_t Index) { + const SourceInfo &Loc = S.Current->getSource(OpPC); + S.FFDiag(Loc, + diag::err_shufflevector_minus_one_is_undefined_behavior_constexpr) + << Index; + return false; +} + +bool CheckPointerToIntegralCast(InterpState &S, CodePtr OpPC, + const Pointer &Ptr, unsigned BitWidth) { + if (Ptr.isDummy()) + return false; + + const SourceInfo &E = S.Current->getSource(OpPC); + S.CCEDiag(E, diag::note_constexpr_invalid_cast) + << 2 << S.getLangOpts().CPlusPlus << S.Current->getRange(OpPC); + + if (Ptr.isBlockPointer() && !Ptr.isZero()) { + // Only allow based lvalue casts if they are lossless. + if (S.getASTContext().getTargetInfo().getPointerWidth(LangAS::Default) != + BitWidth) + return Invalid(S, OpPC); + } + return true; +} + +bool CastPointerIntegralAP(InterpState &S, CodePtr OpPC, uint32_t BitWidth) { + const Pointer &Ptr = S.Stk.pop(); + + if (!CheckPointerToIntegralCast(S, OpPC, Ptr, BitWidth)) + return false; + + S.Stk.push>( + IntegralAP::from(Ptr.getIntegerRepresentation(), BitWidth)); + return true; +} + +bool CastPointerIntegralAPS(InterpState &S, CodePtr OpPC, uint32_t BitWidth) { + const Pointer &Ptr = S.Stk.pop(); + + if (!CheckPointerToIntegralCast(S, OpPC, Ptr, BitWidth)) + return false; + + S.Stk.push>( + IntegralAP::from(Ptr.getIntegerRepresentation(), BitWidth)); + return true; +} + +// https://github.com/llvm/llvm-project/issues/102513 +#if defined(_WIN32) && !defined(__clang__) && !defined(NDEBUG) +#pragma optimize("", off) +#endif bool Interpret(InterpState &S, APValue &Result) { // The current stack frame when we started Interpret(). // This is being used by the ops to determine wheter @@ -1430,6 +1567,10 @@ bool Interpret(InterpState &S, APValue &Result) { } } } +// https://github.com/llvm/llvm-project/issues/102513 +#if defined(_WIN32) && !defined(__clang__) && !defined(NDEBUG) +#pragma optimize("", on) +#endif } // namespace interp } // namespace clang diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h index 5c3ee5e689f1c..dece95971b761 100644 --- a/clang/lib/AST/ByteCode/Interp.h +++ b/clang/lib/AST/ByteCode/Interp.h @@ -161,6 +161,7 @@ bool CallBI(InterpState &S, CodePtr OpPC, const Function *Func, bool CallPtr(InterpState &S, CodePtr OpPC, uint32_t ArgSize, const CallExpr *CE); bool CheckLiteralType(InterpState &S, CodePtr OpPC, const Type *T); +bool InvalidShuffleVectorIndex(InterpState &S, CodePtr OpPC, uint32_t Index); template static bool handleOverflow(InterpState &S, CodePtr OpPC, const T &SrcValue) { @@ -2288,53 +2289,22 @@ static inline bool CastFloatingIntegralAPS(InterpState &S, CodePtr OpPC, return CheckFloatResult(S, OpPC, F, Status, FPO); } +bool CheckPointerToIntegralCast(InterpState &S, CodePtr OpPC, + const Pointer &Ptr, unsigned BitWidth); +bool CastPointerIntegralAP(InterpState &S, CodePtr OpPC, uint32_t BitWidth); +bool CastPointerIntegralAPS(InterpState &S, CodePtr OpPC, uint32_t BitWidth); + template ::T> bool CastPointerIntegral(InterpState &S, CodePtr OpPC) { const Pointer &Ptr = S.Stk.pop(); - if (Ptr.isDummy()) + if (!CheckPointerToIntegralCast(S, OpPC, Ptr, T::bitWidth())) return false; - const SourceInfo &E = S.Current->getSource(OpPC); - S.CCEDiag(E, diag::note_constexpr_invalid_cast) - << 2 << S.getLangOpts().CPlusPlus << S.Current->getRange(OpPC); - S.Stk.push(T::from(Ptr.getIntegerRepresentation())); return true; } -static inline bool CastPointerIntegralAP(InterpState &S, CodePtr OpPC, - uint32_t BitWidth) { - const Pointer &Ptr = S.Stk.pop(); - - if (Ptr.isDummy()) - return false; - - const SourceInfo &E = S.Current->getSource(OpPC); - S.CCEDiag(E, diag::note_constexpr_invalid_cast) - << 2 << S.getLangOpts().CPlusPlus << S.Current->getRange(OpPC); - - S.Stk.push>( - IntegralAP::from(Ptr.getIntegerRepresentation(), BitWidth)); - return true; -} - -static inline bool CastPointerIntegralAPS(InterpState &S, CodePtr OpPC, - uint32_t BitWidth) { - const Pointer &Ptr = S.Stk.pop(); - - if (Ptr.isDummy()) - return false; - - const SourceInfo &E = S.Current->getSource(OpPC); - S.CCEDiag(E, diag::note_constexpr_invalid_cast) - << 2 << S.getLangOpts().CPlusPlus << S.Current->getRange(OpPC); - - S.Stk.push>( - IntegralAP::from(Ptr.getIntegerRepresentation(), BitWidth)); - return true; -} - template ::T> static inline bool CastIntegralFixedPoint(InterpState &S, CodePtr OpPC, uint32_t FPS) { @@ -3006,61 +2976,8 @@ inline bool AllocCN(InterpState &S, CodePtr OpPC, const Descriptor *ElementDesc, return true; } -bool RunDestructors(InterpState &S, CodePtr OpPC, const Block *B); -static inline bool Free(InterpState &S, CodePtr OpPC, bool DeleteIsArrayForm) { - if (!CheckDynamicMemoryAllocation(S, OpPC)) - return false; - - const Expr *Source = nullptr; - const Block *BlockToDelete = nullptr; - { - // Extra scope for this so the block doesn't have this pointer - // pointing to it when we destroy it. - const Pointer &Ptr = S.Stk.pop(); - - // Deleteing nullptr is always fine. - if (Ptr.isZero()) - return true; - - if (!Ptr.isRoot() || Ptr.isOnePastEnd() || Ptr.isArrayElement()) { - const SourceInfo &Loc = S.Current->getSource(OpPC); - S.FFDiag(Loc, diag::note_constexpr_delete_subobject) - << Ptr.toDiagnosticString(S.getASTContext()) << Ptr.isOnePastEnd(); - return false; - } - - Source = Ptr.getDeclDesc()->asExpr(); - BlockToDelete = Ptr.block(); - - if (!CheckDeleteSource(S, OpPC, Source, Ptr)) - return false; - } - assert(Source); - assert(BlockToDelete); - - // Invoke destructors before deallocating the memory. - if (!RunDestructors(S, OpPC, BlockToDelete)) - return false; - - DynamicAllocator &Allocator = S.getAllocator(); - const Descriptor *BlockDesc = BlockToDelete->getDescriptor(); - std::optional AllocForm = - Allocator.getAllocationForm(Source); - - if (!Allocator.deallocate(Source, BlockToDelete, S)) { - // Nothing has been deallocated, this must be a double-delete. - const SourceInfo &Loc = S.Current->getSource(OpPC); - S.FFDiag(Loc, diag::note_constexpr_double_delete); - return false; - } - - assert(AllocForm); - DynamicAllocator::Form DeleteForm = DeleteIsArrayForm - ? DynamicAllocator::Form::Array - : DynamicAllocator::Form::NonArray; - return CheckNewDeleteForms(S, OpPC, *AllocForm, DeleteForm, BlockDesc, - Source); -} +bool Free(InterpState &S, CodePtr OpPC, bool DeleteIsArrayForm, + bool IsGlobalDelete); static inline bool IsConstantContext(InterpState &S, CodePtr OpPC) { S.Stk.push(Boolean::from(S.inConstantContext())); diff --git a/clang/lib/AST/ByteCode/InterpBlock.cpp b/clang/lib/AST/ByteCode/InterpBlock.cpp index 0ce88ca7e5236..9ef44cd29ff87 100644 --- a/clang/lib/AST/ByteCode/InterpBlock.cpp +++ b/clang/lib/AST/ByteCode/InterpBlock.cpp @@ -100,8 +100,8 @@ bool Block::hasPointer(const Pointer *P) const { #endif DeadBlock::DeadBlock(DeadBlock *&Root, Block *Blk) - : Root(Root), - B(~0u, Blk->Desc, Blk->IsStatic, Blk->IsExtern, /*isDead=*/true) { + : Root(Root), B(~0u, Blk->Desc, Blk->IsStatic, Blk->IsExtern, Blk->IsWeak, + /*isDead=*/true) { // Add the block to the chain of dead blocks. if (Root) Root->Prev = this; diff --git a/clang/lib/AST/ByteCode/InterpBlock.h b/clang/lib/AST/ByteCode/InterpBlock.h index a5cd58e3a655a..985e4c152191c 100644 --- a/clang/lib/AST/ByteCode/InterpBlock.h +++ b/clang/lib/AST/ByteCode/InterpBlock.h @@ -50,16 +50,17 @@ class Block final { public: /// Creates a new block. Block(unsigned EvalID, const std::optional &DeclID, - const Descriptor *Desc, bool IsStatic = false, bool IsExtern = false) + const Descriptor *Desc, bool IsStatic = false, bool IsExtern = false, + bool IsWeak = false) : EvalID(EvalID), DeclID(DeclID), IsStatic(IsStatic), IsExtern(IsExtern), - IsDynamic(false), Desc(Desc) { + IsDynamic(false), IsWeak(IsWeak), Desc(Desc) { assert(Desc); } Block(unsigned EvalID, const Descriptor *Desc, bool IsStatic = false, - bool IsExtern = false) + bool IsExtern = false, bool IsWeak = false) : EvalID(EvalID), DeclID((unsigned)-1), IsStatic(IsStatic), - IsExtern(IsExtern), IsDynamic(false), Desc(Desc) { + IsExtern(IsExtern), IsDynamic(false), IsWeak(IsWeak), Desc(Desc) { assert(Desc); } @@ -73,6 +74,7 @@ class Block final { bool isStatic() const { return IsStatic; } /// Checks if the block is temporary. bool isTemporary() const { return Desc->IsTemporary; } + bool isWeak() const { return IsWeak; } bool isDynamic() const { return IsDynamic; } /// Returns the size of the block. unsigned getSize() const { return Desc->getAllocSize(); } @@ -135,9 +137,9 @@ class Block final { friend class DynamicAllocator; Block(unsigned EvalID, const Descriptor *Desc, bool IsExtern, bool IsStatic, - bool IsDead) + bool IsWeak, bool IsDead) : EvalID(EvalID), IsStatic(IsStatic), IsExtern(IsExtern), IsDead(true), - IsDynamic(false), Desc(Desc) { + IsDynamic(false), IsWeak(IsWeak), Desc(Desc) { assert(Desc); } @@ -170,6 +172,7 @@ class Block final { /// Flag indicating if this block has been allocated via dynamic /// memory allocation (e.g. malloc). bool IsDynamic = false; + bool IsWeak = false; /// Pointer to the stack slot descriptor. const Descriptor *Desc; }; diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp b/clang/lib/AST/ByteCode/InterpBuiltin.cpp index 72c94e6fad3e0..ec27aebf84bd8 100644 --- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp +++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp @@ -38,6 +38,14 @@ static T getParam(const InterpFrame *Frame, unsigned Index) { return Frame->getParam(Offset); } +static APSInt getAPSIntParam(const InterpFrame *Frame, unsigned Index) { + APSInt R; + unsigned Offset = Frame->getFunction()->getParamOffset(Index); + INT_TYPE_SWITCH(Frame->getFunction()->getParamType(Index), + R = Frame->getParam(Offset).toAPSInt()); + return R; +} + PrimType getIntPrimType(const InterpState &S) { const TargetInfo &TI = S.getASTContext().getTargetInfo(); unsigned IntWidth = TI.getIntWidth(); @@ -1153,6 +1161,71 @@ static bool interp__builtin_is_aligned_up_down(InterpState &S, CodePtr OpPC, return false; } +/// __builtin_assume_aligned(Ptr, Alignment[, ExtraOffset]) +static bool interp__builtin_assume_aligned(InterpState &S, CodePtr OpPC, + const InterpFrame *Frame, + const Function *Func, + const CallExpr *Call) { + assert(Call->getNumArgs() == 2 || Call->getNumArgs() == 3); + + // Might be called with function pointers in C. + std::optional PtrT = S.Ctx.classify(Call->getArg(0)); + if (PtrT != PT_Ptr) + return false; + + unsigned ArgSize = callArgSize(S, Call); + const Pointer &Ptr = S.Stk.peek(ArgSize); + std::optional ExtraOffset; + APSInt Alignment; + if (Call->getNumArgs() == 2) { + Alignment = peekToAPSInt(S.Stk, *S.Ctx.classify(Call->getArg(1))); + } else { + PrimType AlignmentT = *S.Ctx.classify(Call->getArg(1)); + PrimType ExtraOffsetT = *S.Ctx.classify(Call->getArg(2)); + Alignment = peekToAPSInt(S.Stk, *S.Ctx.classify(Call->getArg(1)), + align(primSize(AlignmentT)) + + align(primSize(ExtraOffsetT))); + ExtraOffset = peekToAPSInt(S.Stk, *S.Ctx.classify(Call->getArg(2))); + } + + CharUnits Align = CharUnits::fromQuantity(Alignment.getZExtValue()); + + // If there is a base object, then it must have the correct alignment. + if (Ptr.isBlockPointer()) { + CharUnits BaseAlignment; + if (const auto *VD = Ptr.getDeclDesc()->asValueDecl()) + BaseAlignment = S.getASTContext().getDeclAlign(VD); + else if (const auto *E = Ptr.getDeclDesc()->asExpr()) + BaseAlignment = GetAlignOfExpr(S.getASTContext(), E, UETT_AlignOf); + + if (BaseAlignment < Align) { + S.CCEDiag(Call->getArg(0), + diag::note_constexpr_baa_insufficient_alignment) + << 0 << BaseAlignment.getQuantity() << Align.getQuantity(); + return false; + } + } + + APValue AV = Ptr.toAPValue(S.getASTContext()); + CharUnits AVOffset = AV.getLValueOffset(); + if (ExtraOffset) + AVOffset -= CharUnits::fromQuantity(ExtraOffset->getZExtValue()); + if (AVOffset.alignTo(Align) != AVOffset) { + if (Ptr.isBlockPointer()) + S.CCEDiag(Call->getArg(0), + diag::note_constexpr_baa_insufficient_alignment) + << 1 << AVOffset.getQuantity() << Align.getQuantity(); + else + S.CCEDiag(Call->getArg(0), + diag::note_constexpr_baa_value_insufficient_alignment) + << AVOffset.getQuantity() << Align.getQuantity(); + return false; + } + + S.Stk.push(Ptr); + return true; +} + static bool interp__builtin_ia32_bextr(InterpState &S, CodePtr OpPC, const InterpFrame *Frame, const Function *Func, @@ -1184,6 +1257,10 @@ static bool interp__builtin_ia32_bzhi(InterpState &S, CodePtr OpPC, const InterpFrame *Frame, const Function *Func, const CallExpr *Call) { + QualType CallType = Call->getType(); + if (!CallType->isIntegerType()) + return false; + PrimType ValT = *S.Ctx.classify(Call->getArg(0)); PrimType IndexT = *S.Ctx.classify(Call->getArg(1)); @@ -1197,7 +1274,7 @@ static bool interp__builtin_ia32_bzhi(InterpState &S, CodePtr OpPC, if (Index < BitWidth) Val.clearHighBits(BitWidth - Index); - pushInteger(S, Val, Call->getType()); + pushInteger(S, Val, CallType); return true; } @@ -1210,7 +1287,7 @@ static bool interp__builtin_ia32_lzcnt(InterpState &S, CodePtr OpPC, return false; APSInt Val = peekToAPSInt(S.Stk, *S.Ctx.classify(Call->getArg(0))); - pushInteger(S, Val.countLeadingZeros(), Call->getType()); + pushInteger(S, Val.countLeadingZeros(), CallType); return true; } @@ -1223,7 +1300,7 @@ static bool interp__builtin_ia32_tzcnt(InterpState &S, CodePtr OpPC, return false; APSInt Val = peekToAPSInt(S.Stk, *S.Ctx.classify(Call->getArg(0))); - pushInteger(S, Val.countTrailingZeros(), Call->getType()); + pushInteger(S, Val.countTrailingZeros(), CallType); return true; } @@ -1269,6 +1346,44 @@ static bool interp__builtin_ia32_pext(InterpState &S, CodePtr OpPC, return true; } +static bool interp__builtin_ia32_addcarry_subborrow(InterpState &S, + CodePtr OpPC, + const InterpFrame *Frame, + const Function *Func, + const CallExpr *Call) { + if (Call->getNumArgs() != 4 || !Call->getArg(0)->getType()->isIntegerType() || + !Call->getArg(1)->getType()->isIntegerType() || + !Call->getArg(2)->getType()->isIntegerType()) + return false; + + unsigned BuiltinOp = Func->getBuiltinID(); + APSInt CarryIn = getAPSIntParam(Frame, 0); + APSInt LHS = getAPSIntParam(Frame, 1); + APSInt RHS = getAPSIntParam(Frame, 2); + + bool IsAdd = BuiltinOp == clang::X86::BI__builtin_ia32_addcarryx_u32 || + BuiltinOp == clang::X86::BI__builtin_ia32_addcarryx_u64; + + unsigned BitWidth = LHS.getBitWidth(); + unsigned CarryInBit = CarryIn.ugt(0) ? 1 : 0; + APInt ExResult = + IsAdd ? (LHS.zext(BitWidth + 1) + (RHS.zext(BitWidth + 1) + CarryInBit)) + : (LHS.zext(BitWidth + 1) - (RHS.zext(BitWidth + 1) + CarryInBit)); + + APInt Result = ExResult.extractBits(BitWidth, 0); + APSInt CarryOut = + APSInt(ExResult.extractBits(1, BitWidth), /*IsUnsigned=*/true); + + Pointer &CarryOutPtr = S.Stk.peek(); + QualType CarryOutType = Call->getArg(3)->getType()->getPointeeType(); + PrimType CarryOutT = *S.getContext().classify(CarryOutType); + assignInteger(CarryOutPtr, CarryOutT, APSInt(Result, true)); + + pushInteger(S, CarryOut, Call->getType()); + + return true; +} + static bool interp__builtin_os_log_format_buffer_size(InterpState &S, CodePtr OpPC, const InterpFrame *Frame, @@ -1854,6 +1969,11 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F, return false; break; + case Builtin::BI__builtin_assume_aligned: + if (!interp__builtin_assume_aligned(S, OpPC, Frame, F, Call)) + return false; + break; + case clang::X86::BI__builtin_ia32_bextr_u32: case clang::X86::BI__builtin_ia32_bextr_u64: case clang::X86::BI__builtin_ia32_bextri_u32: @@ -1894,6 +2014,14 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F, return false; break; + case clang::X86::BI__builtin_ia32_addcarryx_u32: + case clang::X86::BI__builtin_ia32_addcarryx_u64: + case clang::X86::BI__builtin_ia32_subborrow_u32: + case clang::X86::BI__builtin_ia32_subborrow_u64: + if (!interp__builtin_ia32_addcarry_subborrow(S, OpPC, Frame, F, Call)) + return false; + break; + case Builtin::BI__builtin_os_log_format_buffer_size: if (!interp__builtin_os_log_format_buffer_size(S, OpPC, Frame, F, Call)) return false; diff --git a/clang/lib/AST/ByteCode/Opcodes.td b/clang/lib/AST/ByteCode/Opcodes.td index 61b6f2e8daa2f..4fa9b6d61d5ab 100644 --- a/clang/lib/AST/ByteCode/Opcodes.td +++ b/clang/lib/AST/ByteCode/Opcodes.td @@ -773,6 +773,9 @@ def InvalidDeclRef : Opcode { } def SizelessVectorElementSize : Opcode; +def InvalidShuffleVectorIndex : Opcode { + let Args = [ArgUint32]; +} def Assume : Opcode; @@ -815,7 +818,7 @@ def AllocCN : Opcode { } def Free : Opcode { - let Args = [ArgBool]; + let Args = [ArgBool, ArgBool]; } def CheckNewTypeMismatch : Opcode { diff --git a/clang/lib/AST/ByteCode/Pointer.cpp b/clang/lib/AST/ByteCode/Pointer.cpp index 387cad9b137c0..75b00dcb2ab24 100644 --- a/clang/lib/AST/ByteCode/Pointer.cpp +++ b/clang/lib/AST/ByteCode/Pointer.cpp @@ -204,18 +204,22 @@ APValue Pointer::toAPValue(const ASTContext &ASTCtx) const { Path.push_back(APValue::LValuePathEntry( {Ptr.getFieldDesc()->asDecl(), /*IsVirtual=*/false})); - if (const auto *FD = dyn_cast(Ptr.getFieldDesc()->asDecl())) + if (const auto *FD = + dyn_cast_if_present(Ptr.getFieldDesc()->asDecl())) Offset += getFieldOffset(FD); Ptr = Ptr.getBase(); } else if (Ptr.isArrayElement()) { + Ptr = Ptr.expand(); unsigned Index; if (Ptr.isOnePastEnd()) Index = Ptr.getArray().getNumElems(); else Index = Ptr.getIndex(); - Offset += (Index * ASTCtx.getTypeSizeInChars(Ptr.getType())); + QualType ElemType = Ptr.getFieldDesc()->getElemQualType(); + Offset += (Index * ASTCtx.getTypeSizeInChars(ElemType)); + Path.push_back(APValue::LValuePathEntry::ArrayIndex(Index)); Ptr = Ptr.getArray(); } else { @@ -249,11 +253,6 @@ APValue Pointer::toAPValue(const ASTContext &ASTCtx) const { } } - // FIXME(perf): We compute the lvalue path above, but we can't supply it - // for dummy pointers (that causes crashes later in CheckConstantExpression). - if (isDummy()) - Path.clear(); - // We assemble the LValuePath starting from the innermost pointer to the // outermost one. SO in a.b.c, the first element in Path will refer to // the field 'c', while later code expects it to refer to 'a'. diff --git a/clang/lib/AST/ByteCode/Pointer.h b/clang/lib/AST/ByteCode/Pointer.h index ac9b9ed4091b6..72e255dba13f6 100644 --- a/clang/lib/AST/ByteCode/Pointer.h +++ b/clang/lib/AST/ByteCode/Pointer.h @@ -420,8 +420,18 @@ class Pointer { } /// Checks if the pointer points to an array. bool isArrayElement() const { - if (isBlockPointer()) - return inArray() && asBlockPointer().Base != Offset; + if (!isBlockPointer()) + return false; + + const BlockPointer &BP = asBlockPointer(); + if (inArray() && BP.Base != Offset) + return true; + + // Might be a narrow()'ed element in a composite array. + // Check the inline descriptor. + if (BP.Base >= sizeof(InlineDescriptor) && getInlineDesc()->IsArrayElement) + return true; + return false; } /// Pointer points directly to a block. @@ -514,9 +524,7 @@ class Pointer { return false; assert(isBlockPointer()); - if (const ValueDecl *VD = getDeclDesc()->asValueDecl()) - return VD->isWeak(); - return false; + return asBlockPointer().Pointee->isWeak(); } /// Checks if an object was initialized. bool isInitialized() const; diff --git a/clang/lib/AST/ByteCode/Program.cpp b/clang/lib/AST/ByteCode/Program.cpp index 79c645257306e..cd2665f755d7c 100644 --- a/clang/lib/AST/ByteCode/Program.cpp +++ b/clang/lib/AST/ByteCode/Program.cpp @@ -33,7 +33,7 @@ const void *Program::getNativePointer(unsigned Idx) { return NativePointers[Idx]; } -unsigned Program::createGlobalString(const StringLiteral *S) { +unsigned Program::createGlobalString(const StringLiteral *S, const Expr *Base) { const size_t CharWidth = S->getCharByteWidth(); const size_t BitWidth = CharWidth * Ctx.getCharBit(); @@ -52,12 +52,15 @@ unsigned Program::createGlobalString(const StringLiteral *S) { llvm_unreachable("unsupported character width"); } + if (!Base) + Base = S; + // Create a descriptor for the string. - Descriptor *Desc = - allocateDescriptor(S, CharType, Descriptor::GlobalMD, S->getLength() + 1, - /*isConst=*/true, - /*isTemporary=*/false, - /*isMutable=*/false); + Descriptor *Desc = allocateDescriptor(Base, CharType, Descriptor::GlobalMD, + S->getLength() + 1, + /*isConst=*/true, + /*isTemporary=*/false, + /*isMutable=*/false); // Allocate storage for the string. // The byte length does not include the null terminator. @@ -152,10 +155,12 @@ std::optional Program::getOrCreateDummy(const DeclTy &D) { return It->second; QualType QT; + bool IsWeak = false; if (const auto *E = D.dyn_cast()) { QT = E->getType(); } else { const ValueDecl *VD = cast(D.get()); + IsWeak = VD->isWeak(); QT = VD->getType(); if (const auto *RT = QT->getAs()) QT = RT->getPointeeType(); @@ -182,7 +187,7 @@ std::optional Program::getOrCreateDummy(const DeclTy &D) { auto *G = new (Allocator, Desc->getAllocSize()) Global(Ctx.getEvalID(), getCurrentDecl(), Desc, /*IsStatic=*/true, - /*IsExtern=*/false); + /*IsExtern=*/false, IsWeak); G->block()->invokeCtor(); Globals.push_back(G); @@ -193,6 +198,7 @@ std::optional Program::getOrCreateDummy(const DeclTy &D) { std::optional Program::createGlobal(const ValueDecl *VD, const Expr *Init) { bool IsStatic, IsExtern; + bool IsWeak = VD->isWeak(); if (const auto *Var = dyn_cast(VD)) { IsStatic = Context::shouldBeGloballyIndexed(VD); IsExtern = Var->hasExternalStorage(); @@ -207,7 +213,8 @@ std::optional Program::createGlobal(const ValueDecl *VD, // Register all previous declarations as well. For extern blocks, just replace // the index with the new variable. - if (auto Idx = createGlobal(VD, VD->getType(), IsStatic, IsExtern, Init)) { + if (auto Idx = + createGlobal(VD, VD->getType(), IsStatic, IsExtern, IsWeak, Init)) { for (const Decl *P = VD; P; P = P->getPreviousDecl()) { if (P != VD) { unsigned PIdx = GlobalIndices[P]; @@ -225,7 +232,7 @@ std::optional Program::createGlobal(const Expr *E) { if (auto Idx = getGlobal(E)) return Idx; if (auto Idx = createGlobal(E, E->getType(), /*isStatic=*/true, - /*isExtern=*/false)) { + /*isExtern=*/false, /*IsWeak=*/false)) { GlobalIndices[E] = *Idx; return *Idx; } @@ -234,7 +241,7 @@ std::optional Program::createGlobal(const Expr *E) { std::optional Program::createGlobal(const DeclTy &D, QualType Ty, bool IsStatic, bool IsExtern, - const Expr *Init) { + bool IsWeak, const Expr *Init) { // Create a descriptor for the global. Descriptor *Desc; const bool IsConst = Ty.isConstQualified(); @@ -251,8 +258,8 @@ std::optional Program::createGlobal(const DeclTy &D, QualType Ty, // Allocate a block for storage. unsigned I = Globals.size(); - auto *G = new (Allocator, Desc->getAllocSize()) - Global(Ctx.getEvalID(), getCurrentDecl(), Desc, IsStatic, IsExtern); + auto *G = new (Allocator, Desc->getAllocSize()) Global( + Ctx.getEvalID(), getCurrentDecl(), Desc, IsStatic, IsExtern, IsWeak); G->block()->invokeCtor(); // Initialize InlineDescriptor fields. @@ -280,15 +287,13 @@ Record *Program::getOrCreateRecord(const RecordDecl *RD) { if (!RD->isCompleteDefinition()) return nullptr; - // Deduplicate records. - if (auto It = Records.find(RD); It != Records.end()) + // Return an existing record if available. Otherwise, we insert nullptr now + // and replace that later, so recursive calls to this function with the same + // RecordDecl don't run into infinite recursion. + auto [It, Inserted] = Records.try_emplace(RD); + if (!Inserted) return It->second; - // We insert nullptr now and replace that later, so recursive calls - // to this function with the same RecordDecl don't run into - // infinite recursion. - Records.insert({RD, nullptr}); - // Number of bytes required by fields and base classes. unsigned BaseSize = 0; // Number of bytes required by virtual base. diff --git a/clang/lib/AST/ByteCode/Program.h b/clang/lib/AST/ByteCode/Program.h index bd2672a762b82..f676672fb7ced 100644 --- a/clang/lib/AST/ByteCode/Program.h +++ b/clang/lib/AST/ByteCode/Program.h @@ -64,7 +64,8 @@ class Program final { const void *getNativePointer(unsigned Idx); /// Emits a string literal among global data. - unsigned createGlobalString(const StringLiteral *S); + unsigned createGlobalString(const StringLiteral *S, + const Expr *Base = nullptr); /// Returns a pointer to a global. Pointer getPtrGlobal(unsigned Idx) const; @@ -152,7 +153,7 @@ class Program final { std::optional createGlobal(const DeclTy &D, QualType Ty, bool IsStatic, bool IsExtern, - const Expr *Init = nullptr); + bool IsWeak, const Expr *Init = nullptr); /// Reference to the VM context. Context &Ctx; diff --git a/clang/lib/AST/ByteCode/State.h b/clang/lib/AST/ByteCode/State.h index 9a81fa6b7d220..c9ca67e234e5d 100644 --- a/clang/lib/AST/ByteCode/State.h +++ b/clang/lib/AST/ByteCode/State.h @@ -127,6 +127,9 @@ class State { /// constant value. bool InConstantContext = false; + // Whether we're allowed to evaluate contracts + bool EvaluateContracts = true; + private: void addCallStack(unsigned Limit); diff --git a/clang/lib/AST/CXXInheritance.cpp b/clang/lib/AST/CXXInheritance.cpp index 25de2a20a7f3b..eb265a872c125 100644 --- a/clang/lib/AST/CXXInheritance.cpp +++ b/clang/lib/AST/CXXInheritance.cpp @@ -259,12 +259,10 @@ bool CXXBasePaths::lookupInBases(ASTContext &Context, BaseRecord = TD->getTemplatedDecl(); } if (BaseRecord) { - if (!BaseRecord->hasDefinition() || - VisitedDependentRecords.count(BaseRecord)) { + if (!BaseRecord->hasDefinition()) + BaseRecord = nullptr; + else if (!VisitedDependentRecords.insert(BaseRecord).second) BaseRecord = nullptr; - } else { - VisitedDependentRecords.insert(BaseRecord); - } } } else { BaseRecord = cast( diff --git a/clang/lib/AST/ComputeDependence.cpp b/clang/lib/AST/ComputeDependence.cpp index 6ef49790481ac..e37ebec085195 100644 --- a/clang/lib/AST/ComputeDependence.cpp +++ b/clang/lib/AST/ComputeDependence.cpp @@ -252,10 +252,13 @@ ExprDependence clang::computeDependence(ExtVectorElementExpr *E) { return E->getBase()->getDependence(); } -ExprDependence clang::computeDependence(BlockExpr *E) { +ExprDependence clang::computeDependence(BlockExpr *E, + bool ContainsUnexpandedParameterPack) { auto D = toExprDependenceForImpliedType(E->getType()->getDependence()); if (E->getBlockDecl()->isDependentContext()) D |= ExprDependence::Instantiation; + if (ContainsUnexpandedParameterPack) + D |= ExprDependence::UnexpandedPack; return D; } @@ -958,3 +961,9 @@ ExprDependence clang::computeDependence(ObjCMessageExpr *E) { D |= A->getDependence(); return D; } + +ExprDependence clang::computeDependence(OpenACCAsteriskSizeExpr *E) { + // This represents a simple asterisk as typed, so cannot be dependent in any + // way. + return ExprDependence::None; +} diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index 84ef9f74582ef..8539e872fd4c0 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -71,6 +71,7 @@ #include #include #include +#include "clang/Basic/EricWFDebug.h" using namespace clang; @@ -1737,9 +1738,17 @@ void NamedDecl::printNestedNameSpecifier(raw_ostream &OS, continue; // Suppress inline namespace if it doesn't make the result ambiguous. - if (P.SuppressInlineNamespace && Ctx->isInlineNamespace() && NameInScope && - cast(Ctx)->isRedundantInlineQualifierFor(NameInScope)) - continue; + if (Ctx->isInlineNamespace() && NameInScope) { + bool isRedundant = + cast(Ctx)->isRedundantInlineQualifierFor(NameInScope); + if (P.SuppressInlineNamespace == + PrintingPolicy::SuppressInlineNamespaceMode::All || + (P.SuppressInlineNamespace == + PrintingPolicy::SuppressInlineNamespaceMode::Redundant && + isRedundant)) { + continue; + } + } // Skip non-named contexts such as linkage specifications and ExportDecls. const NamedDecl *ND = dyn_cast(Ctx); @@ -2539,11 +2548,12 @@ EvaluatedStmt *VarDecl::getEvaluatedStmt() const { APValue *VarDecl::evaluateValue() const { SmallVector Notes; - return evaluateValueImpl(Notes, hasConstantInitialization()); + return evaluateValueImpl(Notes, hasConstantInitialization(), true); } APValue *VarDecl::evaluateValueImpl(SmallVectorImpl &Notes, - bool IsConstantInitialization) const { + bool IsConstantInitialization, + bool EvaluateContracts) const { EvaluatedStmt *Eval = ensureEvaluatedStmt(); const auto *Init = getInit(); @@ -2563,8 +2573,9 @@ APValue *VarDecl::evaluateValueImpl(SmallVectorImpl &Notes, Eval->IsEvaluating = true; ASTContext &Ctx = getASTContext(); - bool Result = Init->EvaluateAsInitializer(Eval->Evaluated, Ctx, this, Notes, - IsConstantInitialization); + bool Result = + Init->EvaluateAsInitializer(Eval->Evaluated, Ctx, this, Notes, + IsConstantInitialization, EvaluateContracts); // In C++, or in C23 if we're initialising a 'constexpr' variable, this isn't // a constant initializer if we produced notes. In that case, we can't keep @@ -2624,12 +2635,12 @@ bool VarDecl::hasConstantInitialization() const { } bool VarDecl::checkForConstantInitialization( - SmallVectorImpl &Notes) const { + SmallVectorImpl &Notes, bool EvaluateContracts) const { EvaluatedStmt *Eval = ensureEvaluatedStmt(); // If we ask for the value before we know whether we have a constant // initializer, we can compute the wrong value (for example, due to // std::is_constant_evaluated()). - assert(!Eval->WasEvaluated && + assert((!Eval->WasEvaluated) && "already evaluated var value before checking for constant init"); assert((getASTContext().getLangOpts().CPlusPlus || getASTContext().getLangOpts().C23) && @@ -2639,7 +2650,8 @@ bool VarDecl::checkForConstantInitialization( // Evaluate the initializer to check whether it's a constant expression. Eval->HasConstantInitialization = - evaluateValueImpl(Notes, true) && Notes.empty(); + evaluateValueImpl(Notes, true, /*EnableContracts=*/EvaluateContracts) && + Notes.empty(); // If evaluation as a constant initializer failed, allow re-evaluation as a // non-constant initializer if we later find we want the value. @@ -2649,6 +2661,32 @@ bool VarDecl::checkForConstantInitialization( return Eval->HasConstantInitialization; } +bool VarDecl::recheckForConstantInitialization( + SmallVectorImpl &Notes, bool EnableContracts) const { + EvaluatedStmt *Eval = ensureEvaluatedStmt(); + + assert((Eval->WasEvaluated && Eval->HasConstantInitialization) && + "Trial initialization should have been performed first"); + + // If we ask for the value before we know whether we have a constant + // initializer, we can compute the wrong value (for example, due to + // std::is_constant_evaluated()). + + assert((getASTContext().getLangOpts().CPlusPlus || + getASTContext().getLangOpts().C23) && + "only meaningful in C++/C23"); + assert(Notes.empty() && "Reevaluating with old notes?"); + assert(!getInit()->isValueDependent()); + Eval->WasEvaluated = false; + + // Wipe out the previously computed value. + // FIXME(EricWF): We should diagnose with the initializer produces a different + // value the second time around. + Eval->Evaluated = APValue(); + + return evaluateValueImpl(Notes, true, EnableContracts) && Notes.empty(); +} + bool VarDecl::isParameterPack() const { return isa(getType()); } @@ -2696,21 +2734,21 @@ VarDecl *VarDecl::getTemplateInstantiationPattern() const { if (isTemplateInstantiation(VDTemplSpec->getTemplateSpecializationKind())) { auto From = VDTemplSpec->getInstantiatedFrom(); if (auto *VTD = From.dyn_cast()) { - while (!VTD->isMemberSpecialization()) { - auto *NewVTD = VTD->getInstantiatedFromMemberTemplate(); - if (!NewVTD) + while (!VTD->hasMemberSpecialization()) { + if (auto *NewVTD = VTD->getInstantiatedFromMemberTemplate()) + VTD = NewVTD; + else break; - VTD = NewVTD; } return getDefinitionOrSelf(VTD->getTemplatedDecl()); } if (auto *VTPSD = From.dyn_cast()) { - while (!VTPSD->isMemberSpecialization()) { - auto *NewVTPSD = VTPSD->getInstantiatedFromMember(); - if (!NewVTPSD) + while (!VTPSD->hasMemberSpecialization()) { + if (auto *NewVTPSD = VTPSD->getInstantiatedFromMember()) + VTPSD = NewVTPSD; + else break; - VTPSD = NewVTPSD; } return getDefinitionOrSelf(VTPSD); } @@ -2719,15 +2757,14 @@ VarDecl *VarDecl::getTemplateInstantiationPattern() const { // If this is the pattern of a variable template, find where it was // instantiated from. FIXME: Is this necessary? - if (VarTemplateDecl *VarTemplate = VD->getDescribedVarTemplate()) { - while (!VarTemplate->isMemberSpecialization()) { - auto *NewVT = VarTemplate->getInstantiatedFromMemberTemplate(); - if (!NewVT) + if (VarTemplateDecl *VTD = VD->getDescribedVarTemplate()) { + while (!VTD->hasMemberSpecialization()) { + if (auto *NewVTD = VTD->getInstantiatedFromMemberTemplate()) + VTD = NewVTD; + else break; - VarTemplate = NewVT; } - - return getDefinitionOrSelf(VarTemplate->getTemplatedDecl()); + return getDefinitionOrSelf(VTD->getTemplatedDecl()); } if (VD == this) @@ -3037,7 +3074,8 @@ FunctionDecl::FunctionDecl(Kind DK, ASTContext &C, DeclContext *DC, TypeSourceInfo *TInfo, StorageClass S, bool UsesFPIntrin, bool isInlineSpecified, ConstexprSpecKind ConstexprKind, - Expr *TrailingRequiresClause) + Expr *TrailingRequiresClause, + ContractSpecifierDecl *Contracts) : DeclaratorDecl(DK, DC, NameInfo.getLoc(), NameInfo.getName(), T, TInfo, StartLoc), DeclContext(DK), redeclarable_base(C), Body(), ODRHash(0), @@ -3073,6 +3111,8 @@ FunctionDecl::FunctionDecl(Kind DK, ASTContext &C, DeclContext *DC, FunctionDeclBits.FriendConstraintRefersToEnclosingTemplate = false; if (TrailingRequiresClause) setTrailingRequiresClause(TrailingRequiresClause); + if (Contracts) + setContracts(Contracts); } void FunctionDecl::getNameForDiagnostic( @@ -3612,6 +3652,47 @@ FunctionDecl::setPreviousDeclaration(FunctionDecl *PrevDecl) { FunctionDecl *FunctionDecl::getCanonicalDecl() { return getFirstDecl(); } + +FunctionDecl *FunctionDecl::getDeclForContracts() { + // Try getting the contracts from the defining decl, and if those aren't present + // use the contracts on the first declaration. + if (auto *Def = getDefinition(); Def && Def->getContracts()) + return Def; + return getCanonicalDecl(); +} + +const FunctionDecl *FunctionDecl::getDeclForContracts() const { + if (auto *Def = getDefinition(); Def && Def->getContracts()) + return Def; + return getCanonicalDecl(); +} + +void FunctionDecl::setContracts(ContractSpecifierDecl *CSD) { + Contracts = CSD; + if (CSD) + CSD->setOwningFunction(this); +} + +ArrayRef FunctionDecl::contracts() const { + if (auto *CSD = Contracts) + return CSD->contracts(); + return std::nullopt; +} + +FunctionDecl::ContractRange FunctionDecl::postconditions() const { + return llvm::make_filter_range( + contracts(), +[](const ContractStmt *CS) { + return CS->getContractKind() == ContractKind::Post; + }); +} + +FunctionDecl::ContractRange FunctionDecl::preconditions() const { + return llvm::make_filter_range( + contracts(), +[](const ContractStmt *CS) { + return CS->getContractKind() == ContractKind::Pre; + }); +} + /// Returns a value indicating whether this function corresponds to a builtin /// function. /// @@ -4142,11 +4223,11 @@ FunctionDecl::getTemplateInstantiationPattern(bool ForDefinition) const { if (FunctionTemplateDecl *Primary = getPrimaryTemplate()) { // If we hit a point where the user provided a specialization of this // template, we're done looking. - while (!ForDefinition || !Primary->isMemberSpecialization()) { - auto *NewPrimary = Primary->getInstantiatedFromMemberTemplate(); - if (!NewPrimary) + while (!ForDefinition || !Primary->hasMemberSpecialization()) { + if (auto *NewPrimary = Primary->getInstantiatedFromMemberTemplate()) + Primary = NewPrimary; + else break; - Primary = NewPrimary; } return getDefinitionOrSelf(Primary->getTemplatedDecl()); @@ -5402,16 +5483,15 @@ ImplicitParamDecl *ImplicitParamDecl::CreateDeserialized(ASTContext &C, return new (C, ID) ImplicitParamDecl(C, QualType(), ImplicitParamKind::Other); } -FunctionDecl * -FunctionDecl::Create(ASTContext &C, DeclContext *DC, SourceLocation StartLoc, - const DeclarationNameInfo &NameInfo, QualType T, - TypeSourceInfo *TInfo, StorageClass SC, bool UsesFPIntrin, - bool isInlineSpecified, bool hasWrittenPrototype, - ConstexprSpecKind ConstexprKind, - Expr *TrailingRequiresClause) { +FunctionDecl *FunctionDecl::Create( + ASTContext &C, DeclContext *DC, SourceLocation StartLoc, + const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo, + StorageClass SC, bool UsesFPIntrin, bool isInlineSpecified, + bool hasWrittenPrototype, ConstexprSpecKind ConstexprKind, + Expr *TrailingRequiresClause, ContractSpecifierDecl *Contracts) { FunctionDecl *New = new (C, DC) FunctionDecl( Function, C, DC, StartLoc, NameInfo, T, TInfo, SC, UsesFPIntrin, - isInlineSpecified, ConstexprKind, TrailingRequiresClause); + isInlineSpecified, ConstexprKind, TrailingRequiresClause, Contracts); New->setHasWrittenPrototype(hasWrittenPrototype); return New; } diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp index f42857f20efc4..c226ee6438c6c 100644 --- a/clang/lib/AST/DeclBase.cpp +++ b/clang/lib/AST/DeclBase.cpp @@ -888,6 +888,10 @@ unsigned Decl::getIdentifierNamespaceForKind(Kind DeclKind) { // tag types, so we include them in the tag namespace. return IDNS_Ordinary | IDNS_Tag; + // FIXME(EricWF): IDK if this is correct + case ResultName: + return IDNS_Ordinary | IDNS_Tag; + case ObjCCompatibleAlias: case ObjCInterface: return IDNS_Ordinary | IDNS_Type; @@ -986,6 +990,7 @@ unsigned Decl::getIdentifierNamespaceForKind(Kind DeclKind) { case LifetimeExtendedTemporary: case RequiresExprBody: case ImplicitConceptSpecialization: + case ContractSpecifier: // Never looked up by name. return 0; } @@ -1098,6 +1103,7 @@ bool Decl::AccessDeclContextCheck() const { // FIXME: a ParmVarDecl can have ClassTemplateSpecialization // as DeclContext (?). isa(this) || + isa(this) || // FIXME: a ClassTemplateSpecialization or CXXRecordDecl can have // AS_none as access specifier. isa(this) || isa(this)) @@ -1168,6 +1174,10 @@ bool Decl::isInNamedModule() const { return getOwningModule() && getOwningModule()->isNamedModule(); } +bool Decl::isFromHeaderUnit() const { + return getOwningModule() && getOwningModule()->isHeaderUnit(); +} + static Decl::Kind getKind(const Decl *D) { return D->getKind(); } static Decl::Kind getKind(const DeclContext *DC) { return DC->getDeclKind(); } diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp index f5a0aa8f82512..5494d277fa530 100644 --- a/clang/lib/AST/DeclCXX.cpp +++ b/clang/lib/AST/DeclCXX.cpp @@ -1631,13 +1631,42 @@ static bool allLookupResultsAreTheSame(const DeclContext::lookup_result &R) { static NamedDecl* getLambdaCallOperatorHelper(const CXXRecordDecl &RD) { if (!RD.isLambda()) return nullptr; DeclarationName Name = - RD.getASTContext().DeclarationNames.getCXXOperatorName(OO_Call); - DeclContext::lookup_result Calls = RD.lookup(Name); + RD.getASTContext().DeclarationNames.getCXXOperatorName(OO_Call); + DeclContext::lookup_result Calls = RD.lookup(Name); assert(!Calls.empty() && "Missing lambda call operator!"); assert(allLookupResultsAreTheSame(Calls) && "More than one lambda call operator!"); - return Calls.front(); + + // FIXME: If we have multiple call operators, we might be in a situation + // where we merged this lambda with one from another module; in that + // case, return our method (instead of that of the other lambda). + // + // This avoids situations where, given two modules A and B, if we + // try to instantiate A's call operator in a function in B, anything + // in the call operator that relies on local decls in the surrounding + // function will crash because it tries to find A's decls, but we only + // instantiated B's: + // + // template + // void f() { + // using T = int; // We only instantiate B's version of this. + // auto L = [](T) { }; // But A's call operator would want A's here. + // } + // + // Walk the call operator’s redecl chain to find the one that belongs + // to this module. + // + // TODO: We need to fix this properly (see + // https://github.com/llvm/llvm-project/issues/90154). + Module *M = RD.getOwningModule(); + for (Decl *D : Calls.front()->redecls()) { + auto *MD = cast(D); + if (MD->getOwningModule() == M) + return MD; + } + + llvm_unreachable("Couldn't find our call operator!"); } FunctionTemplateDecl* CXXRecordDecl::getDependentLambdaCallOperator() const { @@ -1994,19 +2023,21 @@ const CXXRecordDecl *CXXRecordDecl::getTemplateInstantiationPattern() const { if (auto *TD = dyn_cast(this)) { auto From = TD->getInstantiatedFrom(); if (auto *CTD = From.dyn_cast()) { - while (auto *NewCTD = CTD->getInstantiatedFromMemberTemplate()) { - if (NewCTD->isMemberSpecialization()) + while (!CTD->hasMemberSpecialization()) { + if (auto *NewCTD = CTD->getInstantiatedFromMemberTemplate()) + CTD = NewCTD; + else break; - CTD = NewCTD; } return GetDefinitionOrSelf(CTD->getTemplatedDecl()); } if (auto *CTPSD = From.dyn_cast()) { - while (auto *NewCTPSD = CTPSD->getInstantiatedFromMember()) { - if (NewCTPSD->isMemberSpecialization()) + while (!CTPSD->hasMemberSpecialization()) { + if (auto *NewCTPSD = CTPSD->getInstantiatedFromMemberTemplate()) + CTPSD = NewCTPSD; + else break; - CTPSD = NewCTPSD; } return GetDefinitionOrSelf(CTPSD); } @@ -2329,19 +2360,20 @@ CXXMethodDecl::Create(ASTContext &C, CXXRecordDecl *RD, SourceLocation StartLoc, const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo, StorageClass SC, bool UsesFPIntrin, bool isInline, ConstexprSpecKind ConstexprKind, - SourceLocation EndLocation, - Expr *TrailingRequiresClause) { + SourceLocation EndLocation, Expr *TrailingRequiresClause, + ContractSpecifierDecl *Contracts) { return new (C, RD) CXXMethodDecl( CXXMethod, C, RD, StartLoc, NameInfo, T, TInfo, SC, UsesFPIntrin, - isInline, ConstexprKind, EndLocation, TrailingRequiresClause); + isInline, ConstexprKind, EndLocation, TrailingRequiresClause, Contracts); } CXXMethodDecl *CXXMethodDecl::CreateDeserialized(ASTContext &C, GlobalDeclID ID) { - return new (C, ID) CXXMethodDecl( - CXXMethod, C, nullptr, SourceLocation(), DeclarationNameInfo(), - QualType(), nullptr, SC_None, false, false, - ConstexprSpecKind::Unspecified, SourceLocation(), nullptr); + return new (C, ID) + CXXMethodDecl(CXXMethod, C, nullptr, SourceLocation(), + DeclarationNameInfo(), QualType(), nullptr, SC_None, false, + false, ConstexprSpecKind::Unspecified, SourceLocation(), + nullptr, /*Contracts=*/nullptr); } CXXMethodDecl *CXXMethodDecl::getDevirtualizedMethod(const Expr *Base, @@ -2739,10 +2771,11 @@ CXXConstructorDecl::CXXConstructorDecl( const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo, ExplicitSpecifier ES, bool UsesFPIntrin, bool isInline, bool isImplicitlyDeclared, ConstexprSpecKind ConstexprKind, - InheritedConstructor Inherited, Expr *TrailingRequiresClause) + InheritedConstructor Inherited, Expr *TrailingRequiresClause, + ContractSpecifierDecl *Contracts) : CXXMethodDecl(CXXConstructor, C, RD, StartLoc, NameInfo, T, TInfo, SC_None, UsesFPIntrin, isInline, ConstexprKind, - SourceLocation(), TrailingRequiresClause) { + SourceLocation(), TrailingRequiresClause, Contracts) { setNumCtorInitializers(0); setInheritingConstructor(static_cast(Inherited)); setImplicit(isImplicitlyDeclared); @@ -2766,7 +2799,7 @@ CXXConstructorDecl *CXXConstructorDecl::CreateDeserialized(ASTContext &C, auto *Result = new (C, ID, Extra) CXXConstructorDecl( C, nullptr, SourceLocation(), DeclarationNameInfo(), QualType(), nullptr, ExplicitSpecifier(), false, false, false, ConstexprSpecKind::Unspecified, - InheritedConstructor(), nullptr); + InheritedConstructor(), nullptr, /*Contracts=*/{}); Result->setInheritingConstructor(isInheritingConstructor); Result->CXXConstructorDeclBits.HasTrailingExplicitSpecifier = hasTrailingExplicit; @@ -2779,16 +2812,18 @@ CXXConstructorDecl *CXXConstructorDecl::Create( const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo, ExplicitSpecifier ES, bool UsesFPIntrin, bool isInline, bool isImplicitlyDeclared, ConstexprSpecKind ConstexprKind, - InheritedConstructor Inherited, Expr *TrailingRequiresClause) { + InheritedConstructor Inherited, Expr *TrailingRequiresClause, + ContractSpecifierDecl *Contracts) { assert(NameInfo.getName().getNameKind() == DeclarationName::CXXConstructorName && "Name must refer to a constructor"); unsigned Extra = additionalSizeToAlloc( Inherited ? 1 : 0, ES.getExpr() ? 1 : 0); - return new (C, RD, Extra) CXXConstructorDecl( - C, RD, StartLoc, NameInfo, T, TInfo, ES, UsesFPIntrin, isInline, - isImplicitlyDeclared, ConstexprKind, Inherited, TrailingRequiresClause); + return new (C, RD, Extra) + CXXConstructorDecl(C, RD, StartLoc, NameInfo, T, TInfo, ES, UsesFPIntrin, + isInline, isImplicitlyDeclared, ConstexprKind, + Inherited, TrailingRequiresClause, Contracts); } CXXConstructorDecl::init_const_iterator CXXConstructorDecl::init_begin() const { @@ -2905,20 +2940,21 @@ CXXDestructorDecl *CXXDestructorDecl::CreateDeserialized(ASTContext &C, GlobalDeclID ID) { return new (C, ID) CXXDestructorDecl( C, nullptr, SourceLocation(), DeclarationNameInfo(), QualType(), nullptr, - false, false, false, ConstexprSpecKind::Unspecified, nullptr); + false, false, false, ConstexprSpecKind::Unspecified, nullptr, {}); } CXXDestructorDecl *CXXDestructorDecl::Create( ASTContext &C, CXXRecordDecl *RD, SourceLocation StartLoc, const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo, bool UsesFPIntrin, bool isInline, bool isImplicitlyDeclared, - ConstexprSpecKind ConstexprKind, Expr *TrailingRequiresClause) { + ConstexprSpecKind ConstexprKind, Expr *TrailingRequiresClause, + ContractSpecifierDecl *Contracts) { assert(NameInfo.getName().getNameKind() == DeclarationName::CXXDestructorName && "Name must refer to a destructor"); return new (C, RD) CXXDestructorDecl( C, RD, StartLoc, NameInfo, T, TInfo, UsesFPIntrin, isInline, - isImplicitlyDeclared, ConstexprKind, TrailingRequiresClause); + isImplicitlyDeclared, ConstexprKind, TrailingRequiresClause, Contracts); } void CXXDestructorDecl::setOperatorDelete(FunctionDecl *OD, Expr *ThisArg) { @@ -2946,13 +2982,13 @@ CXXConversionDecl *CXXConversionDecl::Create( const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo, bool UsesFPIntrin, bool isInline, ExplicitSpecifier ES, ConstexprSpecKind ConstexprKind, SourceLocation EndLocation, - Expr *TrailingRequiresClause) { + Expr *TrailingRequiresClause, ContractSpecifierDecl *Contracts) { assert(NameInfo.getName().getNameKind() == DeclarationName::CXXConversionFunctionName && "Name must refer to a conversion function"); return new (C, RD) CXXConversionDecl( C, RD, StartLoc, NameInfo, T, TInfo, UsesFPIntrin, isInline, ES, - ConstexprKind, EndLocation, TrailingRequiresClause); + ConstexprKind, EndLocation, TrailingRequiresClause, Contracts); } bool CXXConversionDecl::isLambdaToBlockPointerConversion() const { @@ -3594,3 +3630,116 @@ const StreamingDiagnostic &clang::operator<<(const StreamingDiagnostic &DB, AccessSpecifier AS) { return DB << getAccessName(AS); } + +ResultNameDecl *ResultNameDecl::Create(ASTContext &C, DeclContext *DC, + SourceLocation IdLoc, IdentifierInfo *Id, + QualType T, + ResultNameDecl *CanonicalResultNameDecl, + bool HasInventedPlaceholderType) { + return new (C, DC) ResultNameDecl(DC, IdLoc, Id, T, CanonicalResultNameDecl, + HasInventedPlaceholderType); +} + +ResultNameDecl *ResultNameDecl::CreateDeserialized(ASTContext &C, GlobalDeclID ID) { + return new (C, ID) ResultNameDecl(nullptr, SourceLocation(), nullptr, QualType()); +} + +void ResultNameDecl::anchor() {} + +bool ContractSpecifierDecl::IsPostconditionPred(const ContractStmt *CS) { + return CS->getContractKind() == ContractKind::Post; +} + +bool ContractSpecifierDecl::IsPreconditionPred(const ContractStmt *CS) { + return CS->getContractKind() == ContractKind::Pre; +} + +void ContractSpecifierDecl::anchor() {} + +bool ContractSpecifierDecl::hasCanonicalResultName() const { + return getCanonicalResultName() != nullptr; +} + +SourceRange ContractSpecifierDecl::getSourceRange() const { + if (contracts().empty()) + return SourceRange(getLocation(), getLocation()); + return SourceRange(getLocation(), contracts().back()->getEndLoc()); +} + +ContractSpecifierDecl *ContractSpecifierDecl::Create( + ASTContext &C, DeclContext *DC, SourceLocation Loc, + ArrayRef Contracts, bool IsInvalid) { + assert((Contracts.size() > 0 || IsInvalid) && + "ContractSpecifierDecl must have at least one contract"); + size_t Extra = additionalSizeToAlloc(Contracts.size()); + return new (C, DC, Extra) + ContractSpecifierDecl(DC, Loc, Contracts, IsInvalid); +} +ContractSpecifierDecl *ContractSpecifierDecl::CreateDeserialized( + clang::ASTContext &C, clang::GlobalDeclID ID, unsigned NumContracts) { + size_t Extra = additionalSizeToAlloc(NumContracts); + auto *Result = new (C, ID, Extra) + ContractSpecifierDecl(nullptr, SourceLocation(), NumContracts); + return Result; +} + +ResultNameDecl * +ContractSpecifierDecl::ExtractResultName(const ContractStmt *CS) { + assert(CS && "Null pointer?"); + if (CS->hasResultName()) + return CS->getResultName(); + return nullptr; +} + +ContractSpecifierDecl::ContractSpecifierDecl(DeclContext *DC, + SourceLocation Loc, + ArrayRef Contracts, + bool IsInvalid) + : Decl(Decl::ContractSpecifier, DC, Loc), NumContracts(Contracts.size()) { + if (IsInvalid) + this->setInvalidDecl(true); + assert((Contracts.size() > 0 || IsInvalid) && + "ContractSpecifierSequence must have at least one contract"); + setContracts(Contracts); +} + +const ResultNameDecl *ContractSpecifierDecl::getCanonicalResultName() const { + for (auto *RN : result_names()) { + // The first result name should be the canonical one. + assert(RN->isCanonicalResultName() && + "Unexpected non-canonical result name"); + return RN; + } + return nullptr; +} + +void ContractSpecifierDecl::setContracts(ArrayRef Contracts) { + assert((Contracts.size() > 0 || isInvalidDecl()) && + "ContractSpecifierDecl must have at least one contract"); + assert(Contracts.size() == NumContracts && + "ContractSpecifierDecl must have at least one contract"); + + std::copy(Contracts.begin(), Contracts.end(), + getTrailingObjects()); + auto *DC = getDeclContext(); + // Update the result names to point to the correct canonical result name. + ResultNameDecl *CanonicalResultName = nullptr; + for (auto *RND : result_names()) { + if (CanonicalResultName) + RND->setCanonicalResultName(CanonicalResultName); + else + CanonicalResultName = RND; + RND->setDeclContext(DC); + } +} + +void ContractSpecifierDecl::setOwningFunction(DeclContext *FD) { + setDeclContext(FD); + for (auto *RND : result_names()) + RND->setDeclContext(FD); +} + +bool ContractSpecifierDecl::hasInventedPlaceholdersTypes() const { + auto *CRND = getCanonicalResultName(); + return CRND && CRND->hasInventedPlaceholderType(); +} diff --git a/clang/lib/AST/DeclPrinter.cpp b/clang/lib/AST/DeclPrinter.cpp index 0d51fdbc7e126..2971a2b8587f3 100644 --- a/clang/lib/AST/DeclPrinter.cpp +++ b/clang/lib/AST/DeclPrinter.cpp @@ -72,6 +72,7 @@ namespace { void VisitVarDecl(VarDecl *D); void VisitLabelDecl(LabelDecl *D); void VisitParmVarDecl(ParmVarDecl *D); + void VisitResultNameDecl(ResultNameDecl *D); void VisitFileScopeAsmDecl(FileScopeAsmDecl *D); void VisitTopLevelStmtDecl(TopLevelStmtDecl *D); void VisitImportDecl(ImportDecl *D); @@ -817,6 +818,16 @@ void DeclPrinter::VisitFunctionDecl(FunctionDecl *D) { TrailingRequiresClause->printPretty(Out, nullptr, SubPolicy, Indentation, "\n", &Context); } + + const ContractSpecifierDecl *Contracts = D->getContracts(); + if (Contracts) { + Out << " [["; + for (const auto *Contract : Contracts->contracts()) { + Contract->printPretty(Out, nullptr, SubPolicy, Indentation, "\n", + &Context); + } + Out << "]]"; + } } else { Ty.print(Out, Policy, Proto); } @@ -1910,3 +1921,18 @@ void DeclPrinter::VisitNonTypeTemplateParmDecl( /*IncludeType=*/false); } } + +void DeclPrinter::VisitResultNameDecl(ResultNameDecl *RND) { + StringRef Name = ""; + if (IdentifierInfo *II = RND->getIdentifier()) { + Name = + Policy.CleanUglifiedParameters ? II->deuglifiedName() : II->getName(); + } + printDeclType(RND->getType(), Name, false); + Out << RND->getDeclName(); + if (!RND->isCanonicalResultName()) { + Out << " = "; + RND->getCanonicalResultName()->printQualifiedName(Out); + Out << " " << RND << " "; + } +} diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp index 6fe817c5ef1c6..d9b67b7bedf5a 100644 --- a/clang/lib/AST/DeclTemplate.cpp +++ b/clang/lib/AST/DeclTemplate.cpp @@ -309,16 +309,16 @@ bool TemplateDecl::isTypeAlias() const { void RedeclarableTemplateDecl::anchor() {} RedeclarableTemplateDecl::CommonBase *RedeclarableTemplateDecl::getCommonPtr() const { - if (Common) - return Common; + if (CommonBase *C = getCommonPtrInternal()) + return C; // Walk the previous-declaration chain until we either find a declaration // with a common pointer or we run out of previous declarations. SmallVector PrevDecls; for (const RedeclarableTemplateDecl *Prev = getPreviousDecl(); Prev; Prev = Prev->getPreviousDecl()) { - if (Prev->Common) { - Common = Prev->Common; + if (CommonBase *C = Prev->getCommonPtrInternal()) { + setCommonPtr(C); break; } @@ -326,18 +326,18 @@ RedeclarableTemplateDecl::CommonBase *RedeclarableTemplateDecl::getCommonPtr() c } // If we never found a common pointer, allocate one now. - if (!Common) { + if (!getCommonPtrInternal()) { // FIXME: If any of the declarations is from an AST file, we probably // need an update record to add the common data. - Common = newCommon(getASTContext()); + setCommonPtr(newCommon(getASTContext())); } // Update any previous declarations we saw with the common pointer. for (const RedeclarableTemplateDecl *Prev : PrevDecls) - Prev->Common = Common; + Prev->setCommonPtr(getCommonPtrInternal()); - return Common; + return getCommonPtrInternal(); } void RedeclarableTemplateDecl::loadLazySpecializationsImpl() const { @@ -463,19 +463,17 @@ void FunctionTemplateDecl::addSpecialization( } void FunctionTemplateDecl::mergePrevDecl(FunctionTemplateDecl *Prev) { - using Base = RedeclarableTemplateDecl; - // If we haven't created a common pointer yet, then it can just be created // with the usual method. - if (!Base::Common) + if (!getCommonPtrInternal()) return; - Common *ThisCommon = static_cast(Base::Common); + Common *ThisCommon = static_cast(getCommonPtrInternal()); Common *PrevCommon = nullptr; SmallVector PreviousDecls; for (; Prev; Prev = Prev->getPreviousDecl()) { - if (Prev->Base::Common) { - PrevCommon = static_cast(Prev->Base::Common); + if (CommonBase *C = Prev->getCommonPtrInternal()) { + PrevCommon = static_cast(C); break; } PreviousDecls.push_back(Prev); @@ -485,7 +483,7 @@ void FunctionTemplateDecl::mergePrevDecl(FunctionTemplateDecl *Prev) { // use this common pointer. if (!PrevCommon) { for (auto *D : PreviousDecls) - D->Base::Common = ThisCommon; + D->setCommonPtr(ThisCommon); return; } @@ -493,7 +491,7 @@ void FunctionTemplateDecl::mergePrevDecl(FunctionTemplateDecl *Prev) { assert(ThisCommon->Specializations.size() == 0 && "Can't merge incompatible declarations!"); - Base::Common = PrevCommon; + setCommonPtr(PrevCommon); } //===----------------------------------------------------------------------===// diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index 2e463fc00c6b6..e5239fd144086 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -443,6 +443,9 @@ DeclRefExpr::DeclRefExpr(const ASTContext &Ctx, ValueDecl *D, DeclRefExprBits.CapturedByCopyInLambdaWithExplicitObjectParameter = false; DeclRefExprBits.NonOdrUseReason = NOUR; DeclRefExprBits.IsImmediateEscalating = false; + DeclRefExprBits.IsInContractContext = false; + DeclRefExprBits.IsConstified = false; + DeclRefExprBits.Loc = L; setDependence(computeDependence(this, Ctx)); } @@ -483,6 +486,8 @@ DeclRefExpr::DeclRefExpr(const ASTContext &Ctx, } DeclRefExprBits.IsImmediateEscalating = false; DeclRefExprBits.HadMultipleCandidates = 0; + DeclRefExprBits.IsConstified = false; + DeclRefExprBits.IsInContractContext = false; setDependence(computeDependence(this, Ctx)); } @@ -3640,6 +3645,7 @@ bool Expr::HasSideEffects(const ASTContext &Ctx, case SYCLUniqueStableNameExprClass: case PackIndexingExprClass: case HLSLOutArgExprClass: + case OpenACCAsteriskSizeExprClass: // These never have a side-effect. return false; @@ -5408,3 +5414,13 @@ HLSLOutArgExpr *HLSLOutArgExpr::Create(const ASTContext &C, QualType Ty, HLSLOutArgExpr *HLSLOutArgExpr::CreateEmpty(const ASTContext &C) { return new (C) HLSLOutArgExpr(EmptyShell()); } + +OpenACCAsteriskSizeExpr *OpenACCAsteriskSizeExpr::Create(const ASTContext &C, + SourceLocation Loc) { + return new (C) OpenACCAsteriskSizeExpr(Loc, C.IntTy); +} + +OpenACCAsteriskSizeExpr * +OpenACCAsteriskSizeExpr::CreateEmpty(const ASTContext &C) { + return new (C) OpenACCAsteriskSizeExpr({}, C.IntTy); +} diff --git a/clang/lib/AST/ExprClassification.cpp b/clang/lib/AST/ExprClassification.cpp index 9d97633309ada..bc7444c07cb0c 100644 --- a/clang/lib/AST/ExprClassification.cpp +++ b/clang/lib/AST/ExprClassification.cpp @@ -471,6 +471,7 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) { case Expr::CoyieldExprClass: return ClassifyInternal(Ctx, cast(E)->getResumeExpr()); case Expr::SYCLUniqueStableNameExprClass: + case Expr::OpenACCAsteriskSizeExprClass: return Cl::CL_PRValue; break; @@ -509,7 +510,8 @@ static Cl::Kinds ClassifyDecl(ASTContext &Ctx, const Decl *D) { else islvalue = isa(D) || + UnnamedGlobalConstantDecl, TemplateParamObjectDecl, ResultNameDecl>( + D) || (Ctx.getLangOpts().CPlusPlus && (isa(D))); diff --git a/clang/lib/AST/ExprConstShared.h b/clang/lib/AST/ExprConstShared.h index efe8ee986d29b..401ae629c86bf 100644 --- a/clang/lib/AST/ExprConstShared.h +++ b/clang/lib/AST/ExprConstShared.h @@ -14,12 +14,17 @@ #ifndef LLVM_CLANG_LIB_AST_EXPRCONSTSHARED_H #define LLVM_CLANG_LIB_AST_EXPRCONSTSHARED_H +#include "clang/Basic/TypeTraits.h" + namespace llvm { class APFloat; } namespace clang { class QualType; class LangOptions; +class ASTContext; +class CharUnits; +class Expr; } // namespace clang using namespace clang; /// Values returned by __builtin_classify_type, chosen to match the values @@ -66,4 +71,7 @@ void HandleComplexComplexDiv(llvm::APFloat A, llvm::APFloat B, llvm::APFloat C, llvm::APFloat D, llvm::APFloat &ResR, llvm::APFloat &ResI); +CharUnits GetAlignOfExpr(const ASTContext &Ctx, const Expr *E, + UnaryExprOrTypeTrait ExprKind); + #endif diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 3a73cea97fcc3..98acc99ee7c72 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -52,6 +52,7 @@ #include "clang/AST/TypeLoc.h" #include "clang/Basic/Builtins.h" #include "clang/Basic/DiagnosticSema.h" +#include "clang/Basic/EricWFDebug.h" #include "clang/Basic/TargetBuiltins.h" #include "clang/Basic/TargetInfo.h" #include "llvm/ADT/APFixedPoint.h" @@ -61,6 +62,7 @@ #include "llvm/Support/Casting.h" #include "llvm/Support/Debug.h" #include "llvm/Support/SaveAndRestore.h" +#include "llvm/Support/Signals.h" #include "llvm/Support/SipHash.h" #include "llvm/Support/TimeProfiler.h" #include "llvm/Support/raw_ostream.h" @@ -520,7 +522,6 @@ namespace { CallRef() : OrigCallee(), CallIndex(0), Version() {} CallRef(const FunctionDecl *Callee, unsigned CallIndex, unsigned Version) : OrigCallee(Callee), CallIndex(CallIndex), Version(Version) {} - explicit operator bool() const { return OrigCallee; } /// Get the parameter that the caller initialized, corresponding to the @@ -535,6 +536,7 @@ namespace { /// virtual override), but this function's parameters are the ones that /// appear in the parameter map. const FunctionDecl *OrigCallee; + /// The call index of the frame that holds the argument values. unsigned CallIndex; /// The version of the parameters corresponding to this call. @@ -598,6 +600,10 @@ namespace { return {Callee, Index, ++CurTempVersion}; } + // The slot for the result of the contract evaluation. + const LValue *ResultSlot = nullptr; + const APValue *ResultValue = nullptr; + // FIXME: Adding this to every 'CallStackFrame' may have a nontrivial impact // on the overall stack usage of deeply-recursing constexpr evaluations. // (We should cache this map rather than recomputing it repeatedly.) @@ -3481,7 +3487,7 @@ static bool evaluateVarDeclInit(EvalInfo &Info, const Expr *E, return true; } - if (isa(VD)) { + if (auto *PVD = dyn_cast(VD); PVD) { // Assume parameters of a potential constant expression are usable in // constant expressions. if (!Info.checkingPotentialConstantExpression() || @@ -4261,6 +4267,21 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E, // started in the current evaluation. BaseVal = Info.EvaluatingDeclValue; } else if (const ValueDecl *D = LVal.Base.dyn_cast()) { + + // Allow reading the result of a function call inside a post contract. + // FIXME(EricWF): There's more validation that needs to be done here. + if (auto *RND = dyn_cast(D); RND) { + assert(RND == RND->getCanonicalResultName()); + + APValue *Val = Frame->getTemporary(RND, LVal.Base.getVersion()); + + if (!Val) { + Info.CCEDiag(RND->getLocation(), diag::err_ericwf_fixme) + << "Failing to find completed object"; + return CompleteObject(); + } + return CompleteObject(LVal.Base, Val, RND->getType()); + } // Allow reading from a GUID declaration. if (auto *GD = dyn_cast(D)) { if (isModification(AK)) { @@ -5330,6 +5351,30 @@ static bool CheckLocalVariableDeclaration(EvalInfo &Info, const VarDecl *VD) { return true; } +static bool EvaluateContract(const ContractStmt *S, EvalInfo &Info) { + using CES = ContractEvaluationSemantic; + auto &Ctx = Info.Ctx; + + if (!Info.EvaluateContracts) + return true; + + CES Sem = S->getSemantic(Ctx); + if (Sem == CES::Ignore) + return true; + + const Expr *E = S->getCond(); + bool Result; + if (!EvaluateCond(Info,nullptr, E, Result)) + return false; + if (!Result) { + Info.CCEDiag(E, Sem == CES::Observe ? diag::warn_constexpr_contract_failure + : diag::err_constexpr_contract_failure) + << E->getSourceRange(); + return Sem == CES::Observe; + } + return true; +} + // Evaluate a statement. static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info, const Stmt *S, const SwitchCase *Case) { @@ -5500,12 +5545,14 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info, case Stmt::ReturnStmtClass: { const Expr *RetExpr = cast(S)->getRetValue(); + FullExpressionRAII Scope(Info); if (RetExpr && RetExpr->isValueDependent()) { EvaluateDependentExpr(RetExpr, Info); // We know we returned, but we don't know what the value is. return ESR_Failed; } + if (RetExpr && !(Result.Slot ? EvaluateInPlace(Result.Value, Info, *Result.Slot, RetExpr) @@ -5567,7 +5614,11 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info, } return Scope.destroy() ? ESR_Succeeded : ESR_Failed; } - + case Stmt::ContractStmtClass: { + if (EvaluateContract(cast(S), Info)) + return ESR_Succeeded; + return ESR_Failed; + } case Stmt::WhileStmtClass: { const WhileStmt *WS = cast(S); while (true) { @@ -6472,6 +6523,69 @@ static bool handleTrivialCopy(EvalInfo &Info, const ParmVarDecl *Param, CopyObjectRepresentation); } +static bool EvaluatePreContracts(EvalInfo &Info, const FunctionDecl *Callee, + CallStackFrame *Frame) { + ContractSpecifierDecl *Contracts = Callee->getContracts(); + if (!Contracts) + return true; + for (auto *S : Contracts->preconditions()) { + if (!EvaluateContract(S, Frame->Info)) { + return false; + } + } + return true; +} + +static bool EvaluatePostContracts(EvalInfo &Info, const FunctionDecl *Callee, + APValue &ResultValue, + const LValue *ResultSlot) { + if (Info.checkingPotentialConstantExpression()) + return false; + + ContractSpecifierDecl *Contracts = Callee->getContracts(); + if (!Contracts) + return true; + BlockScopeRAII Scope(Info); + const ResultNameDecl *CanonicalResultName = + Contracts->getCanonicalResultName(); + + bool NeedsResultSlot = + (!ResultSlot || !ResultSlot->getLValueBase()) && CanonicalResultName; + + APValue *LastValue = nullptr; + LValue LV; + if (NeedsResultSlot) { + auto *RND = CanonicalResultName; + assert(RND->getType().isTriviallyCopyableType(Info.Ctx)); + + APValue &V = Info.CurrentCall->createTemporary( + CanonicalResultName, CanonicalResultName->getType(), ScopeKind::Block, + LV); + + // Perform the trivial copy of the result of the function into the + // temporary. + V = ResultValue; + Info.CurrentCall->ResultSlot = nullptr; + LastValue = &V; + } else { + Info.CurrentCall->ResultSlot = ResultSlot; + Info.CurrentCall->ResultValue = &ResultValue; + } + ((void)LastValue); + + for (auto *S : Contracts->postconditions()) { + if (!EvaluateContract(S, Info)) + return false; + } + // If we used a different result slot, the return value needs to be copied + // back. + if (NeedsResultSlot) { + assert(LastValue); + ResultValue = *LastValue; + } + return true; +} + /// Evaluate a function call. static bool HandleFunctionCall(SourceLocation CallLoc, const FunctionDecl *Callee, const LValue *This, @@ -6517,14 +6631,20 @@ static bool HandleFunctionCall(SourceLocation CallLoc, Frame.LambdaThisCaptureField); } + if (!EvaluatePreContracts(Info, Callee, &Frame)) + return false; + StmtResult Ret = {Result, ResultSlot}; EvalStmtResult ESR = EvaluateStmt(Ret, Info, Body); + if (ESR == ESR_Succeeded) { if (Callee->getReturnType()->isVoidType()) - return true; + return EvaluatePostContracts(Info, Callee, Result, ResultSlot); Info.FFDiag(Callee->getEndLoc(), diag::note_constexpr_no_return); } - return ESR == ESR_Returned; + if (ESR == ESR_Returned) + return EvaluatePostContracts(Info, Callee, Result, ResultSlot); + return false; } /// Evaluate a constructor call. @@ -8087,7 +8207,8 @@ class ExprEvaluatorBase bool VisitCallExpr(const CallExpr *E) { APValue Result; - if (!handleCallExpr(E, Result, nullptr)) + LValue ResultSlot; + if (!handleCallExpr(E, Result, &ResultSlot)) return false; return DerivedSuccess(Result, E); } @@ -8658,6 +8779,7 @@ class LValueExprEvaluator bool VisitCallExpr(const CallExpr *E); bool VisitDeclRefExpr(const DeclRefExpr *E); + bool VisitResultNameDecl(const DeclRefExpr *DE, const ResultNameDecl *E); bool VisitPredefinedExpr(const PredefinedExpr *E) { return Success(E); } bool VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *E); bool VisitCompoundLiteralExpr(const CompoundLiteralExpr *E); @@ -8766,7 +8888,8 @@ static bool EvaluateLValue(const Expr *E, LValue &Result, EvalInfo &Info, bool InvalidBaseOK) { assert(!E->isValueDependent()); assert(E->isGLValue() || E->getType()->isFunctionType() || - E->getType()->isVoidType() || isa(E->IgnoreParens())); + E->getType()->isVoidType() || + isa(E->IgnoreParens())); return LValueExprEvaluator(Info, Result, InvalidBaseOK).Visit(E); } @@ -8775,6 +8898,11 @@ bool LValueExprEvaluator::VisitDeclRefExpr(const DeclRefExpr *E) { if (isa(D)) return Success(cast(D)); + + if (const ResultNameDecl *RND = dyn_cast(D)) { + return VisitResultNameDecl(E, RND); + } + if (const VarDecl *VD = dyn_cast(D)) return VisitVarDecl(E, VD); if (const BindingDecl *BD = dyn_cast(D)) @@ -8782,9 +8910,7 @@ bool LValueExprEvaluator::VisitDeclRefExpr(const DeclRefExpr *E) { return Error(E); } - bool LValueExprEvaluator::VisitVarDecl(const Expr *E, const VarDecl *VD) { - // If we are within a lambda's call operator, check whether the 'VD' referred // to within 'E' actually represents a lambda-capture that maps to a // data-member/field within the closure object, and if so, evaluate to the @@ -8861,6 +8987,28 @@ bool LValueExprEvaluator::VisitVarDecl(const Expr *E, const VarDecl *VD) { return Success(*V, E); } +bool LValueExprEvaluator::VisitResultNameDecl(const DeclRefExpr *E, + const ResultNameDecl *VD) { + + auto *Frame = Info.CurrentCall; + // We don't always have a result value when we're doing this kind of + // evaluation? + if (Info.checkingPotentialConstantExpression()) + return false; + + assert(Frame); + if (Frame->ResultSlot) { + return Success(Frame->ResultSlot->getLValueBase()); + + // return Success(Frame->ResultSlot; + } else { + APValue::LValueBase Base( + VD->getCanonicalResultName(), Frame->Index, + Frame->getCurrentTemporaryVersion(VD->getCanonicalResultName())); + return Success(Base); + } +} + bool LValueExprEvaluator::VisitCallExpr(const CallExpr *E) { if (!IsConstantEvaluatedBuiltinCall(E)) return ExprEvaluatorBaseTy::VisitCallExpr(E); @@ -9620,7 +9768,7 @@ bool PointerExprEvaluator::VisitCastExpr(const CastExpr *E) { return ExprEvaluatorBaseTy::VisitCastExpr(E); } -static CharUnits GetAlignOfType(EvalInfo &Info, QualType T, +static CharUnits GetAlignOfType(const ASTContext &Ctx, QualType T, UnaryExprOrTypeTrait ExprKind) { // C++ [expr.alignof]p3: // When alignof is applied to a reference type, the result is the @@ -9631,23 +9779,22 @@ static CharUnits GetAlignOfType(EvalInfo &Info, QualType T, return CharUnits::One(); const bool AlignOfReturnsPreferred = - Info.Ctx.getLangOpts().getClangABICompat() <= LangOptions::ClangABI::Ver7; + Ctx.getLangOpts().getClangABICompat() <= LangOptions::ClangABI::Ver7; // __alignof is defined to return the preferred alignment. // Before 8, clang returned the preferred alignment for alignof and _Alignof // as well. if (ExprKind == UETT_PreferredAlignOf || AlignOfReturnsPreferred) - return Info.Ctx.toCharUnitsFromBits( - Info.Ctx.getPreferredTypeAlign(T.getTypePtr())); + return Ctx.toCharUnitsFromBits(Ctx.getPreferredTypeAlign(T.getTypePtr())); // alignof and _Alignof are defined to return the ABI alignment. else if (ExprKind == UETT_AlignOf) - return Info.Ctx.getTypeAlignInChars(T.getTypePtr()); + return Ctx.getTypeAlignInChars(T.getTypePtr()); else llvm_unreachable("GetAlignOfType on a non-alignment ExprKind"); } -static CharUnits GetAlignOfExpr(EvalInfo &Info, const Expr *E, - UnaryExprOrTypeTrait ExprKind) { +CharUnits GetAlignOfExpr(const ASTContext &Ctx, const Expr *E, + UnaryExprOrTypeTrait ExprKind) { E = E->IgnoreParens(); // The kinds of expressions that we have special-case logic here for @@ -9657,22 +9804,22 @@ static CharUnits GetAlignOfExpr(EvalInfo &Info, const Expr *E, // alignof decl is always accepted, even if it doesn't make sense: we default // to 1 in those cases. if (const DeclRefExpr *DRE = dyn_cast(E)) - return Info.Ctx.getDeclAlign(DRE->getDecl(), - /*RefAsPointee*/true); + return Ctx.getDeclAlign(DRE->getDecl(), + /*RefAsPointee*/ true); if (const MemberExpr *ME = dyn_cast(E)) - return Info.Ctx.getDeclAlign(ME->getMemberDecl(), - /*RefAsPointee*/true); + return Ctx.getDeclAlign(ME->getMemberDecl(), + /*RefAsPointee*/ true); - return GetAlignOfType(Info, E->getType(), ExprKind); + return GetAlignOfType(Ctx, E->getType(), ExprKind); } static CharUnits getBaseAlignment(EvalInfo &Info, const LValue &Value) { if (const auto *VD = Value.Base.dyn_cast()) return Info.Ctx.getDeclAlign(VD); if (const auto *E = Value.Base.dyn_cast()) - return GetAlignOfExpr(Info, E, UETT_AlignOf); - return GetAlignOfType(Info, Value.Base.getTypeInfoType(), UETT_AlignOf); + return GetAlignOfExpr(Info.Ctx, E, UETT_AlignOf); + return GetAlignOfType(Info.Ctx, Value.Base.getTypeInfoType(), UETT_AlignOf); } /// Evaluate the value of the alignment argument to __builtin_align_{up,down}, @@ -9768,11 +9915,8 @@ bool PointerExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, if (BaseAlignment < Align) { Result.Designator.setInvalid(); - // FIXME: Add support to Diagnostic for long / long long. - CCEDiag(E->getArg(0), - diag::note_constexpr_baa_insufficient_alignment) << 0 - << (unsigned)BaseAlignment.getQuantity() - << (unsigned)Align.getQuantity(); + CCEDiag(E->getArg(0), diag::note_constexpr_baa_insufficient_alignment) + << 0 << BaseAlignment.getQuantity() << Align.getQuantity(); return false; } } @@ -9783,11 +9927,11 @@ bool PointerExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, (OffsetResult.Base ? CCEDiag(E->getArg(0), - diag::note_constexpr_baa_insufficient_alignment) << 1 + diag::note_constexpr_baa_insufficient_alignment) + << 1 : CCEDiag(E->getArg(0), diag::note_constexpr_baa_value_insufficient_alignment)) - << (int)OffsetResult.Offset.getQuantity() - << (unsigned)Align.getQuantity(); + << OffsetResult.Offset.getQuantity() << Align.getQuantity(); return false; } @@ -11873,6 +12017,13 @@ class IntExprEvaluator return Success(E->getValue(), E); } + bool VisitOpenACCAsteriskSizeExpr(const OpenACCAsteriskSizeExpr *E) { + // This should not be evaluated during constant expr evaluation, as it + // should always be in an unevaluated context (the args list of a 'gang' or + // 'tile' clause). + return Error(E); + } + bool VisitUnaryReal(const UnaryOperator *E); bool VisitUnaryImag(const UnaryOperator *E); @@ -11948,6 +12099,8 @@ static bool EvaluateIntegerOrLValue(const Expr *E, APValue &Result, } static bool EvaluateInteger(const Expr *E, APSInt &Result, EvalInfo &Info) { + if (E->isValueDependent()) + E->dumpColor(); assert(!E->isValueDependent()); APValue Val; if (!EvaluateIntegerOrLValue(E, Val, Info)) @@ -14471,11 +14624,11 @@ bool IntExprEvaluator::VisitUnaryExprOrTypeTraitExpr( case UETT_PreferredAlignOf: case UETT_AlignOf: { if (E->isArgumentType()) - return Success(GetAlignOfType(Info, E->getArgumentType(), E->getKind()), - E); + return Success( + GetAlignOfType(Info.Ctx, E->getArgumentType(), E->getKind()), E); else - return Success(GetAlignOfExpr(Info, E->getArgumentExpr(), E->getKind()), - E); + return Success( + GetAlignOfExpr(Info.Ctx, E->getArgumentExpr(), E->getKind()), E); } case UETT_PtrAuthTypeDiscriminator: { @@ -16579,7 +16732,8 @@ bool Expr::EvaluateAsConstantExpr(EvalResult &Result, const ASTContext &Ctx, bool Expr::EvaluateAsInitializer(APValue &Value, const ASTContext &Ctx, const VarDecl *VD, SmallVectorImpl &Notes, - bool IsConstantInitialization) const { + bool IsConstantInitialization, + bool EvaluateContracts) const { assert(!isValueDependent() && "Expression evaluator can't be called on a dependent expression."); @@ -16600,6 +16754,7 @@ bool Expr::EvaluateAsInitializer(APValue &Value, const ASTContext &Ctx, : EvalInfo::EM_ConstantFold); Info.setEvaluatingDecl(VD, Value); Info.InConstantContext = IsConstantInitialization; + Info.EvaluateContracts = EvaluateContracts; SourceLocation DeclLoc = VD->getLocation(); QualType DeclTy = VD->getType(); @@ -16908,6 +17063,7 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext &Ctx) { case Expr::GNUNullExprClass: case Expr::SourceLocExprClass: case Expr::EmbedExprClass: + case Expr::OpenACCAsteriskSizeExprClass: return NoDiag(); case Expr::PackIndexingExprClass: diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp index 70ed9cb0736f5..777cdca1a0c0d 100644 --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -1164,8 +1164,25 @@ void CXXNameMangler::mangleUnscopedName(GlobalDecl GD, const DeclContext *DC, // ::= St # ::std:: assert(!isa(DC) && "unskipped LinkageSpecDecl"); - if (isStdNamespace(DC)) + if (isStdNamespace(DC)) { + if (getASTContext().getTargetInfo().getTriple().isOSSolaris()) { + const NamedDecl *ND = cast(GD.getDecl()); + if (const RecordDecl *RD = dyn_cast(ND)) { + // Issue #33114: Need non-standard mangling of std::tm etc. for + // Solaris ABI compatibility. + // + // ::= tm # ::std::tm, same for the others + if (const IdentifierInfo *II = RD->getIdentifier()) { + StringRef type = II->getName(); + if (llvm::is_contained({"div_t", "ldiv_t", "lconv", "tm"}, type)) { + Out << type.size() << type; + return; + } + } + } + } Out << "St"; + } mangleUnqualifiedName(GD, DC, AdditionalAbiTags); } @@ -5745,6 +5762,15 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity, case Expr::HLSLOutArgExprClass: llvm_unreachable( "cannot mangle hlsl temporary value; mangling wrong thing?"); + case Expr::OpenACCAsteriskSizeExprClass: { + // We shouldn't ever be able to get here, but diagnose anyway. + DiagnosticsEngine &Diags = Context.getDiags(); + unsigned DiagID = Diags.getCustomDiagID( + DiagnosticsEngine::Error, + "cannot yet mangle OpenACC Asterisk Size expression"); + Diags.Report(DiagID); + return; + } } if (AsTemplateArg && !IsPrimaryExpr) diff --git a/clang/lib/AST/JSONNodeDumper.cpp b/clang/lib/AST/JSONNodeDumper.cpp index 565f1e05710c8..ddbe2136a671f 100644 --- a/clang/lib/AST/JSONNodeDumper.cpp +++ b/clang/lib/AST/JSONNodeDumper.cpp @@ -1354,6 +1354,9 @@ void JSONNodeDumper::VisitSYCLUniqueStableNameExpr( createQualType(E->getTypeSourceInfo()->getType())); } +void JSONNodeDumper::VisitOpenACCAsteriskSizeExpr( + const OpenACCAsteriskSizeExpr *E) {} + void JSONNodeDumper::VisitPredefinedExpr(const PredefinedExpr *PE) { JOS.attribute("name", PredefinedExpr::getIdentKindName(PE->getIdentKind())); } diff --git a/clang/lib/AST/MicrosoftMangle.cpp b/clang/lib/AST/MicrosoftMangle.cpp index e4c8663c134fd..4ccf3f76bf0ce 100644 --- a/clang/lib/AST/MicrosoftMangle.cpp +++ b/clang/lib/AST/MicrosoftMangle.cpp @@ -1024,6 +1024,7 @@ void MicrosoftCXXNameMangler::mangleFloat(llvm::APFloat Number) { case APFloat::S_Float8E4M3B11FNUZ: case APFloat::S_Float8E3M4: case APFloat::S_FloatTF32: + case APFloat::S_Float8E8M0FNU: case APFloat::S_Float6E3M2FN: case APFloat::S_Float6E2M3FN: case APFloat::S_Float4E2M1FN: diff --git a/clang/lib/AST/OpenACCClause.cpp b/clang/lib/AST/OpenACCClause.cpp index d864ded33e8d1..6fb8fe0b8cfee 100644 --- a/clang/lib/AST/OpenACCClause.cpp +++ b/clang/lib/AST/OpenACCClause.cpp @@ -24,8 +24,9 @@ bool OpenACCClauseWithParams::classof(const OpenACCClause *C) { } bool OpenACCClauseWithExprs::classof(const OpenACCClause *C) { return OpenACCWaitClause::classof(C) || OpenACCNumGangsClause::classof(C) || + OpenACCTileClause::classof(C) || OpenACCClauseWithSingleIntExpr::classof(C) || - OpenACCClauseWithVarList::classof(C); + OpenACCGangClause::classof(C) || OpenACCClauseWithVarList::classof(C); } bool OpenACCClauseWithVarList::classof(const OpenACCClause *C) { return OpenACCPrivateClause::classof(C) || @@ -124,6 +125,21 @@ OpenACCNumWorkersClause::OpenACCNumWorkersClause(SourceLocation BeginLoc, "Condition expression type not scalar/dependent"); } +OpenACCGangClause::OpenACCGangClause(SourceLocation BeginLoc, + SourceLocation LParenLoc, + ArrayRef GangKinds, + ArrayRef IntExprs, + SourceLocation EndLoc) + : OpenACCClauseWithExprs(OpenACCClauseKind::Gang, BeginLoc, LParenLoc, + EndLoc) { + assert(GangKinds.size() == IntExprs.size() && "Mismatch exprs/kind?"); + std::uninitialized_copy(IntExprs.begin(), IntExprs.end(), + getTrailingObjects()); + setExprs(MutableArrayRef(getTrailingObjects(), IntExprs.size())); + std::uninitialized_copy(GangKinds.begin(), GangKinds.end(), + getTrailingObjects()); +} + OpenACCNumWorkersClause * OpenACCNumWorkersClause::Create(const ASTContext &C, SourceLocation BeginLoc, SourceLocation LParenLoc, Expr *IntExpr, @@ -221,6 +237,16 @@ OpenACCNumGangsClause *OpenACCNumGangsClause::Create(const ASTContext &C, return new (Mem) OpenACCNumGangsClause(BeginLoc, LParenLoc, IntExprs, EndLoc); } +OpenACCTileClause *OpenACCTileClause::Create(const ASTContext &C, + SourceLocation BeginLoc, + SourceLocation LParenLoc, + ArrayRef SizeExprs, + SourceLocation EndLoc) { + void *Mem = + C.Allocate(OpenACCTileClause::totalSizeToAlloc(SizeExprs.size())); + return new (Mem) OpenACCTileClause(BeginLoc, LParenLoc, SizeExprs, EndLoc); +} + OpenACCPrivateClause *OpenACCPrivateClause::Create(const ASTContext &C, SourceLocation BeginLoc, SourceLocation LParenLoc, @@ -365,11 +391,16 @@ OpenACCSeqClause *OpenACCSeqClause::Create(const ASTContext &C, return new (Mem) OpenACCSeqClause(BeginLoc, EndLoc); } -OpenACCGangClause *OpenACCGangClause::Create(const ASTContext &C, - SourceLocation BeginLoc, - SourceLocation EndLoc) { - void *Mem = C.Allocate(sizeof(OpenACCGangClause)); - return new (Mem) OpenACCGangClause(BeginLoc, EndLoc); +OpenACCGangClause * +OpenACCGangClause::Create(const ASTContext &C, SourceLocation BeginLoc, + SourceLocation LParenLoc, + ArrayRef GangKinds, + ArrayRef IntExprs, SourceLocation EndLoc) { + void *Mem = + C.Allocate(OpenACCGangClause::totalSizeToAlloc( + IntExprs.size(), GangKinds.size())); + return new (Mem) + OpenACCGangClause(BeginLoc, LParenLoc, GangKinds, IntExprs, EndLoc); } OpenACCWorkerClause *OpenACCWorkerClause::Create(const ASTContext &C, @@ -420,6 +451,13 @@ void OpenACCClausePrinter::VisitNumGangsClause(const OpenACCNumGangsClause &C) { OS << ")"; } +void OpenACCClausePrinter::VisitTileClause(const OpenACCTileClause &C) { + OS << "tile("; + llvm::interleaveComma(C.getSizeExprs(), OS, + [&](const Expr *E) { printExpr(E); }); + OS << ")"; +} + void OpenACCClausePrinter::VisitNumWorkersClause( const OpenACCNumWorkersClause &C) { OS << "num_workers("; @@ -582,3 +620,21 @@ void OpenACCClausePrinter::VisitCollapseClause(const OpenACCCollapseClause &C) { printExpr(C.getLoopCount()); OS << ")"; } + +void OpenACCClausePrinter::VisitGangClause(const OpenACCGangClause &C) { + OS << "gang"; + + if (C.getNumExprs() > 0) { + OS << "("; + bool first = true; + for (unsigned I = 0; I < C.getNumExprs(); ++I) { + if (!first) + OS << ", "; + first = false; + + OS << C.getExpr(I).first << ": "; + printExpr(C.getExpr(I).second); + } + OS << ")"; + } +} diff --git a/clang/lib/AST/OpenMPClause.cpp b/clang/lib/AST/OpenMPClause.cpp index eb15aa8440690..985c844362d95 100644 --- a/clang/lib/AST/OpenMPClause.cpp +++ b/clang/lib/AST/OpenMPClause.cpp @@ -971,6 +971,25 @@ OMPSizesClause *OMPSizesClause::CreateEmpty(const ASTContext &C, return new (Mem) OMPSizesClause(NumSizes); } +OMPPermutationClause *OMPPermutationClause::Create(const ASTContext &C, + SourceLocation StartLoc, + SourceLocation LParenLoc, + SourceLocation EndLoc, + ArrayRef Args) { + OMPPermutationClause *Clause = CreateEmpty(C, Args.size()); + Clause->setLocStart(StartLoc); + Clause->setLParenLoc(LParenLoc); + Clause->setLocEnd(EndLoc); + Clause->setArgRefs(Args); + return Clause; +} + +OMPPermutationClause *OMPPermutationClause::CreateEmpty(const ASTContext &C, + unsigned NumLoops) { + void *Mem = C.Allocate(totalSizeToAlloc(NumLoops)); + return new (Mem) OMPPermutationClause(NumLoops); +} + OMPFullClause *OMPFullClause::Create(const ASTContext &C, SourceLocation StartLoc, SourceLocation EndLoc) { @@ -1841,6 +1860,14 @@ void OMPClausePrinter::VisitOMPSizesClause(OMPSizesClause *Node) { OS << ")"; } +void OMPClausePrinter::VisitOMPPermutationClause(OMPPermutationClause *Node) { + OS << "permutation("; + llvm::interleaveComma(Node->getArgsRefs(), OS, [&](const Expr *E) { + E->printPretty(OS, nullptr, Policy, 0); + }); + OS << ")"; +} + void OMPClausePrinter::VisitOMPFullClause(OMPFullClause *Node) { OS << "full"; } void OMPClausePrinter::VisitOMPPartialClause(OMPPartialClause *Node) { diff --git a/clang/lib/AST/StmtCXX.cpp b/clang/lib/AST/StmtCXX.cpp index 0d6fc848f7396..e7b86e2ca8b9b 100644 --- a/clang/lib/AST/StmtCXX.cpp +++ b/clang/lib/AST/StmtCXX.cpp @@ -11,8 +11,11 @@ //===----------------------------------------------------------------------===// #include "clang/AST/StmtCXX.h" - #include "clang/AST/ASTContext.h" +#include "clang/AST/Attr.h" +#include "clang/AST/DeclCXX.h" +#include "clang/Lex/Preprocessor.h" +#include "llvm/ADT/StringExtras.h" using namespace clang; @@ -126,3 +129,88 @@ CoroutineBodyStmt::CoroutineBodyStmt(CoroutineBodyStmt::CtorArgs const &Args) std::copy(Args.ParamMoves.begin(), Args.ParamMoves.end(), const_cast(getParamMoves().data())); } + +ContractStmt *ContractStmt::CreateEmpty(const ASTContext &C, ContractKind Kind, + bool HasResultName, unsigned NumAttrs) { + void *Mem = C.Allocate( + totalSizeToAlloc(1 + HasResultName, NumAttrs), + alignof(ContractStmt)); + return new (Mem) ContractStmt(EmptyShell(), Kind, HasResultName); +} + +ContractStmt *ContractStmt::Create(const ASTContext &C, ContractKind Kind, + SourceLocation KeywordLoc, Expr *Condition, + DeclStmt *ResultNameDecl, + ArrayRef Attrs) { + assert((ResultNameDecl == nullptr || Kind == ContractKind::Post) && + "Only a postcondition can have a result name declaration"); + void *Mem = C.Allocate(totalSizeToAlloc( + 1 + (ResultNameDecl != nullptr), Attrs.size()), + alignof(ContractStmt)); + return new (Mem) + ContractStmt(Kind, KeywordLoc, Condition, ResultNameDecl, Attrs); +} + +ResultNameDecl *ContractStmt::getResultName() const { + if (!hasResultName()) + return nullptr; + DeclStmt* D = getResultNameDeclStmt(); + assert(D); + return cast(D->getSingleDecl()); +} + +std::string ContractStmt::getMessage(const clang::ASTContext &Ctx) const { + if (auto *A = getAttrAs()) { + if (A->getIncludeSourceText()) { + return llvm::join_items("", getSourceText(Ctx), ": \"", A->getMessage(), + '"'); + } + return llvm::join_items("", '"', A->getMessage(), '"'); + } + return getSourceText(Ctx); +} + +std::string ContractStmt::getSourceText(const ASTContext &Ctx) const { + auto &SM = Ctx.getSourceManager(); + auto Begin = hasResultName() ? getResultName()->getBeginLoc() + : getCond()->getBeginLoc(); + auto End = getCond()->getEndLoc(); + CharSourceRange ExprRange = Lexer::getAsCharRange( + SM.getExpansionRange(SourceRange(Begin, End)), SM, Ctx.getLangOpts()); + std::string AssertStr = + Lexer::getSourceText(ExprRange, SM, Ctx.getLangOpts()).str(); + return AssertStr; +} + +StringRef ContractStmt::ContractKindAsString(ContractKind K) { + switch (K) { + case ContractKind::Assert: + return "contract_assert"; + case ContractKind::Pre: + return "pre"; + case ContractKind::Post: + return "post"; + } + llvm_unreachable("Unknown contract kind"); +} + +StringRef ContractStmt::SemanticAsString(ContractEvaluationSemantic Sem) { + switch (Sem) { + case ContractEvaluationSemantic::Ignore: + return "ignore"; + case ContractEvaluationSemantic::Observe: + return "observe"; + case ContractEvaluationSemantic::Enforce: + return "enforce"; + case ContractEvaluationSemantic::QuickEnforce: + return "quick_enforce"; + } + llvm_unreachable("Unknown contract kind"); +} + +ContractEvaluationSemantic +ContractStmt::getSemantic(const ASTContext &Ctx) const { + if (auto *A = getAttrAs()) + return Ctx.getLangOpts().ContractOpts.getSemanticForGroup(A->getGroup()); + return Ctx.getLangOpts().ContractOpts.DefaultSemantic; +} diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp index e1b5bec7a50d0..4a3231eead4ad 100644 --- a/clang/lib/AST/StmtPrinter.cpp +++ b/clang/lib/AST/StmtPrinter.cpp @@ -1309,6 +1309,10 @@ void StmtPrinter::VisitPredefinedExpr(PredefinedExpr *Node) { OS << PredefinedExpr::getIdentKindName(Node->getIdentKind()); } +void StmtPrinter::VisitOpenACCAsteriskSizeExpr(OpenACCAsteriskSizeExpr *Node) { + OS << '*'; +} + void StmtPrinter::VisitCharacterLiteral(CharacterLiteral *Node) { CharacterLiteral::print(Node->getValue(), Node->getKind(), OS); } @@ -2639,6 +2643,41 @@ void StmtPrinter::VisitCoyieldExpr(CoyieldExpr *S) { PrintExpr(S->getOperand()); } +// C++ contracts + +void StmtPrinter::VisitContractStmt(ContractStmt *Node) { + const char *Keyword = [=]() { + switch (Node->getContractKind()) { + case ContractKind::Assert: + return "contract_assert"; + case ContractKind::Pre: + return "pre"; + case ContractKind::Post: + return "post"; + } + llvm_unreachable("unhandled case"); + }(); + + // Print the contract keyword... + OS << Keyword; + + // Then any attributes... + // FIXME: We assume any attributes appear in this position rather than at the + // start of the statement. + llvm::ArrayRef Attrs = Node->getAttrs(); + for (const auto *Attr : Attrs) { + OS << " "; + Attr->printPretty(OS, Policy); + if (Attr == Attrs.back()) + OS << " "; + } + OS << "("; + PrintExpr(Node->getCond()); + OS << ")"; + if (Node->getContractKind() == ContractKind::Assert) + OS << ";"; +} + // Obj-C void StmtPrinter::VisitObjCStringLiteral(ObjCStringLiteral *Node) { diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp index c3812844ab8a3..78e0febe294b6 100644 --- a/clang/lib/AST/StmtProfile.cpp +++ b/clang/lib/AST/StmtProfile.cpp @@ -132,6 +132,13 @@ namespace { return; } + // FIXME(EricWF): Temporary hack to get the branch in better shape until I can merge the more correct + // fix from the other branch. + if (auto *RND = dyn_cast(D)) { + VisitType(RND->getType()); + return; + } + if (const TemplateTypeParmDecl *TTP = dyn_cast(D)) { ID.AddInteger(TTP->getDepth()); @@ -493,6 +500,13 @@ void OMPClauseProfiler::VisitOMPSizesClause(const OMPSizesClause *C) { Profiler->VisitExpr(E); } +void OMPClauseProfiler::VisitOMPPermutationClause( + const OMPPermutationClause *C) { + for (Expr *E : C->getArgsRefs()) + if (E) + Profiler->VisitExpr(E); +} + void OMPClauseProfiler::VisitOMPFullClause(const OMPFullClause *C) {} void OMPClauseProfiler::VisitOMPPartialClause(const OMPPartialClause *C) { @@ -1360,6 +1374,11 @@ void StmtProfiler::VisitPredefinedExpr(const PredefinedExpr *S) { ID.AddInteger(llvm::to_underlying(S->getIdentKind())); } +void StmtProfiler::VisitOpenACCAsteriskSizeExpr( + const OpenACCAsteriskSizeExpr *S) { + VisitExpr(S); +} + void StmtProfiler::VisitIntegerLiteral(const IntegerLiteral *S) { VisitExpr(S); S->getValue().Profile(ID); @@ -2326,6 +2345,8 @@ void StmtProfiler::VisitCoyieldExpr(const CoyieldExpr *S) { VisitExpr(S); } +void StmtProfiler::VisitContractStmt(const ContractStmt *S) { VisitStmt(S); } + void StmtProfiler::VisitOpaqueValueExpr(const OpaqueValueExpr *E) { VisitExpr(E); } @@ -2552,6 +2573,11 @@ void OpenACCClauseProfiler::VisitNumGangsClause( Profiler.VisitStmt(E); } +void OpenACCClauseProfiler::VisitTileClause(const OpenACCTileClause &Clause) { + for (auto *E : Clause.getSizeExprs()) + Profiler.VisitStmt(E); +} + void OpenACCClauseProfiler::VisitNumWorkersClause( const OpenACCNumWorkersClause &Clause) { assert(Clause.hasIntExpr() && "num_workers clause requires a valid int expr"); @@ -2629,6 +2655,12 @@ void OpenACCClauseProfiler::VisitIndependentClause( void OpenACCClauseProfiler::VisitSeqClause(const OpenACCSeqClause &Clause) {} +void OpenACCClauseProfiler::VisitGangClause(const OpenACCGangClause &Clause) { + for (unsigned I = 0; I < Clause.getNumExprs(); ++I) { + Profiler.VisitStmt(Clause.getExpr(I).second); + } +} + void OpenACCClauseProfiler::VisitReductionClause( const OpenACCReductionClause &Clause) { for (auto *E : Clause.getVarList()) diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp index 8a74159c7c93e..d5a917a9f66fe 100644 --- a/clang/lib/AST/TextNodeDumper.cpp +++ b/clang/lib/AST/TextNodeDumper.cpp @@ -381,6 +381,11 @@ void TextNodeDumper::Visit(const OMPClause *C) { OS << " "; } +void TextNodeDumper::VisitOpenACCAsteriskSizeExpr( + const OpenACCAsteriskSizeExpr *E) { + // Nothing to do here, only location exists, and that is printed elsewhere. +} + void TextNodeDumper::Visit(const OpenACCClause *C) { if (!C) { ColorScope Color(OS, ShowColors, NullColor); @@ -414,11 +419,23 @@ void TextNodeDumper::Visit(const OpenACCClause *C) { case OpenACCClauseKind::Private: case OpenACCClauseKind::Self: case OpenACCClauseKind::Seq: + case OpenACCClauseKind::Tile: case OpenACCClauseKind::VectorLength: // The condition expression will be printed as a part of the 'children', // but print 'clause' here so it is clear what is happening from the dump. OS << " clause"; break; + case OpenACCClauseKind::Gang: { + OS << " clause"; + // print the list of all GangKinds, so that there is some sort of + // relationship to the expressions listed afterwards. + auto *GC = cast(C); + + for (unsigned I = 0; I < GC->getNumExprs(); ++I) { + OS << " " << GC->getExpr(I).first; + } + break; + } case OpenACCClauseKind::Collapse: OS << " clause"; if (cast(C)->hasForce()) @@ -2303,6 +2320,17 @@ void TextNodeDumper::VisitBindingDecl(const BindingDecl *D) { dumpType(D->getType()); } +void TextNodeDumper::VisitResultNameDecl(const clang::ResultNameDecl *D) { + dumpName(D); + dumpType(D->getType()); + if (!D->isCanonicalResultName()) { + OS << " canonical "; + dumpPointer(D->getCanonicalResultName()); + } else { + dumpPointer(D); + } +} + void TextNodeDumper::VisitCapturedDecl(const CapturedDecl *D) { if (D->isNothrow()) OS << " nothrow"; @@ -2917,6 +2945,38 @@ void TextNodeDumper::VisitEmbedExpr(const EmbedExpr *S) { AddChild("number of elements", [=] { OS << S->getDataElementCount(); }); } + +void TextNodeDumper::VisitContractStmt(const ContractStmt *S) { + VisitStmt(S); + switch (S->getContractKind()) { + case ContractKind::Post: + OS << " post"; + break; + case ContractKind::Pre: + OS << " pre"; + break; + case ContractKind::Assert: + OS << " contract_assert"; + break; + } + if (Context) { + switch (S->getSemantic(*Context)) { + case ContractEvaluationSemantic::Ignore: + OS << " ignore"; + break; + case ContractEvaluationSemantic::Enforce: + OS << " enforce"; + break; + case ContractEvaluationSemantic::QuickEnforce: + OS << " quick_enforce"; + break; + case ContractEvaluationSemantic::Observe: + OS << " observe"; + break; + } + } +} + void TextNodeDumper::VisitAtomicExpr(const AtomicExpr *AE) { OS << ' ' << AE->getOpAsString(); } diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index c703e43f12a9a..6f4958801cfe8 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -5117,8 +5117,6 @@ FunctionEffect::Kind FunctionEffect::oppositeKind() const { return Kind::Allocating; case Kind::Allocating: return Kind::NonAllocating; - case Kind::None: - return Kind::None; } llvm_unreachable("unknown effect kind"); } @@ -5133,53 +5131,41 @@ StringRef FunctionEffect::name() const { return "blocking"; case Kind::Allocating: return "allocating"; - case Kind::None: - return "(none)"; } llvm_unreachable("unknown effect kind"); } -bool FunctionEffect::canInferOnFunction(const Decl &Callee) const { +std::optional FunctionEffect::effectProhibitingInference( + const Decl &Callee, FunctionEffectKindSet CalleeFX) const { switch (kind()) { case Kind::NonAllocating: case Kind::NonBlocking: { - FunctionEffectsRef CalleeFX; - if (auto *FD = Callee.getAsFunction()) - CalleeFX = FD->getFunctionEffects(); - else if (auto *BD = dyn_cast(&Callee)) - CalleeFX = BD->getFunctionEffects(); - else - return false; - for (const FunctionEffectWithCondition &CalleeEC : CalleeFX) { + for (FunctionEffect Effect : CalleeFX) { // nonblocking/nonallocating cannot call allocating. - if (CalleeEC.Effect.kind() == Kind::Allocating) - return false; + if (Effect.kind() == Kind::Allocating) + return Effect; // nonblocking cannot call blocking. - if (kind() == Kind::NonBlocking && - CalleeEC.Effect.kind() == Kind::Blocking) - return false; + if (kind() == Kind::NonBlocking && Effect.kind() == Kind::Blocking) + return Effect; } - return true; + return std::nullopt; } case Kind::Allocating: case Kind::Blocking: - return false; - - case Kind::None: - assert(0 && "canInferOnFunction with None"); + assert(0 && "effectProhibitingInference with non-inferable effect kind"); break; } llvm_unreachable("unknown effect kind"); } bool FunctionEffect::shouldDiagnoseFunctionCall( - bool Direct, ArrayRef CalleeFX) const { + bool Direct, FunctionEffectKindSet CalleeFX) const { switch (kind()) { case Kind::NonAllocating: case Kind::NonBlocking: { const Kind CallerKind = kind(); - for (const auto &Effect : CalleeFX) { + for (FunctionEffect Effect : CalleeFX) { const Kind EK = Effect.kind(); // Does callee have same or stronger constraint? if (EK == CallerKind || @@ -5192,9 +5178,6 @@ bool FunctionEffect::shouldDiagnoseFunctionCall( case Kind::Allocating: case Kind::Blocking: return false; - case Kind::None: - assert(0 && "shouldDiagnoseFunctionCall with None"); - break; } llvm_unreachable("unknown effect kind"); } @@ -5300,21 +5283,24 @@ FunctionEffectSet FunctionEffectSet::getUnion(FunctionEffectsRef LHS, return Combined; } +namespace clang { + +raw_ostream &operator<<(raw_ostream &OS, + const FunctionEffectWithCondition &CFE) { + OS << CFE.Effect.name(); + if (Expr *E = CFE.Cond.getCondition()) { + OS << '('; + E->dump(); + OS << ')'; + } + return OS; +} + +} // namespace clang + LLVM_DUMP_METHOD void FunctionEffectsRef::dump(llvm::raw_ostream &OS) const { OS << "Effects{"; - bool First = true; - for (const auto &CFE : *this) { - if (!First) - OS << ", "; - else - First = false; - OS << CFE.Effect.name(); - if (Expr *E = CFE.Cond.getCondition()) { - OS << '('; - E->dump(); - OS << ')'; - } - } + llvm::interleaveComma(*this, OS); OS << "}"; } @@ -5322,6 +5308,12 @@ LLVM_DUMP_METHOD void FunctionEffectSet::dump(llvm::raw_ostream &OS) const { FunctionEffectsRef(*this).dump(OS); } +LLVM_DUMP_METHOD void FunctionEffectKindSet::dump(llvm::raw_ostream &OS) const { + OS << "Effects{"; + llvm::interleaveComma(*this, OS); + OS << "}"; +} + FunctionEffectsRef FunctionEffectsRef::create(ArrayRef FX, ArrayRef Conds) { diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp index ca75bb97c158e..008e87e7e5c14 100644 --- a/clang/lib/AST/TypePrinter.cpp +++ b/clang/lib/AST/TypePrinter.cpp @@ -1413,7 +1413,9 @@ void TypePrinter::AppendScope(DeclContext *DC, raw_ostream &OS, // Only suppress an inline namespace if the name has the same lookup // results in the enclosing namespace. - if (Policy.SuppressInlineNamespace && NS->isInline() && NameInScope && + if (Policy.SuppressInlineNamespace != + PrintingPolicy::SuppressInlineNamespaceMode::None && + NS->isInline() && NameInScope && NS->isRedundantInlineQualifierFor(NameInScope)) return AppendScope(DC->getParent(), OS, NameInScope); diff --git a/clang/lib/ASTMatchers/ASTMatchersInternal.cpp b/clang/lib/ASTMatchers/ASTMatchersInternal.cpp index 06309d327896b..0360c1427dab0 100644 --- a/clang/lib/ASTMatchers/ASTMatchersInternal.cpp +++ b/clang/lib/ASTMatchers/ASTMatchersInternal.cpp @@ -655,7 +655,9 @@ bool HasNameMatcher::matchesNodeFullSlow(const NamedDecl &Node) const { PrintingPolicy Policy = Node.getASTContext().getPrintingPolicy(); Policy.SuppressUnwrittenScope = SkipUnwritten; - Policy.SuppressInlineNamespace = SkipUnwritten; + Policy.SuppressInlineNamespace = + SkipUnwritten ? PrintingPolicy::SuppressInlineNamespaceMode::All + : PrintingPolicy::SuppressInlineNamespaceMode::None; Node.printQualifiedName(OS, Policy); const StringRef FullName = OS.str(); @@ -1104,5 +1106,11 @@ const internal::VariadicDynCastAllOfMatcher const internal::VariadicDynCastAllOfMatcher cxxDeductionGuideDecl; + +const internal::VariadicDynCastAllOfMatcher contractStmt; +const internal::VariadicDynCastAllOfMatcher resultNameDecl; +const internal::VariadicDynCastAllOfMatcher + contractSpecifierDecl; + } // end namespace ast_matchers } // end namespace clang diff --git a/clang/lib/Analysis/ExprMutationAnalyzer.cpp b/clang/lib/Analysis/ExprMutationAnalyzer.cpp index 6d726ae44104e..5a95ef36d0502 100644 --- a/clang/lib/Analysis/ExprMutationAnalyzer.cpp +++ b/clang/lib/Analysis/ExprMutationAnalyzer.cpp @@ -231,12 +231,11 @@ ExprMutationAnalyzer::Analyzer::findPointeeMutation(const Decl *Dec) { const Stmt *ExprMutationAnalyzer::Analyzer::findMutationMemoized( const Expr *Exp, llvm::ArrayRef Finders, Memoized::ResultMap &MemoizedResults) { - const auto Memoized = MemoizedResults.find(Exp); - if (Memoized != MemoizedResults.end()) + auto [Memoized, Inserted] = MemoizedResults.try_emplace(Exp); + if (!Inserted) return Memoized->second; // Assume Exp is not mutated before analyzing Exp. - MemoizedResults[Exp] = nullptr; if (isUnevaluated(Exp)) return nullptr; diff --git a/clang/lib/Analysis/ProgramPoint.cpp b/clang/lib/Analysis/ProgramPoint.cpp index 2a91749affd2a..768345c8425f0 100644 --- a/clang/lib/Analysis/ProgramPoint.cpp +++ b/clang/lib/Analysis/ProgramPoint.cpp @@ -157,7 +157,7 @@ void ProgramPoint::printJson(llvm::raw_ostream &Out, const char *NL) const { LHS->printJson(Out, nullptr, PP, AddQuotes); } else { Out << "null"; - } + } Out << ", \"rhs\": "; if (const Stmt *RHS = C->getRHS()) { diff --git a/clang/lib/Basic/CMakeLists.txt b/clang/lib/Basic/CMakeLists.txt index e7ebc8f191aa6..d02fd130e753a 100644 --- a/clang/lib/Basic/CMakeLists.txt +++ b/clang/lib/Basic/CMakeLists.txt @@ -61,6 +61,7 @@ add_clang_library(clangBasic CLWarnings.cpp CharInfo.cpp CodeGenOptions.cpp + ContractOptions.cpp Cuda.cpp DarwinSDKInfo.cpp Diagnostic.cpp diff --git a/clang/lib/Basic/ContractOptions.cpp b/clang/lib/Basic/ContractOptions.cpp new file mode 100644 index 0000000000000..fcf703ac5d69e --- /dev/null +++ b/clang/lib/Basic/ContractOptions.cpp @@ -0,0 +1,145 @@ +//===- Contracts.cpp - C Language Family Language Options -----------------===// +// +// 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 classes from ContractOptions.h +// +//===----------------------------------------------------------------------===// + +#include "clang/Basic/ContractOptions.h" +#include "llvm/ADT/Hashing.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/ADT/Twine.h" + +using namespace clang; + +void ContractOptions::setGroupSemantic(StringRef Key, + ContractEvaluationSemantic Value) { + assert(!Key.empty() && + "Set the default group semantic using DefaultSemantic"); + SemanticsByGroup[Key] = Value; +} + +ContractEvaluationSemantic +ContractOptions::getSemanticForGroup(StringRef Group) const { + while (!Group.empty()) { + if (auto Pos = SemanticsByGroup.find(Group); + Pos != SemanticsByGroup.end()) { + return Pos->second; + } + auto NewEnd = Group.rfind('.'); + if (NewEnd == StringRef::npos) + break; + Group = Group.substr(0, NewEnd); + } + return DefaultSemantic; +} + +static const char *semanticToString(ContractEvaluationSemantic Sem) { + switch (Sem) { + case ContractEvaluationSemantic::Ignore: + return "ignore"; + case ContractEvaluationSemantic::Enforce: + return "enforce"; + case ContractEvaluationSemantic::Observe: + return "observe"; + case ContractEvaluationSemantic::QuickEnforce: + return "quick_enforce"; + } + llvm_unreachable("unhandled ContractEvaluationSemantic"); +} + +std::vector ContractOptions::serializeContractGroupArgs() const { + std::vector ArgStrings; + + ArgStrings.reserve(SemanticsByGroup.size() + 1); + ArgStrings.push_back(semanticToString(DefaultSemantic)); + for (const auto &Group : this->SemanticsByGroup) { + ArgStrings.push_back(llvm::Twine(Group.first(), "=") + .concat(semanticToString(Group.second)) + .str()); + } + return ArgStrings; +} + +std::optional semanticFromString(StringRef Str) { + return llvm::StringSwitch>(Str) + .Case("ignore", ContractEvaluationSemantic::Ignore) + .Case("enforce", ContractEvaluationSemantic::Enforce) + .Case("observe", ContractEvaluationSemantic::Observe) + .Case("quick_enforce", ContractEvaluationSemantic::QuickEnforce) + .Default(std::nullopt); +} + +void ContractOptions::addUnparsedContractGroup( + StringRef Group, const ContractOptions::DiagnoseGroupFunc &Diagnoser) { + if (Group.empty()) + return Diagnoser(ContractGroupDiagnostic::Empty, "", ""); + + const bool ParseAsValueOnly = not Group.contains("="); + auto [Key, Value] = Group.split('='); + if (ParseAsValueOnly) { + assert(Value.empty() && "Value should be empty"); + std::swap(Key, Value); + } else { + if (!validateContractGroup(Key, Diagnoser)) + return; + } + + std::optional Sem = semanticFromString(Value); + if (!Sem) + return Diagnoser(ContractGroupDiagnostic::InvalidSemantic, Group, Value); + + if (ParseAsValueOnly) + DefaultSemantic = Sem.value(); + else + SemanticsByGroup[Key] = Sem.value(); +} + +void ContractOptions::parseContractGroups( + const std::vector &Groups, + const ContractOptions::DiagnoseGroupFunc &Diagnoser) { + + for (const auto &GroupStr : Groups) { + addUnparsedContractGroup(GroupStr, Diagnoser); + } +} + +bool ContractOptions::validateContractGroup( + llvm::StringRef GroupName, + const ContractOptions::DiagnoseGroupFunc &Diagnoser) { + using CGD = ContractGroupDiagnostic; + if (GroupName.empty()) { + Diagnoser(CGD::Empty, GroupName, ""); + return false; + } + + if (auto Pos = GroupName.find_first_not_of("abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "0123456789" + "_.-"); + Pos != StringRef::npos) { + Diagnoser(CGD::InvalidChar, GroupName, GroupName.substr(Pos, 1)); + return false; + } + + if (GroupName[0] == '.') { + Diagnoser(CGD::InvalidFirstChar, GroupName, "."); + return false; + } + if (GroupName.back() == '.') { + Diagnoser(CGD::InvalidLastChar, GroupName, "."); + return false; + } + // Diagnose empty subgroups. i.e. "a..b" + if (GroupName.find("..", 0) != StringRef::npos) { + Diagnoser(CGD::EmptySubGroup, GroupName, ".."); + return false; + } + return true; +} diff --git a/clang/lib/Basic/IdentifierTable.cpp b/clang/lib/Basic/IdentifierTable.cpp index c9c9d927a5902..8fe45930dd819 100644 --- a/clang/lib/Basic/IdentifierTable.cpp +++ b/clang/lib/Basic/IdentifierTable.cpp @@ -81,6 +81,7 @@ IdentifierTable::IdentifierTable(const LangOptions &LangOpts, // Constants for TokenKinds.def namespace { + enum TokenKey : unsigned { KEYC99 = 0x1, KEYCXX = 0x2, @@ -111,7 +112,8 @@ enum TokenKey : unsigned { KEYNOZOS = 0x4000000, KEYHLSL = 0x8000000, KEYFIXEDPOINT = 0x10000000, - KEYMAX = KEYFIXEDPOINT, // The maximum key + KEYCONTRACTS = 0x20000000, + KEYMAX = KEYCONTRACTS, // The maximum key KEYALLCXX = KEYCXX | KEYCXX11 | KEYCXX20, KEYALL = (KEYMAX | (KEYMAX - 1)) & ~KEYNOMS18 & ~KEYNOOPENCL & ~KEYNOZOS // KEYNOMS18, KEYNOOPENCL, KEYNOZOS are excluded. @@ -191,6 +193,8 @@ static KeywordStatus getKeywordStatusHelper(const LangOptions &LangOpts, return LangOpts.ZVector ? KS_Enabled : KS_Unknown; case KEYCOROUTINES: return LangOpts.Coroutines ? KS_Enabled : KS_Unknown; + case KEYCONTRACTS: + return LangOpts.Contracts ? KS_Enabled : KS_Unknown; case KEYMODULES: return KS_Unknown; case KEYOPENCLCXX: @@ -406,6 +410,9 @@ ReservedLiteralSuffixIdStatus IdentifierInfo::isReservedLiteralSuffixId() const { StringRef Name = getName(); + // Note: the diag::warn_deprecated_literal_operator_id diagnostic depends on + // this being the first check we do, so if this order changes, we have to fix + // that as well. if (Name[0] != '_') return ReservedLiteralSuffixIdStatus::NotStartsWithUnderscore; diff --git a/clang/lib/Basic/OpenMPKinds.cpp b/clang/lib/Basic/OpenMPKinds.cpp index 630a8898aa229..8d2460bc74fa3 100644 --- a/clang/lib/Basic/OpenMPKinds.cpp +++ b/clang/lib/Basic/OpenMPKinds.cpp @@ -188,6 +188,7 @@ unsigned clang::getOpenMPSimpleClauseType(OpenMPClauseKind Kind, StringRef Str, case OMPC_safelen: case OMPC_simdlen: case OMPC_sizes: + case OMPC_permutation: case OMPC_allocator: case OMPC_allocate: case OMPC_collapse: @@ -512,6 +513,7 @@ const char *clang::getOpenMPSimpleClauseTypeName(OpenMPClauseKind Kind, case OMPC_safelen: case OMPC_simdlen: case OMPC_sizes: + case OMPC_permutation: case OMPC_allocator: case OMPC_allocate: case OMPC_collapse: diff --git a/clang/lib/Basic/TargetID.cpp b/clang/lib/Basic/TargetID.cpp index fa1bfec2aacb9..b42d1f07013c2 100644 --- a/clang/lib/Basic/TargetID.cpp +++ b/clang/lib/Basic/TargetID.cpp @@ -92,11 +92,9 @@ parseTargetIDWithFormatCheckingOnly(llvm::StringRef TargetID, if (Sign != '+' && Sign != '-') return std::nullopt; bool IsOn = Sign == '+'; - auto Loc = FeatureMap->find(Feature); // Each feature can only show up at most once in target ID. - if (Loc != FeatureMap->end()) + if (!FeatureMap->try_emplace(Feature, IsOn).second) return std::nullopt; - (*FeatureMap)[Feature] = IsOn; Features = Splits.second; } return Processor; @@ -147,15 +145,15 @@ getConflictTargetIDCombination(const std::set &TargetIDs) { struct Info { llvm::StringRef TargetID; llvm::StringMap Features; + Info(llvm::StringRef TargetID, const llvm::StringMap &Features) + : TargetID(TargetID), Features(Features) {} }; llvm::StringMap FeatureMap; for (auto &&ID : TargetIDs) { llvm::StringMap Features; llvm::StringRef Proc = *parseTargetIDWithFormatCheckingOnly(ID, &Features); - auto Loc = FeatureMap.find(Proc); - if (Loc == FeatureMap.end()) - FeatureMap[Proc] = Info{ID, Features}; - else { + auto [Loc, Inserted] = FeatureMap.try_emplace(Proc, ID, Features); + if (!Inserted) { auto &ExistingFeatures = Loc->second.Features; if (llvm::any_of(Features, [&](auto &F) { return ExistingFeatures.count(F.first()) == 0; diff --git a/clang/lib/Basic/Targets/AArch64.cpp b/clang/lib/Basic/Targets/AArch64.cpp index 5f5dfcb722f9d..61889861c9c80 100644 --- a/clang/lib/Basic/Targets/AArch64.cpp +++ b/clang/lib/Basic/Targets/AArch64.cpp @@ -373,6 +373,12 @@ void AArch64TargetInfo::getTargetDefinesARMV95A(const LangOptions &Opts, getTargetDefinesARMV94A(Opts, Builder); } +void AArch64TargetInfo::getTargetDefinesARMV96A(const LangOptions &Opts, + MacroBuilder &Builder) const { + // Armv9.6-A does not have a v8.* equivalent, but is a superset of v9.5-A. + getTargetDefinesARMV95A(Opts, Builder); +} + void AArch64TargetInfo::getTargetDefines(const LangOptions &Opts, MacroBuilder &Builder) const { // Target identification. @@ -657,6 +663,8 @@ void AArch64TargetInfo::getTargetDefines(const LangOptions &Opts, getTargetDefinesARMV94A(Opts, Builder); else if (*ArchInfo == llvm::AArch64::ARMV9_5A) getTargetDefinesARMV95A(Opts, Builder); + else if (*ArchInfo == llvm::AArch64::ARMV9_6A) + getTargetDefinesARMV96A(Opts, Builder); // All of the __sync_(bool|val)_compare_and_swap_(1|2|4|8|16) builtins work. Builder.defineMacro("__GCC_HAVE_SYNC_COMPARE_AND_SWAP_1"); @@ -1044,6 +1052,9 @@ bool AArch64TargetInfo::handleTargetFeatures(std::vector &Features, if (Feature == "+v9.5a" && ArchInfo->Version < llvm::AArch64::ARMV9_5A.Version) ArchInfo = &llvm::AArch64::ARMV9_5A; + if (Feature == "+v9.6a" && + ArchInfo->Version < llvm::AArch64::ARMV9_6A.Version) + ArchInfo = &llvm::AArch64::ARMV9_6A; if (Feature == "+v8r") ArchInfo = &llvm::AArch64::ARMV8R; if (Feature == "+fullfp16") { diff --git a/clang/lib/Basic/Targets/AArch64.h b/clang/lib/Basic/Targets/AArch64.h index 526f7f30a3861..1226ce4d4355c 100644 --- a/clang/lib/Basic/Targets/AArch64.h +++ b/clang/lib/Basic/Targets/AArch64.h @@ -148,6 +148,8 @@ class LLVM_LIBRARY_VISIBILITY AArch64TargetInfo : public TargetInfo { MacroBuilder &Builder) const; void getTargetDefinesARMV95A(const LangOptions &Opts, MacroBuilder &Builder) const; + void getTargetDefinesARMV96A(const LangOptions &Opts, + MacroBuilder &Builder) const; void getTargetDefines(const LangOptions &Opts, MacroBuilder &Builder) const override; diff --git a/clang/lib/Basic/Targets/ARM.cpp b/clang/lib/Basic/Targets/ARM.cpp index 7423626d7c3cb..c56b8d9a44850 100644 --- a/clang/lib/Basic/Targets/ARM.cpp +++ b/clang/lib/Basic/Targets/ARM.cpp @@ -228,6 +228,8 @@ StringRef ARMTargetInfo::getCPUAttr() const { return "9_4A"; case llvm::ARM::ArchKind::ARMV9_5A: return "9_5A"; + case llvm::ARM::ArchKind::ARMV9_6A: + return "9_6A"; case llvm::ARM::ArchKind::ARMV8MBaseline: return "8M_BASE"; case llvm::ARM::ArchKind::ARMV8MMainline: @@ -891,6 +893,7 @@ void ARMTargetInfo::getTargetDefines(const LangOptions &Opts, case llvm::ARM::ArchKind::ARMV9_3A: case llvm::ARM::ArchKind::ARMV9_4A: case llvm::ARM::ArchKind::ARMV9_5A: + case llvm::ARM::ArchKind::ARMV9_6A: // Filter __arm_cdp, __arm_ldcl, __arm_stcl in arm_acle.h FeatureCoprocBF = FEATURE_COPROC_B1 | FEATURE_COPROC_B3; break; @@ -1060,6 +1063,7 @@ void ARMTargetInfo::getTargetDefines(const LangOptions &Opts, case llvm::ARM::ArchKind::ARMV9_3A: case llvm::ARM::ArchKind::ARMV9_4A: case llvm::ARM::ArchKind::ARMV9_5A: + case llvm::ARM::ArchKind::ARMV9_6A: getTargetDefinesARMV83A(Opts, Builder); break; } diff --git a/clang/lib/Basic/Targets/DirectX.h b/clang/lib/Basic/Targets/DirectX.h index cf7ea5e83503d..19b61252409b0 100644 --- a/clang/lib/Basic/Targets/DirectX.h +++ b/clang/lib/Basic/Targets/DirectX.h @@ -62,7 +62,7 @@ class LLVM_LIBRARY_VISIBILITY DirectXTargetInfo : public TargetInfo { PlatformName = llvm::Triple::getOSTypeName(Triple.getOS()); resetDataLayout("e-m:e-p:32:32-i1:32-i8:8-i16:16-i32:32-i64:64-f16:16-f32:" "32-f64:64-n8:16:32:64"); - TheCXXABI.set(TargetCXXABI::Microsoft); + TheCXXABI.set(TargetCXXABI::GenericItanium); } bool useFP16ConversionIntrinsics() const override { return false; } void getTargetDefines(const LangOptions &Opts, diff --git a/clang/lib/CIR/.clang-tidy b/clang/lib/CIR/.clang-tidy new file mode 100644 index 0000000000000..aaba4585494d6 --- /dev/null +++ b/clang/lib/CIR/.clang-tidy @@ -0,0 +1,62 @@ +InheritParentConfig: true +Checks: > + -misc-const-correctness, + -llvm-header-guard, + bugprone-argument-comment, + bugprone-assert-side-effect, + bugprone-branch-clone, + bugprone-copy-constructor-init, + bugprone-dangling-handle, + bugprone-dynamic-static-initializers, + bugprone-macro-parentheses, + bugprone-macro-repeated-side-effects, + bugprone-misplaced-widening-cast, + bugprone-move-forwarding-reference, + bugprone-multiple-statement-macro, + bugprone-suspicious-semicolon, + bugprone-swapped-arguments, + bugprone-terminating-continue, + bugprone-unused-raii, + bugprone-unused-return-value, + misc-redundant-expression, + misc-static-assert, + misc-unused-using-decls, + modernize-use-bool-literals, + modernize-loop-convert, + modernize-make-unique, + modernize-raw-string-literal, + modernize-use-equals-default, + modernize-use-default-member-init, + modernize-use-emplace, + modernize-use-nullptr, + modernize-use-override, + modernize-use-using, + performance-for-range-copy, + performance-implicit-conversion-in-loop, + performance-inefficient-algorithm, + performance-inefficient-vector-operation, + performance-move-const-arg, + performance-no-automatic-move, + performance-trivially-destructible, + performance-unnecessary-copy-initialization, + performance-unnecessary-value-param, + readability-avoid-const-params-in-decls, + readability-const-return-type, + readability-container-size-empty, + readability-identifier-naming, + readability-inconsistent-declaration-parameter-name, + readability-misleading-indentation, + readability-redundant-control-flow, + readability-redundant-smartptr-get, + readability-simplify-boolean-expr, + readability-simplify-subscript-expr, + readability-use-anyofallof + + +CheckOptions: + - key: readability-identifier-naming.MemberCase + value: camelBack + - key: readability-identifier-naming.ParameterCase + value: camelBack + - key: readability-identifier-naming.VariableCase + value: camelBack diff --git a/clang/lib/CIR/CMakeLists.txt b/clang/lib/CIR/CMakeLists.txt index d2ff200e0da5f..11cca734808df 100644 --- a/clang/lib/CIR/CMakeLists.txt +++ b/clang/lib/CIR/CMakeLists.txt @@ -2,3 +2,5 @@ include_directories(${LLVM_MAIN_SRC_DIR}/../mlir/include) include_directories(${CMAKE_BINARY_DIR}/tools/mlir/include) add_subdirectory(Dialect) +add_subdirectory(CodeGen) +add_subdirectory(FrontendAction) diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp new file mode 100644 index 0000000000000..95e62326939fc --- /dev/null +++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp @@ -0,0 +1,32 @@ +//===- CIRGenModule.cpp - Per-Module state for CIR generation -------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This is the internal per-translation-unit state used for CIR translation. +// +//===----------------------------------------------------------------------===// + +#include "CIRGenModule.h" + +#include "clang/AST/ASTContext.h" +#include "clang/AST/DeclBase.h" + +#include "mlir/IR/BuiltinOps.h" +#include "mlir/IR/Location.h" +#include "mlir/IR/MLIRContext.h" + +using namespace cir; +CIRGenModule::CIRGenModule(mlir::MLIRContext &context, + clang::ASTContext &astctx, + const clang::CodeGenOptions &cgo, + DiagnosticsEngine &diags) + : astCtx(astctx), langOpts(astctx.getLangOpts()), + theModule{mlir::ModuleOp::create(mlir::UnknownLoc())}, + target(astCtx.getTargetInfo()) {} + +// Emit code for a single top level declaration. +void CIRGenModule::buildTopLevelDecl(Decl *decl) {} diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h new file mode 100644 index 0000000000000..ab2a1d8864659 --- /dev/null +++ b/clang/lib/CIR/CodeGen/CIRGenModule.h @@ -0,0 +1,62 @@ +//===--- CIRGenModule.h - Per-Module state for CIR gen ----------*- 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 is the internal per-translation-unit state used for CIR translation. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_CIR_CODEGEN_CIRGENMODULE_H +#define LLVM_CLANG_LIB_CIR_CODEGEN_CIRGENMODULE_H + +#include "CIRGenTypeCache.h" + +#include "mlir/IR/BuiltinOps.h" +#include "mlir/IR/MLIRContext.h" + +namespace clang { +class ASTContext; +class CodeGenOptions; +class Decl; +class DiagnosticsEngine; +class LangOptions; +class TargetInfo; +} // namespace clang + +using namespace clang; +namespace cir { + +/// This class organizes the cross-function state that is used while generating +/// CIR code. +class CIRGenModule : public CIRGenTypeCache { + CIRGenModule(CIRGenModule &) = delete; + CIRGenModule &operator=(CIRGenModule &) = delete; + +public: + CIRGenModule(mlir::MLIRContext &context, clang::ASTContext &astctx, + const clang::CodeGenOptions &cgo, + clang::DiagnosticsEngine &diags); + + ~CIRGenModule() = default; + +private: + /// Hold Clang AST information. + clang::ASTContext &astCtx; + + const clang::LangOptions &langOpts; + + /// A "module" matches a c/cpp source file: containing a list of functions. + mlir::ModuleOp theModule; + + const clang::TargetInfo ⌖ + +public: + void buildTopLevelDecl(clang::Decl *decl); +}; +} // namespace cir + +#endif // LLVM_CLANG_LIB_CIR_CODEGEN_CIRGENMODULE_H diff --git a/clang/lib/CIR/CodeGen/CIRGenTypeCache.h b/clang/lib/CIR/CodeGen/CIRGenTypeCache.h new file mode 100644 index 0000000000000..6478e0a078099 --- /dev/null +++ b/clang/lib/CIR/CodeGen/CIRGenTypeCache.h @@ -0,0 +1,27 @@ +//===--- CIRGenTypeCache.h - Commonly used LLVM types and info -*- 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 structure provides a set of common types useful during CIR emission. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_CIR_CIRGENTYPECACHE_H +#define LLVM_CLANG_LIB_CIR_CIRGENTYPECACHE_H + +namespace cir { + +/// This structure provides a set of types that are commonly used +/// during IR emission. It's initialized once in CodeGenModule's +/// constructor and then copied around into new CIRGenFunction's. +struct CIRGenTypeCache { + CIRGenTypeCache() = default; +}; + +} // namespace cir + +#endif // LLVM_CLANG_LIB_CIR_CODEGEN_CIRGENTYPECACHE_H diff --git a/clang/lib/CIR/CodeGen/CIRGenerator.cpp b/clang/lib/CIR/CodeGen/CIRGenerator.cpp new file mode 100644 index 0000000000000..159355a99ece8 --- /dev/null +++ b/clang/lib/CIR/CodeGen/CIRGenerator.cpp @@ -0,0 +1,43 @@ +//===--- CIRGenerator.cpp - Emit CIR from ASTs ----------------------------===// +// +// 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 builds an AST and converts it to CIR. +// +//===----------------------------------------------------------------------===// + +#include "CIRGenModule.h" + +#include "clang/AST/DeclGroup.h" +#include "clang/CIR/CIRGenerator.h" + +using namespace cir; +using namespace clang; + +void CIRGenerator::anchor() {} + +CIRGenerator::CIRGenerator(clang::DiagnosticsEngine &diags, + llvm::IntrusiveRefCntPtr vfs, + const CodeGenOptions &cgo) + : diags(diags), fs(std::move(vfs)), codeGenOpts{cgo} {} +CIRGenerator::~CIRGenerator() = default; + +void CIRGenerator::Initialize(ASTContext &astCtx) { + using namespace llvm; + + this->astCtx = &astCtx; + + cgm = std::make_unique(*mlirCtx, astCtx, codeGenOpts, diags); +} + +bool CIRGenerator::HandleTopLevelDecl(DeclGroupRef group) { + + for (Decl *decl : group) + cgm->buildTopLevelDecl(decl); + + return true; +} diff --git a/clang/lib/CIR/CodeGen/CMakeLists.txt b/clang/lib/CIR/CodeGen/CMakeLists.txt new file mode 100644 index 0000000000000..17a3aabfbd7f0 --- /dev/null +++ b/clang/lib/CIR/CodeGen/CMakeLists.txt @@ -0,0 +1,23 @@ +set( + LLVM_LINK_COMPONENTS + Core + Support +) + +get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS) + +add_clang_library(clangCIR + CIRGenerator.cpp + CIRGenModule.cpp + + DEPENDS + MLIRCIR + ${dialect_libs} + + LINK_LIBS + clangAST + clangBasic + clangLex + ${dialect_libs} + MLIRCIR +) diff --git a/clang/lib/CIR/FrontendAction/CIRGenAction.cpp b/clang/lib/CIR/FrontendAction/CIRGenAction.cpp new file mode 100644 index 0000000000000..72b9fa0c13c59 --- /dev/null +++ b/clang/lib/CIR/FrontendAction/CIRGenAction.cpp @@ -0,0 +1,72 @@ +//===--- CIRGenAction.cpp - LLVM Code generation Frontend Action ---------===// +// +// 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 "clang/CIR/FrontendAction/CIRGenAction.h" +#include "clang/CIR/CIRGenerator.h" +#include "clang/Frontend/CompilerInstance.h" + +#include "mlir/IR/MLIRContext.h" +#include "mlir/IR/OwningOpRef.h" + +using namespace cir; +using namespace clang; + +namespace cir { + +class CIRGenConsumer : public clang::ASTConsumer { + + virtual void anchor(); + + std::unique_ptr OutputStream; + + IntrusiveRefCntPtr FS; + std::unique_ptr Gen; + +public: + CIRGenConsumer(CIRGenAction::OutputType Action, + DiagnosticsEngine &DiagnosticsEngine, + IntrusiveRefCntPtr VFS, + const HeaderSearchOptions &HeaderSearchOptions, + const CodeGenOptions &CodeGenOptions, + const TargetOptions &TargetOptions, + const LangOptions &LangOptions, + const FrontendOptions &FEOptions, + std::unique_ptr OS) + : OutputStream(std::move(OS)), FS(VFS), + Gen(std::make_unique(DiagnosticsEngine, std::move(VFS), + CodeGenOptions)) {} + + bool HandleTopLevelDecl(DeclGroupRef D) override { + Gen->HandleTopLevelDecl(D); + return true; + } +}; +} // namespace cir + +void CIRGenConsumer::anchor() {} + +CIRGenAction::CIRGenAction(OutputType Act, mlir::MLIRContext *MLIRCtx) + : MLIRCtx(MLIRCtx ? MLIRCtx : new mlir::MLIRContext), Action(Act) {} + +CIRGenAction::~CIRGenAction() { MLIRMod.release(); } + +std::unique_ptr +CIRGenAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { + std::unique_ptr Out = CI.takeOutputStream(); + + auto Result = std::make_unique( + Action, CI.getDiagnostics(), &CI.getVirtualFileSystem(), + CI.getHeaderSearchOpts(), CI.getCodeGenOpts(), CI.getTargetOpts(), + CI.getLangOpts(), CI.getFrontendOpts(), std::move(Out)); + + return Result; +} + +void EmitCIRAction::anchor() {} +EmitCIRAction::EmitCIRAction(mlir::MLIRContext *MLIRCtx) + : CIRGenAction(OutputType::EmitCIR, MLIRCtx) {} diff --git a/clang/lib/CIR/FrontendAction/CMakeLists.txt b/clang/lib/CIR/FrontendAction/CMakeLists.txt new file mode 100644 index 0000000000000..b0616ab5d64b0 --- /dev/null +++ b/clang/lib/CIR/FrontendAction/CMakeLists.txt @@ -0,0 +1,17 @@ +set(LLVM_LINK_COMPONENTS + Core + Support + ) + +get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS) + +add_clang_library(clangCIRFrontendAction + CIRGenAction.cpp + + LINK_LIBS + clangAST + clangFrontend + clangCIR + MLIRCIR + MLIRIR + ) diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp index 62c6a57e8b7c8..f018130807519 100644 --- a/clang/lib/CodeGen/BackendUtil.cpp +++ b/clang/lib/CodeGen/BackendUtil.cpp @@ -1324,7 +1324,8 @@ static void runThinLTOBackend( if (Error E = thinBackend(Conf, -1, AddStream, *M, *CombinedIndex, ImportList, ModuleToDefinedGVSummaries[M->getModuleIdentifier()], - /* ModuleMap */ nullptr, CGOpts.CmdArgs)) { + /*ModuleMap=*/nullptr, Conf.CodeGenOnly, + /*IRAddStream=*/nullptr, CGOpts.CmdArgs)) { handleAllErrors(std::move(E), [&](ErrorInfoBase &EIB) { errs() << "Error running ThinLTO backend: " << EIB.message() << '\n'; }); diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp index c864714182e01..465afd04740d8 100644 --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -13648,7 +13648,7 @@ Value *CodeGenFunction::EmitBPFBuiltinExpr(unsigned BuiltinID, Value *InfoKind = ConstantInt::get(Int64Ty, C->getSExtValue()); // Built the IR for the preserve_field_info intrinsic. - llvm::Function *FnGetFieldInfo = llvm::Intrinsic::getDeclaration( + llvm::Function *FnGetFieldInfo = llvm::Intrinsic::getOrInsertDeclaration( &CGM.getModule(), llvm::Intrinsic::bpf_preserve_field_info, {FieldAddr->getType()}); return Builder.CreateCall(FnGetFieldInfo, {FieldAddr, InfoKind}); @@ -13670,10 +13670,10 @@ Value *CodeGenFunction::EmitBPFBuiltinExpr(unsigned BuiltinID, llvm::Function *FnDecl; if (BuiltinID == BPF::BI__builtin_btf_type_id) - FnDecl = llvm::Intrinsic::getDeclaration( + FnDecl = llvm::Intrinsic::getOrInsertDeclaration( &CGM.getModule(), llvm::Intrinsic::bpf_btf_type_id, {}); else - FnDecl = llvm::Intrinsic::getDeclaration( + FnDecl = llvm::Intrinsic::getOrInsertDeclaration( &CGM.getModule(), llvm::Intrinsic::bpf_preserve_type_info, {}); CallInst *Fn = Builder.CreateCall(FnDecl, {SeqNumVal, FlagValue}); Fn->setMetadata(LLVMContext::MD_preserve_access_index, DbgInfo); @@ -13708,7 +13708,7 @@ Value *CodeGenFunction::EmitBPFBuiltinExpr(unsigned BuiltinID, Value *FlagValue = ConstantInt::get(Int64Ty, Flag->getSExtValue()); Value *SeqNumVal = ConstantInt::get(Int32Ty, BuiltinSeqNum++); - llvm::Function *IntrinsicFn = llvm::Intrinsic::getDeclaration( + llvm::Function *IntrinsicFn = llvm::Intrinsic::getOrInsertDeclaration( &CGM.getModule(), llvm::Intrinsic::bpf_preserve_enum_value, {}); CallInst *Fn = Builder.CreateCall(IntrinsicFn, {SeqNumVal, EnumStrVal, FlagValue}); @@ -18667,6 +18667,21 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID, IsUnsigned ? Intrinsic::dx_uclamp : Intrinsic::dx_clamp, ArrayRef{OpX, OpMin, OpMax}, nullptr, "dx.clamp"); } + case Builtin::BI__builtin_hlsl_cross: { + Value *Op0 = EmitScalarExpr(E->getArg(0)); + Value *Op1 = EmitScalarExpr(E->getArg(1)); + assert(E->getArg(0)->getType()->hasFloatingRepresentation() && + E->getArg(1)->getType()->hasFloatingRepresentation() && + "cross operands must have a float representation"); + // make sure each vector has exactly 3 elements + assert( + E->getArg(0)->getType()->getAs()->getNumElements() == 3 && + E->getArg(1)->getType()->getAs()->getNumElements() == 3 && + "input vectors must have 3 elements each"); + return Builder.CreateIntrinsic( + /*ReturnType=*/Op0->getType(), CGM.getHLSLRuntime().getCrossIntrinsic(), + ArrayRef{Op0, Op1}, nullptr, "hlsl.cross"); + } case Builtin::BI__builtin_hlsl_dot: { Value *Op0 = EmitScalarExpr(E->getArg(0)); Value *Op1 = EmitScalarExpr(E->getArg(1)); @@ -18740,6 +18755,16 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID, CGM.getHLSLRuntime().getNormalizeIntrinsic(), ArrayRef{X}, nullptr, "hlsl.normalize"); } + case Builtin::BI__builtin_hlsl_elementwise_degrees: { + Value *X = EmitScalarExpr(E->getArg(0)); + + assert(E->getArg(0)->getType()->hasFloatingRepresentation() && + "degree operand must have a float representation"); + + return Builder.CreateIntrinsic( + /*ReturnType=*/X->getType(), CGM.getHLSLRuntime().getDegreesIntrinsic(), + ArrayRef{X}, nullptr, "hlsl.degrees"); + } case Builtin::BI__builtin_hlsl_elementwise_frac: { Value *Op0 = EmitScalarExpr(E->getArg(0)); if (!E->getArg(0)->getType()->hasFloatingRepresentation()) @@ -18852,31 +18877,60 @@ case Builtin::BI__builtin_hlsl_elementwise_isinf: { ArrayRef{Op0, Op1}, nullptr, "hlsl.step"); } case Builtin::BI__builtin_hlsl_wave_get_lane_index: { - return EmitRuntimeCall(CGM.CreateRuntimeFunction( - llvm::FunctionType::get(IntTy, {}, false), "__hlsl_wave_get_lane_index", - {}, false, true)); + // We don't define a SPIR-V intrinsic, instead it is a SPIR-V built-in + // defined in SPIRVBuiltins.td. So instead we manually get the matching name + // for the DirectX intrinsic and the demangled builtin name + switch (CGM.getTarget().getTriple().getArch()) { + case llvm::Triple::dxil: + return EmitRuntimeCall(Intrinsic::getOrInsertDeclaration( + &CGM.getModule(), Intrinsic::dx_wave_getlaneindex)); + case llvm::Triple::spirv: + return EmitRuntimeCall(CGM.CreateRuntimeFunction( + llvm::FunctionType::get(IntTy, {}, false), + "__hlsl_wave_get_lane_index", {}, false, true)); + default: + llvm_unreachable( + "Intrinsic WaveGetLaneIndex not supported by target architecture"); + } } case Builtin::BI__builtin_hlsl_wave_is_first_lane: { Intrinsic::ID ID = CGM.getHLSLRuntime().getWaveIsFirstLaneIntrinsic(); - return EmitRuntimeCall(Intrinsic::getDeclaration(&CGM.getModule(), ID)); + return EmitRuntimeCall( + Intrinsic::getOrInsertDeclaration(&CGM.getModule(), ID)); } case Builtin::BI__builtin_hlsl_elementwise_sign: { - Value *Op0 = EmitScalarExpr(E->getArg(0)); + auto *Arg0 = E->getArg(0); + Value *Op0 = EmitScalarExpr(Arg0); llvm::Type *Xty = Op0->getType(); llvm::Type *retType = llvm::Type::getInt32Ty(this->getLLVMContext()); if (Xty->isVectorTy()) { - auto *XVecTy = E->getArg(0)->getType()->getAs(); + auto *XVecTy = Arg0->getType()->getAs(); retType = llvm::VectorType::get( retType, ElementCount::getFixed(XVecTy->getNumElements())); } - assert((E->getArg(0)->getType()->hasFloatingRepresentation() || - E->getArg(0)->getType()->hasSignedIntegerRepresentation()) && + assert((Arg0->getType()->hasFloatingRepresentation() || + Arg0->getType()->hasIntegerRepresentation()) && "sign operand must have a float or int representation"); + if (Arg0->getType()->hasUnsignedIntegerRepresentation()) { + Value *Cmp = Builder.CreateICmpEQ(Op0, ConstantInt::get(Xty, 0)); + return Builder.CreateSelect(Cmp, ConstantInt::get(retType, 0), + ConstantInt::get(retType, 1), "hlsl.sign"); + } + return Builder.CreateIntrinsic( retType, CGM.getHLSLRuntime().getSignIntrinsic(), ArrayRef{Op0}, nullptr, "hlsl.sign"); } + case Builtin::BI__builtin_hlsl_elementwise_radians: { + Value *Op0 = EmitScalarExpr(E->getArg(0)); + assert(E->getArg(0)->getType()->hasFloatingRepresentation() && + "radians operand must have a float representation"); + return Builder.CreateIntrinsic( + /*ReturnType=*/Op0->getType(), + CGM.getHLSLRuntime().getRadiansIntrinsic(), ArrayRef{Op0}, + nullptr, "hlsl.radians"); + } } return nullptr; } diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp index 4ae981e4013e9..499f3a9e8997c 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -29,6 +29,7 @@ #include "clang/Basic/TargetInfo.h" #include "clang/CodeGen/CGFunctionInfo.h" #include "clang/CodeGen/SwiftCallingConv.h" +#include "llvm/ADT/ScopeExit.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Analysis/ValueTracking.h" #include "llvm/IR/Assumptions.h" @@ -3806,12 +3807,6 @@ void CodeGenFunction::EmitFunctionEpilog(const CGFunctionInfo &FI, return; } - // Functions with no result always return void. - if (!ReturnValue.isValid()) { - Builder.CreateRetVoid(); - return; - } - llvm::DebugLoc RetDbgLoc; llvm::Value *RV = nullptr; QualType RetTy = FI.getReturnType(); @@ -3975,9 +3970,11 @@ void CodeGenFunction::EmitFunctionEpilog(const CGFunctionInfo &FI, if (ITy != nullptr && isa(RetTy.getCanonicalType())) RV = EmitCMSEClearRecord(RV, ITy, RetTy); } + EmitPostContracts(RV); EmitReturnValueCheck(RV); Ret = Builder.CreateRet(RV); } else { + EmitPostContracts(nullptr); Ret = Builder.CreateRetVoid(); } @@ -3985,6 +3982,36 @@ void CodeGenFunction::EmitFunctionEpilog(const CGFunctionInfo &FI, Ret->setDebugLoc(std::move(RetDbgLoc)); } +void CodeGenFunction::EmitPostContracts(llvm::Value *RV) { + SmallVector PostContracts; + const FunctionDecl *FD = dyn_cast_or_null(CurCodeDecl); + + if (!FD || !FD->hasContracts()) + return; + + ContractSpecifierDecl *CSD = FD->getContracts(); + assert(CSD); + + std::optional OVEStore; + std::optional OVEBind; + if (auto CRD = CSD->getCanonicalResultName(); CRD && RV) { + OVEStore.emplace(CRD->getLocation(), CRD->getType(), VK_LValue, OK_Ordinary, + nullptr); + // llvm::Value *SLocPtr = Builder.CreateLoad(ReturnLocation, + // "return.sloc.load"); + OVEBind.emplace(*this, &OVEStore.value(), + MakeAddrLValue(ReturnValue, CRD->getType())); + } + + disableDebugInfo(); + auto Reenabler = llvm::make_scope_exit([this]() { enableDebugInfo(); }); + for (auto *CA : FD->postconditions()) { + // FIXME(EricWF): We're disabling + EmitStmt(CA); + } + enableDebugInfo(); +} + void CodeGenFunction::EmitReturnValueCheck(llvm::Value *RV) { // A current decl may not be available when emitting vtable thunks. if (!CurCodeDecl) diff --git a/clang/lib/CodeGen/CGContracts.cpp b/clang/lib/CodeGen/CGContracts.cpp new file mode 100644 index 0000000000000..ec69a33af0072 --- /dev/null +++ b/clang/lib/CodeGen/CGContracts.cpp @@ -0,0 +1,480 @@ +//===--- CGBlocks.cpp - Emit LLVM Code for declarations ---------*- 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 contains code to emit blocks. +// +//===----------------------------------------------------------------------===// + +#include "CGContracts.h" +#include "CGCXXABI.h" +#include "CGDebugInfo.h" +#include "CGObjCRuntime.h" +#include "CGOpenCLRuntime.h" +#include "CodeGenFunction.h" +#include "CodeGenModule.h" +#include "ConstantEmitter.h" +#include "TargetInfo.h" +#include "clang/AST/Attr.h" +#include "clang/AST/DeclObjC.h" +#include "clang/CodeGen/ConstantInitBuilder.h" +#include "llvm/ADT/ScopeExit.h" +#include "llvm/ADT/SmallSet.h" +#include "llvm/IR/DataLayout.h" +#include "llvm/IR/Module.h" +#include "llvm/Support/ScopedPrinter.h" +#include +#include +#include + +using namespace clang; +using namespace CodeGen; + +constexpr ContractEvaluationSemantic Enforce = + ContractEvaluationSemantic::Enforce; +constexpr ContractEvaluationSemantic QuickEnforce = + ContractEvaluationSemantic::QuickEnforce; +constexpr ContractEvaluationSemantic Observe = + ContractEvaluationSemantic::Observe; +constexpr ContractEvaluationSemantic Ignore = + ContractEvaluationSemantic::Ignore; + +constexpr ContractViolationDetection PredicateFailed = + ContractViolationDetection::PredicateFailed; +constexpr ContractViolationDetection ExceptionRaised = + ContractViolationDetection::ExceptionRaised; + +namespace clang::CodeGen { + +enum ContractCheckpoint { + EmittingContract, + EmittingTryBody, + EmittingCatchBody, +}; + +enum ContractEmissionStyle { + /// Emit the contract violation as an inline basic block immediately following + /// the predicate. The basic block is not shared by other contracts. + Inline, + + /// Emit a single shared contract violation handler per-function. + /// This only works when exceptions are disabled, otherwise the violation + /// handler + /// may throw from the violation handler. + Shared +}; + +template +static llvm::Constant *CreateConstantInt(CodeGenFunction &CGF, T Sem) { + static_assert(std::is_same_v || + std::is_same_v); + return llvm::ConstantInt::get(CGF.IntTy, (int)Sem); +} + +struct CurrentContractInfo { + + const ContractStmt *Contract; + ContractEmissionStyle Style; + ContractCheckpoint Checkpoint = EmittingContract; + ContractEvaluationSemantic Semantic; + + llvm::BasicBlock *Violation = nullptr; + llvm::BasicBlock *End = nullptr; + + llvm::Constant *ViolationInfoGV = nullptr; + Address EHPredicateStore = Address::invalid(); +}; + +// A contract enforce block is a block used to create and call the violation +// handler for contracts set to 'enforce'. Such contracts never return after +// reporting a violation. +// +// It is used to create a single +// block that can be used to handle all contract violations in a function. +// +// The block is created lazily, and is only created if a contract is emitted +// with an enforce semantic. +struct SharedEnforceBlock { + static SharedEnforceBlock Create(CodeGenFunction &CGF) { + SharedEnforceBlock This; + + + auto SavedIP = CGF.Builder.saveAndClearIP(); + + This.Block = CGF.createBasicBlock("contract.violation.handler"); + CGF.Builder.SetInsertPoint(This.Block); + This.IncomingPHI = CGF.Builder.CreatePHI(CGF.VoidPtrTy, 4); + + CGF.EmitHandleContractViolationCall(CreateConstantInt(CGF, Enforce), + CreateConstantInt(CGF, PredicateFailed), + This.IncomingPHI, + /*IsNoReturn=*/true); + CGF.Builder.CreateUnreachable(); + CGF.Builder.ClearInsertionPoint(); + CGF.Builder.restoreIP(SavedIP); + + return This; + } + + llvm::BasicBlock *Block = nullptr; + llvm::PHINode *IncomingPHI = nullptr; + +private: + SharedEnforceBlock() = default; +}; + +static void CreateTrap(CodeGenFunction &CGF) { + auto &Builder = CGF.Builder; + llvm::CallInst *TrapCall = CGF.EmitTrapCall(llvm::Intrinsic::trap); + TrapCall->setDoesNotReturn(); + TrapCall->setDoesNotThrow(); + Builder.CreateUnreachable(); + Builder.ClearInsertionPoint(); +} + +static llvm::BasicBlock *CreateTrapBlock(CodeGenFunction &CGF) { + auto &Builder = CGF.Builder; + CGBuilderTy::InsertPoint SavedIP = Builder.saveAndClearIP(); + // Set up the terminate handler. This block is inserted at the very + // end of the function by FinishFunction. + llvm::BasicBlock *ContractViolationTrapBlock = + CGF.createBasicBlock("contract.violation.trap.handler"); + Builder.SetInsertPoint(ContractViolationTrapBlock); + + CreateTrap(CGF); + + Builder.restoreIP(SavedIP); + return ContractViolationTrapBlock; +} + +struct SharedTrapBlock { + static SharedTrapBlock Create(CodeGenFunction &CGF) { + SharedTrapBlock This; + This.Block = CreateTrapBlock(CGF); + return This; + } + + llvm::BasicBlock *Block = nullptr; + +private: + SharedTrapBlock() = default; +}; + +struct CGContractData { + std::optional EnforceBlock; + std::optional TrapBlock; + std::optional CurContract; + + SharedTrapBlock &GetSharedTrapBlock(CodeGenFunction &CGF) { + if (!TrapBlock) + TrapBlock = SharedTrapBlock::Create(CGF); + return *TrapBlock; + } + + SharedEnforceBlock &GetSharedEnforceBlock(CodeGenFunction &CGF) { + if (!EnforceBlock) + EnforceBlock = SharedEnforceBlock::Create(CGF); + return *EnforceBlock; + } +}; + +} // namespace clang::CodeGen + +CurrentContractInfo *CodeGenFunction::CurContract() { + return ContractData->CurContract ? &ContractData->CurContract.value() + : nullptr; +} + +struct CurrentContractRAII { + CurrentContractRAII(CodeGenFunction &CGF, CurrentContractInfo CurContract) + : CGF(CGF) { + assert(!CGF.ContractData->CurContract); + CGF.ContractData->CurContract.emplace(std::move(CurContract)); + } + ~CurrentContractRAII() { + assert(CGF.ContractData->CurContract); + CGF.ContractData->CurContract.reset(); + } + CodeGenFunction &CGF; +}; + +CGContractData *CGContractDataDeleter::Create() { return new CGContractData(); } +void CGContractDataDeleter::operator()(CGContractData *Data) const { + + if (Data) + delete Data; +} + +llvm::BasicBlock * +CodeGenFunction::GetSharedContractViolationEnforceBlock(bool Create) { + if (!ContractData->EnforceBlock && !Create) + return nullptr; + return ContractData->GetSharedEnforceBlock(*this).Block; +} + +llvm::BasicBlock * +CodeGenFunction::GetSharedContractViolationTrapBlock(bool Create) { + if (!ContractData->TrapBlock && !Create) + return nullptr; + return ContractData->GetSharedTrapBlock(*this).Block; +} + +void CodeGenFunction::EmitHandleContractViolationCall( + llvm::Constant *EvalSemantic, llvm::Constant *DetectionMode, + llvm::Value *ViolationInfoGV, bool IsNoReturn) { + auto &Ctx = getContext(); + + CanQualType ArgTypes[3] = {Ctx.UnsignedIntTy, Ctx.UnsignedIntTy, + Ctx.VoidPtrTy}; + + const CGFunctionInfo &VFuncInfo = + CGM.getTypes().arrangeBuiltinFunctionDeclaration(getContext().VoidTy, + ArgTypes); + + StringRef TargetFuncName = "__handle_contract_violation_v3"; + llvm::FunctionType *VFTy = CGM.getTypes().GetFunctionType(VFuncInfo); + llvm::FunctionCallee VFunc = CGM.CreateRuntimeFunction(VFTy, TargetFuncName); + + if (IsNoReturn) { + llvm::Value *Args[3] = {EvalSemantic, DetectionMode, ViolationInfoGV}; + EmitNoreturnRuntimeCallOrInvoke(VFunc, Args); + Builder.ClearInsertionPoint(); + } else { + CallArgList Args; + Args.add(RValue::get(EvalSemantic), getContext().UnsignedIntTy); + Args.add(RValue::get(DetectionMode), getContext().UnsignedIntTy); + Args.add(RValue::get(ViolationInfoGV), getContext().VoidPtrTy); + EmitCall(VFuncInfo, CGCallee::forDirect(VFunc), ReturnValueSlot(), Args); + } +} + +// Check if function can throw based on prototype noexcept, also works for +// destructors which are implicitly noexcept but can be marked noexcept(false). +static bool FunctionCanThrow(const FunctionDecl *D) { + const auto *Proto = D->getType()->getAs(); + if (!Proto) { + // Function proto is not found, we conservatively assume throwing. + return true; + } + return !isNoexceptExceptionSpec(Proto->getExceptionSpecType()) || + Proto->canThrow() != CT_Cannot; +} + +static bool StmtCanThrow(const Stmt *S) { + if (const auto *CE = dyn_cast(S)) { + const auto *Callee = CE->getDirectCallee(); + if (!Callee) + // We don't have direct callee. Conservatively assume throwing. + return true; + + if (FunctionCanThrow(Callee)) + return true; + + // Fall through to visit the children. + } + + if (isa(S)) + return true; + + if (const auto *TE = dyn_cast(S)) { + // Special handling of CXXBindTemporaryExpr here as calling of Dtor of the + // temporary is not part of `children()` as covered in the fall through. + // We need to mark entire statement as throwing if the destructor of the + // temporary throws. + const auto *Dtor = TE->getTemporary()->getDestructor(); + if (FunctionCanThrow(Dtor)) + return true; + + // Fall through to visit the children. + } + + for (const auto *child : S->children()) + if (StmtCanThrow(child)) + return true; + + return false; +} + +// Emit the contract expression. +void CodeGenFunction::EmitContractStmt(const ContractStmt &S) { + assert(!CurContract() || CurContract()->Contract == &S); + + if (!CurContract()) { + // FIXME(EricWF): Remove this. It's a hack to prevent crashing. + + EmitContractStmtAsFullStmt(S); + + } else if (CurContract()->Checkpoint == EmittingTryBody) { + return EmitContractStmtAsTryBody(S); + } else if (CurContract()->Checkpoint == EmittingCatchBody) { + return EmitContractStmtAsCatchBody(S); + } else { + llvm_unreachable("Invalid checkpoint"); + } +} + +// FIXME(EricWF): Do I really need this? +void CodeGenFunction::EmitContractStmtAsTryBody(const ContractStmt &S) { + assert(CurContract() && CurContract()->Contract == &S && + CurContract()->Checkpoint == EmittingTryBody); + Builder.CreateStore(EmitScalarExpr(S.getCond()), + CurContract()->EHPredicateStore); + +} + +void CodeGenFunction::EmitContractStmtAsCatchBody(const ContractStmt &S) { + assert(CurContract() && CurContract()->Contract == &S && + CurContract()->Checkpoint == EmittingCatchBody); + auto CurInfo = CurContract(); + + if (CurInfo->Semantic == Enforce || CurInfo->Semantic == Observe) { + // We have to emit the contract violation block inside the catch block so + // that the handler can see the exception via std::current_exception + EmitHandleContractViolationCall( + CreateConstantInt(*this, CurInfo->Semantic), + CreateConstantInt(*this, ExceptionRaised), CurInfo->ViolationInfoGV, + /*IsNoReturn=*/CurInfo->Semantic == Enforce); + } else if (CurInfo->Semantic == QuickEnforce) { + CreateTrap(*this); + } else { + llvm_unreachable("Unhandled semantic"); + } +} + +static CXXTryStmt *BuildTryCatch(const ContractStmt &S, CodeGenFunction &CGF) { + auto &Ctx = CGF.getContext(); + auto Loc = S.getCond()->getExprLoc(); + + llvm::SmallVector BodyStmts; + BodyStmts.push_back(const_cast(&S)); + + // FIXME(EricWF): THIS IS A TERRIBLE HACK. + // In order to emit the contract assertion violation in the catch block + // we add the current statement to a dummy handler, and then detect + // when we're inside that dummy handler to only emit the violation + // + // This should have some other representation, but I don't want to eagerly + // build all these nodes in the AST. + + auto *CatchStmt = + CompoundStmt::Create(Ctx, BodyStmts, FPOptionsOverride(), Loc, Loc); + auto *Catch = + new (Ctx) CXXCatchStmt(Loc, /*exDecl=*/nullptr, /*block=*/CatchStmt); + auto *TryBody = + CompoundStmt::Create(Ctx, BodyStmts, FPOptionsOverride(), Loc, Loc); + return CXXTryStmt::Create(Ctx, Loc, TryBody, Catch); +} + +void CodeGenFunction::EmitContractStmtAsFullStmt(const ContractStmt &S) { + assert(CurContract() == nullptr); + // FIXME(EricWF): We recursively call EmitContractStmt to build the catch + // block that reports contract violations that have thrown. In order to do + // this without building additional AST nodes, use this Stmt as the body + // of the catch block, detecting when we're inside the catch block to only + // emit the violation. + + ContractEvaluationSemantic Semantic = S.getSemantic(getContext()); + + // FIXME(EricWF): I think there's a lot more to do that simply this. + if (Semantic == Ignore) + return; + + const auto Style = [&]() { + switch (Semantic) { + case Enforce: { + if (!getLangOpts().Exceptions) + return Shared; + } + LLVM_FALLTHROUGH; + case Observe: + return Inline; + case QuickEnforce: + return Shared; + case ContractEvaluationSemantic::Ignore: + llvm_unreachable("unhandled semantic"); + } + }(); + + auto Violation = [&]() { + switch (Style) { + case Inline: + return createBasicBlock("contract.violation"); + case Shared: + assert(Semantic == Enforce || Semantic == QuickEnforce); + return Semantic == Enforce ? GetSharedContractViolationEnforceBlock() + : GetSharedContractViolationTrapBlock(); + } + }(); + + llvm::BasicBlock *End = createBasicBlock("contract.end"); + + llvm::Constant *ViolationInfo = nullptr; + if (Semantic != ContractEvaluationSemantic::QuickEnforce) { + ViolationInfo = CGM.GetAddrOfUnnamedGlobalConstantDecl( + getContext().BuildViolationObject( + &S, dyn_cast_or_null(CurFuncDecl)), "contract.loc") + .getPointer(); + } + + CurrentContractRAII CurContractRAII(*this, + {.Contract = &S, + .Style = Style, + .Checkpoint = EmittingContract, + .Semantic = Semantic, + .Violation = Violation, + .End = End, + .ViolationInfoGV = ViolationInfo}); + + llvm::Value *BranchOn; + if (getLangOpts().Exceptions && getLangOpts().ContractExceptions && StmtCanThrow(S.getCond())) { + + CurContract()->EHPredicateStore = CreateTempAlloca( + Builder.getInt1Ty(), CharUnits::One(), "contract.pred.value"); + // Set the initial value to true. If the contract throws, we'll see the true + // value after the catch block is done handling the exception. + Builder.CreateStore(Builder.getTrue(), CurContract()->EHPredicateStore); + + assert(Builder.GetInsertBlock()); + EnsureInsertPoint(); + + auto *Try = BuildTryCatch(S, *this); + EnterCXXTryStmt(*Try); + CurContract()->Checkpoint = EmittingTryBody; + EmitStmt(Try->getTryBlock()); + CurContract()->Checkpoint = EmittingCatchBody; + ExitCXXTryStmt(*Try); + CurContract()->Checkpoint = EmittingContract; + + BranchOn = Builder.CreateLoad(CurContract()->EHPredicateStore); + } else { + BranchOn = EmitScalarExpr(S.getCond()); + } + + if (Style == Shared && Semantic == Enforce) { + //assert(!getLangOpts().Exceptions); + EnsureInsertPoint(); + ContractData->GetSharedEnforceBlock(*this).IncomingPHI->addIncoming( + ViolationInfo, Builder.GetInsertBlock()); + } + + Builder.CreateCondBr(BranchOn, End, Violation); + + // If we're creating a trap, the violation block will be created once for the + // function. Otherwise, we need to create a call to the violation handler. + if (Style == Inline) { + EmitBlock(CurContract()->Violation); + Builder.SetInsertPoint(CurContract()->Violation); + EmitHandleContractViolationCall(CreateConstantInt(*this, Semantic), + CreateConstantInt(*this, PredicateFailed), + CurContract()->ViolationInfoGV, + /*IsNoReturn=*/Semantic == Enforce); + if (Semantic != Enforce) + Builder.CreateBr(End); + } + + EmitBlock(End); +} diff --git a/clang/lib/CodeGen/CGContracts.h b/clang/lib/CodeGen/CGContracts.h new file mode 100644 index 0000000000000..2518675023177 --- /dev/null +++ b/clang/lib/CodeGen/CGContracts.h @@ -0,0 +1,16 @@ +//===-- CGContracts.h - state for LLVM CodeGen for Contracts ---*- 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 is the internal state used for llvm translation for contracts +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_CODEGEN_CGCONTRACTS_H +#define LLVM_CLANG_LIB_CODEGEN_CGCONTRACTS_H + +#endif diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp index 8887c4de7c4c8..d008fc4ef211f 100644 --- a/clang/lib/CodeGen/CGDebugInfo.cpp +++ b/clang/lib/CodeGen/CGDebugInfo.cpp @@ -287,7 +287,8 @@ PrintingPolicy CGDebugInfo::getPrintingPolicy() const { PP.SplitTemplateClosers = true; } - PP.SuppressInlineNamespace = false; + PP.SuppressInlineNamespace = + PrintingPolicy::SuppressInlineNamespaceMode::None; PP.PrintCanonicalTypes = true; PP.UsePreferredNames = false; PP.AlwaysIncludeTypeForTemplateArgument = true; @@ -1472,15 +1473,6 @@ llvm::DIType *CGDebugInfo::CreateType(const TemplateSpecializationType *Ty, return AliasTy; } - // Disable PrintCanonicalTypes here because we want - // the DW_AT_name to benefit from the TypePrinter's ability - // to skip defaulted template arguments. - // - // FIXME: Once -gsimple-template-names is enabled by default - // and we attach template parameters to alias template DIEs - // we don't need to worry about customizing the PrintingPolicy - // here anymore. - PP.PrintCanonicalTypes = false; printTemplateArgumentList(OS, Ty->template_arguments(), PP, TD->getTemplateParameters()); return DBuilder.createTypedef(Src, OS.str(), getOrCreateFile(Loc), @@ -4656,8 +4648,8 @@ void CGDebugInfo::CreateLexicalBlock(SourceLocation Loc) { if (!LexicalBlockStack.empty()) Back = LexicalBlockStack.back().get(); LexicalBlockStack.emplace_back(DBuilder.createLexicalBlock( - cast(Back), getOrCreateFile(CurLoc), getLineNumber(CurLoc), - getColumnNumber(CurLoc))); + cast_or_null(Back), getOrCreateFile(CurLoc), + getLineNumber(CurLoc), getColumnNumber(CurLoc))); } void CGDebugInfo::AppendAddressSpaceXDeref( @@ -4679,9 +4671,10 @@ void CGDebugInfo::EmitLexicalBlockStart(CGBuilderTy &Builder, setLocation(Loc); // Emit a line table change for the current location inside the new scope. - Builder.SetCurrentDebugLocation(llvm::DILocation::get( - CGM.getLLVMContext(), getLineNumber(Loc), getColumnNumber(Loc), - LexicalBlockStack.back(), CurInlinedAt)); + if (!LexicalBlockStack.empty()) + Builder.SetCurrentDebugLocation(llvm::DILocation::get( + CGM.getLLVMContext(), getLineNumber(Loc), getColumnNumber(Loc), + LexicalBlockStack.back(), CurInlinedAt)); if (DebugKind <= llvm::codegenoptions::DebugLineTablesOnly) return; diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp index 563f728e29d78..29d85c6f48290 100644 --- a/clang/lib/CodeGen/CGDecl.cpp +++ b/clang/lib/CodeGen/CGDecl.cpp @@ -105,6 +105,7 @@ void CodeGenFunction::EmitDecl(const Decl &D) { case Decl::Binding: case Decl::UnresolvedUsingIfExists: case Decl::HLSLBuffer: + case Decl::ContractSpecifier: llvm_unreachable("Declaration should not be in declstmts!"); case Decl::Record: // struct/union/class X; case Decl::CXXRecord: // struct/union/class X; [C++] @@ -137,6 +138,10 @@ void CodeGenFunction::EmitDecl(const Decl &D) { // None of these decls require codegen support. return; + case Decl::ResultName: // FIXME(EricWF): This should be removed. + llvm_unreachable("result name in function"); + return; + case Decl::NamespaceAlias: if (CGDebugInfo *DI = getDebugInfo()) DI->EmitNamespaceAlias(cast(D)); @@ -2509,8 +2514,8 @@ void CodeGenFunction::pushRegularPartialArrayCleanup(llvm::Value *arrayBegin, llvm::Function *CodeGenModule::getLLVMLifetimeStartFn() { if (LifetimeStartFn) return LifetimeStartFn; - LifetimeStartFn = llvm::Intrinsic::getDeclaration(&getModule(), - llvm::Intrinsic::lifetime_start, AllocaInt8PtrTy); + LifetimeStartFn = llvm::Intrinsic::getOrInsertDeclaration( + &getModule(), llvm::Intrinsic::lifetime_start, AllocaInt8PtrTy); return LifetimeStartFn; } @@ -2518,8 +2523,8 @@ llvm::Function *CodeGenModule::getLLVMLifetimeStartFn() { llvm::Function *CodeGenModule::getLLVMLifetimeEndFn() { if (LifetimeEndFn) return LifetimeEndFn; - LifetimeEndFn = llvm::Intrinsic::getDeclaration(&getModule(), - llvm::Intrinsic::lifetime_end, AllocaInt8PtrTy); + LifetimeEndFn = llvm::Intrinsic::getOrInsertDeclaration( + &getModule(), llvm::Intrinsic::lifetime_end, AllocaInt8PtrTy); return LifetimeEndFn; } diff --git a/clang/lib/CodeGen/CGException.cpp b/clang/lib/CodeGen/CGException.cpp index bb2ed237ee9f3..44a45413dbc45 100644 --- a/clang/lib/CodeGen/CGException.cpp +++ b/clang/lib/CodeGen/CGException.cpp @@ -1843,7 +1843,7 @@ Address CodeGenFunction::recoverAddrOfEscapedLocal(CodeGenFunction &ParentCGF, std::make_pair(ParentAlloca, ParentCGF.EscapedLocals.size())); int FrameEscapeIdx = InsertPair.first->second; // call ptr @llvm.localrecover(ptr @parentFn, ptr %fp, i32 N) - llvm::Function *FrameRecoverFn = llvm::Intrinsic::getDeclaration( + llvm::Function *FrameRecoverFn = llvm::Intrinsic::getOrInsertDeclaration( &CGM.getModule(), llvm::Intrinsic::localrecover); RecoverCall = Builder.CreateCall( FrameRecoverFn, {ParentCGF.CurFn, ParentFP, @@ -1942,7 +1942,7 @@ void CodeGenFunction::EmitCapturedLocals(CodeGenFunction &ParentCGF, // %1 = call ptr @llvm.localrecover(@"?fin$0@0@main@@",..) // %2 = load ptr, ptr %1, align 8 // ==> %2 is the frame-pointer of outermost host function - llvm::Function *FrameRecoverFn = llvm::Intrinsic::getDeclaration( + llvm::Function *FrameRecoverFn = llvm::Intrinsic::getOrInsertDeclaration( &CGM.getModule(), llvm::Intrinsic::localrecover); ParentFP = Builder.CreateCall( FrameRecoverFn, {ParentCGF.CurFn, ParentFP, diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index aaaa4c7cbf2ae..455137ac5f17c 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -1095,6 +1095,8 @@ class StructAccessBase return Visit(E->getBase()); } const Expr *VisitCastExpr(const CastExpr *E) { + if (E->getCastKind() == CK_LValueToRValue) + return IsExpectedRecordDecl(E) ? E : nullptr; return Visit(E->getSubExpr()); } const Expr *VisitParenExpr(const ParenExpr *E) { @@ -1161,19 +1163,15 @@ llvm::Value *CodeGenFunction::EmitLoadOfCountedByField( return nullptr; llvm::Value *Res = nullptr; - if (const auto *DRE = dyn_cast(StructBase)) { - Res = EmitDeclRefLValue(DRE).getPointer(*this); - Res = Builder.CreateAlignedLoad(ConvertType(DRE->getType()), Res, - getPointerAlign(), "dre.load"); - } else if (const MemberExpr *ME = dyn_cast(StructBase)) { - LValue LV = EmitMemberExpr(ME); - Address Addr = LV.getAddress(); - Res = Addr.emitRawPointer(*this); - } else if (StructBase->getType()->isPointerType()) { + if (StructBase->getType()->isPointerType()) { LValueBaseInfo BaseInfo; TBAAAccessInfo TBAAInfo; Address Addr = EmitPointerWithAlignment(StructBase, &BaseInfo, &TBAAInfo); Res = Addr.emitRawPointer(*this); + } else if (StructBase->isLValue()) { + LValue LV = EmitLValue(StructBase); + Address Addr = LV.getAddress(); + Res = Addr.emitRawPointer(*this); } else { return nullptr; } @@ -2368,7 +2366,7 @@ Address CodeGenFunction::EmitExtVectorElementLValue(LValue LV) { return VectorBasePtrPlusIx; } -/// Load of global gamed gegisters are always calls to intrinsics. +/// Load of global named registers are always calls to intrinsics. RValue CodeGenFunction::EmitLoadOfGlobalRegLValue(LValue LV) { assert((LV.getType()->isIntegerType() || LV.getType()->isPointerType()) && "Bad type for register variable"); @@ -3015,6 +3013,12 @@ LValue CodeGenFunction::EmitDeclRefLValue(const DeclRefExpr *E) { assert(E->isNonOdrUse() != NOUR_Unevaluated && "should not emit an unevaluated operand"); + // FIXME(EricWF): There's got to be more to this. + if (const auto *RND = dyn_cast(ND)) { + ((void)RND); + return MakeAddrLValue(ReturnValue, T, AlignmentSource::Decl); + } + if (const auto *VD = dyn_cast(ND)) { // Global Named registers access via intrinsics only if (VD->getStorageClass() == SC_Register && diff --git a/clang/lib/CodeGen/CGExprAgg.cpp b/clang/lib/CodeGen/CGExprAgg.cpp index bbfc6672ecc25..74d4c5ea7bcaa 100644 --- a/clang/lib/CodeGen/CGExprAgg.cpp +++ b/clang/lib/CodeGen/CGExprAgg.cpp @@ -39,7 +39,7 @@ namespace llvm { extern cl::opt EnableSingleByteCoverage; } // namespace llvm -namespace { +namespace { class AggExprEmitter : public StmtVisitor { CodeGenFunction &CGF; CGBuilderTy &Builder; diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp index b7f5b932c56b6..7529d4465f2c3 100644 --- a/clang/lib/CodeGen/CGExprScalar.cpp +++ b/clang/lib/CodeGen/CGExprScalar.cpp @@ -531,6 +531,10 @@ class ScalarExprEmitter return CGF.getOrCreateOpaqueRValueMapping(E).getScalarVal(); } + Value *VisitOpenACCAsteriskSizeExpr(OpenACCAsteriskSizeExpr *E) { + llvm_unreachable("Codegen for this isn't defined/implemented"); + } + // l-values. Value *VisitDeclRefExpr(DeclRefExpr *E) { if (CodeGenFunction::ConstantEmission Constant = CGF.tryEmitAsConstant(E)) diff --git a/clang/lib/CodeGen/CGHLSLRuntime.h b/clang/lib/CodeGen/CGHLSLRuntime.h index a8aabca7348ff..282fa44af212f 100644 --- a/clang/lib/CodeGen/CGHLSLRuntime.h +++ b/clang/lib/CodeGen/CGHLSLRuntime.h @@ -74,6 +74,8 @@ class CGHLSLRuntime { GENERATE_HLSL_INTRINSIC_FUNCTION(All, all) GENERATE_HLSL_INTRINSIC_FUNCTION(Any, any) + GENERATE_HLSL_INTRINSIC_FUNCTION(Cross, cross) + GENERATE_HLSL_INTRINSIC_FUNCTION(Degrees, degrees) GENERATE_HLSL_INTRINSIC_FUNCTION(Frac, frac) GENERATE_HLSL_INTRINSIC_FUNCTION(Length, length) GENERATE_HLSL_INTRINSIC_FUNCTION(Lerp, lerp) @@ -82,6 +84,7 @@ class CGHLSLRuntime { GENERATE_HLSL_INTRINSIC_FUNCTION(Saturate, saturate) GENERATE_HLSL_INTRINSIC_FUNCTION(Sign, sign) GENERATE_HLSL_INTRINSIC_FUNCTION(Step, step) + GENERATE_HLSL_INTRINSIC_FUNCTION(Radians, radians) GENERATE_HLSL_INTRINSIC_FUNCTION(ThreadId, thread_id) GENERATE_HLSL_INTRINSIC_FUNCTION(FDot, fdot) GENERATE_HLSL_INTRINSIC_FUNCTION(SDot, sdot) diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp index 9bf15fca0de48..0a79edc5db0e1 100644 --- a/clang/lib/CodeGen/CGStmt.cpp +++ b/clang/lib/CodeGen/CGStmt.cpp @@ -18,6 +18,7 @@ #include "clang/AST/Attr.h" #include "clang/AST/Expr.h" #include "clang/AST/Stmt.h" +#include "clang/AST/StmtCXX.h" #include "clang/AST/StmtVisitor.h" #include "clang/Basic/Builtins.h" #include "clang/Basic/DiagnosticSema.h" @@ -168,6 +169,9 @@ void CodeGenFunction::EmitStmt(const Stmt *S, ArrayRef Attrs) { case Stmt::CoreturnStmtClass: EmitCoreturnStmt(cast(*S)); break; + case Stmt::ContractStmtClass: + EmitContractStmt(cast(*S)); + break; case Stmt::CapturedStmtClass: { const CapturedStmt *CS = cast(S); EmitCapturedStmt(*CS, CS->getCapturedRegionKind()); @@ -801,10 +805,12 @@ void CodeGenFunction::EmitIndirectGotoStmt(const IndirectGotoStmt &S) { } void CodeGenFunction::EmitIfStmt(const IfStmt &S) { + const Stmt *Else = S.getElse(); + // The else branch of a consteval if statement is always the only branch that // can be runtime evaluated. if (S.isConsteval()) { - const Stmt *Executed = S.isNegatedConsteval() ? S.getThen() : S.getElse(); + const Stmt *Executed = S.isNegatedConsteval() ? S.getThen() : Else; if (Executed) { RunCleanupsScope ExecutedScope(*this); EmitStmt(Executed); @@ -830,8 +836,8 @@ void CodeGenFunction::EmitIfStmt(const IfStmt &S) { S.isConstexpr())) { // Figure out which block (then or else) is executed. const Stmt *Executed = S.getThen(); - const Stmt *Skipped = S.getElse(); - if (!CondConstant) // Condition false? + const Stmt *Skipped = Else; + if (!CondConstant) // Condition false? std::swap(Executed, Skipped); // If the skipped block has no labels in it, just emit the executed block. @@ -852,7 +858,7 @@ void CodeGenFunction::EmitIfStmt(const IfStmt &S) { llvm::BasicBlock *ThenBlock = createBasicBlock("if.then"); llvm::BasicBlock *ContBlock = createBasicBlock("if.end"); llvm::BasicBlock *ElseBlock = ContBlock; - if (S.getElse()) + if (Else) ElseBlock = createBasicBlock("if.else"); // Prefer the PGO based weights over the likelihood attribute. @@ -870,7 +876,7 @@ void CodeGenFunction::EmitIfStmt(const IfStmt &S) { uint64_t ThenCount = getProfileCount(S.getThen()); if (!ThenCount && !getCurrentProfileCount() && CGM.getCodeGenOpts().OptimizationLevel) - LH = Stmt::getLikelihood(S.getThen(), S.getElse()); + LH = Stmt::getLikelihood(S.getThen(), Else); // When measuring MC/DC, always fully evaluate the condition up front using // EvaluateExprAsBool() so that the test vector bitmap can be updated prior to @@ -898,7 +904,7 @@ void CodeGenFunction::EmitIfStmt(const IfStmt &S) { EmitBranch(ContBlock); // Emit the 'else' code if present. - if (const Stmt *Else = S.getElse()) { + if (Else) { { // There is no need to emit line number for an unconditional branch. auto NL = ApplyDebugLocation::CreateEmpty(*this); @@ -1470,6 +1476,7 @@ static bool isSwiftAsyncCallee(const CallExpr *CE) { return calleeType->getCallConv() == CallingConv::CC_SwiftAsync; } + /// EmitReturnStmt - Note that due to GCC extensions, this can have an operand /// if the function returns void, or may be missing one if the function returns /// non-void. Fun stuff :). diff --git a/clang/lib/CodeGen/CMakeLists.txt b/clang/lib/CodeGen/CMakeLists.txt index 868ec847b9634..dd226231a9434 100644 --- a/clang/lib/CodeGen/CMakeLists.txt +++ b/clang/lib/CodeGen/CMakeLists.txt @@ -69,6 +69,7 @@ add_clang_library(clangCodeGen CGClass.cpp CGCleanup.cpp CGCoroutine.cpp + CGContracts.cpp CGDebugInfo.cpp CGDecl.cpp CGDeclCXX.cpp diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp index eda96f3e352ce..b4ac7a8619005 100644 --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/clang/lib/CodeGen/CodeGenFunction.cpp @@ -35,6 +35,7 @@ #include "clang/Basic/TargetInfo.h" #include "clang/CodeGen/CGFunctionInfo.h" #include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/Basic/SourceManager.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/Frontend/OpenMP/OMPIRBuilder.h" #include "llvm/IR/DataLayout.h" @@ -48,6 +49,7 @@ #include "llvm/Support/xxhash.h" #include "llvm/Transforms/Scalar/LowerExpectIntrinsic.h" #include "llvm/Transforms/Utils/PromoteMemToReg.h" +#include "clang/Lex/Lexer.h" #include using namespace clang; @@ -463,7 +465,7 @@ void CodeGenFunction::FinishFunction(SourceLocation EndLoc) { EscapeArgs.resize(EscapedLocals.size()); for (auto &Pair : EscapedLocals) EscapeArgs[Pair.second] = Pair.first; - llvm::Function *FrameEscapeFn = llvm::Intrinsic::getDeclaration( + llvm::Function *FrameEscapeFn = llvm::Intrinsic::getOrInsertDeclaration( &CGM.getModule(), llvm::Intrinsic::localescape); CGBuilderTy(*this, AllocaInsertPt).CreateCall(FrameEscapeFn, EscapeArgs); } @@ -491,6 +493,8 @@ void CodeGenFunction::FinishFunction(SourceLocation EndLoc) { } } + EmitIfUsed(*this, GetSharedContractViolationEnforceBlock(false)); + EmitIfUsed(*this, GetSharedContractViolationTrapBlock(false)); EmitIfUsed(*this, EHResumeBlock); EmitIfUsed(*this, TerminateLandingPad); EmitIfUsed(*this, TerminateHandler); @@ -850,6 +854,8 @@ void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy, for (const FunctionEffectWithCondition &Fe : FD->getFunctionEffects()) { if (Fe.Effect.kind() == FunctionEffect::Kind::NonBlocking) Fn->addFnAttr(llvm::Attribute::SanitizeRealtime); + else if (Fe.Effect.kind() == FunctionEffect::Kind::Blocking) + Fn->addFnAttr(llvm::Attribute::SanitizeRealtimeUnsafe); } // Apply fuzzing attribute to the function. @@ -1528,6 +1534,10 @@ void CodeGenFunction::GenerateCode(GlobalDecl GD, llvm::Function *Fn, // Emit the standard function prologue. StartFunction(GD, ResTy, Fn, FnInfo, Args, Loc, BodyRange.getBegin()); + // FIXME(EricWF): I don't think this should go here. + for (ContractStmt *S : FD->preconditions()) + EmitStmt(S); + // Save parameters for coroutine function. if (Body && isa_and_nonnull(Body)) llvm::append_range(FnArgs, FD->parameters()); @@ -1602,6 +1612,9 @@ void CodeGenFunction::GenerateCode(GlobalDecl GD, llvm::Function *Fn, Builder.ClearInsertionPoint(); } } + // FIXME(EricWF): I don't think this should go here. + // Also we'll need to figure out how to reference the return value + // Post Contracts are emitted in ReturnValueCheck // Emit the standard function epilogue. FinishFunction(BodyRange.getEnd()); @@ -1762,6 +1775,8 @@ void CodeGenFunction::EmitBranchToCounterBlock( if (!InstrumentRegions || !isInstrumentedCondition(Cond)) return EmitBranchOnBoolExpr(Cond, TrueBlock, FalseBlock, TrueCount, LH); + const Stmt *CntrStmt = (CntrIdx ? CntrIdx : Cond); + llvm::BasicBlock *ThenBlock = nullptr; llvm::BasicBlock *ElseBlock = nullptr; llvm::BasicBlock *NextBlock = nullptr; @@ -1814,7 +1829,7 @@ void CodeGenFunction::EmitBranchToCounterBlock( EmitBlock(CounterIncrBlock); // Increment corresponding counter; if index not provided, use Cond as index. - incrementProfileCounter(CntrIdx ? CntrIdx : Cond); + incrementProfileCounter(CntrStmt); // Go to the next block. EmitBranch(NextBlock); @@ -3126,7 +3141,7 @@ void CodeGenFunction::emitAlignmentAssumptionCheck( llvm::Instruction *Assumption) { assert(isa_and_nonnull(Assumption) && cast(Assumption)->getCalledOperand() == - llvm::Intrinsic::getDeclaration( + llvm::Intrinsic::getOrInsertDeclaration( Builder.GetInsertBlock()->getParent()->getParent(), llvm::Intrinsic::assume) && "Assumption should be a call to llvm.assume()."); diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index 9ba0ed02a564d..4a8c45f0967e4 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -14,6 +14,7 @@ #define LLVM_CLANG_LIB_CODEGEN_CODEGENFUNCTION_H #include "CGBuilder.h" +#include "CGContracts.h" #include "CGDebugInfo.h" #include "CGLoopInfo.h" #include "CGValue.h" @@ -100,6 +101,13 @@ class RegionCodeGenTy; class TargetCodeGenInfo; struct OMPTaskDataTy; struct CGCoroData; +struct CurrentContractInfo; +struct CGContractData; +struct CGContractDataDeleter { + static CGContractData *Create(); + + void operator()(CGContractData *) const; +}; /// The kind of evaluation to perform on values of a particular /// type. Basically, is the code in CGExprScalar, CGExprComplex, or @@ -417,6 +425,17 @@ class CodeGenFunction : public CodeGenTypeCache { /// result expression. const Expr *RetExpr = nullptr; + /// If a contract attribute is being visited, this holds the contract + std::unique_ptr ContractData{ + CGContractDataDeleter::Create()}; + + CurrentContractInfo *CurContract(); + + // This is only used when exceptions are fully disabled. In this case, + // an enforced contract violation always terminates the program, so we + // can jump to a shared cleanup block without having to worry about continuing + // or cleanups; + /// Return true if a label was seen in the current scope. bool hasLabelBeenSeenInCurrentScope() const { if (CurLexicalScope) @@ -2553,6 +2572,8 @@ class CodeGenFunction : public CodeGenTypeCache { /// Emit a test that checks if the return value \p RV is nonnull. void EmitReturnValueCheck(llvm::Value *RV); + void EmitPostContracts(llvm::Value *RV); + /// EmitStartEHSpec - Emit the start of the exception spec. void EmitStartEHSpec(const Decl *D); @@ -4377,6 +4398,22 @@ class CodeGenFunction : public CodeGenTypeCache { LValue EmitObjCSelectorLValue(const ObjCSelectorExpr *E); void EmitDeclRefExprDbgValue(const DeclRefExpr *E, const APValue &Init); + llvm::BasicBlock *GetSharedContractViolationTrapBlock(bool Create = true); + llvm::BasicBlock *GetSharedContractViolationEnforceBlock(bool Create = true); + + void EmitContractStmt(const ContractStmt &S); + +private: + void EmitContractStmtAsTryBody(const ContractStmt &); + void EmitContractStmtAsCatchBody(const ContractStmt &S); + void EmitContractStmtAsFullStmt(const ContractStmt &S); + +public: + void EmitHandleContractViolationCall(llvm::Constant *Semantic, + llvm::Constant *DetectionMode, + llvm::Value *ViolationDataGV, + bool IsNoReturn); + //===--------------------------------------------------------------------===// // Scalar Expression Emission //===--------------------------------------------------------------------===// diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index 25c1c496a4f27..ed3940588efbe 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -3663,7 +3663,7 @@ ConstantAddress CodeGenModule::GetAddrOfMSGuidDecl(const MSGuidDecl *GD) { } ConstantAddress CodeGenModule::GetAddrOfUnnamedGlobalConstantDecl( - const UnnamedGlobalConstantDecl *GCD) { + const UnnamedGlobalConstantDecl *GCD, StringRef Name) { CharUnits Alignment = getContext().getTypeAlignInChars(GCD->getType()); llvm::GlobalVariable **Entry = nullptr; @@ -3680,10 +3680,9 @@ ConstantAddress CodeGenModule::GetAddrOfUnnamedGlobalConstantDecl( Init = Emitter.emitForInitializer(V, GCD->getType().getAddressSpace(), GCD->getType()); - auto *GV = new llvm::GlobalVariable(getModule(), Init->getType(), - /*isConstant=*/true, - llvm::GlobalValue::PrivateLinkage, Init, - ".constant"); + auto *GV = new llvm::GlobalVariable( + getModule(), Init->getType(), + /*isConstant=*/true, llvm::GlobalValue::PrivateLinkage, Init, Name); GV->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global); GV->setAlignment(Alignment.getAsAlign()); @@ -4287,8 +4286,13 @@ void CodeGenModule::emitMultiVersionFunctions() { } else if (const auto *TVA = CurFD->getAttr()) { if (TVA->isDefaultVersion() && IsDefined) ShouldEmitResolver = true; - TVA->getFeatures(Feats); llvm::Function *Func = createFunction(CurFD); + if (getTarget().getTriple().isRISCV()) { + Feats.push_back(TVA->getName()); + } else { + assert(getTarget().getTriple().isAArch64()); + TVA->getFeatures(Feats); + } Options.emplace_back(Func, /*Architecture*/ "", Feats); } else if (const auto *TC = CurFD->getAttr()) { if (IsDefined) @@ -6213,8 +6217,8 @@ void CodeGenModule::emitIFuncDefinition(GlobalDecl GD) { llvm::Function *CodeGenModule::getIntrinsic(unsigned IID, ArrayRef Tys) { - return llvm::Intrinsic::getDeclaration(&getModule(), (llvm::Intrinsic::ID)IID, - Tys); + return llvm::Intrinsic::getOrInsertDeclaration(&getModule(), + (llvm::Intrinsic::ID)IID, Tys); } static llvm::StringMapEntry & diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h index c58bb88035ca8..49df7e40cfa50 100644 --- a/clang/lib/CodeGen/CodeGenModule.h +++ b/clang/lib/CodeGen/CodeGenModule.h @@ -1030,7 +1030,8 @@ class CodeGenModule : public CodeGenTypeCache { /// Get the address of a UnnamedGlobalConstant ConstantAddress - GetAddrOfUnnamedGlobalConstantDecl(const UnnamedGlobalConstantDecl *GCD); + GetAddrOfUnnamedGlobalConstantDecl(const UnnamedGlobalConstantDecl *GCD, + StringRef Name = ".constant"); /// Get the address of a template parameter object. ConstantAddress diff --git a/clang/lib/CodeGen/CodeGenPGO.cpp b/clang/lib/CodeGen/CodeGenPGO.cpp index b745ad37fc96b..820bb521ccf85 100644 --- a/clang/lib/CodeGen/CodeGenPGO.cpp +++ b/clang/lib/CodeGen/CodeGenPGO.cpp @@ -1206,14 +1206,12 @@ void CodeGenPGO::emitCounterSetOrIncrement(CGBuilderTy &Builder, const Stmt *S, if (llvm::EnableSingleByteCoverage) Builder.CreateCall(CGM.getIntrinsic(llvm::Intrinsic::instrprof_cover), ArrayRef(Args, 4)); - else { - if (!StepV) - Builder.CreateCall(CGM.getIntrinsic(llvm::Intrinsic::instrprof_increment), - ArrayRef(Args, 4)); - else - Builder.CreateCall( - CGM.getIntrinsic(llvm::Intrinsic::instrprof_increment_step), Args); - } + else if (!StepV) + Builder.CreateCall(CGM.getIntrinsic(llvm::Intrinsic::instrprof_increment), + ArrayRef(Args, 4)); + else + Builder.CreateCall( + CGM.getIntrinsic(llvm::Intrinsic::instrprof_increment_step), Args); } bool CodeGenPGO::canEmitMCDCCoverage(const CGBuilderTy &Builder) { diff --git a/clang/lib/CodeGen/CodeGenTypes.cpp b/clang/lib/CodeGen/CodeGenTypes.cpp index 0b486a644f57b..339632090a5b7 100644 --- a/clang/lib/CodeGen/CodeGenTypes.cpp +++ b/clang/lib/CodeGen/CodeGenTypes.cpp @@ -60,7 +60,8 @@ void CodeGenTypes::addRecordTypeName(const RecordDecl *RD, // example, we should probably enable PrintCanonicalTypes and // FullyQualifiedNames. PrintingPolicy Policy = RD->getASTContext().getPrintingPolicy(); - Policy.SuppressInlineNamespace = false; + Policy.SuppressInlineNamespace = + PrintingPolicy::SuppressInlineNamespaceMode::None; // Name the codegen type after the typedef name // if there is no tag type name available diff --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp b/clang/lib/CodeGen/ItaniumCXXABI.cpp index 965e09a7a760e..75dab596e1b2c 100644 --- a/clang/lib/CodeGen/ItaniumCXXABI.cpp +++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp @@ -2997,6 +2997,10 @@ void ItaniumCXXABI::registerGlobalDtor(CodeGenFunction &CGF, const VarDecl &D, if (D.isNoDestroy(CGM.getContext())) return; + // HLSL doesn't support atexit. + if (CGM.getLangOpts().HLSL) + return CGM.AddCXXDtorEntry(dtor, addr); + // OpenMP offloading supports C++ constructors and destructors but we do not // always have 'atexit' available. Instead lower these to use the LLVM global // destructors which we can handle directly in the runtime. Note that this is diff --git a/clang/lib/CodeGen/MicrosoftCXXABI.cpp b/clang/lib/CodeGen/MicrosoftCXXABI.cpp index 79dcdc04b0996..0b0b45ffead92 100644 --- a/clang/lib/CodeGen/MicrosoftCXXABI.cpp +++ b/clang/lib/CodeGen/MicrosoftCXXABI.cpp @@ -518,7 +518,7 @@ class MicrosoftCXXABI : public CGCXXABI { CGM.IntTy, CGM.IntTy, CGM.IntTy, - getImageRelativeType(getClassHierarchyDescriptorType()->getPointerTo()), + getImageRelativeType(CGM.UnqualPtrTy), }; BaseClassDescriptorType = llvm::StructType::create( CGM.getLLVMContext(), FieldTypes, "rtti.BaseClassDescriptor"); @@ -531,13 +531,8 @@ class MicrosoftCXXABI : public CGCXXABI { // Forward-declare RTTIClassHierarchyDescriptor to break a cycle. ClassHierarchyDescriptorType = llvm::StructType::create( CGM.getLLVMContext(), "rtti.ClassHierarchyDescriptor"); - llvm::Type *FieldTypes[] = { - CGM.IntTy, - CGM.IntTy, - CGM.IntTy, - getImageRelativeType( - getBaseClassDescriptorType()->getPointerTo()->getPointerTo()), - }; + llvm::Type *FieldTypes[] = {CGM.IntTy, CGM.IntTy, CGM.IntTy, + getImageRelativeType(CGM.UnqualPtrTy)}; ClassHierarchyDescriptorType->setBody(FieldTypes); return ClassHierarchyDescriptorType; } @@ -552,7 +547,7 @@ class MicrosoftCXXABI : public CGCXXABI { CGM.IntTy, CGM.IntTy, getImageRelativeType(CGM.Int8PtrTy), - getImageRelativeType(getClassHierarchyDescriptorType()->getPointerTo()), + getImageRelativeType(CGM.UnqualPtrTy), getImageRelativeType(CompleteObjectLocatorType), }; llvm::ArrayRef FieldTypesRef(FieldTypes); @@ -749,8 +744,7 @@ class MicrosoftCXXABI : public CGCXXABI { llvm::SmallString<23> CTATypeName("eh.CatchableTypeArray."); CTATypeName += llvm::utostr(NumEntries); - llvm::Type *CTType = - getImageRelativeType(getCatchableTypeType()->getPointerTo()); + llvm::Type *CTType = getImageRelativeType(CGM.UnqualPtrTy); llvm::Type *FieldTypes[] = { CGM.IntTy, // NumEntries llvm::ArrayType::get(CTType, NumEntries) // CatchableTypes @@ -777,7 +771,7 @@ class MicrosoftCXXABI : public CGCXXABI { llvm::FunctionCallee getThrowFn() { // _CxxThrowException is passed an exception object and a ThrowInfo object // which describes the exception. - llvm::Type *Args[] = {CGM.Int8PtrTy, getThrowInfoType()->getPointerTo()}; + llvm::Type *Args[] = {CGM.Int8PtrTy, CGM.UnqualPtrTy}; llvm::FunctionType *FTy = llvm::FunctionType::get(CGM.VoidTy, Args, /*isVarArg=*/false); llvm::FunctionCallee Throw = @@ -909,9 +903,8 @@ void MicrosoftCXXABI::emitVirtualObjectDelete(CodeGenFunction &CGF, } void MicrosoftCXXABI::emitRethrow(CodeGenFunction &CGF, bool isNoReturn) { - llvm::Value *Args[] = { - llvm::ConstantPointerNull::get(CGM.Int8PtrTy), - llvm::ConstantPointerNull::get(getThrowInfoType()->getPointerTo())}; + llvm::Value *Args[] = {llvm::ConstantPointerNull::get(CGM.Int8PtrTy), + llvm::ConstantPointerNull::get(CGM.UnqualPtrTy)}; llvm::FunctionCallee Fn = getThrowFn(); if (isNoReturn) CGF.EmitNoreturnRuntimeCallOrInvoke(Fn, Args); @@ -1958,13 +1951,13 @@ CGCallee MicrosoftCXXABI::getVirtualFunctionPointer(CodeGenFunction &CGF, SourceLocation Loc) { CGBuilderTy &Builder = CGF.Builder; - Ty = Ty->getPointerTo(); + Ty = CGF.UnqualPtrTy; Address VPtr = adjustThisArgumentForVirtualFunctionCall(CGF, GD, This, true); auto *MethodDecl = cast(GD.getDecl()); - llvm::Value *VTable = CGF.GetVTablePtr(VPtr, Ty->getPointerTo(), - MethodDecl->getParent()); + llvm::Value *VTable = + CGF.GetVTablePtr(VPtr, CGF.UnqualPtrTy, MethodDecl->getParent()); MicrosoftVTableContext &VFTContext = CGM.getMicrosoftVTableContext(); MethodVFTableLocation ML = VFTContext.getMethodVFTableLocation(GD); @@ -2125,9 +2118,9 @@ MicrosoftCXXABI::EmitVirtualMemPtrThunk(const CXXMethodDecl *MD, // Load the vfptr and then callee from the vftable. The callee should have // adjusted 'this' so that the vfptr is at offset zero. - llvm::Type *ThunkPtrTy = ThunkTy->getPointerTo(); - llvm::Value *VTable = CGF.GetVTablePtr( - getThisAddress(CGF), ThunkPtrTy->getPointerTo(), MD->getParent()); + llvm::Type *ThunkPtrTy = CGF.UnqualPtrTy; + llvm::Value *VTable = + CGF.GetVTablePtr(getThisAddress(CGF), CGF.UnqualPtrTy, MD->getParent()); llvm::Value *VFuncPtr = CGF.Builder.CreateConstInBoundsGEP1_64( ThunkPtrTy, VTable, ML.Index, "vfn"); @@ -2551,7 +2544,7 @@ static ConstantAddress getInitThreadEpochPtr(CodeGenModule &CGM) { static llvm::FunctionCallee getInitThreadHeaderFn(CodeGenModule &CGM) { llvm::FunctionType *FTy = llvm::FunctionType::get(llvm::Type::getVoidTy(CGM.getLLVMContext()), - CGM.IntTy->getPointerTo(), /*isVarArg=*/false); + CGM.UnqualPtrTy, /*isVarArg=*/false); return CGM.CreateRuntimeFunction( FTy, "_Init_thread_header", llvm::AttributeList::get(CGM.getLLVMContext(), @@ -2563,7 +2556,7 @@ static llvm::FunctionCallee getInitThreadHeaderFn(CodeGenModule &CGM) { static llvm::FunctionCallee getInitThreadFooterFn(CodeGenModule &CGM) { llvm::FunctionType *FTy = llvm::FunctionType::get(llvm::Type::getVoidTy(CGM.getLLVMContext()), - CGM.IntTy->getPointerTo(), /*isVarArg=*/false); + CGM.UnqualPtrTy, /*isVarArg=*/false); return CGM.CreateRuntimeFunction( FTy, "_Init_thread_footer", llvm::AttributeList::get(CGM.getLLVMContext(), @@ -2575,7 +2568,7 @@ static llvm::FunctionCallee getInitThreadFooterFn(CodeGenModule &CGM) { static llvm::FunctionCallee getInitThreadAbortFn(CodeGenModule &CGM) { llvm::FunctionType *FTy = llvm::FunctionType::get(llvm::Type::getVoidTy(CGM.getLLVMContext()), - CGM.IntTy->getPointerTo(), /*isVarArg=*/false); + CGM.UnqualPtrTy, /*isVarArg=*/false); return CGM.CreateRuntimeFunction( FTy, "_Init_thread_abort", llvm::AttributeList::get(CGM.getLLVMContext(), @@ -3157,8 +3150,8 @@ MicrosoftCXXABI::GetVBaseOffsetFromVBPtr(CodeGenFunction &CGF, VBPtrAlign = CGF.getPointerAlign(); } - llvm::Value *VBTable = Builder.CreateAlignedLoad( - CGM.Int32Ty->getPointerTo(0), VBPtr, VBPtrAlign, "vbtable"); + llvm::Value *VBTable = + Builder.CreateAlignedLoad(CGM.UnqualPtrTy, VBPtr, VBPtrAlign, "vbtable"); // Translate from byte offset to table index. It improves analyzability. llvm::Value *VBTableIndex = Builder.CreateAShr( @@ -3813,8 +3806,7 @@ MSRTTIBuilder::getBaseClassArray(SmallVectorImpl &Classes) { // mode) bytes of padding. We provide a pointer sized amount of padding by // adding +1 to Classes.size(). The sections have pointer alignment and are // marked pick-any so it shouldn't matter. - llvm::Type *PtrType = ABI.getImageRelativeType( - ABI.getBaseClassDescriptorType()->getPointerTo()); + llvm::Type *PtrType = ABI.getImageRelativeType(CGM.UnqualPtrTy); auto *ArrType = llvm::ArrayType::get(PtrType, Classes.size() + 1); auto *BCA = new llvm::GlobalVariable(Module, ArrType, @@ -4360,8 +4352,7 @@ llvm::GlobalVariable *MicrosoftCXXABI::getCatchableTypeArray(QualType T) { CatchableTypes.insert(getCatchableType(getContext().VoidPtrTy)); uint32_t NumEntries = CatchableTypes.size(); - llvm::Type *CTType = - getImageRelativeType(getCatchableTypeType()->getPointerTo()); + llvm::Type *CTType = getImageRelativeType(CGM.UnqualPtrTy); llvm::ArrayType *AT = llvm::ArrayType::get(CTType, NumEntries); llvm::StructType *CTAType = getCatchableTypeArrayType(NumEntries); llvm::Constant *Fields[] = { diff --git a/clang/lib/CodeGen/Targets/AMDGPU.cpp b/clang/lib/CodeGen/Targets/AMDGPU.cpp index 37e6af3d4196a..b852dcffb295c 100644 --- a/clang/lib/CodeGen/Targets/AMDGPU.cpp +++ b/clang/lib/CodeGen/Targets/AMDGPU.cpp @@ -452,9 +452,6 @@ void AMDGPUTargetCodeGenInfo::setTargetAttributes( if (FD) setFunctionDeclAttributes(FD, F, M); - if (M.getContext().getTargetInfo().allowAMDGPUUnsafeFPAtomics()) - F->addFnAttr("amdgpu-unsafe-fp-atomics", "true"); - if (!getABIInfo().getCodeGenOpts().EmitIEEENaNCompliantInsts) F->addFnAttr("amdgpu-ieee", "false"); } diff --git a/clang/lib/CodeGen/Targets/SystemZ.cpp b/clang/lib/CodeGen/Targets/SystemZ.cpp index 56129622f48db..23c96fa5cf98c 100644 --- a/clang/lib/CodeGen/Targets/SystemZ.cpp +++ b/clang/lib/CodeGen/Targets/SystemZ.cpp @@ -110,8 +110,8 @@ class SystemZTargetCodeGenInfo : public TargetCodeGenInfo { if (Ty->isFloatTy() || Ty->isDoubleTy() || Ty->isFP128Ty()) { llvm::Module &M = CGM.getModule(); auto &Ctx = M.getContext(); - llvm::Function *TDCFunc = - llvm::Intrinsic::getDeclaration(&M, llvm::Intrinsic::s390_tdc, Ty); + llvm::Function *TDCFunc = llvm::Intrinsic::getOrInsertDeclaration( + &M, llvm::Intrinsic::s390_tdc, Ty); unsigned TDCBits = 0; switch (BuiltinID) { case Builtin::BI__builtin_isnan: diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp index fba6a8853c396..ba850cf3803e9 100644 --- a/clang/lib/Driver/Driver.cpp +++ b/clang/lib/Driver/Driver.cpp @@ -229,7 +229,14 @@ Driver::Driver(StringRef ClangExecutable, StringRef TargetTriple, } #if defined(CLANG_CONFIG_FILE_SYSTEM_DIR) - SystemConfigDir = CLANG_CONFIG_FILE_SYSTEM_DIR; + if (llvm::sys::path::is_absolute(CLANG_CONFIG_FILE_SYSTEM_DIR)) { + SystemConfigDir = CLANG_CONFIG_FILE_SYSTEM_DIR; + } else { + SmallString<128> configFileDir(Dir); + llvm::sys::path::append(configFileDir, CLANG_CONFIG_FILE_SYSTEM_DIR); + llvm::sys::path::remove_dots(configFileDir, true); + SystemConfigDir = static_cast(configFileDir); + } #endif #if defined(CLANG_CONFIG_FILE_USER_DIR) { @@ -977,8 +984,9 @@ void Driver::CreateOffloadingDeviceToolChains(Compilation &C, } else TC = &getToolChain(C.getInputArgs(), TT); C.addOffloadDeviceToolChain(TC, Action::OFK_OpenMP); - if (DerivedArchs.contains(TT.getTriple())) - KnownArchs[TC] = DerivedArchs[TT.getTriple()]; + auto It = DerivedArchs.find(TT.getTriple()); + if (It != DerivedArchs.end()) + KnownArchs[TC] = It->second; } } } else if (C.getInputArgs().hasArg(options::OPT_fopenmp_targets_EQ)) { @@ -2021,7 +2029,7 @@ void Driver::PrintHelp(bool ShowHidden) const { void Driver::PrintVersion(const Compilation &C, raw_ostream &OS) const { if (IsFlangMode()) { - OS << getClangToolFullVersion("flang-new") << '\n'; + OS << getClangToolFullVersion("flang") << '\n'; } else { // FIXME: The following handlers should use a callback mechanism, we don't // know what the client would like to do. @@ -2314,7 +2322,7 @@ bool Driver::HandleImmediateArgs(Compilation &C) { if (C.getArgs().hasArg(options::OPT_print_multi_lib)) { for (const Multilib &Multilib : TC.getMultilibs()) - if (!Multilib.isFatalError()) + if (!Multilib.isError()) llvm::outs() << Multilib << "\n"; return false; } @@ -3742,11 +3750,10 @@ class OffloadingActionBuilder final { void recordHostAction(Action *HostAction, const Arg *InputArg) { assert(HostAction && "Invalid host action"); assert(InputArg && "Invalid input argument"); - auto Loc = HostActionToInputArgMap.find(HostAction); - if (Loc == HostActionToInputArgMap.end()) - HostActionToInputArgMap[HostAction] = InputArg; - assert(HostActionToInputArgMap[HostAction] == InputArg && + auto Loc = HostActionToInputArgMap.try_emplace(HostAction, InputArg).first; + assert(Loc->second == InputArg && "host action mapped to multiple input arguments"); + (void)Loc; } /// Generate an action that adds device dependences (if any) to a host action. @@ -4022,7 +4029,8 @@ void Driver::handleArguments(Compilation &C, DerivedArgList &Args, // Emitting LLVM while linking disabled except in HIPAMD Toolchain if (Args.hasArg(options::OPT_emit_llvm) && !Args.hasArg(options::OPT_hip_link)) Diag(clang::diag::err_drv_emit_llvm_link); - if (IsCLMode() && LTOMode != LTOK_None && + if (C.getDefaultToolChain().getTriple().isWindowsMSVCEnvironment() && + LTOMode != LTOK_None && !Args.getLastArgValue(options::OPT_fuse_ld_EQ) .equals_insensitive("lld")) Diag(clang::diag::err_drv_lto_without_lld); @@ -5573,8 +5581,9 @@ InputInfoList Driver::BuildJobsForActionNoCache( std::pair ActionTC = { OA->getHostDependence(), GetTriplePlusArchString(TC, BoundArch, TargetDeviceOffloadKind)}; - if (CachedResults.find(ActionTC) != CachedResults.end()) { - InputInfoList Inputs = CachedResults[ActionTC]; + auto It = CachedResults.find(ActionTC); + if (It != CachedResults.end()) { + InputInfoList Inputs = It->second; Inputs.append(OffloadDependencesInputInfo); return Inputs; } diff --git a/clang/lib/Driver/Multilib.cpp b/clang/lib/Driver/Multilib.cpp index e8a27fe9de473..c56417c6f6d0b 100644 --- a/clang/lib/Driver/Multilib.cpp +++ b/clang/lib/Driver/Multilib.cpp @@ -32,10 +32,9 @@ using namespace llvm::sys; Multilib::Multilib(StringRef GCCSuffix, StringRef OSSuffix, StringRef IncludeSuffix, const flags_list &Flags, - StringRef ExclusiveGroup, - std::optional FatalError) + StringRef ExclusiveGroup, std::optional Error) : GCCSuffix(GCCSuffix), OSSuffix(OSSuffix), IncludeSuffix(IncludeSuffix), - Flags(Flags), ExclusiveGroup(ExclusiveGroup), FatalError(FatalError) { + Flags(Flags), ExclusiveGroup(ExclusiveGroup), Error(Error) { assert(GCCSuffix.empty() || (StringRef(GCCSuffix).front() == '/' && GCCSuffix.size() > 1)); assert(OSSuffix.empty() || @@ -100,6 +99,7 @@ bool MultilibSet::select(const Driver &D, const Multilib::flags_list &Flags, llvm::SmallVectorImpl &Selected) const { llvm::StringSet<> FlagSet(expandFlags(Flags)); Selected.clear(); + bool AnyErrors = false; // Decide which multilibs we're going to select at all. llvm::DenseSet ExclusiveGroupsSelected; @@ -123,13 +123,11 @@ bool MultilibSet::select(const Driver &D, const Multilib::flags_list &Flags, continue; } - // If this multilib is actually a placeholder containing a fatal - // error message written by the multilib.yaml author, display that - // error message, and return failure. - if (M.isFatalError()) { - D.Diag(clang::diag::err_drv_multilib_custom_error) << M.getFatalError(); - return false; - } + // If this multilib is actually a placeholder containing an error message + // written by the multilib.yaml author, then set a flag that will cause a + // failure return. Our caller will display the error message. + if (M.isError()) + AnyErrors = true; // Select this multilib. Selected.push_back(M); @@ -139,7 +137,7 @@ bool MultilibSet::select(const Driver &D, const Multilib::flags_list &Flags, // round. std::reverse(Selected.begin(), Selected.end()); - return !Selected.empty(); + return !AnyErrors && !Selected.empty(); } llvm::StringSet<> @@ -173,7 +171,7 @@ static const VersionTuple MultilibVersionCurrent(1, 0); struct MultilibSerialization { std::string Dir; // if this record successfully selects a library dir - std::string FatalError; // if this record reports a fatal error message + std::string Error; // if this record reports a fatal error message std::vector Flags; std::string Group; }; @@ -217,15 +215,15 @@ struct MultilibSetSerialization { template <> struct llvm::yaml::MappingTraits { static void mapping(llvm::yaml::IO &io, MultilibSerialization &V) { io.mapOptional("Dir", V.Dir); - io.mapOptional("FatalError", V.FatalError); + io.mapOptional("Error", V.Error); io.mapRequired("Flags", V.Flags); io.mapOptional("Group", V.Group); } static std::string validate(IO &io, MultilibSerialization &V) { - if (V.Dir.empty() && V.FatalError.empty()) - return "one of the 'Dir' and 'FatalError' keys must be specified"; - if (!V.Dir.empty() && !V.FatalError.empty()) - return "the 'Dir' and 'FatalError' keys may not both be specified"; + if (V.Dir.empty() && V.Error.empty()) + return "one of the 'Dir' and 'Error' keys must be specified"; + if (!V.Dir.empty() && !V.Error.empty()) + return "the 'Dir' and 'Error' keys may not both be specified"; if (StringRef(V.Dir).starts_with("/")) return "paths must be relative but \"" + V.Dir + "\" starts with \"/\""; return std::string{}; @@ -311,8 +309,8 @@ MultilibSet::parseYaml(llvm::MemoryBufferRef Input, multilib_list Multilibs; Multilibs.reserve(MS.Multilibs.size()); for (const auto &M : MS.Multilibs) { - if (!M.FatalError.empty()) { - Multilibs.emplace_back("", "", "", M.Flags, M.Group, M.FatalError); + if (!M.Error.empty()) { + Multilibs.emplace_back("", "", "", M.Flags, M.Group, M.Error); } else { std::string Dir; if (M.Dir != ".") diff --git a/clang/lib/Driver/MultilibBuilder.cpp b/clang/lib/Driver/MultilibBuilder.cpp index 15adf50177809..4b365a164c458 100644 --- a/clang/lib/Driver/MultilibBuilder.cpp +++ b/clang/lib/Driver/MultilibBuilder.cpp @@ -74,13 +74,11 @@ bool MultilibBuilder::isValid() const { llvm::StringMap FlagSet; for (unsigned I = 0, N = Flags.size(); I != N; ++I) { StringRef Flag(Flags[I]); - llvm::StringMap::iterator SI = FlagSet.find(Flag.substr(1)); + auto [SI, Inserted] = FlagSet.try_emplace(Flag.substr(1), I); assert(StringRef(Flag).front() == '-' || StringRef(Flag).front() == '!'); - if (SI == FlagSet.end()) - FlagSet[Flag.substr(1)] = I; - else if (Flags[I] != Flags[SI->getValue()]) + if (!Inserted && Flags[I] != Flags[SI->getValue()]) return false; } return true; diff --git a/clang/lib/Driver/ToolChain.cpp b/clang/lib/Driver/ToolChain.cpp index 16f9b629fc538..4df3177095085 100644 --- a/clang/lib/Driver/ToolChain.cpp +++ b/clang/lib/Driver/ToolChain.cpp @@ -227,6 +227,11 @@ static void getAArch64MultilibFlags(const Driver &D, if (BranchProtectionArg) { Result.push_back(BranchProtectionArg->getAsString(Args)); } + + const Arg *ABIArg = Args.getLastArgNoClaim(options::OPT_mabi_EQ); + if (ABIArg) { + Result.push_back(ABIArg->getAsString(Args)); + } } static void getARMMultilibFlags(const Driver &D, @@ -381,6 +386,9 @@ static const DriverSuffix *FindDriverSuffix(StringRef ProgName, size_t &Pos) { {"cl", "--driver-mode=cl"}, {"++", "--driver-mode=g++"}, {"flang", "--driver-mode=flang"}, + // For backwards compatibility, we create a symlink for `flang` called + // `flang-new`. This will be removed in the future. + {"flang-new", "--driver-mode=flang"}, {"clang-dxc", "--driver-mode=dxc"}, }; diff --git a/clang/lib/Driver/ToolChains/BareMetal.cpp b/clang/lib/Driver/ToolChains/BareMetal.cpp index 8aed9ed6e1881..f9a73f60973e4 100644 --- a/clang/lib/Driver/ToolChains/BareMetal.cpp +++ b/clang/lib/Driver/ToolChains/BareMetal.cpp @@ -97,9 +97,23 @@ static bool findRISCVMultilibs(const Driver &D, return false; } +static std::string computeBaseSysRoot(const Driver &D, bool IncludeTriple) { + if (!D.SysRoot.empty()) + return D.SysRoot; + + SmallString<128> SysRootDir(D.Dir); + llvm::sys::path::append(SysRootDir, "..", "lib", "clang-runtimes"); + + if (IncludeTriple) + llvm::sys::path::append(SysRootDir, D.getTargetTriple()); + + return std::string(SysRootDir); +} + BareMetal::BareMetal(const Driver &D, const llvm::Triple &Triple, const ArgList &Args) - : ToolChain(D, Triple, Args) { + : ToolChain(D, Triple, Args), + SysRoot(computeBaseSysRoot(D, /*IncludeTriple=*/true)) { getProgramPaths().push_back(getDriver().Dir); findMultilibs(D, Triple, Args); @@ -186,34 +200,46 @@ static void findMultilibsFromYAML(const ToolChain &TC, const Driver &D, return; D.Diag(clang::diag::warn_drv_missing_multilib) << llvm::join(Flags, " "); std::stringstream ss; + + // If multilib selection didn't complete successfully, report a list + // of all the configurations the user could have provided. for (const Multilib &Multilib : Result.Multilibs) - if (!Multilib.isFatalError()) + if (!Multilib.isError()) ss << "\n" << llvm::join(Multilib.flags(), " "); D.Diag(clang::diag::note_drv_available_multilibs) << ss.str(); + + // Now report any custom error messages requested by the YAML. We do + // this after displaying the list of available multilibs, because + // that list is probably large, and (in interactive use) risks + // scrolling the useful error message off the top of the user's + // terminal. + for (const Multilib &Multilib : Result.SelectedMultilibs) + if (Multilib.isError()) + D.Diag(clang::diag::err_drv_multilib_custom_error) + << Multilib.getErrorMessage(); + + // If there was an error, clear the SelectedMultilibs vector, in + // case it contains partial data. + Result.SelectedMultilibs.clear(); } static constexpr llvm::StringLiteral MultilibFilename = "multilib.yaml"; -// Get the sysroot, before multilib takes effect. -static std::string computeBaseSysRoot(const Driver &D, - const llvm::Triple &Triple) { - if (!D.SysRoot.empty()) - return D.SysRoot; - - SmallString<128> SysRootDir(D.Dir); - llvm::sys::path::append(SysRootDir, "..", "lib", "clang-runtimes"); - - SmallString<128> MultilibPath(SysRootDir); - llvm::sys::path::append(MultilibPath, MultilibFilename); - - // New behaviour: if multilib.yaml is found then use clang-runtimes as the - // sysroot. - if (D.getVFS().exists(MultilibPath)) - return std::string(SysRootDir); - - // Otherwise fall back to the old behaviour of appending the target triple. - llvm::sys::path::append(SysRootDir, D.getTargetTriple()); - return std::string(SysRootDir); +static std::optional> +getMultilibConfigPath(const Driver &D, const llvm::Triple &Triple, + const ArgList &Args) { + llvm::SmallString<128> MultilibPath; + if (Arg *ConfigFileArg = Args.getLastArg(options::OPT_multi_lib_config)) { + MultilibPath = ConfigFileArg->getValue(); + if (!D.getVFS().exists(MultilibPath)) { + D.Diag(clang::diag::err_drv_no_such_file) << MultilibPath.str(); + return {}; + } + } else { + MultilibPath = computeBaseSysRoot(D, /*IncludeTriple=*/false); + llvm::sys::path::append(MultilibPath, MultilibFilename); + } + return MultilibPath; } void BareMetal::findMultilibs(const Driver &D, const llvm::Triple &Triple, @@ -221,10 +247,15 @@ void BareMetal::findMultilibs(const Driver &D, const llvm::Triple &Triple, DetectedMultilibs Result; // Look for a multilib.yaml before trying target-specific hardwired logic. // If it exists, always do what it specifies. - llvm::SmallString<128> MultilibPath(computeBaseSysRoot(D, Triple)); - llvm::sys::path::append(MultilibPath, MultilibFilename); - if (D.getVFS().exists(MultilibPath)) { - findMultilibsFromYAML(*this, D, MultilibPath, Args, Result); + std::optional> MultilibPath = + getMultilibConfigPath(D, Triple, Args); + if (!MultilibPath) + return; + if (D.getVFS().exists(*MultilibPath)) { + // If multilib.yaml is found, update sysroot so it doesn't use a target + // specific suffix + SysRoot = computeBaseSysRoot(D, /*IncludeTriple=*/false); + findMultilibsFromYAML(*this, D, *MultilibPath, Args, Result); SelectedMultilibs = Result.SelectedMultilibs; Multilibs = Result.Multilibs; } else if (isRISCVBareMetal(Triple)) { @@ -248,9 +279,7 @@ Tool *BareMetal::buildStaticLibTool() const { return new tools::baremetal::StaticLibTool(*this); } -std::string BareMetal::computeSysRoot() const { - return computeBaseSysRoot(getDriver(), getTriple()); -} +std::string BareMetal::computeSysRoot() const { return SysRoot; } BareMetal::OrderedMultilibs BareMetal::getOrderedMultilibs() const { // Get multilibs in reverse order because they're ordered most-specific last. diff --git a/clang/lib/Driver/ToolChains/BareMetal.h b/clang/lib/Driver/ToolChains/BareMetal.h index 67b5aa5998fc3..b385c8cf76aab 100644 --- a/clang/lib/Driver/ToolChains/BareMetal.h +++ b/clang/lib/Driver/ToolChains/BareMetal.h @@ -78,6 +78,8 @@ class LLVM_LIBRARY_VISIBILITY BareMetal : public ToolChain { using OrderedMultilibs = llvm::iterator_range::const_reverse_iterator>; OrderedMultilibs getOrderedMultilibs() const; + + std::string SysRoot; }; } // namespace toolchains diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index b9987288d82d1..d3d33b0877545 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -3604,7 +3604,8 @@ static void RenderSSPOptions(const Driver &D, const ToolChain &TC, if (Arg *A = Args.getLastArg(options::OPT_mstack_protector_guard_EQ)) { StringRef Value = A->getValue(); if (!EffectiveTriple.isX86() && !EffectiveTriple.isAArch64() && - !EffectiveTriple.isARM() && !EffectiveTriple.isThumb()) + !EffectiveTriple.isARM() && !EffectiveTriple.isThumb() && + !EffectiveTriple.isRISCV()) D.Diag(diag::err_drv_unsupported_opt_for_target) << A->getAsString(Args) << TripleStr; if ((EffectiveTriple.isX86() || EffectiveTriple.isARM() || @@ -3644,13 +3645,28 @@ static void RenderSSPOptions(const Driver &D, const ToolChain &TC, << A->getOption().getName() << Value << "sysreg global"; return; } + if (EffectiveTriple.isRISCV()) { + if (Value != "tls" && Value != "global") { + D.Diag(diag::err_drv_invalid_value_with_suggestion) + << A->getOption().getName() << Value << "tls global"; + return; + } + if (Value == "tls") { + if (!Args.hasArg(options::OPT_mstack_protector_guard_offset_EQ)) { + D.Diag(diag::err_drv_ssp_missing_offset_argument) + << A->getAsString(Args); + return; + } + } + } A->render(Args, CmdArgs); } if (Arg *A = Args.getLastArg(options::OPT_mstack_protector_guard_offset_EQ)) { StringRef Value = A->getValue(); if (!EffectiveTriple.isX86() && !EffectiveTriple.isAArch64() && - !EffectiveTriple.isARM() && !EffectiveTriple.isThumb()) + !EffectiveTriple.isARM() && !EffectiveTriple.isThumb() && + !EffectiveTriple.isRISCV()) D.Diag(diag::err_drv_unsupported_opt_for_target) << A->getAsString(Args) << TripleStr; int Offset; @@ -3669,7 +3685,8 @@ static void RenderSSPOptions(const Driver &D, const ToolChain &TC, if (Arg *A = Args.getLastArg(options::OPT_mstack_protector_guard_reg_EQ)) { StringRef Value = A->getValue(); - if (!EffectiveTriple.isX86() && !EffectiveTriple.isAArch64()) + if (!EffectiveTriple.isX86() && !EffectiveTriple.isAArch64() && + !EffectiveTriple.isRISCV()) D.Diag(diag::err_drv_unsupported_opt_for_target) << A->getAsString(Args) << TripleStr; if (EffectiveTriple.isX86() && (Value != "fs" && Value != "gs")) { @@ -3681,6 +3698,11 @@ static void RenderSSPOptions(const Driver &D, const ToolChain &TC, D.Diag(diag::err_drv_invalid_value) << A->getOption().getName() << Value; return; } + if (EffectiveTriple.isRISCV() && Value != "tp") { + D.Diag(diag::err_drv_invalid_value_with_suggestion) + << A->getOption().getName() << Value << "tp"; + return; + } A->render(Args, CmdArgs); } @@ -5117,6 +5139,9 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, } } + if (Args.hasArg(options::OPT_fclangir)) + CmdArgs.push_back("-fclangir"); + if (IsOpenMPDevice) { // We have to pass the triple of the host if compiling for an OpenMP device. std::string NormalizedTriple = @@ -6258,8 +6283,6 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, options::OPT_fno_unique_internal_linkage_names); Args.addOptInFlag(CmdArgs, options::OPT_funique_basic_block_section_names, options::OPT_fno_unique_basic_block_section_names); - Args.addOptInFlag(CmdArgs, options::OPT_fconvergent_functions, - options::OPT_fno_convergent_functions); if (Arg *A = Args.getLastArg(options::OPT_fsplit_machine_functions, options::OPT_fno_split_machine_functions)) { @@ -6276,6 +6299,8 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, Args.AddLastArg(CmdArgs, options::OPT_finstrument_functions, options::OPT_finstrument_functions_after_inlining, options::OPT_finstrument_function_entry_bare); + Args.AddLastArg(CmdArgs, options::OPT_fconvergent_functions, + options::OPT_fno_convergent_functions); // NVPTX/AMDGCN doesn't support PGO or coverage. There's no runtime support // for sampling, overhead of call arc collection is way too high and there's @@ -7403,6 +7428,27 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, CmdArgs.push_back("-faligned-allocation"); } + if (Arg *A = Args.getLastArg(options::OPT_fcontracts, options::OPT_fno_contracts)) { + [&]() { + if (A->getOption().matches(options::OPT_fno_contracts)) { + CmdArgs.push_back("-fno-contracts"); + return; + } + CmdArgs.push_back("-fcontracts"); + if ((A = Args.getLastArg(options::OPT_fcontract_evaluation_semantic_EQ))) { + CmdArgs.push_back(Args.MakeArgString( + Twine("-fcontract-evaluation-semantic=") + A->getValue())); + } + std::vector ContractGroups = + Args.getAllArgValues(options::OPT_fcontract_group_evaluation_semantic_EQ); + CmdArgs.push_back(Args.MakeArgString(Twine("-fcontract-group-evaluation-semantic=") + + llvm::join(ContractGroups, ","))); + + Args.addOptOutFlag(CmdArgs, options::OPT_fcontract_exceptions, + options::OPT_fno_contract_exceptions); + }(); + } + // The default new alignment can be specified using a dedicated option or via // a GCC-compatible option that also turns on aligned allocation. if (Arg *A = Args.getLastArg(options::OPT_fnew_alignment_EQ, @@ -9085,13 +9131,6 @@ void OffloadPackager::ConstructJob(Compilation &C, const JobAction &JA, llvm::copy_if(Features, std::back_inserter(FeatureArgs), [](StringRef Arg) { return !Arg.starts_with("-target"); }); - if (TC->getTriple().isAMDGPU()) { - for (StringRef Feature : llvm::split(Arch.split(':').second, ':')) { - FeatureArgs.emplace_back( - Args.MakeArgString(Feature.take_back() + Feature.drop_back())); - } - } - // TODO: We need to pass in the full target-id and handle it properly in the // linker wrapper. SmallVector Parts{ @@ -9101,7 +9140,7 @@ void OffloadPackager::ConstructJob(Compilation &C, const JobAction &JA, "kind=" + Kind.str(), }; - if (TC->getDriver().isUsingOffloadLTO() || TC->getTriple().isAMDGPU()) + if (TC->getDriver().isUsingOffloadLTO()) for (StringRef Feature : FeatureArgs) Parts.emplace_back("feature=" + Feature.str()); diff --git a/clang/lib/Driver/ToolChains/Cuda.cpp b/clang/lib/Driver/ToolChains/Cuda.cpp index 509cd87b28c37..7a70cf1c5694f 100644 --- a/clang/lib/Driver/ToolChains/Cuda.cpp +++ b/clang/lib/Driver/ToolChains/Cuda.cpp @@ -848,6 +848,10 @@ void CudaToolChain::addClangTargetOptions( if (CudaInstallation.version() >= CudaVersion::CUDA_90) CC1Args.push_back("-fcuda-allow-variadic-functions"); + if (DriverArgs.hasFlag(options::OPT_fcuda_short_ptr, + options::OPT_fno_cuda_short_ptr, false)) + CC1Args.append({"-mllvm", "--nvptx-short-ptr"}); + if (DriverArgs.hasArg(options::OPT_nogpulib)) return; @@ -873,10 +877,6 @@ void CudaToolChain::addClangTargetOptions( clang::CudaVersion CudaInstallationVersion = CudaInstallation.version(); - if (DriverArgs.hasFlag(options::OPT_fcuda_short_ptr, - options::OPT_fno_cuda_short_ptr, false)) - CC1Args.append({"-mllvm", "--nvptx-short-ptr"}); - if (CudaInstallationVersion >= CudaVersion::UNKNOWN) CC1Args.push_back( DriverArgs.MakeArgString(Twine("-target-sdk-version=") + diff --git a/clang/lib/Driver/ToolChains/Flang.cpp b/clang/lib/Driver/ToolChains/Flang.cpp index 98350690f8d20..19b43594b0081 100644 --- a/clang/lib/Driver/ToolChains/Flang.cpp +++ b/clang/lib/Driver/ToolChains/Flang.cpp @@ -787,6 +787,9 @@ void Flang::ConstructJob(Compilation &C, const JobAction &JA, if (Args.hasArg(options::OPT_fopenmp_force_usm)) CmdArgs.push_back("-fopenmp-force-usm"); + // TODO: OpenMP support isn't "done" yet, so for now we warn that it + // is experimental. + D.Diag(diag::warn_openmp_experimental); // FIXME: Clang supports a whole bunch more flags here. break; @@ -881,14 +884,12 @@ void Flang::ConstructJob(Compilation &C, const JobAction &JA, CmdArgs.push_back(Input.getFilename()); - // TODO: Replace flang-new with flang once the new driver replaces the - // throwaway driver - const char *Exec = Args.MakeArgString(D.GetProgramPath("flang-new", TC)); + const char *Exec = Args.MakeArgString(D.GetProgramPath("flang", TC)); C.addCommand(std::make_unique(JA, *this, ResponseFileSupport::AtFileUTF8(), Exec, CmdArgs, Inputs, Output)); } -Flang::Flang(const ToolChain &TC) : Tool("flang-new", "flang frontend", TC) {} +Flang::Flang(const ToolChain &TC) : Tool("flang", "flang frontend", TC) {} Flang::~Flang() {} diff --git a/clang/lib/Driver/ToolChains/ZOS.cpp b/clang/lib/Driver/ToolChains/ZOS.cpp index 074e0556ecd2a..c5ad3ef1b00f1 100644 --- a/clang/lib/Driver/ToolChains/ZOS.cpp +++ b/clang/lib/Driver/ToolChains/ZOS.cpp @@ -37,6 +37,10 @@ void ZOS::addClangTargetOptions(const ArgList &DriverArgs, options::OPT_fno_aligned_allocation)) CC1Args.push_back("-faligned-alloc-unavailable"); + if (DriverArgs.hasFlag(options::OPT_fxl_pragma_pack, + options::OPT_fno_xl_pragma_pack, true)) + CC1Args.push_back("-fxl-pragma-pack"); + // Pass "-fno-sized-deallocation" only when the user hasn't manually enabled // or disabled sized deallocations. if (!DriverArgs.hasArgNoClaim(options::OPT_fsized_deallocation, diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp index d2463b892fbb9..f97127f09d209 100644 --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -44,30 +44,30 @@ struct ScalarEnumerationTraits { template <> struct MappingTraits { static void enumInput(IO &IO, FormatStyle::AlignConsecutiveStyle &Value) { - IO.enumCase(Value, "None", - FormatStyle::AlignConsecutiveStyle( - {/*Enabled=*/false, /*AcrossEmptyLines=*/false, - /*AcrossComments=*/false, /*AlignCompound=*/false, - /*AlignFunctionPointers=*/false, /*PadOperators=*/true})); + IO.enumCase(Value, "None", FormatStyle::AlignConsecutiveStyle({})); IO.enumCase(Value, "Consecutive", FormatStyle::AlignConsecutiveStyle( {/*Enabled=*/true, /*AcrossEmptyLines=*/false, /*AcrossComments=*/false, /*AlignCompound=*/false, + /*AlignFunctionDeclarations=*/true, /*AlignFunctionPointers=*/false, /*PadOperators=*/true})); IO.enumCase(Value, "AcrossEmptyLines", FormatStyle::AlignConsecutiveStyle( {/*Enabled=*/true, /*AcrossEmptyLines=*/true, /*AcrossComments=*/false, /*AlignCompound=*/false, + /*AlignFunctionDeclarations=*/true, /*AlignFunctionPointers=*/false, /*PadOperators=*/true})); IO.enumCase(Value, "AcrossComments", FormatStyle::AlignConsecutiveStyle( {/*Enabled=*/true, /*AcrossEmptyLines=*/false, /*AcrossComments=*/true, /*AlignCompound=*/false, + /*AlignFunctionDeclarations=*/true, /*AlignFunctionPointers=*/false, /*PadOperators=*/true})); IO.enumCase(Value, "AcrossEmptyLinesAndComments", FormatStyle::AlignConsecutiveStyle( {/*Enabled=*/true, /*AcrossEmptyLines=*/true, /*AcrossComments=*/true, /*AlignCompound=*/false, + /*AlignFunctionDeclarations=*/true, /*AlignFunctionPointers=*/false, /*PadOperators=*/true})); // For backward compatibility. @@ -75,12 +75,9 @@ template <> struct MappingTraits { FormatStyle::AlignConsecutiveStyle( {/*Enabled=*/true, /*AcrossEmptyLines=*/false, /*AcrossComments=*/false, /*AlignCompound=*/false, + /*AlignFunctionDeclarations=*/true, /*AlignFunctionPointers=*/false, /*PadOperators=*/true})); - IO.enumCase(Value, "false", - FormatStyle::AlignConsecutiveStyle( - {/*Enabled=*/false, /*AcrossEmptyLines=*/false, - /*AcrossComments=*/false, /*AlignCompound=*/false, - /*AlignFunctionPointers=*/false, /*PadOperators=*/true})); + IO.enumCase(Value, "false", FormatStyle::AlignConsecutiveStyle({})); } static void mapping(IO &IO, FormatStyle::AlignConsecutiveStyle &Value) { @@ -88,6 +85,8 @@ template <> struct MappingTraits { IO.mapOptional("AcrossEmptyLines", Value.AcrossEmptyLines); IO.mapOptional("AcrossComments", Value.AcrossComments); IO.mapOptional("AlignCompound", Value.AlignCompound); + IO.mapOptional("AlignFunctionDeclarations", + Value.AlignFunctionDeclarations); IO.mapOptional("AlignFunctionPointers", Value.AlignFunctionPointers); IO.mapOptional("PadOperators", Value.PadOperators); } @@ -1147,6 +1146,7 @@ template <> struct MappingTraits { IO.mapOptional("TableGenBreakInsideDAGArg", Style.TableGenBreakInsideDAGArg); IO.mapOptional("TabWidth", Style.TabWidth); + IO.mapOptional("TemplateNames", Style.TemplateNames); IO.mapOptional("TypeNames", Style.TypeNames); IO.mapOptional("TypenameMacros", Style.TypenameMacros); IO.mapOptional("UseTab", Style.UseTab); @@ -1440,14 +1440,10 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) { LLVMStyle.AlignAfterOpenBracket = FormatStyle::BAS_Align; LLVMStyle.AlignArrayOfStructures = FormatStyle::AIAS_None; LLVMStyle.AlignConsecutiveAssignments = {}; - LLVMStyle.AlignConsecutiveAssignments.AcrossComments = false; - LLVMStyle.AlignConsecutiveAssignments.AcrossEmptyLines = false; - LLVMStyle.AlignConsecutiveAssignments.AlignCompound = false; - LLVMStyle.AlignConsecutiveAssignments.AlignFunctionPointers = false; - LLVMStyle.AlignConsecutiveAssignments.Enabled = false; LLVMStyle.AlignConsecutiveAssignments.PadOperators = true; LLVMStyle.AlignConsecutiveBitFields = {}; LLVMStyle.AlignConsecutiveDeclarations = {}; + LLVMStyle.AlignConsecutiveDeclarations.AlignFunctionDeclarations = true; LLVMStyle.AlignConsecutiveMacros = {}; LLVMStyle.AlignConsecutiveShortCaseStatements = {}; LLVMStyle.AlignConsecutiveTableGenBreakingDAGArgColons = {}; diff --git a/clang/lib/Format/FormatToken.h b/clang/lib/Format/FormatToken.h index 03c0cbd60961a..7d342a7dcca01 100644 --- a/clang/lib/Format/FormatToken.h +++ b/clang/lib/Format/FormatToken.h @@ -179,6 +179,7 @@ namespace format { TYPE(TrailingReturnArrow) \ TYPE(TrailingUnaryOperator) \ TYPE(TypeDeclarationParen) \ + TYPE(TemplateName) \ TYPE(TypeName) \ TYPE(TypenameMacro) \ TYPE(UnaryOperator) \ diff --git a/clang/lib/Format/FormatTokenLexer.cpp b/clang/lib/Format/FormatTokenLexer.cpp index 63949b2e26bdc..2cdf6cd286b28 100644 --- a/clang/lib/Format/FormatTokenLexer.cpp +++ b/clang/lib/Format/FormatTokenLexer.cpp @@ -72,6 +72,8 @@ FormatTokenLexer::FormatTokenLexer( Macros.insert({Identifier, TT_StatementAttributeLikeMacro}); } + for (const auto &TemplateName : Style.TemplateNames) + TemplateNames.insert(&IdentTable.get(TemplateName)); for (const auto &TypeName : Style.TypeNames) TypeNames.insert(&IdentTable.get(TypeName)); } @@ -1368,6 +1370,8 @@ FormatToken *FormatTokenLexer::getNextToken() { FormatTok->setType(TT_MacroBlockBegin); else if (MacroBlockEndRegex.match(Text)) FormatTok->setType(TT_MacroBlockEnd); + else if (TemplateNames.contains(Identifier)) + FormatTok->setFinalizedType(TT_TemplateName); else if (TypeNames.contains(Identifier)) FormatTok->setFinalizedType(TT_TypeName); } diff --git a/clang/lib/Format/FormatTokenLexer.h b/clang/lib/Format/FormatTokenLexer.h index 277cc0a2dfde6..71389d2ade2b7 100644 --- a/clang/lib/Format/FormatTokenLexer.h +++ b/clang/lib/Format/FormatTokenLexer.h @@ -129,7 +129,7 @@ class FormatTokenLexer { llvm::SmallMapVector Macros; - llvm::SmallPtrSet TypeNames; + llvm::SmallPtrSet TemplateNames, TypeNames; bool FormattingDisabled; diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp index f665ce2ad81eb..364d7e9855e8c 100644 --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -149,36 +149,36 @@ class AnnotatingParser { } bool parseAngle() { - if (!CurrentToken || !CurrentToken->Previous) + if (!CurrentToken) + return false; + + auto *Left = CurrentToken->Previous; // The '<'. + if (!Left) return false; - if (NonTemplateLess.count(CurrentToken->Previous) > 0) + + if (NonTemplateLess.count(Left) > 0) return false; - if (const auto &Previous = *CurrentToken->Previous; // The '<'. - Previous.Previous) { - if (Previous.Previous->Tok.isLiteral()) + const auto *BeforeLess = Left->Previous; + + if (BeforeLess) { + if (BeforeLess->Tok.isLiteral()) return false; - if (Previous.Previous->is(tok::r_brace)) + if (BeforeLess->is(tok::r_brace)) return false; - if (Previous.Previous->is(tok::r_paren) && Contexts.size() > 1 && - (!Previous.Previous->MatchingParen || - Previous.Previous->MatchingParen->isNot( - TT_OverloadedOperatorLParen))) { + if (BeforeLess->is(tok::r_paren) && Contexts.size() > 1 && + !(BeforeLess->MatchingParen && + BeforeLess->MatchingParen->is(TT_OverloadedOperatorLParen))) { return false; } - if (Previous.Previous->is(tok::kw_operator) && - CurrentToken->is(tok::l_paren)) { + if (BeforeLess->is(tok::kw_operator) && CurrentToken->is(tok::l_paren)) return false; - } } - FormatToken *Left = CurrentToken->Previous; Left->ParentBracket = Contexts.back().ContextKind; ScopedContextCreator ContextCreator(*this, tok::less, 12); Contexts.back().IsExpression = false; - const auto *BeforeLess = Left->Previous; - // If there's a template keyword before the opening angle bracket, this is a // template parameter, not an argument. if (BeforeLess && BeforeLess->isNot(tok::kw_template)) @@ -189,25 +189,29 @@ class AnnotatingParser { next(); } - for (bool SeenTernaryOperator = false; CurrentToken;) { + for (bool SeenTernaryOperator = false, MaybeAngles = true; CurrentToken;) { const bool InExpr = Contexts[Contexts.size() - 2].IsExpression; if (CurrentToken->is(tok::greater)) { const auto *Next = CurrentToken->Next; - // Try to do a better job at looking for ">>" within the condition of - // a statement. Conservatively insert spaces between consecutive ">" - // tokens to prevent splitting right bitshift operators and potentially - // altering program semantics. This check is overly conservative and - // will prevent spaces from being inserted in select nested template - // parameter cases, but should not alter program semantics. - if (Next && Next->is(tok::greater) && - Left->ParentBracket != tok::less && - CurrentToken->getStartOfNonWhitespace() == - Next->getStartOfNonWhitespace().getLocWithOffset(-1)) { - return false; - } - if (InExpr && SeenTernaryOperator && - (!Next || !Next->isOneOf(tok::l_paren, tok::l_brace))) { - return false; + if (CurrentToken->isNot(TT_TemplateCloser)) { + // Try to do a better job at looking for ">>" within the condition of + // a statement. Conservatively insert spaces between consecutive ">" + // tokens to prevent splitting right shift operators and potentially + // altering program semantics. This check is overly conservative and + // will prevent spaces from being inserted in select nested template + // parameter cases, but should not alter program semantics. + if (Next && Next->is(tok::greater) && + Left->ParentBracket != tok::less && + CurrentToken->getStartOfNonWhitespace() == + Next->getStartOfNonWhitespace().getLocWithOffset(-1)) { + return false; + } + if (InExpr && SeenTernaryOperator && + (!Next || !Next->isOneOf(tok::l_paren, tok::l_brace))) { + return false; + } + if (!MaybeAngles) + return false; } Left->MatchingParen = CurrentToken; CurrentToken->MatchingParen = Left; @@ -229,6 +233,10 @@ class AnnotatingParser { next(); return true; } + if (BeforeLess && BeforeLess->is(TT_TemplateName)) { + next(); + continue; + } if (CurrentToken->is(tok::question) && Style.Language == FormatStyle::LK_Java) { next(); @@ -243,11 +251,11 @@ class AnnotatingParser { // operator that was misinterpreted because we are parsing template // parameters. // FIXME: This is getting out of hand, write a decent parser. - if (InExpr && !Line.startsWith(tok::kw_template) && + if (MaybeAngles && InExpr && !Line.startsWith(tok::kw_template) && Prev.is(TT_BinaryOperator)) { const auto Precedence = Prev.getPrecedence(); if (Precedence > prec::Conditional && Precedence < prec::Relational) - return false; + MaybeAngles = false; } if (Prev.isOneOf(tok::question, tok::colon) && !Style.isProto()) SeenTernaryOperator = true; @@ -358,7 +366,7 @@ class AnnotatingParser { } else if (OpeningParen.Previous && (OpeningParen.Previous->isOneOf( tok::kw_static_assert, tok::kw_noexcept, tok::kw_explicit, - tok::kw_while, tok::l_paren, tok::comma, + tok::kw_while, tok::l_paren, tok::comma, TT_CastRParen, TT_BinaryOperator) || OpeningParen.Previous->isIf())) { // static_assert, if and while usually contain expressions. @@ -468,16 +476,18 @@ class AnnotatingParser { OpeningParen.Previous && OpeningParen.Previous->is(tok::kw_for); FormatToken *PossibleObjCForInToken = nullptr; while (CurrentToken) { - if (CurrentToken->Previous->is(TT_PointerOrReference) && - CurrentToken->Previous->Previous->isOneOf(tok::l_paren, - tok::coloncolon)) { + const auto &Prev = *CurrentToken->Previous; + if (Prev.is(TT_PointerOrReference) && + Prev.Previous->isOneOf(tok::l_paren, tok::coloncolon)) { ProbablyFunctionType = true; } if (CurrentToken->is(tok::comma)) MightBeFunctionType = false; - if (CurrentToken->Previous->is(TT_BinaryOperator)) + if (Prev.is(TT_BinaryOperator)) Contexts.back().IsExpression = true; if (CurrentToken->is(tok::r_paren)) { + if (Prev.is(TT_PointerOrReference) && Prev.Previous == &OpeningParen) + MightBeFunctionType = true; if (OpeningParen.isNot(TT_CppCastLParen) && MightBeFunctionType && ProbablyFunctionType && CurrentToken->Next && (CurrentToken->Next->is(tok::l_paren) || @@ -550,8 +560,8 @@ class AnnotatingParser { bool ProbablyFunctionTypeLParen = (CurrentToken->is(tok::l_paren) && CurrentToken->Next && CurrentToken->Next->isOneOf(tok::star, tok::amp, tok::caret)); - if ((CurrentToken->Previous->isOneOf(tok::kw_const, tok::kw_auto) || - CurrentToken->Previous->isTypeName(LangOpts)) && + if ((Prev.isOneOf(tok::kw_const, tok::kw_auto) || + Prev.isTypeName(LangOpts)) && !(CurrentToken->is(tok::l_brace) || (CurrentToken->is(tok::l_paren) && !ProbablyFunctionTypeLParen))) { Contexts.back().IsExpression = false; @@ -1165,19 +1175,26 @@ class AnnotatingParser { ScopedContextCreator ContextCreator(*this, tok::l_brace, 1); Contexts.back().ColonIsDictLiteral = true; - if (OpeningBrace.is(BK_BracedInit)) + + const auto *Prev = OpeningBrace.getPreviousNonComment(); + + if (OpeningBrace.is(BK_BracedInit)) { Contexts.back().IsExpression = true; - if (Style.isJavaScript() && OpeningBrace.Previous && - OpeningBrace.Previous->is(TT_JsTypeColon)) { - Contexts.back().IsExpression = false; - } - if (Style.isVerilog() && - (!OpeningBrace.getPreviousNonComment() || - OpeningBrace.getPreviousNonComment()->isNot(Keywords.kw_apostrophe))) { - Contexts.back().VerilogMayBeConcatenation = true; + if (Prev) { + for (auto *Tok = Prev->Previous; Tok && Tok->isPointerOrReference(); + Tok = Tok->Previous) { + Tok->setFinalizedType(TT_PointerOrReference); + } + } } + + if (Style.isJavaScript() && Prev && Prev->is(TT_JsTypeColon)) + Contexts.back().IsExpression = false; + if (Style.isTableGen()) Contexts.back().ColonIsDictLiteral = false; + else if (Style.isVerilog() && !(Prev && Prev->is(Keywords.kw_apostrophe))) + Contexts.back().VerilogMayBeConcatenation = true; unsigned CommaCount = 0; while (CurrentToken) { @@ -1614,7 +1631,7 @@ class AnnotatingParser { return false; break; case tok::greater: - if (Style.Language != FormatStyle::LK_TextProto) + if (Style.Language != FormatStyle::LK_TextProto && Tok->is(TT_Unknown)) Tok->setType(TT_BinaryOperator); if (Tok->Previous && Tok->Previous->is(TT_TemplateCloser)) Tok->SpacesRequiredBefore = 1; @@ -3721,6 +3738,13 @@ static bool isFunctionDeclarationName(const LangOptions &LangOpts, const auto *Prev = Current.getPreviousNonComment(); assert(Prev); + + if (Prev->is(tok::coloncolon)) + Prev = Prev->Previous; + + if (!Prev) + return false; + const auto &Previous = *Prev; if (const auto *PrevPrev = Previous.getPreviousNonComment(); @@ -4886,6 +4910,8 @@ bool TokenAnnotator::spaceRequiredBefore(const AnnotatedLine &Line, if (Left.is(tok::star) && Right.is(tok::comment)) return true; + const auto *BeforeLeft = Left.Previous; + if (IsCpp) { if (Left.is(TT_OverloadedOperator) && Right.isOneOf(TT_TemplateOpener, TT_TemplateCloser)) { @@ -4938,7 +4964,7 @@ bool TokenAnnotator::spaceRequiredBefore(const AnnotatedLine &Line, if (Left.Tok.getIdentifierInfo() && Right.Tok.isLiteral()) return true; } else if (Style.isProto()) { - if (Right.is(tok::period) && + if (Right.is(tok::period) && !(BeforeLeft && BeforeLeft->is(tok::period)) && Left.isOneOf(Keywords.kw_optional, Keywords.kw_required, Keywords.kw_repeated, Keywords.kw_extend)) { return true; @@ -5046,8 +5072,8 @@ bool TokenAnnotator::spaceRequiredBefore(const AnnotatedLine &Line, if (Left.is(TT_FatArrow)) return true; // for await ( ... - if (Right.is(tok::l_paren) && Left.is(Keywords.kw_await) && Left.Previous && - Left.Previous->is(tok::kw_for)) { + if (Right.is(tok::l_paren) && Left.is(Keywords.kw_await) && BeforeLeft && + BeforeLeft->is(tok::kw_for)) { return true; } if (Left.is(Keywords.kw_async) && Right.is(tok::l_paren) && @@ -5084,7 +5110,7 @@ bool TokenAnnotator::spaceRequiredBefore(const AnnotatedLine &Line, return false; // Valid JS method names can include keywords, e.g. `foo.delete()` or // `bar.instanceof()`. Recognize call positions by preceding period. - if (Left.Previous && Left.Previous->is(tok::period) && + if (BeforeLeft && BeforeLeft->is(tok::period) && Left.Tok.getIdentifierInfo()) { return false; } @@ -5102,22 +5128,22 @@ bool TokenAnnotator::spaceRequiredBefore(const AnnotatedLine &Line, // "of" is only a keyword if it appears after another identifier // (e.g. as "const x of y" in a for loop), or after a destructuring // operation (const [x, y] of z, const {a, b} of c). - (Left.is(Keywords.kw_of) && Left.Previous && - (Left.Previous->is(tok::identifier) || - Left.Previous->isOneOf(tok::r_square, tok::r_brace)))) && - (!Left.Previous || Left.Previous->isNot(tok::period))) { + (Left.is(Keywords.kw_of) && BeforeLeft && + (BeforeLeft->is(tok::identifier) || + BeforeLeft->isOneOf(tok::r_square, tok::r_brace)))) && + (!BeforeLeft || BeforeLeft->isNot(tok::period))) { return true; } - if (Left.isOneOf(tok::kw_for, Keywords.kw_as) && Left.Previous && - Left.Previous->is(tok::period) && Right.is(tok::l_paren)) { + if (Left.isOneOf(tok::kw_for, Keywords.kw_as) && BeforeLeft && + BeforeLeft->is(tok::period) && Right.is(tok::l_paren)) { return false; } if (Left.is(Keywords.kw_as) && Right.isOneOf(tok::l_square, tok::l_brace, tok::l_paren)) { return true; } - if (Left.is(tok::kw_default) && Left.Previous && - Left.Previous->is(tok::kw_export)) { + if (Left.is(tok::kw_default) && BeforeLeft && + BeforeLeft->is(tok::kw_export)) { return true; } if (Left.is(Keywords.kw_is) && Right.is(tok::l_brace)) @@ -5428,6 +5454,10 @@ bool TokenAnnotator::spaceRequiredBefore(const AnnotatedLine &Line, } if ((Left.is(TT_TemplateOpener)) != (Right.is(TT_TemplateCloser))) return ShouldAddSpacesInAngles(); + if (Left.is(tok::r_paren) && Right.is(TT_PointerOrReference) && + Right.isOneOf(tok::amp, tok::ampamp)) { + return true; + } // Space before TT_StructuredBindingLSquare. if (Right.is(TT_StructuredBindingLSquare)) { return !Left.isOneOf(tok::amp, tok::ampamp) || diff --git a/clang/lib/Format/UnwrappedLineParser.cpp b/clang/lib/Format/UnwrappedLineParser.cpp index 40f77266fabdc..9c4a065fd93b9 100644 --- a/clang/lib/Format/UnwrappedLineParser.cpp +++ b/clang/lib/Format/UnwrappedLineParser.cpp @@ -2131,6 +2131,11 @@ void UnwrappedLineParser::parseStructuralElement( return; } break; + case tok::greater: + nextToken(); + if (FormatTok->is(tok::l_brace)) + FormatTok->Previous->setFinalizedType(TT_TemplateCloser); + break; default: nextToken(); break; @@ -2556,7 +2561,7 @@ bool UnwrappedLineParser::parseParens(TokenType AmpAmpTokenType) { parseChildBlock(); break; case tok::r_paren: { - const auto *Prev = LeftParen->Previous; + auto *Prev = LeftParen->Previous; if (!MightBeStmtExpr && !MightBeFoldExpr && !Line->InMacroBody && Style.RemoveParentheses > FormatStyle::RPS_Leave) { const auto *Next = Tokens->peekNextToken(); @@ -2585,9 +2590,13 @@ bool UnwrappedLineParser::parseParens(TokenType AmpAmpTokenType) { FormatTok->Optional = true; } } - if (Prev && Prev->is(TT_TypenameMacro)) { - LeftParen->setFinalizedType(TT_TypeDeclarationParen); - FormatTok->setFinalizedType(TT_TypeDeclarationParen); + if (Prev) { + if (Prev->is(TT_TypenameMacro)) { + LeftParen->setFinalizedType(TT_TypeDeclarationParen); + FormatTok->setFinalizedType(TT_TypeDeclarationParen); + } else if (Prev->is(tok::greater) && FormatTok->Previous == LeftParen) { + Prev->setFinalizedType(TT_TemplateCloser); + } } nextToken(); return SeenEqual; diff --git a/clang/lib/Format/WhitespaceManager.cpp b/clang/lib/Format/WhitespaceManager.cpp index fd4a40a86082e..b6b24672f6a39 100644 --- a/clang/lib/Format/WhitespaceManager.cpp +++ b/clang/lib/Format/WhitespaceManager.cpp @@ -1020,7 +1020,7 @@ void WhitespaceManager::alignConsecutiveDeclarations() { return true; } if (C.Tok->is(TT_FunctionDeclarationName)) - return true; + return Style.AlignConsecutiveDeclarations.AlignFunctionDeclarations; if (C.Tok->isNot(TT_StartOfName)) return false; if (C.Tok->Previous && diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index a0291ccfea245..db2a4b5ec5505 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -3588,6 +3588,10 @@ void CompilerInvocationBase::GenerateLangArgs(const LangOptions &Opts, for (StringRef Sanitizer : serializeSanitizerKinds(Opts.Sanitize)) GenerateArg(Consumer, OPT_fsanitize_EQ, Sanitizer); + + for (StringRef ContractGroup : + Opts.ContractOpts.serializeContractGroupArgs()) + GenerateArg(Consumer, OPT_fcontract_group_evaluation_semantic_EQ, ContractGroup); return; } @@ -3687,10 +3691,10 @@ void CompilerInvocationBase::GenerateLangArgs(const LangOptions &Opts, if (Opts.Blocks && !(Opts.OpenCL && Opts.OpenCLVersion == 200)) GenerateArg(Consumer, OPT_fblocks); - if (Opts.ConvergentFunctions && - !(Opts.OpenCL || (Opts.CUDA && Opts.CUDAIsDevice) || Opts.SYCLIsDevice || - Opts.HLSL)) + if (Opts.ConvergentFunctions) GenerateArg(Consumer, OPT_fconvergent_functions); + else + GenerateArg(Consumer, OPT_fno_convergent_functions); if (Opts.NoBuiltin && !Opts.Freestanding) GenerateArg(Consumer, OPT_fno_builtin); @@ -3867,6 +3871,10 @@ void CompilerInvocationBase::GenerateLangArgs(const LangOptions &Opts, if (!Opts.RandstructSeed.empty()) GenerateArg(Consumer, OPT_frandomize_layout_seed_EQ, Opts.RandstructSeed); + + for (StringRef ContractGroup : + Opts.ContractOpts.serializeContractGroupArgs()) + GenerateArg(Consumer, OPT_fcontract_group_evaluation_semantic_EQ, ContractGroup); } bool CompilerInvocation::ParseLangArgs(LangOptions &Opts, ArgList &Args, @@ -4106,9 +4114,12 @@ bool CompilerInvocation::ParseLangArgs(LangOptions &Opts, ArgList &Args, Opts.Blocks = Args.hasArg(OPT_fblocks) || (Opts.OpenCL && Opts.OpenCLVersion == 200); - Opts.ConvergentFunctions = Args.hasArg(OPT_fconvergent_functions) || - Opts.OpenCL || (Opts.CUDA && Opts.CUDAIsDevice) || - Opts.SYCLIsDevice || Opts.HLSL; + bool HasConvergentOperations = Opts.OpenMPIsTargetDevice || Opts.OpenCL || + Opts.CUDAIsDevice || Opts.SYCLIsDevice || + Opts.HLSL || T.isAMDGPU() || T.isNVPTX(); + Opts.ConvergentFunctions = + Args.hasFlag(OPT_fconvergent_functions, OPT_fno_convergent_functions, + HasConvergentOperations); Opts.NoBuiltin = Args.hasArg(OPT_fno_builtin) || Opts.Freestanding; if (!Opts.NoBuiltin) @@ -4164,9 +4175,6 @@ bool CompilerInvocation::ParseLangArgs(LangOptions &Opts, ArgList &Args, bool IsTargetSpecified = Opts.OpenMPIsTargetDevice || Args.hasArg(options::OPT_fopenmp_targets_EQ); - Opts.ConvergentFunctions = - Opts.ConvergentFunctions || Opts.OpenMPIsTargetDevice; - if (Opts.OpenMP || Opts.OpenMPSimd) { if (int Version = getLastArgIntValue( Args, OPT_fopenmp_version_EQ, @@ -4540,6 +4548,17 @@ bool CompilerInvocation::ParseLangArgs(LangOptions &Opts, ArgList &Args, } } + auto EmitContractDiag = [&](ContractGroupDiagnostic CGD, StringRef GroupName, + StringRef InvalidChar = "") { + Diags.Report(diag::err_drv_contract_group_name_invalid) + << (int)CGD << GroupName << InvalidChar; + }; + + std::vector ContractGroupValues = + Args.getAllArgValues(options::OPT_fcontract_group_evaluation_semantic_EQ); + Opts.ContractOpts.parseContractGroups(ContractGroupValues, + EmitContractDiag); + return Diags.getNumErrors() == NumErrorsBefore; } diff --git a/clang/lib/Frontend/FrontendActions.cpp b/clang/lib/Frontend/FrontendActions.cpp index e4b462b9b0fd8..64f90c493c105 100644 --- a/clang/lib/Frontend/FrontendActions.cpp +++ b/clang/lib/Frontend/FrontendActions.cpp @@ -457,8 +457,6 @@ class DefaultTemplateInstCallback : public TemplateInstantiationCallback { return "BuildingDeductionGuides"; case CodeSynthesisContext::TypeAliasTemplateInstantiation: return "TypeAliasTemplateInstantiation"; - case CodeSynthesisContext::PartialOrderingTTP: - return "PartialOrderingTTP"; } return ""; } diff --git a/clang/lib/Frontend/TextDiagnostic.cpp b/clang/lib/Frontend/TextDiagnostic.cpp index a264836a54398..4119ce6048d45 100644 --- a/clang/lib/Frontend/TextDiagnostic.cpp +++ b/clang/lib/Frontend/TextDiagnostic.cpp @@ -1252,10 +1252,10 @@ highlightLines(StringRef FileData, unsigned StartLineNumber, for (unsigned I = 0; I <= Spelling.size(); ++I) { // This line is done. if (I == Spelling.size() || isVerticalWhitespace(Spelling[I])) { - SmallVector &LineRanges = - SnippetRanges[L - StartLineNumber]; - if (L >= StartLineNumber) { + SmallVector &LineRanges = + SnippetRanges[L - StartLineNumber]; + if (L == TokenStartLine) // First line appendStyle(LineRanges, T, StartCol, LineLength); else if (L == TokenEndLine) // Last line diff --git a/clang/lib/FrontendTool/CMakeLists.txt b/clang/lib/FrontendTool/CMakeLists.txt index 51c379ade2704..bfc7652b4c118 100644 --- a/clang/lib/FrontendTool/CMakeLists.txt +++ b/clang/lib/FrontendTool/CMakeLists.txt @@ -12,6 +12,15 @@ set(link_libs clangRewriteFrontend ) +set(deps) + +if(CLANG_ENABLE_CIR) + list(APPEND link_libs + clangCIRFrontendAction + MLIRIR + ) +endif() + if(CLANG_ENABLE_ARCMT) list(APPEND link_libs clangARCMigrate @@ -29,7 +38,13 @@ add_clang_library(clangFrontendTool DEPENDS ClangDriverOptions + ${deps} LINK_LIBS ${link_libs} ) + +if(CLANG_ENABLE_CIR) + target_include_directories(clangFrontendTool PRIVATE ${LLVM_MAIN_SRC_DIR}/../mlir/include) + target_include_directories(clangFrontendTool PRIVATE ${CMAKE_BINARY_DIR}/tools/mlir/include) +endif() diff --git a/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp b/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp index 7476b1076d103..60fde03289cf3 100644 --- a/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp +++ b/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp @@ -31,6 +31,11 @@ #include "llvm/Support/BuryPointer.h" #include "llvm/Support/DynamicLibrary.h" #include "llvm/Support/ErrorHandling.h" + +#if CLANG_ENABLE_CIR +#include "clang/CIR/FrontendAction/CIRGenAction.h" +#endif + using namespace clang; using namespace llvm::opt; @@ -42,6 +47,13 @@ CreateFrontendBaseAction(CompilerInstance &CI) { StringRef Action("unknown"); (void)Action; + unsigned UseCIR = CI.getFrontendOpts().UseClangIRPipeline; + frontend::ActionKind Act = CI.getFrontendOpts().ProgramAction; + bool EmitsCIR = Act == EmitCIR; + + if (!UseCIR && EmitsCIR) + llvm::report_fatal_error("-emit-cir and only valid when using -fclangir"); + switch (CI.getFrontendOpts().ProgramAction) { case ASTDeclList: return std::make_unique(); case ASTDump: return std::make_unique(); @@ -54,7 +66,11 @@ CreateFrontendBaseAction(CompilerInstance &CI) { case EmitAssembly: return std::make_unique(); case EmitBC: return std::make_unique(); case EmitCIR: +#if CLANG_ENABLE_CIR + return std::make_unique<::cir::EmitCIRAction>(); +#else llvm_unreachable("CIR suppport not built into clang"); +#endif case EmitHTML: return std::make_unique(); case EmitLLVM: return std::make_unique(); case EmitLLVMOnly: return std::make_unique(); diff --git a/clang/lib/Headers/avx512fintrin.h b/clang/lib/Headers/avx512fintrin.h index 4f172c74b31cb..45e7eeb5327d0 100644 --- a/clang/lib/Headers/avx512fintrin.h +++ b/clang/lib/Headers/avx512fintrin.h @@ -175,12 +175,21 @@ typedef enum __attribute__((__always_inline__, __nodebug__, \ __target__("avx512f,no-evex512"))) +#if defined(__cplusplus) && (__cplusplus >= 201103L) +#define __DEFAULT_FN_ATTRS_CONSTEXPR __DEFAULT_FN_ATTRS constexpr +#define __DEFAULT_FN_ATTRS512_CONSTEXPR __DEFAULT_FN_ATTRS512 constexpr +#define __DEFAULT_FN_ATTRS128_CONSTEXPR __DEFAULT_FN_ATTRS128 constexpr +#else +#define __DEFAULT_FN_ATTRS_CONSTEXPR __DEFAULT_FN_ATTRS128 +#define __DEFAULT_FN_ATTRS512_CONSTEXPR __DEFAULT_FN_ATTRS512 +#define __DEFAULT_FN_ATTRS128_CONSTEXPR __DEFAULT_FN_ATTRS +#endif + /* Create vectors with repeated elements */ -static __inline __m512i __DEFAULT_FN_ATTRS512 -_mm512_setzero_si512(void) -{ - return __extension__ (__m512i)(__v8di){ 0, 0, 0, 0, 0, 0, 0, 0 }; +static __inline __m512i __DEFAULT_FN_ATTRS512_CONSTEXPR +_mm512_setzero_si512(void) { + return __extension__(__m512i)(__v8di){0, 0, 0, 0, 0, 0, 0, 0}; } #define _mm512_setzero_epi32 _mm512_setzero_si512 @@ -256,20 +265,16 @@ _mm512_maskz_broadcastq_epi64 (__mmask8 __M, __m128i __A) (__v8di) _mm512_setzero_si512()); } - -static __inline __m512 __DEFAULT_FN_ATTRS512 -_mm512_setzero_ps(void) -{ - return __extension__ (__m512){ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }; +static __inline __m512 __DEFAULT_FN_ATTRS512_CONSTEXPR _mm512_setzero_ps(void) { + return __extension__(__m512){0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}; } #define _mm512_setzero _mm512_setzero_ps -static __inline __m512d __DEFAULT_FN_ATTRS512 -_mm512_setzero_pd(void) -{ - return __extension__ (__m512d){ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 }; +static __inline __m512d __DEFAULT_FN_ATTRS512_CONSTEXPR +_mm512_setzero_pd(void) { + return __extension__(__m512d){0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; } static __inline __m512 __DEFAULT_FN_ATTRS512 @@ -9775,5 +9780,8 @@ _mm512_cvtsi512_si32(__m512i __A) { #undef __DEFAULT_FN_ATTRS512 #undef __DEFAULT_FN_ATTRS128 #undef __DEFAULT_FN_ATTRS +#undef __DEFAULT_FN_ATTRS512_CONSTEXPR +#undef __DEFAULT_FN_ATTRS128_CONSTEXPR +#undef __DEFAULT_FN_ATTRS_CONSTEXPR #endif /* __AVX512FINTRIN_H */ diff --git a/clang/lib/Headers/avxintrin.h b/clang/lib/Headers/avxintrin.h index 73707c623065e..bb43b292be01f 100644 --- a/clang/lib/Headers/avxintrin.h +++ b/clang/lib/Headers/avxintrin.h @@ -66,6 +66,14 @@ typedef __bf16 __m256bh __attribute__((__vector_size__(32), __aligned__(32))); __min_vector_width__(128))) #endif +#if defined(__cplusplus) && (__cplusplus >= 201103L) +#define __DEFAULT_FN_ATTRS_CONSTEXPR __DEFAULT_FN_ATTRS constexpr +#define __DEFAULT_FN_ATTRS128_CONSTEXPR __DEFAULT_FN_ATTRS128 constexpr +#else +#define __DEFAULT_FN_ATTRS_CONSTEXPR __DEFAULT_FN_ATTRS128 +#define __DEFAULT_FN_ATTRS128_CONSTEXPR __DEFAULT_FN_ATTRS +#endif + /* Arithmetic */ /// Adds two 256-bit vectors of [4 x double]. /// @@ -4331,10 +4339,8 @@ _mm256_set1_epi64x(long long __q) /// This intrinsic corresponds to the VXORPS instruction. /// /// \returns A 256-bit vector of [4 x double] with all elements set to zero. -static __inline __m256d __DEFAULT_FN_ATTRS -_mm256_setzero_pd(void) -{ - return __extension__ (__m256d){ 0.0, 0.0, 0.0, 0.0 }; +static __inline __m256d __DEFAULT_FN_ATTRS_CONSTEXPR _mm256_setzero_pd(void) { + return __extension__(__m256d){0.0, 0.0, 0.0, 0.0}; } /// Constructs a 256-bit floating-point vector of [8 x float] with all @@ -4345,9 +4351,7 @@ _mm256_setzero_pd(void) /// This intrinsic corresponds to the VXORPS instruction. /// /// \returns A 256-bit vector of [8 x float] with all elements set to zero. -static __inline __m256 __DEFAULT_FN_ATTRS -_mm256_setzero_ps(void) -{ +static __inline __m256 __DEFAULT_FN_ATTRS_CONSTEXPR _mm256_setzero_ps(void) { return __extension__ (__m256){ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }; } @@ -4358,9 +4362,8 @@ _mm256_setzero_ps(void) /// This intrinsic corresponds to the VXORPS instruction. /// /// \returns A 256-bit integer vector initialized to zero. -static __inline __m256i __DEFAULT_FN_ATTRS -_mm256_setzero_si256(void) -{ +static __inline __m256i __DEFAULT_FN_ATTRS_CONSTEXPR +_mm256_setzero_si256(void) { return __extension__ (__m256i)(__v4di){ 0, 0, 0, 0 }; } @@ -5130,6 +5133,8 @@ _mm256_storeu2_m128i(__m128i_u *__addr_hi, __m128i_u *__addr_lo, __m256i __a) } #undef __DEFAULT_FN_ATTRS +#undef __DEFAULT_FN_ATTRS_CONSTEXPR #undef __DEFAULT_FN_ATTRS128 +#undef __DEFAULT_FN_ATTRS128_CONSTEXPR #endif /* __AVXINTRIN_H */ diff --git a/clang/lib/Headers/cpuid.h b/clang/lib/Headers/cpuid.h index 82d995f1b966a..2601aa5724f05 100644 --- a/clang/lib/Headers/cpuid.h +++ b/clang/lib/Headers/cpuid.h @@ -187,17 +187,18 @@ #define bit_ENQCMD 0x20000000 /* Features in %edx for leaf 7 sub-leaf 0 */ -#define bit_AVX5124VNNIW 0x00000004 -#define bit_AVX5124FMAPS 0x00000008 -#define bit_UINTR 0x00000020 -#define bit_SERIALIZE 0x00004000 -#define bit_TSXLDTRK 0x00010000 -#define bit_PCONFIG 0x00040000 -#define bit_IBT 0x00100000 -#define bit_AMXBF16 0x00400000 -#define bit_AVX512FP16 0x00800000 -#define bit_AMXTILE 0x01000000 -#define bit_AMXINT8 0x02000000 +#define bit_AVX5124VNNIW 0x00000004 +#define bit_AVX5124FMAPS 0x00000008 +#define bit_UINTR 0x00000020 +#define bit_AVX512VP2INTERSECT 0x00000100 +#define bit_SERIALIZE 0x00004000 +#define bit_TSXLDTRK 0x00010000 +#define bit_PCONFIG 0x00040000 +#define bit_IBT 0x00100000 +#define bit_AMXBF16 0x00400000 +#define bit_AVX512FP16 0x00800000 +#define bit_AMXTILE 0x01000000 +#define bit_AMXINT8 0x02000000 /* Features in %eax for leaf 7 sub-leaf 1 */ #define bit_SHA512 0x00000001 diff --git a/clang/lib/Headers/emmintrin.h b/clang/lib/Headers/emmintrin.h index 72a32f953e011..d6494762169b2 100644 --- a/clang/lib/Headers/emmintrin.h +++ b/clang/lib/Headers/emmintrin.h @@ -59,6 +59,12 @@ typedef __bf16 __m128bh __attribute__((__vector_size__(16), __aligned__(16))); __min_vector_width__(128))) #endif +#if defined(__cplusplus) && (__cplusplus >= 201103L) +#define __DEFAULT_FN_ATTRS_CONSTEXPR __DEFAULT_FN_ATTRS constexpr +#else +#define __DEFAULT_FN_ATTRS_CONSTEXPR __DEFAULT_FN_ATTRS +#endif + #define __trunc64(x) \ (__m64) __builtin_shufflevector((__v2di)(x), __extension__(__v2di){}, 0) #define __anyext128(x) \ @@ -80,8 +86,8 @@ typedef __bf16 __m128bh __attribute__((__vector_size__(16), __aligned__(16))); /// \returns A 128-bit vector of [2 x double] whose lower 64 bits contain the /// sum of the lower 64 bits of both operands. The upper 64 bits are copied /// from the upper 64 bits of the first source operand. -static __inline__ __m128d __DEFAULT_FN_ATTRS _mm_add_sd(__m128d __a, - __m128d __b) { +static __inline__ __m128d __DEFAULT_FN_ATTRS_CONSTEXPR _mm_add_sd(__m128d __a, + __m128d __b) { __a[0] += __b[0]; return __a; } @@ -98,8 +104,8 @@ static __inline__ __m128d __DEFAULT_FN_ATTRS _mm_add_sd(__m128d __a, /// A 128-bit vector of [2 x double] containing one of the source operands. /// \returns A 128-bit vector of [2 x double] containing the sums of both /// operands. -static __inline__ __m128d __DEFAULT_FN_ATTRS _mm_add_pd(__m128d __a, - __m128d __b) { +static __inline__ __m128d __DEFAULT_FN_ATTRS_CONSTEXPR _mm_add_pd(__m128d __a, + __m128d __b) { return (__m128d)((__v2df)__a + (__v2df)__b); } @@ -120,8 +126,8 @@ static __inline__ __m128d __DEFAULT_FN_ATTRS _mm_add_pd(__m128d __a, /// \returns A 128-bit vector of [2 x double] whose lower 64 bits contain the /// difference of the lower 64 bits of both operands. The upper 64 bits are /// copied from the upper 64 bits of the first source operand. -static __inline__ __m128d __DEFAULT_FN_ATTRS _mm_sub_sd(__m128d __a, - __m128d __b) { +static __inline__ __m128d __DEFAULT_FN_ATTRS_CONSTEXPR _mm_sub_sd(__m128d __a, + __m128d __b) { __a[0] -= __b[0]; return __a; } @@ -138,8 +144,8 @@ static __inline__ __m128d __DEFAULT_FN_ATTRS _mm_sub_sd(__m128d __a, /// A 128-bit vector of [2 x double] containing the subtrahend. /// \returns A 128-bit vector of [2 x double] containing the differences between /// both operands. -static __inline__ __m128d __DEFAULT_FN_ATTRS _mm_sub_pd(__m128d __a, - __m128d __b) { +static __inline__ __m128d __DEFAULT_FN_ATTRS_CONSTEXPR _mm_sub_pd(__m128d __a, + __m128d __b) { return (__m128d)((__v2df)__a - (__v2df)__b); } @@ -159,8 +165,8 @@ static __inline__ __m128d __DEFAULT_FN_ATTRS _mm_sub_pd(__m128d __a, /// \returns A 128-bit vector of [2 x double] whose lower 64 bits contain the /// product of the lower 64 bits of both operands. The upper 64 bits are /// copied from the upper 64 bits of the first source operand. -static __inline__ __m128d __DEFAULT_FN_ATTRS _mm_mul_sd(__m128d __a, - __m128d __b) { +static __inline__ __m128d __DEFAULT_FN_ATTRS_CONSTEXPR _mm_mul_sd(__m128d __a, + __m128d __b) { __a[0] *= __b[0]; return __a; } @@ -177,8 +183,8 @@ static __inline__ __m128d __DEFAULT_FN_ATTRS _mm_mul_sd(__m128d __a, /// A 128-bit vector of [2 x double] containing one of the operands. /// \returns A 128-bit vector of [2 x double] containing the products of both /// operands. -static __inline__ __m128d __DEFAULT_FN_ATTRS _mm_mul_pd(__m128d __a, - __m128d __b) { +static __inline__ __m128d __DEFAULT_FN_ATTRS_CONSTEXPR _mm_mul_pd(__m128d __a, + __m128d __b) { return (__m128d)((__v2df)__a * (__v2df)__b); } @@ -199,8 +205,8 @@ static __inline__ __m128d __DEFAULT_FN_ATTRS _mm_mul_pd(__m128d __a, /// \returns A 128-bit vector of [2 x double] whose lower 64 bits contain the /// quotient of the lower 64 bits of both operands. The upper 64 bits are /// copied from the upper 64 bits of the first source operand. -static __inline__ __m128d __DEFAULT_FN_ATTRS _mm_div_sd(__m128d __a, - __m128d __b) { +static __inline__ __m128d __DEFAULT_FN_ATTRS_CONSTEXPR _mm_div_sd(__m128d __a, + __m128d __b) { __a[0] /= __b[0]; return __a; } @@ -218,8 +224,8 @@ static __inline__ __m128d __DEFAULT_FN_ATTRS _mm_div_sd(__m128d __a, /// A 128-bit vector of [2 x double] containing the divisor. /// \returns A 128-bit vector of [2 x double] containing the quotients of both /// operands. -static __inline__ __m128d __DEFAULT_FN_ATTRS _mm_div_pd(__m128d __a, - __m128d __b) { +static __inline__ __m128d __DEFAULT_FN_ATTRS_CONSTEXPR _mm_div_pd(__m128d __a, + __m128d __b) { return (__m128d)((__v2df)__a / (__v2df)__b); } @@ -367,8 +373,8 @@ static __inline__ __m128d __DEFAULT_FN_ATTRS _mm_max_pd(__m128d __a, /// A 128-bit vector of [2 x double] containing one of the source operands. /// \returns A 128-bit vector of [2 x double] containing the bitwise AND of the /// values between both operands. -static __inline__ __m128d __DEFAULT_FN_ATTRS _mm_and_pd(__m128d __a, - __m128d __b) { +static __inline__ __m128d __DEFAULT_FN_ATTRS_CONSTEXPR _mm_and_pd(__m128d __a, + __m128d __b) { return (__m128d)((__v2du)__a & (__v2du)__b); } @@ -387,8 +393,8 @@ static __inline__ __m128d __DEFAULT_FN_ATTRS _mm_and_pd(__m128d __a, /// \returns A 128-bit vector of [2 x double] containing the bitwise AND of the /// values in the second operand and the one's complement of the first /// operand. -static __inline__ __m128d __DEFAULT_FN_ATTRS _mm_andnot_pd(__m128d __a, - __m128d __b) { +static __inline__ __m128d __DEFAULT_FN_ATTRS_CONSTEXPR +_mm_andnot_pd(__m128d __a, __m128d __b) { return (__m128d)(~(__v2du)__a & (__v2du)__b); } @@ -404,8 +410,8 @@ static __inline__ __m128d __DEFAULT_FN_ATTRS _mm_andnot_pd(__m128d __a, /// A 128-bit vector of [2 x double] containing one of the source operands. /// \returns A 128-bit vector of [2 x double] containing the bitwise OR of the /// values between both operands. -static __inline__ __m128d __DEFAULT_FN_ATTRS _mm_or_pd(__m128d __a, - __m128d __b) { +static __inline__ __m128d __DEFAULT_FN_ATTRS_CONSTEXPR _mm_or_pd(__m128d __a, + __m128d __b) { return (__m128d)((__v2du)__a | (__v2du)__b); } @@ -421,8 +427,8 @@ static __inline__ __m128d __DEFAULT_FN_ATTRS _mm_or_pd(__m128d __a, /// A 128-bit vector of [2 x double] containing one of the source operands. /// \returns A 128-bit vector of [2 x double] containing the bitwise XOR of the /// values between both operands. -static __inline__ __m128d __DEFAULT_FN_ATTRS _mm_xor_pd(__m128d __a, - __m128d __b) { +static __inline__ __m128d __DEFAULT_FN_ATTRS_CONSTEXPR _mm_xor_pd(__m128d __a, + __m128d __b) { return (__m128d)((__v2du)__a ^ (__v2du)__b); } @@ -1300,7 +1306,8 @@ static __inline__ __m128 __DEFAULT_FN_ATTRS _mm_cvtpd_ps(__m128d __a) { /// floating-point elements are converted to double-precision values. The /// upper two elements are unused. /// \returns A 128-bit vector of [2 x double] containing the converted values. -static __inline__ __m128d __DEFAULT_FN_ATTRS _mm_cvtps_pd(__m128 __a) { +static __inline__ __m128d __DEFAULT_FN_ATTRS_CONSTEXPR +_mm_cvtps_pd(__m128 __a) { return (__m128d) __builtin_convertvector( __builtin_shufflevector((__v4sf)__a, (__v4sf)__a, 0, 1), __v2df); } @@ -1321,7 +1328,8 @@ static __inline__ __m128d __DEFAULT_FN_ATTRS _mm_cvtps_pd(__m128 __a) { /// /// The upper two elements are unused. /// \returns A 128-bit vector of [2 x double] containing the converted values. -static __inline__ __m128d __DEFAULT_FN_ATTRS _mm_cvtepi32_pd(__m128i __a) { +static __inline__ __m128d __DEFAULT_FN_ATTRS_CONSTEXPR +_mm_cvtepi32_pd(__m128i __a) { return (__m128d) __builtin_convertvector( __builtin_shufflevector((__v4si)__a, (__v4si)__a, 0, 1), __v2df); } @@ -1407,8 +1415,8 @@ static __inline__ __m128 __DEFAULT_FN_ATTRS _mm_cvtsd_ss(__m128 __a, /// \returns A 128-bit vector of [2 x double]. The lower 64 bits contain the /// converted value from the second parameter. The upper 64 bits are copied /// from the upper 64 bits of the first parameter. -static __inline__ __m128d __DEFAULT_FN_ATTRS _mm_cvtsi32_sd(__m128d __a, - int __b) { +static __inline__ __m128d __DEFAULT_FN_ATTRS_CONSTEXPR +_mm_cvtsi32_sd(__m128d __a, int __b) { __a[0] = __b; return __a; } @@ -1432,8 +1440,8 @@ static __inline__ __m128d __DEFAULT_FN_ATTRS _mm_cvtsi32_sd(__m128d __a, /// \returns A 128-bit vector of [2 x double]. The lower 64 bits contain the /// converted value from the second parameter. The upper 64 bits are copied /// from the upper 64 bits of the first parameter. -static __inline__ __m128d __DEFAULT_FN_ATTRS _mm_cvtss_sd(__m128d __a, - __m128 __b) { +static __inline__ __m128d __DEFAULT_FN_ATTRS_CONSTEXPR +_mm_cvtss_sd(__m128d __a, __m128 __b) { __a[0] = __b[0]; return __a; } @@ -1529,7 +1537,8 @@ static __inline__ __m64 __DEFAULT_FN_ATTRS _mm_cvttpd_pi32(__m128d __a) { /// \param __a /// A 64-bit vector of [2 x i32]. /// \returns A 128-bit vector of [2 x double] containing the converted values. -static __inline__ __m128d __DEFAULT_FN_ATTRS _mm_cvtpi32_pd(__m64 __a) { +static __inline__ __m128d __DEFAULT_FN_ATTRS_CONSTEXPR +_mm_cvtpi32_pd(__m64 __a) { return (__m128d) __builtin_convertvector((__v2si)__a, __v2df); } @@ -1544,7 +1553,8 @@ static __inline__ __m128d __DEFAULT_FN_ATTRS _mm_cvtpi32_pd(__m64 __a) { /// A 128-bit vector of [2 x double]. The lower 64 bits are returned. /// \returns A double-precision floating-point value copied from the lower 64 /// bits of \a __a. -static __inline__ double __DEFAULT_FN_ATTRS _mm_cvtsd_f64(__m128d __a) { +static __inline__ double __DEFAULT_FN_ATTRS_CONSTEXPR +_mm_cvtsd_f64(__m128d __a) { return __a[0]; } @@ -1779,7 +1789,7 @@ static __inline__ __m128d __DEFAULT_FN_ATTRS _mm_undefined_pd(void) { /// \returns An initialized 128-bit floating-point vector of [2 x double]. The /// lower 64 bits contain the value of the parameter. The upper 64 bits are /// set to zero. -static __inline__ __m128d __DEFAULT_FN_ATTRS _mm_set_sd(double __w) { +static __inline__ __m128d __DEFAULT_FN_ATTRS_CONSTEXPR _mm_set_sd(double __w) { return __extension__(__m128d){__w, 0.0}; } @@ -1795,7 +1805,7 @@ static __inline__ __m128d __DEFAULT_FN_ATTRS _mm_set_sd(double __w) { /// A double-precision floating-point value used to initialize each vector /// element of the result. /// \returns An initialized 128-bit floating-point vector of [2 x double]. -static __inline__ __m128d __DEFAULT_FN_ATTRS _mm_set1_pd(double __w) { +static __inline__ __m128d __DEFAULT_FN_ATTRS_CONSTEXPR _mm_set1_pd(double __w) { return __extension__(__m128d){__w, __w}; } @@ -1811,7 +1821,7 @@ static __inline__ __m128d __DEFAULT_FN_ATTRS _mm_set1_pd(double __w) { /// A double-precision floating-point value used to initialize each vector /// element of the result. /// \returns An initialized 128-bit floating-point vector of [2 x double]. -static __inline__ __m128d __DEFAULT_FN_ATTRS _mm_set_pd1(double __w) { +static __inline__ __m128d __DEFAULT_FN_ATTRS_CONSTEXPR _mm_set_pd1(double __w) { return _mm_set1_pd(__w); } @@ -1829,8 +1839,8 @@ static __inline__ __m128d __DEFAULT_FN_ATTRS _mm_set_pd1(double __w) { /// A double-precision floating-point value used to initialize the lower 64 /// bits of the result. /// \returns An initialized 128-bit floating-point vector of [2 x double]. -static __inline__ __m128d __DEFAULT_FN_ATTRS _mm_set_pd(double __w, - double __x) { +static __inline__ __m128d __DEFAULT_FN_ATTRS_CONSTEXPR _mm_set_pd(double __w, + double __x) { return __extension__(__m128d){__x, __w}; } @@ -1849,8 +1859,8 @@ static __inline__ __m128d __DEFAULT_FN_ATTRS _mm_set_pd(double __w, /// A double-precision floating-point value used to initialize the upper 64 /// bits of the result. /// \returns An initialized 128-bit floating-point vector of [2 x double]. -static __inline__ __m128d __DEFAULT_FN_ATTRS _mm_setr_pd(double __w, - double __x) { +static __inline__ __m128d __DEFAULT_FN_ATTRS_CONSTEXPR _mm_setr_pd(double __w, + double __x) { return __extension__(__m128d){__w, __x}; } @@ -1863,7 +1873,7 @@ static __inline__ __m128d __DEFAULT_FN_ATTRS _mm_setr_pd(double __w, /// /// \returns An initialized 128-bit floating-point vector of [2 x double] with /// all elements set to zero. -static __inline__ __m128d __DEFAULT_FN_ATTRS _mm_setzero_pd(void) { +static __inline__ __m128d __DEFAULT_FN_ATTRS_CONSTEXPR _mm_setzero_pd(void) { return __extension__(__m128d){0.0, 0.0}; } @@ -1882,8 +1892,8 @@ static __inline__ __m128d __DEFAULT_FN_ATTRS _mm_setzero_pd(void) { /// A 128-bit vector of [2 x double]. The lower 64 bits are written to the /// lower 64 bits of the result. /// \returns A 128-bit vector of [2 x double] containing the moved values. -static __inline__ __m128d __DEFAULT_FN_ATTRS _mm_move_sd(__m128d __a, - __m128d __b) { +static __inline__ __m128d __DEFAULT_FN_ATTRS_CONSTEXPR +_mm_move_sd(__m128d __a, __m128d __b) { __a[0] = __b[0]; return __a; } @@ -3317,7 +3327,8 @@ static __inline__ long long __DEFAULT_FN_ATTRS _mm_cvttsd_si64(__m128d __a) { /// \param __a /// A 128-bit integer vector. /// \returns A 128-bit vector of [4 x float] containing the converted values. -static __inline__ __m128 __DEFAULT_FN_ATTRS _mm_cvtepi32_ps(__m128i __a) { +static __inline__ __m128 __DEFAULT_FN_ATTRS_CONSTEXPR +_mm_cvtepi32_ps(__m128i __a) { return (__m128) __builtin_convertvector((__v4si)__a, __v4sf); } @@ -3862,7 +3873,7 @@ _mm_setr_epi8(char __b0, char __b1, char __b2, char __b3, char __b4, char __b5, /// /// \returns An initialized 128-bit integer vector with all elements set to /// zero. -static __inline__ __m128i __DEFAULT_FN_ATTRS _mm_setzero_si128(void) { +static __inline__ __m128i __DEFAULT_FN_ATTRS_CONSTEXPR _mm_setzero_si128(void) { return __extension__(__m128i)(__v2di){0LL, 0LL}; } @@ -4645,8 +4656,8 @@ static __inline__ __m128i __DEFAULT_FN_ATTRS _mm_move_epi64(__m128i __a) { /// A 128-bit vector of [2 x double]. \n /// Bits [127:64] are written to bits [127:64] of the destination. /// \returns A 128-bit vector of [2 x double] containing the interleaved values. -static __inline__ __m128d __DEFAULT_FN_ATTRS _mm_unpackhi_pd(__m128d __a, - __m128d __b) { +static __inline__ __m128d __DEFAULT_FN_ATTRS_CONSTEXPR +_mm_unpackhi_pd(__m128d __a, __m128d __b) { return __builtin_shufflevector((__v2df)__a, (__v2df)__b, 1, 2 + 1); } @@ -4665,8 +4676,8 @@ static __inline__ __m128d __DEFAULT_FN_ATTRS _mm_unpackhi_pd(__m128d __a, /// A 128-bit vector of [2 x double]. \n /// Bits [63:0] are written to bits [127:64] of the destination. /// \returns A 128-bit vector of [2 x double] containing the interleaved values. -static __inline__ __m128d __DEFAULT_FN_ATTRS _mm_unpacklo_pd(__m128d __a, - __m128d __b) { +static __inline__ __m128d __DEFAULT_FN_ATTRS_CONSTEXPR +_mm_unpacklo_pd(__m128d __a, __m128d __b) { return __builtin_shufflevector((__v2df)__a, (__v2df)__b, 0, 2 + 0); } @@ -4729,7 +4740,8 @@ static __inline__ int __DEFAULT_FN_ATTRS _mm_movemask_pd(__m128d __a) { /// A 128-bit floating-point vector of [2 x double]. /// \returns A 128-bit floating-point vector of [4 x float] containing the same /// bitwise pattern as the parameter. -static __inline__ __m128 __DEFAULT_FN_ATTRS _mm_castpd_ps(__m128d __a) { +static __inline__ __m128 __DEFAULT_FN_ATTRS_CONSTEXPR +_mm_castpd_ps(__m128d __a) { return (__m128)__a; } @@ -4744,7 +4756,8 @@ static __inline__ __m128 __DEFAULT_FN_ATTRS _mm_castpd_ps(__m128d __a) { /// A 128-bit floating-point vector of [2 x double]. /// \returns A 128-bit integer vector containing the same bitwise pattern as the /// parameter. -static __inline__ __m128i __DEFAULT_FN_ATTRS _mm_castpd_si128(__m128d __a) { +static __inline__ __m128i __DEFAULT_FN_ATTRS_CONSTEXPR +_mm_castpd_si128(__m128d __a) { return (__m128i)__a; } @@ -4900,6 +4913,7 @@ void _mm_pause(void); #undef __anyext128 #undef __trunc64 #undef __DEFAULT_FN_ATTRS +#undef __DEFAULT_FN_ATTRS_CONSTEXPR #define _MM_SHUFFLE2(x, y) (((x) << 1) | (y)) diff --git a/clang/lib/Headers/hlsl/hlsl_intrinsics.h b/clang/lib/Headers/hlsl/hlsl_intrinsics.h index d28f204e352de..137467e5a782c 100644 --- a/clang/lib/Headers/hlsl/hlsl_intrinsics.h +++ b/clang/lib/Headers/hlsl/hlsl_intrinsics.h @@ -766,6 +766,36 @@ uint64_t3 countbits(uint64_t3); _HLSL_BUILTIN_ALIAS(__builtin_elementwise_popcount) uint64_t4 countbits(uint64_t4); +//===----------------------------------------------------------------------===// +// degrees builtins +//===----------------------------------------------------------------------===// + +/// \fn T degrees(T x) +/// \brief Converts the specified value from radians to degrees. +/// \param x The specified input value. + +_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2) +_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_degrees) +half degrees(half); +_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2) +_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_degrees) +half2 degrees(half2); +_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2) +_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_degrees) +half3 degrees(half3); +_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2) +_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_degrees) +half4 degrees(half4); + +_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_degrees) +float degrees(float); +_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_degrees) +float2 degrees(float2); +_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_degrees) +float3 degrees(float3); +_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_degrees) +float4 degrees(float4); + //===----------------------------------------------------------------------===// // dot product builtins //===----------------------------------------------------------------------===// @@ -1642,6 +1672,28 @@ uint64_t3 reversebits(uint64_t3); _HLSL_BUILTIN_ALIAS(__builtin_elementwise_bitreverse) uint64_t4 reversebits(uint64_t4); +//===----------------------------------------------------------------------===// +// cross builtins +//===----------------------------------------------------------------------===// + +/// \fn T cross(T x, T y) +/// \brief Returns the cross product of two floating-point, 3D vectors. +/// \param x [in] The first floating-point, 3D vector. +/// \param y [in] The second floating-point, 3D vector. +/// +/// Result is the cross product of x and y, i.e., the resulting +/// components are, in order : +/// x[1] * y[2] - y[1] * x[2] +/// x[2] * y[0] - y[2] * x[0] +/// x[0] * y[1] - y[0] * x[1] + +_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2) +_HLSL_BUILTIN_ALIAS(__builtin_hlsl_cross) +half3 cross(half3, half3); + +_HLSL_BUILTIN_ALIAS(__builtin_hlsl_cross) +float3 cross(float3, float3); + //===----------------------------------------------------------------------===// // rcp builtins //===----------------------------------------------------------------------===// @@ -2066,6 +2118,19 @@ int3 sign(int16_t3); _HLSL_AVAILABILITY(shadermodel, 6.2) _HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_sign) int4 sign(int16_t4); + +_HLSL_AVAILABILITY(shadermodel, 6.2) +_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_sign) +int sign(uint16_t); +_HLSL_AVAILABILITY(shadermodel, 6.2) +_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_sign) +int2 sign(uint16_t2); +_HLSL_AVAILABILITY(shadermodel, 6.2) +_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_sign) +int3 sign(uint16_t3); +_HLSL_AVAILABILITY(shadermodel, 6.2) +_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_sign) +int4 sign(uint16_t4); #endif _HLSL_16BIT_AVAILABILITY(shadermodel, 6.2) @@ -2090,6 +2155,15 @@ int3 sign(int3); _HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_sign) int4 sign(int4); +_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_sign) +int sign(uint); +_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_sign) +int2 sign(uint2); +_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_sign) +int3 sign(uint3); +_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_sign) +int4 sign(uint4); + _HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_sign) int sign(float); _HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_sign) @@ -2108,6 +2182,15 @@ int3 sign(int64_t3); _HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_sign) int4 sign(int64_t4); +_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_sign) +int sign(uint64_t); +_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_sign) +int2 sign(uint64_t2); +_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_sign) +int3 sign(uint64_t3); +_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_sign) +int4 sign(uint64_t4); + _HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_sign) int sign(double); _HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_sign) @@ -2116,5 +2199,35 @@ _HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_sign) int3 sign(double3); _HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_sign) int4 sign(double4); + +//===----------------------------------------------------------------------===// +// radians builtins +//===----------------------------------------------------------------------===// + +/// \fn T radians(T Val) +/// \brief Converts the specified value from degrees to radians. + +_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2) +_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_radians) +half radians(half); +_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2) +_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_radians) +half2 radians(half2); +_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2) +_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_radians) +half3 radians(half3); +_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2) +_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_radians) +half4 radians(half4); + +_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_radians) +float radians(float); +_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_radians) +float2 radians(float2); +_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_radians) +float3 radians(float3); +_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_radians) +float4 radians(float4); + } // namespace hlsl #endif //_HLSL_HLSL_INTRINSICS_H_ diff --git a/clang/lib/Headers/pmmintrin.h b/clang/lib/Headers/pmmintrin.h index 9ad76579668b3..cd605df7fb52d 100644 --- a/clang/lib/Headers/pmmintrin.h +++ b/clang/lib/Headers/pmmintrin.h @@ -27,6 +27,12 @@ __min_vector_width__(128))) #endif +#if defined(__cplusplus) && (__cplusplus >= 201103L) +#define __DEFAULT_FN_ATTRS_CONSTEXPR __DEFAULT_FN_ATTRS constexpr +#else +#define __DEFAULT_FN_ATTRS_CONSTEXPR __DEFAULT_FN_ATTRS +#endif + /// Loads data from an unaligned memory location to elements in a 128-bit /// vector. /// @@ -128,7 +134,7 @@ _mm_hsub_ps(__m128 __a, __m128 __b) /// destination. /// \returns A 128-bit vector of [4 x float] containing the moved and duplicated /// values. -static __inline__ __m128 __DEFAULT_FN_ATTRS +static __inline__ __m128 __DEFAULT_FN_ATTRS_CONSTEXPR _mm_movehdup_ps(__m128 __a) { return __builtin_shufflevector((__v4sf)__a, (__v4sf)__a, 1, 1, 3, 3); @@ -149,7 +155,7 @@ _mm_movehdup_ps(__m128 __a) /// destination. /// \returns A 128-bit vector of [4 x float] containing the moved and duplicated /// values. -static __inline__ __m128 __DEFAULT_FN_ATTRS +static __inline__ __m128 __DEFAULT_FN_ATTRS_CONSTEXPR _mm_moveldup_ps(__m128 __a) { return __builtin_shufflevector((__v4sf)__a, (__v4sf)__a, 0, 0, 2, 2); @@ -250,7 +256,7 @@ _mm_hsub_pd(__m128d __a, __m128d __b) /// [127:64] and [63:0] of the destination. /// \returns A 128-bit vector of [2 x double] containing the moved and /// duplicated values. -static __inline__ __m128d __DEFAULT_FN_ATTRS +static __inline__ __m128d __DEFAULT_FN_ATTRS_CONSTEXPR _mm_movedup_pd(__m128d __a) { return __builtin_shufflevector((__v2df)__a, (__v2df)__a, 0, 0); @@ -303,5 +309,6 @@ _mm_mwait(unsigned __extensions, unsigned __hints) } #undef __DEFAULT_FN_ATTRS +#undef __DEFAULT_FN_ATTRS_CONSTEXPR #endif /* __PMMINTRIN_H */ diff --git a/clang/lib/Headers/popcntintrin.h b/clang/lib/Headers/popcntintrin.h index 0aa94aecda5b8..b276b4da4dc29 100644 --- a/clang/lib/Headers/popcntintrin.h +++ b/clang/lib/Headers/popcntintrin.h @@ -11,12 +11,13 @@ #define __POPCNTINTRIN_H /* Define the default attributes for the functions in this file. */ -#define __DEFAULT_FN_ATTRS __attribute__((__always_inline__, __nodebug__, __target__("popcnt"))) - #if defined(__cplusplus) && (__cplusplus >= 201103L) -#define __DEFAULT_FN_ATTRS_CONSTEXPR __DEFAULT_FN_ATTRS constexpr +#define __DEFAULT_FN_ATTRS \ + __attribute__((__always_inline__, __nodebug__, \ + __target__("popcnt"))) constexpr #else -#define __DEFAULT_FN_ATTRS_CONSTEXPR __DEFAULT_FN_ATTRS +#define __DEFAULT_FN_ATTRS \ + __attribute__((__always_inline__, __nodebug__, __target__("popcnt"))) #endif /// Counts the number of bits in the source operand having a value of 1. @@ -29,7 +30,7 @@ /// An unsigned 32-bit integer operand. /// \returns A 32-bit integer containing the number of bits with value 1 in the /// source operand. -static __inline__ int __DEFAULT_FN_ATTRS_CONSTEXPR +static __inline__ int __DEFAULT_FN_ATTRS _mm_popcnt_u32(unsigned int __A) { return __builtin_popcount(__A); @@ -46,7 +47,7 @@ _mm_popcnt_u32(unsigned int __A) /// An unsigned 64-bit integer operand. /// \returns A 64-bit integer containing the number of bits with value 1 in the /// source operand. -static __inline__ long long __DEFAULT_FN_ATTRS_CONSTEXPR +static __inline__ long long __DEFAULT_FN_ATTRS _mm_popcnt_u64(unsigned long long __A) { return __builtin_popcountll(__A); @@ -54,6 +55,5 @@ _mm_popcnt_u64(unsigned long long __A) #endif /* __x86_64__ */ #undef __DEFAULT_FN_ATTRS -#undef __DEFAULT_FN_ATTRS_CONSTEXPR #endif /* __POPCNTINTRIN_H */ diff --git a/clang/lib/Headers/xmmintrin.h b/clang/lib/Headers/xmmintrin.h index 9958e91bfceaa..20e66d190113a 100644 --- a/clang/lib/Headers/xmmintrin.h +++ b/clang/lib/Headers/xmmintrin.h @@ -48,6 +48,14 @@ typedef unsigned int __v4su __attribute__((__vector_size__(16))); __min_vector_width__(128))) #endif +#if defined(__cplusplus) && (__cplusplus >= 201103L) +#define __DEFAULT_FN_ATTRS_CONSTEXPR __DEFAULT_FN_ATTRS constexpr +#define __DEFAULT_FN_ATTRS_SSE2_CONSTEXPR __DEFAULT_FN_ATTRS_SSE2 constexpr +#else +#define __DEFAULT_FN_ATTRS_CONSTEXPR __DEFAULT_FN_ATTRS +#define __DEFAULT_FN_ATTRS_SSE2_CONSTEXPR __DEFAULT_FN_ATTRS_SSE2 +#endif + #define __trunc64(x) \ (__m64) __builtin_shufflevector((__v2di)(x), __extension__(__v2di){}, 0) #define __zext128(x) \ @@ -75,9 +83,8 @@ typedef unsigned int __v4su __attribute__((__vector_size__(16))); /// \returns A 128-bit vector of [4 x float] whose lower 32 bits contain the sum /// of the lower 32 bits of both operands. The upper 96 bits are copied from /// the upper 96 bits of the first source operand. -static __inline__ __m128 __DEFAULT_FN_ATTRS -_mm_add_ss(__m128 __a, __m128 __b) -{ +static __inline__ __m128 __DEFAULT_FN_ATTRS_CONSTEXPR +_mm_add_ss(__m128 __a, __m128 __b) { __a[0] += __b[0]; return __a; } @@ -95,9 +102,8 @@ _mm_add_ss(__m128 __a, __m128 __b) /// A 128-bit vector of [4 x float] containing one of the source operands. /// \returns A 128-bit vector of [4 x float] containing the sums of both /// operands. -static __inline__ __m128 __DEFAULT_FN_ATTRS -_mm_add_ps(__m128 __a, __m128 __b) -{ +static __inline__ __m128 __DEFAULT_FN_ATTRS_CONSTEXPR +_mm_add_ps(__m128 __a, __m128 __b) { return (__m128)((__v4sf)__a + (__v4sf)__b); } @@ -117,9 +123,8 @@ _mm_add_ps(__m128 __a, __m128 __b) /// \returns A 128-bit vector of [4 x float] whose lower 32 bits contain the /// difference of the lower 32 bits of both operands. The upper 96 bits are /// copied from the upper 96 bits of the first source operand. -static __inline__ __m128 __DEFAULT_FN_ATTRS -_mm_sub_ss(__m128 __a, __m128 __b) -{ +static __inline__ __m128 __DEFAULT_FN_ATTRS_CONSTEXPR +_mm_sub_ss(__m128 __a, __m128 __b) { __a[0] -= __b[0]; return __a; } @@ -138,9 +143,8 @@ _mm_sub_ss(__m128 __a, __m128 __b) /// A 128-bit vector of [4 x float] containing the subtrahend. /// \returns A 128-bit vector of [4 x float] containing the differences between /// both operands. -static __inline__ __m128 __DEFAULT_FN_ATTRS -_mm_sub_ps(__m128 __a, __m128 __b) -{ +static __inline__ __m128 __DEFAULT_FN_ATTRS_CONSTEXPR +_mm_sub_ps(__m128 __a, __m128 __b) { return (__m128)((__v4sf)__a - (__v4sf)__b); } @@ -160,9 +164,8 @@ _mm_sub_ps(__m128 __a, __m128 __b) /// \returns A 128-bit vector of [4 x float] containing the product of the lower /// 32 bits of both operands. The upper 96 bits are copied from the upper 96 /// bits of the first source operand. -static __inline__ __m128 __DEFAULT_FN_ATTRS -_mm_mul_ss(__m128 __a, __m128 __b) -{ +static __inline__ __m128 __DEFAULT_FN_ATTRS_CONSTEXPR +_mm_mul_ss(__m128 __a, __m128 __b) { __a[0] *= __b[0]; return __a; } @@ -180,9 +183,8 @@ _mm_mul_ss(__m128 __a, __m128 __b) /// A 128-bit vector of [4 x float] containing one of the source operands. /// \returns A 128-bit vector of [4 x float] containing the products of both /// operands. -static __inline__ __m128 __DEFAULT_FN_ATTRS -_mm_mul_ps(__m128 __a, __m128 __b) -{ +static __inline__ __m128 __DEFAULT_FN_ATTRS_CONSTEXPR +_mm_mul_ps(__m128 __a, __m128 __b) { return (__m128)((__v4sf)__a * (__v4sf)__b); } @@ -202,9 +204,8 @@ _mm_mul_ps(__m128 __a, __m128 __b) /// \returns A 128-bit vector of [4 x float] containing the quotients of the /// lower 32 bits of both operands. The upper 96 bits are copied from the /// upper 96 bits of the first source operand. -static __inline__ __m128 __DEFAULT_FN_ATTRS -_mm_div_ss(__m128 __a, __m128 __b) -{ +static __inline__ __m128 __DEFAULT_FN_ATTRS_CONSTEXPR +_mm_div_ss(__m128 __a, __m128 __b) { __a[0] /= __b[0]; return __a; } @@ -221,9 +222,8 @@ _mm_div_ss(__m128 __a, __m128 __b) /// A 128-bit vector of [4 x float] containing the divisor. /// \returns A 128-bit vector of [4 x float] containing the quotients of both /// operands. -static __inline__ __m128 __DEFAULT_FN_ATTRS -_mm_div_ps(__m128 __a, __m128 __b) -{ +static __inline__ __m128 __DEFAULT_FN_ATTRS_CONSTEXPR +_mm_div_ps(__m128 __a, __m128 __b) { return (__m128)((__v4sf)__a / (__v4sf)__b); } @@ -437,9 +437,8 @@ _mm_max_ps(__m128 __a, __m128 __b) /// A 128-bit vector containing one of the source operands. /// \returns A 128-bit vector of [4 x float] containing the bitwise AND of the /// values between both operands. -static __inline__ __m128 __DEFAULT_FN_ATTRS -_mm_and_ps(__m128 __a, __m128 __b) -{ +static __inline__ __m128 __DEFAULT_FN_ATTRS_CONSTEXPR +_mm_and_ps(__m128 __a, __m128 __b) { return (__m128)((__v4su)__a & (__v4su)__b); } @@ -459,9 +458,8 @@ _mm_and_ps(__m128 __a, __m128 __b) /// \returns A 128-bit vector of [4 x float] containing the bitwise AND of the /// one's complement of the first operand and the values in the second /// operand. -static __inline__ __m128 __DEFAULT_FN_ATTRS -_mm_andnot_ps(__m128 __a, __m128 __b) -{ +static __inline__ __m128 __DEFAULT_FN_ATTRS_CONSTEXPR +_mm_andnot_ps(__m128 __a, __m128 __b) { return (__m128)(~(__v4su)__a & (__v4su)__b); } @@ -477,9 +475,8 @@ _mm_andnot_ps(__m128 __a, __m128 __b) /// A 128-bit vector of [4 x float] containing one of the source operands. /// \returns A 128-bit vector of [4 x float] containing the bitwise OR of the /// values between both operands. -static __inline__ __m128 __DEFAULT_FN_ATTRS -_mm_or_ps(__m128 __a, __m128 __b) -{ +static __inline__ __m128 __DEFAULT_FN_ATTRS_CONSTEXPR +_mm_or_ps(__m128 __a, __m128 __b) { return (__m128)((__v4su)__a | (__v4su)__b); } @@ -496,9 +493,8 @@ _mm_or_ps(__m128 __a, __m128 __b) /// A 128-bit vector of [4 x float] containing one of the source operands. /// \returns A 128-bit vector of [4 x float] containing the bitwise exclusive OR /// of the values between both operands. -static __inline__ __m128 __DEFAULT_FN_ATTRS -_mm_xor_ps(__m128 __a, __m128 __b) -{ +static __inline__ __m128 __DEFAULT_FN_ATTRS_CONSTEXPR +_mm_xor_ps(__m128 __a, __m128 __b) { return (__m128)((__v4su)__a ^ (__v4su)__b); } @@ -1622,9 +1618,8 @@ _mm_cvtt_ps2pi(__m128 __a) /// \returns A 128-bit vector of [4 x float] whose lower 32 bits contain the /// converted value of the second operand. The upper 96 bits are copied from /// the upper 96 bits of the first operand. -static __inline__ __m128 __DEFAULT_FN_ATTRS -_mm_cvtsi32_ss(__m128 __a, int __b) -{ +static __inline__ __m128 __DEFAULT_FN_ATTRS_CONSTEXPR _mm_cvtsi32_ss(__m128 __a, + int __b) { __a[0] = __b; return __a; } @@ -1645,9 +1640,8 @@ _mm_cvtsi32_ss(__m128 __a, int __b) /// \returns A 128-bit vector of [4 x float] whose lower 32 bits contain the /// converted value of the second operand. The upper 96 bits are copied from /// the upper 96 bits of the first operand. -static __inline__ __m128 __DEFAULT_FN_ATTRS -_mm_cvt_si2ss(__m128 __a, int __b) -{ +static __inline__ __m128 __DEFAULT_FN_ATTRS_CONSTEXPR _mm_cvt_si2ss(__m128 __a, + int __b) { return _mm_cvtsi32_ss(__a, __b); } @@ -1669,9 +1663,8 @@ _mm_cvt_si2ss(__m128 __a, int __b) /// \returns A 128-bit vector of [4 x float] whose lower 32 bits contain the /// converted value of the second operand. The upper 96 bits are copied from /// the upper 96 bits of the first operand. -static __inline__ __m128 __DEFAULT_FN_ATTRS -_mm_cvtsi64_ss(__m128 __a, long long __b) -{ +static __inline__ __m128 __DEFAULT_FN_ATTRS_CONSTEXPR +_mm_cvtsi64_ss(__m128 __a, long long __b) { __a[0] = __b; return __a; } @@ -1738,9 +1731,8 @@ _mm_cvt_pi2ps(__m128 __a, __m64 __b) /// A 128-bit vector of [4 x float]. The lower 32 bits of this operand are /// used in the extraction. /// \returns A 32-bit float containing the extracted value. -static __inline__ float __DEFAULT_FN_ATTRS -_mm_cvtss_f32(__m128 __a) -{ +static __inline__ float __DEFAULT_FN_ATTRS_CONSTEXPR +_mm_cvtss_f32(__m128 __a) { return __a[0]; } @@ -1931,9 +1923,8 @@ _mm_undefined_ps(void) /// \returns An initialized 128-bit floating-point vector of [4 x float]. The /// lower 32 bits contain the value provided in the source operand. The /// upper 96 bits are set to zero. -static __inline__ __m128 __DEFAULT_FN_ATTRS -_mm_set_ss(float __w) -{ +static __inline__ __m128 __DEFAULT_FN_ATTRS_CONSTEXPR +_mm_set_ss(float __w) { return __extension__ (__m128){ __w, 0.0f, 0.0f, 0.0f }; } @@ -1949,9 +1940,8 @@ _mm_set_ss(float __w) /// A single-precision floating-point value used to initialize each vector /// element of the result. /// \returns An initialized 128-bit floating-point vector of [4 x float]. -static __inline__ __m128 __DEFAULT_FN_ATTRS -_mm_set1_ps(float __w) -{ +static __inline__ __m128 __DEFAULT_FN_ATTRS_CONSTEXPR +_mm_set1_ps(float __w) { return __extension__ (__m128){ __w, __w, __w, __w }; } @@ -1968,9 +1958,8 @@ _mm_set1_ps(float __w) /// A single-precision floating-point value used to initialize each vector /// element of the result. /// \returns An initialized 128-bit floating-point vector of [4 x float]. -static __inline__ __m128 __DEFAULT_FN_ATTRS -_mm_set_ps1(float __w) -{ +static __inline__ __m128 __DEFAULT_FN_ATTRS_CONSTEXPR +_mm_set_ps1(float __w) { return _mm_set1_ps(__w); } @@ -1995,9 +1984,8 @@ _mm_set_ps1(float __w) /// A single-precision floating-point value used to initialize bits [31:0] /// of the result. /// \returns An initialized 128-bit floating-point vector of [4 x float]. -static __inline__ __m128 __DEFAULT_FN_ATTRS -_mm_set_ps(float __z, float __y, float __x, float __w) -{ +static __inline__ __m128 __DEFAULT_FN_ATTRS_CONSTEXPR +_mm_set_ps(float __z, float __y, float __x, float __w) { return __extension__ (__m128){ __w, __x, __y, __z }; } @@ -2023,9 +2011,8 @@ _mm_set_ps(float __z, float __y, float __x, float __w) /// A single-precision floating-point value used to initialize bits [127:96] /// of the result. /// \returns An initialized 128-bit floating-point vector of [4 x float]. -static __inline__ __m128 __DEFAULT_FN_ATTRS -_mm_setr_ps(float __z, float __y, float __x, float __w) -{ +static __inline__ __m128 __DEFAULT_FN_ATTRS_CONSTEXPR +_mm_setr_ps(float __z, float __y, float __x, float __w) { return __extension__ (__m128){ __z, __y, __x, __w }; } @@ -2038,9 +2025,8 @@ _mm_setr_ps(float __z, float __y, float __x, float __w) /// /// \returns An initialized 128-bit floating-point vector of [4 x float] with /// all elements set to zero. -static __inline__ __m128 __DEFAULT_FN_ATTRS -_mm_setzero_ps(void) -{ +static __inline__ __m128 __DEFAULT_FN_ATTRS_CONSTEXPR +_mm_setzero_ps(void) { return __extension__ (__m128){ 0.0f, 0.0f, 0.0f, 0.0f }; } @@ -2786,9 +2772,8 @@ void _mm_setcsr(unsigned int __i); /// Bits [95:64] are written to bits [63:32] of the destination. \n /// Bits [127:96] are written to bits [127:96] of the destination. /// \returns A 128-bit vector of [4 x float] containing the interleaved values. -static __inline__ __m128 __DEFAULT_FN_ATTRS -_mm_unpackhi_ps(__m128 __a, __m128 __b) -{ +static __inline__ __m128 __DEFAULT_FN_ATTRS_CONSTEXPR +_mm_unpackhi_ps(__m128 __a, __m128 __b) { return __builtin_shufflevector((__v4sf)__a, (__v4sf)__b, 2, 6, 3, 7); } @@ -2808,9 +2793,8 @@ _mm_unpackhi_ps(__m128 __a, __m128 __b) /// Bits [31:0] are written to bits [63:32] of the destination. \n /// Bits [63:32] are written to bits [127:96] of the destination. /// \returns A 128-bit vector of [4 x float] containing the interleaved values. -static __inline__ __m128 __DEFAULT_FN_ATTRS -_mm_unpacklo_ps(__m128 __a, __m128 __b) -{ +static __inline__ __m128 __DEFAULT_FN_ATTRS_CONSTEXPR +_mm_unpacklo_ps(__m128 __a, __m128 __b) { return __builtin_shufflevector((__v4sf)__a, (__v4sf)__b, 0, 4, 1, 5); } @@ -2830,9 +2814,8 @@ _mm_unpacklo_ps(__m128 __a, __m128 __b) /// A 128-bit floating-point vector of [4 x float]. The lower 32 bits are /// written to the lower 32 bits of the result. /// \returns A 128-bit floating-point vector of [4 x float]. -static __inline__ __m128 __DEFAULT_FN_ATTRS -_mm_move_ss(__m128 __a, __m128 __b) -{ +static __inline__ __m128 __DEFAULT_FN_ATTRS_CONSTEXPR +_mm_move_ss(__m128 __a, __m128 __b) { __a[0] = __b[0]; return __a; } @@ -2852,9 +2835,8 @@ _mm_move_ss(__m128 __a, __m128 __b) /// A 128-bit floating-point vector of [4 x float]. The upper 64 bits are /// written to the lower 64 bits of the result. /// \returns A 128-bit floating-point vector of [4 x float]. -static __inline__ __m128 __DEFAULT_FN_ATTRS -_mm_movehl_ps(__m128 __a, __m128 __b) -{ +static __inline__ __m128 __DEFAULT_FN_ATTRS_CONSTEXPR +_mm_movehl_ps(__m128 __a, __m128 __b) { return __builtin_shufflevector((__v4sf)__a, (__v4sf)__b, 6, 7, 2, 3); } @@ -2873,9 +2855,8 @@ _mm_movehl_ps(__m128 __a, __m128 __b) /// A 128-bit floating-point vector of [4 x float]. The lower 64 bits are /// written to the upper 64 bits of the result. /// \returns A 128-bit floating-point vector of [4 x float]. -static __inline__ __m128 __DEFAULT_FN_ATTRS -_mm_movelh_ps(__m128 __a, __m128 __b) -{ +static __inline__ __m128 __DEFAULT_FN_ATTRS_CONSTEXPR +_mm_movelh_ps(__m128 __a, __m128 __b) { return __builtin_shufflevector((__v4sf)__a, (__v4sf)__b, 0, 1, 4, 5); } @@ -3207,7 +3188,9 @@ do { \ #undef __anyext128 #undef __zeroupper64 #undef __DEFAULT_FN_ATTRS +#undef __DEFAULT_FN_ATTRS_CONSTEXPR #undef __DEFAULT_FN_ATTRS_SSE2 +#undef __DEFAULT_FN_ATTRS_SSE2_CONSTEXPR /* Ugly hack for backwards-compatibility (compatible with gcc) */ #if defined(__SSE2__) && !__building_module(_Builtin_intrinsics) diff --git a/clang/lib/Lex/PPMacroExpansion.cpp b/clang/lib/Lex/PPMacroExpansion.cpp index 2b62f573857ee..d48bb8a9a9cde 100644 --- a/clang/lib/Lex/PPMacroExpansion.cpp +++ b/clang/lib/Lex/PPMacroExpansion.cpp @@ -1604,16 +1604,46 @@ static bool isTargetVariantEnvironment(const TargetInfo &TI, return false; } -#if defined(__sun__) && defined(__svr4__) +#if defined(__sun__) && defined(__svr4__) && defined(__clang__) && \ + __clang__ < 20 // GCC mangles std::tm as tm for binary compatibility on Solaris (Issue // #33114). We need to match this to allow the std::put_time calls to link -// (PR #99075). +// (PR #99075). clang 20 contains a fix, but the workaround is still needed +// with older versions. asm("_ZNKSt8time_putIcSt19ostreambuf_iteratorIcSt11char_traitsIcEEE3putES3_" "RSt8ios_basecPKSt2tmPKcSB_ = " "_ZNKSt8time_putIcSt19ostreambuf_iteratorIcSt11char_traitsIcEEE3putES3_" "RSt8ios_basecPK2tmPKcSB_"); #endif +static bool IsBuiltinTrait(Token &Tok) { + +#define TYPE_TRAIT_1(Spelling, Name, Key) \ + case tok::kw_##Spelling: \ + return true; +#define TYPE_TRAIT_2(Spelling, Name, Key) \ + case tok::kw_##Spelling: \ + return true; +#define TYPE_TRAIT_N(Spelling, Name, Key) \ + case tok::kw_##Spelling: \ + return true; +#define ARRAY_TYPE_TRAIT(Spelling, Name, Key) \ + case tok::kw_##Spelling: \ + return true; +#define EXPRESSION_TRAIT(Spelling, Name, Key) \ + case tok::kw_##Spelling: \ + return true; +#define TRANSFORM_TYPE_TRAIT_DEF(K, Spelling) \ + case tok::kw___##Spelling: \ + return true; + + switch (Tok.getKind()) { + default: + return false; +#include "clang/Basic/TokenKinds.def" + } +} + /// ExpandBuiltinMacro - If an identifier token is read that is to be expanded /// as a builtin macro, handle it and return the next token as 'Tok'. void Preprocessor::ExpandBuiltinMacro(Token &Tok) { @@ -1812,25 +1842,11 @@ void Preprocessor::ExpandBuiltinMacro(Token &Tok) { getTargetInfo().getTargetOpts().FeatureMap); } return true; - } else if (II->getTokenID() != tok::identifier || - II->hasRevertedTokenIDToIdentifier()) { - // Treat all keywords that introduce a custom syntax of the form - // - // '__some_keyword' '(' [...] ')' - // - // as being "builtin functions", even if the syntax isn't a valid - // function call (for example, because the builtin takes a type - // argument). - if (II->getName().starts_with("__builtin_") || - II->getName().starts_with("__is_") || - II->getName().starts_with("__has_")) - return true; - return llvm::StringSwitch(II->getName()) - .Case("__array_rank", true) - .Case("__array_extent", true) -#define TRANSFORM_TYPE_TRAIT_DEF(_, Trait) .Case("__" #Trait, true) -#include "clang/Basic/TransformTypeTraits.def" - .Default(false); + } else if (IsBuiltinTrait(Tok)) { + return true; + } else if (II->getTokenID() != tok::identifier && + II->getName().starts_with("__builtin_")) { + return true; } else { return llvm::StringSwitch(II->getName()) // Report builtin templates as being builtins. diff --git a/clang/lib/Lex/Preprocessor.cpp b/clang/lib/Lex/Preprocessor.cpp index f0b4593e0cc22..ecc5166d7b814 100644 --- a/clang/lib/Lex/Preprocessor.cpp +++ b/clang/lib/Lex/Preprocessor.cpp @@ -902,6 +902,10 @@ void Preprocessor::Lex(Token &Result) { case tok::r_brace: StdCXXImportSeqState.handleCloseBrace(); break; +#define PRAGMA_ANNOTATION(X) case tok::annot_##X: +// For `#pragma ...` mimic ';'. +#include "clang/Basic/TokenKinds.def" +#undef PRAGMA_ANNOTATION // This token is injected to represent the translation of '#include "a.h"' // into "import a.h;". Mimic the notional ';'. case tok::annot_module_include: diff --git a/clang/lib/Parse/CMakeLists.txt b/clang/lib/Parse/CMakeLists.txt index 22e902f7e1bc5..97dd2568a0f62 100644 --- a/clang/lib/Parse/CMakeLists.txt +++ b/clang/lib/Parse/CMakeLists.txt @@ -9,6 +9,7 @@ set(LLVM_LINK_COMPONENTS add_clang_library(clangParse ParseAST.cpp ParseCXXInlineMethods.cpp + ParseContracts.cpp ParseDecl.cpp ParseDeclCXX.cpp ParseExpr.cpp diff --git a/clang/lib/Parse/ParseCXXInlineMethods.cpp b/clang/lib/Parse/ParseCXXInlineMethods.cpp index b461743833c82..6a63bace6dd50 100644 --- a/clang/lib/Parse/ParseCXXInlineMethods.cpp +++ b/clang/lib/Parse/ParseCXXInlineMethods.cpp @@ -576,6 +576,10 @@ void Parser::ParseLexedMethodDeclaration(LateParsedMethodDeclaration &LM) { LM.ExceptionSpecTokens = nullptr; } + if (!LM.ContractTokens.empty()) + ParseLexedFunctionContracts(LM.ContractTokens, LM.Method, + Parser::CES_Function | Parser::CES_CXXThis); + InFunctionTemplateScope.Scopes.Exit(); // Finish the delayed C++ method declaration. diff --git a/clang/lib/Parse/ParseContracts.cpp b/clang/lib/Parse/ParseContracts.cpp new file mode 100644 index 0000000000000..acc1270557b06 --- /dev/null +++ b/clang/lib/Parse/ParseContracts.cpp @@ -0,0 +1,431 @@ + +#include "clang/Parse/Parser.h" + +#include "clang/AST/ASTContext.h" +#include "clang/AST/PrettyDeclStackTrace.h" +#include "clang/AST/StmtCXX.h" +#include "clang/Basic/CharInfo.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/Basic/TokenKinds.h" +#include "clang/Parse/ParseDiagnostic.h" +#include "clang/Parse/Parser.h" +#include "clang/Parse/RAIIObjectsForParser.h" +#include "clang/Sema/DeclSpec.h" +#include "clang/Sema/EnterExpressionEvaluationContext.h" +#include "clang/Sema/Scope.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/TimeProfiler.h" +#include "llvm/ADT/ScopeExit.h" +#include + +using namespace clang; + +std::optional +Parser::getContractKeyword(const Token &Token) const { + // We offer the reserved keywords as identifiers in C++11 mode. + if (!getLangOpts().CPlusPlus11 || + (Token.isNot(tok::identifier) && !Token.is(tok::kw_contract_assert))) + return std::nullopt; + + // If we have a contract_assert keyword, we've may be using contract as an + // extension, so don't check the language options. + if (Token.is(tok::kw_contract_assert)) + return ContractKind::Assert; + + const IdentifierInfo *II = Token.getIdentifierInfo(); + assert(II && "Missing identifier info"); + + if (!Ident_pre) { + Ident_pre = &PP.getIdentifierTable().get("pre"); + Ident___pre = &PP.getIdentifierTable().get("__pre"); + + Ident_post = &PP.getIdentifierTable().get("post"); + Ident___post = &PP.getIdentifierTable().get("__post"); + } + + if ((II == Ident_pre && getLangOpts().Contracts) || II == Ident___pre) + return ContractKind::Pre; + + if ((II == Ident_post && getLangOpts().Contracts) || II == Ident___post) + return ContractKind::Post; + + return std::nullopt; +} + +void Parser::LateParseFunctionContractSpecifierSeq(CachedTokens &Toks) { + while (isFunctionContractKeyword(Tok)) { + if (!LateParseFunctionContractSpecifier(Toks)) { + return; + } + } +} + +static const char *getContractKeywordStr(ContractKind CK) { + switch (CK) { + case ContractKind::Pre: + return "pre"; + case ContractKind::Post: + return "post"; + case ContractKind::Assert: + return "contract_assert"; + } + llvm_unreachable("unhandled case"); +} + +static ExprResult parseContractCondition(Parser &P, Sema &Actions, + SourceLocation Loc) { + Sema::ContractScopeRAII ContractScope(Actions, Loc); + ExprResult CondResult = Actions.CorrectDelayedTyposInExpr(P.ParseConditionalExpression()); + if (CondResult.isInvalid()) + return CondResult; + return Actions.ActOnContractAssertCondition(CondResult.get()); +} + +bool Parser::LateParseFunctionContractSpecifier(CachedTokens &Toks) { + assert(isFunctionContractKeyword(Tok) && "Not in a contract"); + ContractKind CK = getContractKeyword(Tok).value(); + const char *CKStr = getContractKeywordStr(CK); + + // Consume and cache the starting token. + Token StartTok = Tok; + SourceRange ContractRange = SourceRange(ConsumeToken()); + + // Check for a '('. + if (!Tok.is(tok::l_paren)) { + // If this is a bare 'noexcept', we're done. + + Diag(Tok, diag::err_expected_lparen_after) << CKStr; + return false; + } + + // Cache the tokens for the exception-specification. + + Toks.push_back(StartTok); // 'throw' or 'noexcept' + Toks.push_back(Tok); // '(' + ContractRange.setEnd(ConsumeParen()); // '(' + + ConsumeAndStoreUntil(tok::r_paren, Toks, + /*StopAtSemi=*/true, + /*ConsumeFinalToken=*/true); + ContractRange.setEnd(Toks.back().getLocation()); + return true; +} + +/// ParseContractAssertStatement +/// +/// assertion-statement: +/// 'contract_assert' attribute-specifier-seq[opt] '(' +/// conditional-expression ')' ';' +/// +StmtResult Parser::ParseContractAssertStatement() { + assert((Tok.is(tok::kw_contract_assert)) && + "Not a contract asssert statement"); + bool IsInvalidTmp = false; + return ParseFunctionContractSpecifierImpl({}, IsInvalidTmp); +} + +/// ParseFunctionContractSpecifierSeq - Parse a series of pre/post contracts on +/// a function declaration. +/// +/// function-contract-specifier-seq : +/// function-contract-specifier function-contract-specifier-seq +// +/// function-contract-specifier: +/// precondition-specifier +/// postcondition-specifier +// +/// precondition-specifier: +/// pre attribute-specifier-seq[opt] ( conditional-expression ) +/// +/// postcondition-specifier: +/// post attribute-specifier-seq[opt] ( result-name-introducer[opt] +/// conditional-expression ) +/// +/// result-name-introducer: +/// attributed-identifier : +void Parser::ParseContractSpecifierSequence(Declarator &DeclarationInfo, + bool EnterScope, + QualType TrailingReturnType) { + if (!isFunctionContractKeyword(Tok)) + return; + + std::optional CachedType; + auto ReturnTypeResolver = [&]() { + if (!CachedType) { + QualType ReturnType = TrailingReturnType; + if (ReturnType.isNull()) { + TypeSourceInfo *TInfo = Actions.GetTypeForDeclarator(DeclarationInfo); + assert(TInfo && TInfo->getType()->isFunctionType()); + ReturnType = TInfo->getType()->getAs()->getReturnType(); + } + CachedType = ReturnType; + } + return CachedType.value(); + }; + std::optional ParserScope; + std::optional ThisScope; + + if (EnterScope) { + ParserScope.emplace(this, Scope::DeclScope | Scope::FunctionPrototypeScope | + Scope::FunctionDeclarationScope); + + auto FTI = DeclarationInfo.getFunctionTypeInfo(); + + for (unsigned i = 0; i != FTI.NumParams; ++i) { + ParmVarDecl *Param = cast(FTI.Params[i].Param); + Actions.ActOnReenterCXXMethodParameter(getCurScope(), Param); + } + } + + InitCXXThisScopeForDeclaratorIfRelevant(DeclarationInfo, + DeclarationInfo.getDeclSpec(), + ThisScope); + bool IsInvalid = false; + SourceLocation StartLoc = Tok.getLocation(); + + SmallVector Contracts; + while (isFunctionContractKeyword(Tok)) { + bool IsInvalidTmp = false; + StmtResult Contract = + ParseFunctionContractSpecifierImpl(ReturnTypeResolver, IsInvalidTmp); + IsInvalid |= IsInvalidTmp; + if (Contract.isUsable()) + Contracts.push_back(Contract.getAs()); + } + ContractSpecifierDecl *Seq = Actions.ActOnFinishContractSpecifierSequence( + Contracts, StartLoc, IsInvalid); + + assert(DeclarationInfo.Contracts == nullptr && "Already have contracts?"); + + DeclarationInfo.Contracts = Seq; +} + +StmtResult Parser::ParseFunctionContractSpecifierImpl( + llvm::function_ref ReturnTypeResolver, bool &IsInvalid) { + assert(isAnyContractKeyword(Tok) && "Not a contract keyword?"); + ContractKind CK = getContractKeyword(Tok).value(); + assert((CK == ContractKind::Assert || ReturnTypeResolver) && + "Missing return type resolver for function contract sequence"); + + auto SetInvalidOnExit = llvm::make_scope_exit([&]() { IsInvalid = true; }); + + const char *CKStr = getContractKeywordStr(CK); + + SourceLocation KeywordLoc = Tok.getLocation(); + ConsumeToken(); + + ParsedAttributes CXX11Attrs(AttrFactory); + MaybeParseCXX11Attributes(CXX11Attrs); + + if (Tok.isNot(tok::l_paren)) { + Diag(Tok, diag::err_expected_lparen_after) << CKStr; + SkipUntil({tok::equal, tok::l_brace, tok::arrow, tok::kw_try, tok::comma, + tok::l_paren}, + StopAtSemi | StopBeforeMatch); + + return StmtError(); + } + + BalancedDelimiterTracker T(*this, tok::l_paren); + SourceLocation ExprLoc = Tok.getLocation(); + + if (T.expectAndConsume(diag::err_expected_lparen_after, CKStr, + tok::r_paren)) { + return StmtError(); + } + + ParseScope ContractScope(this, Scope::DeclScope | Scope::ContractAssertScope); + EnterExpressionEvaluationContext EC( + Actions, Sema::ExpressionEvaluationContext::PotentiallyEvaluated); + + ResultNameDecl *RND = nullptr; + // FIXME(EricWF): We allow parsing the result name declarator in `pre` so we + // can diagnose it but we don't do the same for contract assert... Should we? + if ((CK != ContractKind::Assert) && Tok.is(tok::identifier) && + NextToken().is(tok::colon)) { + // Let this parse for non-post contracts. We'll diagnose it later. + + IdentifierInfo *Id = Tok.getIdentifierInfo(); + SourceLocation IdLoc = ConsumeToken(); + + ExprLoc = ConsumeToken(); + QualType ReturnType; + if (ReturnTypeResolver) { + ReturnType = ReturnTypeResolver(); + } + + RND = Actions.ActOnResultNameDeclarator(CK, getCurScope(), ReturnType, + IdLoc, Id); + + if (!RND) + return StmtError(); + + if (RND->isInvalidDecl()) + IsInvalid = true; + } + + ExprResult Cond = parseContractCondition(*this, Actions, ExprLoc); + SourceLocation EndLoc = Tok.getLocation(); + + T.consumeClose(); + + if (Cond.isInvalid()) { + Cond = + Actions.CreateRecoveryExpr(ExprLoc, EndLoc, {}, Actions.Context.BoolTy); + } else { + SetInvalidOnExit.release(); + } + + StmtResult Res = + Actions.ActOnContractAssert(CK, KeywordLoc, Cond.get(), RND, CXX11Attrs); + if (Res.isInvalid()) + IsInvalid = true; + return Res; +} + +static float EOFData = 0.0; + +ContractSpecifierDecl * +Parser::ParseLexedFunctionContractsInScope(CachedTokens &ContractToks, + QualType RetType) { + // Add the 'stop' token. + Token LastContractToken = ContractToks.back(); + Token ContractEnd; + ContractEnd.startToken(); + ContractEnd.setKind(tok::eof); + ContractEnd.setLocation(LastContractToken.getEndLoc()); + ContractEnd.setEofData(&EOFData); + ContractToks.push_back(ContractEnd); + + // Parse the default argument from its saved token stream. + ContractToks.push_back(Tok); // So that the current token doesn't get lost + PP.EnterTokenStream(ContractToks, true, /*IsReinject*/ true); + + // Consume the previously-pushed token. + ConsumeAnyToken(); + SourceLocation StartLoc = Tok.getLocation(); + auto ReturnTypeResolver = [&]() { return RetType; }; + + bool IsInvalid = false; + SmallVector Contracts; + + while (isFunctionContractKeyword(Tok)) { + bool IsInvalidTmp = false; + StmtResult Contract = + ParseFunctionContractSpecifierImpl(ReturnTypeResolver, IsInvalidTmp); + if (Contract.isUsable()) + Contracts.push_back(Contract.getAs()); + IsInvalid |= IsInvalidTmp; + } + DeclResult Req = Actions.ActOnFinishContractSpecifierSequence( + Contracts, StartLoc, IsInvalid); + + // There could be leftover tokens (e.g. because of an error). + // Skip through until we reach the original token position. + while (Tok.isNot(tok::eof)) + ConsumeAnyToken(); + + // Clean up the remaining EOF token. + if (Tok.is(tok::eof) && Tok.getEofData() == &EOFData) + ConsumeAnyToken(); + + ContractToks.clear(); + + if (Req.isInvalid()) + return nullptr; + return Req.getAs(); +} + +bool Parser::ParseLexedFunctionContracts( + CachedTokens &ContractToks, Decl *FD, + Parser::ContractEnterScopeKind ScopesToEnter) { + + // Add the 'stop' token. + Token LastContractToken = ContractToks.back(); + Token ContractEnd; + ContractEnd.startToken(); + ContractEnd.setKind(tok::eof); + ContractEnd.setLocation(LastContractToken.getEndLoc()); + ContractEnd.setEofData(FD); + ContractToks.push_back(ContractEnd); + + // Parse the default argument from its saved token stream. + ContractToks.push_back(Tok); // So that the current token doesn't get lost + PP.EnterTokenStream(ContractToks, true, /*IsReinject*/ true); + + // Consume the previously-pushed token. + ConsumeAnyToken(); + + // C++11 [expr.prim.general]p3: + // If a declaration declares a member function or member function + // template of a class X, the expression this is a prvalue of type + // "pointer to cv-qualifier-seq X" between the optional cv-qualifer-seq + // and the end of the function-definition, member-declarator, or + // declarator. + CXXMethodDecl *Method; + FunctionDecl *FunctionToPush; + if (FunctionTemplateDecl *FunTmpl = + dyn_cast(FD)) + FunctionToPush = FunTmpl->getTemplatedDecl(); + else + FunctionToPush = cast(FD); + + std::optional ParserScope; + if (ScopesToEnter & ContractEnterScopeKind::CES_Prototype) + ParserScope.emplace(this, Scope::DeclScope | Scope::FunctionPrototypeScope | + Scope::FunctionDeclarationScope); + + if (ScopesToEnter & ContractEnterScopeKind::CES_Parameters) { + for (auto *Param : FunctionToPush->parameters()) { + Actions.ActOnReenterCXXMethodParameter(getCurScope(), Param); + } + } + + Method = dyn_cast(FunctionToPush); + + std::optional FnScope; + std::optional FnContext; + + if (ScopesToEnter & ContractEnterScopeKind::CES_Function) { + FnScope.emplace(this, Scope::FnScope); + FnContext.emplace(Actions, FunctionToPush, /*NewThisContext=*/false); + } + + std::optional ThisScope; + if (ScopesToEnter & ContractEnterScopeKind::CES_CXXThis) + ThisScope.emplace(Actions, Method ? Method->getParent() : nullptr, + Method ? Method->getMethodQualifiers() : Qualifiers{}, + Method && getLangOpts().CPlusPlus11); + + // Parse the exception-specification. + SmallVector Contracts; + assert(isFunctionContractKeyword(Tok)); + + SourceLocation StartLoc = Tok.getLocation(); + + auto ReturnTypeResolver = [&]() { return FunctionToPush->getReturnType(); }; + bool IsInvalid = false; + while (isFunctionContractKeyword(Tok)) { + bool IsInvalidTmp = false; + StmtResult Contract = + ParseFunctionContractSpecifierImpl(ReturnTypeResolver, IsInvalidTmp); + if (Contract.isUsable()) + Contracts.push_back(Contract.getAs()); + IsInvalid |= IsInvalidTmp; + } + ContractSpecifierDecl *Seq = Actions.ActOnFinishContractSpecifierSequence( + Contracts, StartLoc, IsInvalid); + FunctionToPush->setContracts(Seq); + + // There could be leftover tokens (e.g. because of an error). + // Skip through until we reach the original token position. + while (Tok.isNot(tok::eof)) + ConsumeAnyToken(); + + // Clean up the remaining EOF token. + if (Tok.is(tok::eof) && Tok.getEofData() == FD) + ConsumeAnyToken(); + + ContractToks.clear(); + return true; +} diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index 122a05be1c039..ac29cfc3accdc 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -17,6 +17,7 @@ #include "clang/Basic/AttributeCommonInfo.h" #include "clang/Basic/Attributes.h" #include "clang/Basic/CharInfo.h" +#include "clang/Basic/EricWFDebug.h" #include "clang/Basic/TargetInfo.h" #include "clang/Basic/TokenKinds.h" #include "clang/Parse/ParseDiagnostic.h" @@ -2357,6 +2358,9 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS, if (Tok.is(tok::kw_requires)) ParseTrailingRequiresClause(D); + if (isFunctionContractKeyword(Tok)) + ParseContractSpecifierSequence(D, /*EnterScope=*/true); + // Save late-parsed attributes for now; they need to be parsed in the // appropriate function scope after the function Decl has been constructed. // These will be parsed in ParseFunctionDefinition or ParseLexedAttrList. @@ -2460,6 +2464,8 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS, ParseFunctionDefinition(D, TemplateInfo, &LateParsedAttrs); } + assert(D.LateParsedContracts.empty()); + return Actions.ConvertDeclToDeclGroup(TheDecl); } @@ -2540,6 +2546,7 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS, if (auto *VD = dyn_cast_or_null(ThisDecl)) VD->setObjCForDecl(true); } + Actions.FinalizeDeclaration(ThisDecl); D.complete(ThisDecl); return Actions.FinalizeDeclaratorGroup(getCurScope(), DS, ThisDecl); @@ -2550,6 +2557,13 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS, ParseDeclarationAfterDeclaratorAndAttributes(D, TemplateInfo, FRI); if (LateParsedAttrs.size() > 0) ParseLexedAttributeList(LateParsedAttrs, FirstDecl, true, false); + if (auto *FD = dyn_cast_or_null(FirstDecl)) { + if (!FD->isInvalidDecl() && !D.LateParsedContracts.empty()) { + assert(false); + assert(!FD->isThisDeclarationADefinition()); + ParseLexedFunctionContracts(D.LateParsedContracts, FD, CES_AllScopes); + } + } D.complete(FirstDecl); if (FirstDecl) DeclsInGroup.push_back(FirstDecl); @@ -2607,8 +2621,15 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS, // init-declarator: // declarator initializer[opt] // declarator requires-clause + if (Tok.is(tok::kw_requires)) ParseTrailingRequiresClause(D); + + // FIXME(EricWF): Is this the correct place? + // Yes: Though parsing here means we need to reenter the correct scope + // and context ourselves, it means we hve the function return type. + // ParseContractSpecifierSequence(D, /*EnterScope=*/true); + Decl *ThisDecl = ParseDeclarationAfterDeclarator(D, TemplateInfo); D.complete(ThisDecl); if (ThisDecl) @@ -7259,8 +7280,9 @@ void Parser::ParseDirectDeclarator(Declarator &D) { Actions.ActOnStartFunctionDeclarationDeclarator(D, TemplateParameterDepth); ParseFunctionDeclarator(D, attrs, T, IsAmbiguous); - if (IsFunctionDeclaration) + if (IsFunctionDeclaration) { Actions.ActOnFinishFunctionDeclarationDeclarator(D); + } PrototypeScope.Exit(); } else if (Tok.is(tok::l_square)) { ParseBracketDeclarator(D); @@ -7494,7 +7516,7 @@ void Parser::ParseParenDeclarator(Declarator &D) { void Parser::InitCXXThisScopeForDeclaratorIfRelevant( const Declarator &D, const DeclSpec &DS, - std::optional &ThisScope) { + std::optional &ThisScope, bool AddConst) { // C++11 [expr.prim.general]p3: // If a declaration declares a member function or member function // template of a class X, the expression this is a prvalue of type @@ -7514,7 +7536,8 @@ void Parser::InitCXXThisScopeForDeclaratorIfRelevant( return; Qualifiers Q = Qualifiers::fromCVRUMask(DS.getTypeQualifiers()); - if (D.getDeclSpec().hasConstexprSpecifier() && !getLangOpts().CPlusPlus14) + if ((D.getDeclSpec().hasConstexprSpecifier() && !getLangOpts().CPlusPlus14) || + AddConst) Q.addConst(); // FIXME: Collect C++ address spaces. // If there are multiple different address spaces, the source is invalid. @@ -7664,7 +7687,8 @@ void Parser::ParseFunctionDeclarator(Declarator &D, // delayed (even if this is a friend declaration). bool Delayed = D.getContext() == DeclaratorContext::Member && D.isFunctionDeclaratorAFunctionDeclaration(); - if (Delayed && Actions.isLibstdcxxEagerExceptionSpecHack(D) && + bool DelayedNoexcept = Delayed; + if (DelayedNoexcept && Actions.isLibstdcxxEagerExceptionSpecHack(D) && GetLookAheadToken(0).is(tok::kw_noexcept) && GetLookAheadToken(1).is(tok::l_paren) && GetLookAheadToken(2).is(tok::kw_noexcept) && @@ -7679,14 +7703,11 @@ void Parser::ParseFunctionDeclarator(Declarator &D, // for 'swap' will only find the function we're currently declaring, // whereas it expects to find a non-member swap through ADL. Turn off // delayed parsing to give it a chance to find what it expects. - Delayed = false; + DelayedNoexcept = false; } - ESpecType = tryParseExceptionSpecification(Delayed, - ESpecRange, - DynamicExceptions, - DynamicExceptionRanges, - NoexceptExpr, - ExceptionSpecTokens); + ESpecType = tryParseExceptionSpecification( + DelayedNoexcept, ESpecRange, DynamicExceptions, + DynamicExceptionRanges, NoexceptExpr, ExceptionSpecTokens); if (ESpecType != EST_None) EndLoc = ESpecRange.getEnd(); @@ -7707,6 +7728,10 @@ void Parser::ParseFunctionDeclarator(Declarator &D, TrailingReturnTypeLoc = Range.getBegin(); EndLoc = Range.getEnd(); } + + if (isFunctionContractKeyword(Tok) && Delayed) { + LateParseFunctionContractSpecifierSeq(D.LateParsedContracts); + } } else { MaybeParseCXX11Attributes(FnAttrs); } @@ -7736,18 +7761,17 @@ void Parser::ParseFunctionDeclarator(Declarator &D, } // Remember that we parsed a function type, and remember the attributes. - D.AddTypeInfo(DeclaratorChunk::getFunction( - HasProto, IsAmbiguous, LParenLoc, ParamInfo.data(), - ParamInfo.size(), EllipsisLoc, RParenLoc, - RefQualifierIsLValueRef, RefQualifierLoc, - /*MutableLoc=*/SourceLocation(), - ESpecType, ESpecRange, DynamicExceptions.data(), - DynamicExceptionRanges.data(), DynamicExceptions.size(), - NoexceptExpr.isUsable() ? NoexceptExpr.get() : nullptr, - ExceptionSpecTokens, DeclsInPrototype, StartLoc, - LocalEndLoc, D, TrailingReturnType, TrailingReturnTypeLoc, - &DS), - std::move(FnAttrs), EndLoc); + D.AddTypeInfo( + DeclaratorChunk::getFunction( + HasProto, IsAmbiguous, LParenLoc, ParamInfo.data(), ParamInfo.size(), + EllipsisLoc, RParenLoc, RefQualifierIsLValueRef, RefQualifierLoc, + /*MutableLoc=*/SourceLocation(), ESpecType, ESpecRange, + DynamicExceptions.data(), DynamicExceptionRanges.data(), + DynamicExceptions.size(), + NoexceptExpr.isUsable() ? NoexceptExpr.get() : nullptr, + ExceptionSpecTokens, DeclsInPrototype, StartLoc, LocalEndLoc, D, + TrailingReturnType, TrailingReturnTypeLoc, &DS), + std::move(FnAttrs), EndLoc); } /// ParseRefQualifier - Parses a member function ref-qualifier. Returns diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp index 6f0f5a0311bc1..e356f370a8d8a 100644 --- a/clang/lib/Parse/ParseDeclCXX.cpp +++ b/clang/lib/Parse/ParseDeclCXX.cpp @@ -13,6 +13,7 @@ #include "clang/AST/ASTContext.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/PrettyDeclStackTrace.h" +#include "clang/AST/StmtCXX.h" #include "clang/Basic/AttributeCommonInfo.h" #include "clang/Basic/Attributes.h" #include "clang/Basic/CharInfo.h" @@ -2062,6 +2063,23 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind, ConsumeBracket(); if (!SkipUntil(tok::r_square, StopAtSemi)) break; + } else if (isFunctionContractKeyword() && + (NextToken().is(tok::l_paren) || + (NextToken().is(tok::l_square) && + GetLookAheadToken(2).is(tok::l_square)))) { + // A contract may have a C++11 attribute between the keyword and the + // opening paren. + ConsumeToken(); + if (Tok.is(tok::l_square)) { + ConsumeBracket(); + if (!SkipUntil(tok::r_square, StopAtSemi)) + break; + } + if (Tok.is(tok::l_paren)) { + ConsumeParen(); + if (!SkipUntil(tok::r_paren, StopAtSemi)) + break; + } } else if (Tok.is(tok::kw_alignas) && NextToken().is(tok::l_paren)) { ConsumeToken(); ConsumeParen(); @@ -2567,6 +2585,8 @@ void Parser::HandleMemberFunctionDeclDelays(Declarator &DeclaratorInfo, } } + NeedLateParse |= !DeclaratorInfo.getLateParsedContracts().empty(); + if (NeedLateParse) { // Push this method onto the stack of late-parsed method // declarations. @@ -2587,6 +2607,8 @@ void Parser::HandleMemberFunctionDeclDelays(Declarator &DeclaratorInfo, LateMethod->ExceptionSpecTokens = FTI.ExceptionSpecTokens; FTI.ExceptionSpecTokens = nullptr; } + + LateMethod->ContractTokens = std::move(DeclaratorInfo.LateParsedContracts); } } @@ -2743,6 +2765,8 @@ bool Parser::ParseCXXMemberDeclaratorBeforeInitializer( VS); } + ParseContractSpecifierSequence(DeclaratorInfo, /*EnterScope=*/true); + // If a simple-asm-expr is present, parse it. if (Tok.is(tok::kw_asm)) { SourceLocation Loc; @@ -4403,7 +4427,6 @@ ExceptionSpecificationType Parser::ParseDynamicExceptionSpecification( Exceptions.empty()); return Exceptions.empty() ? EST_DynamicNone : EST_Dynamic; } - /// ParseTrailingReturnType - Parse a trailing return type on a new-style /// function declaration. TypeResult Parser::ParseTrailingReturnType(SourceRange &Range, @@ -4526,7 +4549,8 @@ void Parser::PopParsingClass(Sema::ParsingClassState state) { ClassStack.pop(); if (Victim->TopLevelClass) { // Deallocate all of the nested classes of this class, - // recursively: we don't need to keep any of this information. + // recursively: we don't need + // to keep any of this information. DeallocateParsedClasses(Victim); return; } diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp index e96cddf88a134..4e010b4bed2b2 100644 --- a/clang/lib/Parse/ParseExprCXX.cpp +++ b/clang/lib/Parse/ParseExprCXX.cpp @@ -1491,7 +1491,7 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer( tok::kw___private, tok::kw___global, tok::kw___local, tok::kw___constant, tok::kw___generic, tok::kw_groupshared, tok::kw_requires, tok::kw_noexcept) || - Tok.isRegularKeywordAttribute() || + isFunctionContractKeyword(Tok) || Tok.isRegularKeywordAttribute() || (Tok.is(tok::l_square) && NextToken().is(tok::l_square)); if (HasSpecifiers && !HasParentheses && !getLangOpts().CPlusPlus23) { @@ -1591,6 +1591,8 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer( if (HasParentheses && Tok.is(tok::kw_requires)) ParseTrailingRequiresClause(D); + + ParseContractSpecifierSequence(D, /*EnterScope=*/false); } // Emit a warning if we see a CUDA host/device/global attribute @@ -1622,13 +1624,35 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer( } StmtResult Stmt(ParseCompoundStatementBody()); + + BodyScope.Exit(); TemplateParamScope.Exit(); LambdaScope.Exit(); if (!Stmt.isInvalid() && !TrailingReturnType.isInvalid() && - !D.isInvalidType()) - return Actions.ActOnLambdaExpr(LambdaBeginLoc, Stmt.get()); + !D.isInvalidType()) { + + ExprResult Lambda = Actions.ActOnLambdaExpr(LambdaBeginLoc, Stmt.get()); + + if (!Lambda.isUsable()) + return Lambda; + + auto *LE = dyn_cast_or_null(Lambda.get()); + if (!LE) { + return Lambda; + } + + if (!D.LateParsedContracts.empty()) { + ParseLexedFunctionContracts(D.LateParsedContracts, LE->getCallOperator(), + CES_AllScopes); + } + assert(LE->getCallOperator() && "LambdaExpr has no call operator"); + assert(!LE->getCallOperator()->getReturnType().isNull() && "Should not be null"); + + + return Lambda; + } Actions.ActOnLambdaError(LambdaBeginLoc, getCurScope()); return ExprError(); diff --git a/clang/lib/Parse/ParseOpenACC.cpp b/clang/lib/Parse/ParseOpenACC.cpp index 87673beb34300..635039b724e6a 100644 --- a/clang/lib/Parse/ParseOpenACC.cpp +++ b/clang/lib/Parse/ParseOpenACC.cpp @@ -743,38 +743,50 @@ bool Parser::ParseOpenACCDeviceTypeList( // int-expr // Note that this is specified under 'gang-arg-list', but also applies to 'tile' // via reference. -bool Parser::ParseOpenACCSizeExpr() { - // FIXME: Ensure these are constant expressions. - +ExprResult Parser::ParseOpenACCSizeExpr(OpenACCClauseKind CK) { // The size-expr ends up being ambiguous when only looking at the current // token, as it could be a deref of a variable/expression. if (getCurToken().is(tok::star) && NextToken().isOneOf(tok::comma, tok::r_paren, tok::annot_pragma_openacc_end)) { - ConsumeToken(); - return false; + SourceLocation AsteriskLoc = ConsumeToken(); + return getActions().OpenACC().ActOnOpenACCAsteriskSizeExpr(AsteriskLoc); } - return getActions() - .CorrectDelayedTyposInExpr(ParseAssignmentExpression()) - .isInvalid(); + ExprResult SizeExpr = + getActions().CorrectDelayedTyposInExpr(ParseConstantExpression()); + + if (!SizeExpr.isUsable()) + return SizeExpr; + + SizeExpr = getActions().OpenACC().ActOnIntExpr( + OpenACCDirectiveKind::Invalid, CK, SizeExpr.get()->getBeginLoc(), + SizeExpr.get()); + + return SizeExpr; } -bool Parser::ParseOpenACCSizeExprList() { - if (ParseOpenACCSizeExpr()) { +bool Parser::ParseOpenACCSizeExprList( + OpenACCClauseKind CK, llvm::SmallVectorImpl &SizeExprs) { + ExprResult SizeExpr = ParseOpenACCSizeExpr(CK); + if (!SizeExpr.isUsable()) { SkipUntil(tok::r_paren, tok::annot_pragma_openacc_end, Parser::StopBeforeMatch); - return false; + return true; } + SizeExprs.push_back(SizeExpr.get()); + while (!getCurToken().isOneOf(tok::r_paren, tok::annot_pragma_openacc_end)) { ExpectAndConsume(tok::comma); - if (ParseOpenACCSizeExpr()) { + SizeExpr = ParseOpenACCSizeExpr(CK); + if (!SizeExpr.isUsable()) { SkipUntil(tok::r_paren, tok::annot_pragma_openacc_end, Parser::StopBeforeMatch); - return false; + return true; } + SizeExprs.push_back(SizeExpr.get()); } return false; } @@ -785,23 +797,26 @@ bool Parser::ParseOpenACCSizeExprList() { /// [num:]int-expr /// dim:int-expr /// static:size-expr -bool Parser::ParseOpenACCGangArg(SourceLocation GangLoc) { +Parser::OpenACCGangArgRes Parser::ParseOpenACCGangArg(SourceLocation GangLoc) { if (isOpenACCSpecialToken(OpenACCSpecialTokenKind::Static, getCurToken()) && NextToken().is(tok::colon)) { // 'static' just takes a size-expr, which is an int-expr or an asterisk. ConsumeToken(); ConsumeToken(); - return ParseOpenACCSizeExpr(); + ExprResult Res = ParseOpenACCSizeExpr(OpenACCClauseKind::Gang); + return {OpenACCGangKind::Static, Res}; } if (isOpenACCSpecialToken(OpenACCSpecialTokenKind::Dim, getCurToken()) && NextToken().is(tok::colon)) { ConsumeToken(); ConsumeToken(); - return ParseOpenACCIntExpr(OpenACCDirectiveKind::Invalid, - OpenACCClauseKind::Gang, GangLoc) - .first.isInvalid(); + // Parse this as a const-expression, and we'll check its integer-ness/value + // in CheckGangExpr. + ExprResult Res = + getActions().CorrectDelayedTyposInExpr(ParseConstantExpression()); + return {OpenACCGangKind::Dim, Res}; } if (isOpenACCSpecialToken(OpenACCSpecialTokenKind::Num, getCurToken()) && @@ -810,27 +825,40 @@ bool Parser::ParseOpenACCGangArg(SourceLocation GangLoc) { ConsumeToken(); // Fallthrough to the 'int-expr' handling for when 'num' is omitted. } + // This is just the 'num' case where 'num' is optional. - return ParseOpenACCIntExpr(OpenACCDirectiveKind::Invalid, - OpenACCClauseKind::Gang, GangLoc) - .first.isInvalid(); + ExprResult Res = ParseOpenACCIntExpr(OpenACCDirectiveKind::Invalid, + OpenACCClauseKind::Gang, GangLoc) + .first; + return {OpenACCGangKind::Num, Res}; } -bool Parser::ParseOpenACCGangArgList(SourceLocation GangLoc) { - if (ParseOpenACCGangArg(GangLoc)) { +bool Parser::ParseOpenACCGangArgList( + SourceLocation GangLoc, llvm::SmallVectorImpl &GKs, + llvm::SmallVectorImpl &IntExprs) { + + Parser::OpenACCGangArgRes Res = ParseOpenACCGangArg(GangLoc); + if (!Res.second.isUsable()) { SkipUntil(tok::r_paren, tok::annot_pragma_openacc_end, Parser::StopBeforeMatch); - return false; + return true; } + GKs.push_back(Res.first); + IntExprs.push_back(Res.second.get()); + while (!getCurToken().isOneOf(tok::r_paren, tok::annot_pragma_openacc_end)) { ExpectAndConsume(tok::comma); - if (ParseOpenACCGangArg(GangLoc)) { + Res = ParseOpenACCGangArg(GangLoc); + if (!Res.second.isUsable()) { SkipUntil(tok::r_paren, tok::annot_pragma_openacc_end, Parser::StopBeforeMatch); - return false; + return true; } + + GKs.push_back(Res.first); + IntExprs.push_back(Res.second.get()); } return false; } @@ -1052,12 +1080,16 @@ Parser::OpenACCClauseParseResult Parser::ParseOpenACCClauseParams( } break; } - case OpenACCClauseKind::Tile: - if (ParseOpenACCSizeExprList()) { + case OpenACCClauseKind::Tile: { + llvm::SmallVector SizeExprs; + if (ParseOpenACCSizeExprList(OpenACCClauseKind::Tile, SizeExprs)) { Parens.skipToEnd(); return OpenACCCanContinue(); } + + ParsedClause.setIntExprDetails(std::move(SizeExprs)); break; + } default: llvm_unreachable("Not a required parens type?"); } @@ -1113,12 +1145,16 @@ Parser::OpenACCClauseParseResult Parser::ParseOpenACCClauseParams( } break; } - case OpenACCClauseKind::Gang: - if (ParseOpenACCGangArgList(ClauseLoc)) { + case OpenACCClauseKind::Gang: { + llvm::SmallVector GKs; + llvm::SmallVector IntExprs; + if (ParseOpenACCGangArgList(ClauseLoc, GKs, IntExprs)) { Parens.skipToEnd(); return OpenACCCanContinue(); } + ParsedClause.setGangDetails(std::move(GKs), std::move(IntExprs)); break; + } case OpenACCClauseKind::Wait: { OpenACCWaitParseInfo Info = ParseOpenACCWaitArgument(ClauseLoc, diff --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp index 64dfcd4729699..108b532be4168 100644 --- a/clang/lib/Parse/ParseOpenMP.cpp +++ b/clang/lib/Parse/ParseOpenMP.cpp @@ -3080,6 +3080,18 @@ OMPClause *Parser::ParseOpenMPSizesClause() { OpenLoc, CloseLoc); } +OMPClause *Parser::ParseOpenMPPermutationClause() { + SourceLocation ClauseNameLoc, OpenLoc, CloseLoc; + SmallVector ArgExprs; + if (ParseOpenMPExprListClause(OMPC_permutation, ClauseNameLoc, OpenLoc, + CloseLoc, ArgExprs, + /*ReqIntConst=*/true)) + return nullptr; + + return Actions.OpenMP().ActOnOpenMPPermutationClause(ArgExprs, ClauseNameLoc, + OpenLoc, CloseLoc); +} + OMPClause *Parser::ParseOpenMPUsesAllocatorClause(OpenMPDirectiveKind DKind) { SourceLocation Loc = Tok.getLocation(); ConsumeAnyToken(); @@ -3377,6 +3389,14 @@ OMPClause *Parser::ParseOpenMPClause(OpenMPDirectiveKind DKind, Clause = ParseOpenMPSizesClause(); break; + case OMPC_permutation: + if (!FirstClause) { + Diag(Tok, diag::err_omp_more_one_clause) + << getOpenMPDirectiveName(DKind) << getOpenMPClauseName(CKind) << 0; + ErrorFound = true; + } + Clause = ParseOpenMPPermutationClause(); + break; case OMPC_uses_allocators: Clause = ParseOpenMPUsesAllocatorClause(DKind); break; diff --git a/clang/lib/Parse/ParsePragma.cpp b/clang/lib/Parse/ParsePragma.cpp index cc6f18b5b319f..12fed448d477c 100644 --- a/clang/lib/Parse/ParsePragma.cpp +++ b/clang/lib/Parse/ParsePragma.cpp @@ -2126,6 +2126,7 @@ void PragmaGCCVisibilityHandler::HandlePragma(Preprocessor &PP, // pack '(' [integer] ')' // pack '(' 'show' ')' // pack '(' ('push' | 'pop') [',' identifier] [, integer] ')' +// pack '(' 'packed' | 'full' | 'twobyte' | 'reset' ')' with -fzos-extensions void PragmaPackHandler::HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer, Token &PackTok) { @@ -2155,10 +2156,35 @@ void PragmaPackHandler::HandlePragma(Preprocessor &PP, ? Sema::PSK_Push_Set : Sema::PSK_Set; } else if (Tok.is(tok::identifier)) { + // Map pragma pack options to pack (integer). + auto MapPack = [&](const char *Literal) { + Action = Sema::PSK_Push_Set; + Alignment = Tok; + Alignment.setKind(tok::numeric_constant); + Alignment.setLiteralData(Literal); + Alignment.setLength(1); + }; + const IdentifierInfo *II = Tok.getIdentifierInfo(); if (II->isStr("show")) { Action = Sema::PSK_Show; PP.Lex(Tok); + } else if (II->isStr("packed") && PP.getLangOpts().ZOSExt) { + // #pragma pack(packed) is the same as #pragma pack(1) + MapPack("1"); + PP.Lex(Tok); + } else if (II->isStr("full") && PP.getLangOpts().ZOSExt) { + // #pragma pack(full) is the same as #pragma pack(4) + MapPack("4"); + PP.Lex(Tok); + } else if (II->isStr("twobyte") && PP.getLangOpts().ZOSExt) { + // #pragma pack(twobyte) is the same as #pragma pack(2) + MapPack("2"); + PP.Lex(Tok); + } else if (II->isStr("reset") && PP.getLangOpts().ZOSExt) { + // #pragma pack(reset) is the same as #pragma pack(pop) on XL + Action = Sema::PSK_Pop; + PP.Lex(Tok); } else { if (II->isStr("push")) { Action = Sema::PSK_Push; diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp index 6480e88316a7d..c71b9eabaa32e 100644 --- a/clang/lib/Parse/ParseStmt.cpp +++ b/clang/lib/Parse/ParseStmt.cpp @@ -356,6 +356,13 @@ StmtResult Parser::ParseStatementOrDeclarationAfterAttributes( SemiError = "co_return"; break; + case tok::kw_contract_assert: // C++20 contract-assert-statement + ProhibitAttributes(CXX11Attrs); + ProhibitAttributes(GNUAttrs); // The attributes go after the keyword + Res = ParseContractAssertStatement(); + SemiError = "contract_assert"; + break; + case tok::kw_asm: { for (const ParsedAttr &AL : CXX11Attrs) // Could be relaxed if asm-related regular keyword attributes are @@ -1956,10 +1963,10 @@ StmtResult Parser::ParseDoStatement() { SourceLocation Start = Tok.getLocation(); ExprResult Cond = ParseExpression(); // Correct the typos in condition before closing the scope. - if (Cond.isUsable()) + if (Cond.isUsable()) { Cond = Actions.CorrectDelayedTyposInExpr(Cond, /*InitDecl=*/nullptr, /*RecoverUncorrectedTypos=*/true); - else { + } else { if (!Tok.isOneOf(tok::r_paren, tok::r_square, tok::r_brace)) SkipUntil(tok::semi); Cond = Actions.CreateRecoveryExpr( diff --git a/clang/lib/Parse/ParseTemplate.cpp b/clang/lib/Parse/ParseTemplate.cpp index de29652abbfd9..0953cfc3c017e 100644 --- a/clang/lib/Parse/ParseTemplate.cpp +++ b/clang/lib/Parse/ParseTemplate.cpp @@ -331,6 +331,8 @@ Parser::ParseConceptDefinition(const ParsedTemplateInfo &TemplateInfo, if (!TryConsumeToken(tok::equal)) { Diag(Tok.getLocation(), diag::err_expected) << tok::equal; SkipUntil(tok::semi); + if (D) + D->setInvalidDecl(); return nullptr; } @@ -338,6 +340,8 @@ Parser::ParseConceptDefinition(const ParsedTemplateInfo &TemplateInfo, Actions.CorrectDelayedTyposInExpr(ParseConstraintExpression()); if (ConstraintExprResult.isInvalid()) { SkipUntil(tok::semi); + if (D) + D->setInvalidDecl(); return nullptr; } diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp index 04c2f1d380bc4..31b6aa3d435a7 100644 --- a/clang/lib/Parse/Parser.cpp +++ b/clang/lib/Parse/Parser.cpp @@ -1,4 +1,4 @@ -//===--- Parser.cpp - C Language Family Parser ----------------------------===// +//===--- Parser.cpp - C Languaa Family Parser ----------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -516,6 +516,10 @@ void Parser::Initialize() { Ident_GNU_final = nullptr; Ident_import = nullptr; Ident_module = nullptr; + Ident_pre = nullptr; + Ident___pre = nullptr; + Ident_post = nullptr; + Ident___post = nullptr; Ident_super = &PP.getIdentifierTable().get("super"); diff --git a/clang/lib/Sema/CMakeLists.txt b/clang/lib/Sema/CMakeLists.txt index 2cee4f5ef6e99..c1c40414ee8ff 100644 --- a/clang/lib/Sema/CMakeLists.txt +++ b/clang/lib/Sema/CMakeLists.txt @@ -43,6 +43,7 @@ add_clang_library(clangSema SemaCodeComplete.cpp SemaConcept.cpp SemaConsumer.cpp + SemaContract.cpp SemaCoroutine.cpp SemaCUDA.cpp SemaDecl.cpp @@ -55,6 +56,7 @@ add_clang_library(clangSema SemaExprMember.cpp SemaExprObjC.cpp SemaFixItUtils.cpp + SemaFunctionEffects.cpp SemaHLSL.cpp SemaHexagon.cpp SemaInit.cpp diff --git a/clang/lib/Sema/JumpDiagnostics.cpp b/clang/lib/Sema/JumpDiagnostics.cpp index 8af36d5c24e3d..00fdffca4e9ea 100644 --- a/clang/lib/Sema/JumpDiagnostics.cpp +++ b/clang/lib/Sema/JumpDiagnostics.cpp @@ -761,8 +761,7 @@ void JumpScopeChecker::VerifyIndirectJumps() { if (CHECK_PERMISSIVE(!LabelAndGotoScopes.count(IG))) continue; unsigned IGScope = LabelAndGotoScopes[IG]; - if (!JumpScopesMap.contains(IGScope)) - JumpScopesMap[IGScope] = IG; + JumpScopesMap.try_emplace(IGScope, IG); } JumpScopes.reserve(JumpScopesMap.size()); for (auto &Pair : JumpScopesMap) diff --git a/clang/lib/Sema/Scope.cpp b/clang/lib/Sema/Scope.cpp index 5bc7e79a68186..648cee9009312 100644 --- a/clang/lib/Sema/Scope.cpp +++ b/clang/lib/Sema/Scope.cpp @@ -47,6 +47,7 @@ void Scope::setFlags(Scope *parent, unsigned flags) { // transmit the parent's 'order' flag, if exists if (parent->getFlags() & OpenMPOrderClauseScope) Flags |= OpenMPOrderClauseScope; + } else { Depth = 0; PrototypeDepth = 0; @@ -113,7 +114,7 @@ bool Scope::containedInPrototypeScope() const { } void Scope::AddFlags(unsigned FlagsToSet) { - assert((FlagsToSet & ~(BreakScope | ContinueScope)) == 0 && + assert((FlagsToSet & ~(BreakScope | ContinueScope | ContractAssertScope)) == 0 && "Unsupported scope flags"); if (FlagsToSet & BreakScope) { assert((Flags & BreakScope) == 0 && "Already set"); @@ -123,6 +124,7 @@ void Scope::AddFlags(unsigned FlagsToSet) { assert((Flags & ContinueScope) == 0 && "Already set"); ContinueParent = this; } + Flags |= FlagsToSet; } @@ -234,6 +236,7 @@ void Scope::dumpImpl(raw_ostream &OS) const { {OpenACCComputeConstructScope, "OpenACCComputeConstructScope"}, {TypeAliasScope, "TypeAliasScope"}, {FriendScope, "FriendScope"}, + {ContractAssertScope, "ContractAssertScope"} }; for (auto Info : FlagInfo) { diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index 4be7dfbc29392..f0d1634af529f 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -643,7 +643,7 @@ void Sema::diagnoseFunctionEffectConversion(QualType DstType, QualType SrcType, const auto SrcFX = FunctionEffectsRef::get(SrcType); const auto DstFX = FunctionEffectsRef::get(DstType); if (SrcFX != DstFX) { - for (const auto &Diff : FunctionEffectDifferences(SrcFX, DstFX)) { + for (const auto &Diff : FunctionEffectDiffVector(SrcFX, DstFX)) { if (Diff.shouldDiagnoseConversion(SrcType, SrcFX, DstType, DstFX)) Diag(Loc, diag::warn_invalid_add_func_effects) << Diff.effectName(); } @@ -1529,6 +1529,9 @@ void Sema::ActOnEndOfTranslationUnit() { AnalysisWarnings.IssueWarnings(Context.getTranslationUnitDecl()); + if (Context.hasAnyFunctionEffects()) + performFunctionEffectAnalysis(Context.getTranslationUnitDecl()); + // Check we've noticed that we're no longer parsing the initializer for every // variable. If we miss cases, then at best we have a performance issue and // at worst a rejects-valid bug. @@ -2382,10 +2385,11 @@ FunctionScopeInfo *Sema::getEnclosingFunction() const { return nullptr; } -LambdaScopeInfo *Sema::getEnclosingLambda() const { +CapturingScopeInfo *Sema::getEnclosingLambdaOrBlock() const { for (auto *Scope : llvm::reverse(FunctionScopes)) { - if (auto *LSI = dyn_cast(Scope)) { - if (LSI->Lambda && !LSI->Lambda->Encloses(CurContext) && + if (auto *CSI = dyn_cast(Scope)) { + auto *LSI = dyn_cast(CSI); + if (LSI && LSI->Lambda && !LSI->Lambda->Encloses(CurContext) && LSI->AfterParameterList) { // We have switched contexts due to template instantiation. // FIXME: We should swap out the FunctionScopes during code synthesis @@ -2393,7 +2397,7 @@ LambdaScopeInfo *Sema::getEnclosingLambda() const { assert(!CodeSynthesisContexts.empty()); return nullptr; } - return LSI; + return CSI; } } return nullptr; @@ -2775,155 +2779,30 @@ bool Sema::isDeclaratorFunctionLike(Declarator &D) { return Result; } -FunctionEffectDifferences::FunctionEffectDifferences( - const FunctionEffectsRef &Old, const FunctionEffectsRef &New) { - - FunctionEffectsRef::iterator POld = Old.begin(); - FunctionEffectsRef::iterator OldEnd = Old.end(); - FunctionEffectsRef::iterator PNew = New.begin(); - FunctionEffectsRef::iterator NewEnd = New.end(); +Attr *Sema::CreateAnnotationAttr(const AttributeCommonInfo &CI, StringRef Annot, + MutableArrayRef Args) { - while (true) { - int cmp = 0; - if (POld == OldEnd) { - if (PNew == NewEnd) - break; - cmp = 1; - } else if (PNew == NewEnd) - cmp = -1; - else { - FunctionEffectWithCondition Old = *POld; - FunctionEffectWithCondition New = *PNew; - if (Old.Effect.kind() < New.Effect.kind()) - cmp = -1; - else if (New.Effect.kind() < Old.Effect.kind()) - cmp = 1; - else { - cmp = 0; - if (Old.Cond.getCondition() != New.Cond.getCondition()) { - // FIXME: Cases where the expressions are equivalent but - // don't have the same identity. - push_back(FunctionEffectDiff{ - Old.Effect.kind(), FunctionEffectDiff::Kind::ConditionMismatch, - Old, New}); - } - } - } - - if (cmp < 0) { - // removal - FunctionEffectWithCondition Old = *POld; - push_back(FunctionEffectDiff{ - Old.Effect.kind(), FunctionEffectDiff::Kind::Removed, Old, {}}); - ++POld; - } else if (cmp > 0) { - // addition - FunctionEffectWithCondition New = *PNew; - push_back(FunctionEffectDiff{ - New.Effect.kind(), FunctionEffectDiff::Kind::Added, {}, New}); - ++PNew; - } else { - ++POld; - ++PNew; - } + auto *A = AnnotateAttr::Create(Context, Annot, Args.data(), Args.size(), CI); + if (!ConstantFoldAttrArgs( + CI, MutableArrayRef(A->args_begin(), A->args_end()))) { + return nullptr; } + return A; } -bool FunctionEffectDiff::shouldDiagnoseConversion( - QualType SrcType, const FunctionEffectsRef &SrcFX, QualType DstType, - const FunctionEffectsRef &DstFX) const { +Attr *Sema::CreateAnnotationAttr(const ParsedAttr &AL) { + // Make sure that there is a string literal as the annotation's first + // argument. + StringRef Str; + if (!checkStringLiteralArgumentAttr(AL, 0, Str)) + return nullptr; - switch (EffectKind) { - case FunctionEffect::Kind::NonAllocating: - // nonallocating can't be added (spoofed) during a conversion, unless we - // have nonblocking. - if (DiffKind == Kind::Added) { - for (const auto &CFE : SrcFX) { - if (CFE.Effect.kind() == FunctionEffect::Kind::NonBlocking) - return false; - } - } - [[fallthrough]]; - case FunctionEffect::Kind::NonBlocking: - // nonblocking can't be added (spoofed) during a conversion. - switch (DiffKind) { - case Kind::Added: - return true; - case Kind::Removed: - return false; - case Kind::ConditionMismatch: - // FIXME: Condition mismatches are too coarse right now -- expressions - // which are equivalent but don't have the same identity are detected as - // mismatches. We're going to diagnose those anyhow until expression - // matching is better. - return true; - } - llvm_unreachable("Unhandled FunctionEffectDiff::Kind enum"); - case FunctionEffect::Kind::Blocking: - case FunctionEffect::Kind::Allocating: - return false; - case FunctionEffect::Kind::None: - break; + llvm::SmallVector Args; + Args.reserve(AL.getNumArgs() - 1); + for (unsigned Idx = 1; Idx < AL.getNumArgs(); Idx++) { + assert(!AL.isArgIdent(Idx)); + Args.push_back(AL.getArgAsExpr(Idx)); } - llvm_unreachable("unknown effect kind"); -} - -bool FunctionEffectDiff::shouldDiagnoseRedeclaration( - const FunctionDecl &OldFunction, const FunctionEffectsRef &OldFX, - const FunctionDecl &NewFunction, const FunctionEffectsRef &NewFX) const { - switch (EffectKind) { - case FunctionEffect::Kind::NonAllocating: - case FunctionEffect::Kind::NonBlocking: - // nonblocking/nonallocating can't be removed in a redeclaration. - switch (DiffKind) { - case Kind::Added: - return false; // No diagnostic. - case Kind::Removed: - return true; // Issue diagnostic. - case Kind::ConditionMismatch: - // All these forms of mismatches are diagnosed. - return true; - } - llvm_unreachable("Unhandled FunctionEffectDiff::Kind enum"); - case FunctionEffect::Kind::Blocking: - case FunctionEffect::Kind::Allocating: - return false; - case FunctionEffect::Kind::None: - break; - } - llvm_unreachable("unknown effect kind"); -} -FunctionEffectDiff::OverrideResult -FunctionEffectDiff::shouldDiagnoseMethodOverride( - const CXXMethodDecl &OldMethod, const FunctionEffectsRef &OldFX, - const CXXMethodDecl &NewMethod, const FunctionEffectsRef &NewFX) const { - switch (EffectKind) { - case FunctionEffect::Kind::NonAllocating: - case FunctionEffect::Kind::NonBlocking: - switch (DiffKind) { - - // If added on an override, that's fine and not diagnosed. - case Kind::Added: - return OverrideResult::NoAction; - - // If missing from an override (removed), propagate from base to derived. - case Kind::Removed: - return OverrideResult::Merge; - - // If there's a mismatch involving the effect's polarity or condition, - // issue a warning. - case Kind::ConditionMismatch: - return OverrideResult::Warn; - } - llvm_unreachable("Unhandled FunctionEffectDiff::Kind enum"); - - case FunctionEffect::Kind::Blocking: - case FunctionEffect::Kind::Allocating: - return OverrideResult::NoAction; - - case FunctionEffect::Kind::None: - break; - } - llvm_unreachable("unknown effect kind"); + return CreateAnnotationAttr(AL, Str, Args); } diff --git a/clang/lib/Sema/SemaARM.cpp b/clang/lib/Sema/SemaARM.cpp index efde354860de4..c3a6e5ef8a9d4 100644 --- a/clang/lib/Sema/SemaARM.cpp +++ b/clang/lib/Sema/SemaARM.cpp @@ -567,15 +567,9 @@ static bool checkArmStreamingBuiltin(Sema &S, CallExpr *TheCall, // * When compiling for SVE only, the caller must be in non-streaming mode. // * When compiling for both SVE and SME, the caller can be in either mode. if (BuiltinType == SemaARM::VerifyRuntimeMode) { - auto DisableFeatures = [](llvm::StringMap &Map, StringRef S) { - for (StringRef K : Map.keys()) - if (K.starts_with(S)) - Map[K] = false; - }; - llvm::StringMap CallerFeatureMapWithoutSVE; S.Context.getFunctionFeatureMap(CallerFeatureMapWithoutSVE, FD); - DisableFeatures(CallerFeatureMapWithoutSVE, "sve"); + CallerFeatureMapWithoutSVE["sve"] = false; // Avoid emitting diagnostics for a function that can never compile. if (FnType == SemaARM::ArmStreaming && !CallerFeatureMapWithoutSVE["sme"]) @@ -583,7 +577,7 @@ static bool checkArmStreamingBuiltin(Sema &S, CallExpr *TheCall, llvm::StringMap CallerFeatureMapWithoutSME; S.Context.getFunctionFeatureMap(CallerFeatureMapWithoutSME, FD); - DisableFeatures(CallerFeatureMapWithoutSME, "sme"); + CallerFeatureMapWithoutSME["sme"] = false; // We know the builtin requires either some combination of SVE flags, or // some combination of SME flags, but we need to figure out which part diff --git a/clang/lib/Sema/SemaAvailability.cpp b/clang/lib/Sema/SemaAvailability.cpp index e04cbeec16555..798cabaa31a47 100644 --- a/clang/lib/Sema/SemaAvailability.cpp +++ b/clang/lib/Sema/SemaAvailability.cpp @@ -1005,25 +1005,54 @@ bool DiagnoseUnguardedAvailability::VisitTypeLoc(TypeLoc Ty) { return true; } +struct ExtractedAvailabilityExpr { + const ObjCAvailabilityCheckExpr *E = nullptr; + bool isNegated = false; +}; + +ExtractedAvailabilityExpr extractAvailabilityExpr(const Expr *IfCond) { + const auto *E = IfCond; + bool IsNegated = false; + while (true) { + E = E->IgnoreParens(); + if (const auto *AE = dyn_cast(E)) { + return ExtractedAvailabilityExpr{AE, IsNegated}; + } + + const auto *UO = dyn_cast(E); + if (!UO || UO->getOpcode() != UO_LNot) { + return ExtractedAvailabilityExpr{}; + } + E = UO->getSubExpr(); + IsNegated = !IsNegated; + } +} + bool DiagnoseUnguardedAvailability::TraverseIfStmt(IfStmt *If) { - VersionTuple CondVersion; - if (auto *E = dyn_cast(If->getCond())) { - CondVersion = E->getVersion(); - - // If we're using the '*' case here or if this check is redundant, then we - // use the enclosing version to check both branches. - if (CondVersion.empty() || CondVersion <= AvailabilityStack.back()) - return TraverseStmt(If->getThen()) && TraverseStmt(If->getElse()); - } else { + ExtractedAvailabilityExpr IfCond = extractAvailabilityExpr(If->getCond()); + if (!IfCond.E) { // This isn't an availability checking 'if', we can just continue. return Base::TraverseIfStmt(If); } + VersionTuple CondVersion = IfCond.E->getVersion(); + // If we're using the '*' case here or if this check is redundant, then we + // use the enclosing version to check both branches. + if (CondVersion.empty() || CondVersion <= AvailabilityStack.back()) { + return TraverseStmt(If->getThen()) && TraverseStmt(If->getElse()); + } + + auto *Guarded = If->getThen(); + auto *Unguarded = If->getElse(); + if (IfCond.isNegated) { + std::swap(Guarded, Unguarded); + } + AvailabilityStack.push_back(CondVersion); - bool ShouldContinue = TraverseStmt(If->getThen()); + bool ShouldContinue = TraverseStmt(Guarded); AvailabilityStack.pop_back(); - return ShouldContinue && TraverseStmt(If->getElse()); + return ShouldContinue && TraverseStmt(Unguarded); } } // end anonymous namespace diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp index 67fc603e9ce1d..e36ee06221371 100644 --- a/clang/lib/Sema/SemaConcept.cpp +++ b/clang/lib/Sema/SemaConcept.cpp @@ -585,8 +585,8 @@ static bool CheckConstraintSatisfaction( ArrayRef TemplateArgs = TemplateArgsLists.getNumSubstitutedLevels() > 0 - ? TemplateArgsLists.getOutermost() - : ArrayRef {}; + ? TemplateArgsLists.getInnermost() + : ArrayRef{}; Sema::InstantiatingTemplate Inst(S, TemplateIDRange.getBegin(), Sema::InstantiatingTemplate::ConstraintsCheck{}, const_cast(Template), TemplateArgs, TemplateIDRange); @@ -728,6 +728,7 @@ bool Sema::addInstantiatedCapturesToScope( ValueDecl *CapturedPattern = CapturePattern.getCapturedVar(); if (!CapturedPattern->isInitCapture()) { + Instantiated++; continue; } @@ -833,7 +834,6 @@ Sema::SetupConstraintCheckingTemplateArgumentsAndScope( getTemplateInstantiationArgs(FD, FD->getLexicalDeclContext(), /*Final=*/false, /*Innermost=*/std::nullopt, /*RelativeToPrimary=*/true, - /*Pattern=*/nullptr, /*ForConstraintInstantiation=*/true); if (SetupConstraintScope(FD, TemplateArgs, MLTAL, Scope)) return std::nullopt; @@ -909,15 +909,13 @@ bool Sema::CheckFunctionConstraints(const FunctionDecl *FD, // Figure out the to-translation-unit depth for this function declaration for // the purpose of seeing if they differ by constraints. This isn't the same as // getTemplateDepth, because it includes already instantiated parents. -static unsigned -CalculateTemplateDepthForConstraints(Sema &S, const NamedDecl *ND, - bool SkipForSpecialization = false) { +static unsigned CalculateTemplateDepthForConstraints(Sema &S, + const NamedDecl *ND) { MultiLevelTemplateArgumentList MLTAL = S.getTemplateInstantiationArgs( ND, ND->getLexicalDeclContext(), /*Final=*/false, /*Innermost=*/std::nullopt, /*RelativeToPrimary=*/true, - /*Pattern=*/nullptr, - /*ForConstraintInstantiation=*/true, SkipForSpecialization); + /*ForConstraintInstantiation=*/true); return MLTAL.getNumLevels(); } @@ -956,8 +954,7 @@ static const Expr *SubstituteConstraintExpressionWithoutSatisfaction( DeclInfo.getDecl(), DeclInfo.getLexicalDeclContext(), /*Final=*/false, /*Innermost=*/std::nullopt, /*RelativeToPrimary=*/true, - /*Pattern=*/nullptr, /*ForConstraintInstantiation=*/true, - /*SkipForSpecialization*/ false); + /*ForConstraintInstantiation=*/true); if (MLTAL.getNumSubstitutedLevels() == 0) return ConstrExpr; @@ -1067,16 +1064,16 @@ bool Sema::AreConstraintExpressionsEqual(const NamedDecl *Old, bool Sema::FriendConstraintsDependOnEnclosingTemplate(const FunctionDecl *FD) { assert(FD->getFriendObjectKind() && "Must be a friend!"); + FunctionTemplateDecl *FTD = FD->getDescribedFunctionTemplate(); // The logic for non-templates is handled in ASTContext::isSameEntity, so we // don't have to bother checking 'DependsOnEnclosingTemplate' for a // non-function-template. - assert(FD->getDescribedFunctionTemplate() && - "Non-function templates don't need to be checked"); + assert(FTD && "Non-function templates don't need to be checked"); SmallVector ACs; - FD->getDescribedFunctionTemplate()->getAssociatedConstraints(ACs); + FTD->getAssociatedConstraints(ACs); - unsigned OldTemplateDepth = CalculateTemplateDepthForConstraints(*this, FD); + unsigned OldTemplateDepth = FTD->getTemplateParameters()->getDepth(); for (const Expr *Constraint : ACs) if (ConstraintExpressionDependsOnEnclosingTemplate(FD, OldTemplateDepth, Constraint)) @@ -1523,7 +1520,6 @@ static bool substituteParameterMappings(Sema &S, NormalizedConstraint &N, CSE->getNamedConcept(), CSE->getNamedConcept()->getLexicalDeclContext(), /*Final=*/false, CSE->getTemplateArguments(), /*RelativeToPrimary=*/true, - /*Pattern=*/nullptr, /*ForConstraintInstantiation=*/true); return substituteParameterMappings(S, N, CSE->getNamedConcept(), MLTAL, @@ -1804,8 +1800,8 @@ bool Sema::IsAtLeastAsConstrained(NamedDecl *D1, return false; } - unsigned Depth1 = CalculateTemplateDepthForConstraints(*this, D1, true); - unsigned Depth2 = CalculateTemplateDepthForConstraints(*this, D2, true); + unsigned Depth1 = CalculateTemplateDepthForConstraints(*this, D1); + unsigned Depth2 = CalculateTemplateDepthForConstraints(*this, D2); for (size_t I = 0; I != AC1.size() && I != AC2.size(); ++I) { if (Depth2 > Depth1) { diff --git a/clang/lib/Sema/SemaContract.cpp b/clang/lib/Sema/SemaContract.cpp new file mode 100644 index 0000000000000..0d2bf16f76355 --- /dev/null +++ b/clang/lib/Sema/SemaContract.cpp @@ -0,0 +1,1028 @@ +//===--- SemaContract.cpp - Semantic Analysis for Contracts ---------------===// +// +// 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 implements semantic analysis for contracts. +// +//===----------------------------------------------------------------------===// + +#include "TreeTransform.h" +#include "TypeLocBuilder.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/ASTDiagnostic.h" +#include "clang/AST/ASTLambda.h" +#include "clang/AST/ASTStructuralEquivalence.h" +#include "clang/AST/CXXInheritance.h" +#include "clang/AST/CharUnits.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/EvaluatedExprVisitor.h" +#include "clang/AST/ExprCXX.h" +#include "clang/AST/ExprObjC.h" +#include "clang/AST/IgnoreExpr.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/AST/StmtCXX.h" +#include "clang/AST/StmtObjC.h" +#include "clang/AST/TypeLoc.h" +#include "clang/AST/TypeOrdering.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Sema/EnterExpressionEvaluationContext.h" +#include "clang/Sema/Initialization.h" +#include "clang/Sema/Lookup.h" +#include "clang/Sema/Ownership.h" +#include "clang/Sema/Scope.h" +#include "clang/Sema/ScopeInfo.h" +#include "clang/Sema/SemaCUDA.h" +#include "clang/Sema/SemaInternal.h" +#include "clang/Sema/SemaObjC.h" +#include "clang/Sema/SemaOpenMP.h" +#include "clang/Sema/Template.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/STLForwardCompat.h" +#include "llvm/ADT/ScopeExit.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringExtras.h" + +using namespace clang; +using namespace sema; +using llvm::DenseSet; + +class clang::SemaContractHelper { +public: + static SmallVector + buildAttributesWithDummyNode(Sema &S, ParsedAttributes &Attrs, + SourceLocation Loc) { + // We shouldn't end up actually emitting any diagnostics pointing an the + // dummy node, but we need to have a valid source location for any + // diagnostics. (Pray they don't call getEndLoc()), which will attempt to + // read past the end of the buffer. + + ContractStmt Dummy(Stmt::EmptyShell(), ContractKind::Pre, false, 0); + Dummy.KeywordLoc = Loc; + SmallVector Result; + S.ProcessStmtAttributes(&Dummy, Attrs, Result); + return Result; + } +}; + +namespace { +template struct ValueRAII { + ValueRAII(T &XDest, T const &Value, bool Enter = true) : Dest(&XDest) { + if (Enter) + push(Value); + } + + ~ValueRAII() { pop(); } + + ValueRAII(ValueRAII const &) = delete; + ValueRAII &operator=(ValueRAII const &) = delete; + + void push(T const &Value) { + assert(!Entered); + OldValue = *Dest; + *Dest = Value; + Entered = true; + } + + void pop() { + if (Entered) + *Dest = OldValue; + Entered = false; + } + +private: + bool Entered = false; + T *Dest; + T OldValue; +}; + +template ValueRAII(T &, T const &) -> ValueRAII; + +} // namespace + +namespace { + +/// Substitute the 'auto' specifier or deduced template specialization type +/// specifier within a type for a given replacement type. +class RebuildAutoResultName : public TreeTransform { + QualType Replacement; + using inherited = TreeTransform; + +public: + RebuildAutoResultName(Sema &SemaRef, QualType Replacement) + : TreeTransform(SemaRef), + Replacement(Replacement) {} +#if 1 + QualType TransformAutoType(TypeLocBuilder &TLB, AutoTypeLoc TL) { + // If we're building the type pattern to deduce against, don't wrap the + // substituted type in an AutoType. Certain template deduction rules + // apply only when a template type parameter appears directly (and not if + // the parameter is found through desugaring). For instance: + // auto &&lref = lvalue; + // must transform into "rvalue reference to T" not "rvalue reference to + // auto type deduced as T" in order for [temp.deduct.call]p3 to apply. + // + // FIXME: Is this still necessary? + + QualType Result = SemaRef.Context.getAutoType( + Replacement, TL.getTypePtr()->getKeyword(), Replacement.isNull(), false, + TL.getTypePtr()->getTypeConstraintConcept(), + TL.getTypePtr()->getTypeConstraintArguments()); + auto NewTL = TLB.push(Result); + NewTL.copy(TL); + return Result; + } +#endif +}; + +} // namespace + +ExprResult Sema::ActOnContractAssertCondition(Expr *Cond) { + assert(currentEvaluationContext().isContractAssertionContext() && + "Wrong context for statement"); + + if (Cond->isTypeDependent()) + return Cond; + + ConditionResult Res = ActOnCondition(getCurScope(), Cond->getExprLoc(), Cond, Sema::ConditionKind::Boolean, /*MissingOK=*/false); + if (Res.isInvalid()) + return ExprError(); + Cond = Res.get().second; + assert(Cond); + if (auto KnownValue = Res.getKnownValue(); KnownValue.has_value()) { + Diag(Cond->getExprLoc(), diag::warn_ericwf_fixme) << + (std::string("Condition always evaluates to ") + (*KnownValue ? "true" : "false")) << Cond; + } + return Cond; +} + +StmtResult Sema::BuildContractStmt(ContractKind CK, SourceLocation KeywordLoc, + Expr *Cond, DeclStmt *RND, + ArrayRef Attrs) { + return ContractStmt::Create(Context, CK, KeywordLoc, Cond, RND, Attrs); +} + +StmtResult Sema::ActOnContractAssert(ContractKind CK, SourceLocation KeywordLoc, + Expr *Cond, ResultNameDecl *RND, + ParsedAttributes &ContractAttrs) { + + DeclStmt *RNDStmt = nullptr; + if (RND) { + StmtResult NewDeclStmt = ActOnDeclStmt( + ConvertDeclToDeclGroup(RND), RND->getLocation(), RND->getLocation()); + + // FIXME(EricWF): Can this happen? + if (NewDeclStmt.isInvalid()) + return StmtError(); + RNDStmt = NewDeclStmt.getAs(); + } + + StmtResult Res = + BuildContractStmt(CK, KeywordLoc, Cond, RNDStmt, + SemaContractHelper::buildAttributesWithDummyNode( + *this, ContractAttrs, KeywordLoc)); + + if (Res.isInvalid()) + return StmtError(); + + if (RND && RND->getType()->isUndeducedAutoType()) { + return Res; + } + + return ActOnFinishFullStmt(Res.get()); +} + +/// ActOnResultNameDeclarator - Called from Parser::ParseFunctionDeclarator() +/// to introduce parameters into function prototype scope. +ResultNameDecl *Sema::ActOnResultNameDeclarator(ContractKind CK, Scope *S, + QualType RetType, + SourceLocation IDLoc, + IdentifierInfo *II) { + // assert(S && S->isContractAssertScope() && "Invalid scope for result name"); + assert(II && "ResultName requires an identifier"); + + bool IsInvalid = false; + + if (RetType->isVoidType()) { + // Adjust the type of the result name to be int so we can actually produce a + // node. + RetType = Context.IntTy; + Diag(IDLoc, diag::err_void_result_name) << II; + IsInvalid = true; + } + + // If needed, invent a fake placeholder type to represent the result name. + // This is needed when we encounter a deduced return type on a non-template + // function. We'll replace this once we've completed the function definition + // (which must be attached to this declaration). + bool HasInventedPlaceholderTypes = + RetType->isUndeducedAutoType() && !RetType->isDependentType(); + if (HasInventedPlaceholderTypes) + RetType = Context.getAutoType(QualType(), AutoTypeKeyword::Auto, true, + false, nullptr, {}); + auto *New = ResultNameDecl::Create(Context, CurContext, IDLoc, II, RetType, + nullptr, HasInventedPlaceholderTypes); + + if (IsInvalid) + New->isInvalidDecl(); + + // Check for redeclaration of parameters, e.g. int foo(int x, int x); + if (II) { + LookupResult R(*this, II, IDLoc, LookupOrdinaryName, + RedeclarationKind::ForVisibleRedeclaration); // FIXME(EricWF) + LookupName(R, S); + if (!R.empty()) { + NamedDecl *PrevDecl = *R.begin(); + if (R.isSingleResult() && PrevDecl->isTemplateParameter()) { + // Maybe we will complain about the shadowed template parameter. + // DiagnoseTemplateParameterShadow(D.getIdentifierLoc(), PrevDecl); + // Just pretend that we didn't see the previous declaration. + PrevDecl = nullptr; + } + // FIXME(EricWF): Diagnose lookup conflicts with lambda captures and + // parameter declarations. + if (auto *PVD = dyn_cast(PrevDecl)) { + Diag(IDLoc, diag::err_result_name_shadows_param) + << II; // FIXME(EricWF): Change the diagnostic here. + Diag(PVD->getLocation(), diag::note_previous_declaration); + New->setInvalidDecl(true); + } + } + } + + if (CK != ContractKind::Post) { + assert(II && "ResultName requires an identifier"); + + Diag(IDLoc, diag::err_result_name_not_allowed) << II; + New->setInvalidDecl(true); + } + + assert(!S || S->isContractAssertScope()); + + // Add the parameter declaration into this scope. + if (S) + S->AddDecl(New); + + IdResolver.AddDecl(New); + + return New; +} + +bool Sema::CheckEquivalentContractSequence(FunctionDecl *OrigDecl, + FunctionDecl *NewDecl) { + // If the new declaration doesn't contain any contracts, that's fine, they can + // be omitted. + if (!NewDecl->hasContracts()) + return false; + + assert(!NewDecl->getContracts()->isInvalidDecl()); + if (OrigDecl->hasContracts() && OrigDecl->getContracts()->isInvalidDecl()) { + NewDecl->getContracts()->setInvalidDecl(true); + NewDecl->setInvalidDecl(true); + return true; + } + ContractSpecifierDecl *OrigContractSpec = OrigDecl->getContracts(); + ContractSpecifierDecl *NewContractSpec = NewDecl->getContracts(); + ArrayRef OrigContracts, NewContracts; + if (OrigContractSpec) + OrigContracts = OrigContractSpec->contracts(); + + NewContracts = NewContractSpec->contracts(); + + if (OrigContractSpec && OrigContractSpec->isInvalidDecl()) { + NewContractSpec->setInvalidDecl(true); + NewDecl->setInvalidDecl(true); + return true; + } + + enum DifferenceKind { + DK_None = -1, + DK_OrigMissing, + DK_NumContracts, + DK_Kind, + DK_ResultName, + DK_Cond + }; + unsigned ContractIndex = 0; + + // p2900 [basic.contract.func] + // A declaration E of a function f that is not a first declaration shall have + // either no function contract-specifier-seq or the same + // function-contract-specifier-seq as any first declaration D reachable from + // E. + const DifferenceKind DK = [&] { + // Contracts may be omitted from following declarations. + if (NewContracts.empty()) + return DK_None; + + // ... But if they exist, they must be present on the original declaration. + if (OrigContracts.empty()) + return DK_OrigMissing; + + if (OrigContracts.size() != NewContracts.size()) + return DK_NumContracts; + + // ... And if they exist on the original declaration, they must be the same. + for (; ContractIndex < OrigContracts.size(); ++ContractIndex) { + auto *OC = OrigContracts[ContractIndex]; + auto *NC = NewContracts[ContractIndex]; + + if (OC->getContractKind() != NC->getContractKind()) + return DK_Kind; + if (OC->hasResultName() != NC->hasResultName()) + return DK_ResultName; + if (!Context.hasSameExpr(OC->getCond(), NC->getCond())) + return DK_Cond; + } + return DK_None; + }(); + + // Nothing to diagnose. + if (DK == DK_None) + return false; + + NewContractSpec->setInvalidDecl(true); + + assert(!NewContracts.empty() && "Cannot diagnose empty contract sequence"); + SourceRange NewContractRange = SourceRange( + NewContracts.front()->getBeginLoc(), NewContracts.back()->getEndLoc()); + SourceRange OrigContractRange = + OrigContracts.empty() + ? SourceRange(OrigDecl->getEndLoc(), OrigDecl->getEndLoc()) + : SourceRange(OrigContracts.front()->getBeginLoc(), + OrigContracts.back()->getEndLoc()); + + // Otherwise, we're producing a diagnostic. + Diag(NewDecl->getLocation(), diag::err_function_different_contract_seq) + << isa(NewDecl) << NewContractRange; + + if (DK == DK_NumContracts || DK == DK_OrigMissing) { + int PluralSelect = + OrigContracts.empty() + (OrigContracts.size() < NewContracts.size()); + Diag(OrigDecl->getLocation(), diag::note_contract_spec_seq_arity_mismatch) + << PluralSelect << OrigContracts.size() << NewContracts.size() + << OrigContractRange; + return true; + } + + auto *OC = OrigContracts[ContractIndex]; + auto *NC = NewContracts[ContractIndex]; + + auto GetRangeForNote = [&](const ContractStmt *CS) { + switch (DK) { + case DK_Kind: + return SourceRange(CS->getBeginLoc(), CS->getBeginLoc()); + case DK_ResultName: + return CS->hasResultName() ? CS->getResultName()->getSourceRange() + : CS->getCond()->getSourceRange(); + case DK_Cond: + return CS->getCond()->getSourceRange(); + case DK_OrigMissing: + case DK_NumContracts: + case DK_None: + llvm_unreachable("unhandled enum value"); + } + llvm_unreachable("unhandled enum value"); + }; + + Diag(NC->getBeginLoc(), diag::note_mismatched_contract) + << GetRangeForNote(NC); + Diag(OC->getBeginLoc(), diag::note_previous_contracts) + << DK << (int)OC->getContractKind() << OC->hasResultName() + << GetRangeForNote(OC); + + return true; +} + +namespace { +/// A checker which white-lists certain expressions whose conversion +/// to or from retainable type would otherwise be forbidden in ARC. +struct ParamReferenceChecker : RecursiveASTVisitor { + typedef RecursiveASTVisitor super; + +private: + Sema &Actions; + const FunctionDecl *FD; + +public: + ContractStmt *CurrentContract = nullptr; + ContractSpecifierDecl *CSD = nullptr; + +public: + ParamReferenceChecker(Sema &S, const FunctionDecl *FD) + : Actions(S), FD(FD), CSD(FD->getContracts()) { + assert(CSD); + } + + bool TraverseContractStmt(ContractStmt *CS) { + ValueRAII SetCurrent(CurrentContract, CS, + CurrentContract == nullptr); + if (CS->getCond()) + TraverseStmt(CS->getCond()); + return true; + } + + enum DiagSelector { + DS_None = -1, + DS_Array = 0, + DS_Function = 1, + DS_NotConst = 2 + }; + + DiagSelector classifyDiagnosableParmVar(const ParmVarDecl *PVD, + const DeclRefExpr *Usage) const { + // We only care about parameter's for the function with the contracts we're + // evaluating. Ensure this parameter belongs to that function. + if (![&] { + if (PVD->getFunctionScopeIndex() < FD->getNumParams()) + return FD->getParamDecl(PVD->getFunctionScopeIndex()) == PVD; + return false; + }()) + return DS_None; + + // We'll diagnose this when it's non-dependent + if (PVD->getType()->isDependentType()) + return DS_None; + + // Skip diagnosing this parameter if we've already done it. + if (DiagnosedDecls.count(PVD)) + return DS_None; + + // [dcl.contract.func] p2900r8 -- + // If a postcondition odr-uses ([basic.def.odr]) a non-reference + // parameter... + if (PVD->getType()->isReferenceType() || Usage->isNonOdrUse()) + return DS_None; + + QualType PVDType = PVD->getOriginalType(); + + // that parameter shall not have an array type... + if (PVDType->isArrayType() || PVDType->isArrayParameterType()) + return DS_Array; + assert(!PVD->getOriginalType()->isArrayType()); + + // or function type... + if (PVDType->isFunctionPointerType()) + return DS_Function; + + // ...and that parameter shall be declared const + if (!PVDType.isConstQualified()) + return DS_NotConst; + + return DS_None; + } + + bool VisitDeclRefExpr(DeclRefExpr *E) { + assert(CurrentContract && "No current contract to use in diagnostics?"); + + // [dcl.contract.func] p2900r8 -- + // If a postcondition odr-uses ([basic.def.odr]) a non-reference + // parameter, ... that parameter shall be declared const and shall not + // have array or function type. [ Note: This requirement applies even to + // declarations that do not specify the postcondition-specifier.] + auto *PVD = dyn_cast_or_null(E->getDecl()); + if (!PVD || PVD->getType()->isDependentType() || DiagnosedDecls.count(PVD)) + return true; + + if (DiagSelector DiagSelect = classifyDiagnosableParmVar(PVD, E); + DiagSelect != DS_None) { + DiagnosedDecls.insert(PVD); + CSD->setInvalidDecl(true); + + Actions.Diag(E->getLocation(), + diag::err_contract_postcondition_parameter_type_invalid) + << PVD->getIdentifier() << DiagSelect + << CurrentContract->getSourceRange(); + Actions.Diag(PVD->getTypeSpecStartLoc(), diag::note_parameter_type) + << (DiagSelect == DS_NotConst ? PVD->getType() + : PVD->getOriginalType()) + << PVD->getSourceRange(); + } + + return true; + } + +private: + DenseSet DiagnosedDecls; +}; + +} // namespace + + +static void diagnoseParamTypes(Sema &S, FunctionDecl *FD, + ContractSpecifierDecl *CSD) { + + // Check for post-conditions that reference non-const parameters. + ParamReferenceChecker Checker(S, FD); + for (auto *CS : CSD->postconditions()) { + Checker.TraverseContractStmt(CS); + // FIXME(EricWF): DIagnose non-const function param types. + } +} + +void Sema::CheckFunctionContracts(FunctionDecl *FD, bool IsDefinition, bool IsInstantiation) { + assert(FD && FD->hasContracts()); + + diagnoseParamTypes(*this, FD, FD->getContracts()); +} + +void Sema::InstantiateContractSpecifier( + SourceLocation PointOfInstantiation, FunctionDecl *Instantiation, + const FunctionDecl *Pattern, + const MultiLevelTemplateArgumentList &TemplateArgs) { + + ContractSpecifierDecl *PatternCSD = Pattern->getContracts(); + if (!PatternCSD) + return; + bool IsInvalid = false; + + auto *Method = const_cast( + dyn_cast_if_present(Instantiation)); + + Sema::ContextRAII SavedContext(*this, Instantiation); + Sema::CXXThisScopeRAII ThisxScope( + SemaRef, Method ? Method->getParent() : nullptr, + Method ? Method->getMethodQualifiers().withConst() : Qualifiers{}, + Method != nullptr); + + LocalInstantiationScope Scope(*this, true); + + SmallVector NewContracts; + for (auto *CS : PatternCSD->contracts()) { + StmtResult NewStmt = SubstStmt(CS, TemplateArgs); + if (NewStmt.isInvalid()) + IsInvalid = true; + else + NewContracts.push_back(NewStmt.getAs()); + } + + ContractSpecifierDecl *NewCSD = BuildContractSpecifierDecl( + NewContracts, Instantiation, PatternCSD->getLocation(), IsInvalid); + assert(NewCSD); + + Instantiation->setContracts(NewCSD); + if (!Instantiation->isDependentContext()) + CheckFunctionContracts(Instantiation, /*IsDefinition=*/false, /*IsInstantiation=*/true); +} + +ContractSpecifierDecl * +Sema::BuildContractSpecifierDecl(ArrayRef Contracts, + DeclContext *DC, SourceLocation Loc, + bool IsInvalid) { + ContractSpecifierDecl *CSD = + ContractSpecifierDecl::Create(Context, DC, Loc, Contracts, IsInvalid); + return CSD; +} + +/// ActOnFinishContractSpecifierSequence - This is called after a +/// contract-specifier-seq has been parsed. +/// +/// It's primary job is to set the canonical result name decl for each result +/// name. +ContractSpecifierDecl * +Sema::ActOnFinishContractSpecifierSequence(ArrayRef Contracts, + SourceLocation Loc, bool IsInvalid) { + assert((!Contracts.empty() || IsInvalid) && "Expected at least one contract"); + + return BuildContractSpecifierDecl(Contracts, CurContext, Loc, IsInvalid); +} + +void Sema::ActOnContractsOnFinishFunctionDecl(FunctionDecl *D, + bool IsDefinition) { + + FunctionDecl *FD; + if (FunctionTemplateDecl *FunTmpl = dyn_cast(D)) { + FD = FunTmpl->getTemplatedDecl(); + } else + FD = D; + if (D != FD && FD->getContracts() == D->getContracts()) + D->setContracts(nullptr); + + if (!FD->hasContracts() && !FD->getFirstDecl()->hasContracts()) + return; + + auto *First = FD->getFirstDecl(); + + // If the definition has omitted the contracts, but the first declaration has + // them, we need to rebuild the contracts to refer to the parameters of the + // definition. + // + // For function templates, we'll create a copy when we instantiate the definition. + if (First->hasContracts() && !FD->hasContracts() && IsDefinition && + !FD->isTemplateInstantiation()) { + // Note: This case is mutually exclusive with the NonDependentPlaceholders + // case, since we can't have a placeholder return type on a declaration that + // isn't a definition. + ContractSpecifierDecl *NewCSD = RebuildContractSpecifierForDecl(First, FD); + NewCSD->setOwningFunction(FD); + FD->setContracts(NewCSD); + } + assert(FD->hasContracts()); + + ContractSpecifierDecl *CSD = FD->getContracts(); + + if (!D->isTemplateInstantiation()) { + // Note: IsInstantiation here means whether we're calling during the instantiation of the contract specifier, + // rather than whether the FD declares a function instantiation. + CheckFunctionContracts(FD, IsDefinition, /*IsInstantiation=*/false); + } + + // When the declared return type of a non-templated function contains a + // placeholder type, a postcondition-specifier with a result-name-introducer + // shall be present only on a definition. + if (CSD->hasInventedPlaceholdersTypes() && !FD->isTemplateInstantiation() && + !FD->isTemplated()) { + if (!IsDefinition && !FD->isThisDeclarationADefinition()) { + Diag(CSD->getCanonicalResultName()->getLocation(), + diag::err_auto_result_name_on_non_def_decl) + << CSD->getCanonicalResultName(); + Diag(FD->getReturnTypeSourceRange().getBegin(), + diag::note_function_return_type) + << FD->getReturnTypeSourceRange(); + for (auto *RND : CSD->result_names()) + RND->setInvalidDecl(true); + } + } +} + +namespace { + +/// DiagnoseDeclRefVisitor - A terrible hack to re-bind (NOT rebuild) the +/// ParmVarDecl references in a contract to the new ParmVarDecls. +struct RebuildFunctionContracts + : public TreeTransform { + + using Inherited = TreeTransform; + bool IsAlwaysRebuild = false; + bool AlwaysRebuild() const { return IsAlwaysRebuild; } + + RebuildFunctionContracts(Sema &S, bool Rebuild = false) + : TreeTransform(S), IsAlwaysRebuild(Rebuild) {} +}; + +} // namespace + +/// Rebuild the contract specifier written on one declaration in the context of +/// a the definition. This rebinds parameters and result names as needed. +ContractSpecifierDecl * +Sema::RebuildContractSpecifierForDecl(FunctionDecl *First, FunctionDecl *Def) { + + assert(Def->getContracts() == First->getContracts() || !Def->hasContracts()); + Def->setContracts(nullptr); + Sema::ContextRAII SavedContext(*this, Def); + std::optional ThisScope; + if (auto *CXXMethod = dyn_cast(Def)) { + ThisScope.emplace(*this, CXXMethod->getParent(), + CXXMethod->getMethodQualifiers().withConst(), + /*IsLambda*/ true); + } + RebuildFunctionContracts Rebuilder(*this, true); + for (unsigned I = 0; I < First->getNumParams(); ++I) { + Rebuilder.transformedLocalDecl(First->getParamDecl(I), + Def->getParamDecl(I)); + } + for (auto *RND : First->getContracts()->result_names()) { + QualType Replacement = Def->getReturnType(); + auto *NewRND = + ActOnResultNameDeclarator(ContractKind::Post, nullptr, Replacement, + RND->getLocation(), RND->getIdentifier()); + Rebuilder.transformedLocalDecl(RND, NewRND); + } + SmallVector NewContracts; + bool IsInvalid = false; + for (auto *CS : First->contracts()) { + StmtResult NewStmt = Rebuilder.TransformContractStmt(CS); + if (NewStmt.isInvalid()) + IsInvalid = true; + else + NewContracts.push_back(NewStmt.getAs()); + } + + auto *CSD = BuildContractSpecifierDecl( + NewContracts, Def, First->getContracts()->getLocation(), IsInvalid); + Def->setContracts(CSD); + + if (CSD->isInvalidDecl()) + Def->isInvalidDecl(); + return CSD; +} + +/// +DeclResult Sema::RebuildContractsWithPlaceholderReturnType(FunctionDecl *FD) { + ContractSpecifierDecl *CSD = FD->getContracts(); + assert(CSD && CSD->hasInventedPlaceholdersTypes() && + "Cannot rebuild contracts without placeholders"); + if (FD->isInvalidDecl()) { + CSD->setInvalidDecl(true); + return DeclResult(/*IsInvalid*/ true); + } + + RebuildFunctionContracts Rebuilder(*this, true); + + QualType Replacement; + auto CheckFunctionReturnType = [&](SourceLocation Loc) -> bool { + if (!Replacement.isNull()) + return false; + + if (FD->getReturnType()->isUndeducedType()) { + assert(!FD->isInvalidDecl()); + assert(!CSD->isInvalidDecl()); + if (DeduceReturnType(FD, Loc, true)) { + Diag(CSD->getLocation(), diag::err_ericwf_unimplemented) + << "IDK what's wrong"; + return true; + } + } + assert(!FD->getReturnType()->isUndeducedType()); + Replacement = FD->getReturnType(); + return false; + }; + + SmallVector> Transformed; + + for (auto *RND : FD->getContracts()->result_names()) { + if (Replacement.isNull()) { + if (CheckFunctionReturnType(RND->getLocation())) { + FD->setInvalidDecl(true); + return DeclResult(/*IsInvalid*/ true); + } + } + assert(!Replacement.isNull()); + auto *NewRND = + ActOnResultNameDeclarator(ContractKind::Post, nullptr, Replacement, + RND->getLocation(), RND->getIdentifier()); + Transformed.emplace_back(RND, NewRND); + } + + for (auto [K, V] : Transformed) + Rebuilder.transformedLocalDecl(K, {V}); + + SmallVector NewContracts; + bool IsInvalid = false; + for (auto *CS : FD->contracts()) { + StmtResult NewStmt = Rebuilder.TransformContractStmt(CS); + if (NewStmt.isInvalid()) + IsInvalid = true; + else + NewContracts.push_back(NewStmt.getAs()); + } + + auto *NewCSD = BuildContractSpecifierDecl( + NewContracts, FD, FD->getContracts()->getLocation(), IsInvalid); + + FD->setContracts(NewCSD); + if (NewCSD->isInvalidDecl()) + FD->isInvalidDecl(); + + return NewCSD; +} + +// ActOnContractsOnFinishFunctionBody +// +// This function ensures there is a usable version of the +// function contracts attached to the definition declaration. +// +// This is already the case if the definition declaration was spelled with +// contracts. +// +// Otherwise, we might have contracts on the first declaration. +// If we do, +// +// (1) attach them to the defining declaration. +// (2) rebind any references to parameters contained within the contracts. +// +// This is important because the defining definitions parameters will +// be the ones evaluated by ExprConstant/CodeGen. +// +// This is probably BAD BAD NOT GOOD. +// But it works nicely. +void Sema::ActOnContractsOnFinishFunctionBody(FunctionDecl *Def) { + + // If we had a deduced return type on a non-template function, we can now + // attempt to deduce the return type and rebuild the contracts with the + // deduced return type. + if (Def->hasContracts() && + Def->getContracts()->hasInventedPlaceholdersTypes()) { + assert((Def->isThisDeclarationADefinition() || + Def->isTemplateInstantiation()) && + "Wrong declaration passed?"); + + DeclResult Res = RebuildContractsWithPlaceholderReturnType(Def); + if (Res.isInvalid()) { + Def->setInvalidDecl(true); + return; + } + auto *NewCSD = Res.getAs(); + assert(NewCSD); + NewCSD->setOwningFunction(Def); + Def->setContracts(NewCSD); + if (NewCSD->isInvalidDecl()) + Def->setInvalidDecl(true); + } + + +} + +Sema::ContractScopeRAII::ContractScopeRAII(Sema &S, SourceLocation Loc, + bool OverrideThis) + : S(S), Record{Loc, + S.CurContext, + S.CXXThisTypeOverride, + (unsigned)S.FunctionScopes.size(), + false, + S.ExprEvalContexts.back().InContractAssertion, + S.CurrentContractEntry} { + + // Setup the constification context when building declref expressions. + S.ExprEvalContexts.back().InContractAssertion = true; + + // P2900R8 [expr.prim.this]p2 + // If the expression 'this' appears ... in a contract assertion + // (including as the result of the implicit transformation in the body of + // a non-static member function and including in the bodies of nested + // lambda-expressions), + // ... + // const is combined with the cv-qualifier-seq used to generate the resulting + // type (see below + if (!S.CXXThisTypeOverride.isNull()) { + assert(S.CXXThisTypeOverride->isPointerType()); + QualType ClassType = S.CXXThisTypeOverride->getPointeeType(); + if (not ClassType.isConstQualified()) { + // If the 'this' object is const-qualified, we need to remove the + // const-qualification for the contract check. + ClassType.addConst(); + Record.AddedConstToCXXThis = true; + S.CXXThisTypeOverride = S.Context.getPointerType(ClassType); + } + } + + if (!S.FunctionScopes.empty()) { + assert(S.FunctionScopes.back() && "No function scope?"); + assert(!S.FunctionScopes.back()->InContract && "Already in contract?"); + S.FunctionScopes.back()->InContract = true; + } + + + S.CurrentContractEntry = &Record; +} + +Sema::ContractScopeRAII::~ContractScopeRAII() { + assert(S.CurrentContractEntry == &Record && "Contract scope mismatch"); + assert(S.ExprEvalContexts.back().InContractAssertion == true); + S.ExprEvalContexts.back().InContractAssertion = Record.WasInContractContext; + S.CXXThisTypeOverride = Record.PreviousCXXThisType; + + + if (!S.FunctionScopes.empty()) + S.FunctionScopes.back()->InContract = false; + + S.CurrentContractEntry = Record.Previous; +} + +bool Sema::isUsageAcrossContract(const ValueDecl *VD) { + // There's no contract scope anywhere above us. + if (!CurrentContractEntry) + return false; + + // Fast Path: We're in an immediate contract assertion expression evaluation context. + if (isContractAssertionContext()) + return true; + + // We're going to walk up from the DeclContext we captured when we entered the contract + // scope to try and find the declaration context of the specified decl. If we do, + // then the decl is used across a contract. + + // FIXME(EricWF): Why is this here? + if (isa(VD)) + VD = cast(VD)->getCanonicalDecl(); + + const DeclContext *DC = VD->getDeclContext(); + if (!DC->isFunctionOrMethod()) + return false; + + // FIXME(EricWF): This seems expensive? + // Make sure the ValueDecl has a DeclContext that is the same as or a parent + // of the most recent contract entry + auto *StartContext = CurrentContractEntry->ContextAtPush; + + while (StartContext) { + if (StartContext->isFileContext()) + break; + if (StartContext->Equals(VD->getDeclContext())) + return true; + StartContext = StartContext->getParent(); + } + return false; +} + +/// [basic.contract.general] +/// Within the predicate of a contract assertion, id-expressions referring to +/// variables with automatic storage duration are const ([expr.prim.id.unqual]) +ContractConstification Sema::getContractConstification(const ValueDecl *VD) { + auto &S = *this; + assert(VD); + if (!S.CurrentContractEntry) + return CC_None; + + // Make sure that there's a contract scope interviening between the current + // context and the declaration of the variable. If there isn't, we don't need + // to constify the variable. + if (!isUsageAcrossContract(VD)) + return CC_None; + + // if the unqualified-id appears in the predicate of a contract assertion + // ([basic.contract]) and the entity is + // ... + + // — the result object of (possibly deduced, see [dcl.spec.auto]) type T of a + // function + // call and the unqualified-id is the result name ([dcl.contract.res]) in a + // postcondition assertion, + if (isa(VD)) + return CC_ApplyConst; + + // — a structured binding of type T whose corresponding variable has automatic + // storage + // duration, or + if (auto *Bound = dyn_cast(VD)) { + if (!Bound->getHoldingVar()) + return CC_None; + auto Var = Bound->getHoldingVar(); + if (Var->isLocalVarDeclOrParm() && + (Var->getStorageDuration() == SD_Automatic || + Var->getKind() == Decl::ParmVar)) + return CC_ApplyConst; + return CC_None; + } + + // — a variable with automatic storage duration ... + if (auto Var = dyn_cast(VD); + Var && Var->isLocalVarDeclOrParm() && + (Var->getStorageDuration() == SD_Automatic || + Var->getKind() == Decl::ParmVar)) { + // ... of object type T, or + if (Var->getType()->isObjectType()) + return CC_ApplyConst; + + // of type 'reference to T' + if (Var->getType()->isReferenceType() && + Var->getType().getNonReferenceType()->isObjectType()) + return CC_ApplyConst; + } + + return CC_None; +} + +// Lifted from +static const DeclContext* walkUpDeclContextToFunction(const DeclContext *DC, bool AllowLambda = false) { + while (true) { + if (isa(DC) || isa(DC) || isa(DC) || + isa(DC)) { + DC = DC->getParent(); + } else if (!AllowLambda && isa(DC) && + cast(DC)->getOverloadedOperator() == OO_Call && + cast(DC->getParent())->isLambda()) { + DC = DC->getParent()->getParent(); + } else break; + } + assert(DC); + return DC; +} + +QualType Sema::adjustCXXThisTypeForContracts(QualType QT) { + if (!CurrentContractEntry) + return QT; + + // 'this' is constified any time the `this` object that is captured by a lambda which exists fully + // within a contract. + // + // We need to ensure that we haven't entered a nested member function context, because in that case we + // don't want to constify the `this` object. + // For example: + // ``` + // struct A { + // void f() { + // [&]() { + // contract_assert([&] { + // struct B { int x; void f() { ++x; } }; + // return true; + // }()); + // }(); + // }}; + // ``` + const DeclContext *ContractContext = walkUpDeclContextToFunction(CurrentContractEntry->ContextAtPush); + const DeclContext *QTContext = walkUpDeclContextToFunction(CurContext ); + if (!ContractContext->Equals(QTContext)) + return QT; + + return Context.getPointerType(QT->getPointeeType().withConst()); +} \ No newline at end of file diff --git a/clang/lib/Sema/SemaCoroutine.cpp b/clang/lib/Sema/SemaCoroutine.cpp index 89a0beadc61f3..4e9dd70a45305 100644 --- a/clang/lib/Sema/SemaCoroutine.cpp +++ b/clang/lib/Sema/SemaCoroutine.cpp @@ -801,6 +801,14 @@ static bool checkSuspensionContext(Sema &S, SourceLocation Loc, return false; } + // P2900: + // An await-expression shall not appear in the predicate of a contract + // assertion ([basic.contract]). + if (S.isContractAssertionContext()) { + S.Diag(Loc, diag::err_keyword_not_allowed_in_contract) << Keyword; + return false; + } + return true; } diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index add28b370bcfc..650846eb2c342 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -2180,6 +2180,7 @@ void Sema::ActOnPopScope(SourceLocation Loc, Scope *S) { S->applyNRVO(); if (S->decl_empty()) return; + assert((S->getFlags() & (Scope::DeclScope | Scope::TemplateParamScope)) && "Scope shouldn't contain decls!"); @@ -3803,7 +3804,7 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, NamedDecl *&OldD, Scope *S, const auto OldFX = Old->getFunctionEffects(); const auto NewFX = New->getFunctionEffects(); if (OldFX != NewFX) { - const auto Diffs = FunctionEffectDifferences(OldFX, NewFX); + const auto Diffs = FunctionEffectDiffVector(OldFX, NewFX); for (const auto &Diff : Diffs) { if (Diff.shouldDiagnoseRedeclaration(*Old, OldFX, *New, NewFX)) { Diag(New->getLocation(), @@ -3839,6 +3840,11 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, NamedDecl *&OldD, Scope *S, OldQTypeForComparison = Context.getFunctionType( OldFPT->getReturnType(), OldFPT->getParamTypes(), EPI); } + if (OldFX.empty()) { + // A redeclaration may add the attribute to a previously seen function + // body which needs to be verified. + maybeAddDeclWithEffects(Old, MergedFX); + } } } } @@ -3982,6 +3988,9 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, NamedDecl *&OldD, Scope *S, if (CheckEquivalentExceptionSpec(Old, New)) return true; + if (CheckEquivalentContractSequence(Old, New)) + return true; + // C++11 [dcl.attr.noreturn]p1: // The first declaration of a function shall specify the noreturn // attribute if any declaration of that function specifies the noreturn @@ -4505,10 +4514,10 @@ void Sema::MergeVarDecl(VarDecl *New, LookupResult &Previous) { adjustDeclContextForDeclaratorDecl(New, Old); // Ensure the template parameters are compatible. - if (NewTemplate && - !TemplateParameterListsAreEqual(NewTemplate->getTemplateParameters(), - OldTemplate->getTemplateParameters(), - /*Complain=*/true, TPL_TemplateMatch)) + if (NewTemplate && !TemplateParameterListsAreEqual( + NewTemplate, NewTemplate->getTemplateParameters(), + OldTemplate, OldTemplate->getTemplateParameters(), + /*Complain=*/true, TPL_TemplateMatch)) return New->setInvalidDecl(); // C++ [class.mem]p1: @@ -7658,7 +7667,7 @@ NamedDecl *Sema::ActOnVariableDeclarator( : SourceLocation(); DeclResult Res = ActOnVarTemplateSpecialization( S, D, TInfo, Previous, TemplateKWLoc, TemplateParams, SC, - IsPartialSpecialization); + IsPartialSpecialization, IsMemberSpecialization); if (Res.isInvalid()) return nullptr; NewVD = cast(Res.get()); @@ -7677,6 +7686,10 @@ NamedDecl *Sema::ActOnVariableDeclarator( VarTemplateDecl::Create(Context, DC, D.getIdentifierLoc(), Name, TemplateParams, NewVD); NewVD->setDescribedVarTemplate(NewTemplate); + // If we are providing an explicit specialization of a static variable + // template, make a note of that. + if (IsMemberSpecialization) + NewTemplate->setMemberSpecialization(); } // If this decl has an auto type in need of deduction, make a note of the @@ -7952,7 +7965,9 @@ NamedDecl *Sema::ActOnVariableDeclarator( } if (!R->isIntegralType(Context) && !R->isPointerType()) { - Diag(D.getBeginLoc(), diag::err_asm_bad_register_type); + Diag(TInfo->getTypeLoc().getBeginLoc(), + diag::err_asm_unsupported_register_type) + << TInfo->getTypeLoc().getSourceRange(); NewVD->setInvalidDecl(true); } } @@ -8052,12 +8067,6 @@ NamedDecl *Sema::ActOnVariableDeclarator( ? TPC_ClassTemplateMember : TPC_VarTemplate)) NewVD->setInvalidDecl(); - - // If we are providing an explicit specialization of a static variable - // template, make a note of that. - if (PrevVarTemplate && - PrevVarTemplate->getInstantiatedFromMemberTemplate()) - PrevVarTemplate->setMemberSpecialization(); } } @@ -9222,7 +9231,7 @@ static FunctionDecl *CreateNewFunctionDecl(Sema &SemaRef, Declarator &D, SemaRef.Context, DC, D.getBeginLoc(), NameInfo, R, TInfo, SC, SemaRef.getCurFPFeatures().isFPConstrained(), isInline, HasPrototype, ConstexprSpecKind::Unspecified, - /*TrailingRequiresClause=*/nullptr); + /*TrailingRequiresClause=*/nullptr, /*Contracts=*/{}); if (D.isInvalidType()) NewFD->setInvalidDecl(); @@ -9231,6 +9240,7 @@ static FunctionDecl *CreateNewFunctionDecl(Sema &SemaRef, Declarator &D, ExplicitSpecifier ExplicitSpecifier = D.getDeclSpec().getExplicitSpecifier(); Expr *TrailingRequiresClause = D.getTrailingRequiresClause(); + ContractSpecifierDecl *Contracts = D.getContracts(); SemaRef.CheckExplicitObjectMemberFunction(DC, D, Name, R); @@ -9244,7 +9254,7 @@ static FunctionDecl *CreateNewFunctionDecl(Sema &SemaRef, Declarator &D, SemaRef.Context, cast(DC), D.getBeginLoc(), NameInfo, R, TInfo, ExplicitSpecifier, SemaRef.getCurFPFeatures().isFPConstrained(), isInline, /*isImplicitlyDeclared=*/false, ConstexprKind, - InheritedConstructor(), TrailingRequiresClause); + InheritedConstructor(), TrailingRequiresClause, Contracts); } else if (Name.getNameKind() == DeclarationName::CXXDestructorName) { // This is a C++ destructor declaration. @@ -9279,7 +9289,8 @@ static FunctionDecl *CreateNewFunctionDecl(Sema &SemaRef, Declarator &D, return FunctionDecl::Create( SemaRef.Context, DC, D.getBeginLoc(), D.getIdentifierLoc(), Name, R, TInfo, SC, SemaRef.getCurFPFeatures().isFPConstrained(), isInline, - /*hasPrototype=*/true, ConstexprKind, TrailingRequiresClause); + /*hasPrototype=*/true, ConstexprKind, TrailingRequiresClause, + Contracts); } } else if (Name.getNameKind() == DeclarationName::CXXConversionFunctionName) { @@ -9298,7 +9309,7 @@ static FunctionDecl *CreateNewFunctionDecl(Sema &SemaRef, Declarator &D, SemaRef.Context, cast(DC), D.getBeginLoc(), NameInfo, R, TInfo, SemaRef.getCurFPFeatures().isFPConstrained(), isInline, ExplicitSpecifier, ConstexprKind, SourceLocation(), - TrailingRequiresClause); + TrailingRequiresClause, Contracts); } else if (Name.getNameKind() == DeclarationName::CXXDeductionGuideName) { if (SemaRef.CheckDeductionGuideDeclarator(D, R, SC)) @@ -9324,7 +9335,7 @@ static FunctionDecl *CreateNewFunctionDecl(Sema &SemaRef, Declarator &D, CXXMethodDecl *Ret = CXXMethodDecl::Create( SemaRef.Context, cast(DC), D.getBeginLoc(), NameInfo, R, TInfo, SC, SemaRef.getCurFPFeatures().isFPConstrained(), isInline, - ConstexprKind, SourceLocation(), TrailingRequiresClause); + ConstexprKind, SourceLocation(), TrailingRequiresClause, Contracts); IsVirtualOkay = !Ret->isStatic(); return Ret; } else { @@ -9336,10 +9347,11 @@ static FunctionDecl *CreateNewFunctionDecl(Sema &SemaRef, Declarator &D, // Determine whether the function was written with a // prototype. This true when: // - we're in C++ (where every function has a prototype), - return FunctionDecl::Create( - SemaRef.Context, DC, D.getBeginLoc(), NameInfo, R, TInfo, SC, - SemaRef.getCurFPFeatures().isFPConstrained(), isInline, - true /*HasPrototype*/, ConstexprKind, TrailingRequiresClause); + return FunctionDecl::Create(SemaRef.Context, DC, D.getBeginLoc(), NameInfo, + R, TInfo, SC, + SemaRef.getCurFPFeatures().isFPConstrained(), + isInline, true /*HasPrototype*/, ConstexprKind, + TrailingRequiresClause, Contracts); } } @@ -9864,6 +9876,8 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, NewFD); FunctionTemplate->setLexicalDeclContext(CurContext); NewFD->setDescribedFunctionTemplate(FunctionTemplate); + if (isMemberSpecialization) + FunctionTemplate->setMemberSpecialization(); // For source fidelity, store the other template param lists. if (TemplateParamLists.size() > 1) { @@ -10324,7 +10338,8 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, // Handle attributes. ProcessDeclAttributes(S, NewFD, D); const auto *NewTVA = NewFD->getAttr(); - if (NewTVA && !NewTVA->isDefaultVersion() && + if (Context.getTargetInfo().getTriple().isAArch64() && NewTVA && + !NewTVA->isDefaultVersion() && !Context.getTargetInfo().hasFeature("fmv")) { // Don't add to scope fmv functions declarations if fmv disabled AddToScope = false; @@ -11033,7 +11048,16 @@ static bool CheckMultiVersionValue(Sema &S, const FunctionDecl *FD) { if (TVA) { llvm::SmallVector Feats; - TVA->getFeatures(Feats); + ParsedTargetAttr ParseInfo; + if (S.getASTContext().getTargetInfo().getTriple().isRISCV()) { + ParseInfo = + S.getASTContext().getTargetInfo().parseTargetAttr(TVA->getName()); + for (auto &Feat : ParseInfo.Features) + Feats.push_back(StringRef{Feat}.substr(1)); + } else { + assert(S.getASTContext().getTargetInfo().getTriple().isAArch64()); + TVA->getFeatures(Feats); + } for (const auto &Feat : Feats) { if (!TargetInfo.validateCpuSupports(Feat)) { S.Diag(FD->getLocation(), diag::err_bad_multiversion_option) @@ -11319,7 +11343,8 @@ static bool PreviousDeclsHaveMultiVersionAttribute(const FunctionDecl *FD) { } static void patchDefaultTargetVersion(FunctionDecl *From, FunctionDecl *To) { - if (!From->getASTContext().getTargetInfo().getTriple().isAArch64()) + if (!From->getASTContext().getTargetInfo().getTriple().isAArch64() && + !From->getASTContext().getTargetInfo().getTriple().isRISCV()) return; MultiVersionKind MVKindFrom = From->getMultiVersionKind(); @@ -12010,10 +12035,7 @@ bool Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD, // If this is an explicit specialization of a member that is a function // template, mark it as a member specialization. - if (IsMemberSpecialization && - NewTemplateDecl->getInstantiatedFromMemberTemplate()) { - NewTemplateDecl->setMemberSpecialization(); - assert(OldTemplateDecl->isMemberSpecialization()); + if (IsMemberSpecialization) { // Explicit specializations of a member template do not inherit deleted // status from the parent member template that they are specializing. if (OldFD->isDeleted()) { @@ -12193,6 +12215,8 @@ bool Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD, CUDA().checkTargetOverload(NewFD, Previous); } + ActOnContractsOnFinishFunctionDecl(NewFD, DeclIsDefn); + // Check if the function definition uses any AArch64 SME features without // having the '+sme' feature enabled and warn user if sme locally streaming // function returns or uses arguments with VL-based types. @@ -14388,6 +14412,27 @@ void Sema::CheckCompleteVariableDeclaration(VarDecl *var) { // do this lazily, because the result might depend on things that change // later, such as which constexpr functions happen to be defined. SmallVector Notes; + + // Evaluate the initializer to see if it's a constant initializer. + // + // For variables that can fallback to dynamic initialization + // C++ contracts require us to do this in two phases: + // + // (1) evaluate the initalizer with all contracts disabled. If it succeeds + // the variable is 'constant initialized'. Otherwise, perform dynamic + // initialization. + // + // (2) On success, reevaluate the initializer with contracts having + // their user-specified + // semantics. If it fails, we need to diagnose the failure instead + // of falling back to dynamic initializer. + // + // FIXME(EricWF): Technically we're required to do this check for constexpr + // variables too, since the initializer may be non-constant only when + // contracts are enabled. + bool ConstantInitializerIsRequired = + var->isConstexpr() || (GlobalStorage && var->hasAttr()); + if (!getLangOpts().CPlusPlus11 && !getLangOpts().C23) { // Prior to C++11, in contexts where a constant initializer is required, // the set of valid constant initializers is described by syntactic rules @@ -14407,8 +14452,42 @@ void Sema::CheckCompleteVariableDeclaration(VarDecl *var) { Notes.back().second << CacheCulprit->getSourceRange(); } } else { - // Evaluate the initializer to see if it's a constant initializer. - HasConstInit = var->checkForConstantInitialization(Notes); + // p2900 [expr.const]p2 + // A variable or temporary object o is constant-initialized if + // ... + // its initialization is a constant expression when interpreted + // ... with all contract assertions having the ignore evaluation + // semantic. + + HasConstInit = var->checkForConstantInitialization( + Notes, + /*EnableContracts=*/ConstantInitializerIsRequired); + } + + // p2900 [intro.compliance]p2 + // If a program contains ... + // - a contract assertion ([basic.contract.eval]) evaluated with a + // checking semantic + // in a manifestly constant-evaluated context resulting in a contract + // violation + // ... shall issue a diagnostic. + if (HasConstInit && getLangOpts().Contracts && + !ConstantInitializerIsRequired) { + if (!var->recheckForConstantInitialization(Notes, + /*EnableContracts=*/true)) { + // If we have a contract failure, we need to diagnose it. + // We need to clear the notes, as we will re-diagnose the contract + // failure. + SourceLocation DiagLoc = var->getLocation(); + // FIXME(EricWF): This diagnostic is bad. What do we say here? Normally + // this error would have been eaten. + Diag(DiagLoc, + diag::err_initialization_of_constant_initialized_variable_failed) + << var; + Diag(DiagLoc, diag::note_initialization_changed_contract_semantic); + for (unsigned I = 0, N = Notes.size(); I != N; ++I) + Diag(Notes[I].first, Notes[I].second); + } } if (HasConstInit) { @@ -15147,8 +15226,8 @@ ParmVarDecl *Sema::CheckParameter(DeclContext *DC, SourceLocation StartLoc, // we know that references to that pack must also be expanded within the // lambda scope. if (New->isParameterPack()) - if (auto *LSI = getEnclosingLambda()) - LSI->LocalPacks.push_back(New); + if (auto *CSI = getEnclosingLambdaOrBlock()) + CSI->LocalPacks.push_back(New); if (New->getType().hasNonTrivialToPrimitiveDestructCUnion() || New->getType().hasNonTrivialToPrimitiveCopyCUnion()) @@ -15436,6 +15515,8 @@ LambdaScopeInfo *Sema::RebuildLambdaScopeInfo(CXXMethodDecl *CallOperator) { if (VD->isInitCapture()) CurrentInstantiationScope->InstantiatedLocal(VD, VD); const bool ByRef = C.getCaptureKind() == LCK_ByRef; + if (ByRef) { + } LSI->addCapture(VD, /*IsBlock*/false, ByRef, /*RefersToEnclosingVariableOrCapture*/true, C.getLocation(), /*EllipsisLoc*/C.isPackExpansion() @@ -15506,7 +15587,8 @@ Decl *Sema::ActOnStartOfFunctionDef(Scope *FnBodyScope, Decl *D, FD->setInvalidDecl(); } if (const auto *Attr = FD->getAttr()) { - if (!Context.getTargetInfo().hasFeature("fmv") && + if (Context.getTargetInfo().getTriple().isAArch64() && + !Context.getTargetInfo().hasFeature("fmv") && !Attr->isDefaultVersion()) { // If function multi versioning disabled skip parsing function body // defined with non-default target_version attribute @@ -15677,6 +15759,11 @@ Decl *Sema::ActOnStartOfFunctionDef(Scope *FnBodyScope, Decl *D, getCurLexicalContext()->getDeclKind() != Decl::ObjCImplementation) Diag(FD->getLocation(), diag::warn_function_def_in_objc_container); + // FIXME(EricWF): I can remove this, right? + // ActOnContractsOnStartOfFunctionDef() + + maybeAddDeclWithEffects(FD); + return D; } @@ -15908,6 +15995,8 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body, } } + ActOnContractsOnFinishFunctionBody(FD); + // If the function implicitly returns zero (like 'main') or is naked, // don't complain about missing return statements. if (FD->hasImplicitReturnZero() || FD->hasAttr()) @@ -17072,8 +17161,8 @@ Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, SourceLocation KWLoc, DeclResult Result = CheckClassTemplate( S, TagSpec, TUK, KWLoc, SS, Name, NameLoc, Attrs, TemplateParams, AS, ModulePrivateLoc, - /*FriendLoc*/ SourceLocation(), TemplateParameterLists.size() - 1, - TemplateParameterLists.data(), SkipBody); + /*FriendLoc*/ SourceLocation(), TemplateParameterLists.drop_back(), + isMemberSpecialization, SkipBody); return Result.get(); } else { // The "template<>" header is extraneous. @@ -20303,59 +20392,3 @@ bool Sema::shouldIgnoreInHostDeviceCheck(FunctionDecl *Callee) { return LangOpts.CUDA && !LangOpts.CUDAIsDevice && CUDA().IdentifyTarget(Callee) == CUDAFunctionTarget::Global; } - -void Sema::diagnoseFunctionEffectMergeConflicts( - const FunctionEffectSet::Conflicts &Errs, SourceLocation NewLoc, - SourceLocation OldLoc) { - for (const FunctionEffectSet::Conflict &Conflict : Errs) { - Diag(NewLoc, diag::warn_conflicting_func_effects) - << Conflict.Kept.description() << Conflict.Rejected.description(); - Diag(OldLoc, diag::note_previous_declaration); - } -} - -bool Sema::diagnoseConflictingFunctionEffect( - const FunctionEffectsRef &FX, const FunctionEffectWithCondition &NewEC, - SourceLocation NewAttrLoc) { - // If the new effect has a condition, we can't detect conflicts until the - // condition is resolved. - if (NewEC.Cond.getCondition() != nullptr) - return false; - - // Diagnose the new attribute as incompatible with a previous one. - auto Incompatible = [&](const FunctionEffectWithCondition &PrevEC) { - Diag(NewAttrLoc, diag::err_attributes_are_not_compatible) - << ("'" + NewEC.description() + "'") - << ("'" + PrevEC.description() + "'") << false; - // We don't necessarily have the location of the previous attribute, - // so no note. - return true; - }; - - // Compare against previous attributes. - FunctionEffect::Kind NewKind = NewEC.Effect.kind(); - - for (const FunctionEffectWithCondition &PrevEC : FX) { - // Again, can't check yet when the effect is conditional. - if (PrevEC.Cond.getCondition() != nullptr) - continue; - - FunctionEffect::Kind PrevKind = PrevEC.Effect.kind(); - // Note that we allow PrevKind == NewKind; it's redundant and ignored. - - if (PrevEC.Effect.oppositeKind() == NewKind) - return Incompatible(PrevEC); - - // A new allocating is incompatible with a previous nonblocking. - if (PrevKind == FunctionEffect::Kind::NonBlocking && - NewKind == FunctionEffect::Kind::Allocating) - return Incompatible(PrevEC); - - // A new nonblocking is incompatible with a previous allocating. - if (PrevKind == FunctionEffect::Kind::Allocating && - NewKind == FunctionEffect::Kind::NonBlocking) - return Incompatible(PrevEC); - } - - return false; -} diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index c9b9f3a0007da..cba6f115e90e1 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -1216,6 +1216,8 @@ static void handlePreferredName(Sema &S, Decl *D, const ParsedAttr &AL) { } bool Sema::isValidPointerAttrType(QualType T, bool RefOkay) { + if (T->isDependentType()) + return true; if (RefOkay) { if (T->isReferenceType()) return true; @@ -1284,7 +1286,7 @@ static void handleNonNullAttr(Sema &S, Decl *D, const ParsedAttr &AL) { for (unsigned I = 0, E = getFunctionOrMethodNumParams(D); I != E && !AnyPointers; ++I) { QualType T = getFunctionOrMethodParamType(D, I); - if (T->isDependentType() || S.isValidPointerAttrType(T)) + if (S.isValidPointerAttrType(T)) AnyPointers = true; } @@ -1409,8 +1411,7 @@ void Sema::AddAllocAlignAttr(Decl *D, const AttributeCommonInfo &CI, AllocAlignAttr TmpAttr(Context, CI, ParamIdx()); SourceLocation AttrLoc = CI.getLoc(); - if (!ResultType->isDependentType() && - !isValidPointerAttrType(ResultType, /* RefOkay */ true)) { + if (!isValidPointerAttrType(ResultType, /* RefOkay */ true)) { Diag(AttrLoc, diag::warn_attribute_return_pointers_refs_only) << &TmpAttr << CI.getRange() << getFunctionOrMethodResultSourceRange(D); return; @@ -1744,7 +1745,8 @@ static void handleCPUSpecificAttr(Sema &S, Decl *D, const ParsedAttr &AL) { return; if (const auto *Other = D->getAttr()) { - S.Diag(AL.getLoc(), diag::err_disallowed_duplicate_attribute) << AL; + S.Diag(AL.getLoc(), diag::err_disallowed_duplicate_attribute) + << AL << /*declaration*/ 0; S.Diag(Other->getLocation(), diag::note_conflicting_attribute); return; } @@ -1753,7 +1755,8 @@ static void handleCPUSpecificAttr(Sema &S, Decl *D, const ParsedAttr &AL) { return; if (const auto *Other = D->getAttr()) { - S.Diag(AL.getLoc(), diag::err_disallowed_duplicate_attribute) << AL; + S.Diag(AL.getLoc(), diag::err_disallowed_duplicate_attribute) + << AL << /*declaration*/ 0; S.Diag(Other->getLocation(), diag::note_conflicting_attribute); return; } @@ -3040,6 +3043,54 @@ bool Sema::checkTargetVersionAttr(SourceLocation LiteralLoc, Decl *D, enum SecondParam { None }; enum ThirdParam { Target, TargetClones, TargetVersion }; llvm::SmallVector Features; + if (Context.getTargetInfo().getTriple().isRISCV()) { + llvm::SmallVector AttrStrs; + AttrStr.split(AttrStrs, ';'); + + bool HasArch = false; + bool HasPriority = false; + bool HasDefault = false; + bool DuplicateAttr = false; + for (auto &AttrStr : AttrStrs) { + // Only support arch=+ext,... syntax. + if (AttrStr.starts_with("arch=+")) { + if (HasArch) + DuplicateAttr = true; + HasArch = true; + ParsedTargetAttr TargetAttr = + Context.getTargetInfo().parseTargetAttr(AttrStr); + + if (TargetAttr.Features.empty() || + llvm::any_of(TargetAttr.Features, [&](const StringRef Ext) { + return !RISCV().isValidFMVExtension(Ext); + })) + return Diag(LiteralLoc, diag::warn_unsupported_target_attribute) + << Unsupported << None << AttrStr << TargetVersion; + } else if (AttrStr.starts_with("default")) { + if (HasDefault) + DuplicateAttr = true; + HasDefault = true; + } else if (AttrStr.consume_front("priority=")) { + if (HasPriority) + DuplicateAttr = true; + HasPriority = true; + int Digit; + if (AttrStr.getAsInteger(0, Digit)) + return Diag(LiteralLoc, diag::warn_unsupported_target_attribute) + << Unsupported << None << AttrStr << TargetVersion; + } else { + return Diag(LiteralLoc, diag::warn_unsupported_target_attribute) + << Unsupported << None << AttrStr << TargetVersion; + } + } + + if (((HasPriority || HasArch) && HasDefault) || DuplicateAttr || + (HasPriority && !HasArch)) + return Diag(LiteralLoc, diag::warn_unsupported_target_attribute) + << Unsupported << None << AttrStr << TargetVersion; + + return false; + } AttrStr.split(Features, "+"); for (auto &CurFeature : Features) { CurFeature = CurFeature.trim(); @@ -3228,7 +3279,7 @@ static void handleTargetClonesAttr(Sema &S, Decl *D, const ParsedAttr &AL) { // Ensure we don't combine these with themselves, since that causes some // confusing behavior. if (const auto *Other = D->getAttr()) { - S.Diag(AL.getLoc(), diag::err_disallowed_duplicate_attribute) << AL; + S.Diag(AL.getLoc(), diag::err_disallowed_duplicate_attribute) << AL << 0; S.Diag(Other->getLocation(), diag::note_conflicting_attribute); return; } @@ -3909,30 +3960,11 @@ static void handleTransparentUnionAttr(Sema &S, Decl *D, const ParsedAttr &AL) { RD->addAttr(::new (S.Context) TransparentUnionAttr(S.Context, AL)); } -void Sema::AddAnnotationAttr(Decl *D, const AttributeCommonInfo &CI, - StringRef Str, MutableArrayRef Args) { - auto *Attr = AnnotateAttr::Create(Context, Str, Args.data(), Args.size(), CI); - if (ConstantFoldAttrArgs( - CI, MutableArrayRef(Attr->args_begin(), Attr->args_end()))) { - D->addAttr(Attr); - } -} - static void handleAnnotateAttr(Sema &S, Decl *D, const ParsedAttr &AL) { - // Make sure that there is a string literal as the annotation's first - // argument. - StringRef Str; - if (!S.checkStringLiteralArgumentAttr(AL, 0, Str)) - return; - - llvm::SmallVector Args; - Args.reserve(AL.getNumArgs() - 1); - for (unsigned Idx = 1; Idx < AL.getNumArgs(); Idx++) { - assert(!AL.isArgIdent(Idx)); - Args.push_back(AL.getArgAsExpr(Idx)); + auto *Attr = S.CreateAnnotationAttr(AL); + if (Attr) { + D->addAttr(Attr); } - - S.AddAnnotationAttr(D, AL, Str, Args); } static void handleAlignValueAttr(Sema &S, Decl *D, const ParsedAttr &AL) { diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index d8cdfcf8c6ec0..cb8326635ae2a 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -711,6 +711,9 @@ bool Sema::MergeCXXFunctionDecl(FunctionDecl *New, FunctionDecl *Old, Old->isDefined(OldDefinition, true)) CheckForFunctionRedefinition(New, OldDefinition); + if (CheckEquivalentContractSequence(Old, New)) + Invalid = true; + return Invalid; } @@ -2136,6 +2139,13 @@ CheckConstexprFunctionStmt(Sema &SemaRef, const FunctionDecl *Dcl, Stmt *S, return true; } + case Stmt::ContractStmtClass: { + if (!Cxx1yLoc.isValid()) + Cxx1yLoc = S->getBeginLoc(); + + return true; + } + case Stmt::IfStmtClass: { // C++1y allows if-statements. if (!Cxx1yLoc.isValid()) @@ -10730,6 +10740,10 @@ void Sema::ActOnFinishDelayedCXXMethodDeclaration(Scope *S, Decl *MethodD) { // Check the default arguments, which we may have added. if (!Method->isInvalidDecl()) CheckCXXDefaultArguments(Method); + + if (!Method->isInvalidDecl() && Method->hasContracts()) + ActOnContractsOnFinishFunctionDecl(Method, + Method->isThisDeclarationADefinition()); } // Emit the given diagnostic for each non-address-space qualifier. @@ -17416,8 +17430,8 @@ DeclResult Sema::ActOnTemplatedFriendTag( return CheckClassTemplate(S, TagSpec, TagUseKind::Friend, TagLoc, SS, Name, NameLoc, Attr, TemplateParams, AS_public, /*ModulePrivateLoc=*/SourceLocation(), - FriendLoc, TempParamLists.size() - 1, - TempParamLists.data()) + FriendLoc, TempParamLists.drop_back(), + IsMemberSpecialization) .get(); } else { // The "template<>" header is extraneous. @@ -18181,7 +18195,7 @@ bool Sema::CheckOverridingFunctionAttributes(CXXMethodDecl *New, if (OldFX != NewFXOrig) { FunctionEffectSet NewFX(NewFXOrig); - const auto Diffs = FunctionEffectDifferences(OldFX, NewFX); + const auto Diffs = FunctionEffectDiffVector(OldFX, NewFX); FunctionEffectSet::Conflicts Errs; for (const auto &Diff : Diffs) { switch (Diff.shouldDiagnoseMethodOverride(*Old, OldFX, *New, NewFX)) { @@ -18194,7 +18208,7 @@ bool Sema::CheckOverridingFunctionAttributes(CXXMethodDecl *New, << Old->getReturnTypeSourceRange(); break; case FunctionEffectDiff::OverrideResult::Merge: { - NewFX.insert(Diff.Old, Errs); + NewFX.insert(Diff.Old.value(), Errs); const auto *NewFT = New->getType()->castAs(); FunctionProtoType::ExtProtoInfo EPI = NewFT->getExtProtoInfo(); EPI.FunctionEffects = FunctionEffectsRef(NewFX); diff --git a/clang/lib/Sema/SemaExceptionSpec.cpp b/clang/lib/Sema/SemaExceptionSpec.cpp index 8aedbfcf878a1..3535008f42b29 100644 --- a/clang/lib/Sema/SemaExceptionSpec.cpp +++ b/clang/lib/Sema/SemaExceptionSpec.cpp @@ -1412,6 +1412,7 @@ CanThrowResult Sema::canThrow(const Stmt *S) { case Stmt::CaseStmtClass: case Stmt::CompoundStmtClass: case Stmt::ContinueStmtClass: + case Stmt::ContractStmtClass: case Stmt::CoreturnStmtClass: case Stmt::CoroutineBodyStmtClass: case Stmt::CXXCatchStmtClass: @@ -1593,6 +1594,8 @@ CanThrowResult Sema::canThrow(const Stmt *S) { case Stmt::SYCLUniqueStableNameExprClass: return CT_Cannot; + case Stmt::OpenACCAsteriskSizeExprClass: + return CT_Cannot; case Stmt::NoStmtClass: llvm_unreachable("Invalid class for statement"); } diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 2db9d1fc69ed1..3a5436a4052c1 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -18,6 +18,7 @@ #include "clang/AST/ASTLambda.h" #include "clang/AST/ASTMutationListener.h" #include "clang/AST/CXXInheritance.h" +#include "clang/AST/Decl.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/EvaluatedExprVisitor.h" @@ -33,6 +34,7 @@ #include "clang/AST/TypeLoc.h" #include "clang/Basic/Builtins.h" #include "clang/Basic/DiagnosticSema.h" +#include "clang/Basic/EricWFDebug.h" #include "clang/Basic/PartialDiagnostic.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/Specifiers.h" @@ -65,6 +67,7 @@ #include "llvm/Support/Casting.h" #include "llvm/Support/ConvertUTF.h" #include "llvm/Support/SaveAndRestore.h" +#include "llvm/Support/TimeProfiler.h" #include "llvm/Support/TypeSize.h" #include @@ -2468,7 +2471,15 @@ bool Sema::DiagnoseEmptyLookup(Scope *S, CXXScopeSpec &SS, LookupResult &R, LookupCtx ? LookupCtx : (SS.isEmpty() ? CurContext : nullptr); while (DC) { if (isa(DC)) { - LookupQualifiedName(R, DC); + if (ExplicitTemplateArgs) { + if (LookupTemplateName( + R, S, SS, Context.getRecordType(cast(DC)), + /*EnteringContext*/ false, TemplateNameIsRequired, + /*RequiredTemplateKind*/ nullptr, /*AllowTypoCorrection*/ true)) + return true; + } else { + LookupQualifiedName(R, DC); + } if (!R.empty()) { // Don't give errors about ambiguities in this lookup. @@ -3210,6 +3221,7 @@ static void diagnoseUncapturableValueReferenceOrBinding(Sema &S, SourceLocation loc, ValueDecl *var); +/// Complete semantic analysis for a reference to the given declaration. ExprResult Sema::BuildDeclarationNameExpr( const CXXScopeSpec &SS, const DeclarationNameInfo &NameInfo, NamedDecl *D, NamedDecl *FoundD, const TemplateArgumentListInfo *TemplateArgs, @@ -3271,6 +3283,8 @@ ExprResult Sema::BuildDeclarationNameExpr( // is expanded by some outer '...' in the context of the use. type = type.getNonPackExpansionType(); + bool TypeWasSetByLambdaCapture = false; + switch (D->getKind()) { // Ignore all the non-ValueDecl kinds. #define ABSTRACT_DECL(kind) @@ -3348,6 +3362,7 @@ ExprResult Sema::BuildDeclarationNameExpr( } [[fallthrough]]; + case Decl::ImplicitParam: case Decl::ParmVar: { // These are always l-values. @@ -3359,13 +3374,20 @@ ExprResult Sema::BuildDeclarationNameExpr( // captured in an unevaluated context, it seems that the answer is no. if (!isUnevaluatedContext()) { QualType CapturedType = getCapturedDeclRefType(cast(VD), Loc); - if (!CapturedType.isNull()) + if (!CapturedType.isNull()) { type = CapturedType; + TypeWasSetByLambdaCapture = true; + } } break; } + case Decl::ResultName: // FIXME(EricWF): Is this even close to correct? + valueKind = VK_LValue; + type = type.getNonReferenceType(); + break; + case Decl::Binding: // These are always lvalues. valueKind = VK_LValue; @@ -3452,9 +3474,24 @@ ExprResult Sema::BuildDeclarationNameExpr( break; } + const ContractConstification Constification = getContractConstification(VD); + bool ApplyConstification = !TypeWasSetByLambdaCapture && + Constification == CC_ApplyConst && + !type.isConstQualified(); + + if (ApplyConstification) + type = type.withConst(); + auto *E = BuildDeclRefExpr(VD, type, valueKind, NameInfo, &SS, FoundD, /*FIXME: TemplateKWLoc*/ SourceLocation(), TemplateArgs); + + if (ApplyConstification) + E->setIsConstified(true); + + if (Constification == CC_ApplyConst) + E->setIsInContractContext(true); + // Clang AST consumers assume a DeclRefExpr refers to a valid decl. We // wrap a DeclRefExpr referring to an invalid decl with a dependent-type // RecoveryExpr to avoid follow-up semantic analysis (thus prevent bogus @@ -5639,6 +5676,8 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) { runWithSufficientStackSpace(Loc, [&] { MarkDeclarationsReferencedInExpr(E, /*SkipLocalVariables=*/false); }); + if (isInLifetimeExtendingContext()) + DiscardCleanupsInEvaluationContext(); // C++11 [class.base.init]p7: // The initialization of each base and member constitutes a // full-expression. @@ -6963,8 +7002,7 @@ ExprResult Sema::BuildResolvedCallExpr(Expr *Fn, NamedDecl *NDecl, } if (CXXMethodDecl *Method = dyn_cast_or_null(FDecl)) - if (!isa(CurContext) && - Method->isImplicitObjectMemberFunction()) + if (Method->isImplicitObjectMemberFunction()) return ExprError(Diag(LParenLoc, diag::err_member_call_without_object) << Fn->getSourceRange() << 0); @@ -13188,7 +13226,7 @@ static bool IsReadonlyMessage(Expr *E, Sema &S) { /// Is the given expression (which must be 'const') a reference to a /// variable which was originally non-const, but which has become /// 'const' due to being captured within a block? -enum NonConstCaptureKind { NCCK_None, NCCK_Block, NCCK_Lambda }; +enum NonConstCaptureKind { NCCK_None, NCCK_Block, NCCK_Lambda, NCCK_Contract }; static NonConstCaptureKind isReferenceToNonConstCapture(Sema &S, Expr *E) { assert(E->isLValue() && E->getType().isConstQualified()); E = E->IgnoreParens(); @@ -13204,6 +13242,9 @@ static NonConstCaptureKind isReferenceToNonConstCapture(Sema &S, Expr *E) { if (var->getType().isConstQualified()) return NCCK_None; assert(var->hasLocalStorage() && "capture added 'const' to non-local?"); + if (DRE && DRE->isInContractContext()) + return NCCK_Contract; + // Decide whether the first capture was for a block or a lambda. DeclContext *DC = S.CurContext, *Prev = nullptr; // Decide whether the first capture was for a block or a lambda. @@ -13243,6 +13284,11 @@ enum { ConstUnknown, // Keep as last element }; +enum { + ConstifiedVariable, + ConstifiedCXXThis, +}; + /// Emit the "read-only variable not assignable" error and print notes to give /// more information about why the variable is not assignable, such as pointing /// to the declaration of a const variable, showing that a method is const, or @@ -13274,7 +13320,6 @@ static void DiagnoseConstAssignment(Sema &S, const Expr *E, assert(DiagnosticEmitted && "Expected diagnostic not emitted."); break; } - if (!IsTypeModifiable(Field->getType(), IsDereference)) { if (!DiagnosticEmitted) { S.Diag(Loc, diag::err_typecheck_assign_const) @@ -13341,6 +13386,10 @@ static void DiagnoseConstAssignment(Sema &S, const Expr *E, } S.Diag(VD->getLocation(), diag::note_typecheck_assign_const) << ConstVariable << VD << VD->getType() << VD->getSourceRange(); + } else if (DRE->isConstified()) { + S.Diag(Loc, diag::err_typecheck_assign_constified) + << ExprRange << /*IsMemberExpr=*/false << VD; + DiagnosticEmitted = true; } } } else if (isa(E)) { @@ -13354,6 +13403,10 @@ static void DiagnoseConstAssignment(Sema &S, const Expr *E, } S.Diag(MD->getLocation(), diag::note_typecheck_assign_const) << ConstMethod << MD << MD->getSourceRange(); + } else if (S.CurrentContractEntry) { + S.Diag(Loc, diag::err_typecheck_assign_constified) + << ExprRange << /*IsMemberExpr*/ true; + DiagnosticEmitted = true; } } } @@ -13364,6 +13417,12 @@ static void DiagnoseConstAssignment(Sema &S, const Expr *E, // Can't determine a more specific message, so display the generic error. S.Diag(Loc, diag::err_typecheck_assign_const) << ExprRange << ConstUnknown; + + // If we're inside a contract statement, note that in case it's helpful. Maybe + // the variable was constified? + if (S.isConstificationContext() && + !S.getCurrentContractKeywordLoc().isInvalid()) + S.Diag(S.getCurrentContractKeywordLoc(), diag::note_contract_context); } enum OriginalExprKind { @@ -13458,6 +13517,8 @@ static bool CheckForModifiableLvalue(Expr *E, SourceLocation Loc, Sema &S) { if (NonConstCaptureKind NCCK = isReferenceToNonConstCapture(S, E)) { if (NCCK == NCCK_Block) DiagID = diag::err_block_decl_ref_not_modifiable_lvalue; + else if (NCCK == NCCK_Contract) + DiagID = diag::err_lambda_decl_ref_not_modifiable_lvalue_contract; else DiagID = diag::err_lambda_decl_ref_not_modifiable_lvalue; break; @@ -14267,7 +14328,7 @@ QualType Sema::CheckAddressOfOperand(ExprResult &OrigOp, SourceLocation OpLoc) { } } } else if (!isa(dcl)) + MSGuidDecl, UnnamedGlobalConstantDecl, ResultNameDecl>(dcl)) llvm_unreachable("Unknown/unexpected decl type"); } @@ -16076,17 +16137,7 @@ void Sema::ActOnBlockArguments(SourceLocation CaretLoc, Declarator &ParamInfo, TypeSourceInfo *Sig = GetTypeForDeclarator(ParamInfo); QualType T = Sig->getType(); - - // FIXME: We should allow unexpanded parameter packs here, but that would, - // in turn, make the block expression contain unexpanded parameter packs. - if (DiagnoseUnexpandedParameterPack(CaretLoc, Sig, UPPC_Block)) { - // Drop the parameters. - FunctionProtoType::ExtProtoInfo EPI; - EPI.HasTrailingReturn = false; - EPI.TypeQuals.addConst(); - T = Context.getFunctionType(Context.DependentTy, std::nullopt, EPI); - Sig = Context.getTrivialTypeSourceInfo(T); - } + DiagnoseUnexpandedParameterPack(CaretLoc, Sig, UPPC_Block); // GetTypeForDeclarator always produces a function type for a block // literal signature. Furthermore, it is always a FunctionProtoType @@ -16212,6 +16263,8 @@ ExprResult Sema::ActOnBlockStmtExpr(SourceLocation CaretLoc, BlockScopeInfo *BSI = cast(FunctionScopes.back()); BlockDecl *BD = BSI->TheDecl; + maybeAddDeclWithEffects(BD); + if (BSI->HasImplicitReturnType) deduceClosureReturnType(*BSI); @@ -16356,7 +16409,8 @@ ExprResult Sema::ActOnBlockStmtExpr(SourceLocation CaretLoc, AnalysisBasedWarnings::Policy WP = AnalysisWarnings.getDefaultPolicy(); PoppedFunctionScopePtr ScopeRAII = PopFunctionScopeInfo(&WP, BD, BlockTy); - BlockExpr *Result = new (Context) BlockExpr(BD, BlockTy); + BlockExpr *Result = new (Context) + BlockExpr(BD, BlockTy, BSI->ContainsUnexpandedParameterPack); // If the block isn't obviously global, i.e. it captures anything at // all, then we need to do a few things in the surrounding context: @@ -16379,6 +16433,8 @@ ExprResult Sema::ActOnBlockStmtExpr(SourceLocation CaretLoc, if (getCurFunction()) getCurFunction()->addBlock(BD); + // This can happen if the block's return type is deduced, but + // the return expression is invalid. if (BD->isInvalidDecl()) return CreateRecoveryExpr(Result->getBeginLoc(), Result->getEndLoc(), {Result}, Result->getType()); @@ -16651,7 +16707,7 @@ ExprResult Sema::ActOnSourceLocExpr(SourceLocIdentKind Kind, case SourceLocIdentKind::Column: ResultTy = Context.UnsignedIntTy; break; - case SourceLocIdentKind::SourceLocStruct: + case SourceLocIdentKind::SourceLocStruct: { if (!StdSourceLocationImplDecl) { StdSourceLocationImplDecl = LookupStdSourceLocationImpl(*this, BuiltinLoc); @@ -16662,6 +16718,7 @@ ExprResult Sema::ActOnSourceLocExpr(SourceLocIdentKind Kind, Context.getRecordType(StdSourceLocationImplDecl).withConst()); break; } + } return BuildSourceLocExpr(Kind, ResultTy, BuiltinLoc, RPLoc, CurContext); } @@ -18143,6 +18200,15 @@ void Sema::MarkFunctionReferenced(SourceLocation Loc, FunctionDecl *Func, Func->setInstantiationIsPending(true); PendingInstantiations.push_back( std::make_pair(Func, PointOfInstantiation)); + if (llvm::isTimeTraceVerbose()) { + llvm::timeTraceAddInstantEvent("DeferInstantiation", [&] { + std::string Name; + llvm::raw_string_ostream OS(Name); + Func->getNameForDiagnostic(OS, getPrintingPolicy(), + /*Qualified=*/true); + return Name; + }); + } // Notify the consumer that a function was implicitly instantiated. Consumer.HandleCXXImplicitFunctionInstantiation(Func); } @@ -18413,7 +18479,6 @@ static DeclContext *getParentOfCapturingContextOrNull(DeclContext *DC, static bool isVariableCapturable(CapturingScopeInfo *CSI, ValueDecl *Var, SourceLocation Loc, const bool Diagnose, Sema &S) { - assert((isa(Var)) && "Only variables and structured bindings can be captured"); @@ -18604,7 +18669,7 @@ static bool captureInLambda(LambdaScopeInfo *LSI, ValueDecl *Var, const bool RefersToCapturedVariable, const Sema::TryCaptureKind Kind, SourceLocation EllipsisLoc, const bool IsTopScope, - Sema &S, bool Invalid) { + Sema &S, bool IsInContract, bool Invalid) { // Determine whether we are capturing by reference or by value. bool ByRef = false; if (IsTopScope && Kind != Sema::TryCapture_Implicit) { @@ -18633,7 +18698,12 @@ static bool captureInLambda(LambdaScopeInfo *LSI, ValueDecl *Var, // to do the former, while EDG does the latter. Core issue 1249 will // clarify, but for now we follow GCC because it's a more permissive and // easily defensible position. - CaptureType = S.Context.getLValueReferenceType(DeclRefType); + QualType DRET = DeclRefType.getNonReferenceType(); + + if (IsInContract) + DRET.addConst(); + + CaptureType = S.Context.getLValueReferenceType(DRET); } else { // C++11 [expr.prim.lambda]p14: // For each entity captured by copy, an unnamed non-static @@ -18646,8 +18716,9 @@ static bool captureInLambda(LambdaScopeInfo *LSI, ValueDecl *Var, // corresponding data member is also a reference to a // function. - end note ] if (const ReferenceType *RefType = CaptureType->getAs()){ - if (!RefType->getPointeeType()->isFunctionType()) + if (!RefType->getPointeeType()->isFunctionType()) { CaptureType = RefType->getPointeeType(); + } } // Forbid the lambda copy-capture of autoreleasing variables. @@ -18678,9 +18749,9 @@ static bool captureInLambda(LambdaScopeInfo *LSI, ValueDecl *Var, } // Compute the type of a reference to this captured variable. - if (ByRef) + if (ByRef) { DeclRefType = CaptureType.getNonReferenceType(); - else { + } else { // C++ [expr.prim.lambda]p5: // The closure type for a lambda-expression has a public inline // function call operator [...]. This function call operator is @@ -18806,6 +18877,7 @@ bool Sema::tryCaptureVariable( ValueDecl *Var, SourceLocation ExprLoc, TryCaptureKind Kind, SourceLocation EllipsisLoc, bool BuildAndDiagnose, QualType &CaptureType, QualType &DeclRefType, const unsigned *const FunctionScopeIndexToStopAt) { + // An init-capture is notionally from the context surrounding its // declaration, but its parent DC is the lambda class. DeclContext *VarDC = Var->getDeclContext(); @@ -18873,6 +18945,7 @@ bool Sema::tryCaptureVariable( bool Nested = false; bool Explicit = (Kind != TryCapture_Implicit); unsigned FunctionScopesIndex = MaxFunctionScopesIndex; + do { LambdaScopeInfo *LSI = nullptr; @@ -19091,7 +19164,10 @@ bool Sema::tryCaptureVariable( Invalid = !captureInLambda(LSI, Var, ExprLoc, BuildAndDiagnose, CaptureType, DeclRefType, Nested, Kind, EllipsisLoc, - /*IsTopScope*/ I == N - 1, *this, Invalid); + /*IsTopScope*/ I == N - 1, *this, + CurrentContractEntry && + CurrentContractEntry->FunctionIndexAtPush >= I, + Invalid); Nested = true; } @@ -19486,7 +19562,7 @@ static ExprResult rebuildPotentialResultsAsNonOdrUsed(Sema &S, Expr *E, ExprResult Sema::CheckLValueToRValueConversionOperand(Expr *E) { // Check whether the operand is or contains an object of non-trivial C union - // type. + // type.f if (E->getType().isVolatileQualified() && (E->getType().hasNonTrivialToPrimitiveDestructCUnion() || E->getType().hasNonTrivialToPrimitiveCopyCUnion())) @@ -20222,6 +20298,8 @@ void Sema::DiagnoseEqualityWithExtraParens(ParenExpr *ParenE) { return; Expr *E = ParenE->IgnoreParens(); + if (ParenE->isProducedByFoldExpansion() && ParenE->getSubExpr() == E) + return; if (BinaryOperator *opE = dyn_cast(E)) if (opE->getOpcode() == BO_EQ && diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index d490452e91c3b..e5e7645933ce0 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -503,17 +503,23 @@ bool Sema::checkLiteralOperatorId(const CXXScopeSpec &SS, const IdentifierInfo *II = Name.Identifier; ReservedIdentifierStatus Status = II->isReserved(PP.getLangOpts()); SourceLocation Loc = Name.getEndLoc(); - if (!PP.getSourceManager().isInSystemHeader(Loc)) { - if (auto Hint = FixItHint::CreateReplacement( - Name.getSourceRange(), - (StringRef("operator\"\"") + II->getName()).str()); - isReservedInAllContexts(Status)) { - Diag(Loc, diag::warn_reserved_extern_symbol) - << II << static_cast(Status) << Hint; - } else { - Diag(Loc, diag::warn_deprecated_literal_operator_id) << II << Hint; - } - } + + auto Hint = FixItHint::CreateReplacement( + Name.getSourceRange(), + (StringRef("operator\"\"") + II->getName()).str()); + + // Only emit this diagnostic if we start with an underscore, else the + // diagnostic for C++11 requiring a space between the quotes and the + // identifier conflicts with this and gets confusing. The diagnostic stating + // this is a reserved name should force the underscore, which gets this + // back. + if (II->isReservedLiteralSuffixId() != + ReservedLiteralSuffixIdStatus::NotStartsWithUnderscore) + Diag(Loc, diag::warn_deprecated_literal_operator_id) << II << Hint; + + if (isReservedInAllContexts(Status)) + Diag(Loc, diag::warn_reserved_extern_symbol) + << II << static_cast(Status) << Hint; } if (!SS.isValid()) @@ -1113,9 +1119,22 @@ bool Sema::CheckCXXThrowOperand(SourceLocation ThrowLoc, return false; } +static QualType adjustCVQualifiersForCXXThisWithinContract(QualType ThisTy, + ASTContext &ASTCtx) { + QualType ClassType = ThisTy->getPointeeType(); + if (not ClassType.isConstQualified()) { + // If the 'this' object is const-qualified, we need to remove the + // const-qualification for the contract check. + ClassType.addConst(); + return ASTCtx.getPointerType(ClassType); + } + return ThisTy; +} + static QualType adjustCVQualifiersForCXXThisWithinLambda( ArrayRef FunctionScopes, QualType ThisTy, - DeclContext *CurSemaContext, ASTContext &ASTCtx) { + DeclContext *CurSemaContext, Sema &SemaRef) { + ASTContext &ASTCtx = SemaRef.Context; QualType ClassType = ThisTy->getPointeeType(); LambdaScopeInfo *CurLSI = nullptr; @@ -1174,6 +1193,7 @@ static QualType adjustCVQualifiersForCXXThisWithinLambda( ClassType.addConst(); return ASTCtx.getPointerType(ClassType); } + } // 2) We've run out of ScopeInfos but check 1. if CurDC is a lambda (which @@ -1241,13 +1261,20 @@ QualType Sema::getCurrentThisType() { // per [expr.prim.general]p4. ThisTy = Context.getPointerType(ClassTy); } + if (!ThisTy.isNull() && + currentEvaluationContext().isConstificationContext()) { + ThisTy = adjustCVQualifiersForCXXThisWithinContract(ThisTy, Context); + } + + if (!ThisTy.isNull()) + ThisTy = adjustCXXThisTypeForContracts(ThisTy); // If we are within a lambda's call operator, the cv-qualifiers of 'this' // might need to be adjusted if the lambda or any of its enclosing lambda's // captures '*this' by copy. if (!ThisTy.isNull() && isLambdaCallOperator(CurContext)) return adjustCVQualifiersForCXXThisWithinLambda(FunctionScopes, ThisTy, - CurContext, Context); + CurContext, *this); return ThisTy; } @@ -1378,6 +1405,16 @@ bool Sema::CheckCXXThisCapture(SourceLocation Loc, const bool Explicit, } if (!BuildAndDiagnose) return false; + auto MinConstificationContext = [&]() -> std::optional { + auto *Ent = CurrentContractEntry; + while (Ent) { + if (!Ent->Previous) + return Ent->FunctionIndexAtPush; + Ent = Ent->Previous; + } + return std::nullopt; + }(); + // If we got here, then the closure at MaxFunctionScopesIndex on the // FunctionScopes stack, can capture the *enclosing object*, so capture it // (including implicit by-reference captures in any enclosing closures). @@ -1392,6 +1429,7 @@ bool Sema::CheckCXXThisCapture(SourceLocation Loc, const bool Explicit, "Only a lambda can capture the enclosing object (referred to by " "*this) by copy"); QualType ThisTy = getCurrentThisType(); + for (int idx = MaxFunctionScopesIndex; NumCapturingClosures; --idx, --NumCapturingClosures) { CapturingScopeInfo *CSI = cast(FunctionScopes[idx]); @@ -1400,6 +1438,15 @@ bool Sema::CheckCXXThisCapture(SourceLocation Loc, const bool Explicit, // copy'). QualType CaptureType = ByCopy ? ThisTy->getPointeeType() : ThisTy; + // Or if we're capturing this by reference and there's an interviening + // contract, we need to capture the constified version of the 'this' object. + if (!ByCopy && MinConstificationContext && + static_cast(idx) >= *MinConstificationContext ) { + assert(!ThisTy.isNull()); + CaptureType = + Context.getPointerType(CaptureType->getPointeeType().withConst()); + } + bool isNested = NumCapturingClosures > 1; CSI->addThisCapture(isNested, Loc, CaptureType, ByCopy); } @@ -8429,7 +8476,8 @@ ExprResult Sema::ActOnPseudoDestructorExpr(Scope *S, Expr *Base, QualType ObjectType; QualType T; TypeLocBuilder TLB; - if (CheckArrow(*this, ObjectType, Base, OpKind, OpLoc)) + if (CheckArrow(*this, ObjectType, Base, OpKind, OpLoc) || + DS.getTypeSpecType() == DeclSpec::TST_error) return ExprError(); switch (DS.getTypeSpecType()) { @@ -8704,6 +8752,12 @@ static void CheckIfAnyEnclosingLambdasMustCaptureAnyPotentialCaptures( !IsFullExprInstantiationDependent) return; +#if 0 + if (auto *DRE = dyn_cast(VarExpr->IgnoreParenImpCasts())) + if (DRE->isInContractContext()) + return; +#endif + VarDecl *UnderlyingVar = Var->getPotentiallyDecomposedVarDecl(); if (!UnderlyingVar) return; diff --git a/clang/lib/Sema/SemaFunctionEffects.cpp b/clang/lib/Sema/SemaFunctionEffects.cpp new file mode 100644 index 0000000000000..0ac5de29f66aa --- /dev/null +++ b/clang/lib/Sema/SemaFunctionEffects.cpp @@ -0,0 +1,1576 @@ +//=== SemaFunctionEffects.cpp - Sema handling of function effects ---------===// +// +// 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 implements Sema handling of function effects. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/AST/Stmt.h" +#include "clang/AST/Type.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Sema/SemaInternal.h" + +#define DEBUG_TYPE "effectanalysis" + +using namespace clang; + +namespace { + +enum class ViolationID : uint8_t { + None = 0, // Sentinel for an empty Violation. + // These first 5 map to a %select{} in one of several FunctionEffects + // diagnostics, e.g. warn_func_effect_violation. + BaseDiagnosticIndex, + AllocatesMemory = BaseDiagnosticIndex, + ThrowsOrCatchesExceptions, + HasStaticLocalVariable, + AccessesThreadLocalVariable, + AccessesObjCMethodOrProperty, + + // These only apply to callees, where the analysis stops at the Decl. + DeclDisallowsInference, + + // These both apply to indirect calls. The difference is that sometimes + // we have an actual Decl (generally a variable) which is the function + // pointer being called, and sometimes, typically due to a cast, we only + // have an expression. + CallsDeclWithoutEffect, + CallsExprWithoutEffect, +}; + +// Information about the AST context in which a violation was found, so +// that diagnostics can point to the correct source. +class ViolationSite { +public: + enum class Kind : uint8_t { + Default, // Function body. + MemberInitializer, + DefaultArgExpr + }; + +private: + llvm::PointerIntPair Impl; + +public: + ViolationSite() = default; + + explicit ViolationSite(CXXDefaultArgExpr *E) + : Impl(E, Kind::DefaultArgExpr) {} + + Kind kind() const { return static_cast(Impl.getInt()); } + CXXDefaultArgExpr *defaultArgExpr() const { return Impl.getPointer(); } + + void setKind(Kind K) { Impl.setPointerAndInt(nullptr, K); } +}; + +// Represents a violation of the rules, potentially for the entire duration of +// the analysis phase, in order to refer to it when explaining why a caller has +// been made unsafe by a callee. Can be transformed into either a Diagnostic +// (warning or a note), depending on whether the violation pertains to a +// function failing to be verifed as holding an effect vs. a function failing to +// be inferred as holding that effect. +struct Violation { + FunctionEffect Effect; + std::optional + CalleeEffectPreventingInference; // Only for certain IDs; can be nullopt. + ViolationID ID = ViolationID::None; + ViolationSite Site; + SourceLocation Loc; + const Decl *Callee = + nullptr; // Only valid for ViolationIDs Calls{Decl,Expr}WithoutEffect. + + Violation(FunctionEffect Effect, ViolationID ID, ViolationSite VS, + SourceLocation Loc, const Decl *Callee = nullptr, + std::optional CalleeEffect = std::nullopt) + : Effect(Effect), CalleeEffectPreventingInference(CalleeEffect), ID(ID), + Site(VS), Loc(Loc), Callee(Callee) {} + + unsigned diagnosticSelectIndex() const { + return unsigned(ID) - unsigned(ViolationID::BaseDiagnosticIndex); + } +}; + +enum class SpecialFuncType : uint8_t { None, OperatorNew, OperatorDelete }; +enum class CallableType : uint8_t { + // Unknown: probably function pointer. + Unknown, + Function, + Virtual, + Block +}; + +// Return whether a function's effects CAN be verified. +// The question of whether it SHOULD be verified is independent. +static bool functionIsVerifiable(const FunctionDecl *FD) { + if (FD->isTrivial()) { + // Otherwise `struct x { int a; };` would have an unverifiable default + // constructor. + return true; + } + return FD->hasBody(); +} + +static bool isNoexcept(const FunctionDecl *FD) { + const auto *FPT = FD->getType()->getAs(); + return FPT && (FPT->isNothrow() || FD->hasAttr()); +} + +// This list is probably incomplete. +// FIXME: Investigate: +// __builtin_eh_return? +// __builtin_allow_runtime_check? +// __builtin_unwind_init and other similar things that sound exception-related. +// va_copy? +// coroutines? +static FunctionEffectKindSet getBuiltinFunctionEffects(unsigned BuiltinID) { + FunctionEffectKindSet Result; + + switch (BuiltinID) { + case 0: // Not builtin. + default: // By default, builtins have no known effects. + break; + + // These allocate/deallocate heap memory. + case Builtin::ID::BI__builtin_calloc: + case Builtin::ID::BI__builtin_malloc: + case Builtin::ID::BI__builtin_realloc: + case Builtin::ID::BI__builtin_free: + case Builtin::ID::BI__builtin_operator_delete: + case Builtin::ID::BI__builtin_operator_new: + case Builtin::ID::BIaligned_alloc: + case Builtin::ID::BIcalloc: + case Builtin::ID::BImalloc: + case Builtin::ID::BImemalign: + case Builtin::ID::BIrealloc: + case Builtin::ID::BIfree: + + case Builtin::ID::BIfopen: + case Builtin::ID::BIpthread_create: + case Builtin::ID::BI_Block_object_dispose: + Result.insert(FunctionEffect(FunctionEffect::Kind::Allocating)); + break; + + // These block in some other way than allocating memory. + // longjmp() and friends are presumed unsafe because they are the moral + // equivalent of throwing a C++ exception, which is unsafe. + case Builtin::ID::BIlongjmp: + case Builtin::ID::BI_longjmp: + case Builtin::ID::BIsiglongjmp: + case Builtin::ID::BI__builtin_longjmp: + case Builtin::ID::BIobjc_exception_throw: + + // Objective-C runtime. + case Builtin::ID::BIobjc_msgSend: + case Builtin::ID::BIobjc_msgSend_fpret: + case Builtin::ID::BIobjc_msgSend_fp2ret: + case Builtin::ID::BIobjc_msgSend_stret: + case Builtin::ID::BIobjc_msgSendSuper: + case Builtin::ID::BIobjc_getClass: + case Builtin::ID::BIobjc_getMetaClass: + case Builtin::ID::BIobjc_enumerationMutation: + case Builtin::ID::BIobjc_assign_ivar: + case Builtin::ID::BIobjc_assign_global: + case Builtin::ID::BIobjc_sync_enter: + case Builtin::ID::BIobjc_sync_exit: + case Builtin::ID::BINSLog: + case Builtin::ID::BINSLogv: + + // stdio.h + case Builtin::ID::BIfread: + case Builtin::ID::BIfwrite: + + // stdio.h: printf family. + case Builtin::ID::BIprintf: + case Builtin::ID::BI__builtin_printf: + case Builtin::ID::BIfprintf: + case Builtin::ID::BIsnprintf: + case Builtin::ID::BIsprintf: + case Builtin::ID::BIvprintf: + case Builtin::ID::BIvfprintf: + case Builtin::ID::BIvsnprintf: + case Builtin::ID::BIvsprintf: + + // stdio.h: scanf family. + case Builtin::ID::BIscanf: + case Builtin::ID::BIfscanf: + case Builtin::ID::BIsscanf: + case Builtin::ID::BIvscanf: + case Builtin::ID::BIvfscanf: + case Builtin::ID::BIvsscanf: + Result.insert(FunctionEffect(FunctionEffect::Kind::Blocking)); + break; + } + + return Result; +} + +// Transitory, more extended information about a callable, which can be a +// function, block, or function pointer. +struct CallableInfo { + // CDecl holds the function's definition, if any. + // FunctionDecl if CallableType::Function or Virtual + // BlockDecl if CallableType::Block + const Decl *CDecl; + + // Remember whether the callable is a function, block, virtual method, + // or (presumed) function pointer. + CallableType CType = CallableType::Unknown; + + // Remember whether the callable is an operator new or delete function, + // so that calls to them are reported more meaningfully, as memory + // allocations. + SpecialFuncType FuncType = SpecialFuncType::None; + + // We inevitably want to know the callable's declared effects, so cache them. + FunctionEffectKindSet Effects; + + CallableInfo(const Decl &CD, SpecialFuncType FT = SpecialFuncType::None) + : CDecl(&CD), FuncType(FT) { + FunctionEffectsRef DeclEffects; + if (auto *FD = dyn_cast(CDecl)) { + // Use the function's definition, if any. + if (const FunctionDecl *Def = FD->getDefinition()) + CDecl = FD = Def; + CType = CallableType::Function; + if (auto *Method = dyn_cast(FD); + Method && Method->isVirtual()) + CType = CallableType::Virtual; + DeclEffects = FD->getFunctionEffects(); + } else if (auto *BD = dyn_cast(CDecl)) { + CType = CallableType::Block; + DeclEffects = BD->getFunctionEffects(); + } else if (auto *VD = dyn_cast(CDecl)) { + // ValueDecl is function, enum, or variable, so just look at its type. + DeclEffects = FunctionEffectsRef::get(VD->getType()); + } + Effects = FunctionEffectKindSet(DeclEffects); + } + + CallableType type() const { return CType; } + + bool isCalledDirectly() const { + return CType == CallableType::Function || CType == CallableType::Block; + } + + bool isVerifiable() const { + switch (CType) { + case CallableType::Unknown: + case CallableType::Virtual: + return false; + case CallableType::Block: + return true; + case CallableType::Function: + return functionIsVerifiable(dyn_cast(CDecl)); + } + llvm_unreachable("undefined CallableType"); + } + + /// Generate a name for logging and diagnostics. + std::string getNameForDiagnostic(Sema &S) const { + std::string Name; + llvm::raw_string_ostream OS(Name); + + if (auto *FD = dyn_cast(CDecl)) + FD->getNameForDiagnostic(OS, S.getPrintingPolicy(), + /*Qualified=*/true); + else if (auto *BD = dyn_cast(CDecl)) + OS << "(block " << BD->getBlockManglingNumber() << ")"; + else if (auto *VD = dyn_cast(CDecl)) + VD->printQualifiedName(OS); + return Name; + } +}; + +// ---------- +// Map effects to single Violations, to hold the first (of potentially many) +// violations pertaining to an effect, per function. +class EffectToViolationMap { + // Since we currently only have a tiny number of effects (typically no more + // than 1), use a SmallVector with an inline capacity of 1. Since it + // is often empty, use a unique_ptr to the SmallVector. + // Note that Violation itself contains a FunctionEffect which is the key. + // FIXME: Is there a way to simplify this using existing data structures? + using ImplVec = llvm::SmallVector; + std::unique_ptr Impl; + +public: + // Insert a new Violation if we do not already have one for its effect. + void maybeInsert(const Violation &Viol) { + if (Impl == nullptr) + Impl = std::make_unique(); + else if (lookup(Viol.Effect) != nullptr) + return; + + Impl->push_back(Viol); + } + + const Violation *lookup(FunctionEffect Key) { + if (Impl == nullptr) + return nullptr; + + auto *Iter = llvm::find_if( + *Impl, [&](const auto &Item) { return Item.Effect == Key; }); + return Iter != Impl->end() ? &*Iter : nullptr; + } + + size_t size() const { return Impl ? Impl->size() : 0; } +}; + +// ---------- +// State pertaining to a function whose AST is walked and whose effect analysis +// is dependent on a subsequent analysis of other functions. +class PendingFunctionAnalysis { + friend class CompleteFunctionAnalysis; + +public: + struct DirectCall { + const Decl *Callee; + SourceLocation CallLoc; + // Not all recursive calls are detected, just enough + // to break cycles. + bool Recursed = false; + ViolationSite VSite; + + DirectCall(const Decl *D, SourceLocation CallLoc, ViolationSite VSite) + : Callee(D), CallLoc(CallLoc), VSite(VSite) {} + }; + + // We always have two disjoint sets of effects to verify: + // 1. Effects declared explicitly by this function. + // 2. All other inferrable effects needing verification. + FunctionEffectKindSet DeclaredVerifiableEffects; + FunctionEffectKindSet EffectsToInfer; + +private: + // Violations pertaining to the function's explicit effects. + SmallVector ViolationsForExplicitEffects; + + // Violations pertaining to other, non-explicit, inferrable effects. + EffectToViolationMap InferrableEffectToFirstViolation; + + // These unverified direct calls are what keeps the analysis "pending", + // until the callees can be verified. + SmallVector UnverifiedDirectCalls; + +public: + PendingFunctionAnalysis(Sema &S, const CallableInfo &CInfo, + FunctionEffectKindSet AllInferrableEffectsToVerify) + : DeclaredVerifiableEffects(CInfo.Effects) { + // Check for effects we are not allowed to infer. + FunctionEffectKindSet InferrableEffects; + + for (FunctionEffect effect : AllInferrableEffectsToVerify) { + std::optional ProblemCalleeEffect = + effect.effectProhibitingInference(*CInfo.CDecl, CInfo.Effects); + if (!ProblemCalleeEffect) + InferrableEffects.insert(effect); + else { + // Add a Violation for this effect if a caller were to + // try to infer it. + InferrableEffectToFirstViolation.maybeInsert(Violation( + effect, ViolationID::DeclDisallowsInference, ViolationSite{}, + CInfo.CDecl->getLocation(), nullptr, ProblemCalleeEffect)); + } + } + // InferrableEffects is now the set of inferrable effects which are not + // prohibited. + EffectsToInfer = FunctionEffectKindSet::difference( + InferrableEffects, DeclaredVerifiableEffects); + } + + // Hide the way that Violations for explicitly required effects vs. inferred + // ones are handled differently. + void checkAddViolation(bool Inferring, const Violation &NewViol) { + if (!Inferring) + ViolationsForExplicitEffects.push_back(NewViol); + else + InferrableEffectToFirstViolation.maybeInsert(NewViol); + } + + void addUnverifiedDirectCall(const Decl *D, SourceLocation CallLoc, + ViolationSite VSite) { + UnverifiedDirectCalls.emplace_back(D, CallLoc, VSite); + } + + // Analysis is complete when there are no unverified direct calls. + bool isComplete() const { return UnverifiedDirectCalls.empty(); } + + const Violation *violationForInferrableEffect(FunctionEffect effect) { + return InferrableEffectToFirstViolation.lookup(effect); + } + + // Mutable because caller may need to set a DirectCall's Recursing flag. + MutableArrayRef unverifiedCalls() { + assert(!isComplete()); + return UnverifiedDirectCalls; + } + + ArrayRef getSortedViolationsForExplicitEffects(SourceManager &SM) { + if (!ViolationsForExplicitEffects.empty()) + llvm::sort(ViolationsForExplicitEffects, + [&SM](const Violation &LHS, const Violation &RHS) { + return SM.isBeforeInTranslationUnit(LHS.Loc, RHS.Loc); + }); + return ViolationsForExplicitEffects; + } + + void dump(Sema &SemaRef, llvm::raw_ostream &OS) const { + OS << "Pending: Declared "; + DeclaredVerifiableEffects.dump(OS); + OS << ", " << ViolationsForExplicitEffects.size() << " violations; "; + OS << " Infer "; + EffectsToInfer.dump(OS); + OS << ", " << InferrableEffectToFirstViolation.size() << " violations"; + if (!UnverifiedDirectCalls.empty()) { + OS << "; Calls: "; + for (const DirectCall &Call : UnverifiedDirectCalls) { + CallableInfo CI(*Call.Callee); + OS << " " << CI.getNameForDiagnostic(SemaRef); + } + } + OS << "\n"; + } +}; + +// ---------- +class CompleteFunctionAnalysis { + // Current size: 2 pointers +public: + // Has effects which are both the declared ones -- not to be inferred -- plus + // ones which have been successfully inferred. These are all considered + // "verified" for the purposes of callers; any issue with verifying declared + // effects has already been reported and is not the problem of any caller. + FunctionEffectKindSet VerifiedEffects; + +private: + // This is used to generate notes about failed inference. + EffectToViolationMap InferrableEffectToFirstViolation; + +public: + // The incoming Pending analysis is consumed (member(s) are moved-from). + CompleteFunctionAnalysis(ASTContext &Ctx, PendingFunctionAnalysis &&Pending, + FunctionEffectKindSet DeclaredEffects, + FunctionEffectKindSet AllInferrableEffectsToVerify) + : VerifiedEffects(DeclaredEffects) { + for (FunctionEffect effect : AllInferrableEffectsToVerify) + if (Pending.violationForInferrableEffect(effect) == nullptr) + VerifiedEffects.insert(effect); + + InferrableEffectToFirstViolation = + std::move(Pending.InferrableEffectToFirstViolation); + } + + const Violation *firstViolationForEffect(FunctionEffect Effect) { + return InferrableEffectToFirstViolation.lookup(Effect); + } + + void dump(llvm::raw_ostream &OS) const { + OS << "Complete: Verified "; + VerifiedEffects.dump(OS); + OS << "; Infer "; + OS << InferrableEffectToFirstViolation.size() << " violations\n"; + } +}; + +// ========== +class Analyzer { + Sema &S; + + // Subset of Sema.AllEffectsToVerify + FunctionEffectKindSet AllInferrableEffectsToVerify; + + using FuncAnalysisPtr = + llvm::PointerUnion; + + // Map all Decls analyzed to FuncAnalysisPtr. Pending state is larger + // than complete state, so use different objects to represent them. + // The state pointers are owned by the container. + class AnalysisMap : llvm::DenseMap { + using Base = llvm::DenseMap; + + public: + ~AnalysisMap(); + + // Use non-public inheritance in order to maintain the invariant + // that lookups and insertions are via the canonical Decls. + + FuncAnalysisPtr lookup(const Decl *Key) const { + return Base::lookup(Key->getCanonicalDecl()); + } + + FuncAnalysisPtr &operator[](const Decl *Key) { + return Base::operator[](Key->getCanonicalDecl()); + } + + /// Shortcut for the case where we only care about completed analysis. + CompleteFunctionAnalysis *completedAnalysisForDecl(const Decl *D) const { + if (FuncAnalysisPtr AP = lookup(D); + isa_and_nonnull(AP)) + return AP.get(); + return nullptr; + } + + void dump(Sema &SemaRef, llvm::raw_ostream &OS) { + OS << "\nAnalysisMap:\n"; + for (const auto &item : *this) { + CallableInfo CI(*item.first); + const auto AP = item.second; + OS << item.first << " " << CI.getNameForDiagnostic(SemaRef) << " : "; + if (AP.isNull()) { + OS << "null\n"; + } else if (isa(AP)) { + auto *CFA = AP.get(); + OS << CFA << " "; + CFA->dump(OS); + } else if (isa(AP)) { + auto *PFA = AP.get(); + OS << PFA << " "; + PFA->dump(SemaRef, OS); + } else + llvm_unreachable("never"); + } + OS << "---\n"; + } + }; + AnalysisMap DeclAnalysis; + +public: + Analyzer(Sema &S) : S(S) {} + + void run(const TranslationUnitDecl &TU) { + // Gather all of the effects to be verified to see what operations need to + // be checked, and to see which ones are inferrable. + for (FunctionEffect Effect : S.AllEffectsToVerify) { + const FunctionEffect::Flags Flags = Effect.flags(); + if (Flags & FunctionEffect::FE_InferrableOnCallees) + AllInferrableEffectsToVerify.insert(Effect); + } + LLVM_DEBUG(llvm::dbgs() << "AllInferrableEffectsToVerify: "; + AllInferrableEffectsToVerify.dump(llvm::dbgs()); + llvm::dbgs() << "\n";); + + // We can use DeclsWithEffectsToVerify as a stack for a + // depth-first traversal; there's no need for a second container. But first, + // reverse it, so when working from the end, Decls are verified in the order + // they are declared. + SmallVector &VerificationQueue = S.DeclsWithEffectsToVerify; + std::reverse(VerificationQueue.begin(), VerificationQueue.end()); + + while (!VerificationQueue.empty()) { + const Decl *D = VerificationQueue.back(); + if (FuncAnalysisPtr AP = DeclAnalysis.lookup(D)) { + if (auto *Pending = AP.dyn_cast()) { + // All children have been traversed; finish analysis. + finishPendingAnalysis(D, Pending); + } + VerificationQueue.pop_back(); + continue; + } + + // Not previously visited; begin a new analysis for this Decl. + PendingFunctionAnalysis *Pending = verifyDecl(D); + if (Pending == nullptr) { + // Completed now. + VerificationQueue.pop_back(); + continue; + } + + // Analysis remains pending because there are direct callees to be + // verified first. Push them onto the queue. + for (PendingFunctionAnalysis::DirectCall &Call : + Pending->unverifiedCalls()) { + FuncAnalysisPtr AP = DeclAnalysis.lookup(Call.Callee); + if (AP.isNull()) { + VerificationQueue.push_back(Call.Callee); + continue; + } + + // This indicates recursion (not necessarily direct). For the + // purposes of effect analysis, we can just ignore it since + // no effects forbid recursion. + assert(isa(AP)); + Call.Recursed = true; + } + } + } + +private: + // Verify a single Decl. Return the pending structure if that was the result, + // else null. This method must not recurse. + PendingFunctionAnalysis *verifyDecl(const Decl *D) { + CallableInfo CInfo(*D); + bool isExternC = false; + + if (const FunctionDecl *FD = dyn_cast(D)) + isExternC = FD->getCanonicalDecl()->isExternCContext(); + + // For C++, with non-extern "C" linkage only - if any of the Decl's declared + // effects forbid throwing (e.g. nonblocking) then the function should also + // be declared noexcept. + if (S.getLangOpts().CPlusPlus && !isExternC) { + for (FunctionEffect Effect : CInfo.Effects) { + if (!(Effect.flags() & FunctionEffect::FE_ExcludeThrow)) + continue; + + bool IsNoexcept = false; + if (auto *FD = D->getAsFunction()) { + IsNoexcept = isNoexcept(FD); + } else if (auto *BD = dyn_cast(D)) { + if (auto *TSI = BD->getSignatureAsWritten()) { + auto *FPT = TSI->getType()->getAs(); + IsNoexcept = FPT->isNothrow() || BD->hasAttr(); + } + } + if (!IsNoexcept) + S.Diag(D->getBeginLoc(), diag::warn_perf_constraint_implies_noexcept) + << GetCallableDeclKind(D, nullptr) << Effect.name(); + break; + } + } + + // Build a PendingFunctionAnalysis on the stack. If it turns out to be + // complete, we'll have avoided a heap allocation; if it's incomplete, it's + // a fairly trivial move to a heap-allocated object. + PendingFunctionAnalysis FAnalysis(S, CInfo, AllInferrableEffectsToVerify); + + LLVM_DEBUG(llvm::dbgs() + << "\nVerifying " << CInfo.getNameForDiagnostic(S) << " "; + FAnalysis.dump(S, llvm::dbgs());); + + FunctionBodyASTVisitor Visitor(*this, FAnalysis, CInfo); + + Visitor.run(); + if (FAnalysis.isComplete()) { + completeAnalysis(CInfo, std::move(FAnalysis)); + return nullptr; + } + // Move the pending analysis to the heap and save it in the map. + PendingFunctionAnalysis *PendingPtr = + new PendingFunctionAnalysis(std::move(FAnalysis)); + DeclAnalysis[D] = PendingPtr; + LLVM_DEBUG(llvm::dbgs() << "inserted pending " << PendingPtr << "\n"; + DeclAnalysis.dump(S, llvm::dbgs());); + return PendingPtr; + } + + // Consume PendingFunctionAnalysis, create with it a CompleteFunctionAnalysis, + // inserted in the container. + void completeAnalysis(const CallableInfo &CInfo, + PendingFunctionAnalysis &&Pending) { + if (ArrayRef Viols = + Pending.getSortedViolationsForExplicitEffects(S.getSourceManager()); + !Viols.empty()) + emitDiagnostics(Viols, CInfo); + + CompleteFunctionAnalysis *CompletePtr = new CompleteFunctionAnalysis( + S.getASTContext(), std::move(Pending), CInfo.Effects, + AllInferrableEffectsToVerify); + DeclAnalysis[CInfo.CDecl] = CompletePtr; + LLVM_DEBUG(llvm::dbgs() << "inserted complete " << CompletePtr << "\n"; + DeclAnalysis.dump(S, llvm::dbgs());); + } + + // Called after all direct calls requiring inference have been found -- or + // not. Repeats calls to FunctionBodyASTVisitor::followCall() but without + // the possibility of inference. Deletes Pending. + void finishPendingAnalysis(const Decl *D, PendingFunctionAnalysis *Pending) { + CallableInfo Caller(*D); + LLVM_DEBUG(llvm::dbgs() << "finishPendingAnalysis for " + << Caller.getNameForDiagnostic(S) << " : "; + Pending->dump(S, llvm::dbgs()); llvm::dbgs() << "\n";); + for (const PendingFunctionAnalysis::DirectCall &Call : + Pending->unverifiedCalls()) { + if (Call.Recursed) + continue; + + CallableInfo Callee(*Call.Callee); + followCall(Caller, *Pending, Callee, Call.CallLoc, + /*AssertNoFurtherInference=*/true, Call.VSite); + } + completeAnalysis(Caller, std::move(*Pending)); + delete Pending; + } + + // Here we have a call to a Decl, either explicitly via a CallExpr or some + // other AST construct. PFA pertains to the caller. + void followCall(const CallableInfo &Caller, PendingFunctionAnalysis &PFA, + const CallableInfo &Callee, SourceLocation CallLoc, + bool AssertNoFurtherInference, ViolationSite VSite) { + const bool DirectCall = Callee.isCalledDirectly(); + + // Initially, the declared effects; inferred effects will be added. + FunctionEffectKindSet CalleeEffects = Callee.Effects; + + bool IsInferencePossible = DirectCall; + + if (DirectCall) + if (CompleteFunctionAnalysis *CFA = + DeclAnalysis.completedAnalysisForDecl(Callee.CDecl)) { + // Combine declared effects with those which may have been inferred. + CalleeEffects.insert(CFA->VerifiedEffects); + IsInferencePossible = false; // We've already traversed it. + } + + if (AssertNoFurtherInference) { + assert(!IsInferencePossible); + } + + if (!Callee.isVerifiable()) + IsInferencePossible = false; + + LLVM_DEBUG(llvm::dbgs() + << "followCall from " << Caller.getNameForDiagnostic(S) + << " to " << Callee.getNameForDiagnostic(S) + << "; verifiable: " << Callee.isVerifiable() << "; callee "; + CalleeEffects.dump(llvm::dbgs()); llvm::dbgs() << "\n"; + llvm::dbgs() << " callee " << Callee.CDecl << " canonical " + << Callee.CDecl->getCanonicalDecl() << "\n";); + + auto Check1Effect = [&](FunctionEffect Effect, bool Inferring) { + if (!Effect.shouldDiagnoseFunctionCall(DirectCall, CalleeEffects)) + return; + + // If inference is not allowed, or the target is indirect (virtual + // method/function ptr?), generate a Violation now. + if (!IsInferencePossible || + !(Effect.flags() & FunctionEffect::FE_InferrableOnCallees)) { + if (Callee.FuncType == SpecialFuncType::None) + PFA.checkAddViolation(Inferring, + {Effect, ViolationID::CallsDeclWithoutEffect, + VSite, CallLoc, Callee.CDecl}); + else + PFA.checkAddViolation( + Inferring, + {Effect, ViolationID::AllocatesMemory, VSite, CallLoc}); + } else { + // Inference is allowed and necessary; defer it. + PFA.addUnverifiedDirectCall(Callee.CDecl, CallLoc, VSite); + } + }; + + for (FunctionEffect Effect : PFA.DeclaredVerifiableEffects) + Check1Effect(Effect, false); + + for (FunctionEffect Effect : PFA.EffectsToInfer) + Check1Effect(Effect, true); + } + + // Describe a callable Decl for a diagnostic. + // (Not an enum class because the value is always converted to an integer for + // use in a diagnostic.) + enum CallableDeclKind { + CDK_Function, + CDK_Constructor, + CDK_Destructor, + CDK_Lambda, + CDK_Block, + CDK_MemberInitializer, + }; + + // Describe a call site or target using an enum mapping to a %select{} + // in a diagnostic, e.g. warn_func_effect_violation, + // warn_perf_constraint_implies_noexcept, and others. + static CallableDeclKind GetCallableDeclKind(const Decl *D, + const Violation *V) { + if (V != nullptr && + V->Site.kind() == ViolationSite::Kind::MemberInitializer) + return CDK_MemberInitializer; + if (isa(D)) + return CDK_Block; + if (auto *Method = dyn_cast(D)) { + if (isa(D)) + return CDK_Constructor; + if (isa(D)) + return CDK_Destructor; + const CXXRecordDecl *Rec = Method->getParent(); + if (Rec->isLambda()) + return CDK_Lambda; + } + return CDK_Function; + }; + + // Should only be called when function's analysis is determined to be + // complete. + void emitDiagnostics(ArrayRef Viols, const CallableInfo &CInfo) { + if (Viols.empty()) + return; + + auto MaybeAddTemplateNote = [&](const Decl *D) { + if (const FunctionDecl *FD = dyn_cast(D)) { + while (FD != nullptr && FD->isTemplateInstantiation()) { + S.Diag(FD->getPointOfInstantiation(), + diag::note_func_effect_from_template); + FD = FD->getTemplateInstantiationPattern(); + } + } + }; + + // For note_func_effect_call_indirect. + enum { Indirect_VirtualMethod, Indirect_FunctionPtr }; + + auto MaybeAddSiteContext = [&](const Decl *D, const Violation &V) { + // If a violation site is a member initializer, add a note pointing to + // the constructor which invoked it. + if (V.Site.kind() == ViolationSite::Kind::MemberInitializer) { + unsigned ImplicitCtor = 0; + if (auto *Ctor = dyn_cast(D); + Ctor && Ctor->isImplicit()) + ImplicitCtor = 1; + S.Diag(D->getLocation(), diag::note_func_effect_in_constructor) + << ImplicitCtor; + } + + // If a violation site is a default argument expression, add a note + // pointing to the call site using the default argument. + else if (V.Site.kind() == ViolationSite::Kind::DefaultArgExpr) + S.Diag(V.Site.defaultArgExpr()->getUsedLocation(), + diag::note_in_evaluating_default_argument); + }; + + // Top-level violations are warnings. + for (const Violation &Viol1 : Viols) { + StringRef effectName = Viol1.Effect.name(); + switch (Viol1.ID) { + case ViolationID::None: + case ViolationID::DeclDisallowsInference: // Shouldn't happen + // here. + llvm_unreachable("Unexpected violation kind"); + break; + case ViolationID::AllocatesMemory: + case ViolationID::ThrowsOrCatchesExceptions: + case ViolationID::HasStaticLocalVariable: + case ViolationID::AccessesThreadLocalVariable: + case ViolationID::AccessesObjCMethodOrProperty: + S.Diag(Viol1.Loc, diag::warn_func_effect_violation) + << GetCallableDeclKind(CInfo.CDecl, &Viol1) << effectName + << Viol1.diagnosticSelectIndex(); + MaybeAddSiteContext(CInfo.CDecl, Viol1); + MaybeAddTemplateNote(CInfo.CDecl); + break; + case ViolationID::CallsExprWithoutEffect: + S.Diag(Viol1.Loc, diag::warn_func_effect_calls_expr_without_effect) + << GetCallableDeclKind(CInfo.CDecl, &Viol1) << effectName; + MaybeAddSiteContext(CInfo.CDecl, Viol1); + MaybeAddTemplateNote(CInfo.CDecl); + break; + + case ViolationID::CallsDeclWithoutEffect: { + CallableInfo CalleeInfo(*Viol1.Callee); + std::string CalleeName = CalleeInfo.getNameForDiagnostic(S); + + S.Diag(Viol1.Loc, diag::warn_func_effect_calls_func_without_effect) + << GetCallableDeclKind(CInfo.CDecl, &Viol1) << effectName + << GetCallableDeclKind(CalleeInfo.CDecl, nullptr) << CalleeName; + MaybeAddSiteContext(CInfo.CDecl, Viol1); + MaybeAddTemplateNote(CInfo.CDecl); + + // Emit notes explaining the transitive chain of inferences: Why isn't + // the callee safe? + for (const Decl *Callee = Viol1.Callee; Callee != nullptr;) { + std::optional MaybeNextCallee; + CompleteFunctionAnalysis *Completed = + DeclAnalysis.completedAnalysisForDecl(CalleeInfo.CDecl); + if (Completed == nullptr) { + // No result - could be + // - non-inline and extern + // - indirect (virtual or through function pointer) + // - effect has been explicitly disclaimed (e.g. "blocking") + + CallableType CType = CalleeInfo.type(); + if (CType == CallableType::Virtual) + S.Diag(Callee->getLocation(), + diag::note_func_effect_call_indirect) + << Indirect_VirtualMethod << effectName; + else if (CType == CallableType::Unknown) + S.Diag(Callee->getLocation(), + diag::note_func_effect_call_indirect) + << Indirect_FunctionPtr << effectName; + else if (CalleeInfo.Effects.contains(Viol1.Effect.oppositeKind())) + S.Diag(Callee->getLocation(), + diag::note_func_effect_call_disallows_inference) + << GetCallableDeclKind(CInfo.CDecl, nullptr) << effectName + << FunctionEffect(Viol1.Effect.oppositeKind()).name(); + else if (const FunctionDecl *FD = dyn_cast(Callee); + FD == nullptr || FD->getBuiltinID() == 0) { + // A builtin callee generally doesn't have a useful source + // location at which to insert a note. + S.Diag(Callee->getLocation(), diag::note_func_effect_call_extern) + << effectName; + } + break; + } + const Violation *PtrViol2 = + Completed->firstViolationForEffect(Viol1.Effect); + if (PtrViol2 == nullptr) + break; + + const Violation &Viol2 = *PtrViol2; + switch (Viol2.ID) { + case ViolationID::None: + llvm_unreachable("Unexpected violation kind"); + break; + case ViolationID::DeclDisallowsInference: + S.Diag(Viol2.Loc, diag::note_func_effect_call_disallows_inference) + << GetCallableDeclKind(CalleeInfo.CDecl, nullptr) << effectName + << Viol2.CalleeEffectPreventingInference->name(); + break; + case ViolationID::CallsExprWithoutEffect: + S.Diag(Viol2.Loc, diag::note_func_effect_call_indirect) + << Indirect_FunctionPtr << effectName; + break; + case ViolationID::AllocatesMemory: + case ViolationID::ThrowsOrCatchesExceptions: + case ViolationID::HasStaticLocalVariable: + case ViolationID::AccessesThreadLocalVariable: + case ViolationID::AccessesObjCMethodOrProperty: + S.Diag(Viol2.Loc, diag::note_func_effect_violation) + << GetCallableDeclKind(CalleeInfo.CDecl, &Viol2) << effectName + << Viol2.diagnosticSelectIndex(); + MaybeAddSiteContext(CalleeInfo.CDecl, Viol2); + break; + case ViolationID::CallsDeclWithoutEffect: + MaybeNextCallee.emplace(*Viol2.Callee); + S.Diag(Viol2.Loc, diag::note_func_effect_calls_func_without_effect) + << GetCallableDeclKind(CalleeInfo.CDecl, &Viol2) << effectName + << GetCallableDeclKind(Viol2.Callee, nullptr) + << MaybeNextCallee->getNameForDiagnostic(S); + break; + } + MaybeAddTemplateNote(Callee); + Callee = Viol2.Callee; + if (MaybeNextCallee) { + CalleeInfo = *MaybeNextCallee; + CalleeName = CalleeInfo.getNameForDiagnostic(S); + } + } + } break; + } + } + } + + // ---------- + // This AST visitor is used to traverse the body of a function during effect + // verification. This happens in 2 situations: + // [1] The function has declared effects which need to be validated. + // [2] The function has not explicitly declared an effect in question, and is + // being checked for implicit conformance. + // + // Violations are always routed to a PendingFunctionAnalysis. + struct FunctionBodyASTVisitor : RecursiveASTVisitor { + using Base = RecursiveASTVisitor; + + Analyzer &Outer; + PendingFunctionAnalysis &CurrentFunction; + CallableInfo &CurrentCaller; + ViolationSite VSite; + + FunctionBodyASTVisitor(Analyzer &Outer, + PendingFunctionAnalysis &CurrentFunction, + CallableInfo &CurrentCaller) + : Outer(Outer), CurrentFunction(CurrentFunction), + CurrentCaller(CurrentCaller) {} + + // -- Entry point -- + void run() { + // The target function may have implicit code paths beyond the + // body: member and base destructors. Visit these first. + if (auto *Dtor = dyn_cast(CurrentCaller.CDecl)) + followDestructor(dyn_cast(Dtor->getParent()), Dtor); + + // Do an AST traversal of the function/block body + TraverseDecl(const_cast(CurrentCaller.CDecl)); + } + + // -- Methods implementing common logic -- + + // Handle a language construct forbidden by some effects. Only effects whose + // flags include the specified flag receive a violation. \p Flag describes + // the construct. + void diagnoseLanguageConstruct(FunctionEffect::FlagBit Flag, + ViolationID VID, SourceLocation Loc, + const Decl *Callee = nullptr) { + // If there are any declared verifiable effects which forbid the construct + // represented by the flag, store just one violation. + for (FunctionEffect Effect : CurrentFunction.DeclaredVerifiableEffects) { + if (Effect.flags() & Flag) { + addViolation(/*inferring=*/false, Effect, VID, Loc, Callee); + break; + } + } + // For each inferred effect which forbids the construct, store a + // violation, if we don't already have a violation for that effect. + for (FunctionEffect Effect : CurrentFunction.EffectsToInfer) + if (Effect.flags() & Flag) + addViolation(/*inferring=*/true, Effect, VID, Loc, Callee); + } + + void addViolation(bool Inferring, FunctionEffect Effect, ViolationID VID, + SourceLocation Loc, const Decl *Callee = nullptr) { + CurrentFunction.checkAddViolation( + Inferring, Violation(Effect, VID, VSite, Loc, Callee)); + } + + // Here we have a call to a Decl, either explicitly via a CallExpr or some + // other AST construct. CallableInfo pertains to the callee. + void followCall(CallableInfo &CI, SourceLocation CallLoc) { + // Check for a call to a builtin function, whose effects are + // handled specially. + if (const auto *FD = dyn_cast(CI.CDecl)) { + if (unsigned BuiltinID = FD->getBuiltinID()) { + CI.Effects = getBuiltinFunctionEffects(BuiltinID); + if (CI.Effects.empty()) { + // A builtin with no known effects is assumed safe. + return; + } + // A builtin WITH effects doesn't get any special treatment for + // being noreturn/noexcept, e.g. longjmp(), so we skip the check + // below. + } else { + // If the callee is both `noreturn` and `noexcept`, it presumably + // terminates. Ignore it for the purposes of effect analysis. + // If not C++, `noreturn` alone is sufficient. + if (FD->isNoReturn() && + (!Outer.S.getLangOpts().CPlusPlus || isNoexcept(FD))) + return; + } + } + + Outer.followCall(CurrentCaller, CurrentFunction, CI, CallLoc, + /*AssertNoFurtherInference=*/false, VSite); + } + + void checkIndirectCall(CallExpr *Call, QualType CalleeType) { + FunctionEffectKindSet CalleeEffects; + if (FunctionEffectsRef Effects = FunctionEffectsRef::get(CalleeType); + !Effects.empty()) + CalleeEffects.insert(Effects); + + auto Check1Effect = [&](FunctionEffect Effect, bool Inferring) { + if (Effect.shouldDiagnoseFunctionCall( + /*direct=*/false, CalleeEffects)) + addViolation(Inferring, Effect, ViolationID::CallsExprWithoutEffect, + Call->getBeginLoc()); + }; + + for (FunctionEffect Effect : CurrentFunction.DeclaredVerifiableEffects) + Check1Effect(Effect, false); + + for (FunctionEffect Effect : CurrentFunction.EffectsToInfer) + Check1Effect(Effect, true); + } + + // This destructor's body should be followed by the caller, but here we + // follow the field and base destructors. + void followDestructor(const CXXRecordDecl *Rec, + const CXXDestructorDecl *Dtor) { + SourceLocation DtorLoc = Dtor->getLocation(); + for (const FieldDecl *Field : Rec->fields()) + followTypeDtor(Field->getType(), DtorLoc); + + if (const auto *Class = dyn_cast(Rec)) + for (const CXXBaseSpecifier &Base : Class->bases()) + followTypeDtor(Base.getType(), DtorLoc); + } + + void followTypeDtor(QualType QT, SourceLocation CallSite) { + const Type *Ty = QT.getTypePtr(); + while (Ty->isArrayType()) { + const ArrayType *Arr = Ty->getAsArrayTypeUnsafe(); + QT = Arr->getElementType(); + Ty = QT.getTypePtr(); + } + + if (Ty->isRecordType()) { + if (const CXXRecordDecl *Class = Ty->getAsCXXRecordDecl()) { + if (CXXDestructorDecl *Dtor = Class->getDestructor(); + Dtor && !Dtor->isDeleted()) { + CallableInfo CI(*Dtor); + followCall(CI, CallSite); + } + } + } + } + + // -- Methods for use of RecursiveASTVisitor -- + + bool shouldVisitImplicitCode() const { return true; } + + bool shouldWalkTypesOfTypeLocs() const { return false; } + + bool VisitCXXThrowExpr(CXXThrowExpr *Throw) { + diagnoseLanguageConstruct(FunctionEffect::FE_ExcludeThrow, + ViolationID::ThrowsOrCatchesExceptions, + Throw->getThrowLoc()); + return true; + } + + bool VisitCXXCatchStmt(CXXCatchStmt *Catch) { + diagnoseLanguageConstruct(FunctionEffect::FE_ExcludeCatch, + ViolationID::ThrowsOrCatchesExceptions, + Catch->getCatchLoc()); + return true; + } + + bool VisitObjCAtThrowStmt(ObjCAtThrowStmt *Throw) { + diagnoseLanguageConstruct(FunctionEffect::FE_ExcludeThrow, + ViolationID::ThrowsOrCatchesExceptions, + Throw->getThrowLoc()); + return true; + } + + bool VisitObjCAtCatchStmt(ObjCAtCatchStmt *Catch) { + diagnoseLanguageConstruct(FunctionEffect::FE_ExcludeCatch, + ViolationID::ThrowsOrCatchesExceptions, + Catch->getAtCatchLoc()); + return true; + } + + bool VisitObjCMessageExpr(ObjCMessageExpr *Msg) { + diagnoseLanguageConstruct(FunctionEffect::FE_ExcludeObjCMessageSend, + ViolationID::AccessesObjCMethodOrProperty, + Msg->getBeginLoc()); + return true; + } + + bool VisitSEHExceptStmt(SEHExceptStmt *Exc) { + diagnoseLanguageConstruct(FunctionEffect::FE_ExcludeCatch, + ViolationID::ThrowsOrCatchesExceptions, + Exc->getExceptLoc()); + return true; + } + + bool VisitCallExpr(CallExpr *Call) { + LLVM_DEBUG(llvm::dbgs() + << "VisitCallExpr : " + << Call->getBeginLoc().printToString(Outer.S.SourceMgr) + << "\n";); + + Expr *CalleeExpr = Call->getCallee(); + if (const Decl *Callee = CalleeExpr->getReferencedDeclOfCallee()) { + CallableInfo CI(*Callee); + followCall(CI, Call->getBeginLoc()); + return true; + } + + if (isa(CalleeExpr)) { + // Just destroying a scalar, fine. + return true; + } + + // No Decl, just an Expr. Just check based on its type. + checkIndirectCall(Call, CalleeExpr->getType()); + + return true; + } + + bool VisitVarDecl(VarDecl *Var) { + LLVM_DEBUG(llvm::dbgs() + << "VisitVarDecl : " + << Var->getBeginLoc().printToString(Outer.S.SourceMgr) + << "\n";); + + if (Var->isStaticLocal()) + diagnoseLanguageConstruct(FunctionEffect::FE_ExcludeStaticLocalVars, + ViolationID::HasStaticLocalVariable, + Var->getLocation()); + + const QualType::DestructionKind DK = + Var->needsDestruction(Outer.S.getASTContext()); + if (DK == QualType::DK_cxx_destructor) + followTypeDtor(Var->getType(), Var->getLocation()); + return true; + } + + bool VisitCXXNewExpr(CXXNewExpr *New) { + // RecursiveASTVisitor does not visit the implicit call to operator new. + if (FunctionDecl *FD = New->getOperatorNew()) { + CallableInfo CI(*FD, SpecialFuncType::OperatorNew); + followCall(CI, New->getBeginLoc()); + } + + // It's a bit excessive to check operator delete here, since it's + // just a fallback for operator new followed by a failed constructor. + // We could check it via New->getOperatorDelete(). + + // It DOES however visit the called constructor + return true; + } + + bool VisitCXXDeleteExpr(CXXDeleteExpr *Delete) { + // RecursiveASTVisitor does not visit the implicit call to operator + // delete. + if (FunctionDecl *FD = Delete->getOperatorDelete()) { + CallableInfo CI(*FD, SpecialFuncType::OperatorDelete); + followCall(CI, Delete->getBeginLoc()); + } + + // It DOES however visit the called destructor + + return true; + } + + bool VisitCXXConstructExpr(CXXConstructExpr *Construct) { + LLVM_DEBUG(llvm::dbgs() << "VisitCXXConstructExpr : " + << Construct->getBeginLoc().printToString( + Outer.S.SourceMgr) + << "\n";); + + // RecursiveASTVisitor does not visit the implicit call to the + // constructor. + const CXXConstructorDecl *Ctor = Construct->getConstructor(); + CallableInfo CI(*Ctor); + followCall(CI, Construct->getLocation()); + + return true; + } + + bool TraverseConstructorInitializer(CXXCtorInitializer *Init) { + ViolationSite PrevVS = VSite; + if (Init->isAnyMemberInitializer()) + VSite.setKind(ViolationSite::Kind::MemberInitializer); + bool Result = Base::TraverseConstructorInitializer(Init); + VSite = PrevVS; + return Result; + } + + bool TraverseCXXDefaultArgExpr(CXXDefaultArgExpr *E) { + LLVM_DEBUG(llvm::dbgs() + << "TraverseCXXDefaultArgExpr : " + << E->getUsedLocation().printToString(Outer.S.SourceMgr) + << "\n";); + + ViolationSite PrevVS = VSite; + if (VSite.kind() == ViolationSite::Kind::Default) + VSite = ViolationSite{E}; + + bool Result = Base::TraverseCXXDefaultArgExpr(E); + VSite = PrevVS; + return Result; + } + + bool TraverseLambdaExpr(LambdaExpr *Lambda) { + // We override this so as to be able to skip traversal of the lambda's + // body. We have to explicitly traverse the captures. Why not return + // false from shouldVisitLambdaBody()? Because we need to visit a lambda's + // body when we are verifying the lambda itself; we only want to skip it + // in the context of the outer function. + for (unsigned I = 0, N = Lambda->capture_size(); I < N; ++I) + TraverseLambdaCapture(Lambda, Lambda->capture_begin() + I, + Lambda->capture_init_begin()[I]); + + return true; + } + + bool TraverseBlockExpr(BlockExpr * /*unused*/) { + // TODO: are the capture expressions (ctor call?) safe? + return true; + } + + bool VisitDeclRefExpr(const DeclRefExpr *E) { + const ValueDecl *Val = E->getDecl(); + if (const auto *Var = dyn_cast(Val)) { + if (Var->getTLSKind() != VarDecl::TLS_None) { + // At least on macOS, thread-local variables are initialized on + // first access, including a heap allocation. + diagnoseLanguageConstruct(FunctionEffect::FE_ExcludeThreadLocalVars, + ViolationID::AccessesThreadLocalVariable, + E->getLocation()); + } + } + return true; + } + + bool TraverseGenericSelectionExpr(GenericSelectionExpr *Node) { + return TraverseStmt(Node->getResultExpr()); + } + bool TraverseUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *Node) { + return true; + } + + bool TraverseTypeOfExprTypeLoc(TypeOfExprTypeLoc Node) { return true; } + + bool TraverseDecltypeTypeLoc(DecltypeTypeLoc Node) { return true; } + + bool TraverseCXXNoexceptExpr(CXXNoexceptExpr *Node) { return true; } + + bool TraverseCXXTypeidExpr(CXXTypeidExpr *Node) { return true; } + + // Skip concept requirements since they don't generate code. + bool TraverseConceptRequirement(concepts::Requirement *R) { return true; } + }; +}; + +Analyzer::AnalysisMap::~AnalysisMap() { + for (const auto &Item : *this) { + FuncAnalysisPtr AP = Item.second; + if (isa(AP)) + delete AP.get(); + else + delete AP.get(); + } +} + +} // anonymous namespace + +namespace clang { + +bool Sema::diagnoseConflictingFunctionEffect( + const FunctionEffectsRef &FX, const FunctionEffectWithCondition &NewEC, + SourceLocation NewAttrLoc) { + // If the new effect has a condition, we can't detect conflicts until the + // condition is resolved. + if (NewEC.Cond.getCondition() != nullptr) + return false; + + // Diagnose the new attribute as incompatible with a previous one. + auto Incompatible = [&](const FunctionEffectWithCondition &PrevEC) { + Diag(NewAttrLoc, diag::err_attributes_are_not_compatible) + << ("'" + NewEC.description() + "'") + << ("'" + PrevEC.description() + "'") << false; + // We don't necessarily have the location of the previous attribute, + // so no note. + return true; + }; + + // Compare against previous attributes. + FunctionEffect::Kind NewKind = NewEC.Effect.kind(); + + for (const FunctionEffectWithCondition &PrevEC : FX) { + // Again, can't check yet when the effect is conditional. + if (PrevEC.Cond.getCondition() != nullptr) + continue; + + FunctionEffect::Kind PrevKind = PrevEC.Effect.kind(); + // Note that we allow PrevKind == NewKind; it's redundant and ignored. + + if (PrevEC.Effect.oppositeKind() == NewKind) + return Incompatible(PrevEC); + + // A new allocating is incompatible with a previous nonblocking. + if (PrevKind == FunctionEffect::Kind::NonBlocking && + NewKind == FunctionEffect::Kind::Allocating) + return Incompatible(PrevEC); + + // A new nonblocking is incompatible with a previous allocating. + if (PrevKind == FunctionEffect::Kind::Allocating && + NewKind == FunctionEffect::Kind::NonBlocking) + return Incompatible(PrevEC); + } + + return false; +} + +void Sema::diagnoseFunctionEffectMergeConflicts( + const FunctionEffectSet::Conflicts &Errs, SourceLocation NewLoc, + SourceLocation OldLoc) { + for (const FunctionEffectSet::Conflict &Conflict : Errs) { + Diag(NewLoc, diag::warn_conflicting_func_effects) + << Conflict.Kept.description() << Conflict.Rejected.description(); + Diag(OldLoc, diag::note_previous_declaration); + } +} + +// Decl should be a FunctionDecl or BlockDecl. +void Sema::maybeAddDeclWithEffects(const Decl *D, + const FunctionEffectsRef &FX) { + if (!D->hasBody()) { + if (const auto *FD = D->getAsFunction(); FD && !FD->willHaveBody()) + return; + } + + if (Diags.getIgnoreAllWarnings() || + (Diags.getSuppressSystemWarnings() && + SourceMgr.isInSystemHeader(D->getLocation()))) + return; + + if (hasUncompilableErrorOccurred()) + return; + + // For code in dependent contexts, we'll do this at instantiation time. + // Without this check, we would analyze the function based on placeholder + // template parameters, and potentially generate spurious diagnostics. + if (cast(D)->isDependentContext()) + return; + + addDeclWithEffects(D, FX); +} + +void Sema::addDeclWithEffects(const Decl *D, const FunctionEffectsRef &FX) { + // To avoid the possibility of conflict, don't add effects which are + // not FE_InferrableOnCallees and therefore not verified; this removes + // blocking/allocating but keeps nonblocking/nonallocating. + // Also, ignore any conditions when building the list of effects. + bool AnyVerifiable = false; + for (const FunctionEffectWithCondition &EC : FX) + if (EC.Effect.flags() & FunctionEffect::FE_InferrableOnCallees) { + AllEffectsToVerify.insert(EC.Effect); + AnyVerifiable = true; + } + + // Record the declaration for later analysis. + if (AnyVerifiable) + DeclsWithEffectsToVerify.push_back(D); +} + +void Sema::performFunctionEffectAnalysis(TranslationUnitDecl *TU) { + if (hasUncompilableErrorOccurred() || Diags.getIgnoreAllWarnings()) + return; + if (TU == nullptr) + return; + Analyzer{*this}.run(*TU); +} + +Sema::FunctionEffectDiffVector::FunctionEffectDiffVector( + const FunctionEffectsRef &Old, const FunctionEffectsRef &New) { + + FunctionEffectsRef::iterator POld = Old.begin(); + FunctionEffectsRef::iterator OldEnd = Old.end(); + FunctionEffectsRef::iterator PNew = New.begin(); + FunctionEffectsRef::iterator NewEnd = New.end(); + + while (true) { + int cmp = 0; + if (POld == OldEnd) { + if (PNew == NewEnd) + break; + cmp = 1; + } else if (PNew == NewEnd) + cmp = -1; + else { + FunctionEffectWithCondition Old = *POld; + FunctionEffectWithCondition New = *PNew; + if (Old.Effect.kind() < New.Effect.kind()) + cmp = -1; + else if (New.Effect.kind() < Old.Effect.kind()) + cmp = 1; + else { + cmp = 0; + if (Old.Cond.getCondition() != New.Cond.getCondition()) { + // FIXME: Cases where the expressions are equivalent but + // don't have the same identity. + push_back(FunctionEffectDiff{ + Old.Effect.kind(), FunctionEffectDiff::Kind::ConditionMismatch, + Old, New}); + } + } + } + + if (cmp < 0) { + // removal + FunctionEffectWithCondition Old = *POld; + push_back(FunctionEffectDiff{Old.Effect.kind(), + FunctionEffectDiff::Kind::Removed, Old, + std::nullopt}); + ++POld; + } else if (cmp > 0) { + // addition + FunctionEffectWithCondition New = *PNew; + push_back(FunctionEffectDiff{New.Effect.kind(), + FunctionEffectDiff::Kind::Added, + std::nullopt, New}); + ++PNew; + } else { + ++POld; + ++PNew; + } + } +} + +bool Sema::FunctionEffectDiff::shouldDiagnoseConversion( + QualType SrcType, const FunctionEffectsRef &SrcFX, QualType DstType, + const FunctionEffectsRef &DstFX) const { + + switch (EffectKind) { + case FunctionEffect::Kind::NonAllocating: + // nonallocating can't be added (spoofed) during a conversion, unless we + // have nonblocking. + if (DiffKind == Kind::Added) { + for (const auto &CFE : SrcFX) { + if (CFE.Effect.kind() == FunctionEffect::Kind::NonBlocking) + return false; + } + } + [[fallthrough]]; + case FunctionEffect::Kind::NonBlocking: + // nonblocking can't be added (spoofed) during a conversion. + switch (DiffKind) { + case Kind::Added: + return true; + case Kind::Removed: + return false; + case Kind::ConditionMismatch: + // FIXME: Condition mismatches are too coarse right now -- expressions + // which are equivalent but don't have the same identity are detected as + // mismatches. We're going to diagnose those anyhow until expression + // matching is better. + return true; + } + case FunctionEffect::Kind::Blocking: + case FunctionEffect::Kind::Allocating: + return false; + } + llvm_unreachable("unknown effect kind"); +} + +bool Sema::FunctionEffectDiff::shouldDiagnoseRedeclaration( + const FunctionDecl &OldFunction, const FunctionEffectsRef &OldFX, + const FunctionDecl &NewFunction, const FunctionEffectsRef &NewFX) const { + switch (EffectKind) { + case FunctionEffect::Kind::NonAllocating: + case FunctionEffect::Kind::NonBlocking: + // nonblocking/nonallocating can't be removed in a redeclaration. + switch (DiffKind) { + case Kind::Added: + return false; // No diagnostic. + case Kind::Removed: + return true; // Issue diagnostic. + case Kind::ConditionMismatch: + // All these forms of mismatches are diagnosed. + return true; + } + case FunctionEffect::Kind::Blocking: + case FunctionEffect::Kind::Allocating: + return false; + } + llvm_unreachable("unknown effect kind"); +} + +Sema::FunctionEffectDiff::OverrideResult +Sema::FunctionEffectDiff::shouldDiagnoseMethodOverride( + const CXXMethodDecl &OldMethod, const FunctionEffectsRef &OldFX, + const CXXMethodDecl &NewMethod, const FunctionEffectsRef &NewFX) const { + switch (EffectKind) { + case FunctionEffect::Kind::NonAllocating: + case FunctionEffect::Kind::NonBlocking: + switch (DiffKind) { + + // If added on an override, that's fine and not diagnosed. + case Kind::Added: + return OverrideResult::NoAction; + + // If missing from an override (removed), propagate from base to derived. + case Kind::Removed: + return OverrideResult::Merge; + + // If there's a mismatch involving the effect's polarity or condition, + // issue a warning. + case Kind::ConditionMismatch: + return OverrideResult::Warn; + } + + case FunctionEffect::Kind::Blocking: + case FunctionEffect::Kind::Allocating: + return OverrideResult::NoAction; + } + llvm_unreachable("unknown effect kind"); +} + +} // namespace clang diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp index 5eda22f560c9d..137b15c8fcfe9 100644 --- a/clang/lib/Sema/SemaHLSL.cpp +++ b/clang/lib/Sema/SemaHLSL.cpp @@ -1708,9 +1708,9 @@ static bool CheckNoDoubleVectors(Sema *S, CallExpr *TheCall) { return CheckArgsTypesAreCorrect(S, TheCall, S->Context.FloatTy, checkDoubleVector); } -static bool CheckFloatingOrSignedIntRepresentation(Sema *S, CallExpr *TheCall) { +static bool CheckFloatingOrIntRepresentation(Sema *S, CallExpr *TheCall) { auto checkAllSignedTypes = [](clang::QualType PassedType) -> bool { - return !PassedType->hasSignedIntegerRepresentation() && + return !PassedType->hasIntegerRepresentation() && !PassedType->hasFloatingRepresentation(); }; return CheckArgsTypesAreCorrect(S, TheCall, S->Context.IntTy, @@ -1828,6 +1828,41 @@ bool SemaHLSL::CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) { return true; break; } + case Builtin::BI__builtin_hlsl_cross: { + if (SemaRef.checkArgCount(TheCall, 2)) + return true; + if (CheckVectorElementCallArgs(&SemaRef, TheCall)) + return true; + if (CheckFloatOrHalfRepresentations(&SemaRef, TheCall)) + return true; + // ensure both args have 3 elements + int NumElementsArg1 = + TheCall->getArg(0)->getType()->getAs()->getNumElements(); + int NumElementsArg2 = + TheCall->getArg(1)->getType()->getAs()->getNumElements(); + + if (NumElementsArg1 != 3) { + int LessOrMore = NumElementsArg1 > 3 ? 1 : 0; + SemaRef.Diag(TheCall->getBeginLoc(), + diag::err_vector_incorrect_num_elements) + << LessOrMore << 3 << NumElementsArg1 << /*operand*/ 1; + return true; + } + if (NumElementsArg2 != 3) { + int LessOrMore = NumElementsArg2 > 3 ? 1 : 0; + + SemaRef.Diag(TheCall->getBeginLoc(), + diag::err_vector_incorrect_num_elements) + << LessOrMore << 3 << NumElementsArg2 << /*operand*/ 1; + return true; + } + + ExprResult A = TheCall->getArg(0); + QualType ArgTyA = A.get()->getType(); + // return type is the same as the input type + TheCall->setType(ArgTyA); + break; + } case Builtin::BI__builtin_hlsl_dot: { if (SemaRef.checkArgCount(TheCall, 2)) return true; @@ -1861,6 +1896,8 @@ bool SemaHLSL::CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) { return true; break; } + case Builtin::BI__builtin_hlsl_elementwise_degrees: + case Builtin::BI__builtin_hlsl_elementwise_radians: case Builtin::BI__builtin_hlsl_elementwise_rsqrt: case Builtin::BI__builtin_hlsl_elementwise_frac: { if (CheckFloatOrHalfRepresentations(&SemaRef, TheCall)) @@ -1930,7 +1967,7 @@ bool SemaHLSL::CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) { break; } case Builtin::BI__builtin_hlsl_elementwise_sign: { - if (CheckFloatingOrSignedIntRepresentation(&SemaRef, TheCall)) + if (CheckFloatingOrIntRepresentation(&SemaRef, TheCall)) return true; if (SemaRef.PrepareBuiltinElementwiseMathOneArgCall(TheCall)) return true; @@ -1956,6 +1993,11 @@ bool SemaHLSL::CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) { return true; break; } + case Builtin::BI__builtin_hlsl_wave_get_lane_index: { + if (SemaRef.checkArgCount(TheCall, 0)) + return true; + break; + } case Builtin::BI__builtin_elementwise_acos: case Builtin::BI__builtin_elementwise_asin: case Builtin::BI__builtin_elementwise_atan: diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index 4d11f2a43fcc6..f560865681fa5 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -763,6 +763,8 @@ void InitListChecker::FillInEmptyInitForField(unsigned Init, FieldDecl *Field, SemaRef.currentEvaluationContext().DelayedDefaultInitializationContext = SemaRef.parentEvaluationContext() .DelayedDefaultInitializationContext; + SemaRef.currentEvaluationContext().InLifetimeExtendingContext = + SemaRef.parentEvaluationContext().InLifetimeExtendingContext; DIE = SemaRef.BuildCXXDefaultInitExpr(Loc, Field); } if (DIE.isInvalid()) { @@ -1976,8 +1978,9 @@ void InitListChecker::CheckVectorType(const InitializedEntity &Entity, if (numEltsInit != maxElements) { if (!VerifyOnly) SemaRef.Diag(IList->getBeginLoc(), - diag::err_vector_incorrect_num_initializers) - << (numEltsInit < maxElements) << maxElements << numEltsInit; + diag::err_vector_incorrect_num_elements) + << (numEltsInit < maxElements) << maxElements << numEltsInit + << /*initialization*/ 0; hadError = true; } } @@ -9950,7 +9953,7 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer( auto SynthesizeAggrGuide = [&](InitListExpr *ListInit) { auto *Pattern = Template; while (Pattern->getInstantiatedFromMemberTemplate()) { - if (Pattern->isMemberSpecialization()) + if (Pattern->hasMemberSpecialization()) break; Pattern = Pattern->getInstantiatedFromMemberTemplate(); } diff --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp index 15ed8572e6084..f5db2de2446a1 100644 --- a/clang/lib/Sema/SemaLambda.cpp +++ b/clang/lib/Sema/SemaLambda.cpp @@ -423,11 +423,11 @@ bool Sema::DiagnoseInvalidExplicitObjectParameterInLambda( // is an empty cast path for the method stored in the context (signalling that // we've already diagnosed it) and then just not building the call, but that // doesn't really seem any simpler than diagnosing it at the call site... - if (auto It = Context.LambdaCastPaths.find(Method); - It != Context.LambdaCastPaths.end()) + auto [It, Inserted] = Context.LambdaCastPaths.try_emplace(Method); + if (!Inserted) return It->second.empty(); - CXXCastPath &Path = Context.LambdaCastPaths[Method]; + CXXCastPath &Path = It->second; CXXBasePaths Paths(/*FindAmbiguities=*/true, /*RecordPaths=*/true, /*DetectVirtual=*/false); if (!IsDerivedFrom(RD->getLocation(), ExplicitObjectParameterType, LambdaType, @@ -791,6 +791,7 @@ QualType Sema::buildLambdaInitCaptureInitialization( TypeLocBuilder TLB; AutoTypeLoc TL = TLB.push(DeductType); TL.setNameLoc(Loc); + if (ByRef) { DeductType = BuildReferenceType(DeductType, true, Loc, Id); assert(!DeductType.isNull() && "can't build reference to auto"); @@ -1393,7 +1394,6 @@ void Sema::ActOnLambdaClosureParameters( void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro, Declarator &ParamInfo, const DeclSpec &DS) { - LambdaScopeInfo *LSI = getCurrentLambdaScopeUnsafe(*this); LSI->CallOperator->setConstexprKind(DS.getConstexprSpecifier()); @@ -1414,6 +1414,7 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro, CXXRecordDecl *Class = LSI->Lambda; CXXMethodDecl *Method = LSI->CallOperator; + Method->setContracts(ParamInfo.Contracts); TypeSourceInfo *MethodTyInfo = getLambdaType( *this, Intro, ParamInfo, getCurScope(), TypeLoc, ExplicitResultType); @@ -1951,6 +1952,9 @@ ExprResult Sema::BuildCaptureInit(const Capture &Cap, ExprResult Sema::ActOnLambdaExpr(SourceLocation StartLoc, Stmt *Body) { LambdaScopeInfo LSI = *cast(FunctionScopes.back()); ActOnFinishFunctionBody(LSI.CallOperator, Body); + + maybeAddDeclWithEffects(LSI.CallOperator); + return BuildLambdaExpr(StartLoc, Body->getEndLoc(), &LSI); } @@ -2350,7 +2354,10 @@ ExprResult Sema::BuildBlockForLambdaConversion(SourceLocation CurrentLocation, Block->setBody(new (Context) CompoundStmt(ConvLocation)); // Create the block literal expression. - Expr *BuildBlock = new (Context) BlockExpr(Block, Conv->getConversionType()); + // TODO: Do we ever get here if we have unexpanded packs in the lambda??? + Expr *BuildBlock = + new (Context) BlockExpr(Block, Conv->getConversionType(), + /*ContainsUnexpandedParameterPack=*/false); ExprCleanupObjects.push_back(Block); Cleanup.setExprNeedsCleanups(true); diff --git a/clang/lib/Sema/SemaOpenACC.cpp b/clang/lib/Sema/SemaOpenACC.cpp index 47b4bd77d86d1..30d73d621db69 100644 --- a/clang/lib/Sema/SemaOpenACC.cpp +++ b/clang/lib/Sema/SemaOpenACC.cpp @@ -354,6 +354,30 @@ bool doesClauseApplyToDirective(OpenACCDirectiveKind DirectiveKind, return false; } } + case OpenACCClauseKind::Tile: { + switch (DirectiveKind) { + case OpenACCDirectiveKind::Loop: + case OpenACCDirectiveKind::ParallelLoop: + case OpenACCDirectiveKind::SerialLoop: + case OpenACCDirectiveKind::KernelsLoop: + return true; + default: + return false; + } + } + + case OpenACCClauseKind::Gang: { + switch (DirectiveKind) { + case OpenACCDirectiveKind::Loop: + case OpenACCDirectiveKind::ParallelLoop: + case OpenACCDirectiveKind::SerialLoop: + case OpenACCDirectiveKind::KernelsLoop: + case OpenACCDirectiveKind::Routine: + return true; + default: + return false; + } + } default: // Do nothing so we can go to the 'unimplemented' diagnostic instead. @@ -448,6 +472,23 @@ class SemaOpenACCClauseVisitor { return nullptr; } + // OpenACC 3.3 2.9: + // A 'gang', 'worker', or 'vector' clause may not appear if a 'seq' clause + // appears. + bool DiagIfSeqClause(SemaOpenACC::OpenACCParsedClause &Clause) { + const auto *Itr = + llvm::find_if(ExistingClauses, llvm::IsaPred); + + if (Itr != ExistingClauses.end()) { + SemaRef.Diag(Clause.getBeginLoc(), diag::err_acc_clause_cannot_combine) + << Clause.getClauseKind() << (*Itr)->getClauseKind(); + SemaRef.Diag((*Itr)->getBeginLoc(), diag::note_acc_previous_clause_here); + + return true; + } + return false; + } + public: SemaOpenACCClauseVisitor(SemaOpenACC &S, ArrayRef ExistingClauses) @@ -459,26 +500,14 @@ class SemaOpenACCClauseVisitor { OpenACCClause *Visit(SemaOpenACC::OpenACCParsedClause &Clause) { switch (Clause.getClauseKind()) { - case OpenACCClauseKind::Gang: - case OpenACCClauseKind::Worker: - case OpenACCClauseKind::Vector: { - // TODO OpenACC: These are only implemented enough for the 'seq' diagnostic, - // otherwise treats itself as unimplemented. When we implement these, we - // can remove them from here. - - // OpenACC 3.3 2.9: - // A 'gang', 'worker', or 'vector' clause may not appear if a 'seq' clause - // appears. - const auto *Itr = - llvm::find_if(ExistingClauses, llvm::IsaPred); - - if (Itr != ExistingClauses.end()) { - SemaRef.Diag(Clause.getBeginLoc(), diag::err_acc_clause_cannot_combine) - << Clause.getClauseKind() << (*Itr)->getClauseKind(); - SemaRef.Diag((*Itr)->getBeginLoc(), diag::note_acc_previous_clause_here); + case OpenACCClauseKind::Worker: + case OpenACCClauseKind::Vector: { + // TODO OpenACC: These are only implemented enough for the 'seq' + // diagnostic, otherwise treats itself as unimplemented. When we + // implement these, we can remove them from here. + DiagIfSeqClause(Clause); + return isNotImplemented(); } - return isNotImplemented(); - } #define VISIT_CLAUSE(CLAUSE_NAME) \ case OpenACCClauseKind::CLAUSE_NAME: \ @@ -526,6 +555,36 @@ OpenACCClause *SemaOpenACCClauseVisitor::VisitDefaultClause( Clause.getLParenLoc(), Clause.getEndLoc()); } +OpenACCClause *SemaOpenACCClauseVisitor::VisitTileClause( + SemaOpenACC::OpenACCParsedClause &Clause) { + if (Clause.getDirectiveKind() != OpenACCDirectiveKind::Loop) + return isNotImplemented(); + + // Duplicates here are not really sensible. We could possible permit + // multiples if they all had the same value, but there isn't really a good + // reason to do so. Also, this simplifies the suppression of duplicates, in + // that we know if we 'find' one after instantiation, that it is the same + // clause, which simplifies instantiation/checking/etc. + if (checkAlreadyHasClauseOfKind(SemaRef, ExistingClauses, Clause)) + return nullptr; + + llvm::SmallVector NewSizeExprs; + + // Make sure these are all positive constant expressions or *. + for (Expr *E : Clause.getIntExprs()) { + ExprResult Res = SemaRef.CheckTileSizeExpr(E); + + if (!Res.isUsable()) + return nullptr; + + NewSizeExprs.push_back(Res.get()); + } + + return OpenACCTileClause::Create(Ctx, Clause.getBeginLoc(), + Clause.getLParenLoc(), NewSizeExprs, + Clause.getEndLoc()); +} + OpenACCClause *SemaOpenACCClauseVisitor::VisitIfClause( SemaOpenACC::OpenACCParsedClause &Clause) { // Restrictions only properly implemented on 'compute' constructs, and @@ -965,6 +1024,84 @@ OpenACCClause *SemaOpenACCClauseVisitor::VisitIndependentClause( Clause.getEndLoc()); } +OpenACCClause *SemaOpenACCClauseVisitor::VisitGangClause( + SemaOpenACC::OpenACCParsedClause &Clause) { + if (DiagIfSeqClause(Clause)) + return nullptr; + + // Restrictions only properly implemented on 'loop' constructs, and it is + // the only construct that can do anything with this, so skip/treat as + // unimplemented for the combined constructs. + if (Clause.getDirectiveKind() != OpenACCDirectiveKind::Loop) + return isNotImplemented(); + + llvm::SmallVector GangKinds; + llvm::SmallVector IntExprs; + + // Store the existing locations, so we can do duplicate checking. Index is + // the int-value of the OpenACCGangKind enum. + SourceLocation ExistingElemLoc[3]; + + for (unsigned I = 0; I < Clause.getIntExprs().size(); ++I) { + OpenACCGangKind GK = Clause.getGangKinds()[I]; + ExprResult ER = SemaRef.CheckGangExpr(GK, Clause.getIntExprs()[I]); + + if (!ER.isUsable()) + continue; + + // OpenACC 3.3 2.9.2: When the parent compute construct is a kernels + // construct, the gang clause behaves as follows. ... An argument with no + // keyword or with num keyword is only allowed when num_gangs does not + // appear on the kernels construct. + if (SemaRef.getActiveComputeConstructInfo().Kind == + OpenACCDirectiveKind::Kernels && + GK == OpenACCGangKind::Num) { + const auto *Itr = + llvm::find_if(SemaRef.getActiveComputeConstructInfo().Clauses, + llvm::IsaPred); + + if (Itr != SemaRef.getActiveComputeConstructInfo().Clauses.end()) { + SemaRef.Diag(ER.get()->getBeginLoc(), + diag::err_acc_gang_num_gangs_conflict); + SemaRef.Diag((*Itr)->getBeginLoc(), + diag::note_acc_previous_clause_here); + continue; + } + } + + // OpenACC 3.3 2.9: 'gang-arg-list' may have at most one num, one dim, and + // one static argument. + if (ExistingElemLoc[static_cast(GK)].isValid()) { + SemaRef.Diag(ER.get()->getBeginLoc(), diag::err_acc_gang_multiple_elt) + << static_cast(GK); + SemaRef.Diag(ExistingElemLoc[static_cast(GK)], + diag::note_acc_previous_expr_here); + continue; + } + + ExistingElemLoc[static_cast(GK)] = ER.get()->getBeginLoc(); + GangKinds.push_back(GK); + IntExprs.push_back(ER.get()); + } + + // OpenACC 3.3 2.9.2: When the parent compute construct is a kernels + // construct, the gang clause behaves as follows. ... The region of a loop + // with a gang clause may not contain another loop with a gang clause unless + // within a nested compute region. + if (SemaRef.LoopGangClauseOnKernelLoc.isValid()) { + // This handles the 'inner loop' diagnostic, but we cannot set that we're on + // one of these until we get to the end of the construct. + SemaRef.Diag(Clause.getBeginLoc(), diag::err_acc_gang_inside_gang); + SemaRef.Diag(SemaRef.LoopGangClauseOnKernelLoc, + diag::note_acc_previous_clause_here); + return nullptr; + } + + return OpenACCGangClause::Create(Ctx, Clause.getBeginLoc(), + Clause.getLParenLoc(), GangKinds, IntExprs, + Clause.getEndLoc()); +} + OpenACCClause *SemaOpenACCClauseVisitor::VisitSeqClause( SemaOpenACC::OpenACCParsedClause &Clause) { // Restrictions only properly implemented on 'loop' constructs, and it is @@ -1077,16 +1214,44 @@ SemaOpenACC::AssociatedStmtRAII::AssociatedStmtRAII( SemaOpenACC &S, OpenACCDirectiveKind DK, ArrayRef UnInstClauses, ArrayRef Clauses) - : SemaRef(S), WasInsideComputeConstruct(S.InsideComputeConstruct), - DirKind(DK), LoopRAII(SemaRef, /*PreserveDepth=*/false) { + : SemaRef(S), OldActiveComputeConstructInfo(S.ActiveComputeConstructInfo), + DirKind(DK), OldLoopGangClauseOnKernelLoc(S.LoopGangClauseOnKernelLoc), + LoopRAII(SemaRef, /*PreserveDepth=*/false) { // Compute constructs end up taking their 'loop'. if (DirKind == OpenACCDirectiveKind::Parallel || DirKind == OpenACCDirectiveKind::Serial || DirKind == OpenACCDirectiveKind::Kernels) { - SemaRef.InsideComputeConstruct = true; + SemaRef.ActiveComputeConstructInfo.Kind = DirKind; + SemaRef.ActiveComputeConstructInfo.Clauses = Clauses; SemaRef.ParentlessLoopConstructs.swap(ParentlessLoopConstructs); + + // OpenACC 3.3 2.9.2: When the parent compute construct is a kernels + // construct, the gang clause behaves as follows. ... The region of a loop + // with a gang clause may not contain another loop with a gang clause unless + // within a nested compute region. + // + // Implement the 'unless within a nested compute region' part. + SemaRef.LoopGangClauseOnKernelLoc = {}; } else if (DirKind == OpenACCDirectiveKind::Loop) { SetCollapseInfoBeforeAssociatedStmt(UnInstClauses, Clauses); + SetTileInfoBeforeAssociatedStmt(UnInstClauses, Clauses); + + // OpenACC 3.3 2.9.2: When the parent compute construct is a kernels + // construct, the gang clause behaves as follows. ... The region of a loop + // with a gang clause may not contain another loop with a gang clause unless + // within a nested compute region. + // + // We don't bother doing this when this is a template instantiation, as + // there is no reason to do these checks: the existance of a + // gang/kernels/etc cannot be dependent. + if (SemaRef.getActiveComputeConstructInfo().Kind == + OpenACCDirectiveKind::Kernels && + UnInstClauses.empty()) { + // This handles the 'outer loop' part of this. + auto *Itr = llvm::find_if(Clauses, llvm::IsaPred); + if (Itr != Clauses.end()) + SemaRef.LoopGangClauseOnKernelLoc = (*Itr)->getBeginLoc(); + } } } @@ -1095,8 +1260,9 @@ void SemaOpenACC::AssociatedStmtRAII::SetCollapseInfoBeforeAssociatedStmt( ArrayRef Clauses) { // Reset this checking for loops that aren't covered in a RAII object. - SemaRef.CollapseInfo.CurLevelHasLoopAlready = false; + SemaRef.LoopInfo.CurLevelHasLoopAlready = false; SemaRef.CollapseInfo.CollapseDepthSatisfied = true; + SemaRef.TileInfo.TileDepthSatisfied = true; // We make sure to take an optional list of uninstantiated clauses, so that // we can check to make sure we don't 'double diagnose' in the event that @@ -1135,8 +1301,30 @@ void SemaOpenACC::AssociatedStmtRAII::SetCollapseInfoBeforeAssociatedStmt( cast(LoopCount)->getResultAsAPSInt(); } +void SemaOpenACC::AssociatedStmtRAII::SetTileInfoBeforeAssociatedStmt( + ArrayRef UnInstClauses, + ArrayRef Clauses) { + // We don't diagnose if this is during instantiation, since the only thing we + // care about is the number of arguments, which we can figure out without + // instantiation, so we don't want to double-diagnose. + if (UnInstClauses.size() > 0) + return; + auto *TileClauseItr = + llvm::find_if(Clauses, llvm::IsaPred); + + if (Clauses.end() == TileClauseItr) + return; + + OpenACCTileClause *TileClause = cast(*TileClauseItr); + SemaRef.TileInfo.ActiveTile = TileClause; + SemaRef.TileInfo.TileDepthSatisfied = false; + SemaRef.TileInfo.CurTileCount = TileClause->getSizeExprs().size(); +} + SemaOpenACC::AssociatedStmtRAII::~AssociatedStmtRAII() { - SemaRef.InsideComputeConstruct = WasInsideComputeConstruct; + SemaRef.ActiveComputeConstructInfo = OldActiveComputeConstructInfo; + SemaRef.LoopGangClauseOnKernelLoc = OldLoopGangClauseOnKernelLoc; + if (DirKind == OpenACCDirectiveKind::Parallel || DirKind == OpenACCDirectiveKind::Serial || DirKind == OpenACCDirectiveKind::Kernels) { @@ -1698,42 +1886,201 @@ ExprResult SemaOpenACC::CheckCollapseLoopCount(Expr *LoopCount) { ConstantExpr::Create(getASTContext(), LoopCount, APValue{*ICE})}; } +namespace { +ExprResult CheckGangStaticExpr(SemaOpenACC &S, Expr *E) { + if (isa(E)) + return E; + return S.ActOnIntExpr(OpenACCDirectiveKind::Invalid, OpenACCClauseKind::Gang, + E->getBeginLoc(), E); +} +} // namespace + +ExprResult SemaOpenACC::CheckGangExpr(OpenACCGangKind GK, Expr *E) { + // Gang Expr legality depends on the associated compute construct. + switch (ActiveComputeConstructInfo.Kind) { + case OpenACCDirectiveKind::Invalid: + case OpenACCDirectiveKind::Parallel: { + switch (GK) { + // OpenACC 3.3 2.9.2: When the parent compute construct is a parallel + // construct, or an orphaned loop construct, the gang clause behaves as + // follows. ... The dim argument must be a constant positive integer value + // 1, 2, or 3. + case OpenACCGangKind::Dim: { + if (!E) + return ExprError(); + ExprResult Res = + ActOnIntExpr(OpenACCDirectiveKind::Invalid, OpenACCClauseKind::Gang, + E->getBeginLoc(), E); + + if (!Res.isUsable()) + return Res; + + if (Res.get()->isInstantiationDependent()) + return Res; + + std::optional ICE = + Res.get()->getIntegerConstantExpr(getASTContext()); + + if (!ICE || *ICE <= 0 || ICE > 3) { + Diag(Res.get()->getBeginLoc(), diag::err_acc_gang_dim_value) + << ICE.has_value() << ICE.value_or(llvm::APSInt{}).getExtValue(); + return ExprError(); + } + + return ExprResult{ + ConstantExpr::Create(getASTContext(), Res.get(), APValue{*ICE})}; + } + // OpenACC 3.3 2.9.2: When the parent compute construct is a parallel + // construct, or an orphaned loop construct, the gang clause behaves as + // follows. ... The num argument is not allowed. + case OpenACCGangKind::Num: + Diag(E->getBeginLoc(), diag::err_acc_gang_arg_invalid) + << GK + << (/*orphan/parallel=*/ActiveComputeConstructInfo.Kind == + OpenACCDirectiveKind::Parallel + ? 1 + : 0); + return ExprError(); + case OpenACCGangKind::Static: + return CheckGangStaticExpr(*this, E); + } + } break; + case OpenACCDirectiveKind::Kernels: { + switch (GK) { + // OpenACC 3.3 2.9.2: When the parent compute construct is a kernels + // construct, the gang clause behaves as follows. ... The dim argument is + // not allowed. + case OpenACCGangKind::Dim: + Diag(E->getBeginLoc(), diag::err_acc_gang_arg_invalid) + << GK << /*kernels=*/2; + return ExprError(); + // OpenACC 3.3 2.9.2: When the parent compute construct is a kernels + // construct, the gang clause behaves as follows. ... An argument with no + // keyword or with num keyword is only allowed when num_gangs does not + // appear on the kernels construct. ... The region of a loop with the gang + // clause may not contain another loop with a gang clause unless within a + // nested compute region. + case OpenACCGangKind::Num: + // This isn't allowed if there is a 'num_gangs' on the kernel construct, + // and makes loop-with-gang-clause ill-formed inside of this 'loop', but + // nothing can be enforced here. + return ExprResult{E}; + case OpenACCGangKind::Static: + return CheckGangStaticExpr(*this, E); + } + } break; + case OpenACCDirectiveKind::Serial: { + switch (GK) { + // 'dim' and 'num' don't really make sense on serial, and GCC rejects them + // too, so we disallow them too. + case OpenACCGangKind::Dim: + case OpenACCGangKind::Num: + Diag(E->getBeginLoc(), diag::err_acc_gang_arg_invalid) + << GK << /*Kernels=*/3; + return ExprError(); + case OpenACCGangKind::Static: + return CheckGangStaticExpr(*this, E); + } + } + default: + llvm_unreachable("Non compute construct in active compute construct?"); + } + + llvm_unreachable("Compute construct directive not handled?"); +} + +ExprResult SemaOpenACC::CheckTileSizeExpr(Expr *SizeExpr) { + if (!SizeExpr) + return ExprError(); + + assert((SizeExpr->isInstantiationDependent() || + SizeExpr->getType()->isIntegerType()) && + "size argument non integer?"); + + // If dependent, or an asterisk, the expression is fine. + if (SizeExpr->isInstantiationDependent() || + isa(SizeExpr)) + return ExprResult{SizeExpr}; + + std::optional ICE = + SizeExpr->getIntegerConstantExpr(getASTContext()); + + // OpenACC 3.3 2.9.8 + // where each tile size is a constant positive integer expression or asterisk. + if (!ICE || *ICE <= 0) { + Diag(SizeExpr->getBeginLoc(), diag::err_acc_size_expr_value) + << ICE.has_value() << ICE.value_or(llvm::APSInt{}).getExtValue(); + return ExprError(); + } + + return ExprResult{ + ConstantExpr::Create(getASTContext(), SizeExpr, APValue{*ICE})}; +} + void SemaOpenACC::ActOnWhileStmt(SourceLocation WhileLoc) { if (!getLangOpts().OpenACC) return; - if (!CollapseInfo.TopLevelLoopSeen) + if (!LoopInfo.TopLevelLoopSeen) return; if (CollapseInfo.CurCollapseCount && *CollapseInfo.CurCollapseCount > 0) { - Diag(WhileLoc, diag::err_acc_invalid_in_collapse_loop) << /*while loop*/ 1; + Diag(WhileLoc, diag::err_acc_invalid_in_loop) + << /*while loop*/ 1 << OpenACCClauseKind::Collapse; assert(CollapseInfo.ActiveCollapse && "Collapse count without object?"); Diag(CollapseInfo.ActiveCollapse->getBeginLoc(), - diag::note_acc_collapse_clause_here); + diag::note_acc_active_clause_here) + << OpenACCClauseKind::Collapse; // Remove the value so that we don't get cascading errors in the body. The // caller RAII object will restore this. CollapseInfo.CurCollapseCount = std::nullopt; } + + if (TileInfo.CurTileCount && *TileInfo.CurTileCount > 0) { + Diag(WhileLoc, diag::err_acc_invalid_in_loop) + << /*while loop*/ 1 << OpenACCClauseKind::Tile; + assert(TileInfo.ActiveTile && "tile count without object?"); + Diag(TileInfo.ActiveTile->getBeginLoc(), diag::note_acc_active_clause_here) + << OpenACCClauseKind::Tile; + + // Remove the value so that we don't get cascading errors in the body. The + // caller RAII object will restore this. + TileInfo.CurTileCount = std::nullopt; + } } void SemaOpenACC::ActOnDoStmt(SourceLocation DoLoc) { if (!getLangOpts().OpenACC) return; - if (!CollapseInfo.TopLevelLoopSeen) + if (!LoopInfo.TopLevelLoopSeen) return; if (CollapseInfo.CurCollapseCount && *CollapseInfo.CurCollapseCount > 0) { - Diag(DoLoc, diag::err_acc_invalid_in_collapse_loop) << /*do loop*/ 2; + Diag(DoLoc, diag::err_acc_invalid_in_loop) + << /*do loop*/ 2 << OpenACCClauseKind::Collapse; assert(CollapseInfo.ActiveCollapse && "Collapse count without object?"); Diag(CollapseInfo.ActiveCollapse->getBeginLoc(), - diag::note_acc_collapse_clause_here); + diag::note_acc_active_clause_here) + << OpenACCClauseKind::Collapse; // Remove the value so that we don't get cascading errors in the body. The // caller RAII object will restore this. CollapseInfo.CurCollapseCount = std::nullopt; } + + if (TileInfo.CurTileCount && *TileInfo.CurTileCount > 0) { + Diag(DoLoc, diag::err_acc_invalid_in_loop) + << /*do loop*/ 2 << OpenACCClauseKind::Tile; + assert(TileInfo.ActiveTile && "tile count without object?"); + Diag(TileInfo.ActiveTile->getBeginLoc(), diag::note_acc_active_clause_here) + << OpenACCClauseKind::Tile; + + // Remove the value so that we don't get cascading errors in the body. The + // caller RAII object will restore this. + TileInfo.CurTileCount = std::nullopt; + } } void SemaOpenACC::ActOnForStmtBegin(SourceLocation ForLoc) { @@ -1741,7 +2088,7 @@ void SemaOpenACC::ActOnForStmtBegin(SourceLocation ForLoc) { return; // Enable the while/do-while checking. - CollapseInfo.TopLevelLoopSeen = true; + LoopInfo.TopLevelLoopSeen = true; if (CollapseInfo.CurCollapseCount && *CollapseInfo.CurCollapseCount > 0) { @@ -1750,11 +2097,12 @@ void SemaOpenACC::ActOnForStmtBegin(SourceLocation ForLoc) { // or loop nest. // This checks for more than 1 loop at the current level, the // 'depth'-satisifed checking manages the 'not zero' case. - if (CollapseInfo.CurLevelHasLoopAlready) { - Diag(ForLoc, diag::err_acc_collapse_multiple_loops); + if (LoopInfo.CurLevelHasLoopAlready) { + Diag(ForLoc, diag::err_acc_clause_multiple_loops) << /*Collapse*/ 0; assert(CollapseInfo.ActiveCollapse && "No collapse object?"); Diag(CollapseInfo.ActiveCollapse->getBeginLoc(), - diag::note_acc_collapse_clause_here); + diag::note_acc_active_clause_here) + << OpenACCClauseKind::Collapse; } else { --(*CollapseInfo.CurCollapseCount); @@ -1765,16 +2113,90 @@ void SemaOpenACC::ActOnForStmtBegin(SourceLocation ForLoc) { } } + if (TileInfo.CurTileCount && *TileInfo.CurTileCount > 0) { + if (LoopInfo.CurLevelHasLoopAlready) { + Diag(ForLoc, diag::err_acc_clause_multiple_loops) << /*Tile*/ 1; + assert(TileInfo.ActiveTile && "No tile object?"); + Diag(TileInfo.ActiveTile->getBeginLoc(), + diag::note_acc_active_clause_here) + << OpenACCClauseKind::Tile; + } else { + --(*TileInfo.CurTileCount); + // Once we've hit zero here, we know we have deep enough 'for' loops to + // get to the bottom. + if (*TileInfo.CurTileCount == 0) + TileInfo.TileDepthSatisfied = true; + } + } + // Set this to 'false' for the body of this loop, so that the next level // checks independently. - CollapseInfo.CurLevelHasLoopAlready = false; + LoopInfo.CurLevelHasLoopAlready = false; +} + +namespace { +SourceLocation FindInterveningCodeInLoop(const Stmt *CurStmt) { + // We should diagnose on anything except `CompoundStmt`, `NullStmt`, + // `ForStmt`, `CXXForRangeStmt`, since those are legal, and `WhileStmt` and + // `DoStmt`, as those are caught as a violation elsewhere. + // For `CompoundStmt` we need to search inside of it. + if (!CurStmt || + isa( + CurStmt)) + return SourceLocation{}; + + // Any other construct is an error anyway, so it has already been diagnosed. + if (isa(CurStmt)) + return SourceLocation{}; + + // Search inside the compound statement, this allows for arbitrary nesting + // of compound statements, as long as there isn't any code inside. + if (const auto *CS = dyn_cast(CurStmt)) { + for (const auto *ChildStmt : CS->children()) { + SourceLocation ChildStmtLoc = FindInterveningCodeInLoop(ChildStmt); + if (ChildStmtLoc.isValid()) + return ChildStmtLoc; + } + // Empty/not invalid compound statements are legal. + return SourceLocation{}; + } + return CurStmt->getBeginLoc(); } +} // namespace void SemaOpenACC::ActOnForStmtEnd(SourceLocation ForLoc, StmtResult Body) { if (!getLangOpts().OpenACC) return; // Set this to 'true' so if we find another one at this level we can diagnose. - CollapseInfo.CurLevelHasLoopAlready = true; + LoopInfo.CurLevelHasLoopAlready = true; + + if (!Body.isUsable()) + return; + + bool IsActiveCollapse = CollapseInfo.CurCollapseCount && + *CollapseInfo.CurCollapseCount > 0 && + !CollapseInfo.ActiveCollapse->hasForce(); + bool IsActiveTile = TileInfo.CurTileCount && *TileInfo.CurTileCount > 0; + + if (IsActiveCollapse || IsActiveTile) { + SourceLocation OtherStmtLoc = FindInterveningCodeInLoop(Body.get()); + + if (OtherStmtLoc.isValid() && IsActiveCollapse) { + Diag(OtherStmtLoc, diag::err_acc_intervening_code) + << OpenACCClauseKind::Collapse; + Diag(CollapseInfo.ActiveCollapse->getBeginLoc(), + diag::note_acc_active_clause_here) + << OpenACCClauseKind::Collapse; + } + + if (OtherStmtLoc.isValid() && IsActiveTile) { + Diag(OtherStmtLoc, diag::err_acc_intervening_code) + << OpenACCClauseKind::Tile; + Diag(TileInfo.ActiveTile->getBeginLoc(), + diag::note_acc_active_clause_here) + << OpenACCClauseKind::Tile; + } + } } bool SemaOpenACC::ActOnStartStmtDirective(OpenACCDirectiveKind K, @@ -1788,11 +2210,19 @@ bool SemaOpenACC::ActOnStartStmtDirective(OpenACCDirectiveKind K, // // ALL constructs are ill-formed if there is an active 'collapse' if (CollapseInfo.CurCollapseCount && *CollapseInfo.CurCollapseCount > 0) { - Diag(StartLoc, diag::err_acc_invalid_in_collapse_loop) - << /*OpenACC Construct*/ 0 << K; + Diag(StartLoc, diag::err_acc_invalid_in_loop) + << /*OpenACC Construct*/ 0 << OpenACCClauseKind::Collapse << K; assert(CollapseInfo.ActiveCollapse && "Collapse count without object?"); Diag(CollapseInfo.ActiveCollapse->getBeginLoc(), - diag::note_acc_collapse_clause_here); + diag::note_acc_active_clause_here) + << OpenACCClauseKind::Collapse; + } + if (TileInfo.CurTileCount && *TileInfo.CurTileCount > 0) { + Diag(StartLoc, diag::err_acc_invalid_in_loop) + << /*OpenACC Construct*/ 0 << OpenACCClauseKind::Tile << K; + assert(TileInfo.ActiveTile && "Tile count without object?"); + Diag(TileInfo.ActiveTile->getBeginLoc(), diag::note_acc_active_clause_here) + << OpenACCClauseKind::Tile; } return diagnoseConstructAppertainment(*this, K, StartLoc, /*IsStmt=*/true); @@ -1829,7 +2259,7 @@ StmtResult SemaOpenACC::ActOnEndStmtDirective(OpenACCDirectiveKind K, // If we are in the scope of a compute construct, add this to the list of // loop constructs that need assigning to the next closing compute // construct. - if (InsideComputeConstruct) + if (isInComputeConstruct()) ParentlessLoopConstructs.push_back(LoopConstruct); return LoopConstruct; @@ -1867,11 +2297,24 @@ StmtResult SemaOpenACC::ActOnAssociatedStmt(SourceLocation DirectiveLoc, return StmtError(); } - if (!CollapseInfo.CollapseDepthSatisfied) { - Diag(DirectiveLoc, diag::err_acc_collapse_insufficient_loops); - assert(CollapseInfo.ActiveCollapse && "Collapse count without object?"); - Diag(CollapseInfo.ActiveCollapse->getBeginLoc(), - diag::note_acc_collapse_clause_here); + if (!CollapseInfo.CollapseDepthSatisfied || !TileInfo.TileDepthSatisfied) { + if (!CollapseInfo.CollapseDepthSatisfied) { + Diag(DirectiveLoc, diag::err_acc_insufficient_loops) + << OpenACCClauseKind::Collapse; + assert(CollapseInfo.ActiveCollapse && "Collapse count without object?"); + Diag(CollapseInfo.ActiveCollapse->getBeginLoc(), + diag::note_acc_active_clause_here) + << OpenACCClauseKind::Collapse; + } + + if (!TileInfo.TileDepthSatisfied) { + Diag(DirectiveLoc, diag::err_acc_insufficient_loops) + << OpenACCClauseKind::Tile; + assert(TileInfo.ActiveTile && "Collapse count without object?"); + Diag(TileInfo.ActiveTile->getBeginLoc(), + diag::note_acc_active_clause_here) + << OpenACCClauseKind::Tile; + } return StmtError(); } @@ -1894,3 +2337,13 @@ bool SemaOpenACC::ActOnStartDeclDirective(OpenACCDirectiveKind K, } DeclGroupRef SemaOpenACC::ActOnEndDeclDirective() { return DeclGroupRef{}; } + +ExprResult +SemaOpenACC::BuildOpenACCAsteriskSizeExpr(SourceLocation AsteriskLoc) { + return OpenACCAsteriskSizeExpr::Create(getASTContext(), AsteriskLoc); +} + +ExprResult +SemaOpenACC::ActOnOpenACCAsteriskSizeExpr(SourceLocation AsteriskLoc) { + return BuildOpenACCAsteriskSizeExpr(AsteriskLoc); +} diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp index 8615da4b044a8..d3e696a79b94f 100644 --- a/clang/lib/Sema/SemaOpenMP.cpp +++ b/clang/lib/Sema/SemaOpenMP.cpp @@ -1405,10 +1405,9 @@ const Expr *DSAStackTy::addUniqueAligned(const ValueDecl *D, assert(!isStackEmpty() && "Data sharing attributes stack is empty"); D = getCanonicalDecl(D); SharingMapTy &StackElem = getTopOfStack(); - auto It = StackElem.AlignedMap.find(D); - if (It == StackElem.AlignedMap.end()) { + auto [It, Inserted] = StackElem.AlignedMap.try_emplace(D, NewDE); + if (Inserted) { assert(NewDE && "Unexpected nullptr expr to be added into aligned map"); - StackElem.AlignedMap[D] = NewDE; return nullptr; } assert(It->second && "Unexpected nullptr expr in the aligned map"); @@ -1420,10 +1419,9 @@ const Expr *DSAStackTy::addUniqueNontemporal(const ValueDecl *D, assert(!isStackEmpty() && "Data sharing attributes stack is empty"); D = getCanonicalDecl(D); SharingMapTy &StackElem = getTopOfStack(); - auto It = StackElem.NontemporalMap.find(D); - if (It == StackElem.NontemporalMap.end()) { + auto [It, Inserted] = StackElem.NontemporalMap.try_emplace(D, NewDE); + if (Inserted) { assert(NewDE && "Unexpected nullptr expr to be added into aligned map"); - StackElem.NontemporalMap[D] = NewDE; return nullptr; } assert(It->second && "Unexpected nullptr expr in the aligned map"); @@ -14958,7 +14956,9 @@ StmtResult SemaOpenMP::ActOnOpenMPInterchangeDirective( return StmtError(); // interchange without permutation clause swaps two loops. - constexpr size_t NumLoops = 2; + const OMPPermutationClause *PermutationClause = + OMPExecutableDirective::getSingleClause(Clauses); + size_t NumLoops = PermutationClause ? PermutationClause->getNumLoops() : 2; // Verify and diagnose loop nest. SmallVector LoopHelpers(NumLoops); @@ -14973,6 +14973,12 @@ StmtResult SemaOpenMP::ActOnOpenMPInterchangeDirective( return OMPInterchangeDirective::Create(Context, StartLoc, EndLoc, Clauses, NumLoops, AStmt, nullptr, nullptr); + // An invalid expression in the permutation clause is set to nullptr in + // ActOnOpenMPPermutationClause. + if (PermutationClause && + llvm::is_contained(PermutationClause->getArgsRefs(), nullptr)) + return StmtError(); + assert(LoopHelpers.size() == NumLoops && "Expecting loop iteration space dimensionaly to match number of " "affected loops"); @@ -14981,7 +14987,44 @@ StmtResult SemaOpenMP::ActOnOpenMPInterchangeDirective( "affected loops"); // Decode the permutation clause. - constexpr uint64_t Permutation[] = {1, 0}; + SmallVector Permutation; + if (!PermutationClause) { + Permutation = {1, 0}; + } else { + ArrayRef PermArgs = PermutationClause->getArgsRefs(); + llvm::BitVector Flags(PermArgs.size()); + for (Expr *PermArg : PermArgs) { + std::optional PermCstExpr = + PermArg->getIntegerConstantExpr(Context); + if (!PermCstExpr) + continue; + uint64_t PermInt = PermCstExpr->getZExtValue(); + assert(1 <= PermInt && PermInt <= NumLoops && + "Must be a permutation; diagnostic emitted in " + "ActOnOpenMPPermutationClause"); + if (Flags[PermInt - 1]) { + SourceRange ExprRange(PermArg->getBeginLoc(), PermArg->getEndLoc()); + Diag(PermArg->getExprLoc(), + diag::err_omp_interchange_permutation_value_repeated) + << PermInt << ExprRange; + continue; + } + Flags[PermInt - 1] = true; + + Permutation.push_back(PermInt - 1); + } + + if (Permutation.size() != NumLoops) + return StmtError(); + } + + // Nothing to transform with trivial permutation. + if (NumLoops <= 1 || llvm::all_of(llvm::enumerate(Permutation), [](auto P) { + auto [Idx, Arg] = P; + return Idx == Arg; + })) + return OMPInterchangeDirective::Create(Context, StartLoc, EndLoc, Clauses, + NumLoops, AStmt, AStmt, nullptr); // Find the affected loops. SmallVector LoopStmts(NumLoops, nullptr); @@ -16113,6 +16156,44 @@ OMPClause *SemaOpenMP::ActOnOpenMPSizesClause(ArrayRef SizeExprs, SanitizedSizeExprs); } +OMPClause *SemaOpenMP::ActOnOpenMPPermutationClause(ArrayRef PermExprs, + SourceLocation StartLoc, + SourceLocation LParenLoc, + SourceLocation EndLoc) { + size_t NumLoops = PermExprs.size(); + SmallVector SanitizedPermExprs; + llvm::append_range(SanitizedPermExprs, PermExprs); + + for (Expr *&PermExpr : SanitizedPermExprs) { + // Skip if template-dependent or already sanitized, e.g. during a partial + // template instantiation. + if (!PermExpr || PermExpr->isInstantiationDependent()) + continue; + + llvm::APSInt PermVal; + ExprResult PermEvalExpr = SemaRef.VerifyIntegerConstantExpression( + PermExpr, &PermVal, Sema::AllowFold); + bool IsValid = PermEvalExpr.isUsable(); + if (IsValid) + PermExpr = PermEvalExpr.get(); + + if (IsValid && (PermVal < 1 || NumLoops < PermVal)) { + SourceRange ExprRange(PermEvalExpr.get()->getBeginLoc(), + PermEvalExpr.get()->getEndLoc()); + Diag(PermEvalExpr.get()->getExprLoc(), + diag::err_omp_interchange_permutation_value_range) + << NumLoops << ExprRange; + IsValid = false; + } + + if (!PermExpr->isInstantiationDependent() && !IsValid) + PermExpr = nullptr; + } + + return OMPPermutationClause::Create(getASTContext(), StartLoc, LParenLoc, + EndLoc, SanitizedPermExprs); +} + OMPClause *SemaOpenMP::ActOnOpenMPFullClause(SourceLocation StartLoc, SourceLocation EndLoc) { return OMPFullClause::Create(getASTContext(), StartLoc, EndLoc); @@ -21650,9 +21731,7 @@ SemaOpenMP::ActOnOpenMPDeclareReductionDirectiveStart( while (Filter.hasNext()) { auto *PrevDecl = cast(Filter.next()); if (InCompoundScope) { - auto I = UsedAsPrevious.find(PrevDecl); - if (I == UsedAsPrevious.end()) - UsedAsPrevious[PrevDecl] = false; + UsedAsPrevious.try_emplace(PrevDecl, false); if (OMPDeclareReductionDecl *D = PrevDecl->getPrevDeclInScope()) UsedAsPrevious[D] = true; } @@ -21906,9 +21985,7 @@ SemaOpenMP::DeclGroupPtrTy SemaOpenMP::ActOnOpenMPDeclareMapperDirective( while (Filter.hasNext()) { auto *PrevDecl = cast(Filter.next()); if (InCompoundScope) { - auto I = UsedAsPrevious.find(PrevDecl); - if (I == UsedAsPrevious.end()) - UsedAsPrevious[PrevDecl] = false; + UsedAsPrevious.try_emplace(PrevDecl, false); if (OMPDeclareMapperDecl *D = PrevDecl->getPrevDeclInScope()) UsedAsPrevious[D] = true; } diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index 2cde8131108fb..1c271b9e5d3da 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -5921,6 +5921,16 @@ ExprResult Sema::PerformImplicitObjectArgumentInitialization( << From->getSourceRange(); Diag(Method->getLocation(), diag::note_previous_decl) << Method->getDeclName(); + + // FIXME(EricWF): We might be here because we've made the 'this' type + // const because we're in a contract statement. But I don't know how do + // reliably check that here. + if (CurrentContractEntry && CurrentContractEntry->AddedConstToCXXThis) { + // If we're in a contract assertion context, we can't modify the + // 'this' type. So we'll just return an error here. + Diag(CurrentContractEntry->KeywordLoc, + diag::note_cxx_this_const_in_contract_introduced_here); + } return ExprError(); } break; diff --git a/clang/lib/Sema/SemaStmtAttr.cpp b/clang/lib/Sema/SemaStmtAttr.cpp index b9b3b4063bc38..f5ae1027b1b5c 100644 --- a/clang/lib/Sema/SemaStmtAttr.cpp +++ b/clang/lib/Sema/SemaStmtAttr.cpp @@ -12,6 +12,7 @@ #include "clang/AST/ASTContext.h" #include "clang/AST/EvaluatedExprVisitor.h" +#include "clang/Basic/ContractOptions.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/TargetInfo.h" #include "clang/Sema/DelayedDiagnostic.h" @@ -310,6 +311,164 @@ static Attr *handleNoInlineAttr(Sema &S, Stmt *St, const ParsedAttr &A, return ::new (S.Context) NoInlineAttr(S.Context, A); } +static bool isContractAttrIncompatible(const Attr *A1, const Attr *A2) { + switch (A1->getKind()) { + case attr::ContractGroup: + return isa(A2); + case attr::ContractSemantic: + return isa(A2); + default: + return false; + } +} + +static void +CheckForDuplicateAndIncompatibleContractAttrs(Sema &S, + ArrayRef Attrs) { + if (Attrs.size() <= 1) + return; + + SmallVector ContractAttrs(Attrs.begin(), Attrs.end()); + + // TODO(EricWF): We don't issue diagnostics for duplicate contract attributes + // in the order they appear in source. The order is still correct relative to + // duplicate attributes, but not for incompatible ones. + std::stable_sort( + ContractAttrs.begin(), ContractAttrs.end(), + [](const Attr *A, const Attr *B) { return A->getKind() < B->getKind(); }); + + for (unsigned I = 0; I + 1 < ContractAttrs.size(); ++I) { + const auto *A1 = ContractAttrs[I]; + const Attr *A2 = ContractAttrs[I + 1]; + while (A2 && A2->getKind() == A1->getKind()) { + S.Diag(A2->getLocation(), diag::err_disallowed_duplicate_attribute) + << A2 << 1; + S.Diag(A1->getLocation(), diag::note_previous_attribute); + ++I; + A2 = I + 1 < ContractAttrs.size() ? ContractAttrs[I + 1] : nullptr; + } + assert(!A2 || A2->getKind() != A1->getKind()); + } + + for (unsigned I = 0; I < Attrs.size(); ++I) { + for (unsigned J = I + 1; J < Attrs.size(); ++J) { + if (isContractAttrIncompatible(Attrs[I], Attrs[J])) { + S.Diag(Attrs[J]->getLocation(), + diag::err_disallow_incompatible_attribute) + << 1 << Attrs[J] << Attrs[I]; + S.Diag(Attrs[I]->getLocation(), diag::note_previous_attribute); + } + } + } +} + +static Attr *handleContractGroupAttr(Sema &S, Stmt *st, const ParsedAttr &A, + SourceRange Range) { + + assert(A.getNumArgs() == 1); + + StringRef GroupName; + assert(A.getArgAsExpr(0)); + if (!S.checkStringLiteralArgumentAttr(A, 0, GroupName, nullptr)) + return nullptr; + + auto EmitDiag = [&](ContractGroupDiagnostic CDK, StringRef Group, + StringRef InvalidRange = "") { + S.Diag(Range.getBegin(), diag::err_contract_group_attribute_invalid_group) + << (int)CDK << Group << InvalidRange + << A.getArgAsExpr(0)->getSourceRange(); + }; + + if (!ContractOptions::validateContractGroup(GroupName, EmitDiag)) + return nullptr; + + return ::new (S.Context) ContractGroupAttr(S.Context, A, GroupName); +} + +static std::optional checkBoolArg(Sema &S, const ParsedAttr &AL, + Expr *&Cond) { + ExprResult Converted = + S.CheckBooleanCondition(Cond->getBeginLoc(), Cond, /*IsConstexpr=*/true); + if (Converted.isInvalid()) + return std::nullopt; + Cond = Converted.get(); + + std::optional ArgVal = Cond->getIntegerConstantExpr(S.Context); + if (!ArgVal) { + S.Diag(Cond->getExprLoc(), diag::err_attribute_argument_type) + << AL << AANT_ArgumentIntegerConstant << Cond->getSourceRange(); + return std::nullopt; + } + return ArgVal->getBoolValue(); +} + +static Attr *handleContractSemanticAttr(Sema &S, Stmt *st, const ParsedAttr &A, + SourceRange Range) { + + assert(A.getNumArgs() == 1 || A.getNumArgs() == 2); + // Check that the argument is a string literal. + StringRef SemStr; + SourceLocation LiteralLoc; + if (!S.checkStringLiteralArgumentAttr(A, 0, SemStr, &LiteralLoc)) + return nullptr; + + ContractEvaluationSemantic Sem; + if (!ContractSemanticAttr::ConvertStrToContractEvaluationSemantic(SemStr, + Sem)) { + S.Diag(LiteralLoc, diag::warn_attribute_type_not_supported) << A << SemStr; + return nullptr; + } + + Expr *Cond = A.getNumArgs() == 2 ? A.getArgAsExpr(1) : nullptr; + std::optional CondValue = true; + if (Cond) + CondValue = checkBoolArg(S, A, Cond); + if (!CondValue) + return nullptr; + return ::new (S.Context) ContractSemanticAttr(S.Context, A, Sem, *CondValue); +} + +static Attr *handleContractEmissionAttr(Sema &S, Stmt *st, const ParsedAttr &A, + SourceRange Range) { + + assert(A.getNumArgs() == 1 || A.getNumArgs() == 2); + // Check that the argument is a string literal. + StringRef EmissionStr; + SourceLocation LiteralLoc; + if (!S.checkStringLiteralArgumentAttr(A, 0, EmissionStr, &LiteralLoc)) + return nullptr; + + ContractEmissionStyle Emission; + if (!ContractEmissionAttr::ConvertStrToContractEmissionStyle(EmissionStr, + Emission)) { + S.Diag(LiteralLoc, diag::warn_attribute_type_not_supported) + << A << EmissionStr; + return nullptr; + } + + return ::new (S.Context) ContractEmissionAttr(S.Context, A, Emission); +} + +static Attr *handleContractMessageAttr(Sema &S, Stmt *st, const ParsedAttr &A, + SourceRange Range) { + + assert(A.getNumArgs() == 1 || A.getNumArgs() == 2); + // Check that the argument is a string literal. + StringRef MessageStr; + SourceLocation LiteralLoc; + if (!S.checkStringLiteralArgumentAttr(A, 0, MessageStr, &LiteralLoc)) + return nullptr; + + Expr *Cond = A.getNumArgs() == 2 ? A.getArgAsExpr(1) : nullptr; + std::optional CondValue = true; + if (Cond) + CondValue = checkBoolArg(S, A, Cond); + if (!CondValue) + return nullptr; + return ::new (S.Context) + ContractMessageAttr(S.Context, A, MessageStr, *CondValue); +} + static Attr *handleAlwaysInlineAttr(Sema &S, Stmt *St, const ParsedAttr &A, SourceRange Range) { AlwaysInlineAttr AIA(S.Context, A); @@ -677,8 +836,18 @@ static Attr *ProcessStmtAttribute(Sema &S, Stmt *St, const ParsedAttr &A, return handleCodeAlignAttr(S, St, A); case ParsedAttr::AT_MSConstexpr: return handleMSConstexprAttr(S, St, A, Range); + case ParsedAttr::AT_ContractGroup: + return handleContractGroupAttr(S, St, A, Range); + case ParsedAttr::AT_ContractSemantic: + return handleContractSemanticAttr(S, St, A, Range); + case ParsedAttr::AT_ContractMessage: + return handleContractMessageAttr(S, St, A, Range); + case ParsedAttr::AT_ContractEmission: + return handleContractEmissionAttr(S, St, A, Range); case ParsedAttr::AT_NoConvergent: return handleNoConvergentAttr(S, St, A, Range); + case ParsedAttr::AT_Annotate: + return S.CreateAnnotationAttr(A); default: // N.B., ClangAttrEmitter.cpp emits a diagnostic helper that ensures a // declaration attribute is not written on a statement, but this code is @@ -698,6 +867,8 @@ void Sema::ProcessStmtAttributes(Stmt *S, const ParsedAttributes &InAttrs, CheckForIncompatibleAttributes(*this, OutAttrs); CheckForDuplicateLoopAttrs(*this, OutAttrs); + if (isa(S)) + CheckForDuplicateAndIncompatibleContractAttrs(*this, OutAttrs); } bool Sema::CheckRebuiltStmtAttributes(ArrayRef Attrs) { diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 226c1172a059d..294eb8e3353cd 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -1010,8 +1010,8 @@ NamedDecl *Sema::ActOnTypeParameter(Scope *S, bool Typename, Param->setAccess(AS_public); if (Param->isParameterPack()) - if (auto *LSI = getEnclosingLambda()) - LSI->LocalPacks.push_back(Param); + if (auto *CSI = getEnclosingLambdaOrBlock()) + CSI->LocalPacks.push_back(Param); if (ParamName) { maybeDiagnoseTemplateParameterShadow(*this, S, ParamNameLoc, ParamName); @@ -1542,8 +1542,8 @@ NamedDecl *Sema::ActOnNonTypeTemplateParameter(Scope *S, Declarator &D, Param->setInvalidDecl(); if (Param->isParameterPack()) - if (auto *LSI = getEnclosingLambda()) - LSI->LocalPacks.push_back(Param); + if (auto *CSI = getEnclosingLambdaOrBlock()) + CSI->LocalPacks.push_back(Param); if (ParamName) { maybeDiagnoseTemplateParameterShadow(*this, S, D.getIdentifierLoc(), @@ -1593,7 +1593,7 @@ NamedDecl *Sema::ActOnTemplateTemplateParameter( Param->setAccess(AS_public); if (Param->isParameterPack()) - if (auto *LSI = getEnclosingLambda()) + if (auto *LSI = getEnclosingLambdaOrBlock()) LSI->LocalPacks.push_back(Param); // If the template template parameter has a name, then link the identifier @@ -1795,8 +1795,9 @@ DeclResult Sema::CheckClassTemplate( CXXScopeSpec &SS, IdentifierInfo *Name, SourceLocation NameLoc, const ParsedAttributesView &Attr, TemplateParameterList *TemplateParams, AccessSpecifier AS, SourceLocation ModulePrivateLoc, - SourceLocation FriendLoc, unsigned NumOuterTemplateParamLists, - TemplateParameterList **OuterTemplateParamLists, SkipBodyInfo *SkipBody) { + SourceLocation FriendLoc, + ArrayRef OuterTemplateParamLists, + bool IsMemberSpecialization, SkipBodyInfo *SkipBody) { assert(TemplateParams && TemplateParams->size() > 0 && "No template parameters"); assert(TUK != TagUseKind::Reference && @@ -1984,19 +1985,6 @@ DeclResult Sema::CheckClassTemplate( } if (PrevClassTemplate) { - // Ensure that the template parameter lists are compatible. Skip this check - // for a friend in a dependent context: the template parameter list itself - // could be dependent. - if (!(TUK == TagUseKind::Friend && CurContext->isDependentContext()) && - !TemplateParameterListsAreEqual( - TemplateCompareNewDeclInfo(SemanticContext ? SemanticContext - : CurContext, - CurContext, KWLoc), - TemplateParams, PrevClassTemplate, - PrevClassTemplate->getTemplateParameters(), /*Complain=*/true, - TPL_TemplateMatch)) - return true; - // C++ [temp.class]p4: // In a redeclaration, partial specialization, explicit // specialization or explicit instantiation of a class template, @@ -2011,30 +1999,6 @@ DeclResult Sema::CheckClassTemplate( Diag(PrevRecordDecl->getLocation(), diag::note_previous_use); Kind = PrevRecordDecl->getTagKind(); } - - // Check for redefinition of this class template. - if (TUK == TagUseKind::Definition) { - if (TagDecl *Def = PrevRecordDecl->getDefinition()) { - // If we have a prior definition that is not visible, treat this as - // simply making that previous definition visible. - NamedDecl *Hidden = nullptr; - if (SkipBody && !hasVisibleDefinition(Def, &Hidden)) { - SkipBody->ShouldSkip = true; - SkipBody->Previous = Def; - auto *Tmpl = cast(Hidden)->getDescribedClassTemplate(); - assert(Tmpl && "original definition of a class template is not a " - "class template?"); - makeMergedDefinitionVisible(Hidden); - makeMergedDefinitionVisible(Tmpl); - } else { - Diag(NameLoc, diag::err_redefinition) << Name; - Diag(Def->getLocation(), diag::note_previous_definition); - // FIXME: Would it make sense to try to "forget" the previous - // definition, as part of error recovery? - return true; - } - } - } } else if (PrevDecl) { // C++ [temp]p5: // A class template shall not have the same name as any other @@ -2046,23 +2010,6 @@ DeclResult Sema::CheckClassTemplate( return true; } - // Check the template parameter list of this declaration, possibly - // merging in the template parameter list from the previous class - // template declaration. Skip this check for a friend in a dependent - // context, because the template parameter list might be dependent. - if (!(TUK == TagUseKind::Friend && CurContext->isDependentContext()) && - CheckTemplateParameterList( - TemplateParams, - PrevClassTemplate ? GetTemplateParameterList(PrevClassTemplate) - : nullptr, - (SS.isSet() && SemanticContext && SemanticContext->isRecord() && - SemanticContext->isDependentContext()) - ? TPC_ClassTemplateMember - : TUK == TagUseKind::Friend ? TPC_FriendClassTemplate - : TPC_ClassTemplate, - SkipBody)) - Invalid = true; - if (SS.isSet()) { // If the name of the template was qualified, we must be defining the // template out-of-line. @@ -2089,10 +2036,8 @@ DeclResult Sema::CheckClassTemplate( PrevClassTemplate->getTemplatedDecl() : nullptr, /*DelayTypeCreation=*/true); SetNestedNameSpecifier(*this, NewClass, SS); - if (NumOuterTemplateParamLists > 0) - NewClass->setTemplateParameterListsInfo( - Context, - llvm::ArrayRef(OuterTemplateParamLists, NumOuterTemplateParamLists)); + if (!OuterTemplateParamLists.empty()) + NewClass->setTemplateParameterListsInfo(Context, OuterTemplateParamLists); // Add alignment attributes if necessary; these attributes are checked when // the ASTContext lays out the structure. @@ -2105,7 +2050,10 @@ DeclResult Sema::CheckClassTemplate( = ClassTemplateDecl::Create(Context, SemanticContext, NameLoc, DeclarationName(Name), TemplateParams, NewClass); - + // If we are providing an explicit specialization of a member that is a + // class template, make a note of that. + if (IsMemberSpecialization) + NewTemplate->setMemberSpecialization(); if (ShouldAddRedecl) NewTemplate->setPreviousDecl(PrevClassTemplate); @@ -2120,12 +2068,6 @@ DeclResult Sema::CheckClassTemplate( assert(T->isDependentType() && "Class template type is not dependent?"); (void)T; - // If we are providing an explicit specialization of a member that is a - // class template, make a note of that. - if (PrevClassTemplate && - PrevClassTemplate->getInstantiatedFromMemberTemplate()) - PrevClassTemplate->setMemberSpecialization(); - // Set the access specifier. if (!Invalid && TUK != TagUseKind::Friend && NewTemplate->getDeclContext()->isRecord()) @@ -2135,8 +2077,62 @@ DeclResult Sema::CheckClassTemplate( NewClass->setLexicalDeclContext(CurContext); NewTemplate->setLexicalDeclContext(CurContext); - if (TUK == TagUseKind::Definition && (!SkipBody || !SkipBody->ShouldSkip)) - NewClass->startDefinition(); + // Ensure that the template parameter lists are compatible. Skip this check + // for a friend in a dependent context: the template parameter list itself + // could be dependent. + if (ShouldAddRedecl && PrevClassTemplate && + !TemplateParameterListsAreEqual( + NewTemplate, TemplateParams, PrevClassTemplate, + PrevClassTemplate->getTemplateParameters(), + /*Complain=*/true, TPL_TemplateMatch)) + return true; + + // Check the template parameter list of this declaration, possibly + // merging in the template parameter list from the previous class + // template declaration. Skip this check for a friend in a dependent + // context, because the template parameter list might be dependent. + if (ShouldAddRedecl && + CheckTemplateParameterList( + TemplateParams, + PrevClassTemplate ? PrevClassTemplate->getTemplateParameters() + : nullptr, + (SS.isSet() && SemanticContext && SemanticContext->isRecord() && + SemanticContext->isDependentContext()) + ? TPC_ClassTemplateMember + : TUK == TagUseKind::Friend ? TPC_FriendClassTemplate + : TPC_ClassTemplate, + SkipBody)) + Invalid = true; + + if (TUK == TagUseKind::Definition) { + if (PrevClassTemplate) { + // Check for redefinition of this class template. + if (TagDecl *Def = + PrevClassTemplate->getTemplatedDecl()->getDefinition()) { + // If we have a prior definition that is not visible, treat this as + // simply making that previous definition visible. + NamedDecl *Hidden = nullptr; + if (SkipBody && !hasVisibleDefinition(Def, &Hidden)) { + SkipBody->ShouldSkip = true; + SkipBody->Previous = Def; + auto *Tmpl = cast(Hidden)->getDescribedClassTemplate(); + assert(Tmpl && "original definition of a class template is not a " + "class template?"); + makeMergedDefinitionVisible(Hidden); + makeMergedDefinitionVisible(Tmpl); + } else { + Diag(NameLoc, diag::err_redefinition) << Name; + Diag(Def->getLocation(), diag::note_previous_definition); + // FIXME: Would it make sense to try to "forget" the previous + // definition, as part of error recovery? + return true; + } + } + } + + if (!SkipBody || !SkipBody->ShouldSkip) + NewClass->startDefinition(); + } ProcessDeclAttributeList(S, NewClass, Attr); ProcessAPINotes(NewClass); @@ -4133,7 +4129,8 @@ void Sema::CheckDeductionGuideTemplate(FunctionTemplateDecl *TD) { DeclResult Sema::ActOnVarTemplateSpecialization( Scope *S, Declarator &D, TypeSourceInfo *DI, LookupResult &Previous, SourceLocation TemplateKWLoc, TemplateParameterList *TemplateParams, - StorageClass SC, bool IsPartialSpecialization) { + StorageClass SC, bool IsPartialSpecialization, + bool IsMemberSpecialization) { // D must be variable template id. assert(D.getName().getKind() == UnqualifiedIdKind::IK_TemplateId && "Variable template specialization is declared with a template id."); @@ -4251,17 +4248,16 @@ DeclResult Sema::ActOnVarTemplateSpecialization( Context, VarTemplate->getDeclContext(), TemplateKWLoc, TemplateNameLoc, TemplateParams, VarTemplate, DI->getType(), DI, SC, CanonicalConverted); + // If we are providing an explicit specialization of a member variable + // template specialization, make a note of that. + if (IsMemberSpecialization) + Partial->setMemberSpecialization(); Partial->setTemplateArgsAsWritten(TemplateArgs); if (!PrevPartial) VarTemplate->AddPartialSpecialization(Partial, InsertPos); Specialization = Partial; - // If we are providing an explicit specialization of a member variable - // template specialization, make a note of that. - if (PrevPartial && PrevPartial->getInstantiatedFromMember()) - PrevPartial->setMemberSpecialization(); - CheckTemplatePartialSpecialization(Partial); } else { // Create a new class template specialization declaration node for @@ -5502,7 +5498,8 @@ bool Sema::CheckTemplateArgumentList( DefaultArgs && ParamIdx >= DefaultArgs.StartPos) { // All written arguments should have been consumed by this point. assert(ArgIdx == NumArgs && "bad default argument deduction"); - if (ParamIdx == DefaultArgs.StartPos) { + // FIXME: Don't ignore parameter packs. + if (ParamIdx == DefaultArgs.StartPos && !(*Param)->isParameterPack()) { assert(Param + DefaultArgs.Args.size() <= ParamEnd); // Default arguments from a DeducedTemplateName are already converted. for (const TemplateArgument &DefArg : DefaultArgs.Args) { @@ -5727,9 +5724,8 @@ bool Sema::CheckTemplateArgumentList( // pack expansions; they might be empty. This can happen even if // PartialTemplateArgs is false (the list of arguments is complete but // still dependent). - if (PartialOrderingTTP || - (CurrentInstantiationScope && - CurrentInstantiationScope->getPartiallySubstitutedPack())) { + if (ArgIdx < NumArgs && CurrentInstantiationScope && + CurrentInstantiationScope->getPartiallySubstitutedPack()) { while (ArgIdx < NumArgs && NewArgs[ArgIdx].getArgument().isPackExpansion()) { const TemplateArgument &Arg = NewArgs[ArgIdx++].getArgument(); @@ -5776,9 +5772,7 @@ bool Sema::CheckTemplateArgumentList( MultiLevelTemplateArgumentList MLTAL = getTemplateInstantiationArgs( Template, NewContext, /*Final=*/false, CanonicalConverted, - /*RelativeToPrimary=*/true, - /*Pattern=*/nullptr, - /*ForConceptInstantiation=*/true); + /*RelativeToPrimary=*/true, /*ForConceptInstantiation=*/true); if (EnsureTemplateArgumentListConstraints( Template, MLTAL, SourceRange(TemplateLoc, TemplateArgs.getRAngleLoc()))) { @@ -7327,46 +7321,64 @@ bool Sema::CheckTemplateTemplateArgument(TemplateTemplateParmDecl *Param, << Template; } - if (!getLangOpts().RelaxedTemplateTemplateArgs) - return !TemplateParameterListsAreEqual( - Template->getTemplateParameters(), Params, /*Complain=*/true, - TPL_TemplateTemplateArgumentMatch, Arg.getLocation()); - // C++1z [temp.arg.template]p3: (DR 150) // A template-argument matches a template template-parameter P when P // is at least as specialized as the template-argument A. - if (!isTemplateTemplateParameterAtLeastAsSpecializedAs( - Params, Param, Template, DefaultArgs, Arg.getLocation(), IsDeduced)) - return true; - // P2113 - // C++20[temp.func.order]p2 - // [...] If both deductions succeed, the partial ordering selects the - // more constrained template (if one exists) as determined below. - SmallVector ParamsAC, TemplateAC; - Params->getAssociatedConstraints(ParamsAC); - // C++20[temp.arg.template]p3 - // [...] In this comparison, if P is unconstrained, the constraints on A - // are not considered. - if (ParamsAC.empty()) - return false; + if (getLangOpts().RelaxedTemplateTemplateArgs) { + // Quick check for the common case: + // If P contains a parameter pack, then A [...] matches P if each of A's + // template parameters matches the corresponding template parameter in + // the template-parameter-list of P. + if (TemplateParameterListsAreEqual( + Template->getTemplateParameters(), Params, false, + TPL_TemplateTemplateArgumentMatch, Arg.getLocation()) && + // If the argument has no associated constraints, then the parameter is + // definitely at least as specialized as the argument. + // Otherwise - we need a more thorough check. + !Template->hasAssociatedConstraints()) + return false; - Template->getAssociatedConstraints(TemplateAC); + if (isTemplateTemplateParameterAtLeastAsSpecializedAs( + Params, Template, DefaultArgs, Arg.getLocation(), IsDeduced)) { + // P2113 + // C++20[temp.func.order]p2 + // [...] If both deductions succeed, the partial ordering selects the + // more constrained template (if one exists) as determined below. + SmallVector ParamsAC, TemplateAC; + Params->getAssociatedConstraints(ParamsAC); + // C++2a[temp.arg.template]p3 + // [...] In this comparison, if P is unconstrained, the constraints on A + // are not considered. + if (ParamsAC.empty()) + return false; - bool IsParamAtLeastAsConstrained; - if (IsAtLeastAsConstrained(Param, ParamsAC, Template, TemplateAC, - IsParamAtLeastAsConstrained)) - return true; - if (!IsParamAtLeastAsConstrained) { - Diag(Arg.getLocation(), - diag::err_template_template_parameter_not_at_least_as_constrained) - << Template << Param << Arg.getSourceRange(); - Diag(Param->getLocation(), diag::note_entity_declared_at) << Param; - Diag(Template->getLocation(), diag::note_entity_declared_at) << Template; - MaybeEmitAmbiguousAtomicConstraintsDiagnostic(Param, ParamsAC, Template, - TemplateAC); - return true; + Template->getAssociatedConstraints(TemplateAC); + + bool IsParamAtLeastAsConstrained; + if (IsAtLeastAsConstrained(Param, ParamsAC, Template, TemplateAC, + IsParamAtLeastAsConstrained)) + return true; + if (!IsParamAtLeastAsConstrained) { + Diag(Arg.getLocation(), + diag::err_template_template_parameter_not_at_least_as_constrained) + << Template << Param << Arg.getSourceRange(); + Diag(Param->getLocation(), diag::note_entity_declared_at) << Param; + Diag(Template->getLocation(), diag::note_entity_declared_at) + << Template; + MaybeEmitAmbiguousAtomicConstraintsDiagnostic(Param, ParamsAC, Template, + TemplateAC); + return true; + } + return false; + } + // FIXME: Produce better diagnostics for deduction failures. } - return false; + + return !TemplateParameterListsAreEqual(Template->getTemplateParameters(), + Params, + true, + TPL_TemplateTemplateArgumentMatch, + Arg.getLocation()); } static Sema::SemaDiagnosticBuilder noteLocation(Sema &S, const NamedDecl &Decl, @@ -8449,15 +8461,12 @@ DeclResult Sema::ActOnClassTemplateSpecialization( Diag(TemplateNameLoc, diag::err_partial_spec_args_match_primary_template) << /*class template*/ 0 << (TUK == TagUseKind::Definition) << FixItHint::CreateRemoval(SourceRange(LAngleLoc, RAngleLoc)); - return CheckClassTemplate(S, TagSpec, TUK, KWLoc, SS, - ClassTemplate->getIdentifier(), - TemplateNameLoc, - Attr, - TemplateParams, - AS_none, /*ModulePrivateLoc=*/SourceLocation(), - /*FriendLoc*/SourceLocation(), - TemplateParameterLists.size() - 1, - TemplateParameterLists.data()); + return CheckClassTemplate( + S, TagSpec, TUK, KWLoc, SS, ClassTemplate->getIdentifier(), + TemplateNameLoc, Attr, TemplateParams, AS_none, + /*ModulePrivateLoc=*/SourceLocation(), + /*FriendLoc*/ SourceLocation(), TemplateParameterLists.drop_back(), + isMemberSpecialization); } // Create a new class template partial specialization declaration node. @@ -8467,6 +8476,11 @@ DeclResult Sema::ActOnClassTemplateSpecialization( ClassTemplatePartialSpecializationDecl::Create( Context, Kind, DC, KWLoc, TemplateNameLoc, TemplateParams, ClassTemplate, CanonicalConverted, CanonType, PrevPartial); + + // If we are providing an explicit specialization of a member class + // template specialization, make a note of that. + if (isMemberSpecialization) + Partial->setMemberSpecialization(); Partial->setTemplateArgsAsWritten(TemplateArgs); SetNestedNameSpecifier(*this, Partial, SS); if (TemplateParameterLists.size() > 1 && SS.isSet()) { @@ -8478,11 +8492,6 @@ DeclResult Sema::ActOnClassTemplateSpecialization( ClassTemplate->AddPartialSpecialization(Partial, InsertPos); Specialization = Partial; - // If we are providing an explicit specialization of a member class - // template specialization, make a note of that. - if (PrevPartial && PrevPartial->getInstantiatedFromMember()) - PrevPartial->setMemberSpecialization(); - CheckTemplatePartialSpecialization(Partial); } else { // Create a new class template specialization declaration node for @@ -9082,8 +9091,8 @@ bool Sema::CheckFunctionTemplateSpecialization( TemplateDeductionInfo Info(FailedCandidates.getLocation()); FunctionDecl *Specialization = nullptr; if (TemplateDeductionResult TDK = DeduceTemplateArguments( - cast(FunTmpl->getFirstDecl()), - ExplicitTemplateArgs ? &Args : nullptr, FT, Specialization, Info); + FunTmpl, ExplicitTemplateArgs ? &Args : nullptr, FT, + Specialization, Info); TDK != TemplateDeductionResult::Success) { // Template argument deduction failed; record why it failed, so // that we can provide nifty diagnostics. @@ -11281,8 +11290,8 @@ class ExplicitSpecializationVisibilityChecker { template void checkTemplate(TemplDecl *TD) { - if (TD->isMemberSpecialization()) { - if (!CheckMemberSpecialization(TD)) + if (TD->getMostRecentDecl()->isMemberSpecialization()) { + if (!CheckMemberSpecialization(TD->getMostRecentDecl())) diagnose(TD->getMostRecentDecl(), false); } } diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index 03ff8145e3b4a..aa62cfa7dcbd1 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -145,9 +145,7 @@ static TemplateDeductionResult DeduceTemplateArgumentsByTypeMatch( PartialOrderingKind POK, bool DeducedFromArrayBound, bool *HasDeducedAnyParam); -/// What directions packs are allowed to match non-packs. -enum class PackFold { ParameterToArgument, ArgumentToParameter, Both }; - +enum class PackFold { ParameterToArgument, ArgumentToParameter }; static TemplateDeductionResult DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams, ArrayRef Ps, @@ -1713,21 +1711,7 @@ static TemplateDeductionResult DeduceTemplateArgumentsByTypeMatch( DeducedTemplateArgument Result = checkDeducedTemplateArguments(S.Context, Deduced[Index], NewDeduced); if (Result.isNull()) { - // We can also get inconsistencies when matching NTTP type. - switch (NamedDecl *Param = TemplateParams->getParam(Index); - Param->getKind()) { - case Decl::TemplateTypeParm: - Info.Param = cast(Param); - break; - case Decl::NonTypeTemplateParm: - Info.Param = cast(Param); - break; - case Decl::TemplateTemplateParm: - Info.Param = cast(Param); - break; - default: - llvm_unreachable("unexpected kind"); - } + Info.Param = cast(TemplateParams->getParam(Index)); Info.FirstArg = Deduced[Index]; Info.SecondArg = NewDeduced; return TemplateDeductionResult::Inconsistent; @@ -2565,31 +2549,8 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams, if (const NonTypeTemplateParmDecl *NTTP = getDeducedParameterFromExpr(Info, P.getAsExpr())) { switch (A.getKind()) { - case TemplateArgument::Expression: { - const Expr *E = A.getAsExpr(); - // When checking NTTP, if either the parameter or the argument is - // dependent, as there would be otherwise nothing to deduce, we force - // the argument to the parameter type using this dependent implicit - // cast, in order to maintain invariants. Now we can deduce the - // resulting type from the original type, and deduce the original type - // against the parameter we are checking. - if (const auto *ICE = dyn_cast(E); - ICE && ICE->getCastKind() == clang::CK_Dependent) { - E = ICE->getSubExpr(); - if (auto Result = DeduceTemplateArgumentsByTypeMatch( - S, TemplateParams, ICE->getType(), E->getType(), Info, - Deduced, TDF_SkipNonDependent, - PartialOrdering ? PartialOrderingKind::NonCall - : PartialOrderingKind::None, - /*DeducedFromArrayBound=*/false, HasDeducedAnyParam); - Result != TemplateDeductionResult::Success) - return Result; - } - return DeduceNonTypeTemplateArgument( - S, TemplateParams, NTTP, DeducedTemplateArgument(A), E->getType(), - Info, PartialOrdering, Deduced, HasDeducedAnyParam); - } case TemplateArgument::Integral: + case TemplateArgument::Expression: case TemplateArgument::StructuralValue: return DeduceNonTypeTemplateArgument( S, TemplateParams, NTTP, DeducedTemplateArgument(A), @@ -2678,75 +2639,50 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams, SmallVectorImpl &Deduced, bool NumberOfArgumentsMustMatch, bool PartialOrdering, PackFold PackFold, bool *HasDeducedAnyParam) { - bool FoldPackParameter = PackFold == PackFold::ParameterToArgument || - PackFold == PackFold::Both, - FoldPackArgument = PackFold == PackFold::ArgumentToParameter || - PackFold == PackFold::Both; - + if (PackFold == PackFold::ArgumentToParameter) + std::swap(Ps, As); // C++0x [temp.deduct.type]p9: // If the template argument list of P contains a pack expansion that is not // the last template argument, the entire template argument list is a // non-deduced context. - if (FoldPackParameter && hasPackExpansionBeforeEnd(Ps)) - return TemplateDeductionResult::Success; - - if (FoldPackArgument && hasPackExpansionBeforeEnd(As)) + if (hasPackExpansionBeforeEnd(Ps)) return TemplateDeductionResult::Success; // C++0x [temp.deduct.type]p9: // If P has a form that contains or , then each argument Pi of the // respective template argument list P is compared with the corresponding // argument Ai of the corresponding template argument list of A. - for (unsigned ArgIdx = 0, ParamIdx = 0; /**/; /**/) { - if (!hasTemplateArgumentForDeduction(Ps, ParamIdx)) - return !FoldPackParameter && hasTemplateArgumentForDeduction(As, ArgIdx) - ? TemplateDeductionResult::MiscellaneousDeductionFailure - : TemplateDeductionResult::Success; - - if (!Ps[ParamIdx].isPackExpansion()) { + unsigned ArgIdx = 0, ParamIdx = 0; + for (; hasTemplateArgumentForDeduction(Ps, ParamIdx); ++ParamIdx) { + const TemplateArgument &P = Ps[ParamIdx]; + if (!P.isPackExpansion()) { // The simple case: deduce template arguments by matching Pi and Ai. // Check whether we have enough arguments. if (!hasTemplateArgumentForDeduction(As, ArgIdx)) - return !FoldPackArgument && NumberOfArgumentsMustMatch + return NumberOfArgumentsMustMatch ? TemplateDeductionResult::MiscellaneousDeductionFailure : TemplateDeductionResult::Success; - if (As[ArgIdx].isPackExpansion()) { - // C++1z [temp.deduct.type]p9: - // During partial ordering, if Ai was originally a pack expansion - // [and] Pi is not a pack expansion, template argument deduction - // fails. - if (!FoldPackArgument) - return TemplateDeductionResult::MiscellaneousDeductionFailure; - - TemplateArgument Pattern = As[ArgIdx].getPackExpansionPattern(); - for (;;) { - // Deduce template parameters from the pattern. - if (auto Result = DeduceTemplateArguments( - S, TemplateParams, Ps[ParamIdx], Pattern, Info, - PartialOrdering, Deduced, HasDeducedAnyParam); - Result != TemplateDeductionResult::Success) - return Result; + // C++1z [temp.deduct.type]p9: + // During partial ordering, if Ai was originally a pack expansion [and] + // Pi is not a pack expansion, template argument deduction fails. + if (As[ArgIdx].isPackExpansion()) + return TemplateDeductionResult::MiscellaneousDeductionFailure; - ++ParamIdx; - if (!hasTemplateArgumentForDeduction(Ps, ParamIdx)) - return TemplateDeductionResult::Success; - if (Ps[ParamIdx].isPackExpansion()) - break; - } - } else { - // Perform deduction for this Pi/Ai pair. - if (auto Result = DeduceTemplateArguments( - S, TemplateParams, Ps[ParamIdx], As[ArgIdx], Info, - PartialOrdering, Deduced, HasDeducedAnyParam); - Result != TemplateDeductionResult::Success) - return Result; + // Perform deduction for this Pi/Ai pair. + TemplateArgument Pi = P, Ai = As[ArgIdx]; + if (PackFold == PackFold::ArgumentToParameter) + std::swap(Pi, Ai); + if (auto Result = DeduceTemplateArguments(S, TemplateParams, Pi, Ai, Info, + PartialOrdering, Deduced, + HasDeducedAnyParam); + Result != TemplateDeductionResult::Success) + return Result; - ++ArgIdx; - ++ParamIdx; - continue; - } + // Move to the next argument. + ++ArgIdx; + continue; } // The parameter is a pack expansion. @@ -2756,7 +2692,7 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams, // each remaining argument in the template argument list of A. Each // comparison deduces template arguments for subsequent positions in the // template parameter packs expanded by Pi. - TemplateArgument Pattern = Ps[ParamIdx].getPackExpansionPattern(); + TemplateArgument Pattern = P.getPackExpansionPattern(); // Prepare to deduce the packs within the pattern. PackDeductionScope PackScope(S, TemplateParams, Deduced, Info, Pattern); @@ -2767,12 +2703,13 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams, for (; hasTemplateArgumentForDeduction(As, ArgIdx) && PackScope.hasNextElement(); ++ArgIdx) { - if (!FoldPackParameter && !As[ArgIdx].isPackExpansion()) - return TemplateDeductionResult::MiscellaneousDeductionFailure; + TemplateArgument Pi = Pattern, Ai = As[ArgIdx]; + if (PackFold == PackFold::ArgumentToParameter) + std::swap(Pi, Ai); // Deduce template arguments from the pattern. - if (auto Result = DeduceTemplateArguments( - S, TemplateParams, Pattern, As[ArgIdx], Info, PartialOrdering, - Deduced, HasDeducedAnyParam); + if (auto Result = DeduceTemplateArguments(S, TemplateParams, Pi, Ai, Info, + PartialOrdering, Deduced, + HasDeducedAnyParam); Result != TemplateDeductionResult::Success) return Result; @@ -2781,8 +2718,12 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams, // Build argument packs for each of the parameter packs expanded by this // pack expansion. - return PackScope.finish(); + if (auto Result = PackScope.finish(); + Result != TemplateDeductionResult::Success) + return Result; } + + return TemplateDeductionResult::Success; } TemplateDeductionResult Sema::DeduceTemplateArguments( @@ -3197,20 +3138,6 @@ template<> struct IsPartialSpecialization { static constexpr bool value = true; }; -template -static bool DeducedArgsNeedReplacement(TemplateDeclT *Template) { - return false; -} -template <> -bool DeducedArgsNeedReplacement( - VarTemplatePartialSpecializationDecl *Spec) { - return !Spec->isClassScopeExplicitSpecialization(); -} -template <> -bool DeducedArgsNeedReplacement( - ClassTemplatePartialSpecializationDecl *Spec) { - return !Spec->isClassScopeExplicitSpecialization(); -} template static TemplateDeductionResult @@ -3221,23 +3148,10 @@ CheckDeducedArgumentConstraints(Sema &S, TemplateDeclT *Template, llvm::SmallVector AssociatedConstraints; Template->getAssociatedConstraints(AssociatedConstraints); - std::optional> Innermost; - // If we don't need to replace the deduced template arguments, - // we can add them immediately as the inner-most argument list. - if (!DeducedArgsNeedReplacement(Template)) - Innermost = CanonicalDeducedArgs; - MultiLevelTemplateArgumentList MLTAL = S.getTemplateInstantiationArgs( - Template, Template->getDeclContext(), /*Final=*/false, Innermost, - /*RelativeToPrimary=*/true, /*Pattern=*/ - nullptr, /*ForConstraintInstantiation=*/true); - - // getTemplateInstantiationArgs picks up the non-deduced version of the - // template args when this is a variable template partial specialization and - // not class-scope explicit specialization, so replace with Deduced Args - // instead of adding to inner-most. - if (!Innermost) - MLTAL.replaceInnermostTemplateArguments(Template, CanonicalDeducedArgs); + Template, Template->getDeclContext(), /*Final=*/false, + /*Innermost=*/CanonicalDeducedArgs, /*RelativeToPrimary=*/true, + /*ForConstraintInstantiation=*/true); if (S.CheckConstraintSatisfaction(Template, AssociatedConstraints, MLTAL, Info.getLocation(), @@ -3361,6 +3275,7 @@ static TemplateDeductionResult FinishTemplateArgumentDeduction( // Unevaluated SFINAE context. EnterExpressionEvaluationContext Unevaluated( S, Sema::ExpressionEvaluationContext::Unevaluated); + Sema::SFINAETrap Trap(S); Sema::ContextRAII SavedContext(S, getAsDeclContextOrEnclosing(Template)); @@ -3378,36 +3293,20 @@ static TemplateDeductionResult FinishTemplateArgumentDeduction( // Check that we produced the correct argument list. TemplateParameterList *TemplateParams = Template->getTemplateParameters(); - auto isSame = [&](unsigned I, const TemplateArgument &P, - const TemplateArgument &A) { - if (isSameTemplateArg(S.Context, P, A, PartialOrdering, - /*PackExpansionMatchesPack=*/true)) - return true; - Info.Param = makeTemplateParameter(TemplateParams->getParam(I)); - Info.FirstArg = P; - Info.SecondArg = A; - return false; - }; for (unsigned I = 0, E = TemplateParams->size(); I != E; ++I) { - const TemplateArgument &P = TemplateArgs[I]; - if (P.isPackExpansion()) { - assert(I == TemplateArgs.size() - 1); - for (/**/; I != E; ++I) { - const TemplateArgument &A = CanonicalBuilder[I]; - if (A.getKind() == TemplateArgument::Pack) { - for (const TemplateArgument &Ai : A.getPackAsArray()) - if (!isSame(I, P, Ai)) - return TemplateDeductionResult::NonDeducedMismatch; - } else if (!isSame(I, P, A)) { - return TemplateDeductionResult::NonDeducedMismatch; - } - } - break; - } - if (!isSame(I, P, CanonicalBuilder[I])) + TemplateArgument InstArg = CanonicalBuilder[I]; + if (!isSameTemplateArg(S.Context, TemplateArgs[I], InstArg, PartialOrdering, + /*PackExpansionMatchesPack=*/true)) { + Info.Param = makeTemplateParameter(TemplateParams->getParam(I)); + Info.FirstArg = TemplateArgs[I]; + Info.SecondArg = InstArg; return TemplateDeductionResult::NonDeducedMismatch; + } } + if (Trap.hasErrorOccurred()) + return TemplateDeductionResult::SubstitutionFailure; + if (!PartialOrdering) { if (auto Result = CheckDeducedArgumentConstraints( S, Template, SugaredBuilder, CanonicalBuilder, Info); @@ -3428,6 +3327,7 @@ static TemplateDeductionResult FinishTemplateArgumentDeduction( // Unevaluated SFINAE context. EnterExpressionEvaluationContext Unevaluated( S, Sema::ExpressionEvaluationContext::Unevaluated); + Sema::SFINAETrap Trap(S); Sema::ContextRAII SavedContext(S, getAsDeclContextOrEnclosing(TD)); @@ -3436,13 +3336,20 @@ static TemplateDeductionResult FinishTemplateArgumentDeduction( // explicitly specified, template argument deduction fails. SmallVector SugaredBuilder, CanonicalBuilder; if (auto Result = ConvertDeducedTemplateArguments( - S, TD, /*IsDeduced=*/false, Deduced, Info, SugaredBuilder, + S, TD, /*IsPartialOrdering=*/false, Deduced, Info, SugaredBuilder, CanonicalBuilder); Result != TemplateDeductionResult::Success) return Result; - return ::CheckDeducedArgumentConstraints(S, TD, SugaredBuilder, - CanonicalBuilder, Info); + if (Trap.hasErrorOccurred()) + return TemplateDeductionResult::SubstitutionFailure; + + if (auto Result = CheckDeducedArgumentConstraints(S, TD, SugaredBuilder, + CanonicalBuilder, Info); + Result != TemplateDeductionResult::Success) + return Result; + + return TemplateDeductionResult::Success; } /// Perform template argument deduction to determine whether the given template @@ -3489,20 +3396,16 @@ DeduceTemplateArguments(Sema &S, T *Partial, if (Inst.isInvalid()) return TemplateDeductionResult::InstantiationDepth; + if (Trap.hasErrorOccurred()) + return TemplateDeductionResult::SubstitutionFailure; + TemplateDeductionResult Result; S.runWithSufficientStackSpace(Info.getLocation(), [&] { Result = ::FinishTemplateArgumentDeduction(S, Partial, /*IsPartialOrdering=*/false, TemplateArgs, Deduced, Info); }); - - if (Result != TemplateDeductionResult::Success) - return Result; - - if (Trap.hasErrorOccurred()) - return TemplateDeductionResult::SubstitutionFailure; - - return TemplateDeductionResult::Success; + return Result; } TemplateDeductionResult @@ -3558,18 +3461,14 @@ Sema::DeduceTemplateArgumentsFromType(TemplateDecl *TD, QualType FromType, if (Inst.isInvalid()) return TemplateDeductionResult::InstantiationDepth; + if (Trap.hasErrorOccurred()) + return TemplateDeductionResult::SubstitutionFailure; + TemplateDeductionResult Result; runWithSufficientStackSpace(Info.getLocation(), [&] { Result = ::FinishTemplateArgumentDeduction(*this, TD, Deduced, Info); }); - - if (Result != TemplateDeductionResult::Success) - return Result; - - if (Trap.hasErrorOccurred()) - return TemplateDeductionResult::SubstitutionFailure; - - return TemplateDeductionResult::Success; + return Result; } /// Determine whether the given type T is a simple-template-id type. @@ -6210,23 +6109,14 @@ static bool isAtLeastAsSpecializedAs(Sema &S, QualType T1, QualType T2, return false; const auto *TST1 = cast(T1); - - Sema::SFINAETrap Trap(S); - - TemplateDeductionResult Result; + bool AtLeastAsSpecialized; S.runWithSufficientStackSpace(Info.getLocation(), [&] { - Result = ::FinishTemplateArgumentDeduction( - S, P2, /*IsPartialOrdering=*/true, TST1->template_arguments(), Deduced, - Info); + AtLeastAsSpecialized = + FinishTemplateArgumentDeduction( + S, P2, /*IsPartialOrdering=*/true, TST1->template_arguments(), + Deduced, Info) == TemplateDeductionResult::Success; }); - - if (Result != TemplateDeductionResult::Success) - return false; - - if (Trap.hasErrorOccurred()) - return false; - - return true; + return AtLeastAsSpecialized; } namespace { @@ -6464,9 +6354,8 @@ bool Sema::isMoreSpecializedThanPrimary( } bool Sema::isTemplateTemplateParameterAtLeastAsSpecializedAs( - TemplateParameterList *P, TemplateDecl *PArg, TemplateDecl *AArg, - const DefaultArguments &DefaultArgs, SourceLocation ArgLoc, - bool IsDeduced) { + TemplateParameterList *P, TemplateDecl *AArg, + const DefaultArguments &DefaultArgs, SourceLocation Loc, bool IsDeduced) { // C++1z [temp.arg.template]p4: (DR 150) // A template template-parameter P is at least as specialized as a // template template-argument A if, given the following rewrite to two @@ -6478,12 +6367,6 @@ bool Sema::isTemplateTemplateParameterAtLeastAsSpecializedAs( // TemplateParameterList *A = AArg->getTemplateParameters(); - Sema::InstantiatingTemplate Inst( - *this, ArgLoc, Sema::InstantiatingTemplate::PartialOrderingTTP(), PArg, - SourceRange(P->getTemplateLoc(), P->getRAngleLoc())); - if (Inst.isInvalid()) - return false; - // Given an invented class template X with the template parameter list of // A (including default arguments): // - Each function template has a single function parameter whose type is @@ -6498,6 +6381,8 @@ bool Sema::isTemplateTemplateParameterAtLeastAsSpecializedAs( // templates. SmallVector PArgs; { + SFINAETrap Trap(*this); + Context.getInjectedTemplateArgs(P, PArgs); TemplateArgumentListInfo PArgList(P->getLAngleLoc(), P->getRAngleLoc()); @@ -6517,17 +6402,18 @@ bool Sema::isTemplateTemplateParameterAtLeastAsSpecializedAs( // C++1z [temp.arg.template]p3: // If the rewrite produces an invalid type, then P is not at least as // specialized as A. - SmallVector CanonicalPArgs; - if (CheckTemplateArgumentList(AArg, ArgLoc, PArgList, DefaultArgs, false, - PArgs, CanonicalPArgs, + SmallVector SugaredPArgs; + if (CheckTemplateArgumentList(AArg, Loc, PArgList, DefaultArgs, false, + SugaredPArgs, PArgs, /*UpdateArgsWithConversions=*/true, /*ConstraintsNotSatisfied=*/nullptr, - /*PartialOrderingTTP=*/true)) + /*PartialOrderTTP=*/true) || + Trap.hasErrorOccurred()) return false; } // Determine whether P1 is at least as specialized as P2. - TemplateDeductionInfo Info(ArgLoc, A->getDepth()); + TemplateDeductionInfo Info(Loc, A->getDepth()); SmallVector Deduced; Deduced.resize(A->size()); @@ -6542,89 +6428,29 @@ bool Sema::isTemplateTemplateParameterAtLeastAsSpecializedAs( // be inverted between Ps and As. On non-deduced context, matching needs to // happen both ways, according to [temp.arg.template]p3, but this is // currently implemented as a special case elsewhere. - switch (::DeduceTemplateArguments( - *this, A, AArgs, PArgs, Info, Deduced, - /*NumberOfArgumentsMustMatch=*/false, /*PartialOrdering=*/true, - IsDeduced ? PackFold::ArgumentToParameter : PackFold::Both, - /*HasDeducedAnyParam=*/nullptr)) { - case clang::TemplateDeductionResult::Success: - break; - - case TemplateDeductionResult::MiscellaneousDeductionFailure: - Diag(AArg->getLocation(), diag::err_template_param_list_different_arity) - << (A->size() > P->size()) << /*isTemplateTemplateParameter=*/true - << SourceRange(A->getTemplateLoc(), P->getRAngleLoc()); + if (::DeduceTemplateArguments(*this, A, AArgs, PArgs, Info, Deduced, + /*NumberOfArgumentsMustMatch=*/false, + /*PartialOrdering=*/true, + IsDeduced ? PackFold::ArgumentToParameter + : PackFold::ParameterToArgument, + /*HasDeducedAnyParam=*/nullptr) != + TemplateDeductionResult::Success) return false; - case TemplateDeductionResult::NonDeducedMismatch: - Diag(AArg->getLocation(), diag::err_non_deduced_mismatch) - << Info.FirstArg << Info.SecondArg; - return false; - case TemplateDeductionResult::Inconsistent: - Diag(getAsNamedDecl(Info.Param)->getLocation(), - diag::err_inconsistent_deduction) - << Info.FirstArg << Info.SecondArg; - return false; - case TemplateDeductionResult::AlreadyDiagnosed: - return false; - - // None of these should happen for a plain deduction. - case TemplateDeductionResult::Invalid: - case TemplateDeductionResult::InstantiationDepth: - case TemplateDeductionResult::Incomplete: - case TemplateDeductionResult::IncompletePack: - case TemplateDeductionResult::Underqualified: - case TemplateDeductionResult::SubstitutionFailure: - case TemplateDeductionResult::DeducedMismatch: - case TemplateDeductionResult::DeducedMismatchNested: - case TemplateDeductionResult::TooManyArguments: - case TemplateDeductionResult::TooFewArguments: - case TemplateDeductionResult::InvalidExplicitArguments: - case TemplateDeductionResult::NonDependentConversionFailure: - case TemplateDeductionResult::ConstraintsNotSatisfied: - case TemplateDeductionResult::CUDATargetMismatch: - llvm_unreachable("Unexpected Result"); - } SmallVector DeducedArgs(Deduced.begin(), Deduced.end()); + Sema::InstantiatingTemplate Inst(*this, Info.getLocation(), AArg, DeducedArgs, + Info); + if (Inst.isInvalid()) + return false; - TemplateDeductionResult TDK; + bool AtLeastAsSpecialized; runWithSufficientStackSpace(Info.getLocation(), [&] { - TDK = ::FinishTemplateArgumentDeduction( - *this, AArg, /*IsPartialOrdering=*/true, PArgs, Deduced, Info); + AtLeastAsSpecialized = + ::FinishTemplateArgumentDeduction( + *this, AArg, /*IsPartialOrdering=*/true, PArgs, Deduced, Info) == + TemplateDeductionResult::Success; }); - switch (TDK) { - case TemplateDeductionResult::Success: - return true; - - // It doesn't seem possible to get a non-deduced mismatch when partial - // ordering TTPs. - case TemplateDeductionResult::NonDeducedMismatch: - llvm_unreachable("Unexpected NonDeducedMismatch"); - - // Substitution failures should have already been diagnosed. - case TemplateDeductionResult::AlreadyDiagnosed: - case TemplateDeductionResult::SubstitutionFailure: - case TemplateDeductionResult::InstantiationDepth: - return false; - - // None of these should happen when just converting deduced arguments. - case TemplateDeductionResult::Invalid: - case TemplateDeductionResult::Incomplete: - case TemplateDeductionResult::IncompletePack: - case TemplateDeductionResult::Inconsistent: - case TemplateDeductionResult::Underqualified: - case TemplateDeductionResult::DeducedMismatch: - case TemplateDeductionResult::DeducedMismatchNested: - case TemplateDeductionResult::TooManyArguments: - case TemplateDeductionResult::TooFewArguments: - case TemplateDeductionResult::InvalidExplicitArguments: - case TemplateDeductionResult::NonDependentConversionFailure: - case TemplateDeductionResult::ConstraintsNotSatisfied: - case TemplateDeductionResult::MiscellaneousDeductionFailure: - case TemplateDeductionResult::CUDATargetMismatch: - llvm_unreachable("Unexpected Result"); - } - llvm_unreachable("Unexpected TDK"); + return AtLeastAsSpecialized; } namespace { diff --git a/clang/lib/Sema/SemaTemplateDeductionGuide.cpp b/clang/lib/Sema/SemaTemplateDeductionGuide.cpp index 545da21183c3c..3a87128a7db0f 100644 --- a/clang/lib/Sema/SemaTemplateDeductionGuide.cpp +++ b/clang/lib/Sema/SemaTemplateDeductionGuide.cpp @@ -70,8 +70,8 @@ class ExtractTypeForDeductionGuide ExtractTypeForDeductionGuide( Sema &SemaRef, llvm::SmallVectorImpl &MaterializedTypedefs, - ClassTemplateDecl *NestedPattern, - const MultiLevelTemplateArgumentList *OuterInstantiationArgs) + ClassTemplateDecl *NestedPattern = nullptr, + const MultiLevelTemplateArgumentList *OuterInstantiationArgs = nullptr) : Base(SemaRef), MaterializedTypedefs(MaterializedTypedefs), NestedPattern(NestedPattern), OuterInstantiationArgs(OuterInstantiationArgs) { @@ -765,7 +765,7 @@ buildAssociatedConstraints(Sema &SemaRef, FunctionTemplateDecl *F, } // Template arguments used to transform the template arguments in // DeducedResults. - SmallVector TemplateArgsForBuildingRC( + SmallVector InnerArgsForBuildingRC( F->getTemplateParameters()->size()); // Transform the transformed template args MultiLevelTemplateArgumentList Args; @@ -778,33 +778,30 @@ buildAssociatedConstraints(Sema &SemaRef, FunctionTemplateDecl *F, NamedDecl *TP = F->getTemplateParameters()->getParam(Index); MultiLevelTemplateArgumentList Args; Args.setKind(TemplateSubstitutionKind::Rewrite); - Args.addOuterTemplateArguments(TemplateArgsForBuildingRC); + Args.addOuterTemplateArguments(InnerArgsForBuildingRC); // Rebuild the template parameter with updated depth and index. NamedDecl *NewParam = transformTemplateParameter(SemaRef, F->getDeclContext(), TP, Args, /*NewIndex=*/FirstUndeducedParamIdx, getDepthAndIndex(TP).first + AdjustDepth); FirstUndeducedParamIdx += 1; - assert(TemplateArgsForBuildingRC[Index].isNull()); - TemplateArgsForBuildingRC[Index] = - Context.getInjectedTemplateArg(NewParam); + assert(InnerArgsForBuildingRC[Index].isNull()); + InnerArgsForBuildingRC[Index] = Context.getInjectedTemplateArg(NewParam); continue; } TemplateArgumentLoc Input = SemaRef.getTrivialTemplateArgumentLoc(D, QualType(), SourceLocation{}); TemplateArgumentLoc Output; if (!SemaRef.SubstTemplateArgument(Input, Args, Output)) { - assert(TemplateArgsForBuildingRC[Index].isNull() && + assert(InnerArgsForBuildingRC[Index].isNull() && "InstantiatedArgs must be null before setting"); - TemplateArgsForBuildingRC[Index] = Output.getArgument(); + InnerArgsForBuildingRC[Index] = Output.getArgument(); } } - // A list of template arguments for transforming the require-clause of F. - // It must contain the entire set of template argument lists. - MultiLevelTemplateArgumentList ArgsForBuildingRC; - ArgsForBuildingRC.setKind(clang::TemplateSubstitutionKind::Rewrite); - ArgsForBuildingRC.addOuterTemplateArguments(TemplateArgsForBuildingRC); + // A list of template arguments for transforming the require-clause using + // the transformed template arguments as the template argument list of F. + // // For 2), if the underlying deduction guide F is nested in a class template, // we need the entire template argument list, as the constraint AST in the // require-clause of F remains completely uninstantiated. @@ -827,25 +824,15 @@ buildAssociatedConstraints(Sema &SemaRef, FunctionTemplateDecl *F, // - The occurrence of U in the function parameter is [depth:0, index:0] // - The template parameter of U is [depth:0, index:0] // - // We add the outer template arguments which is [int] to the multi-level arg - // list to ensure that the occurrence U in `C` will be replaced with int - // during the substitution. - // // NOTE: The underlying deduction guide F is instantiated -- either from an // explicitly-written deduction guide member, or from a constructor. - // getInstantiatedFromMemberTemplate() can only handle the former case, so we - // check the DeclContext kind. - if (F->getLexicalDeclContext()->getDeclKind() == - clang::Decl::ClassTemplateSpecialization) { - auto OuterLevelArgs = SemaRef.getTemplateInstantiationArgs( - F, F->getLexicalDeclContext(), - /*Final=*/false, /*Innermost=*/std::nullopt, - /*RelativeToPrimary=*/true, - /*Pattern=*/nullptr, - /*ForConstraintInstantiation=*/true); - for (auto It : OuterLevelArgs) - ArgsForBuildingRC.addOuterTemplateArguments(It.Args); - } + MultiLevelTemplateArgumentList ArgsForBuildingRC = + SemaRef.getTemplateInstantiationArgs(F, F->getLexicalDeclContext(), + /*Final=*/false, + /*Innermost=*/InnerArgsForBuildingRC, + /*RelativeToPrimary=*/true, + /*ForConstraintInstantiation=*/true); + ArgsForBuildingRC.setKind(clang::TemplateSubstitutionKind::Rewrite); ExprResult E = SemaRef.SubstExpr(RC, ArgsForBuildingRC); if (E.isInvalid()) @@ -1228,10 +1215,25 @@ FunctionTemplateDecl *DeclareAggregateDeductionGuideForTypeAlias( getRHSTemplateDeclAndArgs(SemaRef, AliasTemplate).first; if (!RHSTemplate) return nullptr; + + llvm::SmallVector TypedefDecls; + llvm::SmallVector NewParamTypes; + ExtractTypeForDeductionGuide TypeAliasTransformer(SemaRef, TypedefDecls); + for (QualType P : ParamTypes) { + QualType Type = TypeAliasTransformer.TransformType(P); + if (Type.isNull()) + return nullptr; + NewParamTypes.push_back(Type); + } + auto *RHSDeductionGuide = SemaRef.DeclareAggregateDeductionGuideFromInitList( - RHSTemplate, ParamTypes, Loc); + RHSTemplate, NewParamTypes, Loc); if (!RHSDeductionGuide) return nullptr; + + for (TypedefNameDecl *TD : TypedefDecls) + TD->setDeclContext(RHSDeductionGuide->getTemplatedDecl()); + return BuildDeductionGuideForTypeAlias(SemaRef, AliasTemplate, RHSDeductionGuide, Loc); } diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index 898255ff7c6a3..50d6029e76ded 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -52,38 +52,6 @@ using namespace sema; //===----------------------------------------------------------------------===/ namespace { -namespace TemplateInstArgsHelpers { -struct Response { - const Decl *NextDecl = nullptr; - bool IsDone = false; - bool ClearRelativeToPrimary = true; - static Response Done() { - Response R; - R.IsDone = true; - return R; - } - static Response ChangeDecl(const Decl *ND) { - Response R; - R.NextDecl = ND; - return R; - } - static Response ChangeDecl(const DeclContext *Ctx) { - Response R; - R.NextDecl = Decl::castFromDeclContext(Ctx); - return R; - } - - static Response UseNextDecl(const Decl *CurDecl) { - return ChangeDecl(CurDecl->getDeclContext()); - } - - static Response DontClearRelativeToPrimaryNextDecl(const Decl *CurDecl) { - Response R = Response::UseNextDecl(CurDecl); - R.ClearRelativeToPrimary = false; - return R; - } -}; - // Retrieve the primary template for a lambda call operator. It's // unfortunate that we only have the mappings of call operators rather // than lambda classes. @@ -171,374 +139,396 @@ bool isLambdaEnclosedByTypeAliasDecl( .TraverseType(Underlying); } -// Add template arguments from a variable template instantiation. -Response -HandleVarTemplateSpec(const VarTemplateSpecializationDecl *VarTemplSpec, - MultiLevelTemplateArgumentList &Result, - bool SkipForSpecialization) { - // For a class-scope explicit specialization, there are no template arguments - // at this level, but there may be enclosing template arguments. - if (VarTemplSpec->isClassScopeExplicitSpecialization()) - return Response::DontClearRelativeToPrimaryNextDecl(VarTemplSpec); - - // We're done when we hit an explicit specialization. - if (VarTemplSpec->getSpecializationKind() == TSK_ExplicitSpecialization && - !isa(VarTemplSpec)) - return Response::Done(); - - // If this variable template specialization was instantiated from a - // specialized member that is a variable template, we're done. - assert(VarTemplSpec->getSpecializedTemplate() && "No variable template?"); - llvm::PointerUnion - Specialized = VarTemplSpec->getSpecializedTemplateOrPartial(); - if (VarTemplatePartialSpecializationDecl *Partial = - Specialized.dyn_cast()) { - if (!SkipForSpecialization) - Result.addOuterTemplateArguments( - Partial, VarTemplSpec->getTemplateInstantiationArgs().asArray(), - /*Final=*/false); - if (Partial->isMemberSpecialization()) - return Response::Done(); - } else { - VarTemplateDecl *Tmpl = Specialized.get(); - if (!SkipForSpecialization) - Result.addOuterTemplateArguments( - Tmpl, VarTemplSpec->getTemplateInstantiationArgs().asArray(), - /*Final=*/false); - if (Tmpl->isMemberSpecialization()) - return Response::Done(); +struct TemplateInstantiationArgumentCollecter + : DeclVisitor { + Sema &S; + MultiLevelTemplateArgumentList &Result; + std::optional> Innermost; + bool RelativeToPrimary; + bool ForConstraintInstantiation; + + TemplateInstantiationArgumentCollecter( + Sema &S, MultiLevelTemplateArgumentList &Result, + std::optional> Innermost, + bool RelativeToPrimary, bool ForConstraintInstantiation) + : S(S), Result(Result), Innermost(Innermost), + RelativeToPrimary(RelativeToPrimary), + ForConstraintInstantiation(ForConstraintInstantiation) {} + + Decl *Done() { return nullptr; } + + Decl *ChangeDecl(const Decl *D) { + RelativeToPrimary = false; + return const_cast(D); } - return Response::DontClearRelativeToPrimaryNextDecl(VarTemplSpec); -} -// If we have a template template parameter with translation unit context, -// then we're performing substitution into a default template argument of -// this template template parameter before we've constructed the template -// that will own this template template parameter. In this case, we -// use empty template parameter lists for all of the outer templates -// to avoid performing any substitutions. -Response -HandleDefaultTempArgIntoTempTempParam(const TemplateTemplateParmDecl *TTP, - MultiLevelTemplateArgumentList &Result) { - for (unsigned I = 0, N = TTP->getDepth() + 1; I != N; ++I) - Result.addOuterTemplateArguments(std::nullopt); - return Response::Done(); -} + Decl *ChangeDecl(const DeclContext *DC) { + return ChangeDecl(Decl::castFromDeclContext(DC)); + } -Response HandlePartialClassTemplateSpec( - const ClassTemplatePartialSpecializationDecl *PartialClassTemplSpec, - MultiLevelTemplateArgumentList &Result, bool SkipForSpecialization) { - if (!SkipForSpecialization) - Result.addOuterRetainedLevels(PartialClassTemplSpec->getTemplateDepth()); - return Response::Done(); -} + Decl *UseNextDecl(const Decl *D) { return ChangeDecl(D->getDeclContext()); } -// Add template arguments from a class template instantiation. -Response -HandleClassTemplateSpec(const ClassTemplateSpecializationDecl *ClassTemplSpec, - MultiLevelTemplateArgumentList &Result, - bool SkipForSpecialization) { - if (!ClassTemplSpec->isClassScopeExplicitSpecialization()) { - // We're done when we hit an explicit specialization. - if (ClassTemplSpec->getSpecializationKind() == TSK_ExplicitSpecialization && - !isa(ClassTemplSpec)) - return Response::Done(); + void AddInnermostTemplateArguments(const Decl *D) { + assert(Innermost); + Result.addOuterTemplateArguments(const_cast(D), *Innermost, + /*Final=*/false); + Innermost.reset(); + } - if (!SkipForSpecialization) - Result.addOuterTemplateArguments( - const_cast(ClassTemplSpec), - ClassTemplSpec->getTemplateInstantiationArgs().asArray(), - /*Final=*/false); + void AddOuterTemplateArguments(const Decl *D, ArrayRef Args, + bool Final) { + Result.addOuterTemplateArguments(const_cast(D), Args, Final); + } - // If this class template specialization was instantiated from a - // specialized member that is a class template, we're done. - assert(ClassTemplSpec->getSpecializedTemplate() && "No class template?"); - if (ClassTemplSpec->getSpecializedTemplate()->isMemberSpecialization()) - return Response::Done(); - - // If this was instantiated from a partial template specialization, we need - // to get the next level of declaration context from the partial - // specialization, as the ClassTemplateSpecializationDecl's - // DeclContext/LexicalDeclContext will be for the primary template. - if (auto *InstFromPartialTempl = ClassTemplSpec->getSpecializedTemplateOrPartial() - .dyn_cast()) - return Response::ChangeDecl(InstFromPartialTempl->getLexicalDeclContext()); + Decl *VisitTemplateTemplateParmDecl(TemplateTemplateParmDecl *TTPD) { + if (Innermost) + AddInnermostTemplateArguments(TTPD); + else if (ForConstraintInstantiation) + AddOuterTemplateArguments(nullptr, std::nullopt, /*Final=*/false); + + for (unsigned Depth = TTPD->getDepth() + 1; Depth--;) + AddOuterTemplateArguments(nullptr, std::nullopt, /*Final=*/false); + + return Done(); } - return Response::UseNextDecl(ClassTemplSpec); -} -Response HandleFunction(Sema &SemaRef, const FunctionDecl *Function, - MultiLevelTemplateArgumentList &Result, - const FunctionDecl *Pattern, bool RelativeToPrimary, - bool ForConstraintInstantiation, - bool ForDefaultArgumentSubstitution) { - // Add template arguments from a function template specialization. - if (!RelativeToPrimary && - Function->getTemplateSpecializationKindForInstantiation() == - TSK_ExplicitSpecialization) - return Response::Done(); - - if (!RelativeToPrimary && - Function->getTemplateSpecializationKind() == TSK_ExplicitSpecialization) { - // This is an implicit instantiation of an explicit specialization. We - // don't get any template arguments from this function but might get - // some from an enclosing template. - return Response::UseNextDecl(Function); - } else if (const TemplateArgumentList *TemplateArgs = - Function->getTemplateSpecializationArgs()) { - // Add the template arguments for this specialization. - Result.addOuterTemplateArguments(const_cast(Function), - TemplateArgs->asArray(), - /*Final=*/false); + Decl *VisitFunctionTemplateDecl(FunctionTemplateDecl *FTD) { + assert( + (ForConstraintInstantiation || Result.getNumSubstitutedLevels() == 0) && + "outer template not instantiated?"); + + if (Innermost) + AddInnermostTemplateArguments(FTD); + else if (ForConstraintInstantiation) + AddOuterTemplateArguments(FTD, FTD->getInjectedTemplateArgs(), + /*Final=*/false); + + if (FTD->isMemberSpecialization()) + return Done(); - if (RelativeToPrimary && - (Function->getTemplateSpecializationKind() == - TSK_ExplicitSpecialization || - (Function->getFriendObjectKind() && - !Function->getPrimaryTemplate()->getFriendObjectKind()))) - return Response::UseNextDecl(Function); - - // If this function was instantiated from a specialized member that is - // a function template, we're done. - assert(Function->getPrimaryTemplate() && "No function template?"); - if (!ForDefaultArgumentSubstitution && - Function->getPrimaryTemplate()->isMemberSpecialization()) - return Response::Done(); - - // If this function is a generic lambda specialization, we are done. - if (!ForConstraintInstantiation && - isGenericLambdaCallOperatorOrStaticInvokerSpecialization(Function)) - return Response::Done(); - - } else if (Function->getDescribedFunctionTemplate()) { + if (FTD->getFriendObjectKind()) + return ChangeDecl(FTD->getLexicalDeclContext()); + return UseNextDecl(FTD); + } + + Decl *VisitVarTemplateDecl(VarTemplateDecl *VTD) { assert( (ForConstraintInstantiation || Result.getNumSubstitutedLevels() == 0) && - "Outer template not instantiated?"); + "outer template not instantiated?"); + + if (Innermost) + AddInnermostTemplateArguments(VTD); + else if (ForConstraintInstantiation) + AddOuterTemplateArguments(VTD, VTD->getInjectedTemplateArgs(), + /*Final=*/false); + + if (VTD->isMemberSpecialization()) + return Done(); + + return UseNextDecl(VTD); } - // If this is a friend or local declaration and it declares an entity at - // namespace scope, take arguments from its lexical parent - // instead of its semantic parent, unless of course the pattern we're - // instantiating actually comes from the file's context! - if ((Function->getFriendObjectKind() || Function->isLocalExternDecl()) && - Function->getNonTransparentDeclContext()->isFileContext() && - (!Pattern || !Pattern->getLexicalDeclContext()->isFileContext())) { - return Response::ChangeDecl(Function->getLexicalDeclContext()); + + Decl *VisitVarTemplatePartialSpecializationDecl( + VarTemplatePartialSpecializationDecl *VTPSD) { + assert( + (ForConstraintInstantiation || Result.getNumSubstitutedLevels() == 0) && + "outer template not instantiated?"); + + if (Innermost) + AddInnermostTemplateArguments(VTPSD); + else if (ForConstraintInstantiation) + AddOuterTemplateArguments(VTPSD, VTPSD->getTemplateArgs().asArray(), + /*Final=*/false); + + if (VTPSD->isMemberSpecialization()) + return Done(); + + return UseNextDecl(VTPSD); } - if (ForConstraintInstantiation && Function->getFriendObjectKind()) - return Response::ChangeDecl(Function->getLexicalDeclContext()); - return Response::UseNextDecl(Function); -} + Decl *VisitClassTemplateDecl(ClassTemplateDecl *CTD) { + assert( + (ForConstraintInstantiation || Result.getNumSubstitutedLevels() == 0) && + "outer template not instantiated?"); -Response HandleFunctionTemplateDecl(const FunctionTemplateDecl *FTD, - MultiLevelTemplateArgumentList &Result) { - if (!isa(FTD->getDeclContext())) { - Result.addOuterTemplateArguments( - const_cast(FTD), - const_cast(FTD)->getInjectedTemplateArgs(), - /*Final=*/false); - - NestedNameSpecifier *NNS = FTD->getTemplatedDecl()->getQualifier(); - - while (const Type *Ty = NNS ? NNS->getAsType() : nullptr) { - if (NNS->isInstantiationDependent()) { - if (const auto *TSTy = Ty->getAs()) { - ArrayRef Arguments = TSTy->template_arguments(); - // Prefer template arguments from the injected-class-type if possible. - // For example, - // ```cpp - // template struct S { - // template void foo(); - // }; - // template template - // ^^^^^^^^^^^^^ InjectedTemplateArgs - // They're of kind TemplateArgument::Pack, not of - // TemplateArgument::Type. - // void S::foo() {} - // ^^^^^^^ - // TSTy->template_arguments() (which are of PackExpansionType) - // ``` - // This meets the contract in - // TreeTransform::TryExpandParameterPacks that the template arguments - // for unexpanded parameters should be of a Pack kind. - if (TSTy->isCurrentInstantiation()) { - auto *RD = TSTy->getCanonicalTypeInternal()->getAsCXXRecordDecl(); - if (ClassTemplateDecl *CTD = RD->getDescribedClassTemplate()) - Arguments = CTD->getInjectedTemplateArgs(); - else if (auto *Specialization = - dyn_cast(RD)) - Arguments = - Specialization->getTemplateInstantiationArgs().asArray(); - } - Result.addOuterTemplateArguments( - TSTy->getTemplateName().getAsTemplateDecl(), Arguments, - /*Final=*/false); - } - } + if (Innermost) + AddInnermostTemplateArguments(CTD); + else if (ForConstraintInstantiation) + AddOuterTemplateArguments(CTD, CTD->getInjectedTemplateArgs(), + /*Final=*/false); - NNS = NNS->getPrefix(); - } + if (CTD->isMemberSpecialization()) + return Done(); + + if (CTD->getFriendObjectKind()) + return ChangeDecl(CTD->getLexicalDeclContext()); + return UseNextDecl(CTD); } - return Response::ChangeDecl(FTD->getLexicalDeclContext()); -} + Decl *VisitClassTemplatePartialSpecializationDecl( + ClassTemplatePartialSpecializationDecl *CTPSD) { + assert( + (ForConstraintInstantiation || Result.getNumSubstitutedLevels() == 0) && + "outer template not instantiated?"); + + if (Innermost) + AddInnermostTemplateArguments(CTPSD); + else if (ForConstraintInstantiation) + AddOuterTemplateArguments(CTPSD, CTPSD->getTemplateArgs().asArray(), + /*Final=*/false); + + if (CTPSD->isMemberSpecialization()) + return Done(); + + return UseNextDecl(CTPSD); + } -Response HandleRecordDecl(Sema &SemaRef, const CXXRecordDecl *Rec, - MultiLevelTemplateArgumentList &Result, - ASTContext &Context, - bool ForConstraintInstantiation) { - if (ClassTemplateDecl *ClassTemplate = Rec->getDescribedClassTemplate()) { + Decl *VisitTypeAliasTemplateDecl(TypeAliasTemplateDecl *TATD) { assert( (ForConstraintInstantiation || Result.getNumSubstitutedLevels() == 0) && - "Outer template not instantiated?"); - if (ClassTemplate->isMemberSpecialization()) - return Response::Done(); - if (ForConstraintInstantiation) - Result.addOuterTemplateArguments(const_cast(Rec), - ClassTemplate->getInjectedTemplateArgs(), - /*Final=*/false); + "outer template not instantiated?"); + if (Innermost) + AddInnermostTemplateArguments(TATD); + else if (ForConstraintInstantiation) + AddOuterTemplateArguments(TATD, TATD->getInjectedTemplateArgs(), + /*Final=*/false); + + return UseNextDecl(TATD); } - if (const MemberSpecializationInfo *MSInfo = - Rec->getMemberSpecializationInfo()) - if (MSInfo->getTemplateSpecializationKind() == TSK_ExplicitSpecialization) - return Response::Done(); - - bool IsFriend = Rec->getFriendObjectKind() || - (Rec->getDescribedClassTemplate() && - Rec->getDescribedClassTemplate()->getFriendObjectKind()); - if (ForConstraintInstantiation && IsFriend && - Rec->getNonTransparentDeclContext()->isFileContext()) { - return Response::ChangeDecl(Rec->getLexicalDeclContext()); + Decl *VisitConceptDecl(ConceptDecl *CD) { + assert( + (ForConstraintInstantiation || Result.getNumSubstitutedLevels() == 0) && + "outer template not instantiated?"); + if (Innermost) + AddInnermostTemplateArguments(CD); + + return UseNextDecl(CD); + } + + Decl *VisitFunctionDecl(FunctionDecl *FD) { + assert(!FD->getDescribedFunctionTemplate() && + "not for templated declarations"); + + if (!RelativeToPrimary) { + // Add template arguments from a function template specialization. + if (const MemberSpecializationInfo *MSI = + FD->getMemberSpecializationInfo(); + MSI && + MSI->getTemplateSpecializationKind() == TSK_ExplicitSpecialization) + return Done(); + + // This is an implicit instantiation of an explicit specialization. We + // don't get any template arguments from this function but might get + // some from an enclosing template. + if (FD->getTemplateSpecializationKind() == TSK_ExplicitSpecialization) + return UseNextDecl(FD); + } + + if (const TemplateArgumentList *TemplateArgs = + FD->getTemplateSpecializationArgs()) { + // Add the template arguments for this specialization. + if (Innermost) + AddInnermostTemplateArguments(FD); + else + AddOuterTemplateArguments(FD, TemplateArgs->asArray(), /*Final=*/false); + + if (FD->getTemplateSpecializationKind() == TSK_ExplicitSpecialization || + (FD->getFriendObjectKind() && + !FD->getPrimaryTemplate()->getFriendObjectKind())) + return UseNextDecl(FD); + + // If this function was instantiated from a specialized member that is + // a function template, we're done. + assert(FD->getPrimaryTemplate() && "No function template?"); + if (FD->getPrimaryTemplate()->hasMemberSpecialization()) + return Done(); + + // If this function is a generic lambda specialization, we are done. + if (!ForConstraintInstantiation && + isGenericLambdaCallOperatorOrStaticInvokerSpecialization(FD)) + return Done(); + } + + // If this is a friend or local declaration and it declares an entity at + // namespace scope, take arguments from its lexical parent + // instead of its semantic parent, unless of course the pattern we're + // instantiating actually comes from the file's context! + if ((FD->getFriendObjectKind() || FD->isLocalExternDecl()) && + FD->getNonTransparentDeclContext()->isFileContext()) { + return ChangeDecl(FD->getLexicalDeclContext()); + } + + if (ForConstraintInstantiation && FD->getFriendObjectKind()) + return ChangeDecl(FD->getLexicalDeclContext()); + return UseNextDecl(FD); } - // This is to make sure we pick up the VarTemplateSpecializationDecl or the - // TypeAliasTemplateDecl that this lambda is defined inside of. - if (Rec->isLambda()) { - if (const Decl *LCD = Rec->getLambdaContextDecl()) - return Response::ChangeDecl(LCD); - // Retrieve the template arguments for a using alias declaration. - // This is necessary for constraint checking, since we always keep - // constraints relative to the primary template. - if (auto TypeAlias = getEnclosingTypeAliasTemplateDecl(SemaRef); - ForConstraintInstantiation && TypeAlias) { - if (isLambdaEnclosedByTypeAliasDecl(Rec->getLambdaCallOperator(), - TypeAlias.PrimaryTypeAliasDecl)) { - Result.addOuterTemplateArguments(TypeAlias.Template, - TypeAlias.AssociatedTemplateArguments, - /*Final=*/false); - // Visit the parent of the current type alias declaration rather than - // the lambda thereof. - // E.g., in the following example: - // struct S { - // template using T = decltype([] {} ()); - // }; - // void foo() { - // S::T var; - // } - // The instantiated lambda expression (which we're visiting at 'var') - // has a function DeclContext 'foo' rather than the Record DeclContext - // S. This seems to be an oversight to me that we may want to set a - // Sema Context from the CXXScopeSpec before substituting into T. - return Response::ChangeDecl(TypeAlias.Template->getDeclContext()); + Decl *VisitCXXRecordDecl(CXXRecordDecl *RD) { + assert(!RD->getDescribedClassTemplate() && + "not for templated declarations"); + + if (const MemberSpecializationInfo *MSI = RD->getMemberSpecializationInfo(); + MSI && + MSI->getTemplateSpecializationKind() == TSK_ExplicitSpecialization) + return Done(); + + if (ForConstraintInstantiation && RD->getFriendObjectKind() && + RD->getNonTransparentDeclContext()->isFileContext()) { + return ChangeDecl(RD->getLexicalDeclContext()); + } + + // This is to make sure we pick up the VarTemplateSpecializationDecl or the + // TypeAliasTemplateDecl that this lambda is defined inside of. + if (RD->isLambda()) { + if (Decl *LCD = RD->getLambdaContextDecl()) + return ChangeDecl(LCD); + // Retrieve the template arguments for a using alias declaration. + // This is necessary for constraint checking, since we always keep + // constraints relative to the primary template. + if (auto TypeAlias = getEnclosingTypeAliasTemplateDecl(S); + ForConstraintInstantiation && TypeAlias) { + if (isLambdaEnclosedByTypeAliasDecl(RD->getLambdaCallOperator(), + TypeAlias.PrimaryTypeAliasDecl)) { + AddOuterTemplateArguments(TypeAlias.Template, + TypeAlias.AssociatedTemplateArguments, + /*Final=*/false); + // Visit the parent of the current type alias declaration rather than + // the lambda thereof. + // E.g., in the following example: + // struct S { + // template using T = decltype([] {} ()); + // }; + // void foo() { + // S::T var; + // } + // The instantiated lambda expression (which we're visiting at 'var') + // has a function DeclContext 'foo' rather than the Record DeclContext + // S. This seems to be an oversight to me that we may want to set a + // Sema Context from the CXXScopeSpec before substituting into T. + return ChangeDecl(TypeAlias.Template->getDeclContext()); + } } } + + return UseNextDecl(RD); } - return Response::UseNextDecl(Rec); -} + Decl * + VisitClassTemplateSpecializationDecl(ClassTemplateSpecializationDecl *CTSD) { + // For a class-scope explicit specialization, there are no template + // arguments at this level, but there may be enclosing template arguments. + if (CTSD->isClassScopeExplicitSpecialization()) + return UseNextDecl(CTSD); -Response HandleImplicitConceptSpecializationDecl( - const ImplicitConceptSpecializationDecl *CSD, - MultiLevelTemplateArgumentList &Result) { - Result.addOuterTemplateArguments( - const_cast(CSD), - CSD->getTemplateArguments(), - /*Final=*/false); - return Response::UseNextDecl(CSD); -} + // We're done when we hit an explicit specialization. + if (CTSD->getSpecializationKind() == TSK_ExplicitSpecialization) + return Done(); + + if (Innermost) + AddInnermostTemplateArguments(CTSD); + else + AddOuterTemplateArguments(CTSD, + CTSD->getTemplateInstantiationArgs().asArray(), + /*Final=*/false); + + // If this class template specialization was instantiated from a + // specialized member that is a class template, we're done. + assert(CTSD->getSpecializedTemplate() && "No class template?"); + llvm::PointerUnion + Specialized = CTSD->getSpecializedTemplateOrPartial(); + if (auto *CTPSD = + Specialized.dyn_cast()) { + if (CTPSD->hasMemberSpecialization()) + return Done(); + } else { + auto *CTD = Specialized.get(); + if (CTD->hasMemberSpecialization()) + return Done(); + } + return UseNextDecl(CTSD); + } + + Decl * + VisitVarTemplateSpecializationDecl(VarTemplateSpecializationDecl *VTSD) { + // For a class-scope explicit specialization, there are no template + // arguments at this level, but there may be enclosing template arguments. + if (VTSD->isClassScopeExplicitSpecialization()) + return UseNextDecl(VTSD); + + // We're done when we hit an explicit specialization. + if (VTSD->getSpecializationKind() == TSK_ExplicitSpecialization) + return Done(); + + if (Innermost) + AddInnermostTemplateArguments(VTSD); + else + AddOuterTemplateArguments(VTSD, + VTSD->getTemplateInstantiationArgs().asArray(), + /*Final=*/false); + + // If this variable template specialization was instantiated from a + // specialized member that is a variable template, we're done. + assert(VTSD->getSpecializedTemplate() && "No variable template?"); + llvm::PointerUnion + Specialized = VTSD->getSpecializedTemplateOrPartial(); + if (auto *VTPSD = + Specialized.dyn_cast()) { + if (VTPSD->hasMemberSpecialization()) + return Done(); + } else { + auto *VTD = Specialized.get(); + if (VTD->hasMemberSpecialization()) + return Done(); + } + return UseNextDecl(VTSD); + } + + Decl *VisitImplicitConceptSpecializationDecl( + ImplicitConceptSpecializationDecl *ICSD) { + AddOuterTemplateArguments(ICSD, ICSD->getTemplateArguments(), + /*Final=*/false); + return UseNextDecl(ICSD); + } + + Decl *VisitDecl(Decl *D) { + if (D->isFileContextDecl()) + return Done(); + + if (isa(D)) + RelativeToPrimary = false; + + return UseNextDecl(D); + } + + Decl *Visit(Decl *D) { + if (TemplateDecl *TD = D->getDescribedTemplate()) + D = TD; + return DeclVisitor::Visit(D); + } +}; -Response HandleGenericDeclContext(const Decl *CurDecl) { - return Response::UseNextDecl(CurDecl); -} -} // namespace TemplateInstArgsHelpers } // namespace MultiLevelTemplateArgumentList Sema::getTemplateInstantiationArgs( const NamedDecl *ND, const DeclContext *DC, bool Final, std::optional> Innermost, bool RelativeToPrimary, - const FunctionDecl *Pattern, bool ForConstraintInstantiation, - bool SkipForSpecialization, bool ForDefaultArgumentSubstitution) { + bool ForConstraintInstantiation) { assert((ND || DC) && "Can't find arguments for a decl if one isn't provided"); // Accumulate the set of template argument lists in this structure. MultiLevelTemplateArgumentList Result; - - using namespace TemplateInstArgsHelpers; const Decl *CurDecl = ND; if (!CurDecl) CurDecl = Decl::castFromDeclContext(DC); - if (Innermost) { - Result.addOuterTemplateArguments(const_cast(ND), *Innermost, - Final); - // Populate placeholder template arguments for TemplateTemplateParmDecls. - // This is essential for the case e.g. - // - // template concept Concept = false; - // template