diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index 5b70b18d9dad9a633016906a5ddfd9f7504a93e4..0e24d6132b4cb032d66f505941f1057bf47b527f 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -64,3 +64,6 @@ f6d557ee34b6bbdb1dc32f29e34b4a4a8ad35e81 # [libc++][NFC] clang-format 2d7eb9c9ea1a146412a83603d5c0c6339a5d8284 + +# [libc++] Rename _LIBCPP_INLINE_VISIBILITY to _LIBCPP_HIDE_FROM_ABI +4c198542226223f6a5c5511a1f89b37d15ee10b9 diff --git a/.github/workflows/libcxx-build-and-test.yaml b/.github/workflows/libcxx-build-and-test.yaml index 018f0e45a2441ef0fd37e049cf0b1a5fb4fe8f42..268e1033387dcbfa7dd468fb89bacc44a080797f 100644 --- a/.github/workflows/libcxx-build-and-test.yaml +++ b/.github/workflows/libcxx-build-and-test.yaml @@ -50,11 +50,10 @@ env: jobs: stage1: if: github.repository_owner == 'llvm' - runs-on: - group: libcxx-runners-8 + runs-on: libcxx-runners-8-set continue-on-error: false strategy: - fail-fast: true + fail-fast: false matrix: config: [ 'generic-cxx03', @@ -89,12 +88,11 @@ jobs: **/crash_diagnostics/* stage2: if: github.repository_owner == 'llvm' - runs-on: - group: libcxx-runners-8 + runs-on: libcxx-runners-8-set needs: [ stage1 ] continue-on-error: false strategy: - fail-fast: true + fail-fast: false matrix: config: [ 'generic-cxx11', @@ -169,27 +167,26 @@ jobs: 'benchmarks', 'bootstrapping-build' ] - machine: [ 'libcxx-runners-8' ] + machine: [ 'libcxx-runners-8-set' ] std_modules: [ 'OFF' ] include: - config: 'generic-cxx26' - machine: libcxx-runners-8 + machine: libcxx-runners-8-set std_modules: 'ON' - config: 'generic-asan' - machine: libcxx-runners-8 + machine: libcxx-runners-8-set std_modules: 'OFF' - config: 'generic-tsan' - machine: libcxx-runners-8 + machine: libcxx-runners-8-set std_modules: 'OFF' - config: 'generic-ubsan' - machine: libcxx-runners-8 + machine: libcxx-runners-8-set std_modules: 'OFF' # Use a larger machine for MSAN to avoid timeout and memory allocation issues. - config: 'generic-msan' - machine: libcxx-runners-32 + machine: libcxx-runners-32-set std_modules: 'OFF' - runs-on: - group: ${{ matrix.machine }} + runs-on: ${{ matrix.machine }} steps: - uses: actions/checkout@v4 - name: ${{ matrix.config }} diff --git a/.github/workflows/new-prs.yml b/.github/workflows/new-prs.yml index 9ba55d59ff15b4c754a8e45610c2d79e92bea2e2..18caa408df57b605e73a951c51660ca16cfc8631 100644 --- a/.github/workflows/new-prs.yml +++ b/.github/workflows/new-prs.yml @@ -15,16 +15,43 @@ on: - synchronize jobs: - automate-prs-labels: + greeter: + runs-on: ubuntu-latest permissions: pull-requests: write + # Only comment on PRs that have been opened for the first time, by someone + # new to LLVM or to GitHub as a whole. + if: >- + (github.repository == 'llvm/llvm-project') && + (github.event.action == 'opened') && + (github.event.pull_request.author_association == 'FIRST_TIME_CONTRIBUTOR' || + github.event.pull_request.author_association == 'FIRST_TIMER') + steps: + - name: Setup Automation Script + run: | + curl -O -L --fail https://raw.githubusercontent.com/"$GITHUB_REPOSITORY"/main/llvm/utils/git/github-automation.py + curl -O -L --fail https://raw.githubusercontent.com/"$GITHUB_REPOSITORY"/main/llvm/utils/git/requirements.txt + chmod a+x github-automation.py + pip install -r requirements.txt + + - name: Greet Author + run: | + ./github-automation.py \ + --token '${{ secrets.GITHUB_TOKEN }}' \ + pr-greeter \ + --issue-number "${{ github.event.pull_request.number }}" + + automate-prs-labels: + # Greet first so that only the author gets that notification. + needs: greeter runs-on: ubuntu-latest # Ignore PRs with more than 10 commits. Pull requests with a lot of # commits tend to be accidents usually when someone made a mistake while trying # to rebase. We want to ignore these pull requests to avoid excessive # notifications. + # always() means that even if greeter is skipped, this job will run. if: > - github.repository == 'llvm/llvm-project' && + always() && github.repository == 'llvm/llvm-project' && github.event.pull_request.draft == false && github.event.pull_request.commits < 10 steps: diff --git a/.mailmap b/.mailmap index 867af2c55f3bc4e9f2e217b07ba04e76c7180800..f4d7ffe3013818c430331c665a77093718bcb8d0 100644 --- a/.mailmap +++ b/.mailmap @@ -24,6 +24,7 @@ + @@ -42,3 +43,6 @@ Med Ismail Bennani Ramkumar Ramachandra Saleem Abdulrasool Tommy Chiang +Wang Pengcheng +Wang Pengcheng <137158460+wangpc-pp@users.noreply.github.com> +Wang Pengcheng diff --git a/bolt/lib/Core/BinaryFunction.cpp b/bolt/lib/Core/BinaryFunction.cpp index 76bac2f62441924c59307f6bd4e68840fffaa99a..be033cf07668801fffc5ed6e8c7e609ba3824ec5 100644 --- a/bolt/lib/Core/BinaryFunction.cpp +++ b/bolt/lib/Core/BinaryFunction.cpp @@ -3352,9 +3352,9 @@ void BinaryFunction::fixBranches() { // We are going to generate two branches. Check if their targets are in // the same fragment as this block. If only one target is in the same // fragment, make it the destination of the conditional branch. There - // is a chance it will be a short branch which takes 5 bytes fewer than + // is a chance it will be a short branch which takes 4 bytes fewer than // a long conditional branch. For unconditional branch, the difference - // is 4 bytes. + // is 3 bytes. if (BB->getFragmentNum() != TSuccessor->getFragmentNum() && BB->getFragmentNum() == FSuccessor->getFragmentNum()) swapSuccessors(); diff --git a/bolt/lib/Core/FunctionLayout.cpp b/bolt/lib/Core/FunctionLayout.cpp index 37b07bd28f26f6565d2df173f73cb869dce423ee..27e40de94be660f0ffbe9b5f6ddacb5b0393b0ac 100644 --- a/bolt/lib/Core/FunctionLayout.cpp +++ b/bolt/lib/Core/FunctionLayout.cpp @@ -188,10 +188,6 @@ bool FunctionLayout::update(const ArrayRef NewLayout) { for (BinaryBasicBlock *const BB : NewLayout) { FragmentNum Num = BB->getFragmentNum(); - assert(Num >= Fragments.back()->getFragmentNum() && - "Blocks must be arranged such that fragments are monotonically " - "increasing."); - // Add empty fragments if necessary while (Fragments.back()->getFragmentNum() < Num) addFragment(); diff --git a/bolt/lib/Passes/IndirectCallPromotion.cpp b/bolt/lib/Passes/IndirectCallPromotion.cpp index 8b084c3b63d565a090293b76b86cb1e1c7a5c141..f40f5e7acbf3b8bf0b5aae459011f7f60efeb591 100644 --- a/bolt/lib/Passes/IndirectCallPromotion.cpp +++ b/bolt/lib/Passes/IndirectCallPromotion.cpp @@ -158,6 +158,7 @@ static cl::opt ICPPeelForInline( } // namespace opts +#ifndef NDEBUG static bool verifyProfile(std::map &BFs) { bool IsValid = true; for (auto &BFI : BFs) { @@ -182,6 +183,7 @@ static bool verifyProfile(std::map &BFs) { } return IsValid; } +#endif namespace llvm { namespace bolt { @@ -1467,7 +1469,6 @@ void IndirectCallPromotion::runOnFunctions(BinaryContext &BC) { std::max(TotalIndexBasedCandidates, 1)) << "%\n"; - (void)verifyProfile; #ifndef NDEBUG verifyProfile(BFs); #endif diff --git a/bolt/lib/Passes/ReorderAlgorithm.cpp b/bolt/lib/Passes/ReorderAlgorithm.cpp index 3c3365e1d3d711321c3eda520012d5cbb64e0507..c112e1a8dcea737a2c07565c4d8fbedece98e69e 100644 --- a/bolt/lib/Passes/ReorderAlgorithm.cpp +++ b/bolt/lib/Passes/ReorderAlgorithm.cpp @@ -20,7 +20,7 @@ #include #include -#undef DEBUG_TYPE +#undef DEBUG_TYPE #define DEBUG_TYPE "bolt" using namespace llvm; @@ -425,7 +425,7 @@ void TSPReorderAlgorithm::reorderBasicBlocks(BinaryFunction &BF, } std::vector> DP; - DP.resize(1 << N); + DP.resize(static_cast(1) << N); for (std::vector &Elmt : DP) Elmt.resize(N, -1); diff --git a/bolt/lib/Passes/SplitFunctions.cpp b/bolt/lib/Passes/SplitFunctions.cpp index 79da4cc3b04ba95073d91f0230df933ee7c7bcfd..d9f5da3e3bcab568b28f34b12143f14971d67033 100644 --- a/bolt/lib/Passes/SplitFunctions.cpp +++ b/bolt/lib/Passes/SplitFunctions.cpp @@ -109,6 +109,21 @@ static cl::opt SplitStrategy( "fragment contains exactly a single basic block")), cl::desc("strategy used to partition blocks into fragments"), cl::cat(BoltOptCategory)); + +static cl::opt CallScale( + "call-scale", + cl::desc("Call score scale coefficient (when --split-strategy=cdsplit)"), + cl::init(0.95), cl::ReallyHidden, cl::cat(BoltOptCategory)); + +static cl::opt + CallPower("call-power", + cl::desc("Call score power (when --split-strategy=cdsplit)"), + cl::init(0.05), cl::ReallyHidden, cl::cat(BoltOptCategory)); + +static cl::opt + JumpPower("jump-power", + cl::desc("Jump score power (when --split-strategy=cdsplit)"), + cl::init(0.15), cl::ReallyHidden, cl::cat(BoltOptCategory)); } // namespace opts namespace { @@ -140,12 +155,18 @@ struct SplitProfile2 final : public SplitStrategy { }; struct SplitCacheDirected final : public SplitStrategy { + BinaryContext &BC; using BasicBlockOrder = BinaryFunction::BasicBlockOrderType; bool canSplit(const BinaryFunction &BF) override { return BF.hasValidProfile() && hasFullProfile(BF) && !allBlocksCold(BF); } + explicit SplitCacheDirected(BinaryContext &BC) : BC(BC) { + initializeAuxiliaryVariables(); + buildCallGraph(); + } + // When some functions are hot-warm split and others are hot-warm-cold split, // we do not want to change the fragment numbers of the blocks in the hot-warm // split functions. @@ -173,13 +194,387 @@ struct SplitCacheDirected final : public SplitStrategy { } private: + struct CallInfo { + size_t Length; + size_t Count; + }; + + struct SplitScore { + size_t SplitIndex; + size_t HotSizeReduction = 0; + double LocalScore = 0; + double CoverCallScore = 0; + }; + + // Auxiliary variables used by the algorithm. + size_t TotalNumBlocks{0}; + size_t OrigHotSectionSize{0}; + DenseMap GlobalIndices; + DenseMap BBSizes; + DenseMap BBOffsets; + + // Call graph. + std::vector> Callers; + std::vector> Callees; + + bool shouldConsiderForCallGraph(const BinaryFunction &BF) { + // Only a subset of the functions in the binary will be considered + // for initializing auxiliary variables and building call graph. + return BF.hasValidIndex() && BF.hasValidProfile() && !BF.empty(); + } + + void initializeAuxiliaryVariables() { + for (BinaryFunction *BF : BC.getSortedFunctions()) { + if (!shouldConsiderForCallGraph(*BF)) + continue; + + // Calculate the size of each BB after hot-cold splitting. + // This populates BinaryBasicBlock::OutputAddressRange which + // can be used to compute the size of each BB. + BC.calculateEmittedSize(*BF, /*FixBranches=*/true); + + for (BinaryBasicBlock *BB : BF->getLayout().blocks()) { + // Unique global index. + GlobalIndices[BB] = TotalNumBlocks; + TotalNumBlocks++; + + // Block size after hot-cold splitting. + BBSizes[BB] = BB->getOutputSize(); + + // Hot block offset after hot-cold splitting. + BBOffsets[BB] = OrigHotSectionSize; + if (!BB->isSplit()) + OrigHotSectionSize += BBSizes[BB]; + } + } + } + + void buildCallGraph() { + Callers.resize(TotalNumBlocks); + Callees.resize(TotalNumBlocks); + for (const BinaryFunction *SrcFunction : BC.getSortedFunctions()) { + if (!shouldConsiderForCallGraph(*SrcFunction)) + continue; + + for (BinaryBasicBlock &SrcBB : SrcFunction->blocks()) { + // Skip blocks that are not executed + if (SrcBB.getKnownExecutionCount() == 0) + continue; + + // Find call instructions and extract target symbols from each one + for (const MCInst &Inst : SrcBB) { + if (!BC.MIB->isCall(Inst)) + continue; + + // Call info + const MCSymbol *DstSym = BC.MIB->getTargetSymbol(Inst); + // Ignore calls w/o information + if (!DstSym) + continue; + + const BinaryFunction *DstFunction = BC.getFunctionForSymbol(DstSym); + // Ignore calls that do not have a valid target, but do not ignore + // recursive calls, because caller block could be moved to warm. + if (!DstFunction || DstFunction->getLayout().block_empty()) + continue; + + const BinaryBasicBlock *DstBB = &(DstFunction->front()); + + // Record the call only if DstBB is also in functions to consider for + // call graph. + if (GlobalIndices.contains(DstBB)) { + Callers[GlobalIndices[DstBB]].push_back(&SrcBB); + Callees[GlobalIndices[&SrcBB]].push_back(DstBB); + } + } + } + } + } + + /// Populate BinaryBasicBlock::OutputAddressRange with estimated basic block + /// start and end addresses for hot and warm basic blocks, assuming hot-warm + /// splitting happens at \p SplitIndex. Also return estimated end addresses + /// of the hot fragment before and after splitting. + /// The estimations take into account the potential addition of branch + /// instructions due to split fall through branches as well as the need to + /// use longer branch instructions for split (un)conditional branches. + std::pair + estimatePostSplitBBAddress(const BasicBlockOrder &BlockOrder, + const size_t SplitIndex) { + assert(SplitIndex < BlockOrder.size() && "Invalid split index"); + + // Update function layout assuming hot-warm splitting at SplitIndex + for (size_t Index = 0; Index < BlockOrder.size(); Index++) { + BinaryBasicBlock *BB = BlockOrder[Index]; + if (BB->getFragmentNum() == FragmentNum::cold()) + break; + BB->setFragmentNum(Index <= SplitIndex ? FragmentNum::main() + : FragmentNum::warm()); + } + BinaryFunction *BF = BlockOrder[0]->getFunction(); + BF->getLayout().update(BlockOrder); + // Populate BB.OutputAddressRange under the updated layout. + BC.calculateEmittedSize(*BF); + + // Populate BB.OutputAddressRange with estimated new start and end addresses + // and compute the old end address of the hot section and the new end + // address of the hot section. + size_t OldHotEndAddr; + size_t NewHotEndAddr; + size_t CurrentAddr = BBOffsets[BlockOrder[0]]; + for (BinaryBasicBlock *BB : BlockOrder) { + // We only care about new addresses of blocks in hot/warm. + if (BB->getFragmentNum() == FragmentNum::cold()) + break; + const size_t NewSize = BB->getOutputSize(); + BB->setOutputStartAddress(CurrentAddr); + CurrentAddr += NewSize; + BB->setOutputEndAddress(CurrentAddr); + if (BB->getLayoutIndex() == SplitIndex) { + NewHotEndAddr = CurrentAddr; + // Approximate the start address of the warm fragment of the current + // function using the original hot section size. + CurrentAddr = OrigHotSectionSize; + } + OldHotEndAddr = BBOffsets[BB] + BBSizes[BB]; + } + return std::make_pair(OldHotEndAddr, NewHotEndAddr); + } + + /// Get a collection of "shortenable" calls, that is, calls of type X->Y + /// when the function order is [... X ... BF ... Y ...]. + /// If the hot fragment size of BF is reduced, then such calls are guaranteed + /// to get shorter by the reduced hot fragment size. + std::vector extractCoverCalls(const BinaryFunction &BF) { + // Record the length and the count of the calls that can be shortened + std::vector CoverCalls; + if (opts::CallScale == 0) + return CoverCalls; + + const BinaryFunction *ThisBF = &BF; + const BinaryBasicBlock *ThisBB = &(ThisBF->front()); + const size_t ThisGI = GlobalIndices[ThisBB]; + + for (const BinaryFunction *DstBF : BC.getSortedFunctions()) { + if (!shouldConsiderForCallGraph(*DstBF)) + continue; + + const BinaryBasicBlock *DstBB = &(DstBF->front()); + if (DstBB->getKnownExecutionCount() == 0) + continue; + + const size_t DstGI = GlobalIndices[DstBB]; + for (const BinaryBasicBlock *SrcBB : Callers[DstGI]) { + const BinaryFunction *SrcBF = SrcBB->getFunction(); + if (ThisBF == SrcBF) + continue; + + const size_t CallCount = SrcBB->getKnownExecutionCount(); + + const size_t SrcGI = GlobalIndices[SrcBB]; + + const bool IsCoverCall = (SrcGI < ThisGI && ThisGI < DstGI) || + (DstGI <= ThisGI && ThisGI < SrcGI); + if (!IsCoverCall) + continue; + + const size_t SrcBBEndAddr = BBOffsets[SrcBB] + BBSizes[SrcBB]; + const size_t DstBBStartAddr = BBOffsets[DstBB]; + const size_t CallLength = + AbsoluteDifference(SrcBBEndAddr, DstBBStartAddr); + const CallInfo CI{CallLength, CallCount}; + CoverCalls.emplace_back(CI); + } + } + return CoverCalls; + } + + /// Compute the edge score of a call edge. + double computeCallScore(uint64_t CallCount, size_t CallLength) { + // Increase call lengths by 1 to avoid raising 0 to a negative power. + return opts::CallScale * static_cast(CallCount) / + std::pow(static_cast(CallLength + 1), opts::CallPower); + } + + /// Compute the edge score of a jump (branch) edge. + double computeJumpScore(uint64_t JumpCount, size_t JumpLength) { + // Increase jump lengths by 1 to avoid raising 0 to a negative power. + return static_cast(JumpCount) / + std::pow(static_cast(JumpLength + 1), opts::JumpPower); + } + + /// Compute sum of scores over jumps within \p BlockOrder given \p SplitIndex. + /// Increament Score.LocalScore in place by the sum. + void computeJumpScore(const BasicBlockOrder &BlockOrder, + const size_t SplitIndex, SplitScore &Score) { + + for (const BinaryBasicBlock *SrcBB : BlockOrder) { + if (SrcBB->getKnownExecutionCount() == 0) + continue; + + const size_t SrcBBEndAddr = SrcBB->getOutputAddressRange().second; + + for (const auto Pair : zip(SrcBB->successors(), SrcBB->branch_info())) { + const BinaryBasicBlock *DstBB = std::get<0>(Pair); + const BinaryBasicBlock::BinaryBranchInfo &Branch = std::get<1>(Pair); + const size_t JumpCount = Branch.Count; + + if (JumpCount == 0) + continue; + + const size_t DstBBStartAddr = DstBB->getOutputAddressRange().first; + const size_t NewJumpLength = + AbsoluteDifference(SrcBBEndAddr, DstBBStartAddr); + Score.LocalScore += computeJumpScore(JumpCount, NewJumpLength); + } + } + } + + /// Compute sum of scores over calls originated in the current function + /// given \p SplitIndex. Increament Score.LocalScore in place by the sum. + void computeLocalCallScore(const BasicBlockOrder &BlockOrder, + const size_t SplitIndex, SplitScore &Score) { + if (opts::CallScale == 0) + return; + + // Global index of the last block in the current function. + // This is later used to determine whether a call originated in the current + // function is to a function that comes after the current function. + const size_t LastGlobalIndex = GlobalIndices[BlockOrder.back()]; + + // The length of calls originated in the input function can increase / + // decrease depending on the splitting decision. + for (const BinaryBasicBlock *SrcBB : BlockOrder) { + const size_t CallCount = SrcBB->getKnownExecutionCount(); + // If SrcBB does not call any functions, skip it. + if (CallCount == 0) + continue; + + // Obtain an estimate on the end address of the src basic block + // after splitting at SplitIndex. + const size_t SrcBBEndAddr = SrcBB->getOutputAddressRange().second; + + for (const BinaryBasicBlock *DstBB : Callees[GlobalIndices[SrcBB]]) { + // Obtain an estimate on the start address of the dst basic block + // after splitting at SplitIndex. If DstBB is in a function before + // the current function, then its start address remains unchanged. + size_t DstBBStartAddr = BBOffsets[DstBB]; + // If DstBB is in a function after the current function, then its + // start address should be adjusted based on the reduction in hot size. + if (GlobalIndices[DstBB] > LastGlobalIndex) { + assert(DstBBStartAddr >= Score.HotSizeReduction); + DstBBStartAddr -= Score.HotSizeReduction; + } + const size_t NewCallLength = + AbsoluteDifference(SrcBBEndAddr, DstBBStartAddr); + Score.LocalScore += computeCallScore(CallCount, NewCallLength); + } + } + } + + /// Compute sum of splitting scores for cover calls of the input function. + /// Increament Score.CoverCallScore in place by the sum. + void computeCoverCallScore(const BasicBlockOrder &BlockOrder, + const size_t SplitIndex, + const std::vector &CoverCalls, + SplitScore &Score) { + if (opts::CallScale == 0) + return; + + for (const CallInfo CI : CoverCalls) { + assert(CI.Length >= Score.HotSizeReduction && + "Length of cover calls must exceed reduced size of hot fragment."); + // Compute the new length of the call, which is shorter than the original + // one by the size of the splitted fragment minus the total size increase. + const size_t NewCallLength = CI.Length - Score.HotSizeReduction; + Score.CoverCallScore += computeCallScore(CI.Count, NewCallLength); + } + } + + /// Compute the split score of splitting a function at a given index. + /// The split score consists of local score and cover score. Cover call score + /// is expensive to compute. As a result, we pass in a \p ReferenceScore and + /// compute cover score only when the local score exceeds that in the + /// ReferenceScore or that the size reduction of the hot fragment is larger + /// than that achieved by the split index of the ReferenceScore. This function + /// returns \p Score of SplitScore type. It contains the local score and cover + /// score (if computed) of the current splitting index. For easier book + /// keeping and comparison, it also stores the split index and the resulting + /// reduction in hot fragment size. + SplitScore computeSplitScore(const BinaryFunction &BF, + const BasicBlockOrder &BlockOrder, + const size_t SplitIndex, + const std::vector &CoverCalls, + const SplitScore &ReferenceScore) { + // Populate BinaryBasicBlock::OutputAddressRange with estimated + // new start and end addresses after hot-warm splitting at SplitIndex. + size_t OldHotEnd; + size_t NewHotEnd; + std::tie(OldHotEnd, NewHotEnd) = + estimatePostSplitBBAddress(BlockOrder, SplitIndex); + + SplitScore Score; + Score.SplitIndex = SplitIndex; + + // It's not worth splitting if OldHotEnd < NewHotEnd. + if (OldHotEnd < NewHotEnd) + return Score; + + // Hot fragment size reduction due to splitting. + Score.HotSizeReduction = OldHotEnd - NewHotEnd; + + // First part of LocalScore is the sum over call edges originated in the + // input function. These edges can get shorter or longer depending on + // SplitIndex. Score.LocalScore is increamented in place. + computeLocalCallScore(BlockOrder, SplitIndex, Score); + + // Second part of LocalScore is the sum over jump edges with src basic block + // and dst basic block in the current function. Score.LocalScore is + // increamented in place. + computeJumpScore(BlockOrder, SplitIndex, Score); + + // There is no need to compute CoverCallScore if we have already found + // another split index with a bigger LocalScore and bigger HotSizeReduction. + if (Score.LocalScore <= ReferenceScore.LocalScore && + Score.HotSizeReduction <= ReferenceScore.HotSizeReduction) + return Score; + + // Compute CoverCallScore and store in Score in place. + computeCoverCallScore(BlockOrder, SplitIndex, CoverCalls, Score); + return Score; + } + /// Find the best index for splitting. The returned value is the index of the /// last hot basic block. Hence, "no splitting" is equivalent to returning the /// value which is one less than the size of the function. size_t findSplitIndex(const BinaryFunction &BF, const BasicBlockOrder &BlockOrder) { - // Placeholder: hot-warm split after entry block. - return 0; + // Find all function calls that can be shortened if we move blocks of the + // current function to warm/cold + const std::vector CoverCalls = extractCoverCalls(BF); + + // Try all possible split indices (blocks with Index <= SplitIndex are in + // hot) and find the one maximizing the splitting score. + SplitScore BestScore; + double BestScoreSum = -1.0; + SplitScore ReferenceScore; + for (size_t Index = 0; Index < BlockOrder.size(); Index++) { + const BinaryBasicBlock *LastHotBB = BlockOrder[Index]; + // No need to keep cold blocks in the hot section. + if (LastHotBB->getFragmentNum() == FragmentNum::cold()) + break; + const SplitScore Score = + computeSplitScore(BF, BlockOrder, Index, CoverCalls, ReferenceScore); + double ScoreSum = Score.LocalScore + Score.CoverCallScore; + if (ScoreSum > BestScoreSum) { + BestScoreSum = ScoreSum; + BestScore = Score; + } + if (Score.LocalScore > ReferenceScore.LocalScore) + ReferenceScore = Score; + } + + return BestScore.SplitIndex; } }; @@ -308,7 +703,7 @@ void SplitFunctions::runOnFunctions(BinaryContext &BC) { // before function reordering and hot-warm-cold splitting // (SplitCacheDirected) after function reordering. if (BC.HasFinalizedFunctionOrder) - Strategy = std::make_unique(); + Strategy = std::make_unique(BC); else Strategy = std::make_unique(); opts::AggressiveSplitting = true; diff --git a/bolt/lib/Rewrite/DWARFRewriter.cpp b/bolt/lib/Rewrite/DWARFRewriter.cpp index 0beb865bd48cb1fc3575a3a0fb6e7b8e24ab9a8a..5580d85e3fa7716f6b47ea1ea8cd5f286fc95e0d 100644 --- a/bolt/lib/Rewrite/DWARFRewriter.cpp +++ b/bolt/lib/Rewrite/DWARFRewriter.cpp @@ -679,6 +679,8 @@ void DWARFRewriter::updateDebugInfo() { assert(CompDirAttrInfo && "DW_AT_comp_dir is not in Skeleton CU."); if (!opts::DwarfOutputPath.empty()) { + if (!sys::fs::exists(opts::DwarfOutputPath)) + sys::fs::create_directory(opts::DwarfOutputPath); addStringHelper(DIEBldr, UnitDIE, Unit, CompDirAttrInfo, opts::DwarfOutputPath.c_str()); } diff --git a/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp b/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp index b852b9fbc9c52f4d47d986a60e2501b388c636d2..d90512e2122580788a25b9cffe50f571894e40c4 100644 --- a/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp +++ b/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp @@ -314,6 +314,12 @@ public: bool isRegToRegMove(const MCInst &Inst, MCPhysReg &From, MCPhysReg &To) const override { + if (Inst.getOpcode() == AArch64::FMOVDXr) { + From = Inst.getOperand(1).getReg(); + To = Inst.getOperand(0).getReg(); + return true; + } + if (Inst.getOpcode() != AArch64::ORRXrs) return false; if (Inst.getOperand(1).getReg() != AArch64::XZR) diff --git a/bolt/test/AArch64/fp-reg-spill.s b/bolt/test/AArch64/fp-reg-spill.s new file mode 100644 index 0000000000000000000000000000000000000000..65523148edd3fc122f09b6fe24111dada322816c --- /dev/null +++ b/bolt/test/AArch64/fp-reg-spill.s @@ -0,0 +1,19 @@ +# Check that we correctly handle arm64 fp register spills in +# bolt when we are processing jump tables. +# REQUIRES: system-linux +# RUN: llvm-mc -filetype=obj -triple=aarch64 %s -o %t.o +# RUN: ld.lld --emit-relocs %t.o -o %t.elf +# RUN: llvm-bolt --jump-tables=move %t.elf -o %t.bolt + +.globl _foo, _start + +_foo: + ret + +_start: + adr x6, _foo + fmov d18,x6 + fmov x5,d18 + ldrb w5, [x5, w1, uxtw] + add x5, x6, w5, sxtb #2 + br x5 diff --git a/bolt/test/X86/cdsplit-call-scale.s b/bolt/test/X86/cdsplit-call-scale.s new file mode 100644 index 0000000000000000000000000000000000000000..5b4f92832624cd5eb9e2d1f1ffdd601c9893cbf8 --- /dev/null +++ b/bolt/test/X86/cdsplit-call-scale.s @@ -0,0 +1,137 @@ +# Test the control of aggressiveness of 3-way splitting by -call-scale. +# When -call-scale=0.0, the tested function is 2-way splitted. +# When -call-scale=1.0, the tested function is 3-way splitted with 5 blocks +# in warm because of the increased benefit of shortening the call edges. +# When -call-scale=1000.0, the tested function is 3-way splitted with 7 blocks +# in warm because of the strong benefit of shortening the call edges. + +# RUN: llvm-mc --filetype=obj --triple x86_64-unknown-unknown %s -o %t.o +# RUN: link_fdata %s %t.o %t.fdata +# RUN: llvm-strip --strip-unneeded %t.o +# RUN: %clang %cflags %t.o -o %t.exe -Wl,-q +# RUN: llvm-bolt %t.exe -o %t.bolt --split-functions --split-strategy=cdsplit \ +# RUN: --call-scale=0.0 --print-split --print-only=chain \ +# RUN: --data=%t.fdata --reorder-blocks=ext-tsp \ +# RUN: 2>&1 | FileCheck --check-prefix=LOWINCENTIVE %s +# RUN: llvm-bolt %t.exe -o %t.bolt --split-functions --split-strategy=cdsplit \ +# RUN: --call-scale=1.0 --print-split --print-only=chain \ +# RUN: --data=%t.fdata --reorder-blocks=ext-tsp \ +# RUN: 2>&1 | FileCheck --check-prefix=MEDINCENTIVE %s +# RUN: llvm-bolt %t.exe -o %t.bolt --split-functions --split-strategy=cdsplit \ +# RUN: --call-scale=1000.0 --print-split --print-only=chain \ +# RUN: --data=%t.fdata --reorder-blocks=ext-tsp \ +# RUN: 2>&1 | FileCheck --check-prefix=HIGHINCENTIVE %s + +# LOWINCENTIVE: Binary Function "chain" after split-functions +# LOWINCENTIVE: {{^\.Ltmp5}} +# LOWINCENTIVE: ------- HOT-COLD SPLIT POINT ------- +# LOWINCENTIVE: {{^\.LFT1}} + +# MEDINCENTIVE: Binary Function "chain" after split-functions +# MEDINCENTIVE: {{^\.Ltmp1}} +# MEDINCENTIVE: ------- HOT-COLD SPLIT POINT ------- +# MEDINCENTIVE: {{^\.LFT1}} +# MEDINCENTIVE: ------- HOT-COLD SPLIT POINT ------- +# MEDINCENTIVE: {{^\.Ltmp0}} +# MEDINCENTIVE: {{^\.Ltmp2}} +# MEDINCENTIVE: {{^\.Ltmp3}} +# MEDINCENTIVE: {{^\.Ltmp4}} +# MEDINCENTIVE: {{^\.Ltmp5}} + +# HIGHINCENTIVE: Binary Function "chain" after split-functions +# HIGHINCENTIVE: {{^\.LBB00}} +# HIGHINCENTIVE: ------- HOT-COLD SPLIT POINT ------- +# HIGHINCENTIVE: {{^\.LFT1}} +# HIGHINCENTIVE: ------- HOT-COLD SPLIT POINT ------- +# HIGHINCENTIVE: {{^\.LFT0}} +# HIGHINCENTIVE: {{^\.Ltmp1}} +# HIGHINCENTIVE: {{^\.Ltmp0}} +# HIGHINCENTIVE: {{^\.Ltmp2}} +# HIGHINCENTIVE: {{^\.Ltmp3}} +# HIGHINCENTIVE: {{^\.Ltmp4}} +# HIGHINCENTIVE: {{^\.Ltmp5}} + + + + .text + .globl chain + .type chain, @function +chain: + pushq %rbp + movq %rsp, %rbp + cmpl $2, %edi +LLentry_LLchain_start: + jge LLchain_start +# FDATA: 1 chain #LLentry_LLchain_start# 1 chain #LLchain_start# 0 10 +# FDATA: 1 chain #LLentry_LLchain_start# 1 chain #LLfast# 0 500 +LLfast: + movl $5, %eax +LLfast_LLexit: + jmp LLexit +# FDATA: 1 chain #LLfast_LLexit# 1 chain #LLexit# 0 500 +LLchain_start: + movl $10, %eax +LLchain_start_LLchain1: + jge LLchain1 +# FDATA: 1 chain #LLchain_start_LLchain1# 1 chain #LLchain1# 0 10 +# FDATA: 1 chain #LLchain_start_LLchain1# 1 chain #LLcold# 0 0 +LLcold: + addl $1, %eax + addl $1, %eax + addl $1, %eax + addl $1, %eax + addl $1, %eax + addl $1, %eax +LLchain1: + addl $1, %eax +LLchain1_LLchain2: + jmp LLchain2 +# FDATA: 1 chain #LLchain1_LLchain2# 1 chain #LLchain2# 0 10 +LLchain2: + addl $1, %eax +LLchain2_LLchain3: + jmp LLchain3 +# FDATA: 1 chain #LLchain2_LLchain3# 1 chain #LLchain3# 0 10 +LLchain3: + addl $1, %eax + addl $1, %eax + addl $1, %eax + addl $1, %eax + addl $1, %eax +LLchain3_LLchain4: + jmp LLchain4 +# FDATA: 1 chain #LLchain3_LLchain4# 1 chain #LLchain4# 0 10 +LLchain4: + addl $1, %eax + addl $1, %eax + addl $1, %eax + addl $1, %eax + addl $1, %eax +LLchain4_LLexit: + jmp LLexit +# FDATA: 1 chain #LLchain4_LLexit# 1 chain #LLexit# 0 10 +LLexit: + popq %rbp + ret +LLchain_end: + .size chain, LLchain_end-chain + + + .globl main + .type main, @function +main: + pushq %rbp + movq %rsp, %rbp + movl $1, %edi +LLmain_chain1: + call chain +# FDATA: 1 main #LLmain_chain1# 1 chain 0 0 500 + movl $4, %edi +LLmain_chain2: + call chain +# FDATA: 1 main #LLmain_chain2# 1 chain 0 0 10 + xorl %eax, %eax + popq %rbp + retq +.Lmain_end: + .size main, .Lmain_end-main diff --git a/bolt/test/X86/cdsplit-symbol-names.s b/bolt/test/X86/cdsplit-symbol-names.s new file mode 100644 index 0000000000000000000000000000000000000000..e2259276e2554c406223f9d209a52863fa184ab0 --- /dev/null +++ b/bolt/test/X86/cdsplit-symbol-names.s @@ -0,0 +1,147 @@ +# Test the correctness of section names and function symbol names post cdsplit. +# Warm section should have name .text.warm and warm function fragments should +# have symbol names ending in warm. + +# RUN: llvm-mc --filetype=obj --triple x86_64-unknown-unknown %s -o %t.o +# RUN: link_fdata %s %t.o %t.fdata +# RUN: llvm-strip --strip-unneeded %t.o +# RUN: %clang %cflags %t.o -o %t.exe -Wl,-q +# RUN: llvm-bolt %t.exe -o %t.bolt --split-functions --split-strategy=cdsplit \ +# RUN: --call-scale=2 --data=%t.fdata --reorder-blocks=ext-tsp +# RUN: llvm-objdump --syms %t.bolt | FileCheck %s --check-prefix=CHECK-SYMS-WARM + +# CHECK-SYMS-WARM: .text.warm +# CHECK-SYMS-WARM-SAME: chain.warm +# CHECK-SYMS-WARM: .text.cold +# CHECK-SYMS-WARM-SAME: dummy.cold + + .text + .globl chain + .type chain, @function +chain: + pushq %rbp + movq %rsp, %rbp + cmpl $2, %edi +LLentry_LLchain_start: + jge LLchain_start +# FDATA: 1 chain #LLentry_LLchain_start# 1 chain #LLchain_start# 0 100 +# FDATA: 1 chain #LLentry_LLchain_start# 1 chain #LLfast# 0 500 +LLfast: + movl $5, %eax +LLfast_LLexit: + jmp LLexit +# FDATA: 1 chain #LLfast_LLexit# 1 chain #LLexit# 0 500 +LLchain_start: + movl $10, %eax +LLchain_start_LLchain1: + jge LLchain1 +# FDATA: 1 chain #LLchain_start_LLchain1# 1 chain #LLchain1# 0 99 +# FDATA: 1 chain #LLchain_start_LLchain1# 1 chain #LLloop_entry# 0 1 +LLloop_entry: + addl $1, %eax + addl $1, %eax + addl $1, %eax + addl $1, %eax + xorl %eax, %eax // Initialize result + movl $1000000, %ecx // Set loop counter to a large value +LLloop_entry_LLloop_start: + jmp LLloop_start +# FDATA: 1 chain #LLloop_entry_LLloop_start# 1 chain #LLloop_start# 0 1 +LLloop_start: + addl $1, %eax // Increment result + subl $1, %ecx // Decrement loop counter +LLloop_start_LLloop_start: + jg LLloop_start // Jump if loop counter is greater than 0 +# FDATA: 1 chain #LLloop_start_LLloop_start# 1 chain #LLloop_start# 0 1000000 +# FDATA: 1 chain #LLloop_start_LLloop_start# 1 chain #LLchain1# 0 1 +LLchain1: + addl $1, %eax +LLchain1_LLchain2: + jmp LLchain2 +# FDATA: 1 chain #LLchain1_LLchain2# 1 chain #LLchain2# 0 100 +LLchain2: + addl $1, %eax +LLchain2_LLchain3: + jmp LLchain3 +# FDATA: 1 chain #LLchain2_LLchain3# 1 chain #LLchain3# 0 100 +LLchain3: + addl $1, %eax + addl $1, %eax + addl $1, %eax + addl $1, %eax + addl $1, %eax +LLchain3_LLchain4: + jmp LLchain4 +# FDATA: 1 chain #LLchain3_LLchain4# 1 chain #LLchain4# 0 100 +LLchain4: + addl $1, %eax + addl $1, %eax + addl $1, %eax + addl $1, %eax + addl $1, %eax +LLchain4_LLexit: + jmp LLexit +# FDATA: 1 chain #LLchain4_LLexit# 1 chain #LLexit# 0 100 +LLexit: + popq %rbp + ret +LLchain_end: + .size chain, LLchain_end-chain + + .text + .globl dummy + .type dummy, @function +dummy: + pushq %rbp + movq %rsp, %rbp + cmpl $2, %edi +dummy_dummy_block1: + jg dummy_block1 +# FDATA: 1 dummy #dummy_dummy_block1# 1 dummy #dummy_block1# 0 0 +# FDATA: 1 dummy #dummy_dummy_block1# 1 dummy #dummy_next# 0 100 +dummy_next: + addl $1, %eax + addl $1, %eax +dummy_next_dummy_exit: + jmp dummy_exit +# FDATA: 1 dummy #dummy_next_dummy_exit# 1 dummy #dummy_exit# 0 100 +dummy_block1: + addl $1, %eax + addl $1, %eax + addl $1, %eax + addl $1, %eax + addl $1, %eax +dummy_block1_dummy_block2: + jmp dummy_block2 +# FDATA: 1 dummy #dummy_block1_dummy_block2# 1 dummy #dummy_block2# 0 0 +dummy_block2: + addl $1, %eax + addl $1, %eax + addl $1, %eax + addl $1, %eax + addl $1, %eax +dummy_block2_dummy_exit: + jmp dummy_exit +# FDATA: 1 dummy #dummy_block2_dummy_exit# 1 dummy #dummy_exit# 0 0 +dummy_exit: + popq %rbp + ret + + .globl main + .type main, @function +main: + pushq %rbp + movq %rsp, %rbp + movl $1, %edi +LLmain_chain1: + call chain +# FDATA: 1 main #LLmain_chain1# 1 chain 0 0 600 + movl $4, %edi +LLmain_dummy: + call dummy +# FDATA: 1 main #LLmain_dummy# 1 dummy 0 0 100 + xorl %eax, %eax + popq %rbp + retq +.Lmain_end: + .size main, .Lmain_end-main diff --git a/bolt/test/X86/dwarf5-df-output-dir-same-name.test b/bolt/test/X86/dwarf5-df-output-dir-same-name.test index 4fd42e287a11b71fd0f54cd13f31d0ed94c4e524..1f78da2022b8ced39b8be57fcb458f11b1ffea24 100644 --- a/bolt/test/X86/dwarf5-df-output-dir-same-name.test +++ b/bolt/test/X86/dwarf5-df-output-dir-same-name.test @@ -21,3 +21,19 @@ ; BOLT: split.dwo1.dwo ; BOLT: DW_AT_dwo_name ("split.dwo0.dwo") ; BOLT: DW_AT_dwo_name ("split.dwo1.dwo") + +; Tests that when --dwarf-output-path is specified, but path do not exist BOLT creates it. + +; RUN: rm -rf dwo +; RUN: llvm-bolt main.exe -o main.exe.bolt --update-debug-sections --dwarf-output-path=%t/dwo +; RUN: ls -l %t/dwo > log +; RUN: llvm-dwarfdump --debug-info main.exe.bolt >> log +; RUN: cat log | FileCheck -check-prefix=BOLT1 %s + +; Tests that BOLT handles correctly writing out .dwo files to the same directory when input has input where part of path +; is in DW_AT_dwo_name and the .dwo file names are the same. + +; BOLT1: split.dwo0.dwo +; BOLT1: split.dwo1.dwo +; BOLT1: DW_AT_dwo_name ("split.dwo0.dwo") +; BOLT1: DW_AT_dwo_name ("split.dwo1.dwo") diff --git a/bolt/test/runtime/AArch64/instrumentation-ind-call.c b/bolt/test/runtime/AArch64/instrumentation-ind-call.c index 76ee8c05dd2998dd708bb719f82dda4e1fb8115c..f9056da333b4e88723a32669cc03b102ddb45f07 100644 --- a/bolt/test/runtime/AArch64/instrumentation-ind-call.c +++ b/bolt/test/runtime/AArch64/instrumentation-ind-call.c @@ -14,7 +14,7 @@ int main() { /* REQUIRES: system-linux,bolt-runtime -RUN: %clang %cflags %s -o %t.exe -Wl,-q -nopie -fpie +RUN: %clang %cflags %s -o %t.exe -Wl,-q -no-pie -fpie RUN: llvm-bolt %t.exe --instrument --instrumentation-file=%t.fdata \ RUN: -o %t.instrumented diff --git a/bolt/utils/llvm-bolt-wrapper.py b/bolt/utils/llvm-bolt-wrapper.py index cd48204026fb77cedb3afa400d90fceaeb1d21ae..b9d6fad825e7864137b277230065b275c64e4e37 100755 --- a/bolt/utils/llvm-bolt-wrapper.py +++ b/bolt/utils/llvm-bolt-wrapper.py @@ -112,7 +112,6 @@ def run_cmd(cmd, out_f, cfg): def run_bolt(bolt_path, bolt_args, out_f, cfg): p2b = os.path.basename(sys.argv[0]) == "perf2bolt" # perf2bolt mode bd = os.path.basename(sys.argv[0]) == "llvm-boltdiff" # boltdiff mode - hm = sys.argv[1] == "heatmap" # heatmap mode cmd = ["/usr/bin/time", "-f", "%e %M", bolt_path] + bolt_args if p2b: # -ignore-build-id can occur at most once, hence remove it from cmd @@ -121,7 +120,7 @@ def run_bolt(bolt_path, bolt_args, out_f, cfg): cmd += PERF2BOLT_MODE elif bd: cmd += BOLTDIFF_MODE - elif not cfg.NO_MINIMIZE and not hm: + elif not cfg.NO_MINIMIZE: cmd += MINIMIZE_DIFFS return run_cmd(cmd, out_f, cfg) diff --git a/bolt/utils/nfc-stat-parser.py b/bolt/utils/nfc-stat-parser.py index 72a21e0e24a1a7ddfd4be700e33b5a8c1c8d32e8..4a0d677ec962b3046e848b1a6408df208d6841dc 100755 --- a/bolt/utils/nfc-stat-parser.py +++ b/bolt/utils/nfc-stat-parser.py @@ -19,7 +19,7 @@ def main(): ) parser.add_argument( "--check_longer_than", - default=1, + default=2, type=float, help="Only warn on tests longer than X seconds for at least one side", ) diff --git a/clang-tools-extra/clang-tidy/ClangTidy.cpp b/clang-tools-extra/clang-tidy/ClangTidy.cpp index 4b1a67b6dd98a94ce891230347cc26f903bf856d..565f044778c946397261584fde73454c9549715a 100644 --- a/clang-tools-extra/clang-tidy/ClangTidy.cpp +++ b/clang-tools-extra/clang-tidy/ClangTidy.cpp @@ -147,7 +147,8 @@ public: Files.makeAbsolutePath(FixAbsoluteFilePath); tooling::Replacement R(FixAbsoluteFilePath, Repl.getOffset(), Repl.getLength(), Repl.getReplacementText()); - Replacements &Replacements = FileReplacements[R.getFilePath()]; + auto &Entry = FileReplacements[R.getFilePath()]; + Replacements &Replacements = Entry.Replaces; llvm::Error Err = Replacements.add(R); if (Err) { // FIXME: Implement better conflict handling. @@ -174,6 +175,7 @@ public: } FixLoc = getLocation(FixAbsoluteFilePath, Repl.getOffset()); FixLocations.push_back(std::make_pair(FixLoc, CanBeApplied)); + Entry.BuildDir = Error.BuildDirectory; } } } @@ -189,9 +191,14 @@ public: void finish() { if (TotalFixes > 0) { - Rewriter Rewrite(SourceMgr, LangOpts); + auto &VFS = Files.getVirtualFileSystem(); + auto OriginalCWD = VFS.getCurrentWorkingDirectory(); + bool AnyNotWritten = false; + for (const auto &FileAndReplacements : FileReplacements) { + Rewriter Rewrite(SourceMgr, LangOpts); StringRef File = FileAndReplacements.first(); + VFS.setCurrentWorkingDirectory(FileAndReplacements.second.BuildDir); llvm::ErrorOr> Buffer = SourceMgr.getFileManager().getBufferForFile(File); if (!Buffer) { @@ -208,8 +215,8 @@ public: continue; } llvm::Expected Replacements = - format::cleanupAroundReplacements(Code, FileAndReplacements.second, - *Style); + format::cleanupAroundReplacements( + Code, FileAndReplacements.second.Replaces, *Style); if (!Replacements) { llvm::errs() << llvm::toString(Replacements.takeError()) << "\n"; continue; @@ -226,13 +233,18 @@ public: if (!tooling::applyAllReplacements(Replacements.get(), Rewrite)) { llvm::errs() << "Can't apply replacements for file " << File << "\n"; } + AnyNotWritten &= Rewrite.overwriteChangedFiles(); } - if (Rewrite.overwriteChangedFiles()) { + + if (AnyNotWritten) { llvm::errs() << "clang-tidy failed to apply suggested fixes.\n"; } else { llvm::errs() << "clang-tidy applied " << AppliedFixes << " of " << TotalFixes << " suggested fixes.\n"; } + + if (OriginalCWD) + VFS.setCurrentWorkingDirectory(*OriginalCWD); } } @@ -289,13 +301,18 @@ private: return CharSourceRange::getCharRange(BeginLoc, EndLoc); } + struct ReplacementsWithBuildDir { + StringRef BuildDir; + Replacements Replaces; + }; + FileManager Files; LangOptions LangOpts; // FIXME: use langopts from each original file IntrusiveRefCntPtr DiagOpts; DiagnosticConsumer *DiagPrinter; DiagnosticsEngine Diags; SourceManager SourceMgr; - llvm::StringMap FileReplacements; + llvm::StringMap FileReplacements; ClangTidyContext &Context; FixBehaviour ApplyFixes; unsigned TotalFixes = 0U; diff --git a/clang-tools-extra/clang-tidy/abseil/StringFindStartswithCheck.h b/clang-tools-extra/clang-tidy/abseil/StringFindStartswithCheck.h index 923b5caece5439b1a85bb4ba3627a565b27046a5..de3bd4d4222000998bd2ad0624056e245184efd6 100644 --- a/clang-tools-extra/clang-tidy/abseil/StringFindStartswithCheck.h +++ b/clang-tools-extra/clang-tidy/abseil/StringFindStartswithCheck.h @@ -21,7 +21,6 @@ namespace clang::tidy::abseil { // Find string.find(...) == 0 comparisons and suggest replacing with StartsWith. // FIXME(niko): Add similar check for EndsWith -// FIXME(niko): Add equivalent modernize checks for C++20's std::starts_With class StringFindStartswithCheck : public ClangTidyCheck { public: using ClangTidyCheck::ClangTidyCheck; @@ -31,6 +30,10 @@ public: void registerMatchers(ast_matchers::MatchFinder *Finder) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override; void storeOptions(ClangTidyOptions::OptionMap &Opts) override; + bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { + // Prefer modernize-use-starts-ends-with when C++20 is available. + return LangOpts.CPlusPlus && !LangOpts.CPlusPlus20; + } private: const std::vector StringLikeClasses; diff --git a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt index 717c400c479033002a1dd8347a24f5f7283f9b4c..28ca52f46943a84de51946ec9660a0ee55be9a90 100644 --- a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt @@ -38,6 +38,8 @@ add_clang_library(clangTidyModernizeModule UseNoexceptCheck.cpp UseNullptrCheck.cpp UseOverrideCheck.cpp + UseStartsEndsWithCheck.cpp + UseStdNumbersCheck.cpp UseStdPrintCheck.cpp UseTrailingReturnTypeCheck.cpp UseTransparentFunctorsCheck.cpp diff --git a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp index 73751cf2705068d92c538b7f63d73a16a47549ed..654f4bd0c6ba47275f4053e5cff5abac017cba48 100644 --- a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp @@ -39,6 +39,8 @@ #include "UseNoexceptCheck.h" #include "UseNullptrCheck.h" #include "UseOverrideCheck.h" +#include "UseStartsEndsWithCheck.h" +#include "UseStdNumbersCheck.h" #include "UseStdPrintCheck.h" #include "UseTrailingReturnTypeCheck.h" #include "UseTransparentFunctorsCheck.h" @@ -66,6 +68,10 @@ public: CheckFactories.registerCheck("modernize-make-shared"); CheckFactories.registerCheck("modernize-make-unique"); CheckFactories.registerCheck("modernize-pass-by-value"); + CheckFactories.registerCheck( + "modernize-use-starts-ends-with"); + CheckFactories.registerCheck( + "modernize-use-std-numbers"); CheckFactories.registerCheck("modernize-use-std-print"); CheckFactories.registerCheck( "modernize-raw-string-literal"); diff --git a/clang-tools-extra/clang-tidy/modernize/UseStartsEndsWithCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStartsEndsWithCheck.cpp new file mode 100644 index 0000000000000000000000000000000000000000..062f6e9911dbed35d3848a8cf7e1202ae1ec85ba --- /dev/null +++ b/clang-tools-extra/clang-tidy/modernize/UseStartsEndsWithCheck.cpp @@ -0,0 +1,109 @@ +//===--- UseStartsEndsWithCheck.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 "UseStartsEndsWithCheck.h" + +#include "../utils/OptionsUtils.h" +#include "clang/Lex/Lexer.h" + +#include + +using namespace clang::ast_matchers; + +namespace clang::tidy::modernize { + +UseStartsEndsWithCheck::UseStartsEndsWithCheck(StringRef Name, + ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + +void UseStartsEndsWithCheck::registerMatchers(MatchFinder *Finder) { + const auto ZeroLiteral = integerLiteral(equals(0)); + const auto HasStartsWithMethodWithName = [](const std::string &Name) { + return hasMethod( + cxxMethodDecl(hasName(Name), isConst(), parameterCountIs(1)) + .bind("starts_with_fun")); + }; + const auto HasStartsWithMethod = + anyOf(HasStartsWithMethodWithName("starts_with"), + HasStartsWithMethodWithName("startsWith"), + HasStartsWithMethodWithName("startswith")); + const auto ClassWithStartsWithFunction = cxxRecordDecl(anyOf( + HasStartsWithMethod, hasAnyBase(hasType(hasCanonicalType(hasDeclaration( + cxxRecordDecl(HasStartsWithMethod))))))); + + const auto FindExpr = cxxMemberCallExpr( + // A method call with no second argument or the second argument is zero... + anyOf(argumentCountIs(1), hasArgument(1, ZeroLiteral)), + // ... named find... + callee(cxxMethodDecl(hasName("find")).bind("find_fun")), + // ... on a class with a starts_with function. + on(hasType( + hasCanonicalType(hasDeclaration(ClassWithStartsWithFunction))))); + + const auto RFindExpr = cxxMemberCallExpr( + // A method call with a second argument of zero... + hasArgument(1, ZeroLiteral), + // ... named rfind... + callee(cxxMethodDecl(hasName("rfind")).bind("find_fun")), + // ... on a class with a starts_with function. + on(hasType( + hasCanonicalType(hasDeclaration(ClassWithStartsWithFunction))))); + + const auto FindOrRFindExpr = + cxxMemberCallExpr(anyOf(FindExpr, RFindExpr)).bind("find_expr"); + + Finder->addMatcher( + // Match [=!]= with a zero on one side and a string.(r?)find on the other. + binaryOperator(hasAnyOperatorName("==", "!="), + hasOperands(FindOrRFindExpr, ZeroLiteral)) + .bind("expr"), + this); +} + +void UseStartsEndsWithCheck::check(const MatchFinder::MatchResult &Result) { + const auto *ComparisonExpr = Result.Nodes.getNodeAs("expr"); + const auto *FindExpr = Result.Nodes.getNodeAs("find_expr"); + const auto *FindFun = Result.Nodes.getNodeAs("find_fun"); + const auto *StartsWithFunction = + Result.Nodes.getNodeAs("starts_with_fun"); + + if (ComparisonExpr->getBeginLoc().isMacroID()) { + return; + } + + const bool Neg = ComparisonExpr->getOpcode() == BO_NE; + + auto Diagnostic = + diag(FindExpr->getBeginLoc(), "use %0 instead of %1() %select{==|!=}2 0") + << StartsWithFunction->getName() << FindFun->getName() << Neg; + + // Remove possible zero second argument and ' [!=]= 0' suffix. + Diagnostic << FixItHint::CreateReplacement( + CharSourceRange::getTokenRange( + Lexer::getLocForEndOfToken(FindExpr->getArg(0)->getEndLoc(), 0, + *Result.SourceManager, getLangOpts()), + ComparisonExpr->getEndLoc()), + ")"); + + // Remove possible '0 [!=]= ' prefix. + Diagnostic << FixItHint::CreateRemoval(CharSourceRange::getCharRange( + ComparisonExpr->getBeginLoc(), FindExpr->getBeginLoc())); + + // Replace '(r?)find' with 'starts_with'. + Diagnostic << FixItHint::CreateReplacement( + CharSourceRange::getTokenRange(FindExpr->getExprLoc(), + FindExpr->getExprLoc()), + StartsWithFunction->getName()); + + // Add possible negation '!'. + if (Neg) { + Diagnostic << FixItHint::CreateInsertion(FindExpr->getBeginLoc(), "!"); + } +} + +} // namespace clang::tidy::modernize diff --git a/clang-tools-extra/clang-tidy/modernize/UseStartsEndsWithCheck.h b/clang-tools-extra/clang-tidy/modernize/UseStartsEndsWithCheck.h new file mode 100644 index 0000000000000000000000000000000000000000..34e97177682575c97849f542f5679a3366ba0ec6 --- /dev/null +++ b/clang-tools-extra/clang-tidy/modernize/UseStartsEndsWithCheck.h @@ -0,0 +1,37 @@ +//===--- UseStartsEndsWithCheck.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_MODERNIZE_USESTARTSENDSWITHCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USESTARTSENDSWITHCHECK_H + +#include "../ClangTidyCheck.h" + +namespace clang::tidy::modernize { + +/// Checks whether a ``find`` or ``rfind`` result is compared with 0 and +/// suggests replacing with ``starts_with`` when the method exists in the class. +/// Notably, this will work with ``std::string`` and ``std::string_view``. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/modernize/use-starts-ends-with.html +class UseStartsEndsWithCheck : public ClangTidyCheck { +public: + UseStartsEndsWithCheck(StringRef Name, ClangTidyContext *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; + } + std::optional getCheckTraversalKind() const override { + return TK_IgnoreUnlessSpelledInSource; + } +}; + +} // namespace clang::tidy::modernize + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USESTARTSENDSWITHCHECK_H diff --git a/clang-tools-extra/clang-tidy/modernize/UseStdNumbersCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStdNumbersCheck.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b299afd540b9a3cf4a71babcc04e8077988ef235 --- /dev/null +++ b/clang-tools-extra/clang-tidy/modernize/UseStdNumbersCheck.cpp @@ -0,0 +1,448 @@ +//===--- UseStdNumbersCheck.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 "UseStdNumbersCheck.h" +#include "../ClangTidyDiagnosticConsumer.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" +#include "clang/AST/Expr.h" +#include "clang/AST/Stmt.h" +#include "clang/AST/Type.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/ASTMatchers/ASTMatchers.h" +#include "clang/ASTMatchers/ASTMatchersInternal.h" +#include "clang/ASTMatchers/ASTMatchersMacros.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/LLVM.h" +#include "clang/Basic/LangOptions.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Lex/Lexer.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/MathExtras.h" +#include +#include +#include +#include +#include +#include +#include + +namespace { +using namespace clang::ast_matchers; +using clang::ast_matchers::internal::Matcher; +using llvm::StringRef; + +AST_MATCHER_P2(clang::FloatingLiteral, near, double, Value, double, + DiffThreshold) { + return std::abs(Node.getValueAsApproximateDouble() - Value) < DiffThreshold; +} + +AST_MATCHER_P(clang::QualType, hasCanonicalTypeUnqualified, + Matcher, InnerMatcher) { + return !Node.isNull() && + InnerMatcher.matches(Node->getCanonicalTypeUnqualified(), Finder, + Builder); +} + +AST_MATCHER(clang::QualType, isArithmetic) { + return !Node.isNull() && Node->isArithmeticType(); +} +AST_MATCHER(clang::QualType, isFloating) { + return !Node.isNull() && Node->isFloatingType(); +} + +AST_MATCHER_P(clang::Expr, anyOfExhaustive, std::vector>, + Exprs) { + bool FoundMatch = false; + for (const auto &InnerMatcher : Exprs) { + clang::ast_matchers::internal::BoundNodesTreeBuilder Result = *Builder; + if (InnerMatcher.matches(Node, Finder, &Result)) { + *Builder = std::move(Result); + FoundMatch = true; + } + } + return FoundMatch; +} + +// Using this struct to store the 'DiffThreshold' config value to create the +// matchers without the need to pass 'DiffThreshold' into every matcher. +// 'DiffThreshold' is needed in the 'near' matcher, which is used for matching +// the literal of every constant and for formulas' subexpressions that look at +// literals. +struct MatchBuilder { + auto + ignoreParenAndArithmeticCasting(const Matcher Matcher) const { + return expr(hasType(qualType(isArithmetic())), ignoringParenCasts(Matcher)); + } + + auto ignoreParenAndFloatingCasting(const Matcher Matcher) const { + return expr(hasType(qualType(isFloating())), ignoringParenCasts(Matcher)); + } + + auto matchMathCall(const StringRef FunctionName, + const Matcher ArgumentMatcher) const { + return expr(ignoreParenAndFloatingCasting( + callExpr(callee(functionDecl(hasName(FunctionName), + hasParameter(0, hasType(isArithmetic())))), + hasArgument(0, ArgumentMatcher)))); + } + + auto matchSqrt(const Matcher ArgumentMatcher) const { + return matchMathCall("sqrt", ArgumentMatcher); + } + + // Used for top-level matchers (i.e. the match that replaces Val with its + // constant). + // + // E.g. The matcher of `std::numbers::pi` uses this matcher to look for + // floatLiterals that have the value of pi. + // + // If the match is for a top-level match, we only care about the literal. + auto matchFloatLiteralNear(const StringRef Constant, const double Val) const { + return expr(ignoreParenAndFloatingCasting( + floatLiteral(near(Val, DiffThreshold)).bind(Constant))); + } + + // Used for non-top-level matchers (i.e. matchers that are used as inner + // matchers for top-level matchers). + // + // E.g.: The matcher of `std::numbers::log2e` uses this matcher to check if + // `e` of `log2(e)` is declared constant and initialized with the value for + // eulers number. + // + // Here, we do care about literals and about DeclRefExprs to variable + // declarations that are constant and initialized with `Val`. This allows + // top-level matchers to see through declared constants for their inner + // matches like the `std::numbers::log2e` matcher. + auto matchFloatValueNear(const double Val) const { + const auto Float = floatLiteral(near(Val, DiffThreshold)); + + const auto Dref = declRefExpr( + to(varDecl(hasType(qualType(isConstQualified(), isFloating())), + hasInitializer(ignoreParenAndFloatingCasting(Float))))); + return expr(ignoreParenAndFloatingCasting(anyOf(Float, Dref))); + } + + auto matchValue(const int64_t ValInt) const { + const auto Int = + expr(ignoreParenAndArithmeticCasting(integerLiteral(equals(ValInt)))); + const auto Float = expr(ignoreParenAndFloatingCasting( + matchFloatValueNear(static_cast(ValInt)))); + const auto Dref = declRefExpr(to(varDecl( + hasType(qualType(isConstQualified(), isArithmetic())), + hasInitializer(expr(anyOf(ignoringImplicit(Int), + ignoreParenAndFloatingCasting(Float))))))); + return expr(anyOf(Int, Float, Dref)); + } + + auto match1Div(const Matcher Match) const { + return binaryOperator(hasOperatorName("/"), hasLHS(matchValue(1)), + hasRHS(Match)); + } + + auto matchEuler() const { + return expr(anyOf(matchFloatValueNear(llvm::numbers::e), + matchMathCall("exp", matchValue(1)))); + } + auto matchEulerTopLevel() const { + return expr(anyOf(matchFloatLiteralNear("e_literal", llvm::numbers::e), + matchMathCall("exp", matchValue(1)).bind("e_pattern"))) + .bind("e"); + } + + auto matchLog2Euler() const { + return expr( + anyOf( + matchFloatLiteralNear("log2e_literal", llvm::numbers::log2e), + matchMathCall("log2", matchEuler()).bind("log2e_pattern"))) + .bind("log2e"); + } + + auto matchLog10Euler() const { + return expr( + anyOf( + matchFloatLiteralNear("log10e_literal", + llvm::numbers::log10e), + matchMathCall("log10", matchEuler()).bind("log10e_pattern"))) + .bind("log10e"); + } + + auto matchPi() const { return matchFloatValueNear(llvm::numbers::pi); } + auto matchPiTopLevel() const { + return matchFloatLiteralNear("pi_literal", llvm::numbers::pi).bind("pi"); + } + + auto matchEgamma() const { + return matchFloatLiteralNear("egamma_literal", llvm::numbers::egamma) + .bind("egamma"); + } + + auto matchInvPi() const { + return expr(anyOf(matchFloatLiteralNear("inv_pi_literal", + llvm::numbers::inv_pi), + match1Div(matchPi()).bind("inv_pi_pattern"))) + .bind("inv_pi"); + } + + auto matchInvSqrtPi() const { + return expr(anyOf( + matchFloatLiteralNear("inv_sqrtpi_literal", + llvm::numbers::inv_sqrtpi), + match1Div(matchSqrt(matchPi())).bind("inv_sqrtpi_pattern"))) + .bind("inv_sqrtpi"); + } + + auto matchLn2() const { + return expr(anyOf(matchFloatLiteralNear("ln2_literal", llvm::numbers::ln2), + matchMathCall("log", matchValue(2)).bind("ln2_pattern"))) + .bind("ln2"); + } + + auto machterLn10() const { + return expr( + anyOf(matchFloatLiteralNear("ln10_literal", llvm::numbers::ln10), + matchMathCall("log", matchValue(10)).bind("ln10_pattern"))) + .bind("ln10"); + } + + auto matchSqrt2() const { + return expr(anyOf(matchFloatLiteralNear("sqrt2_literal", + llvm::numbers::sqrt2), + matchSqrt(matchValue(2)).bind("sqrt2_pattern"))) + .bind("sqrt2"); + } + + auto matchSqrt3() const { + return expr(anyOf(matchFloatLiteralNear("sqrt3_literal", + llvm::numbers::sqrt3), + matchSqrt(matchValue(3)).bind("sqrt3_pattern"))) + .bind("sqrt3"); + } + + auto matchInvSqrt3() const { + return expr(anyOf(matchFloatLiteralNear("inv_sqrt3_literal", + llvm::numbers::inv_sqrt3), + match1Div(matchSqrt(matchValue(3))) + .bind("inv_sqrt3_pattern"))) + .bind("inv_sqrt3"); + } + + auto matchPhi() const { + const auto PhiFormula = binaryOperator( + hasOperatorName("/"), + hasLHS(binaryOperator( + hasOperatorName("+"), hasEitherOperand(matchValue(1)), + hasEitherOperand(matchMathCall("sqrt", matchValue(5))))), + hasRHS(matchValue(2))); + return expr(anyOf(PhiFormula.bind("phi_pattern"), + matchFloatLiteralNear("phi_literal", llvm::numbers::phi))) + .bind("phi"); + } + + double DiffThreshold; +}; + +std::string getCode(const StringRef Constant, const bool IsFloat, + const bool IsLongDouble) { + if (IsFloat) { + return ("std::numbers::" + Constant + "_v").str(); + } + if (IsLongDouble) { + return ("std::numbers::" + Constant + "_v").str(); + } + return ("std::numbers::" + Constant).str(); +} + +bool isRangeOfCompleteMacro(const clang::SourceRange &Range, + const clang::SourceManager &SM, + const clang::LangOptions &LO) { + if (!Range.getBegin().isMacroID()) { + return false; + } + if (!clang::Lexer::isAtStartOfMacroExpansion(Range.getBegin(), SM, LO)) { + return false; + } + + if (!Range.getEnd().isMacroID()) { + return false; + } + + if (!clang::Lexer::isAtEndOfMacroExpansion(Range.getEnd(), SM, LO)) { + return false; + } + + return true; +} + +} // namespace + +namespace clang::tidy::modernize { +UseStdNumbersCheck::UseStdNumbersCheck(const StringRef Name, + ClangTidyContext *const Context) + : ClangTidyCheck(Name, Context), + IncludeInserter(Options.getLocalOrGlobal("IncludeStyle", + utils::IncludeSorter::IS_LLVM), + areDiagsSelfContained()), + DiffThresholdString{Options.get("DiffThreshold", "0.001")} { + if (DiffThresholdString.getAsDouble(DiffThreshold)) { + configurationDiag( + "Invalid DiffThreshold config value: '%0', expected a double") + << DiffThresholdString; + DiffThreshold = 0.001; + } +} + +void UseStdNumbersCheck::registerMatchers(MatchFinder *const Finder) { + const auto Matches = MatchBuilder{DiffThreshold}; + std::vector> ConstantMatchers = { + Matches.matchLog2Euler(), Matches.matchLog10Euler(), + Matches.matchEulerTopLevel(), Matches.matchEgamma(), + Matches.matchInvSqrtPi(), Matches.matchInvPi(), + Matches.matchPiTopLevel(), Matches.matchLn2(), + Matches.machterLn10(), Matches.matchSqrt2(), + Matches.matchInvSqrt3(), Matches.matchSqrt3(), + Matches.matchPhi(), + }; + + Finder->addMatcher( + expr( + anyOfExhaustive(std::move(ConstantMatchers)), + unless(hasParent(explicitCastExpr(hasDestinationType(isFloating())))), + hasType(qualType(hasCanonicalTypeUnqualified( + anyOf(qualType(asString("float")).bind("float"), + qualType(asString("double")), + qualType(asString("long double")).bind("long double")))))), + this); +} + +void UseStdNumbersCheck::check(const MatchFinder::MatchResult &Result) { + /* + List of all math constants in the `` header + + e + + log2e + + log10e + + pi + + inv_pi + + inv_sqrtpi + + ln2 + + ln10 + + sqrt2 + + sqrt3 + + inv_sqrt3 + + egamma + + phi + */ + + // The ordering determines what constants are looked at first. + // E.g. look at 'inv_sqrt3' before 'sqrt3' to be able to replace the larger + // expression + constexpr auto Constants = std::array, 13>{ + std::pair{StringRef{"log2e"}, llvm::numbers::log2e}, + std::pair{StringRef{"log10e"}, llvm::numbers::log10e}, + std::pair{StringRef{"e"}, llvm::numbers::e}, + std::pair{StringRef{"egamma"}, llvm::numbers::egamma}, + std::pair{StringRef{"inv_sqrtpi"}, llvm::numbers::inv_sqrtpi}, + std::pair{StringRef{"inv_pi"}, llvm::numbers::inv_pi}, + std::pair{StringRef{"pi"}, llvm::numbers::pi}, + std::pair{StringRef{"ln2"}, llvm::numbers::ln2}, + std::pair{StringRef{"ln10"}, llvm::numbers::ln10}, + std::pair{StringRef{"sqrt2"}, llvm::numbers::sqrt2}, + std::pair{StringRef{"inv_sqrt3"}, llvm::numbers::inv_sqrt3}, + std::pair{StringRef{"sqrt3"}, llvm::numbers::sqrt3}, + std::pair{StringRef{"phi"}, llvm::numbers::phi}, + }; + + auto MatchedLiterals = + llvm::SmallVector>{}; + + const auto &SM = *Result.SourceManager; + const auto &LO = Result.Context->getLangOpts(); + + const auto IsFloat = Result.Nodes.getNodeAs("float") != nullptr; + const auto IsLongDouble = + Result.Nodes.getNodeAs("long double") != nullptr; + + for (const auto &[ConstantName, ConstantValue] : Constants) { + const auto *const Match = Result.Nodes.getNodeAs(ConstantName); + if (Match == nullptr) { + continue; + } + + const auto Range = Match->getSourceRange(); + + const auto IsMacro = Range.getBegin().isMacroID(); + + // We do not want to emit a diagnostic when we are matching a macro, but the + // match inside of the macro does not cover the whole macro. + if (IsMacro && !isRangeOfCompleteMacro(Range, SM, LO)) { + continue; + } + + if (const auto PatternBindString = (ConstantName + "_pattern").str(); + Result.Nodes.getNodeAs(PatternBindString) != nullptr) { + const auto Code = getCode(ConstantName, IsFloat, IsLongDouble); + diag(Range.getBegin(), "prefer '%0' to this %select{formula|macro}1") + << Code << IsMacro << FixItHint::CreateReplacement(Range, Code); + return; + } + + const auto LiteralBindString = (ConstantName + "_literal").str(); + if (const auto *const Literal = + Result.Nodes.getNodeAs(LiteralBindString)) { + MatchedLiterals.emplace_back( + ConstantName, + std::abs(Literal->getValueAsApproximateDouble() - ConstantValue), + Match); + } + } + + // We may have had no matches with literals, but a match with a pattern that + // was a part of a macro which was therefore skipped. + if (MatchedLiterals.empty()) { + return; + } + + llvm::sort(MatchedLiterals, [](const auto &LHS, const auto &RHS) { + return std::get<1>(LHS) < std::get<1>(RHS); + }); + + const auto &[Constant, Diff, Node] = MatchedLiterals.front(); + + const auto Range = Node->getSourceRange(); + const auto IsMacro = Range.getBegin().isMacroID(); + + // We do not want to emit a diagnostic when we are matching a macro, but the + // match inside of the macro does not cover the whole macro. + if (IsMacro && !isRangeOfCompleteMacro(Range, SM, LO)) { + return; + } + + const auto Code = getCode(Constant, IsFloat, IsLongDouble); + diag(Range.getBegin(), + "prefer '%0' to this %select{literal|macro}1, differs by '%2'") + << Code << IsMacro << llvm::formatv("{0:e2}", Diff).str() + << FixItHint::CreateReplacement(Range, Code) + << IncludeInserter.createIncludeInsertion( + Result.SourceManager->getFileID(Range.getBegin()), ""); +} + +void UseStdNumbersCheck::registerPPCallbacks( + const SourceManager &SM, Preprocessor *const PP, + Preprocessor *const ModuleExpanderPP) { + IncludeInserter.registerPreprocessor(PP); +} + +void UseStdNumbersCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { + Options.store(Opts, "IncludeStyle", IncludeInserter.getStyle()); + Options.store(Opts, "DiffThreshold", DiffThresholdString); +} +} // namespace clang::tidy::modernize diff --git a/clang-tools-extra/clang-tidy/modernize/UseStdNumbersCheck.h b/clang-tools-extra/clang-tidy/modernize/UseStdNumbersCheck.h new file mode 100644 index 0000000000000000000000000000000000000000..05fc5ada14b87a15d96e5d31a3d482364f147040 --- /dev/null +++ b/clang-tools-extra/clang-tidy/modernize/UseStdNumbersCheck.h @@ -0,0 +1,49 @@ +//===--- UseStdNumbersCheck.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_MODERNIZE_USESTDNUMBERSCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USESTDNUMBERSCHECK_H + +#include "../ClangTidyCheck.h" +#include "../utils/IncludeInserter.h" + +namespace clang::tidy::modernize { + +/// Finds constants and function calls to math functions that can be replaced +/// with c++20's mathematical constants from the ``numbers`` header and +/// offers fix-it hints. +/// Does not match the use of variables with that value, and instead, +/// offers a replacement at the definition of those variables. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/modernize/use-std-numbers.html +class UseStdNumbersCheck : public ClangTidyCheck { +public: + UseStdNumbersCheck(StringRef Name, ClangTidyContext *Context); + + bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { + return LangOpts.CPlusPlus20; + } + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP, + Preprocessor *ModuleExpanderPP) override; + void storeOptions(ClangTidyOptions::OptionMap &Opts) override; + std::optional getCheckTraversalKind() const override { + return TK_IgnoreUnlessSpelledInSource; + } + +private: + utils::IncludeInserter IncludeInserter; + StringRef DiffThresholdString; + double DiffThreshold; +}; + +} // namespace clang::tidy::modernize + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USESTDNUMBERSCHECK_H diff --git a/clang-tools-extra/clang-tidy/readability/ContainerContainsCheck.cpp b/clang-tools-extra/clang-tidy/readability/ContainerContainsCheck.cpp index 970ed8b83e0a76478bb6e7d498a9f1a0581c3a53..dbb50a060e596011bcfe6c9cdf4d8bc732be2fcb 100644 --- a/clang-tools-extra/clang-tidy/readability/ContainerContainsCheck.cpp +++ b/clang-tools-extra/clang-tidy/readability/ContainerContainsCheck.cpp @@ -131,8 +131,8 @@ void ContainerContainsCheck::check(const MatchFinder::MatchResult &Result) { Diag << FixItHint::CreateReplacement( CharSourceRange::getCharRange(ComparisonBegin, CallBegin), Negated ? "!" : ""); - Diag << FixItHint::CreateRemoval(CharSourceRange::getCharRange( - CallEnd.getLocWithOffset(1), ComparisonEnd.getLocWithOffset(1))); + Diag << FixItHint::CreateRemoval(CharSourceRange::getTokenRange( + CallEnd.getLocWithOffset(1), ComparisonEnd)); } } // namespace clang::tidy::readability diff --git a/clang-tools-extra/clangd/InlayHints.cpp b/clang-tools-extra/clangd/InlayHints.cpp index 3da047f9542138565fe204103967f9dd978a5e00..b540c273cbd59672b07c8eede970e2824f17ca53 100644 --- a/clang-tools-extra/clangd/InlayHints.cpp +++ b/clang-tools-extra/clangd/InlayHints.cpp @@ -589,6 +589,31 @@ public: return true; } + // Carefully recurse into PseudoObjectExprs, which typically incorporate + // a syntactic expression and several semantic expressions. + bool TraversePseudoObjectExpr(PseudoObjectExpr *E) { + Expr *SyntacticExpr = E->getSyntacticForm(); + if (isa(SyntacticExpr)) + // Since the counterpart semantics usually get the identical source + // locations as the syntactic one, visiting those would end up presenting + // confusing hints e.g., __builtin_dump_struct. + // Thus, only traverse the syntactic forms if this is written as a + // CallExpr. This leaves the door open in case the arguments in the + // syntactic form could possibly get parameter names. + return RecursiveASTVisitor::TraverseStmt(SyntacticExpr); + // We don't want the hints for some of the MS property extensions. + // e.g. + // struct S { + // __declspec(property(get=GetX, put=PutX)) int x[]; + // void PutX(int y); + // void Work(int y) { x = y; } // Bad: `x = y: y`. + // }; + if (isa(SyntacticExpr)) + return true; + // FIXME: Handle other forms of a pseudo object expression. + return RecursiveASTVisitor::TraversePseudoObjectExpr(E); + } + bool VisitCallExpr(CallExpr *E) { if (!Cfg.InlayHints.Parameters) return true; diff --git a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp index 20c1cdd985dbc0179644ec5a462e87835ab67002..6e91053632e00bfac1800f2771c2cbee097c53ae 100644 --- a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp +++ b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp @@ -1709,7 +1709,8 @@ TEST(DesignatorHints, NoCrash) { void test() { Foo f{A(), $b[[1]]}; } - )cpp", ExpectedHint{".b=", "b"}); + )cpp", + ExpectedHint{".b=", "b"}); } TEST(InlayHints, RestrictRange) { @@ -1724,6 +1725,38 @@ TEST(InlayHints, RestrictRange) { ElementsAre(labelIs(": int"), labelIs(": char"))); } +TEST(ParameterHints, PseudoObjectExpr) { + Annotations Code(R"cpp( + struct S { + __declspec(property(get=GetX, put=PutX)) int x[]; + int GetX(int y, int z) { return 42 + y; } + void PutX(int) { } + + // This is a PseudoObjectExpression whose syntactic form is a binary + // operator. + void Work(int y) { x = y; } // Not `x = y: y`. + }; + + int printf(const char *Format, ...); + + int main() { + S s; + __builtin_dump_struct(&s, printf); // Not `Format: __builtin_dump_struct()` + printf($Param[["Hello, %d"]], 42); // Normal calls are not affected. + // This builds a PseudoObjectExpr, but here it's useful for showing the + // arguments from the semantic form. + return s.x[ $one[[1]] ][ $two[[2]] ]; // `x[y: 1][z: 2]` + } + )cpp"); + auto TU = TestTU::withCode(Code.code()); + TU.ExtraArgs.push_back("-fms-extensions"); + auto AST = TU.build(); + EXPECT_THAT(inlayHints(AST, std::nullopt), + ElementsAre(HintMatcher(ExpectedHint{"Format: ", "Param"}, Code), + HintMatcher(ExpectedHint{"y: ", "one"}, Code), + HintMatcher(ExpectedHint{"z: ", "two"}, Code))); +} + TEST(ParameterHints, ArgPacksAndConstructors) { assertParameterHints( R"cpp( diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index 6d5f49dc06254512128ba9e0099d2b32e1806b8d..9262f9bbfe62a76fa118f9d2a43cee208a3e5329 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -186,6 +186,20 @@ New checks Replace ``enable_if`` with C++20 requires clauses. +- New :doc:`modernize-use-starts-ends-with + ` check. + + Checks whether a ``find`` or ``rfind`` result is compared with 0 and suggests + replacing with ``starts_with`` when the method exists in the class. Notably, + this will work with ``std::string`` and ``std::string_view``. + +- New :doc:`modernize-use-std-numbers + ` check. + + Finds constants and function calls to math functions that can be replaced + with C++20's mathematical constants from the ``numbers`` header and + offers fix-it hints. + - New :doc:`performance-enum-size ` check. @@ -399,6 +413,10 @@ Changes in existing checks ` diagnositics to highlight the const location +- Improved :doc:`readability-container-contains + ` to correctly handle + interger literals with suffixes in fix-its. + - Improved :doc:`readability-container-size-empty ` check to detect comparison between string and empty string literals and support diff --git a/clang-tools-extra/docs/clang-tidy/checks/abseil/string-find-startswith.rst b/clang-tools-extra/docs/clang-tidy/checks/abseil/string-find-startswith.rst index c82c38772a5c9a8ace23952586dcf686c75f046d..41a7ab500d7ce0410e9d17bdfcf814a4862bbd10 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/abseil/string-find-startswith.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/abseil/string-find-startswith.rst @@ -8,6 +8,10 @@ corresponding ``std::string_view`` methods) result is compared with 0, and suggests replacing with ``absl::StartsWith()``. This is both a readability and performance issue. +``starts_with`` was added as a built-in function on those types in C++20. If +available, prefer enabling :doc:`modernize-use-starts-ends-with +<../modernize/use-starts-ends-with>` instead of this check. + .. code-block:: c++ string s = "..."; diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst index 6f987ba1672e3f25c77f84275f9f4d44871ade97..e6c02fe48fbf86c465d05b0cf61b9c10743b2068 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/list.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -292,6 +292,8 @@ Clang-Tidy Checks :doc:`modernize-use-noexcept `, "Yes" :doc:`modernize-use-nullptr `, "Yes" :doc:`modernize-use-override `, "Yes" + :doc:`modernize-use-starts-ends-with `, "Yes" + :doc:`modernize-use-std-numbers `, "Yes" :doc:`modernize-use-std-print `, "Yes" :doc:`modernize-use-trailing-return-type `, "Yes" :doc:`modernize-use-transparent-functors `, "Yes" diff --git a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-starts-ends-with.rst b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-starts-ends-with.rst new file mode 100644 index 0000000000000000000000000000000000000000..7f8a262d2ab3aac9f4e13064062f584ea5d1460e --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-starts-ends-with.rst @@ -0,0 +1,22 @@ +.. title:: clang-tidy - modernize-use-starts-ends-with + +modernize-use-starts-ends-with +============================== + +Checks whether a ``find`` or ``rfind`` result is compared with 0 and suggests +replacing with ``starts_with`` when the method exists in the class. Notably, +this will work with ``std::string`` and ``std::string_view``. + +.. code-block:: c++ + + std::string s = "..."; + if (s.find("prefix") == 0) { /* do something */ } + if (s.rfind("prefix", 0) == 0) { /* do something */ } + +becomes + +.. code-block:: c++ + + std::string s = "..."; + if (s.starts_with("prefix")) { /* do something */ } + if (s.starts_with("prefix")) { /* do something */ } diff --git a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-std-numbers.rst b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-std-numbers.rst new file mode 100644 index 0000000000000000000000000000000000000000..207e9c00e74ba0f7771c0d7c639e71e56708be72 --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-std-numbers.rst @@ -0,0 +1,74 @@ +.. title:: clang-tidy - modernize-use-std-numbers + +modernize-use-std-numbers +========================= + +Finds constants and function calls to math functions that can be replaced +with C++20's mathematical constants from the ``numbers`` header and offers +fix-it hints. +Does not match the use of variables with that value, and instead, +offers a replacement for the definition of those variables. +Function calls that match the pattern of how the constant is calculated are +matched and replaced with the ``std::numbers`` constant. +The use of macros gets replaced with the corresponding ``std::numbers`` +constant, instead of changing the macro definition. + +The following list of constants from the ``numbers`` header are supported: + +* ``e`` +* ``log2e`` +* ``log10e`` +* ``pi`` +* ``inv_pi`` +* ``inv_sqrtpi`` +* ``ln2`` +* ``ln10`` +* ``sqrt2`` +* ``sqrt3`` +* ``inv_sqrt3`` +* ``egamma`` +* ``phi`` + +The list currently includes all constants as of C++20. + +The replacements use the type of the matched constant and can remove explicit +casts, i.e., switching between ``std::numbers::e``, +``std::numbers::e_v`` and ``std::numbers::e_v`` where +appropriate. + +.. code-block:: c++ + + double sqrt(double); + double log2(double); + void sink(auto&&) {} + void floatSink(float); + + #define MY_PI 3.1415926 + + void foo() { + const double Pi = 3.141592653589; // const double Pi = std::numbers::pi + const auto Use = Pi / 2; // no match for Pi + static constexpr double Euler = 2.7182818; // static constexpr double Euler = std::numbers::e; + + log2(exp(1)); // std::numbers::log2e; + log2(Euler); // std::numbers::log2e; + 1 / sqrt(MY_PI); // std::numbers::inv_sqrtpi; + sink(MY_PI); // sink(std::numbers::pi); + floatSink(MY_PI); // floatSink(std::numbers::pi); + floatSink(static_cast(MY_PI)); // floatSink(std::numbers::pi_v); + } + +Options +------- + +.. option:: DiffThreshold + + A floating point value that sets the detection threshold for when literals + match a constant. A literal matches a constant if + ``abs(literal - constant) < DiffThreshold`` evaluates to ``true``. Default + is `0.001`. + +.. option:: IncludeStyle + + A string specifying which include-style is used, `llvm` or `google`. Default + is `llvm`. diff --git a/clang-tools-extra/docs/clang-tidy/checks/readability/named-parameter.rst b/clang-tools-extra/docs/clang-tidy/checks/readability/named-parameter.rst index 8d28c0aa02169a7cdb98cec4a87c0a2c72756b11..73677a48605f4957ec60779f9c9dc5c64152ee8b 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/readability/named-parameter.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/readability/named-parameter.rst @@ -10,7 +10,16 @@ Guide: https://google.github.io/styleguide/cppguide.html#Function_Declarations_and_Definitions -All parameters should be named, with identical names in the declaration and -implementation. +All parameters should have the same name in both the function declaration and definition. +If a parameter is not utilized, its name can be commented out in a function definition. + +.. code-block:: c++ + + int doingSomething(int a, int b, int c); + + int doingSomething(int a, int b, int /*c*/) { + // Ok: the third param is not used + return a + b; + } Corresponding cpplint.py check name: `readability/function`. diff --git a/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/stddef.h b/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/stddef.h index 24b8ef012d4a00fc88e54cd909c94b6b930e06d9..11feddf0f0676a5389ac361df5192b686d5c5421 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/stddef.h +++ b/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/stddef.h @@ -12,4 +12,4 @@ typedef __PTRDIFF_TYPE__ ptrdiff_t; typedef __SIZE_TYPE__ size_t; -#endif _STDDEF_H_ +#endif // _STDDEF_H_ diff --git a/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/string b/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/string index d0aac7b78ec939b71e7196a4450d25c2879e800b..f2e4159a224513f073890f1c86c9964327336a14 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/string +++ b/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/string @@ -10,8 +10,13 @@ typedef unsigned __INT32_TYPE__ char32; namespace std { template class allocator {}; + template class char_traits {}; + +template > +struct basic_string_view; + template , typename A = allocator> struct basic_string { typedef size_t size_type; @@ -52,6 +57,10 @@ struct basic_string { _Type& insert(size_type pos, const C* s); _Type& insert(size_type pos, const C* s, size_type n); + constexpr bool starts_with(std::basic_string_view sv) const noexcept; + constexpr bool starts_with(C ch) const noexcept; + constexpr bool starts_with(const C* s) const; + _Type& operator[](size_type); const _Type& operator[](size_type) const; @@ -68,7 +77,7 @@ typedef basic_string wstring; typedef basic_string u16string; typedef basic_string u32string; -template > +template struct basic_string_view { typedef size_t size_type; typedef basic_string_view _Type; @@ -86,8 +95,13 @@ struct basic_string_view { size_type rfind(const C* s, size_type pos, size_type count) const; size_type rfind(const C* s, size_type pos = npos) const; + constexpr bool starts_with(basic_string_view sv) const noexcept; + constexpr bool starts_with(C ch) const noexcept; + constexpr bool starts_with(const C* s) const; + static constexpr size_t npos = -1; }; + typedef basic_string_view string_view; typedef basic_string_view wstring_view; typedef basic_string_view u16string_view; diff --git a/clang-tools-extra/test/clang-tidy/checkers/abseil/string-find-startswith.cpp b/clang-tools-extra/test/clang-tidy/checkers/abseil/string-find-startswith.cpp index 417598790bc007f8eeee6ae0bc8a8029f6203f2d..aabb30fe34f782cdcf0a8e609ca20b2a4fe72da2 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/abseil/string-find-startswith.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/abseil/string-find-startswith.cpp @@ -1,4 +1,4 @@ -// RUN: %check_clang_tidy %s abseil-string-find-startswith %t -- \ +// RUN: %check_clang_tidy -std=c++17 %s abseil-string-find-startswith %t -- \ // RUN: -config="{CheckOptions: \ // RUN: {abseil-string-find-startswith.StringLikeClasses: \ // RUN: '::std::basic_string;::std::basic_string_view;::basic_string'}}" \ diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-starts-ends-with.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-starts-ends-with.cpp new file mode 100644 index 0000000000000000000000000000000000000000..65ed9ed895bc4731e8c3fa8ab13ff0db11445ab8 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-starts-ends-with.cpp @@ -0,0 +1,167 @@ +// RUN: %check_clang_tidy -std=c++20 %s modernize-use-starts-ends-with %t -- \ +// RUN: -- -isystem %clang_tidy_headers + +#include + +std::string foo(std::string); +std::string bar(); + +class sub_string : public std::string {}; +class sub_sub_string : public sub_string {}; + +struct string_like { + bool starts_with(const char *s) const; + size_t find(const char *s, size_t pos = 0) const; +}; + +struct string_like_camel { + bool startsWith(const char *s) const; + size_t find(const char *s, size_t pos = 0) const; +}; + +struct prefer_underscore_version { + bool starts_with(const char *s) const; + bool startsWith(const char *s) const; + size_t find(const char *s, size_t pos = 0) const; +}; + +struct prefer_underscore_version_flip { + bool startsWith(const char *s) const; + bool starts_with(const char *s) const; + size_t find(const char *s, size_t pos = 0) const; +}; + +struct prefer_underscore_version_inherit : public string_like { + bool startsWith(const char *s) const; +}; + +void test(std::string s, std::string_view sv, sub_string ss, sub_sub_string sss, + string_like sl, string_like_camel slc, prefer_underscore_version puv, + prefer_underscore_version_flip puvf, + prefer_underscore_version_inherit puvi) { + s.find("a") == 0; + // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with instead of find() == 0 + // CHECK-FIXES: s.starts_with("a"); + + (((((s)).find("a")))) == ((0)); + // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with + // CHECK-FIXES: ((s)).starts_with("a"); + + (s + "a").find("a") == ((0)); + // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with + // CHECK-FIXES: (s + "a").starts_with("a"); + + s.find(s) == 0; + // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with + // CHECK-FIXES: s.starts_with(s); + + s.find("aaa") != 0; + // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with + // CHECK-FIXES: !s.starts_with("aaa"); + + s.find(foo(foo(bar()))) != 0; + // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with + // CHECK-FIXES: !s.starts_with(foo(foo(bar()))); + + if (s.find("....") == 0) { /* do something */ } + // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with + // CHECK-FIXES: if (s.starts_with("....")) + + 0 != s.find("a"); + // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with + // CHECK-FIXES: !s.starts_with("a"); + + s.rfind("a", 0) == 0; + // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with instead of rfind() == 0 + // CHECK-FIXES: s.starts_with("a"); + + s.rfind(s, 0) == 0; + // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with + // CHECK-FIXES: s.starts_with(s); + + s.rfind("aaa", 0) != 0; + // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with + // CHECK-FIXES: !s.starts_with("aaa"); + + s.rfind(foo(foo(bar())), 0) != 0; + // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with + // CHECK-FIXES: !s.starts_with(foo(foo(bar()))); + + if (s.rfind("....", 0) == 0) { /* do something */ } + // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with + // CHECK-FIXES: if (s.starts_with("....")) + + 0 != s.rfind("a", 0); + // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with + // CHECK-FIXES: !s.starts_with("a"); + + #define STR(x) std::string(x) + 0 == STR(s).find("a"); + // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with + // CHECK-FIXES: STR(s).starts_with("a"); + + #define STRING s + if (0 == STRING.find("ala")) { /* do something */} + // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with + // CHECK-FIXES: if (STRING.starts_with("ala")) + + #define FIND find + s.FIND("a") == 0; + // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with + // CHECK-FIXES: s.starts_with("a") + + #define PREFIX "a" + s.find(PREFIX) == 0; + // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with + // CHECK-FIXES: s.starts_with(PREFIX) + + #define ZERO 0 + s.find("a") == ZERO; + // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with + // CHECK-FIXES: s.starts_with("a") + + sv.find("a") == 0; + // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with + // CHECK-FIXES: sv.starts_with("a"); + + sv.rfind("a", 0) != 0; + // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with + // CHECK-FIXES: !sv.starts_with("a"); + + ss.find("a") == 0; + // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with + // CHECK-FIXES: ss.starts_with("a"); + + sss.find("a") == 0; + // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with + // CHECK-FIXES: ss.starts_with("a"); + + sl.find("a") == 0; + // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with + // CHECK-FIXES: sl.starts_with("a"); + + slc.find("a") == 0; + // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use startsWith + // CHECK-FIXES: slc.startsWith("a"); + + puv.find("a") == 0; + // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with + // CHECK-FIXES: puv.starts_with("a"); + + puvf.find("a") == 0; + // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with + // CHECK-FIXES: puvf.starts_with("a"); + + // Here, the subclass has startsWith, the superclass has starts_with. + // We prefer the version from the subclass. + puvi.find("a") == 0; + // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use startsWith + // CHECK-FIXES: puvi.startsWith("a"); + + // Expressions that don't trigger the check are here. + #define EQ(x, y) ((x) == (y)) + EQ(s.find("a"), 0); + + #define DOTFIND(x, y) (x).find(y) + DOTFIND(s, "a") == 0; +} diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-numbers.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-numbers.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6c5762da5e2e841f81a95f71b62bed98ed25a9cd --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-numbers.cpp @@ -0,0 +1,493 @@ +// RUN: %check_clang_tidy -check-suffix=ALL -std=c++20 %s modernize-use-std-numbers %t +// RUN: %check_clang_tidy -check-suffix=ALL,IMPRECISE -std=c++20 %s modernize-use-std-numbers %t -- -config="{CheckOptions: { modernize-use-std-numbers.DiffThreshold: 0.01 }}" + +// CHECK-FIXES-ALL: #include + +namespace bar { + double sqrt(double Arg); + float sqrt(float Arg); + template + auto sqrt(T val) { return sqrt(static_cast(val)); } + + static constexpr double e = 2.718281828459045235360287471352662497757247093; + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:33: warning: prefer 'std::numbers::e' to this literal, differs by '0.00e+00' [modernize-use-std-numbers] + // CHECK-FIXES-ALL: static constexpr double e = std::numbers::e; +} + +double exp(double Arg); +double log(double Arg); + +double log2(double Arg); +float log2(float Arg); +template +auto log2(T val) { return log2(static_cast(val)); } + +double log10(double Arg); + +template +void sink(T&&) { } + +void floatSink(float) {} + +#define MY_PI 3.1415926 + +#define INV_SQRT3 1 / bar::sqrt(3) +#define NOT_INV_SQRT3 1 / bar::sqrt(3) + 1 + +using my_double = double; +using my_float = float; + +void foo(){ + static constexpr double Pi = 3.1415926; + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:34: warning: prefer 'std::numbers::pi' to this literal, differs by '5.36e-08' [modernize-use-std-numbers] + // CHECK-FIXES-ALL: static constexpr double Pi = std::numbers::pi; + + static constexpr double Euler = 2.7182818; + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:37: warning: prefer 'std::numbers::e' to this literal, differs by '2.85e-08' [modernize-use-std-numbers] + // CHECK-FIXES-ALL: static constexpr double Euler = std::numbers::e; + + static constexpr double Phi = 1.6180339; + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:35: warning: prefer 'std::numbers::phi' to this literal, differs by '8.87e-08' [modernize-use-std-numbers] + // CHECK-FIXES-ALL: static constexpr double Phi = std::numbers::phi; + + static constexpr double PiCopy = Pi; + static constexpr double PiDefineFromMacro = MY_PI; + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:49: warning: prefer 'std::numbers::pi' to this macro, differs by '5.36e-08' [modernize-use-std-numbers] + // CHECK-FIXES-ALL: static constexpr double PiDefineFromMacro = std::numbers::pi; + + static constexpr double Pi2 = 3.14; + // CHECK-MESSAGES-IMPRECISE: :[[@LINE-1]]:35: warning: prefer 'std::numbers::pi' to this literal, differs by '1.59e-03' [modernize-use-std-numbers] + // CHECK-FIXES-IMPRECISE: static constexpr double Pi2 = std::numbers::pi; + static constexpr double Euler2 = 2.71; + // CHECK-MESSAGES-IMPRECISE: :[[@LINE-1]]:38: warning: prefer 'std::numbers::e' to this literal, differs by '8.28e-03' [modernize-use-std-numbers] + // CHECK-FIXES-IMPRECISE: static constexpr double Euler2 = std::numbers::e; + static constexpr double Phi2 = 1.61; + // CHECK-MESSAGES-IMPRECISE: :[[@LINE-1]]:36: warning: prefer 'std::numbers::phi' to this literal, differs by '8.03e-03' [modernize-use-std-numbers] + // CHECK-FIXES-IMPRECISE: static constexpr double Phi2 = std::numbers::phi; + + static constexpr double Pi3 = 3.1415926L; + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:35: warning: prefer 'std::numbers::pi_v' to this literal, differs by '5.36e-08' [modernize-use-std-numbers] + // CHECK-FIXES-ALL: static constexpr double Pi3 = std::numbers::pi_v; + + static constexpr double Euler3 = 2.7182818L; + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:38: warning: prefer 'std::numbers::e_v' to this literal, differs by '2.85e-08' [modernize-use-std-numbers] + // CHECK-FIXES-ALL: static constexpr double Euler3 = std::numbers::e_v; + + static constexpr double Phi3 = 1.6180339L; + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:36: warning: prefer 'std::numbers::phi_v' to this literal, differs by '8.87e-08' [modernize-use-std-numbers] + // CHECK-FIXES-ALL: static constexpr double Phi3 = std::numbers::phi_v; + + static constexpr long double Pi4 = 3.1415926L; + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:40: warning: prefer 'std::numbers::pi_v' to this literal, differs by '5.36e-08' [modernize-use-std-numbers] + // CHECK-FIXES-ALL: static constexpr long double Pi4 = std::numbers::pi_v; + + static constexpr long double Euler4 = 2.7182818L; + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:43: warning: prefer 'std::numbers::e_v' to this literal, differs by '2.85e-08' [modernize-use-std-numbers] + // CHECK-FIXES-ALL: static constexpr long double Euler4 = std::numbers::e_v; + + static constexpr long double Phi4 = 1.6180339L; + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:41: warning: prefer 'std::numbers::phi_v' to this literal, differs by '8.87e-08' [modernize-use-std-numbers] + // CHECK-FIXES-ALL: static constexpr long double Phi4 = std::numbers::phi_v; + + static constexpr my_double Euler5 = 2.7182818; + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:41: warning: prefer 'std::numbers::e' to this literal, differs by '2.85e-08' [modernize-use-std-numbers] + // CHECK-FIXES-ALL: static constexpr my_double Euler5 = std::numbers::e; + + static constexpr my_float Euler6 = 2.7182818; + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:40: warning: prefer 'std::numbers::e' to this literal, differs by '2.85e-08' [modernize-use-std-numbers] + // CHECK-FIXES-ALL: static constexpr my_float Euler6 = std::numbers::e; + + static constexpr int NotEuler7 = 2.7182818; + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:38: warning: prefer 'std::numbers::e' to this literal, differs by '2.85e-08' [modernize-use-std-numbers] + // CHECK-FIXES-ALL: static constexpr int NotEuler7 = std::numbers::e; + + static constexpr double InvPi = 1.0 / Pi; + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:37: warning: prefer 'std::numbers::inv_pi' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: static constexpr double InvPi = std::numbers::inv_pi; + + static constexpr my_float Actually2MyFloat = 2; + bar::sqrt(Actually2MyFloat); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: prefer 'std::numbers::sqrt2_v' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: std::numbers::sqrt2_v; + + sink(MY_PI); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:10: warning: prefer 'std::numbers::pi' to this macro, differs by '5.36e-08' [modernize-use-std-numbers] + // CHECK-FIXES-ALL: sink(std::numbers::pi); + + auto X = 42.0; + auto Y = X * 3.14; + // CHECK-MESSAGES-IMPRECISE: :[[@LINE-1]]:18: warning: prefer 'std::numbers::pi' to this literal, differs by '1.59e-03' [modernize-use-std-numbers] + // CHECK-FIXES-IMPRECISE: auto Y = X * std::numbers::pi; + + constexpr static auto One = 1; + constexpr static auto Two = 2; + + bar::sqrt(2); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: prefer 'std::numbers::sqrt2' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: std::numbers::sqrt2; + + bar::sqrt(Two); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: prefer 'std::numbers::sqrt2' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: std::numbers::sqrt2; + + bar::sqrt(2.0); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: prefer 'std::numbers::sqrt2' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: std::numbers::sqrt2; + + auto Not2 = 2; + Not2 = 42; + bar::sqrt(Not2); + + const auto Actually2 = 2; + bar::sqrt(Actually2); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: prefer 'std::numbers::sqrt2' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: std::numbers::sqrt2; + + exp(1); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: prefer 'std::numbers::e' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: std::numbers::e; + + exp(One); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: prefer 'std::numbers::e' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: std::numbers::e; + + exp(1.00000000000001); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: prefer 'std::numbers::e' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: std::numbers::e; + + log2(exp(1)); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: prefer 'std::numbers::log2e' to this formula [modernize-use-std-numbers] + // CHECK-MESSAGES-ALL: :[[@LINE-2]]:10: warning: prefer 'std::numbers::e' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: std::numbers::log2e; + + log2(Euler); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: prefer 'std::numbers::log2e' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: std::numbers::log2e; + + log2(bar::e); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: prefer 'std::numbers::log2e' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: std::numbers::log2e; + + log2(Euler5); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: prefer 'std::numbers::log2e' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: std::numbers::log2e; + + log2(Euler6); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: prefer 'std::numbers::log2e_v' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: std::numbers::log2e_v; + + log2(NotEuler7); + + auto log2e = 1.4426950; + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:18: warning: prefer 'std::numbers::log2e' to this literal, differs by '4.09e-08' [modernize-use-std-numbers] + // CHECK-FIXES-ALL: auto log2e = std::numbers::log2e; + + floatSink(log2(Euler)); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:15: warning: prefer 'std::numbers::log2e' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: floatSink(std::numbers::log2e); + + floatSink(static_cast(log2(Euler))); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:15: warning: prefer 'std::numbers::log2e_v' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: floatSink(std::numbers::log2e_v); + + floatSink(1.4426950); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:15: warning: prefer 'std::numbers::log2e' to this literal, differs by '4.09e-08' [modernize-use-std-numbers] + // CHECK-FIXES-ALL: floatSink(std::numbers::log2e); + + floatSink(static_cast(1.4426950)); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:15: warning: prefer 'std::numbers::log2e_v' to this literal, differs by '4.09e-08' [modernize-use-std-numbers] + // CHECK-FIXES-ALL: floatSink(std::numbers::log2e_v); + + floatSink(log2(static_cast(Euler))); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:15: warning: prefer 'std::numbers::log2e_v' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: floatSink(std::numbers::log2e_v); + + floatSink(static_cast(log2(static_cast(Euler)))); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:15: warning: prefer 'std::numbers::log2e_v' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: floatSink(std::numbers::log2e_v); + + floatSink(static_cast(log2(static_cast(Euler)))); + + floatSink(static_cast(log2(static_cast(Euler)))); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:32: warning: prefer 'std::numbers::log2e_v' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: floatSink(static_cast(std::numbers::log2e_v)); + + floatSink(1.4426950F); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:15: warning: prefer 'std::numbers::log2e_v' to this literal, differs by '1.93e-08' [modernize-use-std-numbers] + // CHECK-FIXES-ALL: floatSink(std::numbers::log2e_v); + + floatSink(static_cast(1.4426950F)); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:15: warning: prefer 'std::numbers::log2e' to this literal, differs by '1.93e-08' [modernize-use-std-numbers] + // CHECK-FIXES-ALL: floatSink(std::numbers::log2e); + + floatSink(static_cast(1.4426950F)); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:32: warning: prefer 'std::numbers::log2e_v' to this literal, differs by '1.93e-08' [modernize-use-std-numbers] + // CHECK-FIXES-ALL: floatSink(static_cast(std::numbers::log2e_v)); + + log10(exp(1)); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: prefer 'std::numbers::log10e' to this formula [modernize-use-std-numbers] + // CHECK-MESSAGES-ALL: :[[@LINE-2]]:11: warning: prefer 'std::numbers::e' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: std::numbers::log10e; + + log10(Euler); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: prefer 'std::numbers::log10e' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: std::numbers::log10e; + + log10(bar::e); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: prefer 'std::numbers::log10e' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: std::numbers::log10e; + + auto log10e = .434294; + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:19: warning: prefer 'std::numbers::log10e' to this literal, differs by '4.82e-07' [modernize-use-std-numbers] + // CHECK-FIXES-ALL: auto log10e = std::numbers::log10e; + + auto egamma = 0.5772156 * 42; + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:19: warning: prefer 'std::numbers::egamma' to this literal, differs by '6.49e-08' [modernize-use-std-numbers] + // CHECK-FIXES-ALL: auto egamma = std::numbers::egamma * 42; + + sink(InvPi); + + sink(1 / Pi); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:10: warning: prefer 'std::numbers::inv_pi' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: sink(std::numbers::inv_pi); + + sink(1 / bar::sqrt(Pi)); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:10: warning: prefer 'std::numbers::inv_sqrtpi' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: sink(std::numbers::inv_sqrtpi); + + sink(1 / bar::sqrt(MY_PI)); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:10: warning: prefer 'std::numbers::inv_sqrtpi' to this formula [modernize-use-std-numbers] + // CHECK-MESSAGES-ALL: :[[@LINE-2]]:24: warning: prefer 'std::numbers::pi' to this macro, differs by '5.36e-08' [modernize-use-std-numbers] + // CHECK-FIXES-ALL: sink(std::numbers::inv_sqrtpi); + + log(2); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: prefer 'std::numbers::ln2' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: std::numbers::ln2; + + log(10); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: prefer 'std::numbers::ln10' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: std::numbers::ln10; + + bar::sqrt(2); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: prefer 'std::numbers::sqrt2' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: std::numbers::sqrt2; + + sink(1 / bar::sqrt(3)); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:10: warning: prefer 'std::numbers::inv_sqrt3' to this formula [modernize-use-std-numbers] + // CHECK-MESSAGES-ALL: :[[@LINE-2]]:14: warning: prefer 'std::numbers::sqrt3' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: sink(std::numbers::inv_sqrt3); + + sink(INV_SQRT3); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:10: warning: prefer 'std::numbers::inv_sqrt3' to this macro [modernize-use-std-numbers] + // CHECK-FIXES-ALL: sink(std::numbers::inv_sqrt3); + + sink(NOT_INV_SQRT3); + + const auto inv_sqrt3f = .577350269F; + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:29: warning: prefer 'std::numbers::inv_sqrt3_v' to this literal, differs by '1.04e-08' [modernize-use-std-numbers] + // CHECK-FIXES-ALL: const auto inv_sqrt3f = std::numbers::inv_sqrt3_v; + + bar::sqrt(3); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: prefer 'std::numbers::sqrt3' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: std::numbers::sqrt3; + + auto somePhi = 1.6180339; + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:20: warning: prefer 'std::numbers::phi' to this literal, differs by '8.87e-08' [modernize-use-std-numbers] + // CHECK-FIXES-ALL: auto somePhi = std::numbers::phi; + + sink(Phi); + + sink((42 + bar::sqrt(5)) / 2); + + sink((1 + bar::sqrt(5)) / 2); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:10: warning: prefer 'std::numbers::phi' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: sink(std::numbers::phi); + + sink((bar::sqrt(5.0F) + 1) / 2); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:10: warning: prefer 'std::numbers::phi_v' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: sink(std::numbers::phi_v); +} + + + +template +void baz(){ + static constexpr T Pi = 3.1415926; + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:29: warning: prefer 'std::numbers::pi' to this literal, differs by '5.36e-08' [modernize-use-std-numbers] + // CHECK-FIXES-ALL: static constexpr T Pi = std::numbers::pi; + + static constexpr T Euler = 2.7182818; + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:32: warning: prefer 'std::numbers::e' to this literal, differs by '2.85e-08' [modernize-use-std-numbers] + // CHECK-FIXES-ALL: static constexpr T Euler = std::numbers::e; + + static constexpr T Phi = 1.6180339; + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:30: warning: prefer 'std::numbers::phi' to this literal, differs by '8.87e-08' [modernize-use-std-numbers] + // CHECK-FIXES-ALL: static constexpr T Phi = std::numbers::phi; + + static constexpr T PiCopy = Pi; + static constexpr T PiDefineFromMacro = MY_PI; + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:44: warning: prefer 'std::numbers::pi' to this macro, differs by '5.36e-08' [modernize-use-std-numbers] + // CHECK-FIXES-ALL: static constexpr T PiDefineFromMacro = std::numbers::pi; + + static constexpr T Pi2 = 3.14; + // CHECK-MESSAGES-IMPRECISE: :[[@LINE-1]]:30: warning: prefer 'std::numbers::pi' to this literal, differs by '1.59e-03' [modernize-use-std-numbers] + // CHECK-FIXES-IMPRECISE: static constexpr T Pi2 = std::numbers::pi; + static constexpr T Euler2 = 2.71; + // CHECK-MESSAGES-IMPRECISE: :[[@LINE-1]]:33: warning: prefer 'std::numbers::e' to this literal, differs by '8.28e-03' [modernize-use-std-numbers] + // CHECK-FIXES-IMPRECISE: static constexpr T Euler2 = std::numbers::e; + static constexpr T Phi2 = 1.61; + // CHECK-MESSAGES-IMPRECISE: :[[@LINE-1]]:31: warning: prefer 'std::numbers::phi' to this literal, differs by '8.03e-03' [modernize-use-std-numbers] + // CHECK-FIXES-IMPRECISE: static constexpr T Phi2 = std::numbers::phi; + + static constexpr T Pi3 = 3.1415926L; + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:30: warning: prefer 'std::numbers::pi_v' to this literal, differs by '5.36e-08' [modernize-use-std-numbers] + // CHECK-FIXES-ALL: static constexpr T Pi3 = std::numbers::pi_v; + + static constexpr T Euler3 = 2.7182818L; + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:33: warning: prefer 'std::numbers::e_v' to this literal, differs by '2.85e-08' [modernize-use-std-numbers] + // CHECK-FIXES-ALL: static constexpr T Euler3 = std::numbers::e_v; + + static constexpr T Phi3 = 1.6180339L; + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:31: warning: prefer 'std::numbers::phi_v' to this literal, differs by '8.87e-08' [modernize-use-std-numbers] + // CHECK-FIXES-ALL: static constexpr T Phi3 = std::numbers::phi_v; + + static constexpr my_float Actually2MyFloat = 2; + bar::sqrt(Actually2MyFloat); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: prefer 'std::numbers::sqrt2_v' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: std::numbers::sqrt2_v; + + constexpr static T One = 1; + constexpr static T Two = 2; + + bar::sqrt(2); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: prefer 'std::numbers::sqrt2' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: std::numbers::sqrt2; + + bar::sqrt(Two); + + bar::sqrt(2.0); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: prefer 'std::numbers::sqrt2' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: std::numbers::sqrt2; + + T Not2 = 2; + Not2 = 42; + bar::sqrt(Not2); + + const T Actually2 = 2; + bar::sqrt(Actually2); + + exp(1); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: prefer 'std::numbers::e' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: std::numbers::e; + + exp(One); + + exp(1.00000000000001); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: prefer 'std::numbers::e' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: std::numbers::e; + + log2(exp(1)); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: prefer 'std::numbers::log2e' to this formula [modernize-use-std-numbers] + // CHECK-MESSAGES-ALL: :[[@LINE-2]]:10: warning: prefer 'std::numbers::e' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: std::numbers::log2e; + + log2(Euler); + + log2(bar::e); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: prefer 'std::numbers::log2e' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: std::numbers::log2e; + + T log2e = 1.4426950; + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:15: warning: prefer 'std::numbers::log2e' to this literal, differs by '4.09e-08' [modernize-use-std-numbers] + // CHECK-FIXES-ALL: T log2e = std::numbers::log2e; + + log10(exp(1)); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: prefer 'std::numbers::log10e' to this formula [modernize-use-std-numbers] + // CHECK-MESSAGES-ALL: :[[@LINE-2]]:11: warning: prefer 'std::numbers::e' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: std::numbers::log10e; + + log10(Euler); + + log10(bar::e); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: prefer 'std::numbers::log10e' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: std::numbers::log10e; + + T log10e = .434294; + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:16: warning: prefer 'std::numbers::log10e' to this literal, differs by '4.82e-07' [modernize-use-std-numbers] + // CHECK-FIXES-ALL: T log10e = std::numbers::log10e; + + T egamma = 0.5772156 * 42; + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:16: warning: prefer 'std::numbers::egamma' to this literal, differs by '6.49e-08' [modernize-use-std-numbers] + // CHECK-FIXES-ALL: T egamma = std::numbers::egamma * 42; + + sink(1 / Pi); + + sink(1 / bar::sqrt(Pi)); + + sink(1 / bar::sqrt(MY_PI)); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:10: warning: prefer 'std::numbers::inv_sqrtpi' to this formula [modernize-use-std-numbers] + // CHECK-MESSAGES-ALL: :[[@LINE-2]]:24: warning: prefer 'std::numbers::pi' to this macro, differs by '5.36e-08' [modernize-use-std-numbers] + // CHECK-FIXES-ALL: sink(std::numbers::inv_sqrtpi); + + + log(2); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: prefer 'std::numbers::ln2' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: std::numbers::ln2; + + log(10); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: prefer 'std::numbers::ln10' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: std::numbers::ln10; + + bar::sqrt(2); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: prefer 'std::numbers::sqrt2' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: std::numbers::sqrt2; + + sink(1 / bar::sqrt(3)); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:10: warning: prefer 'std::numbers::inv_sqrt3' to this formula [modernize-use-std-numbers] + // CHECK-MESSAGES-ALL: :[[@LINE-2]]:14: warning: prefer 'std::numbers::sqrt3' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: sink(std::numbers::inv_sqrt3); + + bar::sqrt(3); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: prefer 'std::numbers::sqrt3' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: std::numbers::sqrt3; + + T phi = 1.6180339; + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:13: warning: prefer 'std::numbers::phi' to this literal, differs by '8.87e-08' [modernize-use-std-numbers] + // CHECK-FIXES-ALL: T phi = std::numbers::phi; + + sink((42 + bar::sqrt(5)) / 2); + + sink((1 + bar::sqrt(5)) / 2); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:10: warning: prefer 'std::numbers::phi' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: sink(std::numbers::phi); + + sink((bar::sqrt(5.0F) + 1) / 2); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:10: warning: prefer 'std::numbers::phi_v' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: sink(std::numbers::phi_v); +} + +template +void foobar(){ + const T Two = 2; + bar::sqrt(Two); +} +void use_templates() { + foobar(); + foobar(); + + baz(); + baz(); +} + +#define BIG_MARCO \ + struct InvSqrt3 { \ + template static T get() { return 1 / bar::sqrt(3); } \ + } + +BIG_MARCO; + +void use_BIG_MACRO() { +InvSqrt3 f{}; +f.get(); +f.get(); +} diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/container-contains.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/container-contains.cpp index 05a4ebc9c8a17777805cd427b671547e94c7050e..0ecb61b2e7df06d710ca54580238fc7e05684ce8 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/readability/container-contains.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/readability/container-contains.cpp @@ -175,6 +175,37 @@ int nonRewrittenCount(std::multimap &MyMap) { return C1 + C2 + C3 + C4; } +// Check different integer literal suffixes +int testDifferentIntegerLiteralSuffixes(std::map &MyMap) { + + auto C1 = MyMap.count(2) != 0U; + // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: use 'contains' to check for membership [readability-container-contains] + // CHECK-FIXES: auto C1 = MyMap.contains(2); + auto C2 = MyMap.count(2) != 0UL; + // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: use 'contains' to check for membership [readability-container-contains] + // CHECK-FIXES: auto C2 = MyMap.contains(2); + auto C3 = 0U != MyMap.count(2); + // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: use 'contains' to check for membership [readability-container-contains] + // CHECK-FIXES: auto C3 = MyMap.contains(2); + auto C4 = 0UL != MyMap.count(2); + // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: use 'contains' to check for membership [readability-container-contains] + // CHECK-FIXES: auto C4 = MyMap.contains(2); + auto C5 = MyMap.count(2) < 1U; + // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: use 'contains' to check for membership [readability-container-contains] + // CHECK-FIXES: auto C5 = !MyMap.contains(2); + auto C6 = MyMap.count(2) < 1UL; + // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: use 'contains' to check for membership [readability-container-contains] + // CHECK-FIXES: auto C6 = !MyMap.contains(2); + auto C7 = 1U > MyMap.count(2); + // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: use 'contains' to check for membership [readability-container-contains] + // CHECK-FIXES: auto C7 = !MyMap.contains(2); + auto C8 = 1UL > MyMap.count(2); + // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: use 'contains' to check for membership [readability-container-contains] + // CHECK-FIXES: auto C8 = !MyMap.contains(2); + + return C1 + C2 + C3 + C4 + C5 + C6 + C7 + C8; +} + // We don't want to rewrite if the `contains` call is from a macro expansion int testMacroExpansion(std::unordered_set &MySet) { #define COUNT_ONES(SET) SET.count(1) diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/container-size-empty.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/container-size-empty.cpp index 29ac86cf1b369c2228ed888b268f70d291bf6fe1..3b9e06084183054138790da63a843fb6bcb38da5 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/readability/container-size-empty.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/readability/container-size-empty.cpp @@ -528,12 +528,12 @@ template void f() { if (s.size()) ; // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used to check for emptiness instead of 'size' [readability-container-size-empty] - // CHECK-MESSAGES: string:28:8: note: method 'basic_string'::empty() defined here + // CHECK-MESSAGES: string:{{[0-9]+}}:8: note: method 'basic_string'::empty() defined here // CHECK-FIXES: {{^ }}if (!s.empty()){{$}} if (s.length()) ; // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used to check for emptiness instead of 'length' [readability-container-size-empty] - // CHECK-MESSAGES: string:28:8: note: method 'basic_string'::empty() defined here + // CHECK-MESSAGES: string:{{[0-9]+}}:8: note: method 'basic_string'::empty() defined here // CHECK-FIXES: {{^ }}if (!s.empty()){{$}} } diff --git a/clang-tools-extra/test/clang-tidy/infrastructure/Inputs/compilation-database/template.json b/clang-tools-extra/test/clang-tidy/infrastructure/Inputs/compilation-database/template.json index 74275d953727cc53de44e03a216805c408209336..53ee3fa7a98da34cf8d77a358b8c0c8db56216ff 100644 --- a/clang-tools-extra/test/clang-tidy/infrastructure/Inputs/compilation-database/template.json +++ b/clang-tools-extra/test/clang-tidy/infrastructure/Inputs/compilation-database/template.json @@ -20,10 +20,15 @@ "file": "test_dir/b/c.cpp" }, { - "directory": "test_dir/b", - "command": "clang++ -I../include -o test.o ../b/d.cpp", + "directory": "test_dir/", + "command": "clang++ -o test.o ./b/d.cpp", "file": "test_dir/b/d.cpp" }, +{ + "directory": "test_dir/b", + "command": "clang++ -I../include -o test.o ../include.cpp", + "file": "test_dir/include.cpp" +}, { "directory": "test_dir/", "command": "clang++ -o test.o test_dir/b/not-exist.cpp", diff --git a/clang-tools-extra/test/clang-tidy/infrastructure/clang-tidy-run-with-database.cpp b/clang-tools-extra/test/clang-tidy/infrastructure/clang-tidy-run-with-database.cpp index f8c05a3f69e956ec5d71bac89b0f8d0222b68926..3c4e8494a4ae3b8e6ffa11297b37c7a553d12cb6 100644 --- a/clang-tools-extra/test/clang-tidy/infrastructure/clang-tidy-run-with-database.cpp +++ b/clang-tools-extra/test/clang-tidy/infrastructure/clang-tidy-run-with-database.cpp @@ -5,8 +5,9 @@ // RUN: echo 'int *AB = 0;' > %T/compilation-database-test/a/b.cpp // RUN: echo 'int *BB = 0;' > %T/compilation-database-test/b/b.cpp // RUN: echo 'int *BC = 0;' > %T/compilation-database-test/b/c.cpp +// RUN: echo 'int *BD = 0;' > %T/compilation-database-test/b/d.cpp // RUN: echo 'int *HP = 0;' > %T/compilation-database-test/include/header.h -// RUN: echo '#include "header.h"' > %T/compilation-database-test/b/d.cpp +// RUN: echo '#include "header.h"' > %T/compilation-database-test/include.cpp // RUN: sed 's|test_dir|%/T/compilation-database-test|g' %S/Inputs/compilation-database/template.json > %T/compile_commands.json // Regression test: shouldn't crash. @@ -15,15 +16,17 @@ // CHECK-NOT-EXIST: unable to handle compilation // CHECK-NOT-EXIST: Found compiler error -// RUN: clang-tidy --checks=-*,modernize-use-nullptr -p %T %T/compilation-database-test/a/a.cpp %T/compilation-database-test/a/b.cpp %T/compilation-database-test/b/b.cpp %T/compilation-database-test/b/c.cpp %T/compilation-database-test/b/d.cpp -header-filter=.* -fix +// RUN: clang-tidy --checks=-*,modernize-use-nullptr -p %T %T/compilation-database-test/a/a.cpp %T/compilation-database-test/a/b.cpp %T/compilation-database-test/b/b.cpp %T/compilation-database-test/b/c.cpp %T/compilation-database-test/b/d.cpp %T/compilation-database-test/include.cpp -header-filter=.* -fix // RUN: FileCheck -input-file=%T/compilation-database-test/a/a.cpp %s -check-prefix=CHECK-FIX1 // RUN: FileCheck -input-file=%T/compilation-database-test/a/b.cpp %s -check-prefix=CHECK-FIX2 // RUN: FileCheck -input-file=%T/compilation-database-test/b/b.cpp %s -check-prefix=CHECK-FIX3 // RUN: FileCheck -input-file=%T/compilation-database-test/b/c.cpp %s -check-prefix=CHECK-FIX4 -// RUN: FileCheck -input-file=%T/compilation-database-test/include/header.h %s -check-prefix=CHECK-FIX5 +// RUN: FileCheck -input-file=%T/compilation-database-test/b/d.cpp %s -check-prefix=CHECK-FIX5 +// RUN: FileCheck -input-file=%T/compilation-database-test/include/header.h %s -check-prefix=CHECK-FIX6 // CHECK-FIX1: int *AA = nullptr; // CHECK-FIX2: int *AB = nullptr; // CHECK-FIX3: int *BB = nullptr; // CHECK-FIX4: int *BC = nullptr; -// CHECK-FIX5: int *HP = nullptr; +// CHECK-FIX5: int *BD = nullptr; +// CHECK-FIX6: int *HP = nullptr; diff --git a/clang/CMakeLists.txt b/clang/CMakeLists.txt index 9b52c58be41e7f7489b66754a18a2e26e8316967..2ca6db02e58791d5f4ea9f6b8e57dccbb425da89 100644 --- a/clang/CMakeLists.txt +++ b/clang/CMakeLists.txt @@ -290,8 +290,10 @@ endif() if(NOT DEFINED CLANG_VERSION_PATCHLEVEL) set(CLANG_VERSION_PATCHLEVEL ${LLVM_VERSION_PATCH}) endif() -# Unlike PACKAGE_VERSION, CLANG_VERSION does not include LLVM_VERSION_SUFFIX. -set(CLANG_VERSION "${CLANG_VERSION_MAJOR}.${CLANG_VERSION_MINOR}.${CLANG_VERSION_PATCHLEVEL}") +if(NOT DEFINED CLANG_VERSION_SUFFIX) + set(CLANG_VERSION_SUFFIX ${LLVM_VERSION_SUFFIX}) +endif() +set(CLANG_VERSION "${CLANG_VERSION_MAJOR}.${CLANG_VERSION_MINOR}.${CLANG_VERSION_PATCHLEVEL}${CLANG_VERSION_SUFFIX}") message(STATUS "Clang version: ${CLANG_VERSION}") # Configure the Version.inc file. @@ -650,6 +652,7 @@ if (CLANG_ENABLE_BOOTSTRAP) CLANG_VERSION_MAJOR CLANG_VERSION_MINOR CLANG_VERSION_PATCHLEVEL + CLANG_VERSION_SUFFIX CLANG_VENDOR LLVM_VERSION_SUFFIX LLVM_BINUTILS_INCDIR diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst index 37d76d5c2dd58c05a47af49a0b4f9ce50314f26e..3d42571e82d8a07b37f084b2d6c66aeb33761a90 100644 --- a/clang/docs/ClangFormatStyleOptions.rst +++ b/clang/docs/ClangFormatStyleOptions.rst @@ -4211,6 +4211,32 @@ the configuration (without a prefix: ``Auto``). }] } +.. _ObjCPropertyAttributeOrder: + +**ObjCPropertyAttributeOrder** (``List of Strings``) :versionbadge:`clang-format 18` :ref:`¶ ` + The order in which ObjC property attributes should appear. + + Attributes in code will be sorted in the order specified. Any attributes + encountered that are not mentioned in this array will be sorted last, in + stable order. Comments between attributes will leave the attributes + untouched. + + .. warning:: + + Using this option could lead to incorrect code formatting due to + clang-format's lack of complete semantic information. As such, extra + care should be taken to review code changes made by this option. + + .. code-block:: yaml + + ObjCPropertyAttributeOrder: [ + class, direct, + atomic, nonatomic, + assign, retain, strong, copy, weak, unsafe_unretained, + readonly, readwrite, getter, setter, + nullable, nonnull, null_resettable, null_unspecified + ] + .. _ObjCSpaceAfterProperty: **ObjCSpaceAfterProperty** (``Boolean``) :versionbadge:`clang-format 3.7` :ref:`¶ ` diff --git a/clang/docs/ClangOffloadBundler.rst b/clang/docs/ClangOffloadBundler.rst index 1d163db1a9a63bcf50908bd1d26544e02db4cfe2..1f8c85a08f8c79c46b9dc375d9ec167d80a78346 100644 --- a/clang/docs/ClangOffloadBundler.rst +++ b/clang/docs/ClangOffloadBundler.rst @@ -500,7 +500,6 @@ Additional Options while Archive Unbundling as defined in :ref:`code-object-composition` before creating device-specific archive(s). - **-debug-only=CodeObjectCompatibility** Verbose printing of matched/unmatched comparisons between bundle entry id of a device binary from HDA and bundle entry ID of a given target processor diff --git a/clang/docs/InternalsManual.rst b/clang/docs/InternalsManual.rst index f8e3da5f9736829ab7f409a2f1372ba8222afd64..05fadf5a034464364191bcd09f8f82021ae71287 100644 --- a/clang/docs/InternalsManual.rst +++ b/clang/docs/InternalsManual.rst @@ -477,9 +477,8 @@ mode. Instead of formatting and printing out the diagnostics, this implementation just captures and remembers the diagnostics as they fly by. Then ``-verify`` compares the list of produced diagnostics to the list of expected ones. If they disagree, it prints out its own output. Full -documentation for the ``-verify`` mode can be found in the Clang API -documentation for `VerifyDiagnosticConsumer -`_. +documentation for the ``-verify`` mode can be found at +:ref:`verifying-diagnostics`. There are many other possible implementations of this interface, and this is why we prefer diagnostics to pass down rich structured information in @@ -3314,6 +3313,8 @@ Testing All functional changes to Clang should come with test coverage demonstrating the change in behavior. +.. _verifying-diagnostics: + Verifying Diagnostics ^^^^^^^^^^^^^^^^^^^^^ Clang ``-cc1`` supports the ``-verify`` command line option as a way to @@ -3375,7 +3376,7 @@ truncated text instead.) Here's an example of the most commonly used way to specify expected diagnostics: -.. code-block: c++ +.. code-block:: c++ int A = B; // expected-error {{use of undeclared identifier 'B'}} @@ -3385,7 +3386,7 @@ more readable, you can use slash-newline to separate out the diagnostics. Alternatively, it is possible to specify the line on which the diagnostic should appear by appending ``@`` to ``expected-``, for example: -.. code-block: c++ +.. code-block:: c++ #warning some text // expected-warning@10 {{some text}} @@ -3398,7 +3399,7 @@ header file, it may be beneficial to be able to declare the file in which the diagnostic will appear, rather than placing the ``expected-*`` directive in the actual file itself. This can be done using the following syntax: -.. code-block: c++ +.. code-block:: c++ // expected-error@path/include.h:15 {{error message}} @@ -3413,7 +3414,7 @@ diagnostic can instead be indicated by a marker of the form ``#``. Markers are specified by including them in a comment, and then referenced by appending the marker to the diagnostic with ``@#``, as with: -.. code-block: c++ +.. code-block:: c++ #warning some text // #1 // ... other code ... @@ -3428,14 +3429,14 @@ syntax is ``expected- {{diag text}}``, where ```` is one of integer. This allows the diagnostic to appear as many times as specified. For example: -.. code-block: c++ +.. code-block:: c++ void f(); // expected-note 2 {{previous declaration is here}} Where the diagnostic is expected to occur a minimum number of times, this can be specified by appending a ``+`` to the number. For example: -.. code-block: c++ +.. code-block:: c++ void f(); // expected-note 0+ {{previous declaration is here}} void g(); // expected-note 1+ {{previous declaration is here}} @@ -3445,13 +3446,13 @@ swallowed if it occurs, but will not generate an error if it does not occur. In the second example, the diagnostic must occur at least once. As a short-hand, "one or more" can be specified simply by ``+``. For example: -.. code-block: c++ +.. code-block:: c++ void g(); // expected-note + {{previous declaration is here}} A range can also be specified by ``-``. For example: -.. code-block: c++ +.. code-block:: c++ void f(); // expected-note 0-1 {{previous declaration is here}} @@ -3460,13 +3461,13 @@ In this example, the diagnostic may appear only once, if at all. Regex matching mode may be selected by appending ``-re`` to the diagnostic type and including regexes wrapped in double curly braces in the directive, such as: -.. code-block: c++ +.. code-block:: text expected-error-re {{format specifies type 'wchar_t **' (aka '{{.+}}')}} Examples matching error: "variable has incomplete type 'struct s'" -.. code-block: c++ +.. code-block:: c++ // expected-error {{variable has incomplete type 'struct s'}} // expected-error {{variable has incomplete type}} diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst index 8e01ef6cbb3997e9d24a9e89b2542bd573a1b0c1..286405850900aab37177b7af98986d64f8576ce6 100644 --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -2821,7 +2821,7 @@ Example output: The ``__builtin_dump_struct`` function is used to print the fields of a simple structure and their values for debugging purposes. The first argument of the -builtin should be a pointer to the struct to dump. The second argument ``f`` +builtin should be a pointer to a complete record type to dump. The second argument ``f`` should be some callable expression, and can be a function object or an overload set. The builtin calls ``f``, passing any further arguments ``args...`` followed by a ``printf``-compatible format string and the corresponding diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 748e2db2f850744dab5edb31560579d803f8ad28..89ea2f0930ceca1cf3bb6ff4d9ca8960c4d9c491 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -184,6 +184,9 @@ C++2c Feature Support - Implemented `P2864R2 Remove Deprecated Arithmetic Conversion on Enumerations From C++26 `_. +- Implemented `P2361R6 Template parameter initialization `_. + This change is applied as a DR in all language modes. + Resolutions to C++ Defect Reports ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -648,6 +651,13 @@ Bug Fixes in This Version - Fixed false positive error emitted by clang when performing qualified name lookup and the current class instantiation has dependent bases. Fixes (`#13826 `_) +- Fix the name of the ifunc symbol emitted for multiversion functions declared with the + ``target_clones`` attribute. This addresses a linker error that would otherwise occur + when these functions are referenced from other TUs. +- Fixes compile error that double colon operator cannot resolve macro with parentheses. + Fixes (`#64467 `_) +- Clang's ``-Wchar-subscripts`` no longer warns on chars whose values are known non-negative constants. + Fixes (`#18763 `_) Bug Fixes to Compiler Builtins ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -789,6 +799,12 @@ Bug Fixes to C++ Support Fixes: (`#68769 `_) +- Clang now rejects incomplete types for ``__builtin_dump_struct``. Fixes: + (`#63506 `_) + +- Fixed a crash for C++98/03 while checking an ill-formed ``_Static_assert`` expression. + Fixes: (`#72025 `_) + - Clang now defers the instantiation of explicit specifier until constraint checking completes (except deduction guides). Fixes: (`#59827 `_) @@ -796,11 +812,6 @@ Bug Fixes to C++ Support - Fix crash when parsing nested requirement. Fixes: (`#73112 `_) -- Clang now immediately instantiates function template specializations - at the end of the definition of the corresponding function template - when the definition appears after the first point of instantiation. - (`#73232 `_) - Bug Fixes to AST Handling ^^^^^^^^^^^^^^^^^^^^^^^^^ - Fixed an import failure of recursive friend class template. @@ -978,6 +989,8 @@ clang-format - Add ``AllowShortCompoundRequirementOnASingleLine`` option. - Change ``BreakAfterAttributes`` from ``Never`` to ``Leave`` in LLVM style. - Add ``BreakAdjacentStringLiterals`` option. +- Add ``ObjCPropertyAttributeOrder`` which can be used to sort ObjC property + attributes (like ``nonatomic, strong, nullable``). libclang -------- diff --git a/clang/docs/UsersManual.rst b/clang/docs/UsersManual.rst index 2e658557b0e310cd6041af964435c7d63bb49860..9d64195ee338e2c4657a2db5c43db2b6e6b2c4d2 100644 --- a/clang/docs/UsersManual.rst +++ b/clang/docs/UsersManual.rst @@ -2348,9 +2348,10 @@ differences between the two: 1. Profile data generated with one cannot be used by the other, and there is no conversion tool that can convert one to the other. So, a profile generated - via ``-fprofile-instr-generate`` must be used with ``-fprofile-instr-use``. - Similarly, sampling profiles generated by external profilers must be - converted and used with ``-fprofile-sample-use``. + via ``-fprofile-generate`` or ``-fprofile-instr-generate`` must be used with + ``-fprofile-use`` or ``-fprofile-instr-use``. Similarly, sampling profiles + generated by external profilers must be converted and used with ``-fprofile-sample-use`` + or ``-fauto-profile``. 2. Instrumentation profile data can be used for code coverage analysis and optimization. @@ -2598,6 +2599,8 @@ Of those, 31,977 were spent inside the body of ``bar``. The last line of the profile (``2: 0``) corresponds to line 2 inside ``main``. No samples were collected there. +.. _prof_instr: + Profiling with Instrumentation ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -2607,11 +2610,25 @@ overhead during the profiling, but it provides more detailed results than a sampling profiler. It also provides reproducible results, at least to the extent that the code behaves consistently across runs. +Clang supports two types of instrumentation: frontend-based and IR-based. +Frontend-based instrumentation can be enabled with the option ``-fprofile-instr-generate``, +and IR-based instrumentation can be enabled with the option ``-fprofile-generate``. +For best performance with PGO, IR-based instrumentation should be used. It has +the benefits of lower instrumentation overhead, smaller raw profile size, and +better runtime performance. Frontend-based instrumentation, on the other hand, +has better source correlation, so it should be used with source line-based +coverage testing. + +The flag ``-fcs-profile-generate`` also instruments programs using the same +instrumentation method as ``-fprofile-generate``. However, it performs a +post-inline late instrumentation and can produce context-sensitive profiles. + + Here are the steps for using profile guided optimization with instrumentation: 1. Build an instrumented version of the code by compiling and linking with the - ``-fprofile-instr-generate`` option. + ``-fprofile-generate`` or ``-fprofile-instr-generate`` option. .. code-block:: console @@ -2674,8 +2691,8 @@ instrumentation: Note that this step is necessary even when there is only one "raw" profile, since the merge operation also changes the file format. -4. Build the code again using the ``-fprofile-instr-use`` option to specify the - collected profile data. +4. Build the code again using the ``-fprofile-use`` or ``-fprofile-instr-use`` + option to specify the collected profile data. .. code-block:: console @@ -2685,13 +2702,10 @@ instrumentation: profile. As you make changes to your code, clang may no longer be able to use the profile data. It will warn you when this happens. -Profile generation using an alternative instrumentation method can be -controlled by the GCC-compatible flags ``-fprofile-generate`` and -``-fprofile-use``. Although these flags are semantically equivalent to -their GCC counterparts, they *do not* handle GCC-compatible profiles. -They are only meant to implement GCC's semantics with respect to -profile creation and use. Flag ``-fcs-profile-generate`` also instruments -programs using the same instrumentation method as ``-fprofile-generate``. +Note that ``-fprofile-use`` option is semantically equivalent to +its GCC counterpart, it *does not* handle profile formats produced by GCC. +Both ``-fprofile-use`` and ``-fprofile-instr-use`` accept profiles in the +indexed format, regardeless whether it is produced by frontend or the IR pass. .. option:: -fprofile-generate[=] @@ -4401,13 +4415,21 @@ Execute ``clang-cl /?`` to see a list of supported options: Instrument only functions from files where names don't match all the regexes separated by a semi-colon -fprofile-filter-files= Instrument only functions from files where names match any regex separated by a semi-colon - -fprofile-instr-generate= - Generate instrumented code to collect execution counts into + -fprofile-generate= + Generate instrumented code to collect execution counts into a raw profile file in the directory specified by the argument. The filename uses default_%m.profraw pattern + (overridden by LLVM_PROFILE_FILE env var) + -fprofile-generate + Generate instrumented code to collect execution counts into default_%m.profraw file + (overridden by '=' form of option or LLVM_PROFILE_FILE env var) + -fprofile-instr-generate= + Generate instrumented code to collect execution counts into the file whose name pattern is specified as the argument (overridden by LLVM_PROFILE_FILE env var) -fprofile-instr-generate Generate instrumented code to collect execution counts into default.profraw file (overridden by '=' form of option or LLVM_PROFILE_FILE env var) -fprofile-instr-use= + Use instrumentation data for coverage testing or profile-guided optimization + -fprofile-use= Use instrumentation data for profile-guided optimization -fprofile-remapping-file= Use the remappings described in to match the profile data against names in the program @@ -4569,7 +4591,7 @@ clang-cl supports several features that require runtime library support: - Address Sanitizer (ASan): ``-fsanitize=address`` - Undefined Behavior Sanitizer (UBSan): ``-fsanitize=undefined`` - Code coverage: ``-fprofile-instr-generate -fcoverage-mapping`` -- Profile Guided Optimization (PGO): ``-fprofile-instr-generate`` +- Profile Guided Optimization (PGO): ``-fprofile-generate`` - Certain math operations (int128 division) require the builtins library In order to use these features, the user must link the right runtime libraries diff --git a/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h b/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h index c5f14cfcd4f7bee6c93f3e6ceabb75dda64021c8..1dffbe8a09c3609e0af40bb83fff89c9a37e4fea 100644 --- a/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h +++ b/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h @@ -234,6 +234,22 @@ runDataflowAnalysis( return std::move(BlockStates); } +// Create an analysis class that is derived from `DataflowAnalysis`. This is an +// SFINAE adapter that allows us to call two different variants of constructor +// (either with or without the optional `Environment` parameter). +// FIXME: Make all classes derived from `DataflowAnalysis` take an `Environment` +// parameter in their constructor so that we can get rid of this abomination. +template +auto createAnalysis(ASTContext &ASTCtx, Environment &Env) + -> decltype(AnalysisT(ASTCtx, Env)) { + return AnalysisT(ASTCtx, Env); +} +template +auto createAnalysis(ASTContext &ASTCtx, Environment &Env) + -> decltype(AnalysisT(ASTCtx)) { + return AnalysisT(ASTCtx); +} + /// Runs a dataflow analysis over the given function and then runs `Diagnoser` /// over the results. Returns a list of diagnostics for `FuncDecl` or an /// error. Currently, errors can occur (at least) because the analysis requires @@ -262,7 +278,7 @@ llvm::Expected> diagnoseFunction( const WatchedLiteralsSolver *Solver = OwnedSolver.get(); DataflowAnalysisContext AnalysisContext(std::move(OwnedSolver)); Environment Env(AnalysisContext, FuncDecl); - AnalysisT Analysis(ASTCtx); + AnalysisT Analysis = createAnalysis(ASTCtx, Env); llvm::SmallVector Diagnostics; if (llvm::Error Err = runTypeErasedDataflowAnalysis( diff --git a/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h b/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h index c1aa8484817a9d9cc6ffc1c48e03b429fd872cbe..20e45cc27b01fa8e9eb34f4f93aae70ce0c3d98d 100644 --- a/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h +++ b/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h @@ -58,6 +58,10 @@ using FieldSet = llvm::SmallSetVector; /// Returns the set of all fields in the type. FieldSet getObjectFields(QualType Type); +/// Returns whether `Fields` and `FieldLocs` contain the same fields. +bool containsSameFields(const FieldSet &Fields, + const RecordStorageLocation::FieldToLoc &FieldLocs); + struct ContextSensitiveOptions { /// The maximum depth to analyze. A value of zero is equivalent to disabling /// context-sensitive analysis entirely. @@ -92,11 +96,39 @@ public: /*Logger=*/nullptr}); ~DataflowAnalysisContext(); + /// Sets a callback that returns the names and types of the synthetic fields + /// to add to a `RecordStorageLocation` of a given type. + /// Typically, this is called from the constructor of a `DataflowAnalysis` + /// + /// To maintain the invariant that all `RecordStorageLocation`s of a given + /// type have the same fields: + /// * The callback must always return the same result for a given type + /// * `setSyntheticFieldCallback()` must be called before any + // `RecordStorageLocation`s are created. + void setSyntheticFieldCallback( + std::function(QualType)> CB) { + assert(!RecordStorageLocationCreated); + SyntheticFieldCallback = CB; + } + /// Returns a new storage location appropriate for `Type`. /// /// A null `Type` is interpreted as the pointee type of `std::nullptr_t`. StorageLocation &createStorageLocation(QualType Type); + /// Creates a `RecordStorageLocation` for the given type and with the given + /// fields. + /// + /// Requirements: + /// + /// `FieldLocs` must contain exactly the fields returned by + /// `getModeledFields(Type)`. + /// `SyntheticFields` must contain exactly the fields returned by + /// `getSyntheticFields(Type)`. + RecordStorageLocation &createRecordStorageLocation( + QualType Type, RecordStorageLocation::FieldToLoc FieldLocs, + RecordStorageLocation::SyntheticFieldMap SyntheticFields); + /// Returns a stable storage location for `D`. StorageLocation &getStableStorageLocation(const ValueDecl &D); @@ -169,6 +201,15 @@ public: /// context. FieldSet getModeledFields(QualType Type); + /// Returns the names and types of the synthetic fields for the given record + /// type. + llvm::StringMap getSyntheticFields(QualType Type) { + assert(Type->isRecordType()); + if (SyntheticFieldCallback) + return SyntheticFieldCallback(Type); + return {}; + } + private: friend class Environment; @@ -250,6 +291,11 @@ private: FieldSet ModeledFields; std::unique_ptr LogOwner; // If created via flags. + + std::function(QualType)> SyntheticFieldCallback; + + /// Has any `RecordStorageLocation` been created yet? + bool RecordStorageLocationCreated = false; }; } // namespace dataflow diff --git a/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h b/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h index 7c1f5491096326b48f77f0e43d639f68f35ad993..d7e39ab2616fd90514327ca57ecc0a226cc58590 100644 --- a/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h +++ b/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h @@ -166,6 +166,15 @@ public: /// with a symbolic representation of the `this` pointee. Environment(DataflowAnalysisContext &DACtx, const DeclContext &DeclCtx); + /// Assigns storage locations and values to all parameters, captures, global + /// variables, fields and functions referenced in the function currently being + /// analyzed. + /// + /// Requirements: + /// + /// The function must have a body. + void initialize(); + /// Returns a new environment that is a copy of this one. /// /// The state of the program is initially the same, but can be mutated without @@ -283,7 +292,15 @@ public: /// Returns the storage location assigned to the `this` pointee in the /// environment or null if the `this` pointee has no assigned storage location /// in the environment. - RecordStorageLocation *getThisPointeeStorageLocation() const; + RecordStorageLocation *getThisPointeeStorageLocation() const { + return ThisPointeeLoc; + } + + /// Sets the storage location assigned to the `this` pointee in the + /// environment. + void setThisPointeeStorageLocation(RecordStorageLocation &Loc) { + ThisPointeeLoc = &Loc; + } /// Returns the location of the result object for a record-type prvalue. /// @@ -367,7 +384,8 @@ public: /// storage locations and values for indirections until it finds a /// non-pointer/non-reference type. /// - /// If `Type` is a class, struct, or union type, calls `setValue()` to + /// If `Type` is a class, struct, or union type, creates values for all + /// modeled fields (including synthetic fields) and calls `setValue()` to /// associate the `RecordValue` with its storage location /// (`RecordValue::getLoc()`). /// @@ -570,6 +588,9 @@ public: return dyn_cast(getDeclCtx()); } + /// Returns the size of the call stack. + size_t callStackSize() const { return CallStack.size(); } + /// Returns whether this `Environment` can be extended to analyze the given /// `Callee` (i.e. if `pushCall` can be used), with recursion disallowed and a /// given `MaxDepth`. diff --git a/clang/include/clang/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.h b/clang/include/clang/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.h index aeaf75b2154222cad737b2f92c5db06da146ab1b..09eb8b93822612f4e885c6416a5e8fc960d096f0 100644 --- a/clang/include/clang/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.h +++ b/clang/include/clang/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.h @@ -45,7 +45,7 @@ struct UncheckedOptionalAccessModelOptions { class UncheckedOptionalAccessModel : public DataflowAnalysis { public: - UncheckedOptionalAccessModel(ASTContext &Ctx); + UncheckedOptionalAccessModel(ASTContext &Ctx, dataflow::Environment &Env); /// Returns a matcher for the optional classes covered by this model. static ast_matchers::DeclarationMatcher optionalClassDecl(); @@ -54,17 +54,6 @@ public: void transfer(const CFGElement &Elt, NoopLattice &L, Environment &Env); - ComparisonResult compare(QualType Type, const Value &Val1, - const Environment &Env1, const Value &Val2, - const Environment &Env2) override; - - bool merge(QualType Type, const Value &Val1, const Environment &Env1, - const Value &Val2, const Environment &Env2, Value &MergedVal, - Environment &MergedEnv) override; - - Value *widen(QualType Type, Value &Prev, const Environment &PrevEnv, - Value &Current, Environment &CurrentEnv) override; - private: CFGMatchSwitch> TransferMatchSwitch; }; diff --git a/clang/include/clang/Analysis/FlowSensitive/RecordOps.h b/clang/include/clang/Analysis/FlowSensitive/RecordOps.h index f007a947a82f1328a1d2cd196fd8b6e146cfa90e..7b87840d626b4b608960bfd76edbaf833dff4d6a 100644 --- a/clang/include/clang/Analysis/FlowSensitive/RecordOps.h +++ b/clang/include/clang/Analysis/FlowSensitive/RecordOps.h @@ -21,10 +21,10 @@ namespace dataflow { /// Copies a record (struct, class, or union) from `Src` to `Dst`. /// -/// This performs a deep copy, i.e. it copies every field and recurses on -/// fields of record type. It also copies properties from the `RecordValue` -/// associated with `Src` to the `RecordValue` associated with `Dst` (if these -/// `RecordValue`s exist). +/// This performs a deep copy, i.e. it copies every field (including synthetic +/// fields) and recurses on fields of record type. It also copies properties +/// from the `RecordValue` associated with `Src` to the `RecordValue` associated +/// with `Dst` (if these `RecordValue`s exist). /// /// If there is a `RecordValue` associated with `Dst` in the environment, this /// function creates a new `RecordValue` and associates it with `Dst`; clients @@ -47,10 +47,11 @@ void copyRecord(RecordStorageLocation &Src, RecordStorageLocation &Dst, /// retrieved from `Env2`. A convenience overload retrieves values for `Loc1` /// and `Loc2` from the same environment. /// -/// This performs a deep comparison, i.e. it compares every field and recurses -/// on fields of record type. Fields of reference type compare equal if they -/// refer to the same storage location. If `RecordValue`s are associated with -/// `Loc1` and `Loc2`, it also compares the properties on those `RecordValue`s. +/// This performs a deep comparison, i.e. it compares every field (including +/// synthetic fields) and recurses on fields of record type. Fields of reference +/// type compare equal if they refer to the same storage location. If +/// `RecordValue`s are associated with `Loc1` and Loc2`, it also compares the +/// properties on those `RecordValue`s. /// /// Note on how to interpret the result: /// - If this returns true, the records are guaranteed to be equal at runtime. diff --git a/clang/include/clang/Analysis/FlowSensitive/StorageLocation.h b/clang/include/clang/Analysis/FlowSensitive/StorageLocation.h index 89d2bbfbb69f9fbbf1bd38440fffa2b6f57af511..8fcc6a44027a02065d150fb8948ca6cad547f2f3 100644 --- a/clang/include/clang/Analysis/FlowSensitive/StorageLocation.h +++ b/clang/include/clang/Analysis/FlowSensitive/StorageLocation.h @@ -73,7 +73,13 @@ public: /// /// Contains storage locations for all modeled fields of the record (also /// referred to as "children"). The child map is flat, so accessible members of -/// the base class are directly accesible as children of this location. +/// the base class are directly accessible as children of this location. +/// +/// Record storage locations may also contain so-called synthetic fields. These +/// are typically used to model the internal state of a class (e.g. the value +/// stored in a `std::optional`) without having to depend on that class's +/// implementation details. All `RecordStorageLocation`s of a given type should +/// have the same synthetic fields. /// /// The storage location for a field of reference type may be null. This /// typically occurs in one of two situations: @@ -88,12 +94,12 @@ public: class RecordStorageLocation final : public StorageLocation { public: using FieldToLoc = llvm::DenseMap; + using SyntheticFieldMap = llvm::StringMap; - explicit RecordStorageLocation(QualType Type) - : RecordStorageLocation(Type, FieldToLoc()) {} - - RecordStorageLocation(QualType Type, FieldToLoc TheChildren) - : StorageLocation(Kind::Record, Type), Children(std::move(TheChildren)) { + RecordStorageLocation(QualType Type, FieldToLoc TheChildren, + SyntheticFieldMap TheSyntheticFields) + : StorageLocation(Kind::Record, Type), Children(std::move(TheChildren)), + SyntheticFields(std::move(TheSyntheticFields)) { assert(!Type.isNull()); assert(Type->isRecordType()); assert([this] { @@ -133,6 +139,19 @@ public: return It->second; } + /// Returns the storage location for the synthetic field `Name`. + /// The synthetic field must exist. + StorageLocation &getSyntheticField(llvm::StringRef Name) const { + StorageLocation *Loc = SyntheticFields.lookup(Name); + assert(Loc != nullptr); + return *Loc; + } + + llvm::iterator_range + synthetic_fields() const { + return {SyntheticFields.begin(), SyntheticFields.end()}; + } + /// Changes the child storage location for a field `D` of reference type. /// All other fields cannot change their storage location and always retain /// the storage location passed to the `RecordStorageLocation` constructor. @@ -151,6 +170,7 @@ public: private: FieldToLoc Children; + SyntheticFieldMap SyntheticFields; }; } // namespace dataflow diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 1800f584c7e1088ac1b0f354f587ad2338dd6042..121ed203829cec44dcf1ec1eb9fc149ff0712e4e 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -2024,6 +2024,14 @@ def BPFPreserveAccessIndex : InheritableAttr, let LangOpts = [COnly]; } +def BPFPreserveStaticOffset : InheritableAttr, + TargetSpecificAttr { + let Spellings = [Clang<"preserve_static_offset">]; + let Subjects = SubjectList<[Record], ErrorDiag>; + let Documentation = [BPFPreserveStaticOffsetDocs]; + let LangOpts = [COnly]; +} + def BTFDeclTag : InheritableAttr { let Spellings = [Clang<"btf_decl_tag">]; let Args = [StringArgument<"BTFDeclTag">]; diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index f2c4eb51b443ddb83b0c059a40fdd9fc68bc05a1..bbe4de94cbabef2f225473ad43b0a2c37e184ee3 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -2199,6 +2199,43 @@ preserving struct or union member access debuginfo indices of this struct or union, similar to clang ``__builtin_preserve_access_index()``. }]; } + +def BPFPreserveStaticOffsetDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +Clang supports the ``__attribute__((preserve_static_offset))`` +attribute for the BPF target. This attribute may be attached to a +struct or union declaration. Reading or writing fields of types having +such annotation is guaranteed to generate LDX/ST/STX instruction with +offset corresponding to the field. + +For example: + +.. code-block:: c + + struct foo { + int a; + int b; + }; + + struct bar { + int a; + struct foo b; + } __attribute__((preserve_static_offset)); + + void buz(struct bar *g) { + g->b.a = 42; + } + +The assignment to ``g``'s field would produce an ST instruction with +offset 8: ``*(u32)(r1 + 8) = 42;``. + +Without this attribute generated instructions might be different, +depending on optimizations behavior. E.g. the example above could be +rewritten as ``r1 += 8; *(u32)(r1 + 0) = 42;``. + }]; +} + def BTFDeclTagDocs : Documentation { let Category = DocCatFunction; let Content = [{ @@ -2515,6 +2552,13 @@ example, the following will emit 4 versions of the function: __attribute__((target_clones("arch=atom,avx2","arch=ivybridge","default"))) void foo() {} +For targets that support the GNU indirect function (IFUNC) feature, dispatch +is performed by emitting an indirect function that is resolved to the appropriate +target clone at load time. The indirect function is given the name the +multiversioned function would have if it had been declared without the attribute. +For backward compatibility with earlier Clang releases, a function alias with an +``.ifunc`` suffix is also emitted. The ``.ifunc`` suffixed symbol is a deprecated +feature and support for it may be removed in the future. }]; } @@ -7547,7 +7591,7 @@ For example, Task foo() { return increment(1); } // Error. foo is not a coroutine. // Fine for a coroutine wrapper to return a CRT. - Task [[clang::coro_wrapper]] foo() { return increment(1); } + [[clang::coro_wrapper]] Task foo() { return increment(1); } void bar() { // Invalid. This intantiates a function which returns a CRT but is not marked as diff --git a/clang/include/clang/Basic/BuiltinsAMDGPU.def b/clang/include/clang/Basic/BuiltinsAMDGPU.def index a19c8bd5f219ec6a2fb1f9b624b1fdb94d64db94..8b59b3790d7bc661361cf6f2b8f154877425658a 100644 --- a/clang/include/clang/Basic/BuiltinsAMDGPU.def +++ b/clang/include/clang/Basic/BuiltinsAMDGPU.def @@ -150,8 +150,8 @@ BUILTIN(__builtin_amdgcn_mqsad_u32_u8, "V4UiWUiUiV4Ui", "nc") // Ballot builtins. //===----------------------------------------------------------------------===// -TARGET_BUILTIN(__builtin_amdgcn_ballot_w32, "Uib", "nc", "wavefrontsize32") -TARGET_BUILTIN(__builtin_amdgcn_ballot_w64, "LUib", "nc", "wavefrontsize64") +TARGET_BUILTIN(__builtin_amdgcn_ballot_w32, "ZUib", "nc", "wavefrontsize32") +TARGET_BUILTIN(__builtin_amdgcn_ballot_w64, "WUib", "nc", "wavefrontsize64") // Deprecated intrinsics in favor of __builtin_amdgn_ballot_{w32|w64} BUILTIN(__builtin_amdgcn_uicmp, "WUiUiUiIi", "nc") diff --git a/clang/include/clang/Basic/BuiltinsSystemZ.def b/clang/include/clang/Basic/BuiltinsSystemZ.def index 079e4113648859ee1647cbf96e0a66840f6a0242..4cfc52ae421686d1750a4143eb674ec4b7fea2d7 100644 --- a/clang/include/clang/Basic/BuiltinsSystemZ.def +++ b/clang/include/clang/Basic/BuiltinsSystemZ.def @@ -80,10 +80,10 @@ TARGET_BUILTIN(__builtin_s390_vavglb, "V16UcV16UcV16Uc", "nc", "vector") TARGET_BUILTIN(__builtin_s390_vavglh, "V8UsV8UsV8Us", "nc", "vector") TARGET_BUILTIN(__builtin_s390_vavglf, "V4UiV4UiV4Ui", "nc", "vector") TARGET_BUILTIN(__builtin_s390_vavglg, "V2ULLiV2ULLiV2ULLi", "nc", "vector") -TARGET_BUILTIN(__builtin_s390_vceqbs, "V16ScV16ScV16Sci*", "nc", "vector") -TARGET_BUILTIN(__builtin_s390_vceqhs, "V8SsV8SsV8Ssi*", "nc", "vector") -TARGET_BUILTIN(__builtin_s390_vceqfs, "V4SiV4SiV4Sii*", "nc", "vector") -TARGET_BUILTIN(__builtin_s390_vceqgs, "V2SLLiV2SLLiV2SLLii*", "nc", "vector") +TARGET_BUILTIN(__builtin_s390_vceqbs, "V16ScV16UcV16Uci*", "nc", "vector") +TARGET_BUILTIN(__builtin_s390_vceqhs, "V8SsV8UsV8Usi*", "nc", "vector") +TARGET_BUILTIN(__builtin_s390_vceqfs, "V4SiV4UiV4Uii*", "nc", "vector") +TARGET_BUILTIN(__builtin_s390_vceqgs, "V2SLLiV2ULLiV2ULLii*", "nc", "vector") TARGET_BUILTIN(__builtin_s390_vchbs, "V16ScV16ScV16Sci*", "nc", "vector") TARGET_BUILTIN(__builtin_s390_vchhs, "V8SsV8SsV8Ssi*", "nc", "vector") TARGET_BUILTIN(__builtin_s390_vchfs, "V4SiV4SiV4Sii*", "nc", "vector") @@ -105,10 +105,10 @@ TARGET_BUILTIN(__builtin_s390_verimb, "V16UcV16UcV16UcV16UcIi", "nc", "vector") TARGET_BUILTIN(__builtin_s390_verimh, "V8UsV8UsV8UsV8UsIi", "nc", "vector") TARGET_BUILTIN(__builtin_s390_verimf, "V4UiV4UiV4UiV4UiIi", "nc", "vector") TARGET_BUILTIN(__builtin_s390_verimg, "V2ULLiV2ULLiV2ULLiV2ULLiIi", "nc", "vector") -TARGET_BUILTIN(__builtin_s390_verllb, "V16UcV16UcUi", "nc", "vector") -TARGET_BUILTIN(__builtin_s390_verllh, "V8UsV8UsUi", "nc", "vector") -TARGET_BUILTIN(__builtin_s390_verllf, "V4UiV4UiUi", "nc", "vector") -TARGET_BUILTIN(__builtin_s390_verllg, "V2ULLiV2ULLiUi", "nc", "vector") +TARGET_BUILTIN(__builtin_s390_verllb, "V16UcV16UcUc", "nc", "vector") +TARGET_BUILTIN(__builtin_s390_verllh, "V8UsV8UsUc", "nc", "vector") +TARGET_BUILTIN(__builtin_s390_verllf, "V4UiV4UiUc", "nc", "vector") +TARGET_BUILTIN(__builtin_s390_verllg, "V2ULLiV2ULLiUc", "nc", "vector") TARGET_BUILTIN(__builtin_s390_verllvb, "V16UcV16UcV16Uc", "nc", "vector") TARGET_BUILTIN(__builtin_s390_verllvh, "V8UsV8UsV8Us", "nc", "vector") TARGET_BUILTIN(__builtin_s390_verllvf, "V4UiV4UiV4Ui", "nc", "vector") @@ -253,8 +253,8 @@ TARGET_BUILTIN(__builtin_s390_vfsqdb, "V2dV2d", "nc", "vector") TARGET_BUILTIN(__builtin_s390_vftcidb, "V2SLLiV2dIii*", "nc", "vector") // Vector-enhancements facility 1 intrinsics. -TARGET_BUILTIN(__builtin_s390_vlrl, "V16ScUivC*", "", "vector-enhancements-1") -TARGET_BUILTIN(__builtin_s390_vstrl, "vV16ScUiv*", "", "vector-enhancements-1") +TARGET_BUILTIN(__builtin_s390_vlrlr, "V16ScUivC*", "", "vector-enhancements-1") +TARGET_BUILTIN(__builtin_s390_vstrlr, "vV16ScUiv*", "", "vector-enhancements-1") TARGET_BUILTIN(__builtin_s390_vbperm, "V2ULLiV16UcV16Uc", "nc", "vector-enhancements-1") TARGET_BUILTIN(__builtin_s390_vmslg, "V16UcV2ULLiV2ULLiV16UcIi", "nc", "vector-enhancements-1") TARGET_BUILTIN(__builtin_s390_vfmaxdb, "V2dV2dV2dIi", "nc", "vector-enhancements-1") diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def index 675645cd534ed76358500294191fe97b0e3d2fb2..0acb5ae134ea245ed60fe91dbe18ff727fc13e71 100644 --- a/clang/include/clang/Basic/CodeGenOptions.def +++ b/clang/include/clang/Basic/CodeGenOptions.def @@ -28,12 +28,8 @@ CODEGENOPT(Name, Bits, Default) #endif CODEGENOPT(DisableIntegratedAS, 1, 0) ///< -no-integrated-as -ENUM_CODEGENOPT(CompressDebugSections, llvm::DebugCompressionType, 2, - llvm::DebugCompressionType::None) CODEGENOPT(RelaxELFRelocations, 1, 1) ///< -Wa,-mrelax-relocations={yes,no} CODEGENOPT(AsmVerbose , 1, 0) ///< -dA, -fverbose-asm. -CODEGENOPT(Dwarf64 , 1, 0) ///< -gdwarf64. -CODEGENOPT(Dwarf32 , 1, 1) ///< -gdwarf32. CODEGENOPT(PreserveAsmComments, 1, 1) ///< -dA, -fno-preserve-as-comments. CODEGENOPT(AssumeSaneOperatorNew , 1, 1) ///< implicit __attribute__((malloc)) operator new CODEGENOPT(AssumeUniqueVTables , 1, 1) ///< Assume a class has only one vtable. @@ -73,10 +69,6 @@ CODEGENOPT(DebugPassManager, 1, 0) ///< Prints debug information for the new CODEGENOPT(DisableRedZone , 1, 0) ///< Set when -mno-red-zone is enabled. CODEGENOPT(EmitCallSiteInfo, 1, 0) ///< Emit call site info only in the case of ///< '-g' + 'O>0' level. -CODEGENOPT(EnableDIPreservationVerify, 1, 0) ///< Enable di preservation verify - ///< each (it means check - ///< the original debug info - ///< metadata preservation). CODEGENOPT(IndirectTlsSegRefs, 1, 0) ///< Set when -mno-tls-direct-seg-refs ///< is specified. CODEGENOPT(DisableTailCalls , 1, 0) ///< Do not emit tail calls. @@ -113,16 +105,10 @@ CODEGENOPT(IndirectBranchCSPrefix, 1, 0) ///< if -mindirect-branch-cs-prefix CODEGENOPT(XRayInstrumentFunctions , 1, 0) ///< Set when -fxray-instrument is ///< enabled. CODEGENOPT(StackSizeSection , 1, 0) ///< Set when -fstack-size-section is enabled. -CODEGENOPT(ForceDwarfFrameSection , 1, 0) ///< Set when -fforce-dwarf-frame is - ///< enabled. ///< Set when -femit-compact-unwind-non-canonical is enabled. CODEGENOPT(EmitCompactUnwindNonCanonical, 1, 0) -///< Set when -femit-dwarf-unwind is passed. -ENUM_CODEGENOPT(EmitDwarfUnwind, llvm::EmitDwarfUnwindType, 2, - llvm::EmitDwarfUnwindType::Default) - ///< Set when -fxray-always-emit-customevents is enabled. CODEGENOPT(XRayAlwaysEmitCustomEvents , 1, 0) @@ -178,8 +164,6 @@ CODEGENOPT(IncrementalLinkerCompatible, 1, 0) ///< Emit an object file which can CODEGENOPT(MergeAllConstants , 1, 1) ///< Merge identical constants. CODEGENOPT(MergeFunctions , 1, 0) ///< Set when -fmerge-functions is enabled. CODEGENOPT(NoCommon , 1, 0) ///< Set when -fno-common or C++ is enabled. -CODEGENOPT(NoDwarfDirectoryAsm , 1, 0) ///< Set when -fno-dwarf-directory-asm is - ///< enabled. CODEGENOPT(NoExecStack , 1, 0) ///< Set when -Wa,--noexecstack is enabled. CODEGENOPT(FatalWarnings , 1, 0) ///< Set when -Wa,--fatal-warnings is ///< enabled. @@ -187,8 +171,6 @@ CODEGENOPT(NoWarn , 1, 0) ///< Set when -Wa,--no-warn is enabled. CODEGENOPT(NoTypeCheck , 1, 0) ///< Set when -Wa,--no-type-check is enabled. CODEGENOPT(MisExpect , 1, 0) ///< Set when -Wmisexpect is enabled CODEGENOPT(EnableSegmentedStacks , 1, 0) ///< Set when -fsplit-stack is enabled. -CODEGENOPT(NoInlineLineTables, 1, 0) ///< Whether debug info should contain - ///< inline line tables. CODEGENOPT(StackClashProtector, 1, 0) ///< Set when -fstack-clash-protection is enabled. CODEGENOPT(NoImplicitFloat , 1, 0) ///< Set when -mno-implicit-float is enabled. CODEGENOPT(NullPointerIsValid , 1, 0) ///< Assume Null pointer deference is defined. @@ -341,37 +323,6 @@ VALUE_CODEGENOPT(StackProbeSize , 32, 4096) ///< Overrides default stack ///< probe size, even if 0. VALUE_CODEGENOPT(WarnStackSize , 32, UINT_MAX) ///< Set via -fwarn-stack-size. CODEGENOPT(NoStackArgProbe, 1, 0) ///< Set when -mno-stack-arg-probe is used -CODEGENOPT(DebugStrictDwarf, 1, 1) ///< Whether or not to use strict DWARF info. - -/// Control the Assignment Tracking debug info feature. -ENUM_CODEGENOPT(AssignmentTrackingMode, AssignmentTrackingOpts, 2, AssignmentTrackingOpts::Disabled) - -CODEGENOPT(DebugColumnInfo, 1, 0) ///< Whether or not to use column information - ///< in debug info. - -CODEGENOPT(DebugTypeExtRefs, 1, 0) ///< Whether or not debug info should contain - ///< external references to a PCH or module. - -CODEGENOPT(DebugExplicitImport, 1, 0) ///< Whether or not debug info should - ///< contain explicit imports for - ///< anonymous namespaces - -/// Set debug info source file hashing algorithm. -ENUM_CODEGENOPT(DebugSrcHash, DebugSrcHashKind, 2, DSH_MD5) - -CODEGENOPT(SplitDwarfInlining, 1, 1) ///< Whether to include inlining info in the - ///< skeleton CU to allow for symbolication - ///< of inline stack frames without .dwo files. -CODEGENOPT(DebugFwdTemplateParams, 1, 0) ///< Whether to emit complete - ///< template parameter descriptions in - ///< forward declarations (versus just - ///< including them in the name). -ENUM_CODEGENOPT(DebugSimpleTemplateNames, llvm::codegenoptions::DebugTemplateNamesKind, 2, llvm::codegenoptions::DebugTemplateNamesKind::Full) ///< Whether to emit template parameters - ///< in the textual names of template - ///< specializations. - ///< Implies DebugFwdTemplateNames to - ///< allow decorated names to be - ///< reconstructed when needed. CODEGENOPT(EmitLLVMUseLists, 1, 0) ///< Control whether to serialize use-lists. CODEGENOPT(WholeProgramVTables, 1, 0) ///< Whether to apply whole-program @@ -395,30 +346,6 @@ VALUE_CODEGENOPT(SmallDataLimit, 32, 0) /// The lower bound for a buffer to be considered for stack protection. VALUE_CODEGENOPT(SSPBufferSize, 32, 0) -/// The kind of generated debug info. -ENUM_CODEGENOPT(DebugInfo, llvm::codegenoptions::DebugInfoKind, 4, llvm::codegenoptions::NoDebugInfo) - -/// Whether to generate macro debug info. -CODEGENOPT(MacroDebugInfo, 1, 0) - -/// Tune the debug info for this debugger. -ENUM_CODEGENOPT(DebuggerTuning, llvm::DebuggerKind, 3, - llvm::DebuggerKind::Default) - -/// Dwarf version. Version zero indicates to LLVM that no DWARF should be -/// emitted. -VALUE_CODEGENOPT(DwarfVersion, 3, 0) - -/// Whether we should emit CodeView debug information. It's possible to emit -/// CodeView and DWARF into the same object. -CODEGENOPT(EmitCodeView, 1, 0) - -/// Whether to emit the .debug$H section containing hashes of CodeView types. -CODEGENOPT(CodeViewGHash, 1, 0) - -/// Whether to emit the compiler path and command line into the CodeView debug information. -CODEGENOPT(CodeViewCommandLine, 1, 0) - /// The kind of inlining to perform. ENUM_CODEGENOPT(Inlining, InliningMethod, 2, NormalInlining) @@ -451,26 +378,14 @@ CODEGENOPT(DirectAccessExternalData, 1, 0) /// paths that reach the end of a function without executing a required return. CODEGENOPT(StrictReturn, 1, 1) -/// Whether emit extra debug info for sample pgo profile collection. -CODEGENOPT(DebugInfoForProfiling, 1, 0) - /// Whether emit pseudo probes for sample pgo profile collection. CODEGENOPT(PseudoProbeForProfiling, 1, 0) /// Whether 3-component vector type is preserved. CODEGENOPT(PreserveVec3Type, 1, 0) -/// Whether to emit .debug_gnu_pubnames section instead of .debug_pubnames. -CODEGENOPT(DebugNameTable, 2, 0) - -/// Whether to use DWARF base address specifiers in .debug_ranges. -CODEGENOPT(DebugRangesBaseAddress, 1, 0) - CODEGENOPT(NoPLT, 1, 0) -/// Whether to embed source in DWARF debug line section. -CODEGENOPT(EmbedSource, 1, 0) - /// Whether to emit all vtables CODEGENOPT(ForceEmitVTables, 1, 0) @@ -515,6 +430,9 @@ ENUM_CODEGENOPT(ZeroCallUsedRegs, llvm::ZeroCallUsedRegs::ZeroCallUsedRegsKind, /// non-deleting destructors. (No effect on Microsoft ABI.) CODEGENOPT(CtorDtorReturnThis, 1, 0) +/// FIXME: Make DebugOptions its own top-level .def file. +#include "DebugOptions.def" + #undef CODEGENOPT #undef ENUM_CODEGENOPT #undef VALUE_CODEGENOPT diff --git a/clang/include/clang/Basic/CodeGenOptions.h b/clang/include/clang/Basic/CodeGenOptions.h index b202d01af0ed6ce13dbaa09b245456b255fdee05..6952b48e898a819bef0f3b38d7d1cca65549c158 100644 --- a/clang/include/clang/Basic/CodeGenOptions.h +++ b/clang/include/clang/Basic/CodeGenOptions.h @@ -530,6 +530,10 @@ public: return SanitizeBinaryMetadataCovered || SanitizeBinaryMetadataAtomics || SanitizeBinaryMetadataUAR; } + + /// Reset all of the options that are not considered when building a + /// module. + void resetNonModularOptions(StringRef ModuleFormat); }; } // end namespace clang diff --git a/clang/include/clang/Basic/DebugOptions.def b/clang/include/clang/Basic/DebugOptions.def new file mode 100644 index 0000000000000000000000000000000000000000..7cd3edf08a17eadb3ae0965e8747654f4e66a593 --- /dev/null +++ b/clang/include/clang/Basic/DebugOptions.def @@ -0,0 +1,146 @@ +//===--- DebugOptions.def - Debug option database ----------------- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines debug-specific codegen options. Users of this file +// must define the CODEGENOPT macro to make use of this information. +// Optionally, the user may also define DEBUGOPT (for flags), ENUM_DEBUGOPT (for +// options that have enumeration type), and VALUE_DEBUGOPT (is a debug option +// that describes a value rather than a flag). +// +// BENIGN_ variants of the macros are used to describe options that do not +// affect the generated PCM. +// +//===----------------------------------------------------------------------===// +#ifndef DEBUGOPT +#define DEBUGOPT(Name, Bits, Default) \ +CODEGENOPT(Name, Bits, Default) +#endif + +#ifndef VALUE_DEBUGOPT +# define VALUE_DEBUGOPT(Name, Bits, Default) \ +VALUE_CODEGENOPT(Name, Bits, Default) +#endif + +#ifndef ENUM_DEBUGOPT +# define ENUM_DEBUGOPT(Name, Type, Bits, Default) \ +ENUM_CODEGENOPT(Name, Type, Bits, Default) +#endif + +#ifndef BENIGN_DEBUGOPT +#define BENIGN_DEBUGOPT(Name, Bits, Default) \ +DEBUGOPT(Name, Bits, Default) +#endif + +#ifndef BENIGN_VALUE_DEBUGOPT +# define BENIGN_VALUE_DEBUGOPT(Name, Bits, Default) \ +VALUE_DEBUGOPT(Name, Bits, Default) +#endif + +#ifndef BENIGN_ENUM_DEBUGOPT +# define BENIGN_ENUM_DEBUGOPT(Name, Type, Bits, Default) \ +ENUM_DEBUGOPT(Name, Type, Bits, Default) +#endif + +BENIGN_ENUM_DEBUGOPT(CompressDebugSections, llvm::DebugCompressionType, 2, + llvm::DebugCompressionType::None) +DEBUGOPT(Dwarf64, 1, 0) ///< -gdwarf64. +BENIGN_DEBUGOPT(EnableDIPreservationVerify, 1, 0) ///< Enable di preservation + ///< verify each (it means + ///< check the original debug + ///< info metadata + ///< preservation). +BENIGN_DEBUGOPT(ForceDwarfFrameSection , 1, 0) ///< Set when -fforce-dwarf-frame + ///< is enabled. + +///< Set when -femit-dwarf-unwind is passed. +BENIGN_ENUM_DEBUGOPT(EmitDwarfUnwind, llvm::EmitDwarfUnwindType, 2, + llvm::EmitDwarfUnwindType::Default) + +BENIGN_DEBUGOPT(NoDwarfDirectoryAsm , 1, 0) ///< Set when -fno-dwarf-directory-asm + ///< is enabled. + +BENIGN_DEBUGOPT(NoInlineLineTables, 1, 0) ///< Whether debug info should contain + ///< inline line tables. + +DEBUGOPT(DebugStrictDwarf, 1, 1) ///< Whether or not to use strict DWARF info. + +/// Control the Assignment Tracking debug info feature. +BENIGN_ENUM_DEBUGOPT(AssignmentTrackingMode, AssignmentTrackingOpts, 2, + AssignmentTrackingOpts::Disabled) + +DEBUGOPT(DebugColumnInfo, 1, 0) ///< Whether or not to use column information + ///< in debug info. + +DEBUGOPT(DebugTypeExtRefs, 1, 0) ///< Whether or not debug info should contain + ///< external references to a PCH or module. + +DEBUGOPT(DebugExplicitImport, 1, 0) ///< Whether or not debug info should + ///< contain explicit imports for + ///< anonymous namespaces + +/// Set debug info source file hashing algorithm. +ENUM_DEBUGOPT(DebugSrcHash, DebugSrcHashKind, 2, DSH_MD5) + +DEBUGOPT(SplitDwarfInlining, 1, 1) ///< Whether to include inlining info in the + ///< skeleton CU to allow for symbolication + ///< of inline stack frames without .dwo files. +DEBUGOPT(DebugFwdTemplateParams, 1, 0) ///< Whether to emit complete + ///< template parameter descriptions in + ///< forward declarations (versus just + ///< including them in the name). +ENUM_DEBUGOPT(DebugSimpleTemplateNames, + llvm::codegenoptions::DebugTemplateNamesKind, 2, + llvm::codegenoptions::DebugTemplateNamesKind::Full) + ///< Whether to emit template parameters in the textual names of + ///< template specializations. + ///< Implies DebugFwdTemplateNames to allow decorated names to be + ///< reconstructed when needed. + +/// The kind of generated debug info. +ENUM_DEBUGOPT(DebugInfo, llvm::codegenoptions::DebugInfoKind, 4, + llvm::codegenoptions::NoDebugInfo) + +/// Whether to generate macro debug info. +DEBUGOPT(MacroDebugInfo, 1, 0) + +/// Tune the debug info for this debugger. +ENUM_DEBUGOPT(DebuggerTuning, llvm::DebuggerKind, 3, + llvm::DebuggerKind::Default) + +/// Dwarf version. Version zero indicates to LLVM that no DWARF should be +/// emitted. +VALUE_DEBUGOPT(DwarfVersion, 3, 0) + +/// Whether we should emit CodeView debug information. It's possible to emit +/// CodeView and DWARF into the same object. +DEBUGOPT(EmitCodeView, 1, 0) + +/// Whether to emit the .debug$H section containing hashes of CodeView types. +DEBUGOPT(CodeViewGHash, 1, 0) + +/// Whether to emit the compiler path and command line into the CodeView debug information. +DEBUGOPT(CodeViewCommandLine, 1, 0) + +/// Whether emit extra debug info for sample pgo profile collection. +DEBUGOPT(DebugInfoForProfiling, 1, 0) + +/// Whether to emit .debug_gnu_pubnames section instead of .debug_pubnames. +DEBUGOPT(DebugNameTable, 2, 0) + +/// Whether to use DWARF base address specifiers in .debug_ranges. +DEBUGOPT(DebugRangesBaseAddress, 1, 0) + +/// Whether to embed source in DWARF debug line section. +DEBUGOPT(EmbedSource, 1, 0) + +#undef DEBUGOPT +#undef ENUM_DEBUGOPT +#undef VALUE_DEBUGOPT +#undef BENIGN_DEBUGOPT +#undef BENIGN_ENUM_DEBUGOPT +#undef BENIGN_VALUE_DEBUGOPT diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td index 21fe6066d5876105928a8560508ccf216331002f..e0e199e0d7300392537c72d88669ce64a0d2439a 100644 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -1362,7 +1362,7 @@ def warn_pragma_acc_unimplemented_clause_parsing : Warning<"OpenACC clause parsing not yet implemented">, InGroup; def err_acc_invalid_directive - : Error<"invalid OpenACC directive '%select{%1|%1 %2}0'">; + : Error<"invalid OpenACC directive %select{%1|'%1 %2'}0">; def err_acc_missing_directive : Error<"expected OpenACC directive">; def err_acc_invalid_open_paren : Error<"expected clause-list or newline in OpenACC directive">; diff --git a/clang/include/clang/Basic/FileEntry.h b/clang/include/clang/Basic/FileEntry.h index bc65463735488417dc241961c63df4df4fb7ae52..6351aeae92e2c457c1b196c095b27d9c291c37d2 100644 --- a/clang/include/clang/Basic/FileEntry.h +++ b/clang/include/clang/Basic/FileEntry.h @@ -394,6 +394,7 @@ class FileEntry { public: ~FileEntry(); + LLVM_DEPRECATED("Use FileEntryRef::getName() instead.", "") StringRef getName() const { return LastRef->getName(); } StringRef tryGetRealPathName() const { return RealPathName; } diff --git a/clang/include/clang/Basic/OpenACCKinds.h b/clang/include/clang/Basic/OpenACCKinds.h index 1a5bf7e0e831ce35db7480d5e61efac4c395fc6d..449a75638b43f5e8316043baf7b76cfe28c851fc 100644 --- a/clang/include/clang/Basic/OpenACCKinds.h +++ b/clang/include/clang/Basic/OpenACCKinds.h @@ -34,7 +34,7 @@ enum class OpenACCDirectiveKind { // Misc. Loop, - // FIXME: 'cache' + Cache, // Combined Constructs. ParallelLoop, diff --git a/clang/include/clang/Basic/arm_sme.td b/clang/include/clang/Basic/arm_sme.td index d55deeaa40bbcd54615682c5e00aa7d70146cf07..fcff6fe35b7ba3f856d5b56ca05b5af1853d5c24 100644 --- a/clang/include/clang/Basic/arm_sme.td +++ b/clang/include/clang/Basic/arm_sme.td @@ -314,3 +314,42 @@ let TargetGuard = "sme2" in { def SVBMOPS : Inst<"svbmops_za32[_{d}]_m", "viPPdd", "iUi", MergeNone, "aarch64_sme_bmops_za32", [IsSharedZA, IsStreaming], [ImmCheck<0, ImmCheck0_3>]>; } + +// +// Spill and fill of ZT0 +// +let TargetGuard = "sme2" in { + def SVLDR_ZT : Inst<"svldr_zt", "viQ", "", MergeNone, "aarch64_sme_ldr_zt", [IsOverloadNone, IsStreamingCompatible, IsSharedZA], [ImmCheck<0, ImmCheck0_0>]>; + def SVSTR_ZT : Inst<"svstr_zt", "vi%", "", MergeNone, "aarch64_sme_str_zt", [IsOverloadNone, IsStreamingCompatible, IsSharedZA, IsPreservesZA], [ImmCheck<0, ImmCheck0_0>]>; +} + +// +// Zero ZT0 +// +let TargetGuard = "sme2" in { + def SVZERO_ZT : Inst<"svzero_zt", "vi", "", MergeNone, "aarch64_sme_zero_zt", [IsOverloadNone, IsStreamingCompatible, IsSharedZA], [ImmCheck<0, ImmCheck0_0>]>; +} + +// +// lookup table expand four contiguous registers +// +let TargetGuard = "sme2" in { + def SVLUTI2_LANE_ZT_X4 : Inst<"svluti2_lane_zt_{d}_x4", "4.di[i", "cUcsUsiUibhf", MergeNone, "aarch64_sme_luti2_lane_zt_x4", [IsStreaming, IsSharedZA, IsPreservesZA], [ImmCheck<0, ImmCheck0_0>, ImmCheck<2, ImmCheck0_3>]>; + def SVLUTI4_LANE_ZT_X4 : Inst<"svluti4_lane_zt_{d}_x4", "4.di[i", "sUsiUibhf", MergeNone, "aarch64_sme_luti4_lane_zt_x4", [IsStreaming, IsSharedZA, IsPreservesZA], [ImmCheck<0, ImmCheck0_0>, ImmCheck<2, ImmCheck0_1>]>; +} + +// +// lookup table expand one register +// +let TargetGuard = "sme2" in { + def SVLUTI2_LANE_ZT : Inst<"svluti2_lane_zt_{d}", "di[i", "cUcsUsiUibhf", MergeNone, "aarch64_sme_luti2_lane_zt", [IsStreaming, IsSharedZA, IsPreservesZA], [ImmCheck<0, ImmCheck0_0>, ImmCheck<2, ImmCheck0_15>]>; + def SVLUTI4_LANE_ZT : Inst<"svluti4_lane_zt_{d}", "di[i", "cUcsUsiUibhf", MergeNone, "aarch64_sme_luti4_lane_zt", [IsStreaming, IsSharedZA, IsPreservesZA], [ImmCheck<0, ImmCheck0_0>, ImmCheck<2, ImmCheck0_7>]>; +} + +// +// lookup table expand two contiguous registers +// +let TargetGuard = "sme2" in { + def SVLUTI2_LANE_ZT_X2 : Inst<"svluti2_lane_zt_{d}_x2", "2.di[i", "cUcsUsiUibhf", MergeNone, "aarch64_sme_luti2_lane_zt_x2", [IsStreaming, IsSharedZA, IsPreservesZA], [ImmCheck<0, ImmCheck0_0>, ImmCheck<2, ImmCheck0_7>]>; + def SVLUTI4_LANE_ZT_X2 : Inst<"svluti4_lane_zt_{d}_x2", "2.di[i", "cUcsUsiUibhf", MergeNone, "aarch64_sme_luti4_lane_zt_x2", [IsStreaming, IsSharedZA, IsPreservesZA], [ImmCheck<0, ImmCheck0_0>, ImmCheck<2, ImmCheck0_3>]>; +} diff --git a/clang/include/clang/Basic/arm_sve.td b/clang/include/clang/Basic/arm_sve.td index 7d1af51fb30ba10f52e8ba924cfedfb8c48416c3..896c6f6e8033cdc7e15b2494bf3034f96c363f92 100644 --- a/clang/include/clang/Basic/arm_sve.td +++ b/clang/include/clang/Basic/arm_sve.td @@ -1925,8 +1925,6 @@ def SVBGRP_N : SInst<"svbgrp[_n_{d}]", "dda", "UcUsUiUl", MergeNone, "aarch64_sv let TargetGuard = "sve2p1" in { def SVFCLAMP : SInst<"svclamp[_{d}]", "dddd", "hfd", MergeNone, "aarch64_sve_fclamp", [], []>; -def SVPTRUE_COUNT : SInst<"svptrue_{d}", "}v", "QcQsQiQl", MergeNone, "aarch64_sve_ptrue_{d}", [IsOverloadNone], []>; -def SVPFALSE_COUNT_ALIAS : SInst<"svpfalse_c", "}v", "", MergeNone, "", [IsOverloadNone]>; def SVPEXT_SINGLE : SInst<"svpext_lane_{d}", "P}i", "QcQsQiQl", MergeNone, "aarch64_sve_pext", [], [ImmCheck<1, ImmCheck0_3>]>; def SVPEXT_X2 : SInst<"svpext_lane_{d}_x2", "2.P}i", "QcQsQiQl", MergeNone, "aarch64_sve_pext_x2", [], [ImmCheck<1, ImmCheck0_1>]>; @@ -2045,6 +2043,12 @@ def SVCNTP_COUNT : SInst<"svcntp_{d}", "n}i", "QcQsQiQl", MergeNone, "aarch64_sv defm SVREVD : SInstZPZ<"svrevd", "csilUcUsUiUl", "aarch64_sve_revd">; } +let TargetGuard = "sve2p1|sme2" in { + //FIXME: Replace IsStreamingCompatible with IsStreamingOrHasSVE2p1 when available + def SVPTRUE_COUNT : SInst<"svptrue_{d}", "}v", "QcQsQiQl", MergeNone, "aarch64_sve_ptrue_{d}", [IsOverloadNone, IsStreamingCompatible], []>; + + def SVPFALSE_COUNT_ALIAS : SInst<"svpfalse_c", "}v", "", MergeNone, "", [IsOverloadNone, IsStreamingCompatible]>; +} let TargetGuard = "sve2p1,b16b16" in { defm SVMUL_BF : SInstZPZZ<"svmul", "b", "aarch64_sve_fmul", "aarch64_sve_fmul_u">; @@ -2119,6 +2123,17 @@ let TargetGuard = "sme2" in { // 2-way and 4-way selects def SVSEL_X2 : SInst<"svsel[_{d}_x2]", "2}22", "cUcsUsiUilUlbhfd", MergeNone, "aarch64_sve_sel_x2", [IsStreaming], []>; def SVSEL_X4 : SInst<"svsel[_{d}_x4]", "4}44", "cUcsUsiUilUlbhfd", MergeNone, "aarch64_sve_sel_x4", [IsStreaming], []>; + + // SRSHL / URSHL + def SVSRSHL_SINGLE_X2 : SInst<"svrshl[_single_{d}_x2]", "22d", "csil", MergeNone, "aarch64_sve_srshl_single_x2", [IsStreaming], []>; + def SVURSHL_SINGLE_X2 : SInst<"svrshl[_single_{d}_x2]", "22d", "UcUsUiUl", MergeNone, "aarch64_sve_urshl_single_x2", [IsStreaming], []>; + def SVSRSHL_SINGLE_X4 : SInst<"svrshl[_single_{d}_x4]", "44d", "csil", MergeNone, "aarch64_sve_srshl_single_x4", [IsStreaming], []>; + def SVURSHL_SINGLE_X4 : SInst<"svrshl[_single_{d}_x4]", "44d", "UcUsUiUl", MergeNone, "aarch64_sve_urshl_single_x4", [IsStreaming], []>; + + def SVSRSHL_X2 : SInst<"svrshl[_{d}_x2]", "222", "csil", MergeNone, "aarch64_sve_srshl_x2", [IsStreaming], []>; + def SVURSHL_X2 : SInst<"svrshl[_{d}_x2]", "222", "UcUsUiUl", MergeNone, "aarch64_sve_urshl_x2", [IsStreaming], []>; + def SVSRSHL_X4 : SInst<"svrshl[_{d}_x4]", "444", "csil", MergeNone, "aarch64_sve_srshl_x4", [IsStreaming], []>; + def SVURSHL_X4 : SInst<"svrshl[_{d}_x4]", "444", "UcUsUiUl", MergeNone, "aarch64_sve_urshl_x4", [IsStreaming], []>; } let TargetGuard = "sve2p1" in { @@ -2153,3 +2168,63 @@ let TargetGuard = "sve2p1" in { defm SVPMOV_TO_VEC_LANE_S : PMOV_TO_VEC<"svpmov", "iUi", "aarch64_sve_pmov_to_vector_lane", [], ImmCheck1_3>; defm SVPMOV_TO_VEC_LANE_D : PMOV_TO_VEC<"svpmov", "lUl", "aarch64_sve_pmov_to_vector_lane" ,[], ImmCheck1_7>; } + +// +// Multi-vector convert to/from floating-point. +// +let TargetGuard = "sme2" in { + def SVCVT_F16_X2 : SInst<"svcvt_f16[_f32_x2]", "e2", "f", MergeNone, "aarch64_sve_fcvt_x2", [IsStreaming],[]>; + def SVCVT_BF16_X2 : SInst<"svcvt_bf16[_f32_x2]", "$2", "f", MergeNone, "aarch64_sve_bfcvt_x2", [IsOverloadNone, IsStreaming],[]>; + + def SVCVT_F32_U32_X2 : SInst<"svcvt_{d}[_u32_x2]", "2.d2.u", "f", MergeNone, "aarch64_sve_fcvtu_x2", [IsStreaming], []>; + def SVCVT_U32_F32_X2 : SInst<"svcvt_u32[_{d}_x2]", "2.u2.d", "f", MergeNone, "aarch64_sve_ucvtf_x2", [IsStreaming], []>; + def SVCVT_F32_S32_X2 : SInst<"svcvt_{d}[_s32_x2]", "2.d2.x", "f", MergeNone, "aarch64_sve_fcvts_x2", [IsStreaming], []>; + def SVCVT_S32_F32_X2 : SInst<"svcvt_s32[_{d}_x2]", "2.x2.d", "f", MergeNone, "aarch64_sve_scvtf_x2", [IsStreaming], []>; + + def SVCVT_F32_U32_X4 : SInst<"svcvt_{d}[_u32_x4]", "4.d4.u", "f", MergeNone, "aarch64_sve_fcvtu_x4", [IsStreaming], []>; + def SVCVT_U32_F32_X4 : SInst<"svcvt_u32[_{d}_x4]", "4.u4.d", "f", MergeNone, "aarch64_sve_ucvtf_x4", [IsStreaming], []>; + def SVCVT_F32_S32_X4 : SInst<"svcvt_{d}[_s32_x4]", "4.d4.x", "f", MergeNone, "aarch64_sve_fcvts_x4", [IsStreaming], []>; + def SVCVT_S32_F32_X4 : SInst<"svcvt_s32[_{d}_x4]", "4.x4.d", "f", MergeNone, "aarch64_sve_scvtf_x4", [IsStreaming], []>; +} + +// +// Multi-vector floating-point convert from single-precision to interleaved half-precision/BFloat16 +// +let TargetGuard = "sme2" in { + def SVCVTN_F16_X2 : SInst<"svcvtn_f16[_f32_x2]", "e2", "f", MergeNone, "aarch64_sve_fcvtn_x2", [IsStreaming],[]>; + def SVCVTN_BF16_X2 : SInst<"svcvtn_bf16[_f32_x2]", "$2", "f", MergeNone, "aarch64_sve_bfcvtn_x2", [IsOverloadNone, IsStreaming],[]>; +} + +// +// Multi-vector saturating extract narrow +// +let TargetGuard = "sme2" in { + def SVQCVT_S16_S32_X2 : SInst<"svqcvt_s16[_{d}_x2]", "h2.d", "i", MergeNone, "aarch64_sve_sqcvt_x2", [IsStreaming], []>; + def SVQCVT_U16_U32_X2 : SInst<"svqcvt_u16[_{d}_x2]", "e2.d", "Ui", MergeNone, "aarch64_sve_uqcvt_x2", [IsStreaming], []>; + def SVQCVT_U16_S32_X2 : SInst<"svqcvt_u16[_{d}_x2]", "e2.d", "i", MergeNone, "aarch64_sve_sqcvtu_x2", [IsStreaming], []>; + + def SVQCVT_S8_S32_X4 : SInst<"svqcvt_s8[_{d}_x4]", "q4.d", "i", MergeNone, "aarch64_sve_sqcvt_x4", [IsStreaming], []>; + def SVQCVT_U8_U32_X4 : SInst<"svqcvt_u8[_{d}_x4]", "b4.d", "Ui", MergeNone, "aarch64_sve_uqcvt_x4", [IsStreaming], []>; + def SVQCVT_U8_S32_X4 : SInst<"svqcvt_u8[_{d}_x4]", "b4.d", "i", MergeNone, "aarch64_sve_sqcvtu_x4", [IsStreaming], []>; + + def SVQCVT_S16_S64_X4 : SInst<"svqcvt_s16[_{d}_x4]", "q4.d", "l", MergeNone, "aarch64_sve_sqcvt_x4", [IsStreaming], []>; + def SVQCVT_U16_U64_X4 : SInst<"svqcvt_u16[_{d}_x4]", "b4.d", "Ul", MergeNone, "aarch64_sve_uqcvt_x4", [IsStreaming], []>; + def SVQCVT_U16_S64_X4 : SInst<"svqcvt_u16[_{d}_x4]", "b4.d", "l", MergeNone, "aarch64_sve_sqcvtu_x4", [IsStreaming], []>; +} + +// +// Multi-vector saturating extract narrow and interleave +// +let TargetGuard = "sme2" in { + def SVQCVTN_S16_S32_X2 : SInst<"svqcvtn_s16[_{d}_x2]", "h2.d", "i", MergeNone, "aarch64_sve_sqcvtn_x2", [IsStreamingCompatible], []>; + def SVQCVTN_U16_U32_X2 : SInst<"svqcvtn_u16[_{d}_x2]", "e2.d", "Ui", MergeNone, "aarch64_sve_uqcvtn_x2", [IsStreamingCompatible], []>; + def SVQCVTN_U16_S32_X2 : SInst<"svqcvtn_u16[_{d}_x2]", "e2.d", "i", MergeNone, "aarch64_sve_sqcvtun_x2", [IsStreamingCompatible], []>; + + def SVQCVTN_S8_S32_X4 : SInst<"svqcvtn_s8[_{d}_x4]", "q4.d", "i", MergeNone, "aarch64_sve_sqcvtn_x4", [IsStreaming], []>; + def SVQCVTN_U8_U32_X4 : SInst<"svqcvtn_u8[_{d}_x4]", "b4.d", "Ui", MergeNone, "aarch64_sve_uqcvtn_x4", [IsStreaming], []>; + def SVQCVTN_U8_S32_X4 : SInst<"svqcvtn_u8[_{d}_x4]", "b4.d", "i", MergeNone, "aarch64_sve_sqcvtun_x4", [IsStreaming], []>; + + def SVQCVTN_S16_S64_X4 : SInst<"svqcvtn_s16[_{d}_x4]", "q4.d", "l", MergeNone, "aarch64_sve_sqcvtn_x4", [IsStreaming], []>; + def SVQCVTN_U16_U64_X4 : SInst<"svqcvtn_u16[_{d}_x4]", "b4.d", "Ul", MergeNone, "aarch64_sve_uqcvtn_x4", [IsStreaming], []>; + def SVQCVTN_U16_S64_X4 : SInst<"svqcvtn_u16[_{d}_x4]", "b4.d", "l", MergeNone, "aarch64_sve_sqcvtun_x4", [IsStreaming], []>; +} diff --git a/clang/include/clang/Basic/arm_sve_sme_incl.td b/clang/include/clang/Basic/arm_sve_sme_incl.td index 21dac067ab66e615354da7701e08162bc06f886e..040ce95a57de3dc43385269897de2e0816c5da40 100644 --- a/clang/include/clang/Basic/arm_sve_sme_incl.td +++ b/clang/include/clang/Basic/arm_sve_sme_incl.td @@ -93,6 +93,7 @@ // m: uint32_t // n: uint64_t +// [: svuint8_t // t: svint32_t // z: svuint32_t // g: svuint64_t diff --git a/clang/include/clang/Driver/Multilib.h b/clang/include/clang/Driver/Multilib.h index 1416559414f894b8db92ffcd730fc2d6d9d6447d..6a9533e6dd831f15ffd5acf3d5db6d4a50692adb 100644 --- a/clang/include/clang/Driver/Multilib.h +++ b/clang/include/clang/Driver/Multilib.h @@ -39,13 +39,22 @@ private: std::string IncludeSuffix; flags_list Flags; + // Optionally, a multilib can be assigned a string tag indicating that it's + // part of a group of mutually exclusive possibilities. If two or more + // multilibs have the same non-empty value of ExclusiveGroup, then only the + // last matching one of them will be selected. + // + // Setting this to the empty string is a special case, indicating that the + // directory is not mutually exclusive with anything else. + std::string ExclusiveGroup; + public: /// GCCSuffix, OSSuffix & IncludeSuffix will be appended directly to the /// sysroot string so they must either be empty or begin with a '/' character. /// This is enforced with an assert in the constructor. Multilib(StringRef GCCSuffix = {}, StringRef OSSuffix = {}, - StringRef IncludeSuffix = {}, - const flags_list &Flags = flags_list()); + StringRef IncludeSuffix = {}, const flags_list &Flags = flags_list(), + StringRef ExclusiveGroup = {}); /// Get the detected GCC installation path suffix for the multi-arch /// target variant. Always starts with a '/', unless empty @@ -63,6 +72,9 @@ public: /// All elements begin with either '-' or '!' const flags_list &flags() const { return Flags; } + /// Get the exclusive group label. + const std::string &exclusiveGroup() const { return ExclusiveGroup; } + LLVM_DUMP_METHOD void dump() const; /// print summary of the Multilib void print(raw_ostream &OS) const; diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index 7dd2755350f7a56a765f0cec8e0d6317b4b8ee51..0eec2b352637623f1841216f5f72887e61a96220 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -994,7 +994,7 @@ def all__load : Flag<["-"], "all_load">; def allowable__client : Separate<["-"], "allowable_client">; def ansi : Flag<["-", "--"], "ansi">, Group; def arch__errors__fatal : Flag<["-"], "arch_errors_fatal">; -def arch : Separate<["-"], "arch">, Flags<[NoXarchOption]>; +def arch : Separate<["-"], "arch">, Flags<[NoXarchOption,TargetSpecific]>; def arch__only : Separate<["-"], "arch_only">; def autocomplete : Joined<["--"], "autocomplete=">; def bind__at__load : Flag<["-"], "bind_at_load">; @@ -3172,7 +3172,8 @@ def fno_ms_compatibility : Flag<["-"], "fno-ms-compatibility">, Group, def fno_objc_legacy_dispatch : Flag<["-"], "fno-objc-legacy-dispatch">, Group; def fno_objc_weak : Flag<["-"], "fno-objc-weak">, Group, Visibility<[ClangOption, CC1Option]>; -def fno_omit_frame_pointer : Flag<["-"], "fno-omit-frame-pointer">, Group; +def fno_omit_frame_pointer : Flag<["-"], "fno-omit-frame-pointer">, Group, + Visibility<[ClangOption, FlangOption]>; defm operator_names : BoolFOption<"operator-names", LangOpts<"CXXOperatorNames">, Default, NegFlag>; def fomit_frame_pointer : Flag<["-"], "fomit-frame-pointer">, Group, + Visibility<[ClangOption, FlangOption]>, HelpText<"Omit the frame pointer from functions that don't need it. " "Some stack unwinding cases, such as profilers and sanitizers, may prefer specifying -fno-omit-frame-pointer. " "On many targets, -O1 and higher omit the frame pointer by default. " @@ -5169,7 +5171,7 @@ def nodriverkitlib : Flag<["-"], "nodriverkitlib">; def nofixprebinding : Flag<["-"], "nofixprebinding">; def nolibc : Flag<["-"], "nolibc">; def nomultidefs : Flag<["-"], "nomultidefs">; -def nopie : Flag<["-"], "nopie">, Visibility<[ClangOption, FlangOption]>; +def nopie : Flag<["-"], "nopie">, Visibility<[ClangOption, FlangOption]>, Flags<[TargetSpecific]>; // OpenBSD def no_pie : Flag<["-"], "no-pie">, Visibility<[ClangOption, FlangOption]>; def noprebind : Flag<["-"], "noprebind">; def noprofilelib : Flag<["-"], "noprofilelib">; @@ -5997,6 +5999,15 @@ def mno_gather : Flag<["-"], "mno-gather">, Group, HelpText<"Disable generation of gather instructions in auto-vectorization(x86 only)">; def mno_scatter : Flag<["-"], "mno-scatter">, Group, HelpText<"Disable generation of scatter instructions in auto-vectorization(x86 only)">; +def mapx_features_EQ : CommaJoined<["-"], "mapx-features=">, Group, + HelpText<"Enable features of APX">, Values<"egpr,push2pop2,ppx,ndd,ccmp,cf">; +def mno_apx_features_EQ : CommaJoined<["-"], "mno-apx-features=">, Group, + HelpText<"Disable features of APX">, Values<"egpr,push2pop2,ppx,ndd,ccmp,cf">; +// Features egpr, push2pop2 and ppx are validated with llvm-test-suite && cpu2017 on Intel SDE. +// For stability, we turn on these features only for -mapxf. After a feature pass the validation, +// we will add it to -mapxf. +def mapxf : Flag<["-"], "mapxf">, Alias, AliasArgs<["egpr","push2pop2","ppx"]>; +def mno_apxf : Flag<["-"], "mno-apxf">, Alias, AliasArgs<["egpr","push2pop2","ppx"]>; } // let Flags = [TargetSpecific] // VE feature flags @@ -6333,9 +6344,6 @@ defm stack_arrays : BoolOptionWithoutMarshalling<"f", "stack-arrays", defm loop_versioning : BoolOptionWithoutMarshalling<"f", "version-loops-for-stride", PosFlag, NegFlag>; -defm alias_analysis : BoolOptionWithoutMarshalling<"f", "alias-analysis", - PosFlag, - NegFlag>; } // let Visibility = [FC1Option, FlangOption] def J : JoinedOrSeparate<["-"], "J">, @@ -6786,10 +6794,6 @@ def new_struct_path_tbaa : Flag<["-"], "new-struct-path-tbaa">, def mdebug_pass : Separate<["-"], "mdebug-pass">, HelpText<"Enable additional debug output">, MarshallingInfoString>; -def mframe_pointer_EQ : Joined<["-"], "mframe-pointer=">, - HelpText<"Specify which frame pointers to retain.">, Values<"all,non-leaf,none">, - NormalizedValuesScope<"CodeGenOptions::FramePointerKind">, NormalizedValues<["All", "NonLeaf", "None"]>, - MarshallingInfoEnum, "None">; def mabi_EQ_ieeelongdouble : Flag<["-"], "mabi=ieeelongdouble">, HelpText<"Use IEEE 754 quadruple-precision for long double">, MarshallingInfoFlag>; @@ -7400,6 +7404,11 @@ def pic_is_pie : Flag<["-"], "pic-is-pie">, HelpText<"File is for a position independent executable">, MarshallingInfoFlag>; +def mframe_pointer_EQ : Joined<["-"], "mframe-pointer=">, + HelpText<"Specify which frame pointers to retain.">, Values<"all,non-leaf,none">, + NormalizedValuesScope<"CodeGenOptions::FramePointerKind">, NormalizedValues<["All", "NonLeaf", "None"]>, + MarshallingInfoEnum, "None">; + def dependent_lib : Joined<["--"], "dependent-lib=">, HelpText<"Add dependent library">, diff --git a/clang/include/clang/Format/.clang-format b/clang/include/clang/Format/.clang-format index 63d45e0307a3dfebe23e458478f608e837174a0b..d7331b3c8cf02ef7944e83a4280476627e33b31d 100644 --- a/clang/include/clang/Format/.clang-format +++ b/clang/include/clang/Format/.clang-format @@ -1,5 +1 @@ -BasedOnStyle: LLVM -InsertBraces: true -InsertNewlineAtEOF: true -LineEnding: LF -RemoveBracesLLVM: true +BasedOnStyle: clang-format diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h index 4fbcc4ef0705b6a10e8bad76afa734065ffc748b..8604dea689f9371669ed9d7570dea224de4b1dae 100644 --- a/clang/include/clang/Format/Format.h +++ b/clang/include/clang/Format/Format.h @@ -3264,6 +3264,29 @@ struct FormatStyle { /// \version 11 bool ObjCBreakBeforeNestedBlockParam; + /// The order in which ObjC property attributes should appear. + /// + /// Attributes in code will be sorted in the order specified. Any attributes + /// encountered that are not mentioned in this array will be sorted last, in + /// stable order. Comments between attributes will leave the attributes + /// untouched. + /// \warning + /// Using this option could lead to incorrect code formatting due to + /// clang-format's lack of complete semantic information. As such, extra + /// care should be taken to review code changes made by this option. + /// \endwarning + /// \code{.yaml} + /// ObjCPropertyAttributeOrder: [ + /// class, direct, + /// atomic, nonatomic, + /// assign, retain, strong, copy, weak, unsafe_unretained, + /// readonly, readwrite, getter, setter, + /// nullable, nonnull, null_resettable, null_unspecified + /// ] + /// \endcode + /// \version 18 + std::vector ObjCPropertyAttributeOrder; + /// Add a space after ``@property`` in Objective-C, i.e. use /// ``@property (readonly)`` instead of ``@property(readonly)``. /// \version 3.7 @@ -4821,6 +4844,7 @@ struct FormatStyle { ObjCBlockIndentWidth == R.ObjCBlockIndentWidth && ObjCBreakBeforeNestedBlockParam == R.ObjCBreakBeforeNestedBlockParam && + ObjCPropertyAttributeOrder == R.ObjCPropertyAttributeOrder && ObjCSpaceAfterProperty == R.ObjCSpaceAfterProperty && ObjCSpaceBeforeProtocolList == R.ObjCSpaceBeforeProtocolList && PackConstructorInitializers == R.PackConstructorInitializers && diff --git a/clang/include/clang/IndexSerialization/SerializablePathCollection.h b/clang/include/clang/IndexSerialization/SerializablePathCollection.h index 06948db6fc95f2a97eee547b2d75eecc08461a13..6337a81196688f04523961f3da46de094f6c1fbd 100644 --- a/clang/include/clang/IndexSerialization/SerializablePathCollection.h +++ b/clang/include/clang/IndexSerialization/SerializablePathCollection.h @@ -110,7 +110,7 @@ public: /// Stores path to \p FE if it hasn't been stored yet. /// \returns index to array exposed by getPathsBuffer(). - size_t tryStoreFilePath(const clang::FileEntry &FE); + size_t tryStoreFilePath(FileEntryRef FE); private: /// Stores \p Path if it is non-empty. diff --git a/clang/include/clang/Lex/PPCallbacks.h b/clang/include/clang/Lex/PPCallbacks.h index 94f96cf9c512541b5670c906075c38a9b94f89cc..e3942af7be2803026c3243d1a6b5bd9e5721c90a 100644 --- a/clang/include/clang/Lex/PPCallbacks.h +++ b/clang/include/clang/Lex/PPCallbacks.h @@ -22,11 +22,11 @@ #include "llvm/ADT/StringRef.h" namespace clang { - class Token; - class IdentifierInfo; - class MacroDefinition; - class MacroDirective; - class MacroArgs; +class Token; +class IdentifierInfo; +class MacroDefinition; +class MacroDirective; +class MacroArgs; /// This interface provides a way to observe the actions of the /// preprocessor as it does its thing. diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index ca29ce46873e8a4a9fef861fb5c6a0548a934385..32d0b76c35b0d7780db09dfc1d412e7473d56e54 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -3538,7 +3538,12 @@ public: private: void ParseOpenACCDirective(); - ExprResult ParseOpenACCRoutineName(); + /// Helper that parses an ID Expression based on the language options. + ExprResult ParseOpenACCIDExpression(); + /// Parses the variable list for the `cache` construct. + void ParseOpenACCCacheVarList(); + /// Parses a single variable in a variable list for the 'cache' construct. + bool ParseOpenACCCacheVar(); private: //===--------------------------------------------------------------------===// diff --git a/clang/include/clang/Sema/ExternalSemaSource.h b/clang/include/clang/Sema/ExternalSemaSource.h index 8b41c5483458a0f41a09934924fc311fc051c59c..22d1ee2df115a6e7d88f8e4f80fb2887252819b0 100644 --- a/clang/include/clang/Sema/ExternalSemaSource.h +++ b/clang/include/clang/Sema/ExternalSemaSource.h @@ -181,9 +181,6 @@ public: SmallVectorImpl > &Pending) {} - virtual void ReadPendingInstantiationsOfConstexprEntity( - const NamedDecl *D, llvm::SmallSetVector &Decls){}; - /// Read the set of late parsed template functions for this source. /// /// The external source should insert its own late parsed template functions diff --git a/clang/include/clang/Sema/MultiplexExternalSemaSource.h b/clang/include/clang/Sema/MultiplexExternalSemaSource.h index 6054ef39e54ff9a663085ffa060a4c9f996eee4d..2bf91cb5212c5eb786c897e7ad7c916a59316290 100644 --- a/clang/include/clang/Sema/MultiplexExternalSemaSource.h +++ b/clang/include/clang/Sema/MultiplexExternalSemaSource.h @@ -319,9 +319,6 @@ public: void ReadPendingInstantiations( SmallVectorImpl >& Pending) override; - virtual void ReadPendingInstantiationsOfConstexprEntity( - const NamedDecl *D, llvm::SmallSetVector &Decls) override; - /// Read the set of late parsed template functions for this source. /// /// The external source should insert its own late parsed template functions diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index a4912e3625c389fc987230bbd3f83bdc673a6103..f45e0a7d3d52d43b0ddb3285964c85268ae5f15a 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -59,7 +59,6 @@ #include "clang/Sema/TypoCorrection.h" #include "clang/Sema/Weak.h" #include "llvm/ADT/ArrayRef.h" -#include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SetVector.h" #include "llvm/ADT/SmallBitVector.h" #include "llvm/ADT/SmallPtrSet.h" @@ -3933,6 +3932,11 @@ public: APValue &Value, CCEKind CCE, NamedDecl *Dest = nullptr); + ExprResult + EvaluateConvertedConstantExpression(Expr *E, QualType T, APValue &Value, + CCEKind CCE, bool RequireInt, + const APValue &PreNarrowingValue); + /// Abstract base class used to perform a contextual implicit /// conversion from an expression to any type passing a filter. class ContextualImplicitConverter { @@ -10092,12 +10096,6 @@ public: /// but have not yet been performed. std::deque PendingInstantiations; - /// Track constexpr functions referenced before they are (lexically) defined. - /// The key is the pattern, associated with a list of specialisations that - /// need to be instantiated when the pattern is defined. - llvm::DenseMap> - PendingInstantiationsOfConstexprEntities; - /// Queue of implicit template instantiations that cannot be performed /// eagerly. SmallVector LateParsedInstantiations; @@ -10416,9 +10414,6 @@ public: bool Recursive = false, bool DefinitionRequired = false, bool AtEndOfTU = false); - - void PerformPendingInstantiationsOfConstexprFunctions(FunctionDecl *Template); - VarTemplateSpecializationDecl *BuildVarTemplateInstantiation( VarTemplateDecl *VarTemplate, VarDecl *FromVar, const TemplateArgumentList &TemplateArgList, @@ -13850,6 +13845,8 @@ private: CallExpr *TheCall); bool CheckMVEBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall); bool CheckSVEBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall); + bool ParseSVEImmChecks(CallExpr *TheCall, + SmallVector, 3> &ImmChecks); bool CheckCDEBuiltinFunctionCall(const TargetInfo &TI, unsigned BuiltinID, CallExpr *TheCall); bool CheckARMCoprocessorImmediate(const TargetInfo &TI, const Expr *CoprocArg, diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h index 08642889b0cf3eb715f8e36c523690843e24eac5..fdd64f2abbe93752510ff51d6d99796d2ffa8c04 100644 --- a/clang/include/clang/Serialization/ASTBitCodes.h +++ b/clang/include/clang/Serialization/ASTBitCodes.h @@ -695,10 +695,6 @@ enum ASTRecordTypes { /// Record code for an unterminated \#pragma clang assume_nonnull begin /// recorded in a preamble. PP_ASSUME_NONNULL_LOC = 67, - - /// Record code for constexpr templated entities that have been used but not - /// yet instantiated. - PENDING_INSTANTIATIONS_OF_CONSTEXPR_ENTITIES = 68, }; /// Record types used within a source manager block. diff --git a/clang/include/clang/Serialization/ASTReader.h b/clang/include/clang/Serialization/ASTReader.h index 407fc614f483e6cd32d5b1919d963369ae74aa5b..7eefdca6815cdadb81a8fd34bbdae8c2d0760800 100644 --- a/clang/include/clang/Serialization/ASTReader.h +++ b/clang/include/clang/Serialization/ASTReader.h @@ -814,9 +814,6 @@ private: /// is the instantiation location. SmallVector PendingInstantiations; - llvm::DenseMap> - PendingInstantiationsOfConstexprEntities; - //@} /// \name DiagnosticsEngine-relevant special data @@ -2104,9 +2101,6 @@ public: SmallVectorImpl> &Pending) override; - virtual void ReadPendingInstantiationsOfConstexprEntity( - const NamedDecl *D, llvm::SmallSetVector &Decls) override; - void ReadLateParsedTemplates( llvm::MapVector> &LPTMap) override; diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h index 4e94ad49a783360e9dd5da30f4680dcccfac42cb..4de04bc4d397ac4788d6719df2dace5199a76420 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h @@ -110,6 +110,22 @@ public: return nullptr; } + /// Attempt to return the minimal possible value for a given symbol. Note + /// that a ConstraintManager is not obligated to return a lower bound, it may + /// also return nullptr. + virtual const llvm::APSInt *getSymMinVal(ProgramStateRef state, + SymbolRef sym) const { + return nullptr; + } + + /// Attempt to return the minimal possible value for a given symbol. Note + /// that a ConstraintManager is not obligated to return a lower bound, it may + /// also return nullptr. + virtual const llvm::APSInt *getSymMaxVal(ProgramStateRef state, + SymbolRef sym) const { + return nullptr; + } + /// Scan all symbols referenced by the constraints. If the symbol is not /// alive, remove it. virtual ProgramStateRef removeDeadBindings(ProgramStateRef state, diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h index a64cf7ae4efcb82db450d0f67ed792c461f406af..d7cff49036cb81064e1c24e1cacfc8772e163009 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h @@ -110,6 +110,14 @@ public: /// that value is returned. Otherwise, returns NULL. virtual const llvm::APSInt *getKnownValue(ProgramStateRef state, SVal val) = 0; + /// Tries to get the minimal possible (integer) value of a given SVal. If the + /// constraint manager cannot provide an useful answer, this returns NULL. + virtual const llvm::APSInt *getMinValue(ProgramStateRef state, SVal val) = 0; + + /// Tries to get the maximal possible (integer) value of a given SVal. If the + /// constraint manager cannot provide an useful answer, this returns NULL. + virtual const llvm::APSInt *getMaxValue(ProgramStateRef state, SVal val) = 0; + /// Simplify symbolic expressions within a given SVal. Return an SVal /// that represents the same value, but is hopefully easier to work with /// than the original SVal. diff --git a/clang/include/module.modulemap b/clang/include/module.modulemap index 7dbb7cf02b4f82c19f428348ff9b2f302b4fa294..52395ee9b0fc9ffca68550876c54d846941f3ebb 100644 --- a/clang/include/module.modulemap +++ b/clang/include/module.modulemap @@ -68,6 +68,7 @@ module Clang_Basic { textual header "clang/Basic/BuiltinsX86_64.def" textual header "clang/Basic/BuiltinsXCore.def" textual header "clang/Basic/CodeGenOptions.def" + textual header "clang/Basic/DebugOptions.def" textual header "clang/Basic/DiagnosticOptions.def" textual header "clang/Basic/Features.def" textual header "clang/Basic/FPOptions.def" diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 2aafe5bd5289fc25e72ada474471fd996ed52e45..986302e1fd225f2194880f02c49a5c5b2715443f 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -8618,7 +8618,7 @@ bool LValueExprEvaluator::VisitMaterializeTemporaryExpr( Result.set(E); } else { Value = &Info.CurrentCall->createTemporary( - E, E->getType(), + E, Inner->getType(), E->getStorageDuration() == SD_FullExpression ? ScopeKind::FullExpression : ScopeKind::Block, Result); diff --git a/clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp b/clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp index 0a2fcd4ad695a30ccb3ec9b39ff15553eb55c1a4..fa114979c8e3263dd86e04d04dcb6b12ee09758f 100644 --- a/clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp +++ b/clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp @@ -68,11 +68,38 @@ StorageLocation &DataflowAnalysisContext::createStorageLocation(QualType Type) { else FieldLocs.insert({Field, &createStorageLocation( Field->getType().getNonReferenceType())}); - return arena().create(Type, std::move(FieldLocs)); + + RecordStorageLocation::SyntheticFieldMap SyntheticFields; + for (const auto &Entry : getSyntheticFields(Type)) + SyntheticFields.insert( + {Entry.getKey(), + &createStorageLocation(Entry.getValue().getNonReferenceType())}); + + return createRecordStorageLocation(Type, std::move(FieldLocs), + std::move(SyntheticFields)); } return arena().create(Type); } +// Returns the keys for a given `StringMap`. +// Can't use `StringSet` as the return type as it doesn't support `operator==`. +template +static llvm::DenseSet getKeys(const llvm::StringMap &Map) { + return llvm::DenseSet(Map.keys().begin(), Map.keys().end()); +} + +RecordStorageLocation &DataflowAnalysisContext::createRecordStorageLocation( + QualType Type, RecordStorageLocation::FieldToLoc FieldLocs, + RecordStorageLocation::SyntheticFieldMap SyntheticFields) { + assert(Type->isRecordType()); + assert(containsSameFields(getModeledFields(Type), FieldLocs)); + assert(getKeys(getSyntheticFields(Type)) == getKeys(SyntheticFields)); + + RecordStorageLocationCreated = true; + return arena().create(Type, std::move(FieldLocs), + std::move(SyntheticFields)); +} + StorageLocation & DataflowAnalysisContext::getStableStorageLocation(const ValueDecl &D) { if (auto *Loc = DeclToLoc.lookup(&D)) @@ -367,3 +394,14 @@ clang::dataflow::FieldSet clang::dataflow::getObjectFields(QualType Type) { getFieldsFromClassHierarchy(Type, Fields); return Fields; } + +bool clang::dataflow::containsSameFields( + const clang::dataflow::FieldSet &Fields, + const clang::dataflow::RecordStorageLocation::FieldToLoc &FieldLocs) { + if (Fields.size() != FieldLocs.size()) + return false; + for ([[maybe_unused]] auto [Field, Loc] : FieldLocs) + if (!Fields.contains(cast_or_null(Field))) + return false; + return true; +} diff --git a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp index 525ab188b01b8aa43bdc4c843e514d612d0c41fd..b98037b73645225ab3cf9a35d648181ef275854f 100644 --- a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp +++ b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp @@ -300,9 +300,12 @@ static void insertIfFunction(const Decl &D, } static MemberExpr *getMemberForAccessor(const CXXMemberCallExpr &C) { - if (!C.getMethodDecl()) + // Use getCalleeDecl instead of getMethodDecl in order to handle + // pointer-to-member calls. + const auto *MethodDecl = dyn_cast_or_null(C.getCalleeDecl()); + if (!MethodDecl) return nullptr; - auto *Body = dyn_cast_or_null(C.getMethodDecl()->getBody()); + auto *Body = dyn_cast_or_null(MethodDecl->getBody()); if (!Body || Body->size() != 1) return nullptr; if (auto *RS = dyn_cast(*Body->body_begin())) @@ -367,6 +370,59 @@ getFieldsGlobalsAndFuncs(const Stmt &S, FieldSet &Fields, } } +Environment::Environment(DataflowAnalysisContext &DACtx) + : DACtx(&DACtx), + FlowConditionToken(DACtx.arena().makeFlowConditionToken()) {} + +Environment::Environment(DataflowAnalysisContext &DACtx, + const DeclContext &DeclCtx) + : Environment(DACtx) { + CallStack.push_back(&DeclCtx); +} + +void Environment::initialize() { + const DeclContext *DeclCtx = getDeclCtx(); + if (DeclCtx == nullptr) + return; + + if (const auto *FuncDecl = dyn_cast(DeclCtx)) { + assert(FuncDecl->getBody() != nullptr); + + initFieldsGlobalsAndFuncs(FuncDecl); + + for (const auto *ParamDecl : FuncDecl->parameters()) { + assert(ParamDecl != nullptr); + setStorageLocation(*ParamDecl, createObject(*ParamDecl, nullptr)); + } + } + + if (const auto *MethodDecl = dyn_cast(DeclCtx)) { + auto *Parent = MethodDecl->getParent(); + assert(Parent != nullptr); + + if (Parent->isLambda()) { + for (auto Capture : Parent->captures()) { + if (Capture.capturesVariable()) { + const auto *VarDecl = Capture.getCapturedVar(); + assert(VarDecl != nullptr); + setStorageLocation(*VarDecl, createObject(*VarDecl, nullptr)); + } else if (Capture.capturesThis()) { + const auto *SurroundingMethodDecl = + cast(DeclCtx->getNonClosureAncestor()); + QualType ThisPointeeType = + SurroundingMethodDecl->getFunctionObjectParameterType(); + setThisPointeeStorageLocation( + cast(createValue(ThisPointeeType))->getLoc()); + } + } + } else if (MethodDecl->isImplicitObjectMemberFunction()) { + QualType ThisPointeeType = MethodDecl->getFunctionObjectParameterType(); + setThisPointeeStorageLocation( + cast(createValue(ThisPointeeType))->getLoc()); + } + } +} + // FIXME: Add support for resetting globals after function calls to enable // the implementation of sound analyses. void Environment::initFieldsGlobalsAndFuncs(const FunctionDecl *FuncDecl) { @@ -416,59 +472,12 @@ void Environment::initFieldsGlobalsAndFuncs(const FunctionDecl *FuncDecl) { } } -Environment::Environment(DataflowAnalysisContext &DACtx) - : DACtx(&DACtx), - FlowConditionToken(DACtx.arena().makeFlowConditionToken()) {} - Environment Environment::fork() const { Environment Copy(*this); Copy.FlowConditionToken = DACtx->forkFlowCondition(FlowConditionToken); return Copy; } -Environment::Environment(DataflowAnalysisContext &DACtx, - const DeclContext &DeclCtx) - : Environment(DACtx) { - CallStack.push_back(&DeclCtx); - - if (const auto *FuncDecl = dyn_cast(&DeclCtx)) { - assert(FuncDecl->getBody() != nullptr); - - initFieldsGlobalsAndFuncs(FuncDecl); - - for (const auto *ParamDecl : FuncDecl->parameters()) { - assert(ParamDecl != nullptr); - setStorageLocation(*ParamDecl, createObject(*ParamDecl, nullptr)); - } - } - - if (const auto *MethodDecl = dyn_cast(&DeclCtx)) { - auto *Parent = MethodDecl->getParent(); - assert(Parent != nullptr); - - if (Parent->isLambda()) { - for (auto Capture : Parent->captures()) { - if (Capture.capturesVariable()) { - const auto *VarDecl = Capture.getCapturedVar(); - assert(VarDecl != nullptr); - setStorageLocation(*VarDecl, createObject(*VarDecl, nullptr)); - } else if (Capture.capturesThis()) { - const auto *SurroundingMethodDecl = - cast(DeclCtx.getNonClosureAncestor()); - QualType ThisPointeeType = - SurroundingMethodDecl->getFunctionObjectParameterType(); - ThisPointeeLoc = - &cast(createValue(ThisPointeeType))->getLoc(); - } - } - } else if (MethodDecl->isImplicitObjectMemberFunction()) { - QualType ThisPointeeType = MethodDecl->getFunctionObjectParameterType(); - ThisPointeeLoc = - &cast(createValue(ThisPointeeType))->getLoc(); - } - } -} - bool Environment::canDescend(unsigned MaxDepth, const DeclContext *Callee) const { return CallStack.size() <= MaxDepth && !llvm::is_contained(CallStack, Callee); @@ -727,10 +736,6 @@ StorageLocation *Environment::getStorageLocation(const Expr &E) const { return getStorageLocationInternal(E); } -RecordStorageLocation *Environment::getThisPointeeStorageLocation() const { - return ThisPointeeLoc; -} - RecordStorageLocation & Environment::getResultObjectLocation(const Expr &RecordPRValue) { assert(RecordPRValue.getType()->isRecordType()); @@ -852,8 +857,16 @@ Value *Environment::createValueUnlessSelfReferential( CreatedValuesCount)}); } - RecordStorageLocation &Loc = - arena().create(Type, std::move(FieldLocs)); + RecordStorageLocation::SyntheticFieldMap SyntheticFieldLocs; + for (const auto &Entry : DACtx->getSyntheticFields(Type)) { + SyntheticFieldLocs.insert( + {Entry.getKey(), + &createLocAndMaybeValue(Entry.getValue(), Visited, Depth + 1, + CreatedValuesCount)}); + } + + RecordStorageLocation &Loc = DACtx->createRecordStorageLocation( + Type, std::move(FieldLocs), std::move(SyntheticFieldLocs)); RecordValue &RecordVal = create(Loc); // As we already have a storage location for the `RecordValue`, we can and diff --git a/clang/lib/Analysis/FlowSensitive/HTMLLogger.cpp b/clang/lib/Analysis/FlowSensitive/HTMLLogger.cpp index 8329367098b1dbb6ea2544f10c69dea9ad7335a5..7430ef599e032d0c897461a777903d099948932f 100644 --- a/clang/lib/Analysis/FlowSensitive/HTMLLogger.cpp +++ b/clang/lib/Analysis/FlowSensitive/HTMLLogger.cpp @@ -136,6 +136,10 @@ public: if (Value *Val = Env.getValue(*Child.second)) dump(*Val); }); + + for (const auto &SyntheticField : RLoc->synthetic_fields()) + JOS.attributeObject(("sf:" + SyntheticField.first()).str(), + [&] { dump(*SyntheticField.second); }); } } diff --git a/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp b/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp index 55d0713639d90da267f938067cb8391b3162019c..69ac2c2b82cff2407c97e30ce007a7e313f2d033 100644 --- a/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp +++ b/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp @@ -122,12 +122,6 @@ auto nulloptTypeDecl() { auto hasNulloptType() { return hasType(nulloptTypeDecl()); } -// `optional` or `nullopt_t` -auto hasAnyOptionalType() { - return hasType(hasUnqualifiedDesugaredType( - recordType(hasDeclaration(anyOf(nulloptTypeDecl(), optionalClass()))))); -} - auto inPlaceClass() { return recordDecl(hasAnyName("std::in_place_t", "absl::in_place_t", "base::in_place_t", "folly::in_place_t")); @@ -162,11 +156,6 @@ auto isOptionalValueOrConversionAssignment() { argumentCountIs(2), hasArgument(1, unless(hasNulloptType()))); } -auto isNulloptConstructor() { - return cxxConstructExpr(hasNulloptType(), argumentCountIs(1), - hasArgument(0, hasNulloptType())); -} - auto isOptionalNulloptAssignment() { return cxxOperatorCallExpr(hasOverloadedOperatorName("="), callee(cxxMethodDecl(ofClass(optionalClass()))), @@ -246,10 +235,19 @@ const Formula &forceBoolValue(Environment &Env, const Expr &Expr) { return Value->formula(); } +StorageLocation &locForHasValue(const RecordStorageLocation &OptionalLoc) { + return OptionalLoc.getSyntheticField("has_value"); +} + +StorageLocation &locForValue(const RecordStorageLocation &OptionalLoc) { + return OptionalLoc.getSyntheticField("value"); +} + /// Sets `HasValueVal` as the symbolic value that represents the "has_value" -/// property of the optional value `OptionalVal`. -void setHasValue(Value &OptionalVal, BoolValue &HasValueVal) { - OptionalVal.setProperty("has_value", HasValueVal); +/// property of the optional at `OptionalLoc`. +void setHasValue(RecordStorageLocation &OptionalLoc, BoolValue &HasValueVal, + Environment &Env) { + Env.setValue(locForHasValue(OptionalLoc), HasValueVal); } /// Creates a symbolic value for an `optional` value at an existing storage @@ -259,23 +257,22 @@ RecordValue &createOptionalValue(RecordStorageLocation &Loc, BoolValue &HasValueVal, Environment &Env) { auto &OptionalVal = Env.create(Loc); Env.setValue(Loc, OptionalVal); - setHasValue(OptionalVal, HasValueVal); + setHasValue(Loc, HasValueVal, Env); return OptionalVal; } /// Returns the symbolic value that represents the "has_value" property of the -/// optional value `OptionalVal`. Returns null if `OptionalVal` is null. -BoolValue *getHasValue(Environment &Env, Value *OptionalVal) { - if (OptionalVal != nullptr) { - auto *HasValueVal = - cast_or_null(OptionalVal->getProperty("has_value")); - if (HasValueVal == nullptr) { - HasValueVal = &Env.makeAtomicBoolValue(); - OptionalVal->setProperty("has_value", *HasValueVal); - } - return HasValueVal; +/// optional at `OptionalLoc`. Returns null if `OptionalLoc` is null. +BoolValue *getHasValue(Environment &Env, RecordStorageLocation *OptionalLoc) { + if (OptionalLoc == nullptr) + return nullptr; + StorageLocation &HasValueLoc = locForHasValue(*OptionalLoc); + auto *HasValueVal = cast_or_null(Env.getValue(HasValueLoc)); + if (HasValueVal == nullptr) { + HasValueVal = &Env.makeAtomicBoolValue(); + Env.setValue(HasValueLoc, *HasValueVal); } - return nullptr; + return HasValueVal; } /// Returns true if and only if `Type` is an optional type. @@ -302,155 +299,31 @@ int countOptionalWrappers(const ASTContext &ASTCtx, QualType Type) { .getDesugaredType(ASTCtx)); } -/// Tries to initialize the `optional`'s value (that is, contents), and return -/// its location. Returns nullptr if the value can't be represented. -StorageLocation *maybeInitializeOptionalValueMember(QualType Q, - Value &OptionalVal, - Environment &Env) { - // The "value" property represents a synthetic field. As such, it needs - // `StorageLocation`, like normal fields (and other variables). So, we model - // it with a `PointerValue`, since that includes a storage location. Once - // the property is set, it will be shared by all environments that access the - // `Value` representing the optional (here, `OptionalVal`). - if (auto *ValueProp = OptionalVal.getProperty("value")) { - auto *ValuePtr = clang::cast(ValueProp); - auto &ValueLoc = ValuePtr->getPointeeLoc(); - if (Env.getValue(ValueLoc) != nullptr) - return &ValueLoc; - - // The property was previously set, but the value has been lost. This can - // happen in various situations, for example: - // - Because of an environment merge (where the two environments mapped the - // property to different values, which resulted in them both being - // discarded). - // - When two blocks in the CFG, with neither a dominator of the other, - // visit the same optional value. (FIXME: This is something we can and - // should fix -- see also the lengthy FIXME below.) - // - Or even when a block is revisited during testing to collect - // per-statement state. - // FIXME: This situation means that the optional contents are not shared - // between branches and the like. Practically, this lack of sharing - // reduces the precision of the model when the contents are relevant to - // the check, like another optional or a boolean that influences control - // flow. - if (ValueLoc.getType()->isRecordType()) { - refreshRecordValue(cast(ValueLoc), Env); - return &ValueLoc; - } else { - auto *ValueVal = Env.createValue(ValueLoc.getType()); - if (ValueVal == nullptr) - return nullptr; - Env.setValue(ValueLoc, *ValueVal); - return &ValueLoc; - } - } - - auto Ty = Q.getNonReferenceType(); - auto &ValueLoc = Env.createObject(Ty); - auto &ValuePtr = Env.create(ValueLoc); - // FIXME: - // The change we make to the `value` property below may become visible to - // other blocks that aren't successors of the current block and therefore - // don't see the change we made above mapping `ValueLoc` to `ValueVal`. For - // example: - // - // void target(optional oo, bool b) { - // // `oo` is associated with a `RecordValue` here, which we will call - // // `OptionalVal`. - // - // // The `has_value` property is set on `OptionalVal` (but not the - // // `value` property yet). - // if (!oo.has_value()) return; - // - // if (b) { - // // Let's assume we transfer the `if` branch first. - // // - // // This causes us to call `maybeInitializeOptionalValueMember()`, - // // which causes us to set the `value` property on `OptionalVal` - // // (which had not been set until this point). This `value` property - // // refers to a `PointerValue`, which in turn refers to a - // // StorageLocation` that is associated to an `IntegerValue`. - // oo.value(); - // } else { - // // Let's assume we transfer the `else` branch after the `if` branch. - // // - // // We see the `value` property that the `if` branch set on - // // `OptionalVal`, but in the environment for this block, the - // // `StorageLocation` in the `PointerValue` is not associated with any - // // `Value`. - // oo.value(); - // } - // } - // - // This situation is currently "saved" by the code above that checks whether - // the `value` property is already set, and if, the `ValueLoc` is not - // associated with a `ValueVal`, creates a new `ValueVal`. - // - // However, what we should really do is to make sure that the change to the - // `value` property does not "leak" to other blocks that are not successors - // of this block. To do this, instead of simply setting the `value` property - // on the existing `OptionalVal`, we should create a new `Value` for the - // optional, set the property on that, and associate the storage location that - // is currently associated with the existing `OptionalVal` with the newly - // created `Value` instead. - OptionalVal.setProperty("value", ValuePtr); - return &ValueLoc; -} - -void initializeOptionalReference(const Expr *OptionalExpr, - const MatchFinder::MatchResult &, - LatticeTransferState &State) { - if (auto *OptionalVal = State.Env.getValue(*OptionalExpr)) { - if (OptionalVal->getProperty("has_value") == nullptr) { - setHasValue(*OptionalVal, State.Env.makeAtomicBoolValue()); - } +StorageLocation *getLocBehindPossiblePointer(const Expr &E, + const Environment &Env) { + if (E.isPRValue()) { + if (auto *PointerVal = dyn_cast_or_null(Env.getValue(E))) + return &PointerVal->getPointeeLoc(); + return nullptr; } -} - -/// Returns true if and only if `OptionalVal` is initialized and known to be -/// empty in `Env`. -bool isEmptyOptional(const Value &OptionalVal, const Environment &Env) { - auto *HasValueVal = - cast_or_null(OptionalVal.getProperty("has_value")); - return HasValueVal != nullptr && - Env.proves(Env.arena().makeNot(HasValueVal->formula())); -} - -/// Returns true if and only if `OptionalVal` is initialized and known to be -/// non-empty in `Env`. -bool isNonEmptyOptional(const Value &OptionalVal, const Environment &Env) { - auto *HasValueVal = - cast_or_null(OptionalVal.getProperty("has_value")); - return HasValueVal != nullptr && Env.proves(HasValueVal->formula()); -} - -Value *getValueBehindPossiblePointer(const Expr &E, const Environment &Env) { - Value *Val = Env.getValue(E); - if (auto *PointerVal = dyn_cast_or_null(Val)) - return Env.getValue(PointerVal->getPointeeLoc()); - return Val; + return Env.getStorageLocation(E); } void transferUnwrapCall(const Expr *UnwrapExpr, const Expr *ObjectExpr, LatticeTransferState &State) { - if (auto *OptionalVal = - getValueBehindPossiblePointer(*ObjectExpr, State.Env)) { + if (auto *OptionalLoc = cast_or_null( + getLocBehindPossiblePointer(*ObjectExpr, State.Env))) { if (State.Env.getStorageLocation(*UnwrapExpr) == nullptr) - if (auto *Loc = maybeInitializeOptionalValueMember( - UnwrapExpr->getType(), *OptionalVal, State.Env)) - State.Env.setStorageLocation(*UnwrapExpr, *Loc); + State.Env.setStorageLocation(*UnwrapExpr, locForValue(*OptionalLoc)); } } void transferArrowOpCall(const Expr *UnwrapExpr, const Expr *ObjectExpr, LatticeTransferState &State) { - if (auto *OptionalVal = - getValueBehindPossiblePointer(*ObjectExpr, State.Env)) { - if (auto *Loc = maybeInitializeOptionalValueMember( - UnwrapExpr->getType()->getPointeeType(), *OptionalVal, State.Env)) { - State.Env.setValue(*UnwrapExpr, State.Env.create(*Loc)); - } - } + if (auto *OptionalLoc = cast_or_null( + getLocBehindPossiblePointer(*ObjectExpr, State.Env))) + State.Env.setValue( + *UnwrapExpr, State.Env.create(locForValue(*OptionalLoc))); } void transferMakeOptionalCall(const CallExpr *E, @@ -465,8 +338,7 @@ void transferOptionalHasValueCall(const CXXMemberCallExpr *CallExpr, const MatchFinder::MatchResult &, LatticeTransferState &State) { if (auto *HasValueVal = getHasValue( - State.Env, getValueBehindPossiblePointer( - *CallExpr->getImplicitObjectArgument(), State.Env))) { + State.Env, getImplicitObjectLocation(*CallExpr, State.Env))) { State.Env.setValue(*CallExpr, *HasValueVal); } } @@ -480,12 +352,11 @@ void transferValueOrImpl( const Formula &HasValueVal)) { auto &Env = State.Env; - const auto *ObjectArgumentExpr = - Result.Nodes.getNodeAs(ValueOrCallID) - ->getImplicitObjectArgument(); + const auto *MCE = + Result.Nodes.getNodeAs(ValueOrCallID); - auto *HasValueVal = getHasValue( - State.Env, getValueBehindPossiblePointer(*ObjectArgumentExpr, State.Env)); + auto *HasValueVal = + getHasValue(State.Env, getImplicitObjectLocation(*MCE, State.Env)); if (HasValueVal == nullptr) return; @@ -578,7 +449,9 @@ BoolValue &valueOrConversionHasValue(const FunctionDecl &F, const Expr &E, // This is a constructor/assignment call for `optional` with argument of // type `optional` such that `T` is constructible from `U`. - if (auto *HasValueVal = getHasValue(State.Env, State.Env.getValue(E))) + auto *Loc = + cast_or_null(State.Env.getStorageLocation(E)); + if (auto *HasValueVal = getHasValue(State.Env, Loc)) return *HasValueVal; return State.Env.makeAtomicBoolValue(); } @@ -645,11 +518,11 @@ void transferSwap(RecordStorageLocation *Loc1, RecordStorageLocation *Loc2, // allows for local reasoning about the value. To avoid the above, we would // need *lazy* value allocation. // FIXME: allocate values lazily, instead of just creating a fresh value. - BoolValue *BoolVal1 = getHasValue(Env, Env.getValue(*Loc1)); + BoolValue *BoolVal1 = getHasValue(Env, Loc1); if (BoolVal1 == nullptr) BoolVal1 = &Env.makeAtomicBoolValue(); - BoolValue *BoolVal2 = getHasValue(Env, Env.getValue(*Loc2)); + BoolValue *BoolVal2 = getHasValue(Env, Loc2); if (BoolVal2 == nullptr) BoolVal2 = &Env.makeAtomicBoolValue(); @@ -712,20 +585,26 @@ void transferOptionalAndOptionalCmp(const clang::CXXOperatorCallExpr *CmpExpr, Environment &Env = State.Env; auto &A = Env.arena(); auto *CmpValue = &forceBoolValue(Env, *CmpExpr); - if (auto *LHasVal = getHasValue(Env, Env.getValue(*CmpExpr->getArg(0)))) - if (auto *RHasVal = getHasValue(Env, Env.getValue(*CmpExpr->getArg(1)))) { + auto *Arg0Loc = cast_or_null( + Env.getStorageLocation(*CmpExpr->getArg(0))); + if (auto *LHasVal = getHasValue(Env, Arg0Loc)) { + auto *Arg1Loc = cast_or_null( + Env.getStorageLocation(*CmpExpr->getArg(1))); + if (auto *RHasVal = getHasValue(Env, Arg1Loc)) { if (CmpExpr->getOperator() == clang::OO_ExclaimEqual) CmpValue = &A.makeNot(*CmpValue); Env.assume(evaluateEquality(A, *CmpValue, LHasVal->formula(), RHasVal->formula())); } + } } void transferOptionalAndValueCmp(const clang::CXXOperatorCallExpr *CmpExpr, const clang::Expr *E, Environment &Env) { auto &A = Env.arena(); auto *CmpValue = &forceBoolValue(Env, *CmpExpr); - if (auto *HasVal = getHasValue(Env, Env.getValue(*E))) { + auto *Loc = cast_or_null(Env.getStorageLocation(*E)); + if (auto *HasVal = getHasValue(Env, Loc)) { if (CmpExpr->getOperator() == clang::OO_ExclaimEqual) CmpValue = &A.makeNot(*CmpValue); Env.assume( @@ -733,6 +612,19 @@ void transferOptionalAndValueCmp(const clang::CXXOperatorCallExpr *CmpExpr, } } +void transferOptionalAndNulloptCmp(const clang::CXXOperatorCallExpr *CmpExpr, + const clang::Expr *E, Environment &Env) { + auto &A = Env.arena(); + auto *CmpValue = &forceBoolValue(Env, *CmpExpr); + auto *Loc = cast_or_null(Env.getStorageLocation(*E)); + if (auto *HasVal = getHasValue(Env, Loc)) { + if (CmpExpr->getOperator() == clang::OO_ExclaimEqual) + CmpValue = &A.makeNot(*CmpValue); + Env.assume(evaluateEquality(A, *CmpValue, HasVal->formula(), + A.makeLiteral(false))); + } +} + std::optional ignorableOptional(const UncheckedOptionalAccessModelOptions &Options) { if (Options.IgnoreSmartPointerDereference) { @@ -762,12 +654,6 @@ auto buildTransferMatchSwitch() { // lot of duplicated work (e.g. string comparisons), consider providing APIs // that avoid it through memoization. return CFGMatchSwitchBuilder() - // Attach a symbolic "has_value" state to optional values that we see for - // the first time. - .CaseOfCFGStmt( - expr(anyOf(declRefExpr(), memberExpr()), hasOptionalType()), - initializeOptionalReference) - // make_optional .CaseOfCFGStmt(isMakeOptionalCall(), transferMakeOptionalCall) @@ -779,14 +665,6 @@ auto buildTransferMatchSwitch() { constructOptionalValue(*E, State.Env, State.Env.getBoolLiteralValue(true)); }) - // nullopt_t::nullopt_t - .CaseOfCFGStmt( - isNulloptConstructor(), - [](const CXXConstructExpr *E, const MatchFinder::MatchResult &, - LatticeTransferState &State) { - constructOptionalValue(*E, State.Env, - State.Env.getBoolLiteralValue(false)); - }) // optional::optional(nullopt_t) .CaseOfCFGStmt( isOptionalNulloptConstructor(), @@ -887,18 +765,32 @@ auto buildTransferMatchSwitch() { // Comparisons (==, !=): .CaseOfCFGStmt( - isComparisonOperatorCall(hasAnyOptionalType(), hasAnyOptionalType()), + isComparisonOperatorCall(hasOptionalType(), hasOptionalType()), transferOptionalAndOptionalCmp) .CaseOfCFGStmt( - isComparisonOperatorCall(hasOptionalType(), - unless(hasAnyOptionalType())), + isComparisonOperatorCall(hasOptionalType(), hasNulloptType()), + [](const clang::CXXOperatorCallExpr *Cmp, + const MatchFinder::MatchResult &, LatticeTransferState &State) { + transferOptionalAndNulloptCmp(Cmp, Cmp->getArg(0), State.Env); + }) + .CaseOfCFGStmt( + isComparisonOperatorCall(hasNulloptType(), hasOptionalType()), + [](const clang::CXXOperatorCallExpr *Cmp, + const MatchFinder::MatchResult &, LatticeTransferState &State) { + transferOptionalAndNulloptCmp(Cmp, Cmp->getArg(1), State.Env); + }) + .CaseOfCFGStmt( + isComparisonOperatorCall( + hasOptionalType(), + unless(anyOf(hasOptionalType(), hasNulloptType()))), [](const clang::CXXOperatorCallExpr *Cmp, const MatchFinder::MatchResult &, LatticeTransferState &State) { transferOptionalAndValueCmp(Cmp, Cmp->getArg(0), State.Env); }) .CaseOfCFGStmt( - isComparisonOperatorCall(unless(hasAnyOptionalType()), - hasOptionalType()), + isComparisonOperatorCall( + unless(anyOf(hasOptionalType(), hasNulloptType())), + hasOptionalType()), [](const clang::CXXOperatorCallExpr *Cmp, const MatchFinder::MatchResult &, LatticeTransferState &State) { transferOptionalAndValueCmp(Cmp, Cmp->getArg(1), State.Env); @@ -913,8 +805,9 @@ auto buildTransferMatchSwitch() { llvm::SmallVector diagnoseUnwrapCall(const Expr *ObjectExpr, const Environment &Env) { - if (auto *OptionalVal = getValueBehindPossiblePointer(*ObjectExpr, Env)) { - auto *Prop = OptionalVal->getProperty("has_value"); + if (auto *OptionalLoc = cast_or_null( + getLocBehindPossiblePointer(*ObjectExpr, Env))) { + auto *Prop = Env.getValue(locForHasValue(*OptionalLoc)); if (auto *HasValueVal = cast_or_null(Prop)) { if (Env.proves(HasValueVal->formula())) return {}; @@ -960,9 +853,24 @@ UncheckedOptionalAccessModel::optionalClassDecl() { return optionalClass(); } -UncheckedOptionalAccessModel::UncheckedOptionalAccessModel(ASTContext &Ctx) +static QualType valueTypeFromOptionalType(QualType OptionalTy) { + auto *CTSD = + cast(OptionalTy->getAsCXXRecordDecl()); + return CTSD->getTemplateArgs()[0].getAsType(); +} + +UncheckedOptionalAccessModel::UncheckedOptionalAccessModel(ASTContext &Ctx, + Environment &Env) : DataflowAnalysis(Ctx), - TransferMatchSwitch(buildTransferMatchSwitch()) {} + TransferMatchSwitch(buildTransferMatchSwitch()) { + Env.getDataflowAnalysisContext().setSyntheticFieldCallback( + [&Ctx](QualType Ty) -> llvm::StringMap { + if (!isOptionalType(Ty)) + return {}; + return {{"value", valueTypeFromOptionalType(Ty)}, + {"has_value", Ctx.BoolTy}}; + }); +} void UncheckedOptionalAccessModel::transfer(const CFGElement &Elt, NoopLattice &L, Environment &Env) { @@ -970,76 +878,6 @@ void UncheckedOptionalAccessModel::transfer(const CFGElement &Elt, TransferMatchSwitch(Elt, getASTContext(), State); } -ComparisonResult UncheckedOptionalAccessModel::compare( - QualType Type, const Value &Val1, const Environment &Env1, - const Value &Val2, const Environment &Env2) { - if (!isOptionalType(Type)) - return ComparisonResult::Unknown; - bool MustNonEmpty1 = isNonEmptyOptional(Val1, Env1); - bool MustNonEmpty2 = isNonEmptyOptional(Val2, Env2); - if (MustNonEmpty1 && MustNonEmpty2) - return ComparisonResult::Same; - // If exactly one is true, then they're different, no reason to check whether - // they're definitely empty. - if (MustNonEmpty1 || MustNonEmpty2) - return ComparisonResult::Different; - // Check if they're both definitely empty. - return (isEmptyOptional(Val1, Env1) && isEmptyOptional(Val2, Env2)) - ? ComparisonResult::Same - : ComparisonResult::Different; -} - -bool UncheckedOptionalAccessModel::merge(QualType Type, const Value &Val1, - const Environment &Env1, - const Value &Val2, - const Environment &Env2, - Value &MergedVal, - Environment &MergedEnv) { - if (!isOptionalType(Type)) - return true; - // FIXME: uses same approach as join for `BoolValues`. Requires non-const - // values, though, so will require updating the interface. - auto &HasValueVal = MergedEnv.makeAtomicBoolValue(); - bool MustNonEmpty1 = isNonEmptyOptional(Val1, Env1); - bool MustNonEmpty2 = isNonEmptyOptional(Val2, Env2); - if (MustNonEmpty1 && MustNonEmpty2) - MergedEnv.assume(HasValueVal.formula()); - else if ( - // Only make the costly calls to `isEmptyOptional` if we got "unknown" - // (false) for both calls to `isNonEmptyOptional`. - !MustNonEmpty1 && !MustNonEmpty2 && isEmptyOptional(Val1, Env1) && - isEmptyOptional(Val2, Env2)) - MergedEnv.assume(MergedEnv.arena().makeNot(HasValueVal.formula())); - setHasValue(MergedVal, HasValueVal); - return true; -} - -Value *UncheckedOptionalAccessModel::widen(QualType Type, Value &Prev, - const Environment &PrevEnv, - Value &Current, - Environment &CurrentEnv) { - switch (compare(Type, Prev, PrevEnv, Current, CurrentEnv)) { - case ComparisonResult::Same: - return &Prev; - case ComparisonResult::Different: - if (auto *PrevHasVal = - cast_or_null(Prev.getProperty("has_value"))) { - if (isa(PrevHasVal)) - return &Prev; - } - if (auto *CurrentHasVal = - cast_or_null(Current.getProperty("has_value"))) { - if (isa(CurrentHasVal)) - return &Current; - } - return &createOptionalValue(cast(Current).getLoc(), - CurrentEnv.makeTopBoolValue(), CurrentEnv); - case ComparisonResult::Unknown: - return nullptr; - } - llvm_unreachable("all cases covered in switch"); -} - UncheckedOptionalAccessDiagnoser::UncheckedOptionalAccessDiagnoser( UncheckedOptionalAccessModelOptions Options) : DiagnoseMatchSwitch(buildDiagnoseMatchSwitch(Options)) {} diff --git a/clang/lib/Analysis/FlowSensitive/RecordOps.cpp b/clang/lib/Analysis/FlowSensitive/RecordOps.cpp index 38638f8a9e5892d62dadbfb9f6b9ded066c3726e..caaf443382b02cf4ef04eeb4bff0ec7168117a04 100644 --- a/clang/lib/Analysis/FlowSensitive/RecordOps.cpp +++ b/clang/lib/Analysis/FlowSensitive/RecordOps.cpp @@ -54,6 +54,18 @@ void clang::dataflow::copyRecord(RecordStorageLocation &Src, } } + for (const auto &[Name, SynthFieldLoc] : Src.synthetic_fields()) { + if (SynthFieldLoc->getType()->isRecordType()) { + copyRecord(*cast(SynthFieldLoc), + cast(Dst.getSyntheticField(Name)), Env); + } else { + if (Value *Val = Env.getValue(*SynthFieldLoc)) + Env.setValue(Dst.getSyntheticField(Name), *Val); + else + Env.clearValue(Dst.getSyntheticField(Name)); + } + } + RecordValue *SrcVal = cast_or_null(Env.getValue(Src)); RecordValue *DstVal = cast_or_null(Env.getValue(Dst)); @@ -101,6 +113,18 @@ bool clang::dataflow::recordsEqual(const RecordStorageLocation &Loc1, } } + for (const auto &[Name, SynthFieldLoc1] : Loc1.synthetic_fields()) { + if (SynthFieldLoc1->getType()->isRecordType()) { + if (!recordsEqual( + *cast(SynthFieldLoc1), Env1, + cast(Loc2.getSyntheticField(Name)), Env2)) + return false; + } else if (Env1.getValue(*SynthFieldLoc1) != + Env2.getValue(Loc2.getSyntheticField(Name))) { + return false; + } + } + llvm::StringMap Props1, Props2; if (RecordValue *Val1 = cast_or_null(Env1.getValue(Loc1))) diff --git a/clang/lib/Analysis/FlowSensitive/Transfer.cpp b/clang/lib/Analysis/FlowSensitive/Transfer.cpp index 4343af790081301cd7e39065714f04462428bf62..bbf5f12359bc70e91ef4c3f2a1780a594cda0603 100644 --- a/clang/lib/Analysis/FlowSensitive/Transfer.cpp +++ b/clang/lib/Analysis/FlowSensitive/Transfer.cpp @@ -703,20 +703,18 @@ public: // `InitListExpr`, all fields in the class, including those from base // classes, are included in the set of modeled fields. The code above // should therefore populate exactly the modeled fields. - assert([&]() { - auto ModeledFields = - Env.getDataflowAnalysisContext().getModeledFields(Type); - if (ModeledFields.size() != FieldLocs.size()) - return false; - for ([[maybe_unused]] auto [Field, Loc] : FieldLocs) - if (!ModeledFields.contains(cast_or_null(Field))) - return false; - return true; - }()); - - auto &Loc = - Env.getDataflowAnalysisContext().arena().create( - Type, std::move(FieldLocs)); + assert(containsSameFields( + Env.getDataflowAnalysisContext().getModeledFields(Type), FieldLocs)); + + RecordStorageLocation::SyntheticFieldMap SyntheticFieldLocs; + for (const auto &Entry : + Env.getDataflowAnalysisContext().getSyntheticFields(Type)) { + SyntheticFieldLocs.insert( + {Entry.getKey(), &Env.createObject(Entry.getValue())}); + } + + auto &Loc = Env.getDataflowAnalysisContext().createRecordStorageLocation( + Type, std::move(FieldLocs), std::move(SyntheticFieldLocs)); RecordValue &RecordVal = Env.create(Loc); Env.setValue(Loc, RecordVal); diff --git a/clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp b/clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp index ade8c84f19366f4b8dd0becbcfafc9f62bc3d4f7..8c9360235da7c1f670507960917293dbf37966f3 100644 --- a/clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp +++ b/clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp @@ -501,6 +501,14 @@ runTypeErasedDataflowAnalysis( PostVisitCFG) { PrettyStackTraceAnalysis CrashInfo(CFCtx, "runTypeErasedDataflowAnalysis"); + std::optional MaybeStartingEnv; + if (InitEnv.callStackSize() == 1) { + MaybeStartingEnv = InitEnv.fork(); + MaybeStartingEnv->initialize(); + } + const Environment &StartingEnv = + MaybeStartingEnv ? *MaybeStartingEnv : InitEnv; + const clang::CFG &CFG = CFCtx.getCFG(); PostOrderCFGView POV(&CFG); ForwardDataflowWorklist Worklist(CFG, &POV); @@ -511,10 +519,10 @@ runTypeErasedDataflowAnalysis( // The entry basic block doesn't contain statements so it can be skipped. const CFGBlock &Entry = CFG.getEntry(); BlockStates[Entry.getBlockID()] = {Analysis.typeErasedInitialElement(), - InitEnv.fork()}; + StartingEnv.fork()}; Worklist.enqueueSuccessors(&Entry); - AnalysisContext AC(CFCtx, Analysis, InitEnv, BlockStates); + AnalysisContext AC(CFCtx, Analysis, StartingEnv, BlockStates); // Bugs in lattices and transfer functions can prevent the analysis from // converging. To limit the damage (infinite loops) that these bugs can cause, diff --git a/clang/lib/Basic/CodeGenOptions.cpp b/clang/lib/Basic/CodeGenOptions.cpp index 0c609cfa61de18561626e4f0753e0e15cd1c866c..182d0a2fa4d88f212ce607284f3db9b06ef477cb 100644 --- a/clang/lib/Basic/CodeGenOptions.cpp +++ b/clang/lib/Basic/CodeGenOptions.cpp @@ -20,4 +20,39 @@ CodeGenOptions::CodeGenOptions() { memcpy(CoverageVersion, "408*", 4); } +void CodeGenOptions::resetNonModularOptions(StringRef ModuleFormat) { + // First reset all CodeGen options only. The Debug options are handled later. +#define DEBUGOPT(Name, Bits, Default) +#define VALUE_DEBUGOPT(Name, Bits, Default) +#define ENUM_DEBUGOPT(Name, Type, Bits, Default) +#define CODEGENOPT(Name, Bits, Default) Name = Default; +#define ENUM_CODEGENOPT(Name, Type, Bits, Default) set##Name(Default); +#include "clang/Basic/CodeGenOptions.def" + + // Next reset all debug options that can always be reset, because they never + // affect the PCM. +#define DEBUGOPT(Name, Bits, Default) +#define VALUE_DEBUGOPT(Name, Bits, Default) +#define ENUM_DEBUGOPT(Name, Type, Bits, Default) +#define BENIGN_DEBUGOPT(Name, Bits, Default) Name = Default; +#define BENIGN_VALUE_DEBUGOPT(Name, Bits, Default) Name = Default; +#define BENIGN_ENUM_DEBUGOPT(Name, Type, Bits, Default) set##Name(Default); +#include "clang/Basic/DebugOptions.def" + + // Conditionally reset debug options that only matter when the debug info is + // emitted into the PCM (-gmodules). + if (ModuleFormat == "raw" && !DebugTypeExtRefs) { +#define DEBUGOPT(Name, Bits, Default) Name = Default; +#define VALUE_DEBUGOPT(Name, Bits, Default) Name = Default; +#define ENUM_DEBUGOPT(Name, Type, Bits, Default) set##Name(Default); +#define BENIGN_DEBUGOPT(Name, Bits, Default) +#define BENIGN_VALUE_DEBUGOPT(Name, Bits, Default) +#define BENIGN_ENUM_DEBUGOPT(Name, Type, Bits, Default) +#include "clang/Basic/DebugOptions.def" + } + + RelocationModel = llvm::Reloc::PIC_; + memcpy(CoverageVersion, "408*", 4); +} + } // end namespace clang diff --git a/clang/lib/Basic/Targets/RISCV.cpp b/clang/lib/Basic/Targets/RISCV.cpp index 2e9ce468b5d3e36b2d080a0823a9e9ba62c13c0f..13f934e9947212c67535d7fd4cb0e8434e7611e3 100644 --- a/clang/lib/Basic/Targets/RISCV.cpp +++ b/clang/lib/Basic/Targets/RISCV.cpp @@ -376,7 +376,7 @@ bool RISCVTargetInfo::handleTargetFeatures(std::vector &Features, if (ISAInfo->hasExtension("zfh") || ISAInfo->hasExtension("zhinx")) HasLegalHalfType = true; - FastUnalignedAccess = llvm::is_contained(Features, "+unaligned-scalar-mem"); + FastUnalignedAccess = llvm::is_contained(Features, "+fast-unaligned-access"); return true; } diff --git a/clang/lib/Basic/Targets/SystemZ.h b/clang/lib/Basic/Targets/SystemZ.h index 9ba255745cf2cc52676208d062cb8cff31bc801a..e4ec338880f21095693fabe84780ff30ebacdef1 100644 --- a/clang/lib/Basic/Targets/SystemZ.h +++ b/clang/lib/Basic/Targets/SystemZ.h @@ -60,7 +60,7 @@ public: resetDataLayout("E-m:e-i1:8:16-i8:8:16-i64:64-f128:64" "-v128:64-a:8:16-n32:64"); } - MaxAtomicPromoteWidth = MaxAtomicInlineWidth = 64; + MaxAtomicPromoteWidth = MaxAtomicInlineWidth = 128; HasStrictFP = true; } diff --git a/clang/lib/Basic/Targets/X86.cpp b/clang/lib/Basic/Targets/X86.cpp index 85d0697ad63cae399a10e8586023cb8191da1402..b97f88647fa49f26e604ff2b99eafa5716944626 100644 --- a/clang/lib/Basic/Targets/X86.cpp +++ b/clang/lib/Basic/Targets/X86.cpp @@ -428,6 +428,18 @@ bool X86TargetInfo::handleTargetFeatures(std::vector &Features, HasX87 = true; } else if (Feature == "+fullbf16") { HasFullBFloat16 = true; + } else if (Feature == "+egpr") { + HasEGPR = true; + } else if (Feature == "+push2pop2") { + HasPush2Pop2 = true; + } else if (Feature == "+ppx") { + HasPPX = true; + } else if (Feature == "+ndd") { + HasNDD = true; + } else if (Feature == "+ccmp") { + HasCCMP = true; + } else if (Feature == "+cf") { + HasCF = true; } X86SSEEnum Level = llvm::StringSwitch(Feature) @@ -927,6 +939,18 @@ void X86TargetInfo::getTargetDefines(const LangOptions &Opts, Builder.defineMacro("__USERMSR__"); if (HasCRC32) Builder.defineMacro("__CRC32__"); + if (HasEGPR) + Builder.defineMacro("__EGPR__"); + if (HasPush2Pop2) + Builder.defineMacro("__PUSH2POP2__"); + if (HasPPX) + Builder.defineMacro("__PPX__"); + if (HasNDD) + Builder.defineMacro("__NDD__"); + if (HasCCMP) + Builder.defineMacro("__CCMP__"); + if (HasCF) + Builder.defineMacro("__CF__"); // Each case falls through to the previous one here. switch (SSELevel) { @@ -1122,6 +1146,12 @@ bool X86TargetInfo::isValidFeatureName(StringRef Name) const { .Case("xsavec", true) .Case("xsaves", true) .Case("xsaveopt", true) + .Case("egpr", true) + .Case("push2pop2", true) + .Case("ppx", true) + .Case("ndd", true) + .Case("ccmp", true) + .Case("cf", true) .Default(false); } @@ -1238,6 +1268,12 @@ bool X86TargetInfo::hasFeature(StringRef Feature) const { .Case("xsaves", HasXSAVES) .Case("xsaveopt", HasXSAVEOPT) .Case("fullbf16", HasFullBFloat16) + .Case("egpr", HasEGPR) + .Case("push2pop2", HasPush2Pop2) + .Case("ppx", HasPPX) + .Case("ndd", HasNDD) + .Case("ccmp", HasCCMP) + .Case("cf", HasCF) .Default(false); } diff --git a/clang/lib/Basic/Targets/X86.h b/clang/lib/Basic/Targets/X86.h index 99a64501d263ce4b330287a09cbf94c40285123b..0ab1c10833db26201e59e7304a4cd32e5f84b880 100644 --- a/clang/lib/Basic/Targets/X86.h +++ b/clang/lib/Basic/Targets/X86.h @@ -168,6 +168,12 @@ class LLVM_LIBRARY_VISIBILITY X86TargetInfo : public TargetInfo { bool HasUINTR = false; bool HasCRC32 = false; bool HasX87 = false; + bool HasEGPR = false; + bool HasPush2Pop2 = false; + bool HasPPX = false; + bool HasNDD = false; + bool HasCCMP = false; + bool HasCF = false; protected: llvm::X86::CPUKind CPU = llvm::X86::CK_None; diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp index f01a6514f6effb0f102d51ffe66c5a60d043a3a4..8c666e2cb463c6ccfee6259d1339d693893f9a07 100644 --- a/clang/lib/CodeGen/BackendUtil.cpp +++ b/clang/lib/CodeGen/BackendUtil.cpp @@ -996,9 +996,8 @@ void EmitAssemblyHelper::RunOptimizationPipeline( } if (CodeGenOpts.FatLTO) { - MPM.addPass(PB.buildFatLTODefaultPipeline( - Level, PrepareForThinLTO, - PrepareForThinLTO || shouldEmitRegularLTOSummary())); + assert(CodeGenOpts.UnifiedLTO && "FatLTO requires UnifiedLTO"); + MPM.addPass(PB.buildFatLTODefaultPipeline(Level)); } else if (PrepareForThinLTO) { MPM.addPass(PB.buildThinLTOPreLinkDefaultPipeline(Level)); } else if (PrepareForLTO) { @@ -1042,7 +1041,6 @@ void EmitAssemblyHelper::RunOptimizationPipeline( MPM.addPass(PrintModulePass(*OS, "", CodeGenOpts.EmitLLVMUseLists, /*EmitLTOSummary=*/true)); } - } else { // Emit a module summary by default for Regular LTO except for ld64 // targets @@ -1073,7 +1071,8 @@ void EmitAssemblyHelper::RunOptimizationPipeline( if (!TheModule->getModuleFlag("EnableSplitLTOUnit")) TheModule->addModuleFlag(llvm::Module::Error, "EnableSplitLTOUnit", uint32_t(CodeGenOpts.EnableSplitLTOUnit)); - if (CodeGenOpts.UnifiedLTO && !TheModule->getModuleFlag("UnifiedLTO")) + // FatLTO always means UnifiedLTO + if (!TheModule->getModuleFlag("UnifiedLTO")) TheModule->addModuleFlag(llvm::Module::Error, "UnifiedLTO", uint32_t(1)); } diff --git a/clang/lib/CodeGen/CGAtomic.cpp b/clang/lib/CodeGen/CGAtomic.cpp index 6005d5c51c0e1ac9be09d8dc3d80c8deaf2503dd..379c833af32a2de87e57b5057cafe8026d59220a 100644 --- a/clang/lib/CodeGen/CGAtomic.cpp +++ b/clang/lib/CodeGen/CGAtomic.cpp @@ -383,8 +383,7 @@ static void emitAtomicCmpXchg(CodeGenFunction &CGF, AtomicExpr *E, bool IsWeak, llvm::Value *Desired = CGF.Builder.CreateLoad(Val2); llvm::AtomicCmpXchgInst *Pair = CGF.Builder.CreateAtomicCmpXchg( - Ptr.getPointer(), Expected, Desired, SuccessOrder, FailureOrder, - Scope); + Ptr, Expected, Desired, SuccessOrder, FailureOrder, Scope); Pair->setVolatile(E->isVolatile()); Pair->setWeak(IsWeak); @@ -699,7 +698,7 @@ static void EmitAtomicOp(CodeGenFunction &CGF, AtomicExpr *E, Address Dest, llvm::Value *LoadVal1 = CGF.Builder.CreateLoad(Val1); llvm::AtomicRMWInst *RMWI = - CGF.Builder.CreateAtomicRMW(Op, Ptr.getPointer(), LoadVal1, Order, Scope); + CGF.Builder.CreateAtomicRMW(Op, Ptr, LoadVal1, Order, Scope); RMWI->setVolatile(E->isVolatile()); // For __atomic_*_fetch operations, perform the operation again to @@ -1740,8 +1739,7 @@ std::pair AtomicInfo::EmitAtomicCompareExchangeOp( llvm::AtomicOrdering Success, llvm::AtomicOrdering Failure, bool IsWeak) { // Do the atomic store. Address Addr = getAtomicAddressAsAtomicIntPointer(); - auto *Inst = CGF.Builder.CreateAtomicCmpXchg(Addr.getPointer(), - ExpectedVal, DesiredVal, + auto *Inst = CGF.Builder.CreateAtomicCmpXchg(Addr, ExpectedVal, DesiredVal, Success, Failure); // Other decoration. Inst->setVolatile(LVal.isVolatileQualified()); diff --git a/clang/lib/CodeGen/CGBuilder.h b/clang/lib/CodeGen/CGBuilder.h index 68535920088c49b0018de7d72d3e0cc6d6bdb59c..bf5ab171d720d9b148544ac6467652a5efea0edb 100644 --- a/clang/lib/CodeGen/CGBuilder.h +++ b/clang/lib/CodeGen/CGBuilder.h @@ -126,25 +126,22 @@ public: return CreateAlignedStore(getInt1(Value), Addr, CharUnits::One()); } - // Temporarily use old signature; clang will be updated to an Address overload - // in a subsequent patch. llvm::AtomicCmpXchgInst * - CreateAtomicCmpXchg(llvm::Value *Ptr, llvm::Value *Cmp, llvm::Value *New, + CreateAtomicCmpXchg(Address Addr, llvm::Value *Cmp, llvm::Value *New, llvm::AtomicOrdering SuccessOrdering, llvm::AtomicOrdering FailureOrdering, llvm::SyncScope::ID SSID = llvm::SyncScope::System) { return CGBuilderBaseTy::CreateAtomicCmpXchg( - Ptr, Cmp, New, llvm::MaybeAlign(), SuccessOrdering, FailureOrdering, - SSID); + Addr.getPointer(), Cmp, New, Addr.getAlignment().getAsAlign(), + SuccessOrdering, FailureOrdering, SSID); } - // Temporarily use old signature; clang will be updated to an Address overload - // in a subsequent patch. llvm::AtomicRMWInst * - CreateAtomicRMW(llvm::AtomicRMWInst::BinOp Op, llvm::Value *Ptr, - llvm::Value *Val, llvm::AtomicOrdering Ordering, + CreateAtomicRMW(llvm::AtomicRMWInst::BinOp Op, Address Addr, llvm::Value *Val, + llvm::AtomicOrdering Ordering, llvm::SyncScope::ID SSID = llvm::SyncScope::System) { - return CGBuilderBaseTy::CreateAtomicRMW(Op, Ptr, Val, llvm::MaybeAlign(), + return CGBuilderBaseTy::CreateAtomicRMW(Op, Addr.getPointer(), Val, + Addr.getAlignment().getAsAlign(), Ordering, SSID); } diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp index 65d9862621061d85799579c6ef3d90d31a9b4589..0d8b3e4aaad4705994ea6d0fdc9ebbcd090671ee 100644 --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -188,8 +188,7 @@ static Value *EmitFromInt(CodeGenFunction &CGF, llvm::Value *V, return V; } -static llvm::Value *CheckAtomicAlignment(CodeGenFunction &CGF, - const CallExpr *E) { +static Address CheckAtomicAlignment(CodeGenFunction &CGF, const CallExpr *E) { ASTContext &Ctx = CGF.getContext(); Address Ptr = CGF.EmitPointerWithAlignment(E->getArg(0)); unsigned Bytes = Ptr.getElementType()->isPointerTy() @@ -199,8 +198,10 @@ static llvm::Value *CheckAtomicAlignment(CodeGenFunction &CGF, if (Align % Bytes != 0) { DiagnosticsEngine &Diags = CGF.CGM.getDiags(); Diags.Report(E->getBeginLoc(), diag::warn_sync_op_misaligned); + // Force address to be at least naturally-aligned. + return Ptr.withAlignment(CharUnits::fromQuantity(Bytes)); } - return Ptr.getPointer(); + return Ptr; } /// Utility to insert an atomic instruction based on Intrinsic::ID @@ -215,19 +216,17 @@ static Value *MakeBinaryAtomicValue( E->getArg(0)->getType()->getPointeeType())); assert(CGF.getContext().hasSameUnqualifiedType(T, E->getArg(1)->getType())); - llvm::Value *DestPtr = CheckAtomicAlignment(CGF, E); + Address DestAddr = CheckAtomicAlignment(CGF, E); llvm::IntegerType *IntType = llvm::IntegerType::get( CGF.getLLVMContext(), CGF.getContext().getTypeSize(T)); - llvm::Value *Args[2]; - Args[0] = DestPtr; - Args[1] = CGF.EmitScalarExpr(E->getArg(1)); - llvm::Type *ValueType = Args[1]->getType(); - Args[1] = EmitToInt(CGF, Args[1], T, IntType); + llvm::Value *Val = CGF.EmitScalarExpr(E->getArg(1)); + llvm::Type *ValueType = Val->getType(); + Val = EmitToInt(CGF, Val, T, IntType); - llvm::Value *Result = CGF.Builder.CreateAtomicRMW( - Kind, Args[0], Args[1], Ordering); + llvm::Value *Result = + CGF.Builder.CreateAtomicRMW(Kind, DestAddr, Val, Ordering); return EmitFromInt(CGF, Result, T, ValueType); } @@ -270,20 +269,18 @@ static RValue EmitBinaryAtomicPost(CodeGenFunction &CGF, E->getArg(0)->getType()->getPointeeType())); assert(CGF.getContext().hasSameUnqualifiedType(T, E->getArg(1)->getType())); - llvm::Value *DestPtr = CheckAtomicAlignment(CGF, E); + Address DestAddr = CheckAtomicAlignment(CGF, E); llvm::IntegerType *IntType = llvm::IntegerType::get( CGF.getLLVMContext(), CGF.getContext().getTypeSize(T)); - llvm::Value *Args[2]; - Args[1] = CGF.EmitScalarExpr(E->getArg(1)); - llvm::Type *ValueType = Args[1]->getType(); - Args[1] = EmitToInt(CGF, Args[1], T, IntType); - Args[0] = DestPtr; + llvm::Value *Val = CGF.EmitScalarExpr(E->getArg(1)); + llvm::Type *ValueType = Val->getType(); + Val = EmitToInt(CGF, Val, T, IntType); llvm::Value *Result = CGF.Builder.CreateAtomicRMW( - Kind, Args[0], Args[1], llvm::AtomicOrdering::SequentiallyConsistent); - Result = CGF.Builder.CreateBinOp(Op, Result, Args[1]); + Kind, DestAddr, Val, llvm::AtomicOrdering::SequentiallyConsistent); + Result = CGF.Builder.CreateBinOp(Op, Result, Val); if (Invert) Result = CGF.Builder.CreateBinOp(llvm::Instruction::Xor, Result, @@ -309,20 +306,18 @@ static RValue EmitBinaryAtomicPost(CodeGenFunction &CGF, static Value *MakeAtomicCmpXchgValue(CodeGenFunction &CGF, const CallExpr *E, bool ReturnBool) { QualType T = ReturnBool ? E->getArg(1)->getType() : E->getType(); - llvm::Value *DestPtr = CheckAtomicAlignment(CGF, E); + Address DestAddr = CheckAtomicAlignment(CGF, E); llvm::IntegerType *IntType = llvm::IntegerType::get( CGF.getLLVMContext(), CGF.getContext().getTypeSize(T)); - Value *Args[3]; - Args[0] = DestPtr; - Args[1] = CGF.EmitScalarExpr(E->getArg(1)); - llvm::Type *ValueType = Args[1]->getType(); - Args[1] = EmitToInt(CGF, Args[1], T, IntType); - Args[2] = EmitToInt(CGF, CGF.EmitScalarExpr(E->getArg(2)), T, IntType); + Value *Cmp = CGF.EmitScalarExpr(E->getArg(1)); + llvm::Type *ValueType = Cmp->getType(); + Cmp = EmitToInt(CGF, Cmp, T, IntType); + Value *New = EmitToInt(CGF, CGF.EmitScalarExpr(E->getArg(2)), T, IntType); Value *Pair = CGF.Builder.CreateAtomicCmpXchg( - Args[0], Args[1], Args[2], llvm::AtomicOrdering::SequentiallyConsistent, + DestAddr, Cmp, New, llvm::AtomicOrdering::SequentiallyConsistent, llvm::AtomicOrdering::SequentiallyConsistent); if (ReturnBool) // Extract boolean success flag and zext it to int. @@ -358,7 +353,8 @@ Value *EmitAtomicCmpXchgForMSIntrin(CodeGenFunction &CGF, const CallExpr *E, assert(CGF.getContext().hasSameUnqualifiedType(E->getType(), E->getArg(2)->getType())); - auto *Destination = CGF.EmitScalarExpr(E->getArg(0)); + Address DestAddr = CheckAtomicAlignment(CGF, E); + auto *Comparand = CGF.EmitScalarExpr(E->getArg(2)); auto *Exchange = CGF.EmitScalarExpr(E->getArg(1)); @@ -372,8 +368,7 @@ Value *EmitAtomicCmpXchgForMSIntrin(CodeGenFunction &CGF, const CallExpr *E, // _Interlocked* operations in the future, we will have to remove the volatile // marker. auto *Result = CGF.Builder.CreateAtomicCmpXchg( - Destination, Comparand, Exchange, - SuccessOrdering, FailureOrdering); + DestAddr, Comparand, Exchange, SuccessOrdering, FailureOrdering); Result->setVolatile(true); return CGF.Builder.CreateExtractValue(Result, 0); } @@ -386,29 +381,34 @@ Value *EmitAtomicCmpXchgForMSIntrin(CodeGenFunction &CGF, const CallExpr *E, // __int64 _ExchangeHigh, // __int64 _ExchangeLow, // __int64 * _ComparandResult); +// +// Note that Destination is assumed to be at least 16-byte aligned, despite +// being typed int64. + static Value *EmitAtomicCmpXchg128ForMSIntrin(CodeGenFunction &CGF, const CallExpr *E, AtomicOrdering SuccessOrdering) { assert(E->getNumArgs() == 4); - llvm::Value *Destination = CGF.EmitScalarExpr(E->getArg(0)); + llvm::Value *DestPtr = CGF.EmitScalarExpr(E->getArg(0)); llvm::Value *ExchangeHigh = CGF.EmitScalarExpr(E->getArg(1)); llvm::Value *ExchangeLow = CGF.EmitScalarExpr(E->getArg(2)); - llvm::Value *ComparandPtr = CGF.EmitScalarExpr(E->getArg(3)); + Address ComparandAddr = CGF.EmitPointerWithAlignment(E->getArg(3)); - assert(Destination->getType()->isPointerTy()); + assert(DestPtr->getType()->isPointerTy()); assert(!ExchangeHigh->getType()->isPointerTy()); assert(!ExchangeLow->getType()->isPointerTy()); - assert(ComparandPtr->getType()->isPointerTy()); // For Release ordering, the failure ordering should be Monotonic. auto FailureOrdering = SuccessOrdering == AtomicOrdering::Release ? AtomicOrdering::Monotonic : SuccessOrdering; - // Convert to i128 pointers and values. + // Convert to i128 pointers and values. Alignment is also overridden for + // destination pointer. llvm::Type *Int128Ty = llvm::IntegerType::get(CGF.getLLVMContext(), 128); - Address ComparandResult(ComparandPtr, Int128Ty, - CGF.getContext().toCharUnitsFromBits(128)); + Address DestAddr(DestPtr, Int128Ty, + CGF.getContext().toCharUnitsFromBits(128)); + ComparandAddr = ComparandAddr.withElementType(Int128Ty); // (((i128)hi) << 64) | ((i128)lo) ExchangeHigh = CGF.Builder.CreateZExt(ExchangeHigh, Int128Ty); @@ -418,9 +418,9 @@ static Value *EmitAtomicCmpXchg128ForMSIntrin(CodeGenFunction &CGF, llvm::Value *Exchange = CGF.Builder.CreateOr(ExchangeHigh, ExchangeLow); // Load the comparand for the instruction. - llvm::Value *Comparand = CGF.Builder.CreateLoad(ComparandResult); + llvm::Value *Comparand = CGF.Builder.CreateLoad(ComparandAddr); - auto *CXI = CGF.Builder.CreateAtomicCmpXchg(Destination, Comparand, Exchange, + auto *CXI = CGF.Builder.CreateAtomicCmpXchg(DestAddr, Comparand, Exchange, SuccessOrdering, FailureOrdering); // The atomic instruction is marked volatile for consistency with MSVC. This @@ -431,7 +431,7 @@ static Value *EmitAtomicCmpXchg128ForMSIntrin(CodeGenFunction &CGF, // Store the result as an outparameter. CGF.Builder.CreateStore(CGF.Builder.CreateExtractValue(CXI, 0), - ComparandResult); + ComparandAddr); // Get the success boolean and zero extend it to i8. Value *Success = CGF.Builder.CreateExtractValue(CXI, 1); @@ -443,24 +443,21 @@ static Value *EmitAtomicIncrementValue(CodeGenFunction &CGF, const CallExpr *E, assert(E->getArg(0)->getType()->isPointerType()); auto *IntTy = CGF.ConvertType(E->getType()); + Address DestAddr = CheckAtomicAlignment(CGF, E); auto *Result = CGF.Builder.CreateAtomicRMW( - AtomicRMWInst::Add, - CGF.EmitScalarExpr(E->getArg(0)), - ConstantInt::get(IntTy, 1), - Ordering); + AtomicRMWInst::Add, DestAddr, ConstantInt::get(IntTy, 1), Ordering); return CGF.Builder.CreateAdd(Result, ConstantInt::get(IntTy, 1)); } -static Value *EmitAtomicDecrementValue(CodeGenFunction &CGF, const CallExpr *E, +static Value *EmitAtomicDecrementValue( + CodeGenFunction &CGF, const CallExpr *E, AtomicOrdering Ordering = AtomicOrdering::SequentiallyConsistent) { assert(E->getArg(0)->getType()->isPointerType()); auto *IntTy = CGF.ConvertType(E->getType()); + Address DestAddr = CheckAtomicAlignment(CGF, E); auto *Result = CGF.Builder.CreateAtomicRMW( - AtomicRMWInst::Sub, - CGF.EmitScalarExpr(E->getArg(0)), - ConstantInt::get(IntTy, 1), - Ordering); + AtomicRMWInst::Sub, DestAddr, ConstantInt::get(IntTy, 1), Ordering); return CGF.Builder.CreateSub(Result, ConstantInt::get(IntTy, 1)); } @@ -1215,8 +1212,7 @@ static llvm::Value *EmitBitTestIntrinsic(CodeGenFunction &CGF, Mask = CGF.Builder.CreateNot(Mask); RMWOp = llvm::AtomicRMWInst::And; } - OldByte = CGF.Builder.CreateAtomicRMW(RMWOp, ByteAddr.getPointer(), Mask, - Ordering); + OldByte = CGF.Builder.CreateAtomicRMW(RMWOp, ByteAddr, Mask, Ordering); } else { // Emit a plain load for the non-interlocked intrinsics. OldByte = CGF.Builder.CreateLoad(ByteAddr, "bittest.byte"); @@ -4456,14 +4452,13 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID, case Builtin::BI__sync_lock_release_4: case Builtin::BI__sync_lock_release_8: case Builtin::BI__sync_lock_release_16: { - Value *Ptr = CheckAtomicAlignment(*this, E); + Address Ptr = CheckAtomicAlignment(*this, E); QualType ElTy = E->getArg(0)->getType()->getPointeeType(); - CharUnits StoreSize = getContext().getTypeSizeInChars(ElTy); - llvm::Type *ITy = - llvm::IntegerType::get(getLLVMContext(), StoreSize.getQuantity() * 8); + + llvm::Type *ITy = llvm::IntegerType::get(getLLVMContext(), + getContext().getTypeSize(ElTy)); llvm::StoreInst *Store = - Builder.CreateAlignedStore(llvm::Constant::getNullValue(ITy), Ptr, - StoreSize); + Builder.CreateStore(llvm::Constant::getNullValue(ITy), Ptr); Store->setAtomic(llvm::AtomicOrdering::Release); return RValue::get(nullptr); } @@ -4514,7 +4509,9 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID, bool Volatile = PtrTy->castAs()->getPointeeType().isVolatileQualified(); - Value *Ptr = EmitScalarExpr(E->getArg(0)); + Address Ptr = + EmitPointerWithAlignment(E->getArg(0)).withElementType(Int8Ty); + Value *NewVal = Builder.getInt8(1); Value *Order = EmitScalarExpr(E->getArg(1)); if (isa(Order)) { @@ -5035,7 +5032,7 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID, llvm::IntegerType *IntType = IntegerType::get( getLLVMContext(), getContext().getTypeSize(E->getType())); - llvm::Value *Destination = EmitScalarExpr(E->getArg(0)); + Address DestAddr = CheckAtomicAlignment(*this, E); llvm::Value *Exchange = EmitScalarExpr(E->getArg(1)); RTy = Exchange->getType(); @@ -5048,7 +5045,7 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID, BuiltinID == Builtin::BI_InterlockedCompareExchangePointer_nf ? AtomicOrdering::Monotonic : AtomicOrdering::SequentiallyConsistent; - auto Result = Builder.CreateAtomicCmpXchg(Destination, Comparand, Exchange, + auto Result = Builder.CreateAtomicCmpXchg(DestAddr, Comparand, Exchange, Ordering, Ordering); Result->setVolatile(true); @@ -11901,12 +11898,12 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID, } case clang::AArch64::BI_InterlockedAdd: { - Value *Arg0 = EmitScalarExpr(E->getArg(0)); - Value *Arg1 = EmitScalarExpr(E->getArg(1)); - AtomicRMWInst *RMWI = Builder.CreateAtomicRMW( - AtomicRMWInst::Add, Arg0, Arg1, - llvm::AtomicOrdering::SequentiallyConsistent); - return Builder.CreateAdd(RMWI, Arg1); + Address DestAddr = CheckAtomicAlignment(*this, E); + Value *Val = EmitScalarExpr(E->getArg(1)); + AtomicRMWInst *RMWI = + Builder.CreateAtomicRMW(AtomicRMWInst::Add, DestAddr, Val, + llvm::AtomicOrdering::SequentiallyConsistent); + return Builder.CreateAdd(RMWI, Val); } } @@ -18219,7 +18216,7 @@ Value *CodeGenFunction::EmitAMDGPUBuiltinExpr(unsigned BuiltinID, break; } - Value *Ptr = EmitScalarExpr(E->getArg(0)); + Address Ptr = CheckAtomicAlignment(*this, E); Value *Val = EmitScalarExpr(E->getArg(1)); ProcessOrderScopeAMDGCN(EmitScalarExpr(E->getArg(2)), @@ -18337,6 +18334,32 @@ Value *CodeGenFunction::EmitSystemZBuiltinExpr(unsigned BuiltinID, return Builder.CreateCall(F, {X, Undef}); } + case SystemZ::BI__builtin_s390_verllb: + case SystemZ::BI__builtin_s390_verllh: + case SystemZ::BI__builtin_s390_verllf: + case SystemZ::BI__builtin_s390_verllg: { + llvm::Type *ResultType = ConvertType(E->getType()); + llvm::Value *Src = EmitScalarExpr(E->getArg(0)); + llvm::Value *Amt = EmitScalarExpr(E->getArg(1)); + // Splat scalar rotate amount to vector type. + unsigned NumElts = cast(ResultType)->getNumElements(); + Amt = Builder.CreateIntCast(Amt, ResultType->getScalarType(), false); + Amt = Builder.CreateVectorSplat(NumElts, Amt); + Function *F = CGM.getIntrinsic(Intrinsic::fshl, ResultType); + return Builder.CreateCall(F, { Src, Src, Amt }); + } + + case SystemZ::BI__builtin_s390_verllvb: + case SystemZ::BI__builtin_s390_verllvh: + case SystemZ::BI__builtin_s390_verllvf: + case SystemZ::BI__builtin_s390_verllvg: { + llvm::Type *ResultType = ConvertType(E->getType()); + llvm::Value *Src = EmitScalarExpr(E->getArg(0)); + llvm::Value *Amt = EmitScalarExpr(E->getArg(1)); + Function *F = CGM.getIntrinsic(Intrinsic::fshl, ResultType); + return Builder.CreateCall(F, { Src, Src, Amt }); + } + case SystemZ::BI__builtin_s390_vfsqsb: case SystemZ::BI__builtin_s390_vfsqdb: { llvm::Type *ResultType = ConvertType(E->getType()); @@ -19082,9 +19105,10 @@ Value *CodeGenFunction::EmitNVPTXBuiltinExpr(unsigned BuiltinID, case NVPTX::BI__nvvm_atom_add_gen_f: case NVPTX::BI__nvvm_atom_add_gen_d: { - Value *Ptr = EmitScalarExpr(E->getArg(0)); + Address DestAddr = EmitPointerWithAlignment(E->getArg(0)); Value *Val = EmitScalarExpr(E->getArg(1)); - return Builder.CreateAtomicRMW(llvm::AtomicRMWInst::FAdd, Ptr, Val, + + return Builder.CreateAtomicRMW(llvm::AtomicRMWInst::FAdd, DestAddr, Val, AtomicOrdering::SequentiallyConsistent); } diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp index 4c2577126e48b3c9b3b3a40f5b157bbe35704741..a24aeea7ae32bfc9b58a8fe5c2b024fb3d46f2e7 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -5608,6 +5608,10 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, BundleList); EmitBlock(Cont); } + if (CI->getCalledFunction() && CI->getCalledFunction()->hasName() && + CI->getCalledFunction()->getName().startswith("_Z4sqrt")) { + SetSqrtFPAccuracy(CI); + } if (callOrInvoke) *callOrInvoke = CI; diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index 9d1f1a58f9e1c5e85e8b83b4db91649718ba804b..69cf7f76be9a7091742e3498c1a904ec838eafe5 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -3833,6 +3833,33 @@ static QualType getFixedSizeElementType(const ASTContext &ctx, return eltType; } +static bool hasBPFPreserveStaticOffset(const RecordDecl *D) { + return D && D->hasAttr(); +} + +static bool hasBPFPreserveStaticOffset(const Expr *E) { + if (!E) + return false; + QualType PointeeType = E->getType()->getPointeeType(); + if (PointeeType.isNull()) + return false; + if (const auto *BaseDecl = PointeeType->getAsRecordDecl()) + return hasBPFPreserveStaticOffset(BaseDecl); + return false; +} + +// Wraps Addr with a call to llvm.preserve.static.offset intrinsic. +static Address wrapWithBPFPreserveStaticOffset(CodeGenFunction &CGF, + Address &Addr) { + if (!CGF.getTarget().getTriple().isBPF()) + return Addr; + + llvm::Function *Fn = + CGF.CGM.getIntrinsic(llvm::Intrinsic::preserve_static_offset); + llvm::CallInst *Call = CGF.Builder.CreateCall(Fn, {Addr.getPointer()}); + return Address(Call, Addr.getElementType(), Addr.getAlignment()); +} + /// Given an array base, check whether its member access belongs to a record /// with preserve_access_index attribute or not. static bool IsPreserveAIArrayBase(CodeGenFunction &CGF, const Expr *ArrayBase) { @@ -3894,6 +3921,9 @@ static Address emitArraySubscriptGEP(CodeGenFunction &CGF, Address addr, CharUnits eltAlign = getArrayElementAlign(addr.getAlignment(), indices.back(), eltSize); + if (hasBPFPreserveStaticOffset(Base)) + addr = wrapWithBPFPreserveStaticOffset(CGF, addr); + llvm::Value *eltPtr; auto LastIndex = dyn_cast(indices.back()); if (!LastIndex || @@ -4522,6 +4552,8 @@ LValue CodeGenFunction::EmitLValueForField(LValue base, Address Addr = base.getAddress(*this); unsigned Idx = RL.getLLVMFieldNo(field); const RecordDecl *rec = field->getParent(); + if (hasBPFPreserveStaticOffset(rec)) + Addr = wrapWithBPFPreserveStaticOffset(*this, Addr); if (!UseVolatile) { if (!IsInPreservedAIRegion && (!getDebugInfo() || !rec->hasAttr())) { @@ -4594,6 +4626,8 @@ LValue CodeGenFunction::EmitLValueForField(LValue base, } Address addr = base.getAddress(*this); + if (hasBPFPreserveStaticOffset(rec)) + addr = wrapWithBPFPreserveStaticOffset(*this, addr); if (auto *ClassDef = dyn_cast(rec)) { if (CGM.getCodeGenOpts().StrictVTablePointers && ClassDef->isDynamicClass()) { diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp index 378437364767f693a072fe90a6957862efbc4743..41ad2ddac30d2d3b45758fde246a4fa83891011e 100644 --- a/clang/lib/CodeGen/CGExprScalar.cpp +++ b/clang/lib/CodeGen/CGExprScalar.cpp @@ -2571,7 +2571,7 @@ ScalarExprEmitter::EmitScalarPrePostIncDec(const UnaryOperator *E, LValue LV, // For atomic bool increment, we just store true and return it for // preincrement, do an atomic swap with true for postincrement return Builder.CreateAtomicRMW( - llvm::AtomicRMWInst::Xchg, LV.getPointer(CGF), True, + llvm::AtomicRMWInst::Xchg, LV.getAddress(CGF), True, llvm::AtomicOrdering::SequentiallyConsistent); } // Special case for atomic increment / decrement on integers, emit @@ -2589,7 +2589,7 @@ ScalarExprEmitter::EmitScalarPrePostIncDec(const UnaryOperator *E, LValue LV, llvm::Value *amt = CGF.EmitToMemory( llvm::ConstantInt::get(ConvertType(type), 1, true), type); llvm::Value *old = - Builder.CreateAtomicRMW(aop, LV.getPointer(CGF), amt, + Builder.CreateAtomicRMW(aop, LV.getAddress(CGF), amt, llvm::AtomicOrdering::SequentiallyConsistent); return isPre ? Builder.CreateBinOp(op, old, amt) : old; } @@ -3314,7 +3314,7 @@ LValue ScalarExprEmitter::EmitCompoundAssignLValue( E->getExprLoc()), LHSTy); Value *OldVal = Builder.CreateAtomicRMW( - AtomicOp, LHSLV.getPointer(CGF), Amt, + AtomicOp, LHSLV.getAddress(CGF), Amt, llvm::AtomicOrdering::SequentiallyConsistent); // Since operation is atomic, the result type is guaranteed to be the diff --git a/clang/lib/CodeGen/CGObjC.cpp b/clang/lib/CodeGen/CGObjC.cpp index fff89c8939a55a4e057df6f60d00b01e447d614c..acc85165a470be7327e36b6c3865e2aafd154490 100644 --- a/clang/lib/CodeGen/CGObjC.cpp +++ b/clang/lib/CodeGen/CGObjC.cpp @@ -827,11 +827,8 @@ static void emitStructGetterCall(CodeGenFunction &CGF, ObjCIvarDecl *ivar, // sizeof (Type of Ivar), isAtomic, false); CallArgList args; - llvm::Value *dest = - CGF.Builder.CreateBitCast(CGF.ReturnValue.getPointer(), CGF.VoidPtrTy); + llvm::Value *dest = CGF.ReturnValue.getPointer(); args.add(RValue::get(dest), Context.VoidPtrTy); - - src = CGF.Builder.CreateBitCast(src, CGF.VoidPtrTy); args.add(RValue::get(src), Context.VoidPtrTy); CharUnits size = CGF.getContext().getTypeSizeInChars(ivar->getType()); @@ -1098,7 +1095,6 @@ static void emitCPPObjectAtomicGetterCall(CodeGenFunction &CGF, llvm::Value *ivarAddr = CGF.EmitLValueForIvar(CGF.TypeOfSelfObject(), CGF.LoadObjCSelf(), ivar, 0) .getPointer(CGF); - ivarAddr = CGF.Builder.CreateBitCast(ivarAddr, CGF.Int8PtrTy); args.add(RValue::get(ivarAddr), CGF.getContext().VoidPtrTy); // Third argument is the helper function. @@ -1340,7 +1336,6 @@ static void emitStructSetterCall(CodeGenFunction &CGF, ObjCMethodDecl *OMD, argVar->getType().getNonReferenceType(), VK_LValue, SourceLocation()); llvm::Value *argAddr = CGF.EmitLValue(&argRef).getPointer(CGF); - argAddr = CGF.Builder.CreateBitCast(argAddr, CGF.Int8PtrTy); args.add(RValue::get(argAddr), CGF.getContext().VoidPtrTy); // The third argument is the sizeof the type. @@ -1377,7 +1372,6 @@ static void emitCPPObjectAtomicSetterCall(CodeGenFunction &CGF, llvm::Value *ivarAddr = CGF.EmitLValueForIvar(CGF.TypeOfSelfObject(), CGF.LoadObjCSelf(), ivar, 0) .getPointer(CGF); - ivarAddr = CGF.Builder.CreateBitCast(ivarAddr, CGF.Int8PtrTy); args.add(RValue::get(ivarAddr), CGF.getContext().VoidPtrTy); // The second argument is the address of the parameter variable. @@ -1386,7 +1380,6 @@ static void emitCPPObjectAtomicSetterCall(CodeGenFunction &CGF, argVar->getType().getNonReferenceType(), VK_LValue, SourceLocation()); llvm::Value *argAddr = CGF.EmitLValue(&argRef).getPointer(CGF); - argAddr = CGF.Builder.CreateBitCast(argAddr, CGF.Int8PtrTy); args.add(RValue::get(argAddr), CGF.getContext().VoidPtrTy); // Third argument is the helper function. @@ -3685,7 +3678,6 @@ void CodeGenFunction::EmitExtendGCLifetime(llvm::Value *object) { /* constraints */ "r", /* side effects */ true); - object = Builder.CreateBitCast(object, VoidPtrTy); EmitNounwindRuntimeCall(extender, object); } diff --git a/clang/lib/CodeGen/CGOpenMPRuntime.cpp b/clang/lib/CodeGen/CGOpenMPRuntime.cpp index d2be8141a3a4b31a95af680fa544ac0132fae5ce..55648963df36ab97811fecb15502f5a3cc85f421 100644 --- a/clang/lib/CodeGen/CGOpenMPRuntime.cpp +++ b/clang/lib/CodeGen/CGOpenMPRuntime.cpp @@ -1441,6 +1441,7 @@ llvm::Value *CGOpenMPRuntime::getThreadID(CodeGenFunction &CGF, setLocThreadIdInsertPt(CGF); CGBuilderTy::InsertPointGuard IPG(CGF.Builder); CGF.Builder.SetInsertPoint(Elem.second.ServiceInsertPt); + auto DL = ApplyDebugLocation::CreateDefaultArtificial(CGF, Loc); llvm::CallInst *Call = CGF.Builder.CreateCall( OMPBuilder.getOrCreateRuntimeFunction(CGM.getModule(), OMPRTL___kmpc_global_thread_num), diff --git a/clang/lib/CodeGen/CGOpenMPRuntimeGPU.cpp b/clang/lib/CodeGen/CGOpenMPRuntimeGPU.cpp index 5b9dbbf7e83a9687db0de3067afa1bc641dae605..293ccaa3413cdf372d72a02ff4a93f4c2a28b947 100644 --- a/clang/lib/CodeGen/CGOpenMPRuntimeGPU.cpp +++ b/clang/lib/CodeGen/CGOpenMPRuntimeGPU.cpp @@ -3017,11 +3017,7 @@ CGOpenMPRuntimeGPU::getParameterAddress(CodeGenFunction &CGF, QualType TargetTy = TargetParam->getType(); llvm::Value *TargetAddr = CGF.EmitLoadOfScalar(LocalAddr, /*Volatile=*/false, TargetTy, SourceLocation()); - // First cast to generic. - TargetAddr = CGF.Builder.CreatePointerBitCastOrAddrSpaceCast( - TargetAddr, - llvm::PointerType::get(CGF.getLLVMContext(), /*AddrSpace=*/0)); - // Cast from generic to native address space. + // Cast to native address space. TargetAddr = CGF.Builder.CreatePointerBitCastOrAddrSpaceCast( TargetAddr, llvm::PointerType::get(CGF.getLLVMContext(), NativePointeeAddrSpace)); @@ -3048,11 +3044,8 @@ void CGOpenMPRuntimeGPU::emitOutlinedFunctionCall( TargetArgs.emplace_back(NativeArg); continue; } - llvm::Value *TargetArg = CGF.Builder.CreatePointerBitCastOrAddrSpaceCast( - NativeArg, - llvm::PointerType::get(CGF.getLLVMContext(), /*AddrSpace*/ 0)); TargetArgs.emplace_back( - CGF.Builder.CreatePointerBitCastOrAddrSpaceCast(TargetArg, TargetType)); + CGF.Builder.CreatePointerBitCastOrAddrSpaceCast(NativeArg, TargetType)); } CGOpenMPRuntime::emitOutlinedFunctionCall(CGF, Loc, OutlinedFn, TargetArgs); } diff --git a/clang/lib/CodeGen/CGStmtOpenMP.cpp b/clang/lib/CodeGen/CGStmtOpenMP.cpp index 90c7ed450e54b99cb2374a663f8b410cdf20f43a..ed426098ac691564edd4d2d23d7ffdee5e07c1b0 100644 --- a/clang/lib/CodeGen/CGStmtOpenMP.cpp +++ b/clang/lib/CodeGen/CGStmtOpenMP.cpp @@ -2558,9 +2558,9 @@ static void emitOMPSimdRegion(CodeGenFunction &CGF, const OMPLoopDirective &S, (void)CGF.EmitOMPLinearClauseInit(S); { CodeGenFunction::OMPPrivateScope LoopScope(CGF); + CGF.EmitOMPPrivateClause(S, LoopScope); CGF.EmitOMPPrivateLoopCounters(S, LoopScope); CGF.EmitOMPLinearClause(S, LoopScope); - CGF.EmitOMPPrivateClause(S, LoopScope); CGF.EmitOMPReductionClauseInit(S, LoopScope); CGOpenMPRuntime::LastprivateConditionalRAII LPCRegion( CGF, S, CGF.EmitLValue(S.getIterationVariable())); @@ -6206,7 +6206,7 @@ static std::pair emitOMPAtomicRMW(CodeGenFunction &CGF, LValue X, X.getAddress(CGF).getElementType()); } llvm::Value *Res = - CGF.Builder.CreateAtomicRMW(RMWOp, X.getPointer(CGF), UpdateVal, AO); + CGF.Builder.CreateAtomicRMW(RMWOp, X.getAddress(CGF), UpdateVal, AO); return std::make_pair(true, RValue::get(Res)); } diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index dea58a7ff4146a65ec9267308e2e13e83114ee29..6a20723bf2bca0c85e198a257dbcdaf13534c27c 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -4178,8 +4178,29 @@ void CodeGenModule::emitMultiVersionFunctions() { } llvm::Constant *ResolverConstant = GetOrCreateMultiVersionResolver(GD); - if (auto *IFunc = dyn_cast(ResolverConstant)) + if (auto *IFunc = dyn_cast(ResolverConstant)) { ResolverConstant = IFunc->getResolver(); + // In Aarch64, default versions of multiversioned functions are mangled to + // their 'normal' assembly name. This deviates from other targets which + // append a '.default' string. As a result we need to continue appending + // .ifunc in Aarch64. + // FIXME: Should Aarch64 mangling for 'default' multiversion function and + // in turn ifunc function match that of other targets? + if (FD->isTargetClonesMultiVersion() && + !getTarget().getTriple().isAArch64()) { + const CGFunctionInfo &FI = getTypes().arrangeGlobalDeclaration(GD); + llvm::FunctionType *DeclTy = getTypes().GetFunctionType(FI); + std::string MangledName = getMangledNameImpl( + *this, GD, FD, /*OmitMultiVersionMangling=*/true); + // In prior versions of Clang, the mangling for ifuncs incorrectly + // included an .ifunc suffix. This alias is generated for backward + // compatibility. It is deprecated, and may be removed in the future. + auto *Alias = llvm::GlobalAlias::create( + DeclTy, 0, getMultiversionLinkage(*this, GD), + MangledName + ".ifunc", IFunc, &getModule()); + SetCommonAttributes(FD, Alias); + } + } llvm::Function *ResolverFunc = cast(ResolverConstant); ResolverFunc->setLinkage(getMultiversionLinkage(*this, GD)); @@ -4346,10 +4367,19 @@ llvm::Constant *CodeGenModule::GetOrCreateMultiVersionResolver(GlobalDecl GD) { // Holds the name of the resolver, in ifunc mode this is the ifunc (which has // a separate resolver). std::string ResolverName = MangledName; - if (getTarget().supportsIFunc()) - ResolverName += ".ifunc"; - else if (FD->isTargetMultiVersion()) + if (getTarget().supportsIFunc()) { + // In Aarch64, default versions of multiversioned functions are mangled to + // their 'normal' assembly name. This deviates from other targets which + // append a '.default' string. As a result we need to continue appending + // .ifunc in Aarch64. + // FIXME: Should Aarch64 mangling for 'default' multiversion function and + // in turn ifunc function match that of other targets? + if (!FD->isTargetClonesMultiVersion() || + getTarget().getTriple().isAArch64()) + ResolverName += ".ifunc"; + } else if (FD->isTargetMultiVersion()) { ResolverName += ".resolver"; + } // If the resolver has already been created, just return it. if (llvm::GlobalValue *ResolverGV = GetGlobalValue(ResolverName)) diff --git a/clang/lib/CodeGen/CodeGenTBAA.cpp b/clang/lib/CodeGen/CodeGenTBAA.cpp index 8705d3d65f1a573bb7493c92c083376e36044f09..5906b14dd93cf088bf9c25443d70c9a531416a00 100644 --- a/clang/lib/CodeGen/CodeGenTBAA.cpp +++ b/clang/lib/CodeGen/CodeGenTBAA.cpp @@ -342,7 +342,7 @@ llvm::MDNode *CodeGenTBAA::getBaseTypeInfoHelper(const Type *Ty) { // field. Virtual bases are more complex and omitted, but avoid an // incomplete view for NewStructPathTBAA. if (CodeGenOpts.NewStructPathTBAA && CXXRD->getNumVBases() != 0) - return BaseTypeMetadataCache[Ty] = nullptr; + return nullptr; for (const CXXBaseSpecifier &B : CXXRD->bases()) { if (B.isVirtual()) continue; @@ -354,7 +354,7 @@ llvm::MDNode *CodeGenTBAA::getBaseTypeInfoHelper(const Type *Ty) { ? getBaseTypeInfo(BaseQTy) : getTypeInfo(BaseQTy); if (!TypeNode) - return BaseTypeMetadataCache[Ty] = nullptr; + return nullptr; uint64_t Offset = Layout.getBaseClassOffset(BaseRD).getQuantity(); uint64_t Size = Context.getASTRecordLayout(BaseRD).getDataSize().getQuantity(); @@ -378,7 +378,7 @@ llvm::MDNode *CodeGenTBAA::getBaseTypeInfoHelper(const Type *Ty) { llvm::MDNode *TypeNode = isValidBaseType(FieldQTy) ? getBaseTypeInfo(FieldQTy) : getTypeInfo(FieldQTy); if (!TypeNode) - return BaseTypeMetadataCache[Ty] = nullptr; + return nullptr; uint64_t BitOffset = Layout.getFieldOffset(Field->getFieldIndex()); uint64_t Offset = Context.toCharUnitsFromBits(BitOffset).getQuantity(); @@ -418,14 +418,20 @@ llvm::MDNode *CodeGenTBAA::getBaseTypeInfo(QualType QTy) { return nullptr; const Type *Ty = Context.getCanonicalType(QTy).getTypePtr(); - if (llvm::MDNode *N = BaseTypeMetadataCache[Ty]) - return N; - // Note that the following helper call is allowed to add new nodes to the - // cache, which invalidates all its previously obtained iterators. So we - // first generate the node for the type and then add that node to the cache. + // nullptr is a valid value in the cache, so use find rather than [] + auto I = BaseTypeMetadataCache.find(Ty); + if (I != BaseTypeMetadataCache.end()) + return I->second; + + // First calculate the metadata, before recomputing the insertion point, as + // the helper can recursively call us. llvm::MDNode *TypeNode = getBaseTypeInfoHelper(Ty); - return BaseTypeMetadataCache[Ty] = TypeNode; + LLVM_ATTRIBUTE_UNUSED auto inserted = + BaseTypeMetadataCache.insert({Ty, TypeNode}); + assert(inserted.second && "BaseType metadata was already inserted"); + + return TypeNode; } llvm::MDNode *CodeGenTBAA::getAccessTagInfo(TBAAAccessInfo Info) { diff --git a/clang/lib/CodeGen/MicrosoftCXXABI.cpp b/clang/lib/CodeGen/MicrosoftCXXABI.cpp index 44b9acdee62648a1aa3512b170268269145f29af..172c4c937b97280d30ca5689ec58b600a852a809 100644 --- a/clang/lib/CodeGen/MicrosoftCXXABI.cpp +++ b/clang/lib/CodeGen/MicrosoftCXXABI.cpp @@ -2266,7 +2266,6 @@ MicrosoftCXXABI::performReturnAdjustment(CodeGenFunction &CGF, Address Ret, if (RA.isEmpty()) return Ret.getPointer(); - auto OrigTy = Ret.getType(); Ret = Ret.withElementType(CGF.Int8Ty); llvm::Value *V = Ret.getPointer(); @@ -2283,8 +2282,7 @@ MicrosoftCXXABI::performReturnAdjustment(CodeGenFunction &CGF, Address Ret, if (RA.NonVirtual) V = CGF.Builder.CreateConstInBoundsGEP1_32(CGF.Int8Ty, V, RA.NonVirtual); - // Cast back to the original type. - return CGF.Builder.CreateBitCast(V, OrigTy); + return V; } bool MicrosoftCXXABI::requiresArrayCookie(const CXXDeleteExpr *expr, diff --git a/clang/lib/Driver/Multilib.cpp b/clang/lib/Driver/Multilib.cpp index 48a494d9fa38db524971892ee37803385e38b5b6..7681c1a3ce6756f6cfc00cc13ed56b796a830ac5 100644 --- a/clang/lib/Driver/Multilib.cpp +++ b/clang/lib/Driver/Multilib.cpp @@ -9,6 +9,7 @@ #include "clang/Driver/Multilib.h" #include "clang/Basic/LLVM.h" #include "clang/Basic/Version.h" +#include "llvm/ADT/DenseSet.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Compiler.h" @@ -29,9 +30,10 @@ using namespace driver; using namespace llvm::sys; Multilib::Multilib(StringRef GCCSuffix, StringRef OSSuffix, - StringRef IncludeSuffix, const flags_list &Flags) + StringRef IncludeSuffix, const flags_list &Flags, + StringRef ExclusiveGroup) : GCCSuffix(GCCSuffix), OSSuffix(OSSuffix), IncludeSuffix(IncludeSuffix), - Flags(Flags) { + Flags(Flags), ExclusiveGroup(ExclusiveGroup) { assert(GCCSuffix.empty() || (StringRef(GCCSuffix).front() == '/' && GCCSuffix.size() > 1)); assert(OSSuffix.empty() || @@ -96,13 +98,37 @@ bool MultilibSet::select(const Multilib::flags_list &Flags, llvm::SmallVector &Selected) const { llvm::StringSet<> FlagSet(expandFlags(Flags)); Selected.clear(); - llvm::copy_if(Multilibs, std::back_inserter(Selected), - [&FlagSet](const Multilib &M) { - for (const std::string &F : M.flags()) - if (!FlagSet.contains(F)) - return false; - return true; - }); + + // Decide which multilibs we're going to select at all. + llvm::DenseSet ExclusiveGroupsSelected; + for (const Multilib &M : llvm::reverse(Multilibs)) { + // If this multilib doesn't match all our flags, don't select it. + if (!llvm::all_of(M.flags(), [&FlagSet](const std::string &F) { + return FlagSet.contains(F); + })) + continue; + + const std::string &group = M.exclusiveGroup(); + if (!group.empty()) { + // If this multilib has the same ExclusiveGroup as one we've already + // selected, skip it. We're iterating in reverse order, so the group + // member we've selected already is preferred. + // + // Otherwise, add the group name to the set of groups we've already + // selected a member of. + auto [It, Inserted] = ExclusiveGroupsSelected.insert(group); + if (!Inserted) + continue; + } + + // Select this multilib. + Selected.push_back(M); + } + + // We iterated in reverse order, so now put Selected back the right way + // round. + std::reverse(Selected.begin(), Selected.end()); + return !Selected.empty(); } @@ -138,10 +164,39 @@ static const VersionTuple MultilibVersionCurrent(1, 0); struct MultilibSerialization { std::string Dir; std::vector Flags; + std::string Group; +}; + +enum class MultilibGroupType { + /* + * The only group type currently supported is 'Exclusive', which indicates a + * group of multilibs of which at most one may be selected. + */ + Exclusive, + + /* + * Future possibility: a second group type indicating a set of library + * directories that are mutually _dependent_ rather than mutually exclusive: + * if you include one you must include them all. + * + * It might also be useful to allow groups to be members of other groups, so + * that a mutually exclusive group could contain a mutually dependent set of + * library directories, or vice versa. + * + * These additional features would need changes in the implementation, but + * the YAML schema is set up so they can be added without requiring changes + * in existing users' multilib.yaml files. + */ +}; + +struct MultilibGroupSerialization { + std::string Name; + MultilibGroupType Type; }; struct MultilibSetSerialization { llvm::VersionTuple MultilibVersion; + std::vector Groups; std::vector Multilibs; std::vector FlagMatchers; }; @@ -152,6 +207,7 @@ template <> struct llvm::yaml::MappingTraits { static void mapping(llvm::yaml::IO &io, MultilibSerialization &V) { io.mapRequired("Dir", V.Dir); io.mapRequired("Flags", V.Flags); + io.mapOptional("Group", V.Group); } static std::string validate(IO &io, MultilibSerialization &V) { if (StringRef(V.Dir).starts_with("/")) @@ -160,6 +216,19 @@ template <> struct llvm::yaml::MappingTraits { } }; +template <> struct llvm::yaml::ScalarEnumerationTraits { + static void enumeration(IO &io, MultilibGroupType &Val) { + io.enumCase(Val, "Exclusive", MultilibGroupType::Exclusive); + } +}; + +template <> struct llvm::yaml::MappingTraits { + static void mapping(llvm::yaml::IO &io, MultilibGroupSerialization &V) { + io.mapRequired("Name", V.Name); + io.mapRequired("Type", V.Type); + } +}; + template <> struct llvm::yaml::MappingTraits { static void mapping(llvm::yaml::IO &io, MultilibSet::FlagMatcher &M) { io.mapRequired("Match", M.Match); @@ -180,6 +249,7 @@ template <> struct llvm::yaml::MappingTraits { static void mapping(llvm::yaml::IO &io, MultilibSetSerialization &M) { io.mapRequired("MultilibVersion", M.MultilibVersion); io.mapRequired("Variants", M.Multilibs); + io.mapOptional("Groups", M.Groups); io.mapOptional("Mappings", M.FlagMatchers); } static std::string validate(IO &io, MultilibSetSerialization &M) { @@ -191,11 +261,25 @@ template <> struct llvm::yaml::MappingTraits { if (M.MultilibVersion.getMinor() > MultilibVersionCurrent.getMinor()) return "multilib version " + M.MultilibVersion.getAsString() + " is unsupported"; + for (const MultilibSerialization &Lib : M.Multilibs) { + if (!Lib.Group.empty()) { + bool Found = false; + for (const MultilibGroupSerialization &Group : M.Groups) + if (Group.Name == Lib.Group) { + Found = true; + break; + } + if (!Found) + return "multilib \"" + Lib.Dir + + "\" specifies undefined group name \"" + Lib.Group + "\""; + } + } return std::string{}; } }; LLVM_YAML_IS_SEQUENCE_VECTOR(MultilibSerialization) +LLVM_YAML_IS_SEQUENCE_VECTOR(MultilibGroupSerialization) LLVM_YAML_IS_SEQUENCE_VECTOR(MultilibSet::FlagMatcher) llvm::ErrorOr @@ -214,7 +298,11 @@ MultilibSet::parseYaml(llvm::MemoryBufferRef Input, std::string Dir; if (M.Dir != ".") Dir = "/" + M.Dir; - Multilibs.emplace_back(Dir, Dir, Dir, M.Flags); + // We transfer M.Group straight into the ExclusiveGroup parameter for the + // Multilib constructor. If we later support more than one type of group, + // we'll have to look up the group name in MS.Groups, check its type, and + // decide what to do here. + Multilibs.emplace_back(Dir, Dir, Dir, M.Flags, M.Group); } return MultilibSet(std::move(Multilibs), std::move(MS.FlagMatchers)); diff --git a/clang/lib/Driver/ToolChains/Arch/AArch64.cpp b/clang/lib/Driver/ToolChains/Arch/AArch64.cpp index 1f77c987051749ca5caba5bc77821b405e308000..097258b16924442abf7b067343eea81d9c8cb0ec 100644 --- a/clang/lib/Driver/ToolChains/Arch/AArch64.cpp +++ b/clang/lib/Driver/ToolChains/Arch/AArch64.cpp @@ -57,9 +57,8 @@ std::string aarch64::getAArch64TargetCPU(const ArgList &Args, if (Triple.isArm64e()) return "apple-a12"; - // Make sure we pick the appropriate Apple CPU if -arch is used or when - // targetting a Darwin OS. - if (Args.getLastArg(options::OPT_arch) || Triple.isOSDarwin()) + // Make sure we pick the appropriate Apple CPU when targetting a Darwin OS. + if (Triple.isOSDarwin()) return Triple.getArch() == llvm::Triple::aarch64_32 ? "apple-s4" : "apple-a7"; @@ -274,7 +273,7 @@ void aarch64::getAArch64TargetFeatures(const Driver &D, success = getAArch64ArchFeaturesFromMarch(D, A->getValue(), Args, Features); else if ((A = Args.getLastArg(options::OPT_mcpu_EQ))) success = getAArch64ArchFeaturesFromMcpu(D, A->getValue(), Args, Features); - else if (Args.hasArg(options::OPT_arch) || isCPUDeterminedByTriple(Triple)) + else if (isCPUDeterminedByTriple(Triple)) success = getAArch64ArchFeaturesFromMcpu( D, getAArch64TargetCPU(Args, Triple, A), Args, Features); else @@ -287,8 +286,7 @@ void aarch64::getAArch64TargetFeatures(const Driver &D, else if (success && (A = Args.getLastArg(options::OPT_mcpu_EQ))) success = getAArch64MicroArchFeaturesFromMcpu(D, A->getValue(), Args, Features); - else if (success && - (Args.hasArg(options::OPT_arch) || isCPUDeterminedByTriple(Triple))) + else if (success && isCPUDeterminedByTriple(Triple)) success = getAArch64MicroArchFeaturesFromMcpu( D, getAArch64TargetCPU(Args, Triple, A), Args, Features); diff --git a/clang/lib/Driver/ToolChains/Arch/RISCV.cpp b/clang/lib/Driver/ToolChains/Arch/RISCV.cpp index 346fb67ff277e9c53f3ef25ce37363653c5eb41b..5d990ba78e5cc880f72bd31d9b250b1cc0a9eefa 100644 --- a/clang/lib/Driver/ToolChains/Arch/RISCV.cpp +++ b/clang/lib/Driver/ToolChains/Arch/RISCV.cpp @@ -65,7 +65,7 @@ static void getRISCFeaturesFromMcpu(const Driver &D, const Arg *A, } if (llvm::RISCV::hasFastUnalignedAccess(Mcpu)) - Features.push_back("+unaligned-scalar-mem"); + Features.push_back("+fast-unaligned-access"); } void riscv::getRISCVTargetFeatures(const Driver &D, const llvm::Triple &Triple, @@ -171,18 +171,12 @@ void riscv::getRISCVTargetFeatures(const Driver &D, const llvm::Triple &Triple, Features.push_back("-save-restore"); // -mno-unaligned-access is default, unless -munaligned-access is specified. - bool HasV = llvm::is_contained(Features, "+zve32x"); if (const Arg *A = Args.getLastArg(options::OPT_munaligned_access, options::OPT_mno_unaligned_access)) { - if (A->getOption().matches(options::OPT_munaligned_access)) { - Features.push_back("+unaligned-scalar-mem"); - if (HasV) - Features.push_back("+unaligned-vector-mem"); - } else { - Features.push_back("-unaligned-scalar-mem"); - if (HasV) - Features.push_back("-unaligned-vector-mem"); - } + if (A->getOption().matches(options::OPT_munaligned_access)) + Features.push_back("+fast-unaligned-access"); + else + Features.push_back("-fast-unaligned-access"); } // Now add any that the user explicitly requested on the command line, diff --git a/clang/lib/Driver/ToolChains/Arch/X86.cpp b/clang/lib/Driver/ToolChains/Arch/X86.cpp index fbe665bdd5c8afb6e5257d046d075d85c302d186..3e51b2b5ce8642e0ac08afc98617e96b630e2816 100644 --- a/clang/lib/Driver/ToolChains/Arch/X86.cpp +++ b/clang/lib/Driver/ToolChains/Arch/X86.cpp @@ -270,6 +270,21 @@ void x86::getX86TargetFeatures(const Driver &D, const llvm::Triple &Triple, } bool IsNegative = Name.startswith("no-"); + if (A->getOption().matches(options::OPT_mapx_features_EQ) || + A->getOption().matches(options::OPT_mno_apx_features_EQ)) { + + for (StringRef Value : A->getValues()) { + if (Value == "egpr" || Value == "push2pop2" || Value == "ppx" || + Value == "ndd" || Value == "ccmp" || Value == "cf") { + Features.push_back( + Args.MakeArgString((IsNegative ? "-" : "+") + Value)); + continue; + } + D.Diag(clang::diag::err_drv_unsupported_option_argument) + << A->getSpelling() << Value; + } + continue; + } if (IsNegative) Name = Name.substr(3); Features.push_back(Args.MakeArgString((IsNegative ? "-" : "+") + Name)); diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index 3c8df8a9037d6957395abba867eeb5d816e84d81..f02f7c841b91f0ba56dd41e377429397404fb9d3 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -409,139 +409,6 @@ static bool ShouldEnableAutolink(const ArgList &Args, const ToolChain &TC, Default); } -static bool mustUseNonLeafFramePointerForTarget(const llvm::Triple &Triple) { - switch (Triple.getArch()){ - default: - return false; - case llvm::Triple::arm: - case llvm::Triple::thumb: - // ARM Darwin targets require a frame pointer to be always present to aid - // offline debugging via backtraces. - return Triple.isOSDarwin(); - } -} - -static bool useFramePointerForTargetByDefault(const ArgList &Args, - const llvm::Triple &Triple) { - if (Args.hasArg(options::OPT_pg) && !Args.hasArg(options::OPT_mfentry)) - return true; - - if (Triple.isAndroid()) { - switch (Triple.getArch()) { - case llvm::Triple::aarch64: - case llvm::Triple::arm: - case llvm::Triple::armeb: - case llvm::Triple::thumb: - case llvm::Triple::thumbeb: - case llvm::Triple::riscv64: - return true; - default: - break; - } - } - - switch (Triple.getArch()) { - case llvm::Triple::xcore: - case llvm::Triple::wasm32: - case llvm::Triple::wasm64: - case llvm::Triple::msp430: - // XCore never wants frame pointers, regardless of OS. - // WebAssembly never wants frame pointers. - return false; - case llvm::Triple::ppc: - case llvm::Triple::ppcle: - case llvm::Triple::ppc64: - case llvm::Triple::ppc64le: - case llvm::Triple::riscv32: - case llvm::Triple::riscv64: - case llvm::Triple::sparc: - case llvm::Triple::sparcel: - case llvm::Triple::sparcv9: - case llvm::Triple::amdgcn: - case llvm::Triple::r600: - case llvm::Triple::csky: - case llvm::Triple::loongarch32: - case llvm::Triple::loongarch64: - return !areOptimizationsEnabled(Args); - default: - break; - } - - if (Triple.isOSFuchsia() || Triple.isOSNetBSD()) { - return !areOptimizationsEnabled(Args); - } - - if (Triple.isOSLinux() || Triple.isOSHurd()) { - switch (Triple.getArch()) { - // Don't use a frame pointer on linux if optimizing for certain targets. - case llvm::Triple::arm: - case llvm::Triple::armeb: - case llvm::Triple::thumb: - case llvm::Triple::thumbeb: - case llvm::Triple::mips64: - case llvm::Triple::mips64el: - case llvm::Triple::mips: - case llvm::Triple::mipsel: - case llvm::Triple::systemz: - case llvm::Triple::x86: - case llvm::Triple::x86_64: - return !areOptimizationsEnabled(Args); - default: - return true; - } - } - - if (Triple.isOSWindows()) { - switch (Triple.getArch()) { - case llvm::Triple::x86: - return !areOptimizationsEnabled(Args); - case llvm::Triple::x86_64: - return Triple.isOSBinFormatMachO(); - case llvm::Triple::arm: - case llvm::Triple::thumb: - // Windows on ARM builds with FPO disabled to aid fast stack walking - return true; - default: - // All other supported Windows ISAs use xdata unwind information, so frame - // pointers are not generally useful. - return false; - } - } - - return true; -} - -static CodeGenOptions::FramePointerKind -getFramePointerKind(const ArgList &Args, const llvm::Triple &Triple) { - // We have 4 states: - // - // 00) leaf retained, non-leaf retained - // 01) leaf retained, non-leaf omitted (this is invalid) - // 10) leaf omitted, non-leaf retained - // (what -momit-leaf-frame-pointer was designed for) - // 11) leaf omitted, non-leaf omitted - // - // "omit" options taking precedence over "no-omit" options is the only way - // to make 3 valid states representable - Arg *A = Args.getLastArg(options::OPT_fomit_frame_pointer, - options::OPT_fno_omit_frame_pointer); - bool OmitFP = A && A->getOption().matches(options::OPT_fomit_frame_pointer); - bool NoOmitFP = - A && A->getOption().matches(options::OPT_fno_omit_frame_pointer); - bool OmitLeafFP = - Args.hasFlag(options::OPT_momit_leaf_frame_pointer, - options::OPT_mno_omit_leaf_frame_pointer, - Triple.isAArch64() || Triple.isPS() || Triple.isVE() || - (Triple.isAndroid() && Triple.isRISCV64())); - if (NoOmitFP || mustUseNonLeafFramePointerForTarget(Triple) || - (!OmitFP && useFramePointerForTargetByDefault(Args, Triple))) { - if (OmitLeafFP) - return CodeGenOptions::FramePointerKind::NonLeaf; - return CodeGenOptions::FramePointerKind::All; - } - return CodeGenOptions::FramePointerKind::None; -} - /// Add a CC1 option to specify the debug compilation directory. static const char *addDebugCompDirArg(const ArgList &Args, ArgStringList &CmdArgs, @@ -4862,7 +4729,9 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, bool UnifiedLTO = false; if (IsUsingLTO) { UnifiedLTO = Args.hasFlag(options::OPT_funified_lto, - options::OPT_fno_unified_lto, Triple.isPS()); + options::OPT_fno_unified_lto, Triple.isPS()) || + Args.hasFlag(options::OPT_ffat_lto_objects, + options::OPT_fno_fat_lto_objects, false); if (UnifiedLTO) CmdArgs.push_back("-funified-lto"); } diff --git a/clang/lib/Driver/ToolChains/CommonArgs.cpp b/clang/lib/Driver/ToolChains/CommonArgs.cpp index efb8b71f24a932c593db253cd2ed30ac4409d400..0ae8e2dce32e94a8703ea73cfae44f95f4a16a68 100644 --- a/clang/lib/Driver/ToolChains/CommonArgs.cpp +++ b/clang/lib/Driver/ToolChains/CommonArgs.cpp @@ -24,6 +24,7 @@ #include "MSP430.h" #include "Solaris.h" #include "clang/Basic/CharInfo.h" +#include "clang/Basic/CodeGenOptions.h" #include "clang/Basic/LangOptions.h" #include "clang/Basic/ObjCRuntime.h" #include "clang/Basic/Version.h" @@ -71,6 +72,144 @@ using namespace clang::driver::tools; using namespace clang; using namespace llvm::opt; +static bool useFramePointerForTargetByDefault(const llvm::opt::ArgList &Args, + const llvm::Triple &Triple) { + if (Args.hasArg(clang::driver::options::OPT_pg) && + !Args.hasArg(clang::driver::options::OPT_mfentry)) + return true; + + if (Triple.isAndroid()) { + switch (Triple.getArch()) { + case llvm::Triple::aarch64: + case llvm::Triple::arm: + case llvm::Triple::armeb: + case llvm::Triple::thumb: + case llvm::Triple::thumbeb: + case llvm::Triple::riscv64: + return true; + default: + break; + } + } + + switch (Triple.getArch()) { + case llvm::Triple::xcore: + case llvm::Triple::wasm32: + case llvm::Triple::wasm64: + case llvm::Triple::msp430: + // XCore never wants frame pointers, regardless of OS. + // WebAssembly never wants frame pointers. + return false; + case llvm::Triple::ppc: + case llvm::Triple::ppcle: + case llvm::Triple::ppc64: + case llvm::Triple::ppc64le: + case llvm::Triple::riscv32: + case llvm::Triple::riscv64: + case llvm::Triple::sparc: + case llvm::Triple::sparcel: + case llvm::Triple::sparcv9: + case llvm::Triple::amdgcn: + case llvm::Triple::r600: + case llvm::Triple::csky: + case llvm::Triple::loongarch32: + case llvm::Triple::loongarch64: + return !clang::driver::tools::areOptimizationsEnabled(Args); + default: + break; + } + + if (Triple.isOSFuchsia() || Triple.isOSNetBSD()) { + return !clang::driver::tools::areOptimizationsEnabled(Args); + } + + if (Triple.isOSLinux() || Triple.isOSHurd()) { + switch (Triple.getArch()) { + // Don't use a frame pointer on linux if optimizing for certain targets. + case llvm::Triple::arm: + case llvm::Triple::armeb: + case llvm::Triple::thumb: + case llvm::Triple::thumbeb: + case llvm::Triple::mips64: + case llvm::Triple::mips64el: + case llvm::Triple::mips: + case llvm::Triple::mipsel: + case llvm::Triple::systemz: + case llvm::Triple::x86: + case llvm::Triple::x86_64: + return !clang::driver::tools::areOptimizationsEnabled(Args); + default: + return true; + } + } + + if (Triple.isOSWindows()) { + switch (Triple.getArch()) { + case llvm::Triple::x86: + return !clang::driver::tools::areOptimizationsEnabled(Args); + case llvm::Triple::x86_64: + return Triple.isOSBinFormatMachO(); + case llvm::Triple::arm: + case llvm::Triple::thumb: + // Windows on ARM builds with FPO disabled to aid fast stack walking + return true; + default: + // All other supported Windows ISAs use xdata unwind information, so frame + // pointers are not generally useful. + return false; + } + } + + return true; +} + +static bool mustUseNonLeafFramePointerForTarget(const llvm::Triple &Triple) { + switch (Triple.getArch()) { + default: + return false; + case llvm::Triple::arm: + case llvm::Triple::thumb: + // ARM Darwin targets require a frame pointer to be always present to aid + // offline debugging via backtraces. + return Triple.isOSDarwin(); + } +} + +clang::CodeGenOptions::FramePointerKind +getFramePointerKind(const llvm::opt::ArgList &Args, + const llvm::Triple &Triple) { + // We have 4 states: + // + // 00) leaf retained, non-leaf retained + // 01) leaf retained, non-leaf omitted (this is invalid) + // 10) leaf omitted, non-leaf retained + // (what -momit-leaf-frame-pointer was designed for) + // 11) leaf omitted, non-leaf omitted + // + // "omit" options taking precedence over "no-omit" options is the only way + // to make 3 valid states representable + llvm::opt::Arg *A = + Args.getLastArg(clang::driver::options::OPT_fomit_frame_pointer, + clang::driver::options::OPT_fno_omit_frame_pointer); + + bool OmitFP = A && A->getOption().matches( + clang::driver::options::OPT_fomit_frame_pointer); + bool NoOmitFP = A && A->getOption().matches( + clang::driver::options::OPT_fno_omit_frame_pointer); + bool OmitLeafFP = + Args.hasFlag(clang::driver::options::OPT_momit_leaf_frame_pointer, + clang::driver::options::OPT_mno_omit_leaf_frame_pointer, + Triple.isAArch64() || Triple.isPS() || Triple.isVE() || + (Triple.isAndroid() && Triple.isRISCV64())); + if (NoOmitFP || mustUseNonLeafFramePointerForTarget(Triple) || + (!OmitFP && useFramePointerForTargetByDefault(Args, Triple))) { + if (OmitLeafFP) + return clang::CodeGenOptions::FramePointerKind::NonLeaf; + return clang::CodeGenOptions::FramePointerKind::All; + } + return clang::CodeGenOptions::FramePointerKind::None; +} + static void renderRpassOptions(const ArgList &Args, ArgStringList &CmdArgs, const StringRef PluginOptPrefix) { if (const Arg *A = Args.getLastArg(options::OPT_Rpass_EQ)) diff --git a/clang/lib/Driver/ToolChains/CommonArgs.h b/clang/lib/Driver/ToolChains/CommonArgs.h index 0a0951c5386e6013c18fc6df70b511e5d8f911a6..25d68345a9f9ebf382c4df5a99bc21b66d0da83a 100644 --- a/clang/lib/Driver/ToolChains/CommonArgs.h +++ b/clang/lib/Driver/ToolChains/CommonArgs.h @@ -9,6 +9,7 @@ #ifndef LLVM_CLANG_LIB_DRIVER_TOOLCHAINS_COMMONARGS_H #define LLVM_CLANG_LIB_DRIVER_TOOLCHAINS_COMMONARGS_H +#include "clang/Basic/CodeGenOptions.h" #include "clang/Driver/Driver.h" #include "clang/Driver/InputInfo.h" #include "clang/Driver/Multilib.h" @@ -215,4 +216,7 @@ void addOpenMPDeviceRTL(const Driver &D, const llvm::opt::ArgList &DriverArgs, } // end namespace driver } // end namespace clang +clang::CodeGenOptions::FramePointerKind +getFramePointerKind(const llvm::opt::ArgList &Args, const llvm::Triple &Triple); + #endif // LLVM_CLANG_LIB_DRIVER_TOOLCHAINS_COMMONARGS_H diff --git a/clang/lib/Driver/ToolChains/Flang.cpp b/clang/lib/Driver/ToolChains/Flang.cpp index 033051b95288a4c0927d8be341a7d565b431f1dc..9b21fe952af7a8293bf92e41758a290563c3c2d1 100644 --- a/clang/lib/Driver/ToolChains/Flang.cpp +++ b/clang/lib/Driver/ToolChains/Flang.cpp @@ -9,6 +9,7 @@ #include "Flang.h" #include "CommonArgs.h" +#include "clang/Basic/CodeGenOptions.h" #include "clang/Driver/Options.h" #include "llvm/Frontend/Debug/Options.h" #include "llvm/Support/FileSystem.h" @@ -146,9 +147,7 @@ void Flang::addCodegenOptions(const ArgList &Args, options::OPT_flang_deprecated_no_hlfir, options::OPT_flang_experimental_polymorphism, options::OPT_fno_ppc_native_vec_elem_order, - options::OPT_fppc_native_vec_elem_order, - options::OPT_falias_analysis, - options::OPT_fno_alias_analysis}); + options::OPT_fppc_native_vec_elem_order}); } void Flang::addPicOptions(const ArgList &Args, ArgStringList &CmdArgs) const { @@ -204,6 +203,30 @@ void Flang::AddAArch64TargetArgs(const ArgList &Args, } } +static void addVSDefines(const ToolChain &TC, const ArgList &Args, + ArgStringList &CmdArgs) { + + unsigned ver = 0; + const VersionTuple vt = TC.computeMSVCVersion(nullptr, Args); + ver = vt.getMajor() * 10000000 + vt.getMinor().value_or(0) * 100000 + + vt.getSubminor().value_or(0); + CmdArgs.push_back(Args.MakeArgString("-D_MSC_VER=" + Twine(ver / 100000))); + CmdArgs.push_back(Args.MakeArgString("-D_MSC_FULL_VER=" + Twine(ver))); + CmdArgs.push_back(Args.MakeArgString("-D_WIN32")); + + llvm::Triple triple = TC.getTriple(); + if (triple.isAArch64()) { + CmdArgs.push_back("-D_M_ARM64=1"); + } else if (triple.isX86() && triple.isArch32Bit()) { + CmdArgs.push_back("-D_M_IX86=600"); + } else if (triple.isX86() && triple.isArch64Bit()) { + CmdArgs.push_back("-D_M_X64=100"); + } else { + llvm_unreachable( + "Flang on Windows only supports X86_32, X86_64 and AArch64"); + } +} + static void processVSRuntimeLibrary(const ToolChain &TC, const ArgList &Args, ArgStringList &CmdArgs) { assert(TC.getTriple().isKnownWindowsMSVCEnvironment() && @@ -333,6 +356,7 @@ void Flang::addTargetOptions(const ArgList &Args, if (Triple.isKnownWindowsMSVCEnvironment()) { processVSRuntimeLibrary(TC, Args, CmdArgs); + addVSDefines(TC, Args, CmdArgs); } // TODO: Add target specific flags, ABI, mtune option etc. @@ -674,6 +698,24 @@ void Flang::ConstructJob(Compilation &C, const JobAction &JA, // Forward -Xflang arguments to -fc1 Args.AddAllArgValues(CmdArgs, options::OPT_Xflang); + CodeGenOptions::FramePointerKind FPKeepKind = + getFramePointerKind(Args, Triple); + + const char *FPKeepKindStr = nullptr; + switch (FPKeepKind) { + case CodeGenOptions::FramePointerKind::None: + FPKeepKindStr = "-mframe-pointer=none"; + break; + case CodeGenOptions::FramePointerKind::NonLeaf: + FPKeepKindStr = "-mframe-pointer=non-leaf"; + break; + case CodeGenOptions::FramePointerKind::All: + FPKeepKindStr = "-mframe-pointer=all"; + break; + } + assert(FPKeepKindStr && "unknown FramePointerKind"); + CmdArgs.push_back(FPKeepKindStr); + // Forward -mllvm options to the LLVM option parser. In practice, this means // forwarding to `-fc1` as that's where the LLVM parser is run. for (const Arg *A : Args.filtered(options::OPT_mllvm)) { diff --git a/clang/lib/Driver/ToolChains/FreeBSD.cpp b/clang/lib/Driver/ToolChains/FreeBSD.cpp index d46feb3459a6344d6da4f38280710a89c0f8aba5..b7c9e0e51cdb666a76585c8dbfcbeb9dc53f0502 100644 --- a/clang/lib/Driver/ToolChains/FreeBSD.cpp +++ b/clang/lib/Driver/ToolChains/FreeBSD.cpp @@ -400,13 +400,6 @@ FreeBSD::FreeBSD(const Driver &D, const llvm::Triple &Triple, getFilePaths().push_back(concat(getDriver().SysRoot, "/usr/lib")); } -unsigned FreeBSD::GetDefaultDwarfVersion() const { - unsigned Major = getTriple().getOSMajorVersion(); - if (Major >= 12 || Major == 0) - return 4; - return 2; -} - void FreeBSD::AddClangSystemIncludeArgs( const llvm::opt::ArgList &DriverArgs, llvm::opt::ArgStringList &CC1Args) const { @@ -510,13 +503,3 @@ SanitizerMask FreeBSD::getSupportedSanitizers() const { } return Res; } - -void FreeBSD::addClangTargetOptions(const ArgList &DriverArgs, - ArgStringList &CC1Args, - Action::OffloadKind) const { - unsigned Major = getTriple().getOSMajorVersion(); - if (!DriverArgs.hasFlag(options::OPT_fuse_init_array, - options::OPT_fno_use_init_array, - (Major >= 12 || Major == 0))) - CC1Args.push_back("-fno-use-init-array"); -} diff --git a/clang/lib/Driver/ToolChains/FreeBSD.h b/clang/lib/Driver/ToolChains/FreeBSD.h index 959cbf78ddc79d194ef05b22c56e01781e5d3197..7ab63905ed4f991829ffd4c6e3ace523f9579229 100644 --- a/clang/lib/Driver/ToolChains/FreeBSD.h +++ b/clang/lib/Driver/ToolChains/FreeBSD.h @@ -82,14 +82,10 @@ public: getDefaultUnwindTableLevel(const llvm::opt::ArgList &Args) const override; bool isPIEDefault(const llvm::opt::ArgList &Args) const override; SanitizerMask getSupportedSanitizers() const override; - unsigned GetDefaultDwarfVersion() const override; + unsigned GetDefaultDwarfVersion() const override { return 4; } // Until dtrace (via CTF) and LLDB can deal with distributed debug info, // FreeBSD defaults to standalone/full debug info. bool GetDefaultStandaloneDebug() const override { return true; } - void - addClangTargetOptions(const llvm::opt::ArgList &DriverArgs, - llvm::opt::ArgStringList &CC1Args, - Action::OffloadKind DeviceOffloadKind) const override; protected: Tool *buildAssembler() const override; diff --git a/clang/lib/Driver/ToolChains/HIPUtility.cpp b/clang/lib/Driver/ToolChains/HIPUtility.cpp index 04efdcba20ea7403e0b209d1ac3cbb98a1f72cf3..f692458b775de2b6c4bcfafa177a9e8c7ff40430 100644 --- a/clang/lib/Driver/ToolChains/HIPUtility.cpp +++ b/clang/lib/Driver/ToolChains/HIPUtility.cpp @@ -150,6 +150,8 @@ void HIP::constructGenerateObjFileFromHIPFatBinary( ObjStream << " .incbin "; llvm::sys::printArg(ObjStream, BundleFile, /*Quote=*/true); ObjStream << "\n"; + if (HostTriple.isOSLinux() && HostTriple.isOSBinFormatELF()) + ObjStream << " .section .note.GNU-stack, \"\", @progbits\n"; ObjStream.flush(); // Dump the contents of the temp object file gen if the user requested that. diff --git a/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp b/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp index 3aba3bf44547cf660073f5d7fe4a1571cfee994b..fe282dfb19e8aa7fa9bf6ff43ee2c18b309864a7 100644 --- a/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp +++ b/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp @@ -17,6 +17,7 @@ #include "clang/AST/ASTContext.h" #include "clang/AST/DeclObjC.h" #include "clang/Basic/DiagnosticFrontend.h" +#include "clang/Basic/FileEntry.h" #include "clang/Basic/SourceLocation.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/TargetInfo.h" @@ -167,6 +168,12 @@ std::optional getRelativeIncludeName(const CompilerInstance &CI, return std::nullopt; } +std::optional getRelativeIncludeName(const CompilerInstance &CI, + FileEntryRef FE, + bool *IsQuoted = nullptr) { + return getRelativeIncludeName(CI, FE.getNameAsRequested(), IsQuoted); +} + struct LocationFileChecker { bool operator()(SourceLocation Loc) { // If the loc refers to a macro expansion we need to first get the file @@ -187,11 +194,9 @@ struct LocationFileChecker { if (ExternalFileEntries.count(*File)) return false; - StringRef FileName = SM.getFileManager().getCanonicalName(*File); - // Try to reduce the include name the same way we tried to include it. bool IsQuoted = false; - if (auto IncludeName = getRelativeIncludeName(CI, FileName, &IsQuoted)) + if (auto IncludeName = getRelativeIncludeName(CI, *File, &IsQuoted)) if (llvm::any_of(KnownFiles, [&IsQuoted, &IncludeName](const auto &KnownFile) { return KnownFile.first.equals(*IncludeName) && diff --git a/clang/lib/Format/.clang-format b/clang/lib/Format/.clang-format index 63d45e0307a3dfebe23e458478f608e837174a0b..d7331b3c8cf02ef7944e83a4280476627e33b31d 100644 --- a/clang/lib/Format/.clang-format +++ b/clang/lib/Format/.clang-format @@ -1,5 +1 @@ -BasedOnStyle: LLVM -InsertBraces: true -InsertNewlineAtEOF: true -LineEnding: LF -RemoveBracesLLVM: true +BasedOnStyle: clang-format diff --git a/clang/lib/Format/CMakeLists.txt b/clang/lib/Format/CMakeLists.txt index 3d3f3c6bf22e732655f4b2a040eec871c7ca069b..015ec7c0cc84e3f68b18c41a4cbd602d09f75e18 100644 --- a/clang/lib/Format/CMakeLists.txt +++ b/clang/lib/Format/CMakeLists.txt @@ -12,6 +12,7 @@ add_clang_library(clangFormat MacroCallReconstructor.cpp MacroExpander.cpp NamespaceEndCommentsFixer.cpp + ObjCPropertyAttributeOrderFixer.cpp QualifierAlignmentFixer.cpp SortJavaScriptImports.cpp TokenAnalyzer.cpp diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp index db0cb8a31084952424325034b047ef7a30d9455d..b09487435adb2da7a8f3908006b798d55d47dfc5 100644 --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -22,6 +22,7 @@ #include "FormatTokenLexer.h" #include "IntegerLiteralSeparatorFixer.h" #include "NamespaceEndCommentsFixer.h" +#include "ObjCPropertyAttributeOrderFixer.h" #include "QualifierAlignmentFixer.h" #include "SortJavaScriptImports.h" #include "TokenAnalyzer.h" @@ -1039,6 +1040,8 @@ template <> struct MappingTraits { IO.mapOptional("ObjCBlockIndentWidth", Style.ObjCBlockIndentWidth); IO.mapOptional("ObjCBreakBeforeNestedBlockParam", Style.ObjCBreakBeforeNestedBlockParam); + IO.mapOptional("ObjCPropertyAttributeOrder", + Style.ObjCPropertyAttributeOrder); IO.mapOptional("ObjCSpaceAfterProperty", Style.ObjCSpaceAfterProperty); IO.mapOptional("ObjCSpaceBeforeProtocolList", Style.ObjCSpaceBeforeProtocolList); @@ -3711,6 +3714,13 @@ reformat(const FormatStyle &Style, StringRef Code, }); } + if (Style.Language == FormatStyle::LK_ObjC && + !Style.ObjCPropertyAttributeOrder.empty()) { + Passes.emplace_back([&](const Environment &Env) { + return ObjCPropertyAttributeOrderFixer(Env, Expanded).process(); + }); + } + if (Style.isJavaScript() && Style.JavaScriptQuotes != FormatStyle::JSQS_Leave) { Passes.emplace_back([&](const Environment &Env) { diff --git a/clang/lib/Format/ObjCPropertyAttributeOrderFixer.cpp b/clang/lib/Format/ObjCPropertyAttributeOrderFixer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c91d6251425eab508c3c196c66ba77774f9fabdb --- /dev/null +++ b/clang/lib/Format/ObjCPropertyAttributeOrderFixer.cpp @@ -0,0 +1,220 @@ +//===--- ObjCPropertyAttributeOrderFixer.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 +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file implements ObjCPropertyAttributeOrderFixer, a TokenAnalyzer that +/// adjusts the order of attributes in an ObjC `@property(...)` declaration, +/// depending on the style. +/// +//===----------------------------------------------------------------------===// + +#include "ObjCPropertyAttributeOrderFixer.h" + +#include + +namespace clang { +namespace format { + +ObjCPropertyAttributeOrderFixer::ObjCPropertyAttributeOrderFixer( + const Environment &Env, const FormatStyle &Style) + : TokenAnalyzer(Env, Style) { + // Create an "order priority" map to use to sort properties. + unsigned Index = 0; + for (const auto &Property : Style.ObjCPropertyAttributeOrder) + SortOrderMap[Property] = Index++; +} + +struct ObjCPropertyEntry { + StringRef Attribute; // eg, `readwrite` + StringRef Value; // eg, the `foo` of the attribute `getter=foo` +}; + +void ObjCPropertyAttributeOrderFixer::sortPropertyAttributes( + const SourceManager &SourceMgr, tooling::Replacements &Fixes, + const FormatToken *BeginTok, const FormatToken *EndTok) { + assert(BeginTok); + assert(EndTok); + assert(EndTok->Previous); + + // If there are zero or one tokens, nothing to do. + if (BeginTok == EndTok || BeginTok->Next == EndTok) + return; + + // Use a set to sort attributes and remove duplicates. + std::set Ordinals; + + // Create a "remapping index" on how to reorder the attributes. + SmallVector Indices; + + // Collect the attributes. + SmallVector PropertyAttributes; + bool HasDuplicates = false; + int Index = 0; + for (auto Tok = BeginTok; Tok != EndTok; Tok = Tok->Next) { + assert(Tok); + if (Tok->is(tok::comma)) { + // Ignore the comma separators. + continue; + } + + // Most attributes look like identifiers, but `class` is a keyword. + if (!Tok->isOneOf(tok::identifier, tok::kw_class)) { + // If we hit any other kind of token, just bail. + return; + } + + const StringRef Attribute{Tok->TokenText}; + StringRef Value; + + // Also handle `getter=getFoo` attributes. + // (Note: no check needed against `EndTok`, since its type is not + // BinaryOperator or Identifier) + assert(Tok->Next); + if (Tok->Next->is(tok::equal)) { + Tok = Tok->Next; + assert(Tok->Next); + if (Tok->Next->isNot(tok::identifier)) { + // If we hit any other kind of token, just bail. It's unusual/illegal. + return; + } + Tok = Tok->Next; + Value = Tok->TokenText; + } + + auto It = SortOrderMap.find(Attribute); + if (It == SortOrderMap.end()) + It = SortOrderMap.insert({Attribute, SortOrderMap.size()}).first; + + // Sort the indices based on the priority stored in `SortOrderMap`. + const auto Ordinal = It->second; + if (!Ordinals.insert(Ordinal).second) { + HasDuplicates = true; + continue; + } + + if (Ordinal >= Indices.size()) + Indices.resize(Ordinal + 1); + Indices[Ordinal] = Index++; + + // Memoize the attribute. + PropertyAttributes.push_back({Attribute, Value}); + } + + if (!HasDuplicates) { + // There's nothing to do unless there's more than one attribute. + if (PropertyAttributes.size() < 2) + return; + + int PrevIndex = -1; + bool IsSorted = true; + for (const auto Ordinal : Ordinals) { + const auto Index = Indices[Ordinal]; + if (Index < PrevIndex) { + IsSorted = false; + break; + } + assert(Index > PrevIndex); + PrevIndex = Index; + } + + // If the property order is already correct, then no fix-up is needed. + if (IsSorted) + return; + } + + // Generate the replacement text. + std::string NewText; + bool IsFirst = true; + for (const auto Ordinal : Ordinals) { + if (IsFirst) + IsFirst = false; + else + NewText += ", "; + + const auto &PropertyEntry = PropertyAttributes[Indices[Ordinal]]; + NewText += PropertyEntry.Attribute; + + if (const auto Value = PropertyEntry.Value; !Value.empty()) { + NewText += '='; + NewText += Value; + } + } + + auto Range = CharSourceRange::getCharRange( + BeginTok->getStartOfNonWhitespace(), EndTok->Previous->Tok.getEndLoc()); + auto Replacement = tooling::Replacement(SourceMgr, Range, NewText); + auto Err = Fixes.add(Replacement); + if (Err) { + llvm::errs() << "Error while reodering ObjC property attributes : " + << llvm::toString(std::move(Err)) << "\n"; + } +} + +void ObjCPropertyAttributeOrderFixer::analyzeObjCPropertyDecl( + const SourceManager &SourceMgr, const AdditionalKeywords &Keywords, + tooling::Replacements &Fixes, const FormatToken *Tok) { + assert(Tok); + + // Expect `property` to be the very next token or else just bail early. + const FormatToken *const PropertyTok = Tok->Next; + if (!PropertyTok || PropertyTok->isNot(Keywords.kw_property)) + return; + + // Expect the opening paren to be the next token or else just bail early. + const FormatToken *const LParenTok = PropertyTok->getNextNonComment(); + if (!LParenTok || LParenTok->isNot(tok::l_paren)) + return; + + // Get the matching right-paren, the bounds for property attributes. + const FormatToken *const RParenTok = LParenTok->MatchingParen; + if (!RParenTok) + return; + + sortPropertyAttributes(SourceMgr, Fixes, LParenTok->Next, RParenTok); +} + +std::pair +ObjCPropertyAttributeOrderFixer::analyze( + TokenAnnotator & /*Annotator*/, + SmallVectorImpl &AnnotatedLines, + FormatTokenLexer &Tokens) { + tooling::Replacements Fixes; + const AdditionalKeywords &Keywords = Tokens.getKeywords(); + const SourceManager &SourceMgr = Env.getSourceManager(); + AffectedRangeMgr.computeAffectedLines(AnnotatedLines); + + for (AnnotatedLine *Line : AnnotatedLines) { + assert(Line); + if (!Line->Affected || Line->Type != LT_ObjCProperty) + continue; + FormatToken *First = Line->First; + assert(First); + if (First->Finalized) + continue; + + const auto *Last = Line->Last; + + for (const auto *Tok = First; Tok != Last; Tok = Tok->Next) { + assert(Tok); + + // Skip until the `@` of a `@property` declaration. + if (Tok->isNot(TT_ObjCProperty)) + continue; + + analyzeObjCPropertyDecl(SourceMgr, Keywords, Fixes, Tok); + + // There are never two `@property` in a line (they are split + // by other passes), so this pass can break after just one. + break; + } + } + return {Fixes, 0}; +} + +} // namespace format +} // namespace clang diff --git a/clang/lib/Format/ObjCPropertyAttributeOrderFixer.h b/clang/lib/Format/ObjCPropertyAttributeOrderFixer.h new file mode 100644 index 0000000000000000000000000000000000000000..d9ce85d144afb2b683731c12ae768d198b5ebead --- /dev/null +++ b/clang/lib/Format/ObjCPropertyAttributeOrderFixer.h @@ -0,0 +1,51 @@ +//===--- ObjCPropertyAttributeOrderFixer.h ------------------------------*- C++ +//-*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file declares ObjCPropertyAttributeOrderFixer, a TokenAnalyzer that +/// adjusts the order of attributes in an ObjC `@property(...)` declaration, +/// depending on the style. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_FORMAT_OBJCPROPERTYATTRIBUTEORDERFIXER_H +#define LLVM_CLANG_LIB_FORMAT_OBJCPROPERTYATTRIBUTEORDERFIXER_H + +#include "TokenAnalyzer.h" + +namespace clang { +namespace format { + +class ObjCPropertyAttributeOrderFixer : public TokenAnalyzer { + llvm::StringMap SortOrderMap; + + void analyzeObjCPropertyDecl(const SourceManager &SourceMgr, + const AdditionalKeywords &Keywords, + tooling::Replacements &Fixes, + const FormatToken *Tok); + + void sortPropertyAttributes(const SourceManager &SourceMgr, + tooling::Replacements &Fixes, + const FormatToken *BeginTok, + const FormatToken *EndTok); + + std::pair + analyze(TokenAnnotator &Annotator, + SmallVectorImpl &AnnotatedLines, + FormatTokenLexer &Tokens) override; + +public: + ObjCPropertyAttributeOrderFixer(const Environment &Env, + const FormatStyle &Style); +}; + +} // end namespace format +} // end namespace clang + +#endif diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp index 86a68edc8abc0ac531c92ef03b8d4dd707083b75..eaccb5881ca30f5e381f20b1e17d36894041985d 100644 --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -4721,8 +4721,15 @@ bool TokenAnnotator::spaceRequiredBefore(const AnnotatedLine &Line, Left.isOneOf(TT_VerilogDimensionedTypeName, Keywords.kw_function)) { return true; } + // In a tagged union expression, there should be a space after the tag. + if (Right.isOneOf(tok::period, Keywords.kw_apostrophe) && + Keywords.isVerilogIdentifier(Left) && Left.getPreviousNonComment() && + Left.getPreviousNonComment()->is(Keywords.kw_tagged)) { + return true; + } // Don't add spaces between a casting type and the quote or repetition count - // and the brace. + // and the brace. The case of tagged union expressions is handled by the + // previous rule. if ((Right.is(Keywords.kw_apostrophe) || (Right.is(BK_BracedInit) && Right.is(tok::l_brace))) && !(Left.isOneOf(Keywords.kw_assign, Keywords.kw_unique) || diff --git a/clang/lib/Format/TokenAnnotator.h b/clang/lib/Format/TokenAnnotator.h index 58e2cf79f488fa42cc245d0eec03050abab164a4..05a6daa87d8034045bb23a6493839604d10913c5 100644 --- a/clang/lib/Format/TokenAnnotator.h +++ b/clang/lib/Format/TokenAnnotator.h @@ -156,6 +156,11 @@ public: return First->is(tok::comment) ? First->getNextNonComment() : First; } + FormatToken *getLastNonComment() const { + assert(Last); + return Last->is(tok::comment) ? Last->getPreviousNonComment() : Last; + } + FormatToken *First; FormatToken *Last; diff --git a/clang/lib/Format/UnwrappedLineFormatter.cpp b/clang/lib/Format/UnwrappedLineFormatter.cpp index 40730cd53529ed356fc5be78005f88e069bbe08d..b4930c2e4621dc200f1901f748de3cda3304201d 100644 --- a/clang/lib/Format/UnwrappedLineFormatter.cpp +++ b/clang/lib/Format/UnwrappedLineFormatter.cpp @@ -346,14 +346,10 @@ private: return false; // Check if the found line starts a record. - const FormatToken *LastNonComment = Line->Last; + const auto *LastNonComment = Line->getLastNonComment(); + // There must be another token (usually `{`), because we chose a + // non-PPDirective and non-comment line that has a smaller level. assert(LastNonComment); - if (LastNonComment->is(tok::comment)) { - LastNonComment = LastNonComment->getPreviousNonComment(); - // There must be another token (usually `{`), because we chose a - // non-PPDirective and non-comment line that has a smaller level. - assert(LastNonComment); - } return isRecordLBrace(*LastNonComment); } } @@ -363,12 +359,9 @@ private: bool MergeShortFunctions = ShouldMergeShortFunctions(); - const FormatToken *FirstNonComment = TheLine->First; - if (FirstNonComment->is(tok::comment)) { - FirstNonComment = FirstNonComment->getNextNonComment(); - if (!FirstNonComment) - return 0; - } + const auto *FirstNonComment = TheLine->getFirstNonComment(); + if (!FirstNonComment) + return 0; // FIXME: There are probably cases where we should use FirstNonComment // instead of TheLine->First. diff --git a/clang/lib/Format/UnwrappedLineParser.cpp b/clang/lib/Format/UnwrappedLineParser.cpp index a5a4419b98239e622bb0b819bced8a01909f3ef4..57126b8dfeac5de62da5b519c1ad0e0b3b5cdc62 100644 --- a/clang/lib/Format/UnwrappedLineParser.cpp +++ b/clang/lib/Format/UnwrappedLineParser.cpp @@ -47,8 +47,7 @@ void printLine(llvm::raw_ostream &OS, const UnwrappedLine &Line, OS << Prefix; NewLine = false; } - OS << I->Tok->Tok.getName() << "[" - << "T=" << (unsigned)I->Tok->getType() + OS << I->Tok->Tok.getName() << "[" << "T=" << (unsigned)I->Tok->getType() << ", OC=" << I->Tok->OriginalColumn << ", \"" << I->Tok->TokenText << "\"] "; for (SmallVectorImpl::const_iterator @@ -496,7 +495,7 @@ void UnwrappedLineParser::calculateBraceTypes(bool ExpectClassBody) { do { NextTok = Tokens->getNextToken(); } while (NextTok->is(tok::comment)); - while (NextTok->is(tok::hash)) { + while (NextTok->is(tok::hash) && !Line->InMacroBody) { NextTok = Tokens->getNextToken(); do { NextTok = Tokens->getNextToken(); diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index 5a8e4cf9843de2bc827ba64125caa3d178943d0b..56de0f75928ca4245aaa54e900e1636659099274 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -1861,6 +1861,20 @@ bool CompilerInvocation::ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, if (Args.hasArg(OPT_funified_lto)) Opts.PrepareForThinLTO = true; } + if (Arg *A = Args.getLastArg(options::OPT_ffat_lto_objects, + options::OPT_fno_fat_lto_objects)) { + if (A->getOption().matches(options::OPT_ffat_lto_objects)) { + if (Arg *Uni = Args.getLastArg(options::OPT_funified_lto, + options::OPT_fno_unified_lto)) { + if (Uni->getOption().matches(options::OPT_fno_unified_lto)) + Diags.Report(diag::err_drv_incompatible_options) + << A->getAsString(Args) << "-fno-unified-lto"; + } else + Diags.Report(diag::err_drv_argument_only_allowed_with) + << A->getAsString(Args) << "-funified-lto"; + } + } + if (Arg *A = Args.getLastArg(OPT_fthinlto_index_EQ)) { if (IK.getLanguage() != Language::LLVM_IR) Diags.Report(diag::err_drv_argument_only_allowed_with) @@ -4145,10 +4159,10 @@ bool CompilerInvocation::ParseLangArgs(LangOptions &Opts, ArgList &Args, if (Arg *A = Args.getLastArg(OPT_msign_return_address_key_EQ)) { StringRef SignKey = A->getValue(); if (!SignScope.empty() && !SignKey.empty()) { - if (SignKey.equals_insensitive("a_key")) + if (SignKey == "a_key") Opts.setSignReturnAddressKey( LangOptions::SignReturnAddressKeyKind::AKey); - else if (SignKey.equals_insensitive("b_key")) + else if (SignKey == "b_key") Opts.setSignReturnAddressKey( LangOptions::SignReturnAddressKeyKind::BKey); else @@ -4773,6 +4787,18 @@ std::string CompilerInvocation::getModuleHash() const { if (getCodeGenOpts().DebugTypeExtRefs) HBuilder.addRange(getCodeGenOpts().DebugPrefixMap); + // Extend the signature with the affecting debug options. + if (getHeaderSearchOpts().ModuleFormat == "obj") { +#define DEBUGOPT(Name, Bits, Default) HBuilder.add(CodeGenOpts->Name); +#define VALUE_DEBUGOPT(Name, Bits, Default) HBuilder.add(CodeGenOpts->Name); +#define ENUM_DEBUGOPT(Name, Type, Bits, Default) \ + HBuilder.add(static_cast(CodeGenOpts->get##Name())); +#define BENIGN_DEBUGOPT(Name, Bits, Default) +#define BENIGN_VALUE_DEBUGOPT(Name, Bits, Default) +#define BENIGN_ENUM_DEBUGOPT(Name, Type, Bits, Default) +#include "clang/Basic/DebugOptions.def" + } + // Extend the signature with the enabled sanitizers, if at least one is // enabled. Sanitizers which cannot affect AST generation aren't hashed. SanitizerSet SanHash = getLangOpts().Sanitize; @@ -4819,6 +4845,7 @@ std::vector CompilerInvocationBase::getCC1CommandLine() const { void CompilerInvocation::resetNonModularOptions() { getLangOpts().resetNonModularOptions(); getPreprocessorOpts().resetNonModularOptions(); + getCodeGenOpts().resetNonModularOptions(getHeaderSearchOpts().ModuleFormat); } void CompilerInvocation::clearImplicitModuleBuildOptions() { diff --git a/clang/lib/Headers/bmiintrin.h b/clang/lib/Headers/bmiintrin.h index ffb94bea639af6993956c11d4485e64df8759bd8..bc7c8a03c5e2a0de64a2a8bfe7e9a5b563ad7453 100644 --- a/clang/lib/Headers/bmiintrin.h +++ b/clang/lib/Headers/bmiintrin.h @@ -19,7 +19,7 @@ to use it as a potentially faster version of BSF. */ #define __RELAXED_FN_ATTRS __attribute__((__always_inline__, __nodebug__)) -#define _tzcnt_u16(a) (__tzcnt_u16((a))) +#define _tzcnt_u16 __tzcnt_u16 /// Counts the number of trailing zero bits in the operand. /// @@ -71,7 +71,7 @@ _mm_tzcnt_32(unsigned int __X) return (int)__builtin_ia32_tzcnt_u32(__X); } -#define _tzcnt_u32(a) (__tzcnt_u32((a))) +#define _tzcnt_u32 __tzcnt_u32 #ifdef __x86_64__ @@ -109,7 +109,7 @@ _mm_tzcnt_64(unsigned long long __X) return (long long)__builtin_ia32_tzcnt_u64(__X); } -#define _tzcnt_u64(a) (__tzcnt_u64((a))) +#define _tzcnt_u64 __tzcnt_u64 #endif /* __x86_64__ */ @@ -121,14 +121,14 @@ _mm_tzcnt_64(unsigned long long __X) /* Define the default attributes for the functions in this file. */ #define __DEFAULT_FN_ATTRS __attribute__((__always_inline__, __nodebug__, __target__("bmi"))) -#define _andn_u32(a, b) (__andn_u32((a), (b))) +#define _andn_u32 __andn_u32 /* _bextr_u32 != __bextr_u32 */ -#define _blsi_u32(a) (__blsi_u32((a))) +#define _blsi_u32 __blsi_u32 -#define _blsmsk_u32(a) (__blsmsk_u32((a))) +#define _blsmsk_u32 __blsmsk_u32 -#define _blsr_u32(a) (__blsr_u32((a))) +#define _blsr_u32 __blsr_u32 /// Performs a bitwise AND of the second operand with the one's /// complement of the first operand. @@ -272,14 +272,14 @@ __blsr_u32(unsigned int __X) #ifdef __x86_64__ -#define _andn_u64(a, b) (__andn_u64((a), (b))) +#define _andn_u64 __andn_u64 /* _bextr_u64 != __bextr_u64 */ -#define _blsi_u64(a) (__blsi_u64((a))) +#define _blsi_u64 __blsi_u64 -#define _blsmsk_u64(a) (__blsmsk_u64((a))) +#define _blsmsk_u64 __blsmsk_u64 -#define _blsr_u64(a) (__blsr_u64((a))) +#define _blsr_u64 __blsr_u64 /// Performs a bitwise AND of the second operand with the one's /// complement of the first operand. diff --git a/clang/lib/Headers/opencl-c-base.h b/clang/lib/Headers/opencl-c-base.h index d56e5ceae652ad51a00424f403523874da87da9b..2494f6213fc5695d9a4c3b70d29ba4f42e0ed7d0 100644 --- a/clang/lib/Headers/opencl-c-base.h +++ b/clang/lib/Headers/opencl-c-base.h @@ -819,64 +819,6 @@ int printf(__constant const char* st, ...) __attribute__((format(printf, 1, 2))) #endif // cl_intel_device_side_avc_motion_estimation -/** - * Compute square root. - * - * Provide inline implementations using the builtin so that we get appropriate - * !fpmath based on -cl-fp32-correctly-rounded-divide-sqrt, attached to - * llvm.sqrt. The implementation should still provide an external definition. - */ -#define __ovld __attribute__((overloadable)) -#define __cnfn __attribute__((const)) - -inline float __ovld __cnfn sqrt(float __x) { - return __builtin_elementwise_sqrt(__x); -} - -inline float2 __ovld __cnfn sqrt(float2 __x) { - return __builtin_elementwise_sqrt(__x); -} - -inline float3 __ovld __cnfn sqrt(float3 __x) { - return __builtin_elementwise_sqrt(__x); -} - -inline float4 __ovld __cnfn sqrt(float4 __x) { - return __builtin_elementwise_sqrt(__x); -} - -inline float8 __ovld __cnfn sqrt(float8 __x) { - return __builtin_elementwise_sqrt(__x); -} - -inline float16 __ovld __cnfn sqrt(float16 __x) { - return __builtin_elementwise_sqrt(__x); -} - -// We only really want to define the float variants here. However -// -fdeclare-opencl-builtins will not work if some overloads are already - // provided in the base header, so provide all overloads here. - -#ifdef cl_khr_fp64 -double __ovld __cnfn sqrt(double); -double2 __ovld __cnfn sqrt(double2); -double3 __ovld __cnfn sqrt(double3); -double4 __ovld __cnfn sqrt(double4); -double8 __ovld __cnfn sqrt(double8); -double16 __ovld __cnfn sqrt(double16); -#endif //cl_khr_fp64 -#ifdef cl_khr_fp16 -half __ovld __cnfn sqrt(half); -half2 __ovld __cnfn sqrt(half2); -half3 __ovld __cnfn sqrt(half3); -half4 __ovld __cnfn sqrt(half4); -half8 __ovld __cnfn sqrt(half8); -half16 __ovld __cnfn sqrt(half16); -#endif //cl_khr_fp16 - -#undef __cnfn -#undef __ovld - // Disable any extensions we may have enabled previously. #pragma OPENCL EXTENSION all : disable diff --git a/clang/lib/Headers/opencl-c.h b/clang/lib/Headers/opencl-c.h index 1efbbf8f8ee6a01b024986bcff847a1cb8cc17a8..288bb18bc654ebc8208ec6b56fab4fa45019a733 100644 --- a/clang/lib/Headers/opencl-c.h +++ b/clang/lib/Headers/opencl-c.h @@ -8496,6 +8496,32 @@ half8 __ovld __cnfn sinpi(half8); half16 __ovld __cnfn sinpi(half16); #endif //cl_khr_fp16 +/** + * Compute square root. + */ +float __ovld __cnfn sqrt(float); +float2 __ovld __cnfn sqrt(float2); +float3 __ovld __cnfn sqrt(float3); +float4 __ovld __cnfn sqrt(float4); +float8 __ovld __cnfn sqrt(float8); +float16 __ovld __cnfn sqrt(float16); +#ifdef cl_khr_fp64 +double __ovld __cnfn sqrt(double); +double2 __ovld __cnfn sqrt(double2); +double3 __ovld __cnfn sqrt(double3); +double4 __ovld __cnfn sqrt(double4); +double8 __ovld __cnfn sqrt(double8); +double16 __ovld __cnfn sqrt(double16); +#endif //cl_khr_fp64 +#ifdef cl_khr_fp16 +half __ovld __cnfn sqrt(half); +half2 __ovld __cnfn sqrt(half2); +half3 __ovld __cnfn sqrt(half3); +half4 __ovld __cnfn sqrt(half4); +half8 __ovld __cnfn sqrt(half8); +half16 __ovld __cnfn sqrt(half16); +#endif //cl_khr_fp16 + /** * Compute tangent. */ diff --git a/clang/lib/Headers/vecintrin.h b/clang/lib/Headers/vecintrin.h index ec1dbfd015f6ef4d20fe344afb870409bfa6fba6..ecfd6cd1a2f87d87c38d228e6ef67cc2aef2e78d 100644 --- a/clang/lib/Headers/vecintrin.h +++ b/clang/lib/Headers/vecintrin.h @@ -1543,7 +1543,7 @@ vec_load_len(const double *__ptr, unsigned int __len) { #if __ARCH__ >= 12 static inline __ATTRS_ai __vector unsigned char vec_load_len_r(const unsigned char *__ptr, unsigned int __len) { - return (__vector unsigned char)__builtin_s390_vlrl(__len, __ptr); + return (__vector unsigned char)__builtin_s390_vlrlr(__len, __ptr); } #endif @@ -1617,7 +1617,7 @@ vec_store_len(__vector double __vec, double *__ptr, static inline __ATTRS_ai void vec_store_len_r(__vector unsigned char __vec, unsigned char *__ptr, unsigned int __len) { - __builtin_s390_vstrl((__vector signed char)__vec, __len, __ptr); + __builtin_s390_vstrlr((__vector signed char)__vec, __len, __ptr); } #endif @@ -2689,7 +2689,8 @@ vec_cmplt(__vector double __a, __vector double __b) { static inline __ATTRS_o_ai int vec_all_eq(__vector signed char __a, __vector signed char __b) { int __cc; - __builtin_s390_vceqbs(__a, __b, &__cc); + __builtin_s390_vceqbs((__vector unsigned char)__a, + (__vector unsigned char)__b, &__cc); return __cc == 0; } @@ -2697,7 +2698,8 @@ vec_all_eq(__vector signed char __a, __vector signed char __b) { static inline __ATTRS_o_ai int vec_all_eq(__vector signed char __a, __vector __bool char __b) { int __cc; - __builtin_s390_vceqbs(__a, (__vector signed char)__b, &__cc); + __builtin_s390_vceqbs((__vector unsigned char)__a, + (__vector unsigned char)__b, &__cc); return __cc == 0; } @@ -2705,15 +2707,15 @@ vec_all_eq(__vector signed char __a, __vector __bool char __b) { static inline __ATTRS_o_ai int vec_all_eq(__vector __bool char __a, __vector signed char __b) { int __cc; - __builtin_s390_vceqbs((__vector signed char)__a, __b, &__cc); + __builtin_s390_vceqbs((__vector unsigned char)__a, + (__vector unsigned char)__b, &__cc); return __cc == 0; } static inline __ATTRS_o_ai int vec_all_eq(__vector unsigned char __a, __vector unsigned char __b) { int __cc; - __builtin_s390_vceqbs((__vector signed char)__a, - (__vector signed char)__b, &__cc); + __builtin_s390_vceqbs(__a, __b, &__cc); return __cc == 0; } @@ -2721,8 +2723,7 @@ vec_all_eq(__vector unsigned char __a, __vector unsigned char __b) { static inline __ATTRS_o_ai int vec_all_eq(__vector unsigned char __a, __vector __bool char __b) { int __cc; - __builtin_s390_vceqbs((__vector signed char)__a, - (__vector signed char)__b, &__cc); + __builtin_s390_vceqbs(__a, (__vector unsigned char)__b, &__cc); return __cc == 0; } @@ -2730,23 +2731,23 @@ vec_all_eq(__vector unsigned char __a, __vector __bool char __b) { static inline __ATTRS_o_ai int vec_all_eq(__vector __bool char __a, __vector unsigned char __b) { int __cc; - __builtin_s390_vceqbs((__vector signed char)__a, - (__vector signed char)__b, &__cc); + __builtin_s390_vceqbs((__vector unsigned char)__a, __b, &__cc); return __cc == 0; } static inline __ATTRS_o_ai int vec_all_eq(__vector __bool char __a, __vector __bool char __b) { int __cc; - __builtin_s390_vceqbs((__vector signed char)__a, - (__vector signed char)__b, &__cc); + __builtin_s390_vceqbs((__vector unsigned char)__a, + (__vector unsigned char)__b, &__cc); return __cc == 0; } static inline __ATTRS_o_ai int vec_all_eq(__vector signed short __a, __vector signed short __b) { int __cc; - __builtin_s390_vceqhs(__a, __b, &__cc); + __builtin_s390_vceqhs((__vector unsigned short)__a, + (__vector unsigned short)__b, &__cc); return __cc == 0; } @@ -2754,7 +2755,8 @@ vec_all_eq(__vector signed short __a, __vector signed short __b) { static inline __ATTRS_o_ai int vec_all_eq(__vector signed short __a, __vector __bool short __b) { int __cc; - __builtin_s390_vceqhs(__a, (__vector signed short)__b, &__cc); + __builtin_s390_vceqhs((__vector unsigned short)__a, + (__vector unsigned short)__b, &__cc); return __cc == 0; } @@ -2762,15 +2764,15 @@ vec_all_eq(__vector signed short __a, __vector __bool short __b) { static inline __ATTRS_o_ai int vec_all_eq(__vector __bool short __a, __vector signed short __b) { int __cc; - __builtin_s390_vceqhs((__vector signed short)__a, __b, &__cc); + __builtin_s390_vceqhs((__vector unsigned short)__a, + (__vector unsigned short)__b, &__cc); return __cc == 0; } static inline __ATTRS_o_ai int vec_all_eq(__vector unsigned short __a, __vector unsigned short __b) { int __cc; - __builtin_s390_vceqhs((__vector signed short)__a, - (__vector signed short)__b, &__cc); + __builtin_s390_vceqhs(__a, __b, &__cc); return __cc == 0; } @@ -2778,8 +2780,7 @@ vec_all_eq(__vector unsigned short __a, __vector unsigned short __b) { static inline __ATTRS_o_ai int vec_all_eq(__vector unsigned short __a, __vector __bool short __b) { int __cc; - __builtin_s390_vceqhs((__vector signed short)__a, - (__vector signed short)__b, &__cc); + __builtin_s390_vceqhs(__a, (__vector unsigned short)__b, &__cc); return __cc == 0; } @@ -2787,23 +2788,23 @@ vec_all_eq(__vector unsigned short __a, __vector __bool short __b) { static inline __ATTRS_o_ai int vec_all_eq(__vector __bool short __a, __vector unsigned short __b) { int __cc; - __builtin_s390_vceqhs((__vector signed short)__a, - (__vector signed short)__b, &__cc); + __builtin_s390_vceqhs((__vector unsigned short)__a, __b, &__cc); return __cc == 0; } static inline __ATTRS_o_ai int vec_all_eq(__vector __bool short __a, __vector __bool short __b) { int __cc; - __builtin_s390_vceqhs((__vector signed short)__a, - (__vector signed short)__b, &__cc); + __builtin_s390_vceqhs((__vector unsigned short)__a, + (__vector unsigned short)__b, &__cc); return __cc == 0; } static inline __ATTRS_o_ai int vec_all_eq(__vector signed int __a, __vector signed int __b) { int __cc; - __builtin_s390_vceqfs(__a, __b, &__cc); + __builtin_s390_vceqfs((__vector unsigned int)__a, + (__vector unsigned int)__b, &__cc); return __cc == 0; } @@ -2811,7 +2812,8 @@ vec_all_eq(__vector signed int __a, __vector signed int __b) { static inline __ATTRS_o_ai int vec_all_eq(__vector signed int __a, __vector __bool int __b) { int __cc; - __builtin_s390_vceqfs(__a, (__vector signed int)__b, &__cc); + __builtin_s390_vceqfs((__vector unsigned int)__a, + (__vector unsigned int)__b, &__cc); return __cc == 0; } @@ -2819,15 +2821,15 @@ vec_all_eq(__vector signed int __a, __vector __bool int __b) { static inline __ATTRS_o_ai int vec_all_eq(__vector __bool int __a, __vector signed int __b) { int __cc; - __builtin_s390_vceqfs((__vector signed int)__a, __b, &__cc); + __builtin_s390_vceqfs((__vector unsigned int)__a, + (__vector unsigned int)__b, &__cc); return __cc == 0; } static inline __ATTRS_o_ai int vec_all_eq(__vector unsigned int __a, __vector unsigned int __b) { int __cc; - __builtin_s390_vceqfs((__vector signed int)__a, - (__vector signed int)__b, &__cc); + __builtin_s390_vceqfs(__a, __b, &__cc); return __cc == 0; } @@ -2835,8 +2837,7 @@ vec_all_eq(__vector unsigned int __a, __vector unsigned int __b) { static inline __ATTRS_o_ai int vec_all_eq(__vector unsigned int __a, __vector __bool int __b) { int __cc; - __builtin_s390_vceqfs((__vector signed int)__a, - (__vector signed int)__b, &__cc); + __builtin_s390_vceqfs(__a, (__vector unsigned int)__b, &__cc); return __cc == 0; } @@ -2844,23 +2845,23 @@ vec_all_eq(__vector unsigned int __a, __vector __bool int __b) { static inline __ATTRS_o_ai int vec_all_eq(__vector __bool int __a, __vector unsigned int __b) { int __cc; - __builtin_s390_vceqfs((__vector signed int)__a, - (__vector signed int)__b, &__cc); + __builtin_s390_vceqfs((__vector unsigned int)__a, __b, &__cc); return __cc == 0; } static inline __ATTRS_o_ai int vec_all_eq(__vector __bool int __a, __vector __bool int __b) { int __cc; - __builtin_s390_vceqfs((__vector signed int)__a, - (__vector signed int)__b, &__cc); + __builtin_s390_vceqfs((__vector unsigned int)__a, + (__vector unsigned int)__b, &__cc); return __cc == 0; } static inline __ATTRS_o_ai int vec_all_eq(__vector signed long long __a, __vector signed long long __b) { int __cc; - __builtin_s390_vceqgs(__a, __b, &__cc); + __builtin_s390_vceqgs((__vector unsigned long long)__a, + (__vector unsigned long long)__b, &__cc); return __cc == 0; } @@ -2868,7 +2869,8 @@ vec_all_eq(__vector signed long long __a, __vector signed long long __b) { static inline __ATTRS_o_ai int vec_all_eq(__vector signed long long __a, __vector __bool long long __b) { int __cc; - __builtin_s390_vceqgs(__a, (__vector signed long long)__b, &__cc); + __builtin_s390_vceqgs((__vector unsigned long long)__a, + (__vector unsigned long long)__b, &__cc); return __cc == 0; } @@ -2876,15 +2878,15 @@ vec_all_eq(__vector signed long long __a, __vector __bool long long __b) { static inline __ATTRS_o_ai int vec_all_eq(__vector __bool long long __a, __vector signed long long __b) { int __cc; - __builtin_s390_vceqgs((__vector signed long long)__a, __b, &__cc); + __builtin_s390_vceqgs((__vector unsigned long long)__a, + (__vector unsigned long long)__b, &__cc); return __cc == 0; } static inline __ATTRS_o_ai int vec_all_eq(__vector unsigned long long __a, __vector unsigned long long __b) { int __cc; - __builtin_s390_vceqgs((__vector signed long long)__a, - (__vector signed long long)__b, &__cc); + __builtin_s390_vceqgs(__a, __b, &__cc); return __cc == 0; } @@ -2892,8 +2894,7 @@ vec_all_eq(__vector unsigned long long __a, __vector unsigned long long __b) { static inline __ATTRS_o_ai int vec_all_eq(__vector unsigned long long __a, __vector __bool long long __b) { int __cc; - __builtin_s390_vceqgs((__vector signed long long)__a, - (__vector signed long long)__b, &__cc); + __builtin_s390_vceqgs(__a, (__vector unsigned long long)__b, &__cc); return __cc == 0; } @@ -2901,16 +2902,15 @@ vec_all_eq(__vector unsigned long long __a, __vector __bool long long __b) { static inline __ATTRS_o_ai int vec_all_eq(__vector __bool long long __a, __vector unsigned long long __b) { int __cc; - __builtin_s390_vceqgs((__vector signed long long)__a, - (__vector signed long long)__b, &__cc); + __builtin_s390_vceqgs((__vector unsigned long long)__a, __b, &__cc); return __cc == 0; } static inline __ATTRS_o_ai int vec_all_eq(__vector __bool long long __a, __vector __bool long long __b) { int __cc; - __builtin_s390_vceqgs((__vector signed long long)__a, - (__vector signed long long)__b, &__cc); + __builtin_s390_vceqgs((__vector unsigned long long)__a, + (__vector unsigned long long)__b, &__cc); return __cc == 0; } @@ -2935,7 +2935,8 @@ vec_all_eq(__vector double __a, __vector double __b) { static inline __ATTRS_o_ai int vec_all_ne(__vector signed char __a, __vector signed char __b) { int __cc; - __builtin_s390_vceqbs(__a, __b, &__cc); + __builtin_s390_vceqbs((__vector unsigned char)__a, + (__vector unsigned char)__b, &__cc); return __cc == 3; } @@ -2943,7 +2944,8 @@ vec_all_ne(__vector signed char __a, __vector signed char __b) { static inline __ATTRS_o_ai int vec_all_ne(__vector signed char __a, __vector __bool char __b) { int __cc; - __builtin_s390_vceqbs(__a, (__vector signed char)__b, &__cc); + __builtin_s390_vceqbs((__vector unsigned char)__a, + (__vector unsigned char)__b, &__cc); return __cc == 3; } @@ -2951,15 +2953,16 @@ vec_all_ne(__vector signed char __a, __vector __bool char __b) { static inline __ATTRS_o_ai int vec_all_ne(__vector __bool char __a, __vector signed char __b) { int __cc; - __builtin_s390_vceqbs((__vector signed char)__a, __b, &__cc); + __builtin_s390_vceqbs((__vector unsigned char)__a, + (__vector unsigned char)__b, &__cc); return __cc == 3; } static inline __ATTRS_o_ai int vec_all_ne(__vector unsigned char __a, __vector unsigned char __b) { int __cc; - __builtin_s390_vceqbs((__vector signed char)__a, - (__vector signed char)__b, &__cc); + __builtin_s390_vceqbs((__vector unsigned char)__a, + (__vector unsigned char)__b, &__cc); return __cc == 3; } @@ -2967,8 +2970,7 @@ vec_all_ne(__vector unsigned char __a, __vector unsigned char __b) { static inline __ATTRS_o_ai int vec_all_ne(__vector unsigned char __a, __vector __bool char __b) { int __cc; - __builtin_s390_vceqbs((__vector signed char)__a, - (__vector signed char)__b, &__cc); + __builtin_s390_vceqbs(__a, (__vector unsigned char)__b, &__cc); return __cc == 3; } @@ -2976,23 +2978,23 @@ vec_all_ne(__vector unsigned char __a, __vector __bool char __b) { static inline __ATTRS_o_ai int vec_all_ne(__vector __bool char __a, __vector unsigned char __b) { int __cc; - __builtin_s390_vceqbs((__vector signed char)__a, - (__vector signed char)__b, &__cc); + __builtin_s390_vceqbs((__vector unsigned char)__a, __b, &__cc); return __cc == 3; } static inline __ATTRS_o_ai int vec_all_ne(__vector __bool char __a, __vector __bool char __b) { int __cc; - __builtin_s390_vceqbs((__vector signed char)__a, - (__vector signed char)__b, &__cc); + __builtin_s390_vceqbs((__vector unsigned char)__a, + (__vector unsigned char)__b, &__cc); return __cc == 3; } static inline __ATTRS_o_ai int vec_all_ne(__vector signed short __a, __vector signed short __b) { int __cc; - __builtin_s390_vceqhs(__a, __b, &__cc); + __builtin_s390_vceqhs((__vector unsigned short)__a, + (__vector unsigned short)__b, &__cc); return __cc == 3; } @@ -3000,7 +3002,8 @@ vec_all_ne(__vector signed short __a, __vector signed short __b) { static inline __ATTRS_o_ai int vec_all_ne(__vector signed short __a, __vector __bool short __b) { int __cc; - __builtin_s390_vceqhs(__a, (__vector signed short)__b, &__cc); + __builtin_s390_vceqhs((__vector unsigned short)__a, + (__vector unsigned short)__b, &__cc); return __cc == 3; } @@ -3008,15 +3011,15 @@ vec_all_ne(__vector signed short __a, __vector __bool short __b) { static inline __ATTRS_o_ai int vec_all_ne(__vector __bool short __a, __vector signed short __b) { int __cc; - __builtin_s390_vceqhs((__vector signed short)__a, __b, &__cc); + __builtin_s390_vceqhs((__vector unsigned short)__a, + (__vector unsigned short)__b, &__cc); return __cc == 3; } static inline __ATTRS_o_ai int vec_all_ne(__vector unsigned short __a, __vector unsigned short __b) { int __cc; - __builtin_s390_vceqhs((__vector signed short)__a, - (__vector signed short)__b, &__cc); + __builtin_s390_vceqhs(__a, __b, &__cc); return __cc == 3; } @@ -3024,8 +3027,7 @@ vec_all_ne(__vector unsigned short __a, __vector unsigned short __b) { static inline __ATTRS_o_ai int vec_all_ne(__vector unsigned short __a, __vector __bool short __b) { int __cc; - __builtin_s390_vceqhs((__vector signed short)__a, - (__vector signed short)__b, &__cc); + __builtin_s390_vceqhs(__a, (__vector unsigned short)__b, &__cc); return __cc == 3; } @@ -3033,23 +3035,23 @@ vec_all_ne(__vector unsigned short __a, __vector __bool short __b) { static inline __ATTRS_o_ai int vec_all_ne(__vector __bool short __a, __vector unsigned short __b) { int __cc; - __builtin_s390_vceqhs((__vector signed short)__a, - (__vector signed short)__b, &__cc); + __builtin_s390_vceqhs((__vector unsigned short)__a, __b, &__cc); return __cc == 3; } static inline __ATTRS_o_ai int vec_all_ne(__vector __bool short __a, __vector __bool short __b) { int __cc; - __builtin_s390_vceqhs((__vector signed short)__a, - (__vector signed short)__b, &__cc); + __builtin_s390_vceqhs((__vector unsigned short)__a, + (__vector unsigned short)__b, &__cc); return __cc == 3; } static inline __ATTRS_o_ai int vec_all_ne(__vector signed int __a, __vector signed int __b) { int __cc; - __builtin_s390_vceqfs(__a, __b, &__cc); + __builtin_s390_vceqfs((__vector unsigned int)__a, + (__vector unsigned int)__b, &__cc); return __cc == 3; } @@ -3057,7 +3059,8 @@ vec_all_ne(__vector signed int __a, __vector signed int __b) { static inline __ATTRS_o_ai int vec_all_ne(__vector signed int __a, __vector __bool int __b) { int __cc; - __builtin_s390_vceqfs(__a, (__vector signed int)__b, &__cc); + __builtin_s390_vceqfs((__vector unsigned int)__a, + (__vector unsigned int)__b, &__cc); return __cc == 3; } @@ -3065,15 +3068,15 @@ vec_all_ne(__vector signed int __a, __vector __bool int __b) { static inline __ATTRS_o_ai int vec_all_ne(__vector __bool int __a, __vector signed int __b) { int __cc; - __builtin_s390_vceqfs((__vector signed int)__a, __b, &__cc); + __builtin_s390_vceqfs((__vector unsigned int)__a, + (__vector unsigned int)__b, &__cc); return __cc == 3; } static inline __ATTRS_o_ai int vec_all_ne(__vector unsigned int __a, __vector unsigned int __b) { int __cc; - __builtin_s390_vceqfs((__vector signed int)__a, - (__vector signed int)__b, &__cc); + __builtin_s390_vceqfs(__a, __b, &__cc); return __cc == 3; } @@ -3081,8 +3084,7 @@ vec_all_ne(__vector unsigned int __a, __vector unsigned int __b) { static inline __ATTRS_o_ai int vec_all_ne(__vector unsigned int __a, __vector __bool int __b) { int __cc; - __builtin_s390_vceqfs((__vector signed int)__a, - (__vector signed int)__b, &__cc); + __builtin_s390_vceqfs(__a, (__vector unsigned int)__b, &__cc); return __cc == 3; } @@ -3090,23 +3092,23 @@ vec_all_ne(__vector unsigned int __a, __vector __bool int __b) { static inline __ATTRS_o_ai int vec_all_ne(__vector __bool int __a, __vector unsigned int __b) { int __cc; - __builtin_s390_vceqfs((__vector signed int)__a, - (__vector signed int)__b, &__cc); + __builtin_s390_vceqfs((__vector unsigned int)__a, __b, &__cc); return __cc == 3; } static inline __ATTRS_o_ai int vec_all_ne(__vector __bool int __a, __vector __bool int __b) { int __cc; - __builtin_s390_vceqfs((__vector signed int)__a, - (__vector signed int)__b, &__cc); + __builtin_s390_vceqfs((__vector unsigned int)__a, + (__vector unsigned int)__b, &__cc); return __cc == 3; } static inline __ATTRS_o_ai int vec_all_ne(__vector signed long long __a, __vector signed long long __b) { int __cc; - __builtin_s390_vceqgs(__a, __b, &__cc); + __builtin_s390_vceqgs((__vector unsigned long long)__a, + (__vector unsigned long long)__b, &__cc); return __cc == 3; } @@ -3114,7 +3116,8 @@ vec_all_ne(__vector signed long long __a, __vector signed long long __b) { static inline __ATTRS_o_ai int vec_all_ne(__vector signed long long __a, __vector __bool long long __b) { int __cc; - __builtin_s390_vceqgs(__a, (__vector signed long long)__b, &__cc); + __builtin_s390_vceqgs((__vector unsigned long long)__a, + (__vector unsigned long long)__b, &__cc); return __cc == 3; } @@ -3122,15 +3125,15 @@ vec_all_ne(__vector signed long long __a, __vector __bool long long __b) { static inline __ATTRS_o_ai int vec_all_ne(__vector __bool long long __a, __vector signed long long __b) { int __cc; - __builtin_s390_vceqgs((__vector signed long long)__a, __b, &__cc); + __builtin_s390_vceqgs((__vector unsigned long long)__a, + (__vector unsigned long long)__b, &__cc); return __cc == 3; } static inline __ATTRS_o_ai int vec_all_ne(__vector unsigned long long __a, __vector unsigned long long __b) { int __cc; - __builtin_s390_vceqgs((__vector signed long long)__a, - (__vector signed long long)__b, &__cc); + __builtin_s390_vceqgs(__a, __b, &__cc); return __cc == 3; } @@ -3138,8 +3141,7 @@ vec_all_ne(__vector unsigned long long __a, __vector unsigned long long __b) { static inline __ATTRS_o_ai int vec_all_ne(__vector unsigned long long __a, __vector __bool long long __b) { int __cc; - __builtin_s390_vceqgs((__vector signed long long)__a, - (__vector signed long long)__b, &__cc); + __builtin_s390_vceqgs(__a, (__vector unsigned long long)__b, &__cc); return __cc == 3; } @@ -3147,16 +3149,15 @@ vec_all_ne(__vector unsigned long long __a, __vector __bool long long __b) { static inline __ATTRS_o_ai int vec_all_ne(__vector __bool long long __a, __vector unsigned long long __b) { int __cc; - __builtin_s390_vceqgs((__vector signed long long)__a, - (__vector signed long long)__b, &__cc); + __builtin_s390_vceqgs((__vector unsigned long long)__a, __b, &__cc); return __cc == 3; } static inline __ATTRS_o_ai int vec_all_ne(__vector __bool long long __a, __vector __bool long long __b) { int __cc; - __builtin_s390_vceqgs((__vector signed long long)__a, - (__vector signed long long)__b, &__cc); + __builtin_s390_vceqgs((__vector unsigned long long)__a, + (__vector unsigned long long)__b, &__cc); return __cc == 3; } @@ -4241,7 +4242,8 @@ vec_all_numeric(__vector double __a) { static inline __ATTRS_o_ai int vec_any_eq(__vector signed char __a, __vector signed char __b) { int __cc; - __builtin_s390_vceqbs(__a, __b, &__cc); + __builtin_s390_vceqbs((__vector unsigned char)__a, + (__vector unsigned char)__b, &__cc); return __cc <= 1; } @@ -4249,7 +4251,8 @@ vec_any_eq(__vector signed char __a, __vector signed char __b) { static inline __ATTRS_o_ai int vec_any_eq(__vector signed char __a, __vector __bool char __b) { int __cc; - __builtin_s390_vceqbs(__a, (__vector signed char)__b, &__cc); + __builtin_s390_vceqbs((__vector unsigned char)__a, + (__vector unsigned char)__b, &__cc); return __cc <= 1; } @@ -4257,15 +4260,15 @@ vec_any_eq(__vector signed char __a, __vector __bool char __b) { static inline __ATTRS_o_ai int vec_any_eq(__vector __bool char __a, __vector signed char __b) { int __cc; - __builtin_s390_vceqbs((__vector signed char)__a, __b, &__cc); + __builtin_s390_vceqbs((__vector unsigned char)__a, + (__vector unsigned char)__b, &__cc); return __cc <= 1; } static inline __ATTRS_o_ai int vec_any_eq(__vector unsigned char __a, __vector unsigned char __b) { int __cc; - __builtin_s390_vceqbs((__vector signed char)__a, - (__vector signed char)__b, &__cc); + __builtin_s390_vceqbs(__a, __b, &__cc); return __cc <= 1; } @@ -4273,8 +4276,7 @@ vec_any_eq(__vector unsigned char __a, __vector unsigned char __b) { static inline __ATTRS_o_ai int vec_any_eq(__vector unsigned char __a, __vector __bool char __b) { int __cc; - __builtin_s390_vceqbs((__vector signed char)__a, - (__vector signed char)__b, &__cc); + __builtin_s390_vceqbs(__a, (__vector unsigned char)__b, &__cc); return __cc <= 1; } @@ -4282,23 +4284,23 @@ vec_any_eq(__vector unsigned char __a, __vector __bool char __b) { static inline __ATTRS_o_ai int vec_any_eq(__vector __bool char __a, __vector unsigned char __b) { int __cc; - __builtin_s390_vceqbs((__vector signed char)__a, - (__vector signed char)__b, &__cc); + __builtin_s390_vceqbs((__vector unsigned char)__a, __b, &__cc); return __cc <= 1; } static inline __ATTRS_o_ai int vec_any_eq(__vector __bool char __a, __vector __bool char __b) { int __cc; - __builtin_s390_vceqbs((__vector signed char)__a, - (__vector signed char)__b, &__cc); + __builtin_s390_vceqbs((__vector unsigned char)__a, + (__vector unsigned char)__b, &__cc); return __cc <= 1; } static inline __ATTRS_o_ai int vec_any_eq(__vector signed short __a, __vector signed short __b) { int __cc; - __builtin_s390_vceqhs(__a, __b, &__cc); + __builtin_s390_vceqhs((__vector unsigned short)__a, + (__vector unsigned short)__b, &__cc); return __cc <= 1; } @@ -4306,7 +4308,8 @@ vec_any_eq(__vector signed short __a, __vector signed short __b) { static inline __ATTRS_o_ai int vec_any_eq(__vector signed short __a, __vector __bool short __b) { int __cc; - __builtin_s390_vceqhs(__a, (__vector signed short)__b, &__cc); + __builtin_s390_vceqhs((__vector unsigned short)__a, + (__vector unsigned short)__b, &__cc); return __cc <= 1; } @@ -4314,15 +4317,15 @@ vec_any_eq(__vector signed short __a, __vector __bool short __b) { static inline __ATTRS_o_ai int vec_any_eq(__vector __bool short __a, __vector signed short __b) { int __cc; - __builtin_s390_vceqhs((__vector signed short)__a, __b, &__cc); + __builtin_s390_vceqhs((__vector unsigned short)__a, + (__vector unsigned short)__b, &__cc); return __cc <= 1; } static inline __ATTRS_o_ai int vec_any_eq(__vector unsigned short __a, __vector unsigned short __b) { int __cc; - __builtin_s390_vceqhs((__vector signed short)__a, - (__vector signed short)__b, &__cc); + __builtin_s390_vceqhs(__a, __b, &__cc); return __cc <= 1; } @@ -4330,8 +4333,7 @@ vec_any_eq(__vector unsigned short __a, __vector unsigned short __b) { static inline __ATTRS_o_ai int vec_any_eq(__vector unsigned short __a, __vector __bool short __b) { int __cc; - __builtin_s390_vceqhs((__vector signed short)__a, - (__vector signed short)__b, &__cc); + __builtin_s390_vceqhs(__a, (__vector unsigned short)__b, &__cc); return __cc <= 1; } @@ -4339,23 +4341,23 @@ vec_any_eq(__vector unsigned short __a, __vector __bool short __b) { static inline __ATTRS_o_ai int vec_any_eq(__vector __bool short __a, __vector unsigned short __b) { int __cc; - __builtin_s390_vceqhs((__vector signed short)__a, - (__vector signed short)__b, &__cc); + __builtin_s390_vceqhs((__vector unsigned short)__a, __b, &__cc); return __cc <= 1; } static inline __ATTRS_o_ai int vec_any_eq(__vector __bool short __a, __vector __bool short __b) { int __cc; - __builtin_s390_vceqhs((__vector signed short)__a, - (__vector signed short)__b, &__cc); + __builtin_s390_vceqhs((__vector unsigned short)__a, + (__vector unsigned short)__b, &__cc); return __cc <= 1; } static inline __ATTRS_o_ai int vec_any_eq(__vector signed int __a, __vector signed int __b) { int __cc; - __builtin_s390_vceqfs(__a, __b, &__cc); + __builtin_s390_vceqfs((__vector unsigned int)__a, + (__vector unsigned int)__b, &__cc); return __cc <= 1; } @@ -4363,7 +4365,8 @@ vec_any_eq(__vector signed int __a, __vector signed int __b) { static inline __ATTRS_o_ai int vec_any_eq(__vector signed int __a, __vector __bool int __b) { int __cc; - __builtin_s390_vceqfs(__a, (__vector signed int)__b, &__cc); + __builtin_s390_vceqfs((__vector unsigned int)__a, + (__vector unsigned int)__b, &__cc); return __cc <= 1; } @@ -4371,15 +4374,15 @@ vec_any_eq(__vector signed int __a, __vector __bool int __b) { static inline __ATTRS_o_ai int vec_any_eq(__vector __bool int __a, __vector signed int __b) { int __cc; - __builtin_s390_vceqfs((__vector signed int)__a, __b, &__cc); + __builtin_s390_vceqfs((__vector unsigned int)__a, + (__vector unsigned int)__b, &__cc); return __cc <= 1; } static inline __ATTRS_o_ai int vec_any_eq(__vector unsigned int __a, __vector unsigned int __b) { int __cc; - __builtin_s390_vceqfs((__vector signed int)__a, - (__vector signed int)__b, &__cc); + __builtin_s390_vceqfs(__a, __b, &__cc); return __cc <= 1; } @@ -4387,8 +4390,7 @@ vec_any_eq(__vector unsigned int __a, __vector unsigned int __b) { static inline __ATTRS_o_ai int vec_any_eq(__vector unsigned int __a, __vector __bool int __b) { int __cc; - __builtin_s390_vceqfs((__vector signed int)__a, - (__vector signed int)__b, &__cc); + __builtin_s390_vceqfs(__a, (__vector unsigned int)__b, &__cc); return __cc <= 1; } @@ -4396,23 +4398,23 @@ vec_any_eq(__vector unsigned int __a, __vector __bool int __b) { static inline __ATTRS_o_ai int vec_any_eq(__vector __bool int __a, __vector unsigned int __b) { int __cc; - __builtin_s390_vceqfs((__vector signed int)__a, - (__vector signed int)__b, &__cc); + __builtin_s390_vceqfs((__vector unsigned int)__a, __b, &__cc); return __cc <= 1; } static inline __ATTRS_o_ai int vec_any_eq(__vector __bool int __a, __vector __bool int __b) { int __cc; - __builtin_s390_vceqfs((__vector signed int)__a, - (__vector signed int)__b, &__cc); + __builtin_s390_vceqfs((__vector unsigned int)__a, + (__vector unsigned int)__b, &__cc); return __cc <= 1; } static inline __ATTRS_o_ai int vec_any_eq(__vector signed long long __a, __vector signed long long __b) { int __cc; - __builtin_s390_vceqgs(__a, __b, &__cc); + __builtin_s390_vceqgs((__vector unsigned long long)__a, + (__vector unsigned long long)__b, &__cc); return __cc <= 1; } @@ -4420,7 +4422,8 @@ vec_any_eq(__vector signed long long __a, __vector signed long long __b) { static inline __ATTRS_o_ai int vec_any_eq(__vector signed long long __a, __vector __bool long long __b) { int __cc; - __builtin_s390_vceqgs(__a, (__vector signed long long)__b, &__cc); + __builtin_s390_vceqgs((__vector unsigned long long)__a, + (__vector unsigned long long)__b, &__cc); return __cc <= 1; } @@ -4428,15 +4431,15 @@ vec_any_eq(__vector signed long long __a, __vector __bool long long __b) { static inline __ATTRS_o_ai int vec_any_eq(__vector __bool long long __a, __vector signed long long __b) { int __cc; - __builtin_s390_vceqgs((__vector signed long long)__a, __b, &__cc); + __builtin_s390_vceqgs((__vector unsigned long long)__a, + (__vector unsigned long long)__b, &__cc); return __cc <= 1; } static inline __ATTRS_o_ai int vec_any_eq(__vector unsigned long long __a, __vector unsigned long long __b) { int __cc; - __builtin_s390_vceqgs((__vector signed long long)__a, - (__vector signed long long)__b, &__cc); + __builtin_s390_vceqgs(__a, __b, &__cc); return __cc <= 1; } @@ -4444,8 +4447,7 @@ vec_any_eq(__vector unsigned long long __a, __vector unsigned long long __b) { static inline __ATTRS_o_ai int vec_any_eq(__vector unsigned long long __a, __vector __bool long long __b) { int __cc; - __builtin_s390_vceqgs((__vector signed long long)__a, - (__vector signed long long)__b, &__cc); + __builtin_s390_vceqgs(__a, (__vector unsigned long long)__b, &__cc); return __cc <= 1; } @@ -4453,16 +4455,15 @@ vec_any_eq(__vector unsigned long long __a, __vector __bool long long __b) { static inline __ATTRS_o_ai int vec_any_eq(__vector __bool long long __a, __vector unsigned long long __b) { int __cc; - __builtin_s390_vceqgs((__vector signed long long)__a, - (__vector signed long long)__b, &__cc); + __builtin_s390_vceqgs((__vector unsigned long long)__a, __b, &__cc); return __cc <= 1; } static inline __ATTRS_o_ai int vec_any_eq(__vector __bool long long __a, __vector __bool long long __b) { int __cc; - __builtin_s390_vceqgs((__vector signed long long)__a, - (__vector signed long long)__b, &__cc); + __builtin_s390_vceqgs((__vector unsigned long long)__a, + (__vector unsigned long long)__b, &__cc); return __cc <= 1; } @@ -4487,7 +4488,8 @@ vec_any_eq(__vector double __a, __vector double __b) { static inline __ATTRS_o_ai int vec_any_ne(__vector signed char __a, __vector signed char __b) { int __cc; - __builtin_s390_vceqbs(__a, __b, &__cc); + __builtin_s390_vceqbs((__vector unsigned char)__a, + (__vector unsigned char)__b, &__cc); return __cc != 0; } @@ -4495,7 +4497,8 @@ vec_any_ne(__vector signed char __a, __vector signed char __b) { static inline __ATTRS_o_ai int vec_any_ne(__vector signed char __a, __vector __bool char __b) { int __cc; - __builtin_s390_vceqbs(__a, (__vector signed char)__b, &__cc); + __builtin_s390_vceqbs((__vector unsigned char)__a, + (__vector unsigned char)__b, &__cc); return __cc != 0; } @@ -4503,15 +4506,15 @@ vec_any_ne(__vector signed char __a, __vector __bool char __b) { static inline __ATTRS_o_ai int vec_any_ne(__vector __bool char __a, __vector signed char __b) { int __cc; - __builtin_s390_vceqbs((__vector signed char)__a, __b, &__cc); + __builtin_s390_vceqbs((__vector unsigned char)__a, + (__vector unsigned char)__b, &__cc); return __cc != 0; } static inline __ATTRS_o_ai int vec_any_ne(__vector unsigned char __a, __vector unsigned char __b) { int __cc; - __builtin_s390_vceqbs((__vector signed char)__a, - (__vector signed char)__b, &__cc); + __builtin_s390_vceqbs(__a, __b, &__cc); return __cc != 0; } @@ -4519,8 +4522,7 @@ vec_any_ne(__vector unsigned char __a, __vector unsigned char __b) { static inline __ATTRS_o_ai int vec_any_ne(__vector unsigned char __a, __vector __bool char __b) { int __cc; - __builtin_s390_vceqbs((__vector signed char)__a, - (__vector signed char)__b, &__cc); + __builtin_s390_vceqbs(__a, (__vector unsigned char)__b, &__cc); return __cc != 0; } @@ -4528,23 +4530,23 @@ vec_any_ne(__vector unsigned char __a, __vector __bool char __b) { static inline __ATTRS_o_ai int vec_any_ne(__vector __bool char __a, __vector unsigned char __b) { int __cc; - __builtin_s390_vceqbs((__vector signed char)__a, - (__vector signed char)__b, &__cc); + __builtin_s390_vceqbs((__vector unsigned char)__a, __b, &__cc); return __cc != 0; } static inline __ATTRS_o_ai int vec_any_ne(__vector __bool char __a, __vector __bool char __b) { int __cc; - __builtin_s390_vceqbs((__vector signed char)__a, - (__vector signed char)__b, &__cc); + __builtin_s390_vceqbs((__vector unsigned char)__a, + (__vector unsigned char)__b, &__cc); return __cc != 0; } static inline __ATTRS_o_ai int vec_any_ne(__vector signed short __a, __vector signed short __b) { int __cc; - __builtin_s390_vceqhs(__a, __b, &__cc); + __builtin_s390_vceqhs((__vector unsigned short)__a, + (__vector unsigned short)__b, &__cc); return __cc != 0; } @@ -4552,7 +4554,8 @@ vec_any_ne(__vector signed short __a, __vector signed short __b) { static inline __ATTRS_o_ai int vec_any_ne(__vector signed short __a, __vector __bool short __b) { int __cc; - __builtin_s390_vceqhs(__a, (__vector signed short)__b, &__cc); + __builtin_s390_vceqhs((__vector unsigned short)__a, + (__vector unsigned short)__b, &__cc); return __cc != 0; } @@ -4560,15 +4563,15 @@ vec_any_ne(__vector signed short __a, __vector __bool short __b) { static inline __ATTRS_o_ai int vec_any_ne(__vector __bool short __a, __vector signed short __b) { int __cc; - __builtin_s390_vceqhs((__vector signed short)__a, __b, &__cc); + __builtin_s390_vceqhs((__vector unsigned short)__a, + (__vector unsigned short)__b, &__cc); return __cc != 0; } static inline __ATTRS_o_ai int vec_any_ne(__vector unsigned short __a, __vector unsigned short __b) { int __cc; - __builtin_s390_vceqhs((__vector signed short)__a, - (__vector signed short)__b, &__cc); + __builtin_s390_vceqhs(__a, __b, &__cc); return __cc != 0; } @@ -4576,8 +4579,7 @@ vec_any_ne(__vector unsigned short __a, __vector unsigned short __b) { static inline __ATTRS_o_ai int vec_any_ne(__vector unsigned short __a, __vector __bool short __b) { int __cc; - __builtin_s390_vceqhs((__vector signed short)__a, - (__vector signed short)__b, &__cc); + __builtin_s390_vceqhs(__a, (__vector unsigned short)__b, &__cc); return __cc != 0; } @@ -4585,23 +4587,23 @@ vec_any_ne(__vector unsigned short __a, __vector __bool short __b) { static inline __ATTRS_o_ai int vec_any_ne(__vector __bool short __a, __vector unsigned short __b) { int __cc; - __builtin_s390_vceqhs((__vector signed short)__a, - (__vector signed short)__b, &__cc); + __builtin_s390_vceqhs((__vector unsigned short)__a, __b, &__cc); return __cc != 0; } static inline __ATTRS_o_ai int vec_any_ne(__vector __bool short __a, __vector __bool short __b) { int __cc; - __builtin_s390_vceqhs((__vector signed short)__a, - (__vector signed short)__b, &__cc); + __builtin_s390_vceqhs((__vector unsigned short)__a, + (__vector unsigned short)__b, &__cc); return __cc != 0; } static inline __ATTRS_o_ai int vec_any_ne(__vector signed int __a, __vector signed int __b) { int __cc; - __builtin_s390_vceqfs(__a, __b, &__cc); + __builtin_s390_vceqfs((__vector unsigned int)__a, + (__vector unsigned int)__b, &__cc); return __cc != 0; } @@ -4609,7 +4611,8 @@ vec_any_ne(__vector signed int __a, __vector signed int __b) { static inline __ATTRS_o_ai int vec_any_ne(__vector signed int __a, __vector __bool int __b) { int __cc; - __builtin_s390_vceqfs(__a, (__vector signed int)__b, &__cc); + __builtin_s390_vceqfs((__vector unsigned int)__a, + (__vector unsigned int)__b, &__cc); return __cc != 0; } @@ -4617,15 +4620,15 @@ vec_any_ne(__vector signed int __a, __vector __bool int __b) { static inline __ATTRS_o_ai int vec_any_ne(__vector __bool int __a, __vector signed int __b) { int __cc; - __builtin_s390_vceqfs((__vector signed int)__a, __b, &__cc); + __builtin_s390_vceqfs((__vector unsigned int)__a, + (__vector unsigned int)__b, &__cc); return __cc != 0; } static inline __ATTRS_o_ai int vec_any_ne(__vector unsigned int __a, __vector unsigned int __b) { int __cc; - __builtin_s390_vceqfs((__vector signed int)__a, - (__vector signed int)__b, &__cc); + __builtin_s390_vceqfs(__a, __b, &__cc); return __cc != 0; } @@ -4633,8 +4636,7 @@ vec_any_ne(__vector unsigned int __a, __vector unsigned int __b) { static inline __ATTRS_o_ai int vec_any_ne(__vector unsigned int __a, __vector __bool int __b) { int __cc; - __builtin_s390_vceqfs((__vector signed int)__a, - (__vector signed int)__b, &__cc); + __builtin_s390_vceqfs(__a, (__vector unsigned int)__b, &__cc); return __cc != 0; } @@ -4642,23 +4644,23 @@ vec_any_ne(__vector unsigned int __a, __vector __bool int __b) { static inline __ATTRS_o_ai int vec_any_ne(__vector __bool int __a, __vector unsigned int __b) { int __cc; - __builtin_s390_vceqfs((__vector signed int)__a, - (__vector signed int)__b, &__cc); + __builtin_s390_vceqfs((__vector unsigned int)__a, __b, &__cc); return __cc != 0; } static inline __ATTRS_o_ai int vec_any_ne(__vector __bool int __a, __vector __bool int __b) { int __cc; - __builtin_s390_vceqfs((__vector signed int)__a, - (__vector signed int)__b, &__cc); + __builtin_s390_vceqfs((__vector unsigned int)__a, + (__vector unsigned int)__b, &__cc); return __cc != 0; } static inline __ATTRS_o_ai int vec_any_ne(__vector signed long long __a, __vector signed long long __b) { int __cc; - __builtin_s390_vceqgs(__a, __b, &__cc); + __builtin_s390_vceqgs((__vector unsigned long long)__a, + (__vector unsigned long long)__b, &__cc); return __cc != 0; } @@ -4666,7 +4668,8 @@ vec_any_ne(__vector signed long long __a, __vector signed long long __b) { static inline __ATTRS_o_ai int vec_any_ne(__vector signed long long __a, __vector __bool long long __b) { int __cc; - __builtin_s390_vceqgs(__a, (__vector signed long long)__b, &__cc); + __builtin_s390_vceqgs((__vector unsigned long long)__a, + (__vector unsigned long long)__b, &__cc); return __cc != 0; } @@ -4674,15 +4677,15 @@ vec_any_ne(__vector signed long long __a, __vector __bool long long __b) { static inline __ATTRS_o_ai int vec_any_ne(__vector __bool long long __a, __vector signed long long __b) { int __cc; - __builtin_s390_vceqgs((__vector signed long long)__a, __b, &__cc); + __builtin_s390_vceqgs((__vector unsigned long long)__a, + (__vector unsigned long long)__b, &__cc); return __cc != 0; } static inline __ATTRS_o_ai int vec_any_ne(__vector unsigned long long __a, __vector unsigned long long __b) { int __cc; - __builtin_s390_vceqgs((__vector signed long long)__a, - (__vector signed long long)__b, &__cc); + __builtin_s390_vceqgs(__a, __b, &__cc); return __cc != 0; } @@ -4690,8 +4693,7 @@ vec_any_ne(__vector unsigned long long __a, __vector unsigned long long __b) { static inline __ATTRS_o_ai int vec_any_ne(__vector unsigned long long __a, __vector __bool long long __b) { int __cc; - __builtin_s390_vceqgs((__vector signed long long)__a, - (__vector signed long long)__b, &__cc); + __builtin_s390_vceqgs(__a, (__vector unsigned long long)__b, &__cc); return __cc != 0; } @@ -4699,16 +4701,15 @@ vec_any_ne(__vector unsigned long long __a, __vector __bool long long __b) { static inline __ATTRS_o_ai int vec_any_ne(__vector __bool long long __a, __vector unsigned long long __b) { int __cc; - __builtin_s390_vceqgs((__vector signed long long)__a, - (__vector signed long long)__b, &__cc); + __builtin_s390_vceqgs((__vector unsigned long long)__a, __b, &__cc); return __cc != 0; } static inline __ATTRS_o_ai int vec_any_ne(__vector __bool long long __a, __vector __bool long long __b) { int __cc; - __builtin_s390_vceqgs((__vector signed long long)__a, - (__vector signed long long)__b, &__cc); + __builtin_s390_vceqgs((__vector unsigned long long)__a, + (__vector unsigned long long)__b, &__cc); return __cc != 0; } @@ -6565,45 +6566,45 @@ vec_rl(__vector unsigned long long __a, __vector unsigned long long __b) { static inline __ATTRS_o_ai __vector signed char vec_rli(__vector signed char __a, unsigned long __b) { return (__vector signed char)__builtin_s390_verllb( - (__vector unsigned char)__a, (int)__b); + (__vector unsigned char)__a, (unsigned char)__b); } static inline __ATTRS_o_ai __vector unsigned char vec_rli(__vector unsigned char __a, unsigned long __b) { - return __builtin_s390_verllb(__a, (int)__b); + return __builtin_s390_verllb(__a, (unsigned char)__b); } static inline __ATTRS_o_ai __vector signed short vec_rli(__vector signed short __a, unsigned long __b) { return (__vector signed short)__builtin_s390_verllh( - (__vector unsigned short)__a, (int)__b); + (__vector unsigned short)__a, (unsigned char)__b); } static inline __ATTRS_o_ai __vector unsigned short vec_rli(__vector unsigned short __a, unsigned long __b) { - return __builtin_s390_verllh(__a, (int)__b); + return __builtin_s390_verllh(__a, (unsigned char)__b); } static inline __ATTRS_o_ai __vector signed int vec_rli(__vector signed int __a, unsigned long __b) { return (__vector signed int)__builtin_s390_verllf( - (__vector unsigned int)__a, (int)__b); + (__vector unsigned int)__a, (unsigned char)__b); } static inline __ATTRS_o_ai __vector unsigned int vec_rli(__vector unsigned int __a, unsigned long __b) { - return __builtin_s390_verllf(__a, (int)__b); + return __builtin_s390_verllf(__a, (unsigned char)__b); } static inline __ATTRS_o_ai __vector signed long long vec_rli(__vector signed long long __a, unsigned long __b) { return (__vector signed long long)__builtin_s390_verllg( - (__vector unsigned long long)__a, (int)__b); + (__vector unsigned long long)__a, (unsigned char)__b); } static inline __ATTRS_o_ai __vector unsigned long long vec_rli(__vector unsigned long long __a, unsigned long __b) { - return __builtin_s390_verllg(__a, (int)__b); + return __builtin_s390_verllg(__a, (unsigned char)__b); } /*-- vec_rl_mask ------------------------------------------------------------*/ diff --git a/clang/lib/IndexSerialization/SerializablePathCollection.cpp b/clang/lib/IndexSerialization/SerializablePathCollection.cpp index 34663738088e8fa4fb40c793faa47cbf4779ff67..bd5f861bf482e511168ca428bdf7046aacb4751b 100644 --- a/clang/lib/IndexSerialization/SerializablePathCollection.cpp +++ b/clang/lib/IndexSerialization/SerializablePathCollection.cpp @@ -45,8 +45,8 @@ SerializablePathCollection::SerializablePathCollection( SysRootPath(Paths.addDirPath(SysRoot)), OutputFilePath(Paths.addDirPath(OutputFile)) {} -size_t SerializablePathCollection::tryStoreFilePath(const FileEntry &FE) { - auto FileIt = UniqueFiles.find(&FE); +size_t SerializablePathCollection::tryStoreFilePath(FileEntryRef FE) { + auto FileIt = UniqueFiles.find(FE); if (FileIt != UniqueFiles.end()) return FileIt->second; @@ -54,7 +54,7 @@ size_t SerializablePathCollection::tryStoreFilePath(const FileEntry &FE) { const auto FileIdx = Paths.addFilePath(Dir.Root, Dir.Path, sys::path::filename(FE.getName())); - UniqueFiles.try_emplace(&FE, FileIdx); + UniqueFiles.try_emplace(FE, FileIdx); return FileIdx; } diff --git a/clang/lib/Parse/ParseOpenACC.cpp b/clang/lib/Parse/ParseOpenACC.cpp index 4021d50318561fd4bf3d9ab3116c1ee3577abfc8..71cb665a563271ca77c517aa6e96c6c8758f9dbd 100644 --- a/clang/lib/Parse/ParseOpenACC.cpp +++ b/clang/lib/Parse/ParseOpenACC.cpp @@ -36,15 +36,19 @@ enum class OpenACCDirectiveKindEx { // identifies the first token), and doesn't fully handle 'enter data', 'exit // data', nor any of the 'atomic' variants, just the first token of each. So // this should only be used by `ParseOpenACCDirectiveKind`. -OpenACCDirectiveKindEx getOpenACCDirectiveKind(StringRef Name) { +OpenACCDirectiveKindEx getOpenACCDirectiveKind(Token Tok) { + if (!Tok.is(tok::identifier)) + return OpenACCDirectiveKindEx::Invalid; OpenACCDirectiveKind DirKind = - llvm::StringSwitch(Name) + llvm::StringSwitch( + Tok.getIdentifierInfo()->getName()) .Case("parallel", OpenACCDirectiveKind::Parallel) .Case("serial", OpenACCDirectiveKind::Serial) .Case("kernels", OpenACCDirectiveKind::Kernels) .Case("data", OpenACCDirectiveKind::Data) .Case("host_data", OpenACCDirectiveKind::HostData) .Case("loop", OpenACCDirectiveKind::Loop) + .Case("cache", OpenACCDirectiveKind::Cache) .Case("atomic", OpenACCDirectiveKind::Atomic) .Case("routine", OpenACCDirectiveKind::Routine) .Case("declare", OpenACCDirectiveKind::Declare) @@ -57,7 +61,8 @@ OpenACCDirectiveKindEx getOpenACCDirectiveKind(StringRef Name) { if (DirKind != OpenACCDirectiveKind::Invalid) return static_cast(DirKind); - return llvm::StringSwitch(Name) + return llvm::StringSwitch( + Tok.getIdentifierInfo()->getName()) .Case("enter", OpenACCDirectiveKindEx::Enter) .Case("exit", OpenACCDirectiveKindEx::Exit) .Default(OpenACCDirectiveKindEx::Invalid); @@ -65,8 +70,11 @@ OpenACCDirectiveKindEx getOpenACCDirectiveKind(StringRef Name) { // Since 'atomic' is effectively a compound directive, this will decode the // second part of the directive. -OpenACCAtomicKind getOpenACCAtomicKind(StringRef Name) { - return llvm::StringSwitch(Name) +OpenACCAtomicKind getOpenACCAtomicKind(Token Tok) { + if (!Tok.is(tok::identifier)) + return OpenACCAtomicKind::Invalid; + return llvm::StringSwitch( + Tok.getIdentifierInfo()->getName()) .Case("read", OpenACCAtomicKind::Read) .Case("write", OpenACCAtomicKind::Write) .Case("update", OpenACCAtomicKind::Update) @@ -74,20 +82,25 @@ OpenACCAtomicKind getOpenACCAtomicKind(StringRef Name) { .Default(OpenACCAtomicKind::Invalid); } -bool isOpenACCDirectiveKind(OpenACCDirectiveKind Kind, StringRef Tok) { +bool isOpenACCDirectiveKind(OpenACCDirectiveKind Kind, Token Tok) { + if (!Tok.is(tok::identifier)) + return false; + switch (Kind) { case OpenACCDirectiveKind::Parallel: - return Tok == "parallel"; + return Tok.getIdentifierInfo()->isStr("parallel"); case OpenACCDirectiveKind::Serial: - return Tok == "serial"; + return Tok.getIdentifierInfo()->isStr("serial"); case OpenACCDirectiveKind::Kernels: - return Tok == "kernels"; + return Tok.getIdentifierInfo()->isStr("kernels"); case OpenACCDirectiveKind::Data: - return Tok == "data"; + return Tok.getIdentifierInfo()->isStr("data"); case OpenACCDirectiveKind::HostData: - return Tok == "host_data"; + return Tok.getIdentifierInfo()->isStr("host_data"); case OpenACCDirectiveKind::Loop: - return Tok == "loop"; + return Tok.getIdentifierInfo()->isStr("loop"); + case OpenACCDirectiveKind::Cache: + return Tok.getIdentifierInfo()->isStr("cache"); case OpenACCDirectiveKind::ParallelLoop: case OpenACCDirectiveKind::SerialLoop: @@ -97,19 +110,19 @@ bool isOpenACCDirectiveKind(OpenACCDirectiveKind Kind, StringRef Tok) { return false; case OpenACCDirectiveKind::Atomic: - return Tok == "atomic"; + return Tok.getIdentifierInfo()->isStr("atomic"); case OpenACCDirectiveKind::Routine: - return Tok == "routine"; + return Tok.getIdentifierInfo()->isStr("routine"); case OpenACCDirectiveKind::Declare: - return Tok == "declare"; + return Tok.getIdentifierInfo()->isStr("declare"); case OpenACCDirectiveKind::Init: - return Tok == "init"; + return Tok.getIdentifierInfo()->isStr("init"); case OpenACCDirectiveKind::Shutdown: - return Tok == "shutdown"; + return Tok.getIdentifierInfo()->isStr("shutdown"); case OpenACCDirectiveKind::Set: - return Tok == "set"; + return Tok.getIdentifierInfo()->isStr("set"); case OpenACCDirectiveKind::Update: - return Tok == "update"; + return Tok.getIdentifierInfo()->isStr("update"); case OpenACCDirectiveKind::Invalid: return false; } @@ -118,20 +131,22 @@ bool isOpenACCDirectiveKind(OpenACCDirectiveKind Kind, StringRef Tok) { OpenACCDirectiveKind ParseOpenACCEnterExitDataDirective(Parser &P, Token FirstTok, - StringRef FirstTokSpelling, OpenACCDirectiveKindEx ExtDirKind) { Token SecondTok = P.getCurToken(); if (SecondTok.isAnnotation()) { - P.Diag(FirstTok, diag::err_acc_invalid_directive) << 0 << FirstTokSpelling; + P.Diag(FirstTok, diag::err_acc_invalid_directive) + << 0 << FirstTok.getIdentifierInfo(); return OpenACCDirectiveKind::Invalid; } - std::string SecondTokSpelling = P.getPreprocessor().getSpelling(SecondTok); - - if (!isOpenACCDirectiveKind(OpenACCDirectiveKind::Data, SecondTokSpelling)) { - P.Diag(FirstTok, diag::err_acc_invalid_directive) - << 1 << FirstTokSpelling << SecondTokSpelling; + if (!isOpenACCDirectiveKind(OpenACCDirectiveKind::Data, SecondTok)) { + if (!SecondTok.is(tok::identifier)) + P.Diag(SecondTok, diag::err_expected) << tok::identifier; + else + P.Diag(FirstTok, diag::err_acc_invalid_directive) + << 1 << FirstTok.getIdentifierInfo()->getName() + << SecondTok.getIdentifierInfo()->getName(); return OpenACCDirectiveKind::Invalid; } @@ -149,9 +164,7 @@ OpenACCAtomicKind ParseOpenACCAtomicKind(Parser &P) { if (AtomicClauseToken.isAnnotation()) return OpenACCAtomicKind::Update; - std::string AtomicClauseSpelling = - P.getPreprocessor().getSpelling(AtomicClauseToken); - OpenACCAtomicKind AtomicKind = getOpenACCAtomicKind(AtomicClauseSpelling); + OpenACCAtomicKind AtomicKind = getOpenACCAtomicKind(AtomicClauseToken); // If we don't know what this is, treat it as 'nothing', and treat the rest of // this as a clause list, which, despite being invalid, is likely what the @@ -175,9 +188,8 @@ OpenACCDirectiveKind ParseOpenACCDirectiveKind(Parser &P) { } P.ConsumeToken(); - std::string FirstTokSpelling = P.getPreprocessor().getSpelling(FirstTok); - OpenACCDirectiveKindEx ExDirKind = getOpenACCDirectiveKind(FirstTokSpelling); + OpenACCDirectiveKindEx ExDirKind = getOpenACCDirectiveKind(FirstTok); // OpenACCDirectiveKindEx is meant to be an extended list // over OpenACCDirectiveKind, so any value below Invalid is one of the @@ -187,14 +199,17 @@ OpenACCDirectiveKind ParseOpenACCDirectiveKind(Parser &P) { // immediately cast it and use it as that. if (ExDirKind >= OpenACCDirectiveKindEx::Invalid) { switch (ExDirKind) { - case OpenACCDirectiveKindEx::Invalid: - P.Diag(FirstTok, diag::err_acc_invalid_directive) - << 0 << FirstTokSpelling; + case OpenACCDirectiveKindEx::Invalid: { + if (!FirstTok.is(tok::identifier)) + P.Diag(FirstTok, diag::err_expected) << tok::identifier; + else + P.Diag(FirstTok, diag::err_acc_invalid_directive) + << 0 << FirstTok.getIdentifierInfo(); return OpenACCDirectiveKind::Invalid; + } case OpenACCDirectiveKindEx::Enter: case OpenACCDirectiveKindEx::Exit: - return ParseOpenACCEnterExitDataDirective(P, FirstTok, FirstTokSpelling, - ExDirKind); + return ParseOpenACCEnterExitDataDirective(P, FirstTok, ExDirKind); } } @@ -205,8 +220,7 @@ OpenACCDirectiveKind ParseOpenACCDirectiveKind(Parser &P) { // clause. Token SecondTok = P.getCurToken(); if (!SecondTok.isAnnotation() && - isOpenACCDirectiveKind(OpenACCDirectiveKind::Loop, - P.getPreprocessor().getSpelling(SecondTok))) { + isOpenACCDirectiveKind(OpenACCDirectiveKind::Loop, SecondTok)) { switch (DirKind) { default: // Nothing to do except in the below cases, as they should be diagnosed as @@ -237,10 +251,7 @@ void ParseOpenACCClauseList(Parser &P) { } // namespace -// Routine has an optional paren-wrapped name of a function in the local scope. -// We parse the name, emitting any diagnostics -ExprResult Parser::ParseOpenACCRoutineName() { - +ExprResult Parser::ParseOpenACCIDExpression() { ExprResult Res; if (getLangOpts().CPlusPlus) { Res = ParseCXXIdExpression(/*isAddressOfOperand=*/false); @@ -248,8 +259,10 @@ ExprResult Parser::ParseOpenACCRoutineName() { // There isn't anything quite the same as ParseCXXIdExpression for C, so we // need to get the identifier, then call into Sema ourselves. - if (expectIdentifier()) + if (Tok.isNot(tok::identifier)) { + Diag(Tok, diag::err_expected) << tok::identifier; return ExprError(); + } Token FuncName = getCurToken(); UnqualifiedId Name; @@ -268,6 +281,86 @@ ExprResult Parser::ParseOpenACCRoutineName() { return getActions().CorrectDelayedTyposInExpr(Res); } +/// OpenACC 3.3, section 2.10: +/// A 'var' in a cache directive must be a single array element or a simple +/// subarray. In C and C++, a simple subarray is an array name followed by an +/// extended array range specification in brackets, with a start and length such +/// as: +/// +/// arr[lower:length] +/// +bool Parser::ParseOpenACCCacheVar() { + ExprResult ArrayName = ParseOpenACCIDExpression(); + if (ArrayName.isInvalid()) + return true; + + // If the expression is invalid, just continue parsing the brackets, there + // is likely other useful diagnostics we can emit inside of those. + + BalancedDelimiterTracker SquareBrackets(*this, tok::l_square, + tok::annot_pragma_openacc_end); + + // Square brackets are required, so error here, and try to recover by moving + // until the next comma, or the close paren/end of pragma. + if (SquareBrackets.expectAndConsume()) { + SkipUntil(tok::comma, tok::r_paren, tok::annot_pragma_openacc_end, + Parser::StopBeforeMatch); + return true; + } + + ExprResult Lower = getActions().CorrectDelayedTyposInExpr(ParseExpression()); + if (Lower.isInvalid()) + return true; + + // The 'length' expression is optional, as this could be a single array + // element. If there is no colon, we can treat it as that. + if (getCurToken().is(tok::colon)) { + ConsumeToken(); + ExprResult Length = + getActions().CorrectDelayedTyposInExpr(ParseExpression()); + if (Length.isInvalid()) + return true; + } + + // Diagnose the square bracket being in the wrong place and continue. + return SquareBrackets.consumeClose(); +} + +/// OpenACC 3.3, section 2.10: +/// In C and C++, the syntax of the cache directive is: +/// +/// #pragma acc cache ([readonly:]var-list) new-line +void Parser::ParseOpenACCCacheVarList() { + // If this is the end of the line, just return 'false' and count on the close + // paren diagnostic to catch the issue. + if (getCurToken().isAnnotation()) + return; + + // The VarList is an optional `readonly:` followed by a list of a variable + // specifications. First, see if we have `readonly:`, else we back-out and + // treat it like the beginning of a reference to a potentially-existing + // `readonly` variable. + if (getCurToken().is(tok::identifier) && + getCurToken().getIdentifierInfo()->isStr("readonly") && + NextToken().is(tok::colon)) { + // Consume both tokens. + ConsumeToken(); + ConsumeToken(); + // FIXME: Record that this is a 'readonly' so that we can use that during + // Sema/AST generation. + } + + bool FirstArray = true; + while (!getCurToken().isOneOf(tok::r_paren, tok::annot_pragma_openacc_end)) { + if (!FirstArray) + ExpectAndConsume(tok::comma); + FirstArray = false; + if (ParseOpenACCCacheVar()) + SkipUntil(tok::r_paren, tok::annot_pragma_openacc_end, tok::comma, + StopBeforeMatch); + } +} + void Parser::ParseOpenACCDirective() { OpenACCDirectiveKind DirKind = ParseOpenACCDirectiveKind(*this); @@ -289,7 +382,9 @@ void Parser::ParseOpenACCDirective() { T.skipToEnd(); break; case OpenACCDirectiveKind::Routine: { - ExprResult RoutineName = ParseOpenACCRoutineName(); + // Routine has an optional paren-wrapped name of a function in the local + // scope. We parse the name, emitting any diagnostics + ExprResult RoutineName = ParseOpenACCIDExpression(); // If the routine name is invalid, just skip until the closing paren to // recover more gracefully. if (RoutineName.isInvalid()) @@ -298,7 +393,18 @@ void Parser::ParseOpenACCDirective() { T.consumeClose(); break; } + case OpenACCDirectiveKind::Cache: + ParseOpenACCCacheVarList(); + // The ParseOpenACCCacheVarList function manages to recover from failures, + // so we can always consume the close. + T.consumeClose(); + break; } + } else if (DirKind == OpenACCDirectiveKind::Cache) { + // Cache's paren var-list is required, so error here if it isn't provided. + // We know that the consumeOpen above left the first non-paren here, so + // diagnose, then continue as if it was completely omitted. + Diag(Tok, diag::err_expected) << tok::l_paren; } // Parses the list of clauses, if present. diff --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp index fb7e7a979e49f7ea7fad309a76434a67f643f022..da5f6605c6ffac9ed166f8a45553abd34fc2f287 100644 --- a/clang/lib/Parse/ParseOpenMP.cpp +++ b/clang/lib/Parse/ParseOpenMP.cpp @@ -2528,7 +2528,8 @@ StmtResult Parser::ParseOpenMPDeclarativeOrExecutableDirective( skipUntilPragmaOpenMPEnd(DKind); if (Tok.is(tok::annot_pragma_openmp_end)) ConsumeAnnotationToken(); - break; + // return an empty statement + return StmtEmpty(); case OMPD_metadirective: { ConsumeToken(); SmallVector VMIs; diff --git a/clang/lib/Parse/ParseTemplate.cpp b/clang/lib/Parse/ParseTemplate.cpp index f556d0e6d4f8b6ec5eeb87ffc267c74c1fda296f..64fe4d50bba27bf143d02324e7d7e90401b53702 100644 --- a/clang/lib/Parse/ParseTemplate.cpp +++ b/clang/lib/Parse/ParseTemplate.cpp @@ -1062,8 +1062,7 @@ Parser::ParseNonTypeTemplateParameter(unsigned Depth, unsigned Position) { ++CurTemplateDepthTracker; EnterExpressionEvaluationContext ConstantEvaluated( Actions, Sema::ExpressionEvaluationContext::ConstantEvaluated); - DefaultArg = - Actions.CorrectDelayedTyposInExpr(ParseAssignmentExpression()); + DefaultArg = Actions.CorrectDelayedTyposInExpr(ParseInitializer()); if (DefaultArg.isInvalid()) SkipUntil(tok::comma, tok::greater, StopAtSemi | StopBeforeMatch); } @@ -1582,6 +1581,8 @@ ParsedTemplateArgument Parser::ParseTemplateTemplateArgument() { /// constant-expression /// type-id /// id-expression +/// braced-init-list [C++26, DR] +/// ParsedTemplateArgument Parser::ParseTemplateArgument() { // C++ [temp.arg]p2: // In a template-argument, an ambiguity between a type-id and an @@ -1619,8 +1620,12 @@ ParsedTemplateArgument Parser::ParseTemplateArgument() { } // Parse a non-type template argument. + ExprResult ExprArg; SourceLocation Loc = Tok.getLocation(); - ExprResult ExprArg = ParseConstantExpressionInExprEvalContext(MaybeTypeCast); + if (getLangOpts().CPlusPlus11 && Tok.is(tok::l_brace)) + ExprArg = ParseBraceInitializer(); + else + ExprArg = ParseConstantExpressionInExprEvalContext(MaybeTypeCast); if (ExprArg.isInvalid() || !ExprArg.get()) { return ParsedTemplateArgument(); } diff --git a/clang/lib/Rewrite/Rewriter.cpp b/clang/lib/Rewrite/Rewriter.cpp index ef2858990dd954e222b1473a557d513fdc05a475..0896221dd0bdeb5672b89ea2d450f6faa78b8d3c 100644 --- a/clang/lib/Rewrite/Rewriter.cpp +++ b/clang/lib/Rewrite/Rewriter.cpp @@ -412,12 +412,13 @@ bool Rewriter::overwriteChangedFiles() { unsigned OverwriteFailure = Diag.getCustomDiagID( DiagnosticsEngine::Error, "unable to overwrite file %0: %1"); for (buffer_iterator I = buffer_begin(), E = buffer_end(); I != E; ++I) { - const FileEntry *Entry = getSourceMgr().getFileEntryForID(I->first); - if (auto Error = - llvm::writeToOutput(Entry->getName(), [&](llvm::raw_ostream &OS) { - I->second.write(OS); - return llvm::Error::success(); - })) { + OptionalFileEntryRef Entry = getSourceMgr().getFileEntryRefForID(I->first); + llvm::SmallString<128> Path(Entry->getName()); + getSourceMgr().getFileManager().makeAbsolutePath(Path); + if (auto Error = llvm::writeToOutput(Path, [&](llvm::raw_ostream &OS) { + I->second.write(OS); + return llvm::Error::success(); + })) { Diag.Report(OverwriteFailure) << Entry->getName() << llvm::toString(std::move(Error)); AllWritten = false; diff --git a/clang/lib/Sema/MultiplexExternalSemaSource.cpp b/clang/lib/Sema/MultiplexExternalSemaSource.cpp index 100794de60ee03d366cd80822abb60268cb8652e..058e22cb2b814e6d4f53c801662ff71311f04d5f 100644 --- a/clang/lib/Sema/MultiplexExternalSemaSource.cpp +++ b/clang/lib/Sema/MultiplexExternalSemaSource.cpp @@ -310,12 +310,6 @@ void MultiplexExternalSemaSource::ReadPendingInstantiations( Sources[i]->ReadPendingInstantiations(Pending); } -void MultiplexExternalSemaSource::ReadPendingInstantiationsOfConstexprEntity( - const NamedDecl *D, llvm::SmallSetVector &Decls) { - for (size_t i = 0; i < Sources.size(); ++i) - Sources[i]->ReadPendingInstantiationsOfConstexprEntity(D, Decls); -} - void MultiplexExternalSemaSource::ReadLateParsedTemplates( llvm::MapVector> &LPTMap) { diff --git a/clang/lib/Sema/OpenCLBuiltins.td b/clang/lib/Sema/OpenCLBuiltins.td index 9db450281912d2f983c90e32ffdbbb5e3b86a2f2..0cceba090bd8f26f06f5b42f95e96da8f2db15a4 100644 --- a/clang/lib/Sema/OpenCLBuiltins.td +++ b/clang/lib/Sema/OpenCLBuiltins.td @@ -563,15 +563,12 @@ foreach name = ["acos", "acosh", "acospi", "log", "log2", "log10", "log1p", "logb", "rint", "round", "rsqrt", "sin", "sinh", "sinpi", + "sqrt", "tan", "tanh", "tanpi", "tgamma", "trunc", "lgamma"] in { def : Builtin; } - -// sqrt is handled in opencl-c-base.h to handle -// -cl-fp32-correctly-rounded-divide-sqrt. - foreach name = ["nan"] in { def : Builtin; def : Builtin; diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index 9771aaa2f3b0371530354c878488982a414e9523..2c7ecf4610de0f064127eface82bb132377c034c 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -2084,8 +2084,8 @@ void Sema::checkTypeSupport(QualType Ty, SourceLocation Loc, ValueDecl *D) { if (Ty->isSVESizelessBuiltinType() && FD && FD->hasBody()) { llvm::StringMap CallerFeatureMap; Context.getFunctionFeatureMap(CallerFeatureMap, FD); - if (!Builtin::evaluateRequiredTargetFeatures( - "sve", CallerFeatureMap)) + if (!Builtin::evaluateRequiredTargetFeatures("sve", CallerFeatureMap) && + !Builtin::evaluateRequiredTargetFeatures("sme", CallerFeatureMap)) Diag(D->getLocation(), diag::err_sve_vector_in_non_sve_target) << Ty; } }; diff --git a/clang/lib/Sema/SemaCUDA.cpp b/clang/lib/Sema/SemaCUDA.cpp index 318174f7be8fa95d9f7abf5499d26f6c8402d6d7..6a66ecf6f94c178da3dbf86687175c4f63d76f7d 100644 --- a/clang/lib/Sema/SemaCUDA.cpp +++ b/clang/lib/Sema/SemaCUDA.cpp @@ -225,6 +225,15 @@ Sema::CUDAFunctionPreference Sema::IdentifyCUDAPreference(const FunctionDecl *Caller, const FunctionDecl *Callee) { assert(Callee && "Callee must be valid."); + + // Treat ctor/dtor as host device function in device var initializer to allow + // trivial ctor/dtor without device attr to be used. Non-trivial ctor/dtor + // will be diagnosed by checkAllowedCUDAInitializer. + if (Caller == nullptr && CurCUDATargetCtx.Kind == CTCK_InitGlobalVar && + CurCUDATargetCtx.Target == CFT_Device && + (isa(Callee) || isa(Callee))) + return CFP_HostDevice; + CUDAFunctionTarget CallerTarget = IdentifyCUDATarget(Caller); CUDAFunctionTarget CalleeTarget = IdentifyCUDATarget(Callee); diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index 77c8334f3ca25d305ca5fd772b2b033749abe276..fc6ee6b2c5ab4fd9754df06d5761c47f111cf350 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -712,8 +712,13 @@ static ExprResult SemaBuiltinDumpStruct(Sema &S, CallExpr *TheCall) { << 1 << TheCall->getDirectCallee() << PtrArgType; return ExprError(); } - const RecordDecl *RD = PtrArgType->getPointeeType()->getAsRecordDecl(); - + QualType Pointee = PtrArgType->getPointeeType(); + const RecordDecl *RD = Pointee->getAsRecordDecl(); + // Try to instantiate the class template as appropriate; otherwise, access to + // its data() may lead to a crash. + if (S.RequireCompleteType(PtrArgResult.get()->getBeginLoc(), Pointee, + diag::err_incomplete_type)) + return ExprError(); // Second argument is a callable, but we can't fully validate it until we try // calling it. QualType FnArgType = TheCall->getArg(1)->getType(); @@ -2995,58 +3000,15 @@ static QualType getNeonEltType(NeonTypeFlags Flags, ASTContext &Context, enum ArmStreamingType { ArmNonStreaming, ArmStreaming, ArmStreamingCompatible }; -static ArmStreamingType getArmStreamingFnType(const FunctionDecl *FD) { - if (FD->hasAttr()) - return ArmStreaming; - if (const auto *T = FD->getType()->getAs()) { - if (T->getAArch64SMEAttributes() & FunctionType::SME_PStateSMEnabledMask) - return ArmStreaming; - if (T->getAArch64SMEAttributes() & FunctionType::SME_PStateSMCompatibleMask) - return ArmStreamingCompatible; - } - return ArmNonStreaming; -} - -static void checkArmStreamingBuiltin(Sema &S, CallExpr *TheCall, - const FunctionDecl *FD, - ArmStreamingType BuiltinType) { - ArmStreamingType FnType = getArmStreamingFnType(FD); - - if (FnType == ArmStreaming && BuiltinType == ArmNonStreaming) { - S.Diag(TheCall->getBeginLoc(), diag::warn_attribute_arm_sm_incompat_builtin) - << TheCall->getSourceRange() << "streaming"; - } - - if (FnType == ArmStreamingCompatible && - BuiltinType != ArmStreamingCompatible) { - S.Diag(TheCall->getBeginLoc(), diag::warn_attribute_arm_sm_incompat_builtin) - << TheCall->getSourceRange() << "streaming compatible"; - return; - } -} - -bool Sema::CheckSVEBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) { - // Range check SVE intrinsics that take immediate values. - SmallVector, 3> ImmChecks; - - switch (BuiltinID) { - default: - return false; -#define GET_SVE_IMMEDIATE_CHECK -#include "clang/Basic/arm_sve_sema_rangechecks.inc" -#undef GET_SVE_IMMEDIATE_CHECK -#define GET_SME_IMMEDIATE_CHECK -#include "clang/Basic/arm_sme_sema_rangechecks.inc" -#undef GET_SME_IMMEDIATE_CHECK - } - +bool Sema::ParseSVEImmChecks( + CallExpr *TheCall, SmallVector, 3> &ImmChecks) { // Perform all the immediate checks for this builtin call. bool HasError = false; for (auto &I : ImmChecks) { int ArgNum, CheckTy, ElementSizeInBits; std::tie(ArgNum, CheckTy, ElementSizeInBits) = I; - typedef bool(*OptionSetCheckFnTy)(int64_t Value); + typedef bool (*OptionSetCheckFnTy)(int64_t Value); // Function that checks whether the operand (ArgNum) is an immediate // that is one of the predefined values. @@ -3178,6 +3140,54 @@ bool Sema::CheckSVEBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) { return HasError; } +static ArmStreamingType getArmStreamingFnType(const FunctionDecl *FD) { + if (FD->hasAttr()) + return ArmStreaming; + if (const auto *T = FD->getType()->getAs()) { + if (T->getAArch64SMEAttributes() & FunctionType::SME_PStateSMEnabledMask) + return ArmStreaming; + if (T->getAArch64SMEAttributes() & FunctionType::SME_PStateSMCompatibleMask) + return ArmStreamingCompatible; + } + return ArmNonStreaming; +} + +static void checkArmStreamingBuiltin(Sema &S, CallExpr *TheCall, + const FunctionDecl *FD, + ArmStreamingType BuiltinType) { + ArmStreamingType FnType = getArmStreamingFnType(FD); + + if (FnType == ArmStreaming && BuiltinType == ArmNonStreaming) { + S.Diag(TheCall->getBeginLoc(), diag::warn_attribute_arm_sm_incompat_builtin) + << TheCall->getSourceRange() << "streaming"; + } + + if (FnType == ArmStreamingCompatible && + BuiltinType != ArmStreamingCompatible) { + S.Diag(TheCall->getBeginLoc(), diag::warn_attribute_arm_sm_incompat_builtin) + << TheCall->getSourceRange() << "streaming compatible"; + return; + } +} + +bool Sema::CheckSVEBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) { + // Range check SVE intrinsics that take immediate values. + SmallVector, 3> ImmChecks; + + switch (BuiltinID) { + default: + return false; +#define GET_SVE_IMMEDIATE_CHECK +#include "clang/Basic/arm_sve_sema_rangechecks.inc" +#undef GET_SVE_IMMEDIATE_CHECK +#define GET_SME_IMMEDIATE_CHECK +#include "clang/Basic/arm_sme_sema_rangechecks.inc" +#undef GET_SME_IMMEDIATE_CHECK + } + + return ParseSVEImmChecks(TheCall, ImmChecks); +} + bool Sema::CheckNeonBuiltinFunctionCall(const TargetInfo &TI, unsigned BuiltinID, CallExpr *TheCall) { if (const FunctionDecl *FD = getCurFunctionDecl()) { diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index e6d4655644c625989b177f92c59e1818dc7ccc60..e3c122de39dfe62571404fecd7ced261b9f2e7a3 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -16286,9 +16286,6 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body, if (FD && !FD->isDeleted()) checkTypeSupport(FD->getType(), FD->getLocation(), FD); - if (FD && FD->isConstexpr() && FD->isTemplated()) - PerformPendingInstantiationsOfConstexprFunctions(FD); - return dcl; } diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 87c78d742d0ff4d36c1427d26d84becae186a8b2..a345978bb8701ce151b8ecf97c0e686c235cf08d 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -9036,6 +9036,9 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL, case ParsedAttr::AT_BPFPreserveAccessIndex: handleBPFPreserveAccessIndexAttr(S, D, AL); break; + case ParsedAttr::AT_BPFPreserveStaticOffset: + handleSimpleAttribute(S, D, AL); + break; case ParsedAttr::AT_BTFDeclTag: handleBTFDeclTagAttr(S, D, AL); break; diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index bece54b4bfa14506be4957032692054f68ecdbfe..c6218a491aececec442a08fbd0d64431bfc774eb 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -12114,6 +12114,24 @@ public: } +static void DiagnoseInvisibleNamespace(const TypoCorrection &Corrected, + Sema &S) { + auto *ND = cast(Corrected.getFoundDecl()); + Module *M = ND->getOwningModule(); + assert(M && "hidden namespace definition not in a module?"); + + if (M->isExplicitGlobalModule()) + S.Diag(Corrected.getCorrectionRange().getBegin(), + diag::err_module_unimported_use_header) + << (int)Sema::MissingImportKind::Declaration << Corrected.getFoundDecl() + << /*Header Name*/ false; + else + S.Diag(Corrected.getCorrectionRange().getBegin(), + diag::err_module_unimported_use) + << (int)Sema::MissingImportKind::Declaration << Corrected.getFoundDecl() + << M->getTopLevelModuleName(); +} + static bool TryNamespaceTypoCorrection(Sema &S, LookupResult &R, Scope *Sc, CXXScopeSpec &SS, SourceLocation IdentLoc, @@ -12123,7 +12141,16 @@ static bool TryNamespaceTypoCorrection(Sema &S, LookupResult &R, Scope *Sc, if (TypoCorrection Corrected = S.CorrectTypo(R.getLookupNameInfo(), R.getLookupKind(), Sc, &SS, CCC, Sema::CTK_ErrorRecovery)) { - if (DeclContext *DC = S.computeDeclContext(SS, false)) { + // Generally we find it is confusing more than helpful to diagnose the + // invisible namespace. + // See https://github.com/llvm/llvm-project/issues/73893. + // + // However, we should diagnose when the users are trying to using an + // invisible namespace. So we handle the case specially here. + if (isa_and_nonnull(Corrected.getFoundDecl()) && + Corrected.requiresImport()) { + DiagnoseInvisibleNamespace(Corrected, S); + } else if (DeclContext *DC = S.computeDeclContext(SS, false)) { std::string CorrectedStr(Corrected.getAsString(S.getLangOpts())); bool DroppedSpecifier = Corrected.WillReplaceSpecifier() && Ident->getName().equals(CorrectedStr); diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index b204cb01a0deffdc429edfb89bc3f4d79ffc9c6a..d629be083d8c38c2da587dd40210f19ee23deb86 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -6053,9 +6053,14 @@ Sema::CreateBuiltinArraySubscriptExpr(Expr *Base, SourceLocation LLoc, << IndexExpr->getSourceRange()); if ((IndexExpr->getType()->isSpecificBuiltinType(BuiltinType::Char_S) || - IndexExpr->getType()->isSpecificBuiltinType(BuiltinType::Char_U)) - && !IndexExpr->isTypeDependent()) - Diag(LLoc, diag::warn_subscript_is_char) << IndexExpr->getSourceRange(); + IndexExpr->getType()->isSpecificBuiltinType(BuiltinType::Char_U)) && + !IndexExpr->isTypeDependent()) { + std::optional IntegerContantExpr = + IndexExpr->getIntegerConstantExpr(getASTContext()); + if (!IntegerContantExpr.has_value() || + IntegerContantExpr.value().isNegative()) + Diag(LLoc, diag::warn_subscript_is_char) << IndexExpr->getSourceRange(); + } // C99 6.5.2.1p1: "shall have type "pointer to *object* type". Similarly, // C++ [expr.sub]p1: The type "T" shall be a completely-defined object @@ -19053,17 +19058,12 @@ void Sema::MarkFunctionReferenced(SourceLocation Loc, FunctionDecl *Func, CodeSynthesisContexts.size()) PendingLocalImplicitInstantiations.push_back( std::make_pair(Func, PointOfInstantiation)); - else if (Func->isConstexpr()) { + else if (Func->isConstexpr()) // Do not defer instantiations of constexpr functions, to avoid the // expression evaluator needing to call back into Sema if it sees a // call to such a function. InstantiateFunctionDefinition(PointOfInstantiation, Func); - if (!Func->isDefined()) { - PendingInstantiationsOfConstexprEntities - [Func->getTemplateInstantiationPattern()->getCanonicalDecl()] - .push_back(Func); - } - } else { + else { Func->setInstantiationIsPending(true); PendingInstantiations.push_back( std::make_pair(Func, PointOfInstantiation)); diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp index 54b48f7ab80eb9c93859bbdc7cadd89166e0c0a4..996f8b57233ba223c1003c7357ae562c2d9ceac0 100644 --- a/clang/lib/Sema/SemaLookup.cpp +++ b/clang/lib/Sema/SemaLookup.cpp @@ -5712,6 +5712,11 @@ void Sema::diagnoseMissingImport(SourceLocation UseLoc, const NamedDecl *Decl, MissingImportKind MIK, bool Recover) { assert(!Modules.empty()); + // See https://github.com/llvm/llvm-project/issues/73893. It is generally + // confusing than helpful to show the namespace is not visible. + if (isa(Decl)) + return; + auto NotePrevious = [&] { // FIXME: Suppress the note backtrace even under // -fdiagnostics-show-note-include-stack. We don't care how this diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index 3a3e9234469d3933c165f0dc324c3a1876dbf18a..5026e1d603e5ee24556c6b55273020b3874348b9 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -6123,61 +6123,6 @@ static ExprResult BuildConvertedConstantExpression(Sema &S, Expr *From, return Result; } -/// EvaluateConvertedConstantExpression - Evaluate an Expression -/// That is a converted constant expression -/// (which was built with BuildConvertedConstantExpression) -static ExprResult EvaluateConvertedConstantExpression( - Sema &S, Expr *E, QualType T, APValue &Value, Sema::CCEKind CCE, - bool RequireInt, const APValue &PreNarrowingValue) { - ExprResult Result = E; - // Check the expression is a constant expression. - SmallVector Notes; - Expr::EvalResult Eval; - Eval.Diag = &Notes; - - ConstantExprKind Kind; - if (CCE == Sema::CCEK_TemplateArg && T->isRecordType()) - Kind = ConstantExprKind::ClassTemplateArgument; - else if (CCE == Sema::CCEK_TemplateArg) - Kind = ConstantExprKind::NonClassTemplateArgument; - else - Kind = ConstantExprKind::Normal; - - if (!E->EvaluateAsConstantExpr(Eval, S.Context, Kind) || - (RequireInt && !Eval.Val.isInt())) { - // The expression can't be folded, so we can't keep it at this position in - // the AST. - Result = ExprError(); - } else { - Value = Eval.Val; - - if (Notes.empty()) { - // It's a constant expression. - Expr *E = ConstantExpr::Create(S.Context, Result.get(), Value); - if (!PreNarrowingValue.isAbsent()) - Value = std::move(PreNarrowingValue); - return E; - } - } - - // It's not a constant expression. Produce an appropriate diagnostic. - if (Notes.size() == 1 && - Notes[0].second.getDiagID() == diag::note_invalid_subexpr_in_const_expr) { - S.Diag(Notes[0].first, diag::err_expr_not_cce) << CCE; - } else if (!Notes.empty() && Notes[0].second.getDiagID() == - diag::note_constexpr_invalid_template_arg) { - Notes[0].second.setDiagID(diag::err_constexpr_invalid_template_arg); - for (unsigned I = 0; I < Notes.size(); ++I) - S.Diag(Notes[I].first, Notes[I].second); - } else { - S.Diag(E->getBeginLoc(), diag::err_expr_not_cce) - << CCE << E->getSourceRange(); - for (unsigned I = 0; I < Notes.size(); ++I) - S.Diag(Notes[I].first, Notes[I].second); - } - return ExprError(); -} - /// CheckConvertedConstantExpression - Check that the expression From is a /// converted constant expression of type T, perform the conversion and produce /// the converted expression, per C++11 [expr.const]p3. @@ -6194,8 +6139,8 @@ static ExprResult CheckConvertedConstantExpression(Sema &S, Expr *From, Value = APValue(); return Result; } - return EvaluateConvertedConstantExpression(S, Result.get(), T, Value, CCE, - RequireInt, PreNarrowingValue); + return S.EvaluateConvertedConstantExpression(Result.get(), T, Value, CCE, + RequireInt, PreNarrowingValue); } ExprResult Sema::BuildConvertedConstantExpression(Expr *From, QualType T, @@ -6226,6 +6171,62 @@ ExprResult Sema::CheckConvertedConstantExpression(Expr *From, QualType T, return R; } +/// EvaluateConvertedConstantExpression - Evaluate an Expression +/// That is a converted constant expression +/// (which was built with BuildConvertedConstantExpression) +ExprResult +Sema::EvaluateConvertedConstantExpression(Expr *E, QualType T, APValue &Value, + Sema::CCEKind CCE, bool RequireInt, + const APValue &PreNarrowingValue) { + + ExprResult Result = E; + // Check the expression is a constant expression. + SmallVector Notes; + Expr::EvalResult Eval; + Eval.Diag = &Notes; + + ConstantExprKind Kind; + if (CCE == Sema::CCEK_TemplateArg && T->isRecordType()) + Kind = ConstantExprKind::ClassTemplateArgument; + else if (CCE == Sema::CCEK_TemplateArg) + Kind = ConstantExprKind::NonClassTemplateArgument; + else + Kind = ConstantExprKind::Normal; + + if (!E->EvaluateAsConstantExpr(Eval, Context, Kind) || + (RequireInt && !Eval.Val.isInt())) { + // The expression can't be folded, so we can't keep it at this position in + // the AST. + Result = ExprError(); + } else { + Value = Eval.Val; + + if (Notes.empty()) { + // It's a constant expression. + Expr *E = ConstantExpr::Create(Context, Result.get(), Value); + if (!PreNarrowingValue.isAbsent()) + Value = std::move(PreNarrowingValue); + return E; + } + } + + // It's not a constant expression. Produce an appropriate diagnostic. + if (Notes.size() == 1 && + Notes[0].second.getDiagID() == diag::note_invalid_subexpr_in_const_expr) { + Diag(Notes[0].first, diag::err_expr_not_cce) << CCE; + } else if (!Notes.empty() && Notes[0].second.getDiagID() == + diag::note_constexpr_invalid_template_arg) { + Notes[0].second.setDiagID(diag::err_constexpr_invalid_template_arg); + for (unsigned I = 0; I < Notes.size(); ++I) + Diag(Notes[I].first, Notes[I].second); + } else { + Diag(E->getBeginLoc(), diag::err_expr_not_cce) + << CCE << E->getSourceRange(); + for (unsigned I = 0; I < Notes.size(); ++I) + Diag(Notes[I].first, Notes[I].second); + } + return ExprError(); +} /// dropPointerConversions - If the given standard conversion sequence /// involves any pointer conversions, remove them. This may change diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index be9e294a72f31a4797a5bde58a4d870eacf99d65..603ad2b79cecb2901f8b16a448a83322066a5f90 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -895,7 +895,7 @@ void Sema::DiagnoseTemplateParameterShadow(SourceLocation Loc, Decl *PrevDecl) { // Make this a warning when MSVC compatibility is requested. unsigned DiagId = getLangOpts().MSVCCompat ? diag::ext_template_param_shadow : diag::err_template_param_shadow; - NamedDecl *ND = cast(PrevDecl); + const auto *ND = cast(PrevDecl); Diag(Loc, DiagId) << ND->getDeclName(); NoteTemplateParameterLocation(*ND); } @@ -7307,49 +7307,74 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, return E; } + QualType CanonParamType = Context.getCanonicalType(ParamType); + // Avoid making a copy when initializing a template parameter of class type + // from a template parameter object of the same type. This is going beyond + // the standard, but is required for soundness: in + // template struct X { X *p; X *q; }; + // ... we need p and q to have the same type. + // + // Similarly, don't inject a call to a copy constructor when initializing + // from a template parameter of the same type. + Expr *InnerArg = Arg->IgnoreParenImpCasts(); + if (ParamType->isRecordType() && isa(InnerArg) && + Context.hasSameUnqualifiedType(ParamType, InnerArg->getType())) { + NamedDecl *ND = cast(InnerArg)->getDecl(); + if (auto *TPO = dyn_cast(ND)) { + + SugaredConverted = TemplateArgument(TPO, ParamType); + CanonicalConverted = + TemplateArgument(TPO->getCanonicalDecl(), CanonParamType); + return Arg; + } + if (isa(ND)) { + SugaredConverted = TemplateArgument(Arg); + CanonicalConverted = + Context.getCanonicalTemplateArgument(SugaredConverted); + return Arg; + } + } + // The initialization of the parameter from the argument is // a constant-evaluated context. EnterExpressionEvaluationContext ConstantEvaluated( *this, Sema::ExpressionEvaluationContext::ConstantEvaluated); - if (getLangOpts().CPlusPlus17) { - QualType CanonParamType = Context.getCanonicalType(ParamType); - - // Avoid making a copy when initializing a template parameter of class type - // from a template parameter object of the same type. This is going beyond - // the standard, but is required for soundness: in - // template struct X { X *p; X *q; }; - // ... we need p and q to have the same type. - // - // Similarly, don't inject a call to a copy constructor when initializing - // from a template parameter of the same type. - Expr *InnerArg = Arg->IgnoreParenImpCasts(); - if (ParamType->isRecordType() && isa(InnerArg) && - Context.hasSameUnqualifiedType(ParamType, InnerArg->getType())) { - NamedDecl *ND = cast(InnerArg)->getDecl(); - if (auto *TPO = dyn_cast(ND)) { - - SugaredConverted = TemplateArgument(TPO, ParamType); - CanonicalConverted = - TemplateArgument(TPO->getCanonicalDecl(), CanonParamType); - return Arg; - } - if (isa(ND)) { - SugaredConverted = TemplateArgument(Arg); - CanonicalConverted = - Context.getCanonicalTemplateArgument(SugaredConverted); - return Arg; - } - } + bool IsConvertedConstantExpression = true; + if (isa(Arg) || ParamType->isRecordType()) { + InitializationKind Kind = InitializationKind::CreateForInit( + Arg->getBeginLoc(), /*DirectInit=*/false, Arg); + Expr *Inits[1] = {Arg}; + InitializedEntity Entity = + InitializedEntity::InitializeTemplateParameter(ParamType, Param); + InitializationSequence InitSeq(*this, Entity, Kind, Inits); + ExprResult Result = InitSeq.Perform(*this, Entity, Kind, Inits); + if (Result.isInvalid() || !Result.get()) + return ExprError(); + Result = ActOnConstantExpression(Result.get()); + if (Result.isInvalid() || !Result.get()) + return ExprError(); + Arg = ActOnFinishFullExpr(Result.get(), Arg->getBeginLoc(), + /*DiscardedValue=*/false, + /*IsConstexpr=*/true, /*IsTemplateArgument=*/true) + .get(); + IsConvertedConstantExpression = false; + } + if (getLangOpts().CPlusPlus17) { // C++17 [temp.arg.nontype]p1: // A template-argument for a non-type template parameter shall be // a converted constant expression of the type of the template-parameter. APValue Value; - ExprResult ArgResult = CheckConvertedConstantExpression( - Arg, ParamType, Value, CCEK_TemplateArg, Param); - if (ArgResult.isInvalid()) - return ExprError(); + ExprResult ArgResult; + if (IsConvertedConstantExpression) { + ArgResult = BuildConvertedConstantExpression(Arg, ParamType, + CCEK_TemplateArg, Param); + if (ArgResult.isInvalid()) + return ExprError(); + } else { + ArgResult = Arg; + } // For a value-dependent argument, CheckConvertedConstantExpression is // permitted (and expected) to be unable to determine a value. @@ -7360,6 +7385,13 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, return ArgResult; } + APValue PreNarrowingValue; + ArgResult = EvaluateConvertedConstantExpression( + ArgResult.get(), ParamType, Value, CCEK_TemplateArg, /*RequireInt=*/ + false, PreNarrowingValue); + if (ArgResult.isInvalid()) + return ExprError(); + // Convert the APValue to a TemplateArgument. switch (Value.getKind()) { case APValue::None: diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index aa367e0083e6dbf5988565da3bc9492f3e5eec02..d768bb72e07c09a3cdd8972f2dd1f7daa75b21ab 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -6495,34 +6495,6 @@ void Sema::PerformPendingInstantiations(bool LocalOnly) { PendingInstantiations.swap(delayedPCHInstantiations); } -// Instantiate all referenced specializations of the given function template -// definition. This make sure that constexpr function templates that are defined -// after the point of instantiation of their use can be evaluated after they -// are defined. see CWG2497. -void Sema::PerformPendingInstantiationsOfConstexprFunctions(FunctionDecl *Tpl) { - - auto InstantiateAll = [&](const auto &Range) { - for (NamedDecl *D : Range) { - FunctionDecl *Fun = cast(D); - InstantiateFunctionDefinition(Fun->getPointOfInstantiation(), Fun); - } - }; - - auto It = - PendingInstantiationsOfConstexprEntities.find(Tpl->getCanonicalDecl()); - if (It != PendingInstantiationsOfConstexprEntities.end()) { - auto Decls = std::move(It->second); - PendingInstantiationsOfConstexprEntities.erase(It); - InstantiateAll(Decls); - } - - llvm::SmallSetVector Decls; - if (ExternalSource) { - ExternalSource->ReadPendingInstantiationsOfConstexprEntity(Tpl, Decls); - InstantiateAll(Decls); - } -} - void Sema::PerformDependentDiagnostics(const DeclContext *Pattern, const MultiLevelTemplateArgumentList &TemplateArgs) { for (auto *DD : Pattern->ddiags()) { diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp index ef191c23650752c9a81fe49d283c6569ab5bac44..f22da838424b4155599c83341ac0f913f70a47f8 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -3709,19 +3709,6 @@ llvm::Error ASTReader::ReadASTBlock(ModuleFile &F, } break; - case PENDING_INSTANTIATIONS_OF_CONSTEXPR_ENTITIES: - if (Record.size() % 2 != 0) - return llvm::createStringError( - std::errc::illegal_byte_sequence, - "Invalid PENDING_INSTANTIATIONS_OF_CONSTEXPR_ENTITIES block"); - - for (unsigned I = 0, N = Record.size(); I != N; /* in loop */) { - DeclID Key = getGlobalDeclID(F, Record[I++]); - DeclID Value = getGlobalDeclID(F, Record[I++]); - PendingInstantiationsOfConstexprEntities[Key].insert(Value); - } - break; - case SEMA_DECL_REFS: if (Record.size() != 3) return llvm::createStringError(std::errc::illegal_byte_sequence, @@ -8731,20 +8718,6 @@ void ASTReader::ReadPendingInstantiations( PendingInstantiations.clear(); } -void ASTReader::ReadPendingInstantiationsOfConstexprEntity( - const NamedDecl *D, llvm::SmallSetVector &Decls) { - for (auto *Redecl : D->redecls()) { - if (!Redecl->isFromASTFile()) - continue; - DeclID Id = Redecl->getGlobalID(); - auto It = PendingInstantiationsOfConstexprEntities.find(Id); - if (It == PendingInstantiationsOfConstexprEntities.end()) - continue; - for (DeclID InstantiationId : It->second) - Decls.insert(cast(GetDecl(InstantiationId))); - } -} - void ASTReader::ReadLateParsedTemplates( llvm::MapVector> &LPTMap) { diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp index 8c8048fce026ef7a30c8bf3b5c44717d7eddc1cd..6df815234e235fb50437308ae6dc75238123229c 100644 --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -849,7 +849,6 @@ void ASTWriter::WriteBlockInfoBlock() { RECORD(SEMA_DECL_REFS); RECORD(WEAK_UNDECLARED_IDENTIFIERS); RECORD(PENDING_IMPLICIT_INSTANTIATIONS); - RECORD(PENDING_INSTANTIATIONS_OF_CONSTEXPR_ENTITIES); RECORD(UPDATE_VISIBLE); RECORD(DECL_UPDATE_OFFSETS); RECORD(DECL_UPDATES); @@ -4837,16 +4836,6 @@ ASTFileSignature ASTWriter::WriteASTCore(Sema &SemaRef, StringRef isysroot, assert(SemaRef.PendingLocalImplicitInstantiations.empty() && "There are local ones at end of translation unit!"); - // Build a record containing all pending instantiations of constexpr - // entities. - RecordData PendingInstantiationsOfConstexprEntities; - for (const auto &I : SemaRef.PendingInstantiationsOfConstexprEntities) { - for (const auto &Elem : I.second) { - AddDeclRef(I.first, PendingInstantiationsOfConstexprEntities); - AddDeclRef(Elem, PendingInstantiationsOfConstexprEntities); - } - } - // Build a record containing some declaration references. RecordData SemaDeclRefs; if (SemaRef.StdNamespace || SemaRef.StdBadAlloc || SemaRef.StdAlignValT) { @@ -5164,11 +5153,6 @@ ASTFileSignature ASTWriter::WriteASTCore(Sema &SemaRef, StringRef isysroot, if (!PendingInstantiations.empty()) Stream.EmitRecord(PENDING_IMPLICIT_INSTANTIATIONS, PendingInstantiations); - // Write the record containing pending instantiations of constexpr entities. - if (!PendingInstantiationsOfConstexprEntities.empty()) - Stream.EmitRecord(PENDING_INSTANTIATIONS_OF_CONSTEXPR_ENTITIES, - PendingInstantiationsOfConstexprEntities); - // Write the record containing declaration references of Sema. if (!SemaDeclRefs.empty()) Stream.EmitRecord(SEMA_DECL_REFS, SemaDeclRefs); diff --git a/clang/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp b/clang/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp index ffc7236d1e2551a8aa7c79444a78fe746fcfe698..6c7a1601402efac74ed7009617e1cd9ff2409aad 100644 --- a/clang/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "clang/AST/CharUnits.h" +#include "clang/AST/ParentMapContext.h" #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/StaticAnalyzer/Checkers/Taint.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" @@ -34,20 +35,46 @@ using llvm::formatv; namespace { enum OOB_Kind { OOB_Precedes, OOB_Exceeds, OOB_Taint }; -class ArrayBoundCheckerV2 : - public Checker { +struct Messages { + std::string Short, Full; +}; + +// NOTE: The `ArraySubscriptExpr` and `UnaryOperator` callbacks are `PostStmt` +// instead of `PreStmt` because the current implementation passes the whole +// expression to `CheckerContext::getSVal()` which only works after the +// symbolic evaluation of the expression. (To turn them into `PreStmt` +// callbacks, we'd need to duplicate the logic that evaluates these +// expressions.) The `MemberExpr` callback would work as `PreStmt` but it's +// defined as `PostStmt` for the sake of consistency with the other callbacks. +class ArrayBoundCheckerV2 : public Checker, + check::PostStmt, + check::PostStmt> { BugType BT{this, "Out-of-bound access"}; BugType TaintBT{this, "Out-of-bound access", categories::TaintedData}; + void performCheck(const Expr *E, CheckerContext &C) const; + void reportOOB(CheckerContext &C, ProgramStateRef ErrorState, OOB_Kind Kind, - NonLoc Offset, std::string RegName, std::string Msg) const; + NonLoc Offset, Messages Msgs) const; static bool isFromCtypeMacro(const Stmt *S, ASTContext &AC); + static bool isInAddressOf(const Stmt *S, ASTContext &AC); + public: - void checkLocation(SVal l, bool isLoad, const Stmt *S, - CheckerContext &C) const; + void checkPostStmt(const ArraySubscriptExpr *E, CheckerContext &C) const { + performCheck(E, C); + } + void checkPostStmt(const UnaryOperator *E, CheckerContext &C) const { + if (E->getOpcode() == UO_Deref) + performCheck(E, C); + } + void checkPostStmt(const MemberExpr *E, CheckerContext &C) const { + if (E->isArrow()) + performCheck(E->getBase(), C); + } }; + } // anonymous namespace /// For a given Location that can be represented as a symbolic expression @@ -149,9 +176,11 @@ getSimplifiedOffsets(NonLoc offset, nonloc::ConcreteInt extent, // where the first one corresponds to "value below threshold" and the second // corresponds to "value at or above threshold". Returns {nullptr, nullptr} in // the case when the evaluation fails. +// If the optional argument CheckEquality is true, then use BO_EQ instead of +// the default BO_LT after consistently applying the same simplification steps. static std::pair compareValueToThreshold(ProgramStateRef State, NonLoc Value, NonLoc Threshold, - SValBuilder &SVB) { + SValBuilder &SVB, bool CheckEquality = false) { if (auto ConcreteThreshold = Threshold.getAs()) { std::tie(Value, Threshold) = getSimplifiedOffsets(Value, *ConcreteThreshold, SVB); } @@ -167,8 +196,10 @@ compareValueToThreshold(ProgramStateRef State, NonLoc Value, NonLoc Threshold, return {nullptr, State}; } } + const BinaryOperatorKind OpKind = CheckEquality ? BO_EQ : BO_LT; auto BelowThreshold = - SVB.evalBinOpNN(State, BO_LT, Value, Threshold, SVB.getConditionType()).getAs(); + SVB.evalBinOpNN(State, OpKind, Value, Threshold, SVB.getConditionType()) + .getAs(); if (BelowThreshold) return State->assume(*BelowThreshold); @@ -217,16 +248,19 @@ static std::string getShortMsg(OOB_Kind Kind, std::string RegName) { return formatv(ShortMsgTemplates[Kind], RegName); } -static std::string getPrecedesMsg(std::string RegName, NonLoc Offset) { +static Messages getPrecedesMsgs(const SubRegion *Region, NonLoc Offset) { + std::string RegName = getRegionName(Region); SmallString<128> Buf; llvm::raw_svector_ostream Out(Buf); Out << "Access of " << RegName << " at negative byte offset"; if (auto ConcreteIdx = Offset.getAs()) Out << ' ' << ConcreteIdx->getValue(); - return std::string(Buf); + return {getShortMsg(OOB_Precedes, RegName), std::string(Buf)}; } -static std::string getExceedsMsg(ASTContext &ACtx, std::string RegName, - NonLoc Offset, NonLoc Extent, SVal Location) { + +static Messages getExceedsMsgs(ASTContext &ACtx, const SubRegion *Region, + NonLoc Offset, NonLoc Extent, SVal Location) { + std::string RegName = getRegionName(Region); const auto *EReg = Location.getAsRegion()->getAs(); assert(EReg && "this checker only handles element access"); QualType ElemType = EReg->getElementType(); @@ -273,20 +307,18 @@ static std::string getExceedsMsg(ASTContext &ACtx, std::string RegName, Out << "s"; } - return std::string(Buf); -} -static std::string getTaintMsg(std::string RegName) { - SmallString<128> Buf; - llvm::raw_svector_ostream Out(Buf); - Out << "Access of " << RegName - << " with a tainted offset that may be too large"; - return std::string(Buf); + return {getShortMsg(OOB_Exceeds, RegName), std::string(Buf)}; } -void ArrayBoundCheckerV2::checkLocation(SVal Location, bool IsLoad, - const Stmt *LoadS, - CheckerContext &C) const { +static Messages getTaintMsgs(const SubRegion *Region, const char *OffsetName) { + std::string RegName = getRegionName(Region); + return {formatv("Potential out of bound access to {0} with tainted {1}", + RegName, OffsetName), + formatv("Access of {0} with a tainted {1} that may be too large", + RegName, OffsetName)}; +} +void ArrayBoundCheckerV2::performCheck(const Expr *E, CheckerContext &C) const { // NOTE: Instead of using ProgramState::assumeInBound(), we are prototyping // some new logic here that reasons directly about memory region extents. // Once that logic is more mature, we can bring it back to assumeInBound() @@ -297,12 +329,14 @@ void ArrayBoundCheckerV2::checkLocation(SVal Location, bool IsLoad, // have some flexibility in defining the base region, we can achieve // various levels of conservatism in our buffer overflow checking. + const SVal Location = C.getSVal(E); + // The header ctype.h (from e.g. glibc) implements the isXXXXX() macros as // #define isXXXXX(arg) (LOOKUP_TABLE[arg] & BITMASK_FOR_XXXXX) // and incomplete analysis of these leads to false positives. As even // accurate reports would be confusing for the users, just disable reports // from these macros: - if (isFromCtypeMacro(LoadS, C.getASTContext())) + if (isFromCtypeMacro(E, C.getASTContext())) return; ProgramStateRef State = C.getState(); @@ -331,9 +365,8 @@ void ArrayBoundCheckerV2::checkLocation(SVal Location, bool IsLoad, if (PrecedesLowerBound && !WithinLowerBound) { // We know that the index definitely precedes the lower bound. - std::string RegName = getRegionName(Reg); - std::string Msg = getPrecedesMsg(RegName, ByteOffset); - reportOOB(C, PrecedesLowerBound, OOB_Precedes, ByteOffset, RegName, Msg); + Messages Msgs = getPrecedesMsgs(Reg, ByteOffset); + reportOOB(C, PrecedesLowerBound, OOB_Precedes, ByteOffset, Msgs); return; } @@ -350,17 +383,38 @@ void ArrayBoundCheckerV2::checkLocation(SVal Location, bool IsLoad, if (ExceedsUpperBound) { if (!WithinUpperBound) { // We know that the index definitely exceeds the upper bound. - std::string RegName = getRegionName(Reg); - std::string Msg = getExceedsMsg(C.getASTContext(), RegName, ByteOffset, - *KnownSize, Location); - reportOOB(C, ExceedsUpperBound, OOB_Exceeds, ByteOffset, RegName, Msg); + if (isa(E) && isInAddressOf(E, C.getASTContext())) { + // ...but this is within an addressof expression, so we need to check + // for the exceptional case that `&array[size]` is valid. + auto [EqualsToThreshold, NotEqualToThreshold] = + compareValueToThreshold(ExceedsUpperBound, ByteOffset, *KnownSize, + SVB, /*CheckEquality=*/true); + if (EqualsToThreshold && !NotEqualToThreshold) { + // We are definitely in the exceptional case, so return early + // instead of reporting a bug. + C.addTransition(EqualsToThreshold); + return; + } + } + Messages Msgs = getExceedsMsgs(C.getASTContext(), Reg, ByteOffset, + *KnownSize, Location); + reportOOB(C, ExceedsUpperBound, OOB_Exceeds, ByteOffset, Msgs); return; } if (isTainted(State, ByteOffset)) { - // Both cases are possible, but the index is tainted, so report. + // Both cases are possible, but the offset is tainted, so report. std::string RegName = getRegionName(Reg); - std::string Msg = getTaintMsg(RegName); - reportOOB(C, ExceedsUpperBound, OOB_Taint, ByteOffset, RegName, Msg); + + // Diagnostic detail: "tainted offset" is always correct, but the + // common case is that 'idx' is tainted in 'arr[idx]' and then it's + // nicer to say "tainted index". + const char *OffsetName = "offset"; + if (const auto *ASE = dyn_cast(E)) + if (isTainted(State, ASE->getIdx(), C.getLocationContext())) + OffsetName = "index"; + + Messages Msgs = getTaintMsgs(Reg, OffsetName); + reportOOB(C, ExceedsUpperBound, OOB_Taint, ByteOffset, Msgs); return; } } @@ -374,17 +428,14 @@ void ArrayBoundCheckerV2::checkLocation(SVal Location, bool IsLoad, void ArrayBoundCheckerV2::reportOOB(CheckerContext &C, ProgramStateRef ErrorState, OOB_Kind Kind, - NonLoc Offset, std::string RegName, - std::string Msg) const { + NonLoc Offset, Messages Msgs) const { ExplodedNode *ErrorNode = C.generateErrorNode(ErrorState); if (!ErrorNode) return; - std::string ShortMsg = getShortMsg(Kind, RegName); - auto BR = std::make_unique( - Kind == OOB_Taint ? TaintBT : BT, ShortMsg, Msg, ErrorNode); + Kind == OOB_Taint ? TaintBT : BT, Msgs.Short, Msgs.Full, ErrorNode); // Track back the propagation of taintedness. if (Kind == OOB_Taint) @@ -413,6 +464,18 @@ bool ArrayBoundCheckerV2::isFromCtypeMacro(const Stmt *S, ASTContext &ACtx) { (MacroName == "isupper") || (MacroName == "isxdigit")); } +bool ArrayBoundCheckerV2::isInAddressOf(const Stmt *S, ASTContext &ACtx) { + ParentMapContext &ParentCtx = ACtx.getParentMapContext(); + do { + const DynTypedNodeList Parents = ParentCtx.getParents(*S); + if (Parents.empty()) + return false; + S = Parents[0].get(); + } while (isa_and_nonnull(S)); + const auto *UnaryOp = dyn_cast_or_null(S); + return UnaryOp && UnaryOp->getOpcode() == UO_AddrOf; +} + void ento::registerArrayBoundCheckerV2(CheckerManager &mgr) { mgr.registerChecker(); } diff --git a/clang/lib/StaticAnalyzer/Checkers/BitwiseShiftChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/BitwiseShiftChecker.cpp index 8a933d124d7700d10d65909a4f28ff385d5cae77..d4aa9fa1339f47cacd307e01f8a61e30fa254807 100644 --- a/clang/lib/StaticAnalyzer/Checkers/BitwiseShiftChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/BitwiseShiftChecker.cpp @@ -172,18 +172,24 @@ BugReportPtr BitwiseShiftValidator::checkOvershift() { const SVal Right = Ctx.getSVal(operandExpr(OperandSide::Right)); - std::string RightOpStr = ""; + std::string RightOpStr = "", LowerBoundStr = ""; if (auto ConcreteRight = Right.getAs()) RightOpStr = formatv(" '{0}'", ConcreteRight->getValue()); + else { + SValBuilder &SVB = Ctx.getSValBuilder(); + if (const llvm::APSInt *MinRight = SVB.getMinValue(FoldedState, Right)) { + LowerBoundStr = formatv(" >= {0},", MinRight->getExtValue()); + } + } std::string ShortMsg = formatv( "{0} shift{1}{2} overflows the capacity of '{3}'", isLeftShift() ? "Left" : "Right", RightOpStr.empty() ? "" : " by", RightOpStr, LHSTy.getAsString()); - std::string Msg = - formatv("The result of {0} shift is undefined because the right " - "operand{1} is not smaller than {2}, the capacity of '{3}'", - shiftDir(), RightOpStr, LHSBitWidth, LHSTy.getAsString()); + std::string Msg = formatv( + "The result of {0} shift is undefined because the right " + "operand{1} is{2} not smaller than {3}, the capacity of '{4}'", + shiftDir(), RightOpStr, LowerBoundStr, LHSBitWidth, LHSTy.getAsString()); return createBugReport(ShortMsg, Msg); } diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index a4799b5f762caee2957d9ade00bfbaff85e75d0d..925fc90e355431778894e2ee0aa717a4a6e87002 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -1072,6 +1072,9 @@ void StreamChecker::evalFtell(const FnDescription *Desc, const CallEvent &Call, ProgramStateRef StateFailed = State->BindExpr( CE, C.getLocationContext(), SVB.makeIntVal(-1, C.getASTContext().LongTy)); + // This function does not affect the stream state. + // Still we add success and failure state with the appropriate return value. + // StdLibraryFunctionsChecker can change these states (set the 'errno' state). C.addTransition(StateNotFailed); C.addTransition(StateFailed); } diff --git a/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp b/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp index 5de99384449a4c8c1b3080938c66b5275e4e915f..25d066c4652f2bf1474b61b1835591e1f077cfea 100644 --- a/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp +++ b/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp @@ -1876,6 +1876,12 @@ public: const llvm::APSInt *getSymVal(ProgramStateRef State, SymbolRef Sym) const override; + const llvm::APSInt *getSymMinVal(ProgramStateRef State, + SymbolRef Sym) const override; + + const llvm::APSInt *getSymMaxVal(ProgramStateRef State, + SymbolRef Sym) const override; + ProgramStateRef removeDeadBindings(ProgramStateRef State, SymbolReaper &SymReaper) override; @@ -2863,6 +2869,22 @@ const llvm::APSInt *RangeConstraintManager::getSymVal(ProgramStateRef St, return T ? T->getConcreteValue() : nullptr; } +const llvm::APSInt *RangeConstraintManager::getSymMinVal(ProgramStateRef St, + SymbolRef Sym) const { + const RangeSet *T = getConstraint(St, Sym); + if (!T || T->isEmpty()) + return nullptr; + return &T->getMinValue(); +} + +const llvm::APSInt *RangeConstraintManager::getSymMaxVal(ProgramStateRef St, + SymbolRef Sym) const { + const RangeSet *T = getConstraint(St, Sym); + if (!T || T->isEmpty()) + return nullptr; + return &T->getMaxValue(); +} + //===----------------------------------------------------------------------===// // Remove dead symbols from existing constraints //===----------------------------------------------------------------------===// diff --git a/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp b/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp index 0f2d6c8fc80bb0229fbb4895761625bd17d2998e..45e48d435aca6a2c2d374dd95d074d687f21f2a4 100644 --- a/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp +++ b/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp @@ -28,7 +28,11 @@ class SimpleSValBuilder : public SValBuilder { // returns NULL. // This is an implementation detail. Checkers should use `getKnownValue()` // instead. - const llvm::APSInt *getConstValue(ProgramStateRef state, SVal V); + static const llvm::APSInt *getConstValue(ProgramStateRef state, SVal V); + + // Helper function that returns the value stored in a nonloc::ConcreteInt or + // loc::ConcreteInt. + static const llvm::APSInt *getConcreteValue(SVal V); // With one `simplifySValOnce` call, a compound symbols might collapse to // simpler symbol tree that is still possible to further simplify. Thus, we @@ -76,6 +80,16 @@ public: /// (integer) value, that value is returned. Otherwise, returns NULL. const llvm::APSInt *getKnownValue(ProgramStateRef state, SVal V) override; + /// Evaluates a given SVal by recursively evaluating and simplifying the + /// children SVals, then returns its minimal possible (integer) value. If the + /// constraint manager cannot provide a meaningful answer, this returns NULL. + const llvm::APSInt *getMinValue(ProgramStateRef state, SVal V) override; + + /// Evaluates a given SVal by recursively evaluating and simplifying the + /// children SVals, then returns its maximal possible (integer) value. If the + /// constraint manager cannot provide a meaningful answer, this returns NULL. + const llvm::APSInt *getMaxValue(ProgramStateRef state, SVal V) override; + SVal simplifySVal(ProgramStateRef State, SVal V) override; SVal MakeSymIntVal(const SymExpr *LHS, BinaryOperator::Opcode op, @@ -1182,18 +1196,22 @@ SVal SimpleSValBuilder::evalBinOpLN(ProgramStateRef state, const llvm::APSInt *SimpleSValBuilder::getConstValue(ProgramStateRef state, SVal V) { - if (V.isUnknownOrUndef()) - return nullptr; + if (const llvm::APSInt *Res = getConcreteValue(V)) + return Res; + if (SymbolRef Sym = V.getAsSymbol()) + return state->getConstraintManager().getSymVal(state, Sym); + + return nullptr; +} + +const llvm::APSInt *SimpleSValBuilder::getConcreteValue(SVal V) { if (std::optional X = V.getAs()) return &X->getValue(); if (std::optional X = V.getAs()) return &X->getValue(); - if (SymbolRef Sym = V.getAsSymbol()) - return state->getConstraintManager().getSymVal(state, Sym); - return nullptr; } @@ -1202,6 +1220,32 @@ const llvm::APSInt *SimpleSValBuilder::getKnownValue(ProgramStateRef state, return getConstValue(state, simplifySVal(state, V)); } +const llvm::APSInt *SimpleSValBuilder::getMinValue(ProgramStateRef state, + SVal V) { + V = simplifySVal(state, V); + + if (const llvm::APSInt *Res = getConcreteValue(V)) + return Res; + + if (SymbolRef Sym = V.getAsSymbol()) + return state->getConstraintManager().getSymMinVal(state, Sym); + + return nullptr; +} + +const llvm::APSInt *SimpleSValBuilder::getMaxValue(ProgramStateRef state, + SVal V) { + V = simplifySVal(state, V); + + if (const llvm::APSInt *Res = getConcreteValue(V)) + return Res; + + if (SymbolRef Sym = V.getAsSymbol()) + return state->getConstraintManager().getSymMaxVal(state, Sym); + + return nullptr; +} + SVal SimpleSValBuilder::simplifyUntilFixpoint(ProgramStateRef State, SVal Val) { SVal SimplifiedVal = simplifySValOnce(State, Val); while (SimplifiedVal != Val) { diff --git a/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp b/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp index 9099c18391e4d290320697c6a5c39970ecf00dd7..1058ddb8254cde9246e855a27bef64bd69643a09 100644 --- a/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp +++ b/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp @@ -316,7 +316,8 @@ void ModuleDepCollector::applyDiscoveredDependencies(CompilerInvocation &CI) { static std::string getModuleContextHash(const ModuleDeps &MD, const CowCompilerInvocation &CI, - bool EagerLoadModules) { + bool EagerLoadModules, + llvm::vfs::FileSystem &VFS) { llvm::HashBuilder, llvm::endianness::native> HashBuilder; SmallString<32> Scratch; @@ -325,6 +326,9 @@ static std::string getModuleContextHash(const ModuleDeps &MD, // will be readable. HashBuilder.add(getClangFullRepositoryVersion()); HashBuilder.add(serialization::VERSION_MAJOR, serialization::VERSION_MINOR); + llvm::ErrorOr CWD = VFS.getCurrentWorkingDirectory(); + if (CWD) + HashBuilder.add(*CWD); // Hash the BuildInvocation without any input files. SmallString<0> ArgVec; @@ -356,7 +360,8 @@ static std::string getModuleContextHash(const ModuleDeps &MD, void ModuleDepCollector::associateWithContextHash( const CowCompilerInvocation &CI, ModuleDeps &Deps) { - Deps.ID.ContextHash = getModuleContextHash(Deps, CI, EagerLoadModules); + Deps.ID.ContextHash = getModuleContextHash( + Deps, CI, EagerLoadModules, ScanInstance.getVirtualFileSystem()); bool Inserted = ModuleDepsByID.insert({Deps.ID, &Deps}).second; (void)Inserted; assert(Inserted && "duplicate module mapping"); diff --git a/clang/test/Analysis/bitwise-shift-common.c b/clang/test/Analysis/bitwise-shift-common.c index fe49f0f992072dd6e17526beba2fef58e593a1c4..39108bc838bf277d7745348d33fd8b531450644a 100644 --- a/clang/test/Analysis/bitwise-shift-common.c +++ b/clang/test/Analysis/bitwise-shift-common.c @@ -1,4 +1,5 @@ // RUN: %clang_analyze_cc1 -analyzer-checker=core.BitwiseShift \ +// RUN: -analyzer-checker=debug.ExprInspection \ // RUN: -analyzer-output=text -verify \ // RUN: -triple x86_64-pc-linux-gnu -x c %s \ // RUN: -Wno-shift-count-negative -Wno-shift-negative-value \ @@ -6,6 +7,7 @@ // RUN: -Wno-shift-sign-overflow // // RUN: %clang_analyze_cc1 -analyzer-checker=core.BitwiseShift \ +// RUN: -analyzer-checker=debug.ExprInspection \ // RUN: -analyzer-config core.BitwiseShift:Pedantic=true \ // RUN: -analyzer-output=text -verify \ // RUN: -triple x86_64-pc-linux-gnu -x c++ -std=c++20 %s \ @@ -85,17 +87,23 @@ int too_large_right_operand_symbolic(int left, int right) { return 0; return left >> right; // expected-warning@-1 {{Right shift overflows the capacity of 'int'}} - // expected-note@-2 {{The result of right shift is undefined because the right operand is not smaller than 32, the capacity of 'int'}} - // NOTE: the messages of the checker are a bit vague in this case, but the - // tracking of the variables reveals our knowledge about them. + // expected-note@-2 {{The result of right shift is undefined because the right operand is >= 32, not smaller than 32, the capacity of 'int'}} } +void clang_analyzer_value(int); int too_large_right_operand_compound(unsigned short arg) { // Note: this would be valid code with an 'unsigned int' because // unsigned addition is allowed to overflow. + clang_analyzer_value(32+arg); + // expected-warning@-1 {{32s:{ [-2147483648, 2147483647] }} + // expected-note@-2 {{32s:{ [-2147483648, 2147483647] }} return 1 << (32 + arg); // expected-warning@-1 {{Left shift overflows the capacity of 'int'}} // expected-note@-2 {{The result of left shift is undefined because the right operand is not smaller than 32, the capacity of 'int'}} + // FIXME: this message should be + // {{The result of left shift is undefined because the right operand is >= 32, not smaller than 32, the capacity of 'int'}} + // but for some reason neither the new logic, nor debug.ExprInspection and + // clang_analyzer_value reports this range information. } // TEST STATE UPDATES diff --git a/clang/test/Analysis/out-of-bounds-diagnostics.c b/clang/test/Analysis/out-of-bounds-diagnostics.c index da1573665fa795120de9cabdcd045b91de5fd405..769a8954f7966915a346a2259e40ea6cef0cb7a3 100644 --- a/clang/test/Analysis/out-of-bounds-diagnostics.c +++ b/clang/test/Analysis/out-of-bounds-diagnostics.c @@ -9,6 +9,14 @@ void arrayUnderflow(void) { // expected-note@-2 {{Access of 'array' at negative byte offset -12}} } +int underflowWithDeref(void) { + int *p = array; + --p; + return *p; + // expected-warning@-1 {{Out of bound access to memory preceding 'array'}} + // expected-note@-2 {{Access of 'array' at negative byte offset -4}} +} + int scanf(const char *restrict fmt, ...); void taintedIndex(void) { @@ -17,6 +25,38 @@ void taintedIndex(void) { // expected-note@-1 {{Taint originated here}} // expected-note@-2 {{Taint propagated to the 2nd argument}} array[index] = 5; + // expected-warning@-1 {{Potential out of bound access to 'array' with tainted index}} + // expected-note@-2 {{Access of 'array' with a tainted index that may be too large}} +} + +int *taintedIndexAfterTheEndPtr(void) { + // NOTE: Technically speaking, this testcase does not trigger any UB because + // &array[10] is the after-the-end pointer which is well-defined; but this is + // a bug-prone situation and far from the idiomatic use of `&array[size]`, so + // it's better to report an error. This report can be easily silenced by + // writing array+index instead of &array[index]. + int index; + scanf("%d", &index); + // expected-note@-1 {{Taint originated here}} + // expected-note@-2 {{Taint propagated to the 2nd argument}} + if (index < 0 || index > 10) + return array; + // expected-note@-2 {{Assuming 'index' is >= 0}} + // expected-note@-3 {{Left side of '||' is false}} + // expected-note@-4 {{Assuming 'index' is <= 10}} + // expected-note@-5 {{Taking false branch}} + return &array[index]; + // expected-warning@-1 {{Potential out of bound access to 'array' with tainted index}} + // expected-note@-2 {{Access of 'array' with a tainted index that may be too large}} +} + +void taintedOffset(void) { + int index; + scanf("%d", &index); + // expected-note@-1 {{Taint originated here}} + // expected-note@-2 {{Taint propagated to the 2nd argument}} + int *p = array + index; + p[0] = 5; // expected-warning@-1 {{Potential out of bound access to 'array' with tainted offset}} // expected-note@-2 {{Access of 'array' with a tainted offset that may be too large}} } @@ -27,6 +67,47 @@ void arrayOverflow(void) { // expected-note@-2 {{Access of 'array' at index 12, while it holds only 10 'int' elements}} } +void flippedOverflow(void) { + 12[array] = 5; + // expected-warning@-1 {{Out of bound access to memory after the end of 'array'}} + // expected-note@-2 {{Access of 'array' at index 12, while it holds only 10 'int' elements}} +} + +int *afterTheEndPtr(void) { + // This is an unusual but standard-compliant way of writing (array + 10). + return &array[10]; // no-warning +} + +int useAfterTheEndPtr(void) { + // ... but dereferencing the after-the-end pointer is still invalid. + return *afterTheEndPtr(); + // expected-warning@-1 {{Out of bound access to memory after the end of 'array'}} + // expected-note@-2 {{Access of 'array' at index 10, while it holds only 10 'int' elements}} +} + +int *afterAfterTheEndPtr(void) { + // This is UB, it's invalid to form an after-after-the-end pointer. + return &array[11]; + // expected-warning@-1 {{Out of bound access to memory after the end of 'array'}} + // expected-note@-2 {{Access of 'array' at index 11, while it holds only 10 'int' elements}} +} + +int *potentialAfterTheEndPtr(int idx) { + if (idx < 10) { /* ...do something... */ } + // expected-note@-1 {{Assuming 'idx' is >= 10}} + // expected-note@-2 {{Taking false branch}} + return &array[idx]; + // expected-warning@-1 {{Out of bound access to memory after the end of 'array'}} + // expected-note@-2 {{Access of 'array' at an overflowing index, while it holds only 10 'int' elements}} + // NOTE: On the idx >= 10 branch the normal "optimistic" behavior would've + // been continuing with the assumption that idx == 10 and the return value is + // a legitimate after-the-end pointer. The checker deviates from this by + // reporting an error because this situation is very suspicious and far from + // the idiomatic `&array[size]` expressions. If the report is FP, the + // developer can easily silence it by writing array+idx instead of + // &array[idx]. +} + int scalar; int scalarOverflow(void) { return (&scalar)[1]; @@ -41,12 +122,6 @@ int oneElementArrayOverflow(void) { // expected-note@-2 {{Access of 'oneElementArray' at index 1, while it holds only a single 'int' element}} } -short convertedArray(void) { - return ((short*)array)[47]; - // expected-warning@-1 {{Out of bound access to memory after the end of 'array'}} - // expected-note@-2 {{Access of 'array' at index 47, while it holds only 20 'short' elements}} -} - struct vec { int len; double elems[64]; @@ -64,6 +139,28 @@ double arrayInStructPtr(struct vec *pv) { // expected-note@-2 {{Access of the field 'elems' at index 64, while it holds only 64 'double' elements}} } +struct item { + int a, b; +} itemArray[20] = {0}; + +int arrayOfStructs(void) { + return itemArray[35].a; + // expected-warning@-1 {{Out of bound access to memory after the end of 'itemArray'}} + // expected-note@-2 {{Access of 'itemArray' at index 35, while it holds only 20 'struct item' elements}} +} + +int arrayOfStructsArrow(void) { + return (itemArray + 35)->b; + // expected-warning@-1 {{Out of bound access to memory after the end of 'itemArray'}} + // expected-note@-2 {{Access of 'itemArray' at index 35, while it holds only 20 'struct item' elements}} +} + +short convertedArray(void) { + return ((short*)array)[47]; + // expected-warning@-1 {{Out of bound access to memory after the end of 'array'}} + // expected-note@-2 {{Access of 'array' at index 47, while it holds only 20 'short' elements}} +} + struct two_bytes { char lo, hi; }; @@ -85,7 +182,9 @@ int intFromString(void) { } int intFromStringDivisible(void) { - // However, this is reported with indices/elements, because the extent happens to be a multiple of 4. + // However, this is reported with indices/elements, because the extent + // (of the string that consists of 'a', 'b', 'c' and '\0') happens to be a + // multiple of 4 bytes (= sizeof(int)). return ((const int*)"abc")[20]; // expected-warning@-1 {{Out of bound access to memory after the end of the string literal}} // expected-note@-2 {{Access of the string literal at index 20, while it holds only a single 'int' element}} diff --git a/clang/test/Analysis/out-of-bounds-new.cpp b/clang/test/Analysis/out-of-bounds-new.cpp index af4ec47d8358aab4f7d3b240a711be9b7bb1eae8..f541bdf810d79ce6ee20360a137f92f94802e39b 100644 --- a/clang/test/Analysis/out-of-bounds-new.cpp +++ b/clang/test/Analysis/out-of-bounds-new.cpp @@ -154,3 +154,29 @@ void test_dynamic_size2(unsigned m,unsigned n){ unsigned *U = nullptr; U = new unsigned[m + n + 1]; } + +//Test creating invalid references, which break the invariant that a reference +//is always holding a value, and could lead to nasty runtime errors. +//(This is not related to operator new, but placed in this file because the +//other test files are not C++.) +int array[10] = {0}; + +void test_after_the_end_reference() { + int &ref = array[10]; // expected-warning{{Out of bound access to memory}} +} + +void test_after_after_the_end_reference() { + int &ref = array[11]; // expected-warning{{Out of bound access to memory}} +} + +int test_reference_that_might_be_after_the_end(int idx) { + // This TC produces no warning because separate analysis of (idx == 10) is + // only introduced _after_ the creation of the reference ref. + if (idx < 0 || idx > 10) + return -2; + int &ref = array[idx]; + if (idx == 10) + return -1; + return ref; +} + diff --git a/clang/test/Analysis/taint-diagnostic-visitor.c b/clang/test/Analysis/taint-diagnostic-visitor.c index 67dc67e627b3b9c0ffb743bf96809a2710396e36..a3fa1639bffeee5c0e366090edd02749f9309599 100644 --- a/clang/test/Analysis/taint-diagnostic-visitor.c +++ b/clang/test/Analysis/taint-diagnostic-visitor.c @@ -29,8 +29,8 @@ int taintDiagnosticOutOfBound(void) { int Array[] = {1, 2, 3, 4, 5}; scanf("%d", &index); // expected-note {{Taint originated here}} // expected-note@-1 {{Taint propagated to the 2nd argument}} - return Array[index]; // expected-warning {{Potential out of bound access to 'Array' with tainted offset}} - // expected-note@-1 {{Access of 'Array' with a tainted offset that may be too large}} + return Array[index]; // expected-warning {{Potential out of bound access to 'Array' with tainted index}} + // expected-note@-1 {{Access of 'Array' with a tainted index that may be too large}} } int taintDiagnosticDivZero(int operand) { diff --git a/clang/test/CXX/basic/basic.lookup/basic.lookup.argdep/p5-ex2.cpp b/clang/test/CXX/basic/basic.lookup/basic.lookup.argdep/p5-ex2.cpp index c1a824bd51493202cc2ad83696403aa74b437684..a27946bd90a46c88227e9325d6dd2f060067c3e3 100644 --- a/clang/test/CXX/basic/basic.lookup/basic.lookup.argdep/p5-ex2.cpp +++ b/clang/test/CXX/basic/basic.lookup/basic.lookup.argdep/p5-ex2.cpp @@ -50,9 +50,7 @@ void test() { auto x = make(); // error: R and R::f are not visible here - R::f(x); // expected-error {{declaration of 'R' must be imported from module 'N' before it is required}} - // expected-note@N.cpp:4 {{declaration here is not visible}} - // expected-error@-2 {{no type named 'f' in namespace 'R'}} + R::f(x); // expected-error {{no type named 'f' in namespace 'R'}} f(x); // Found by [basic.lookup.argdep] / p4.3 diff --git a/clang/test/CXX/drs/dr0xx.cpp b/clang/test/CXX/drs/dr0xx.cpp index 0d60d55326c8781b1c87b12b02cf000ce8346495..e5f675fb448986177b0c5165259461a1aeba92ea 100644 --- a/clang/test/CXX/drs/dr0xx.cpp +++ b/clang/test/CXX/drs/dr0xx.cpp @@ -1,9 +1,9 @@ -// RUN: %clang_cc1 -std=c++98 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors -Wno-bind-to-temporary-copy -// RUN: %clang_cc1 -std=c++11 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors -triple %itanium_abi_triple -// RUN: %clang_cc1 -std=c++14 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors -triple %itanium_abi_triple -// RUN: %clang_cc1 -std=c++17 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors -triple %itanium_abi_triple -// RUN: %clang_cc1 -std=c++20 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors -triple %itanium_abi_triple -// RUN: %clang_cc1 -std=c++23 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors -triple %itanium_abi_triple +// RUN: %clang_cc1 -std=c++98 %s -verify=expected,cxx98,cxx98-14 -fexceptions -fcxx-exceptions -pedantic-errors -Wno-bind-to-temporary-copy +// RUN: %clang_cc1 -std=c++11 %s -verify=expected,since-cxx11,cxx98-14,cxx11-14 -fexceptions -fcxx-exceptions -pedantic-errors -triple %itanium_abi_triple +// RUN: %clang_cc1 -std=c++14 %s -verify=expected,since-cxx11,cxx98-14,cxx11-14 -fexceptions -fcxx-exceptions -pedantic-errors -triple %itanium_abi_triple +// RUN: %clang_cc1 -std=c++17 %s -verify=expected,since-cxx11,since-cxx17 -fexceptions -fcxx-exceptions -pedantic-errors -triple %itanium_abi_triple +// RUN: %clang_cc1 -std=c++20 %s -verify=expected,since-cxx11,since-cxx17 -fexceptions -fcxx-exceptions -pedantic-errors -triple %itanium_abi_triple +// RUN: %clang_cc1 -std=c++23 %s -verify=expected,since-cxx11,since-cxx17 -fexceptions -fcxx-exceptions -pedantic-errors -triple %itanium_abi_triple namespace dr1 { // dr1: no namespace X { extern "C" void dr1_f(int a = 1); } @@ -26,9 +26,11 @@ namespace dr1 { // dr1: no namespace X { void z(int); } - void X::z(int = 1) {} // expected-note {{previous}} + void X::z(int = 1) {} // #dr1-z namespace X { - void z(int = 1); // expected-error {{redefinition of default argument}} + void z(int = 1); + // expected-error@-1 {{redefinition of default argument}} + // expected-note@#dr1-z {{previous definition is here}} } void i(int = 1); @@ -50,17 +52,21 @@ namespace dr1 { // dr1: no namespace dr3 { // dr3: yes template struct A {}; - template void f(T) { A a; } // expected-note {{implicit instantiation}} + template void f(T) { A a; } // #dr3-f-T template void f(int); - template<> struct A {}; // expected-error {{explicit specialization of 'dr3::A' after instantiation}} + template<> struct A {}; + // expected-error@-1 {{explicit specialization of 'dr3::A' after instantiation}} + // expected-note@#dr3-f-T {{implicit instantiation first required here}} } -namespace dr4 { // dr4: yes +namespace dr4 { // dr4: 2.8 extern "C" { static void dr4_f(int) {} static void dr4_f(float) {} - void dr4_g(int) {} // expected-note {{previous}} - void dr4_g(float) {} // expected-error {{conflicting types}} + void dr4_g(int) {} // #dr4-g-int + void dr4_g(float) {} + // expected-error@-1 {{conflicting types for 'dr4_g'}} + // expected-note@#dr4-g-int {{previous definition is here}} } } @@ -80,14 +86,20 @@ namespace dr5 { // dr5: 3.1 namespace dr7 { // dr7: 3.4 class A { public: ~A(); }; - class B : virtual private A {}; // expected-note 2 {{declared private here}} - class C : public B {} c; // expected-error 2 {{inherited virtual base class 'A' has private destructor}} \ - // expected-note {{implicit default constructor for 'dr7::C' first required here}} \ - // expected-note {{implicit destructor for 'dr7::C' first required here}} + class B : virtual private A {}; // #dr7-B + class C : public B {} c; // #dr7-C + // expected-error@#dr7-C {{inherited virtual base class 'A' has private destructor}} + // expected-note@#dr7-C {{in implicit default constructor for 'dr7::C' first required here}} + // expected-note@#dr7-B {{declared private here}} + // expected-error@#dr7-C {{inherited virtual base class 'A' has private destructor}} + // expected-note@#dr7-C {{in implicit destructor for 'dr7::C' first required here}} + // expected-note@#dr7-B {{declared private here}} class VeryDerivedC : public B, virtual public A {} vdc; - class X { ~X(); }; // expected-note {{here}} - class Y : X { ~Y() {} }; // expected-error {{private destructor}} + class X { ~X(); }; // #dr7-X + class Y : X { ~Y() {} }; + // expected-error@-1 {{base class 'X' has private destructor}} + // expected-note@#dr7-X {{implicitly declared private here}} namespace PR16370 { // This regressed the first time DR7 was fixed. struct S1 { virtual ~S1(); }; @@ -117,13 +129,16 @@ namespace dr8 { // dr8: dup 45 namespace dr9 { // dr9: 2.8 struct B { protected: - int m; // expected-note {{here}} + int m; // #dr9-m friend int R1(); }; - struct N : protected B { // expected-note {{protected}} + struct N : protected B { // #dr9-N friend int R2(); } n; - int R1() { return n.m; } // expected-error {{protected member}} + int R1() { return n.m; } + // expected-error@-1 {{'m' is a protected member of 'dr9::B'}} + // expected-note@#dr9-N {{constrained by protected inheritance here}} + // expected-note@#dr9-m {{member is declared here}} int R2() { return n.m; } } @@ -142,7 +157,8 @@ namespace dr11 { // dr11: yes }; template struct B : T { using T::V; - V v; // expected-error {{unknown type name}} + V v; + // expected-error@-1 {{unknown type name 'V'}} }; struct X { typedef int U; }; A ax; @@ -190,27 +206,40 @@ namespace dr14 { // dr14: 3.4 extern "C" int dr14_f() { return c.k; } } - namespace X { typedef int T; typedef int U; } // expected-note {{candidate}} - namespace Y { typedef int T; typedef long U; } // expected-note {{candidate}} + namespace X { typedef int T; typedef int U; } // #dr14-X-U + namespace Y { typedef int T; typedef long U; } // #dr14-Y-U T t; // ok, same type both times - U u; // expected-error {{ambiguous}} + U u; + // expected-error@-1 {{reference to 'U' is ambiguous}} + // expected-note@#dr14-X-U {{candidate found by name lookup is 'dr14::X::U'}} + // expected-note@#dr14-Y-U {{candidate found by name lookup is 'dr14::Y::U'}} } namespace dr15 { // dr15: yes - template void f(int); // expected-note {{previous}} - template void f(int = 0); // expected-error {{default arguments cannot be added}} + template void f(int); // #dr15-f-decl-first + template void f(int = 0); + // expected-error@-1 {{default arguments cannot be added to a function template that has already been declared}} + // expected-note@#dr15-f-decl-first {{previous template declaration is here}} } namespace dr16 { // dr16: 2.8 - class A { // expected-note {{here}} - void f(); // expected-note {{here}} + class A { // #dr16-A + void f(); // #dr16-A-f-decl friend class C; }; - class B : A {}; // expected-note 3{{here}} + class B : A {}; // #dr16-B class C : B { void g() { - f(); // expected-error {{private member}} - A::f(); // expected-error {{private member}} expected-error {{private base}} + f(); + // expected-error@-1 {{'f' is a private member of 'dr16::A'}} + // expected-note@#dr16-B {{constrained by implicitly private inheritance here}} + // expected-note@#dr16-A-f-decl {{member is declared here}} + A::f(); // #dr16-A-f-call + // expected-error@#dr16-A-f-call {{'A' is a private member of 'dr16::A'}} + // expected-note@#dr16-B {{constrained by implicitly private inheritance here}} + // expected-note@#dr16-A {{member is declared here}} + // expected-error@#dr16-A-f-call {{cannot cast 'dr16::C' to its private base class 'dr16::A'}} + // expected-note@#dr16-B {{implicitly declared private here}} } }; } @@ -232,13 +261,16 @@ namespace dr17 { // dr17: yes namespace dr19 { // dr19: 3.1 struct A { - int n; // expected-note {{here}} + int n; // #dr19-n }; - struct B : protected A { // expected-note {{here}} + struct B : protected A { // #dr19-B }; struct C : B {} c; struct D : B { - int get1() { return c.n; } // expected-error {{protected member}} + int get1() { return c.n; } + // expected-error@-1 {{'n' is a protected member of 'dr19::A'}} + // expected-note@#dr19-B {{constrained by protected inheritance here}} + // expected-note@#dr19-n {{member is declared here}} int get2() { return ((A&)c).n; } // ok, A is an accessible base of B from here }; } @@ -248,86 +280,94 @@ namespace dr20 { // dr20: 2.8 public: X(); private: - X(const X&); // expected-note {{here}} + X(const X&); // #dr20-X-ctor }; X &f(); - X x = f(); // expected-error {{private}} + X x = f(); + // expected-error@-1 {{calling a private constructor of class 'dr20::X'}} + // expected-note@#dr20-X-ctor {{declared private here}} } namespace dr21 { // dr21: 3.4 template struct A; struct X { - template friend struct A; // expected-error {{default template argument not permitted on a friend template}} - template friend struct B; // expected-error {{default template argument not permitted on a friend template}} + template friend struct A; + // expected-error@-1 {{default template argument not permitted on a friend template}} + template friend struct B; + // expected-error@-1 {{default template argument not permitted on a friend template}} }; } namespace dr22 { // dr22: sup 481 - template struct X; // expected-error {{unknown type name 'dr22_T'}} + template struct X; + // expected-error@-1 {{unknown type name 'dr22_T'}} typedef int T; template struct Y; } namespace dr23 { // dr23: yes - template void f(T, T); // expected-note {{candidate}} - template void f(T, int); // expected-note {{candidate}} - void g() { f(0, 0); } // expected-error {{ambiguous}} + template void f(T, T); // #dr23-f-T-T + template void f(T, int); // #dr23-f-T-int + void g() { f(0, 0); } + // expected-error@-1 {{call to 'f' is ambiguous}} + // expected-note@#dr23-f-T-T {{candidate function [with T = int]}} + // expected-note@#dr23-f-T-int {{candidate function [with T = int]}} } // dr24: na namespace dr25 { // dr25: yes struct A { - void f() throw(int); // expected-error 0-1{{ISO C++17 does not allow}} expected-note 0-1{{use 'noexcept}} + void f() throw(int); + // since-cxx17-error@-1 {{ISO C++17 does not allow dynamic exception specifications}} + // since-cxx17-note@-2 {{use 'noexcept(false)' instead}} }; - void (A::*f)() throw (int); // expected-error 0-1{{ISO C++17 does not allow}} expected-note 0-1{{use 'noexcept}} + void (A::*f)() throw (int); + // since-cxx17-error@-1 {{ISO C++17 does not allow dynamic exception specifications}} + // since-cxx17-note@-2 {{use 'noexcept(false)' instead}} void (A::*g)() throw () = f; -#if __cplusplus <= 201402L - // expected-error@-2 {{is not superset of source}} -#else - // expected-error@-4 {{different exception specifications}} -#endif + // cxx98-14-error@-1 {{target exception specification is not superset of source}} + // since-cxx17-error@-2 {{different exception specifications}} void (A::*g2)() throw () = 0; - void (A::*h)() throw (int, char) = f; // expected-error 0-1{{ISO C++17 does not allow}} expected-note 0-1{{use 'noexcept}} + void (A::*h)() throw (int, char) = f; + // since-cxx17-error@-1 {{ISO C++17 does not allow dynamic exception specifications}} + // since-cxx17-note@-2 {{use 'noexcept(false)' instead}} void (A::*i)() throw () = &A::f; -#if __cplusplus <= 201402L - // expected-error@-2 {{is not superset of source}} -#else - // expected-error@-4 {{different exception specifications}} -#endif + // cxx98-14-error@-1 {{target exception specification is not superset of source}} + // since-cxx17-error@-2 {{different exception specifications}} void (A::*i2)() throw () = 0; - void (A::*j)() throw (int, char) = &A::f; // expected-error 0-1{{ISO C++17 does not allow}} expected-note 0-1{{use 'noexcept}} + void (A::*j)() throw (int, char) = &A::f; + // since-cxx17-error@-1 {{ISO C++17 does not allow dynamic exception specifications}} + // since-cxx17-note@-2 {{use 'noexcept(false)' instead}} void x() { g2 = f; -#if __cplusplus <= 201402L - // expected-error@-2 {{is not superset of source}} -#else - // expected-error@-4 {{different exception specifications}} -#endif + // cxx98-14-error@-1 {{target exception specification is not superset of source}} + // since-cxx17-error@-2 {{different exception specifications}} h = f; i2 = &A::f; -#if __cplusplus <= 201402L - // expected-error@-2 {{is not superset of source}} -#else - // expected-error@-4 {{different exception specifications}} -#endif + // cxx98-14-error@-1 {{target exception specification is not superset of source}} + // since-cxx17-error@-2 {{different exception specifications}} j = &A::f; } } namespace dr26 { // dr26: yes - struct A { A(A, const A & = A()); }; // expected-error {{must pass its first argument by reference}} + struct A { A(A, const A & = A()); }; + // expected-error@-1 {{copy constructor must pass its first argument by reference}} struct B { B(); // FIXME: In C++98, we diagnose this twice. B(const B &, B = B()); -#if __cplusplus <= 201402L - // expected-error@-2 1+{{recursive evaluation of default argument}} expected-note@-2 1+{{used here}} -#endif + // cxx98-14-error@-1 {{recursive evaluation of default argument}} + // cxx98-14-note@-2 {{default argument used here}} + // cxx98-error@-3 {{recursive evaluation of default argument}} + // cxx98-note@-4 {{default argument used here}} }; struct C { static C &f(); - C(const C &, C = f()); // expected-error {{recursive evaluation of default argument}} expected-note {{used here}} + C(const C &, C = f()); + // expected-error@-1 {{recursive evaluation of default argument}} + // expected-note@-2 {{default argument used here}} }; } @@ -339,24 +379,34 @@ namespace dr27 { // dr27: yes // dr28: na lib namespace dr29 { // dr29: 3.4 - void dr29_f0(); // expected-note {{here}} + void dr29_f0(); // #dr29-f0 void g0() { void dr29_f0(); } extern "C++" void g0_cxx() { void dr29_f0(); } - extern "C" void g0_c() { void dr29_f0(); } // expected-error {{different language linkage}} + extern "C" void g0_c() { void dr29_f0(); } + // expected-error@-1 {{declaration of 'dr29_f0' has a different language linkage}} + // expected-note@#dr29-f0 {{previous declaration is here}} - extern "C" void dr29_f1(); // expected-note {{here}} + extern "C" void dr29_f1(); // #dr29-f1 void g1() { void dr29_f1(); } extern "C" void g1_c() { void dr29_f1(); } - extern "C++" void g1_cxx() { void dr29_f1(); } // expected-error {{different language linkage}} + extern "C++" void g1_cxx() { void dr29_f1(); } + // expected-error@-1 {{declaration of 'dr29_f1' has a different language linkage}} + // expected-note@#dr29-f1 {{previous declaration is here}} - void g2() { void dr29_f2(); } // expected-note {{here}} - extern "C" void dr29_f2(); // expected-error {{different language linkage}} + void g2() { void dr29_f2(); } // #dr29-f2 + extern "C" void dr29_f2(); + // expected-error@-1 {{declaration of 'dr29_f2' has a different language linkage}} + // expected-note@#dr29-f2 {{previous declaration is here}} - extern "C" void g3() { void dr29_f3(); } // expected-note {{here}} - extern "C++" void dr29_f3(); // expected-error {{different language linkage}} + extern "C" void g3() { void dr29_f3(); } // #dr29-f3 + extern "C++" void dr29_f3(); + // expected-error@-1 {{declaration of 'dr29_f3' has a different language linkage}} + // expected-note@#dr29-f3 {{previous declaration is here}} - extern "C++" void g4() { void dr29_f4(); } // expected-note {{here}} - extern "C" void dr29_f4(); // expected-error {{different language linkage}} + extern "C++" void g4() { void dr29_f4(); } // #dr29-f4 + extern "C" void dr29_f4(); + // expected-error@-1 {{declaration of 'dr29_f4' has a different language linkage}} + // expected-note@#dr29-f4 {{previous declaration is here}} extern "C" void g5(); extern "C++" void dr29_f5(); @@ -371,15 +421,19 @@ namespace dr29 { // dr29: 3.4 } extern "C" void g7(); - extern "C++" void dr29_f7(); // expected-note {{here}} + extern "C++" void dr29_f7(); // #dr29-f7 extern "C" void g7() { - void dr29_f7(); // expected-error {{different language linkage}} + void dr29_f7(); + // expected-error@-1 {{declaration of 'dr29_f7' has a different language linkage}} + // expected-note@#dr29-f7 {{previous declaration is here}} } extern "C++" void g8(); - extern "C" void dr29_f8(); // expected-note {{here}} + extern "C" void dr29_f8(); // #dr29-f8 extern "C++" void g8() { - void dr29_f8(); // expected-error {{different language linkage}} + void dr29_f8(); + // expected-error@-1 {{declaration of 'dr29_f8' has a different language linkage}} + // expected-note@#dr29-f8 {{previous declaration is here}} } } @@ -387,38 +441,42 @@ namespace dr30 { // dr30: sup 468 c++11 struct A { template static int f(); } a, *p = &a; + // FIXME: It's not clear whether DR468 applies to C++98 too. int x = A::template f<0>(); + // cxx98-error@-1 {{'template' keyword outside of a template}} int y = a.template f<0>(); + // cxx98-error@-1 {{'template' keyword outside of a template}} int z = p->template f<0>(); -#if __cplusplus < 201103L - // FIXME: It's not clear whether DR468 applies to C++98 too. - // expected-error@-5 {{'template' keyword outside of a template}} - // expected-error@-5 {{'template' keyword outside of a template}} - // expected-error@-5 {{'template' keyword outside of a template}} -#endif + // cxx98-error@-1 {{'template' keyword outside of a template}} } namespace dr31 { // dr31: 2.8 class X { private: - void operator delete(void*); // expected-note {{here}} + void operator delete(void*); // #dr31-delete }; // We would call X::operator delete if X() threw (even though it can't, // and even though we allocated the X using ::operator delete). - X *p = new X; // expected-error {{private}} + X *p = new X; + // expected-error@-1 {{'operator delete' is a private member of 'dr31::X'}} + // expected-note@#dr31-delete {{declared private here}} } // dr32: na namespace dr33 { // dr33: 9 - namespace X { struct S; void f(void (*)(S)); } // expected-note {{candidate}} - namespace Y { struct T; void f(void (*)(T)); } // expected-note {{candidate}} + namespace X { struct S; void f(void (*)(S)); } // #dr33-f-S + namespace Y { struct T; void f(void (*)(T)); } // #dr33-f-T void g(X::S); template Z g(Y::T); - void h() { f(&g); } // expected-error {{ambiguous}} + void h() { f(&g); } + // expected-error@-1 {{call to 'f' is ambiguous}} + // expected-note@#dr33-f-S {{candidate function}} + // expected-note@#dr33-f-T {{candidate function}} template void t(X::S); - template void u(X::S); // expected-error 0-1{{default template argument}} + template void u(X::S); + // expected-error@-1 0-1 {{default template arguments for a function template are a C++11 extension}} void templ() { f(t); f(u); } // Even though v cannot select the first overload, ADL considers it @@ -436,7 +494,8 @@ namespace dr33 { // dr33: 9 }; template void f(Y); - int use = X() + f; // expected-error {{invalid operands}} + int use = X() + f; + // expected-error@-1 {{invalid operands to binary expression ('X' and 'void (Y)')}} } namespace member { @@ -451,7 +510,7 @@ namespace dr33 { // dr33: 9 // dr34: na // dr35: dup 178 -namespace dr36 { // dr36: yes +namespace dr36 { // dr36: 2.8 namespace example1 { namespace A { int i; @@ -481,17 +540,25 @@ namespace example2 { struct D : virtual B, virtual C { - using B::i; // expected-note {{previous using declaration}} - using B::i; // expected-error {{redeclaration of using declaration}} - - using C::i; // expected-note {{previous using declaration}} - using C::i; // expected-error {{redeclaration of using declaration}} - - using B::j; // expected-note {{previous using declaration}} - using B::j; // expected-error {{redeclaration of using declaration}} - - using C::j; // expected-note {{previous using declaration}} - using C::j; // expected-error {{redeclaration of using declaration}} + using B::i; // #dr36-ex2-B-i-first + using B::i; + // expected-error@-1 {{redeclaration of using declaration}} + // expected-note@#dr36-ex2-B-i-first {{previous using declaration}} + + using C::i; // #dr36-ex2-C-i-first + using C::i; + // expected-error@-1 {{redeclaration of using declaration}} + // expected-note@#dr36-ex2-C-i-first {{previous using declaration}} + + using B::j; // #dr36-ex2-B-j-first + using B::j; + // expected-error@-1 {{redeclaration of using declaration}} + // expected-note@#dr36-ex2-B-j-first {{previous using declaration}} + + using C::j; // #dr36-ex2-C-j-first + using C::j; + // expected-error@-1 {{redeclaration of using declaration}} + // expected-note@#dr36-ex2-C-j-first {{previous using declaration}} }; } @@ -511,17 +578,25 @@ namespace example3 { template struct D : virtual B, virtual C { - using B::i; // expected-note {{previous using declaration}} - using B::i; // expected-error {{redeclaration of using declaration}} - - using C::i; // expected-note {{previous using declaration}} - using C::i; // expected-error {{redeclaration of using declaration}} - - using B::j; // expected-note {{previous using declaration}} - using B::j; // expected-error {{redeclaration of using declaration}} - - using C::j; // expected-note {{previous using declaration}} - using C::j; // expected-error {{redeclaration of using declaration}} + using B::i; // #dr36-ex3-B-i-first + using B::i; + // expected-error@-1 {{redeclaration of using declaration}} + // expected-note@#dr36-ex3-B-i-first {{previous using declaration}} + + using C::i; // #dr36-ex3-C-i-first + using C::i; + // expected-error@-1 {{redeclaration of using declaration}} + // expected-note@#dr36-ex3-C-i-first {{previous using declaration}} + + using B::j; // #dr36-ex3-B-j-first + using B::j; + // expected-error@-1 {{redeclaration of using declaration}} + // expected-note@#dr36-ex3-B-j-first {{previous using declaration}} + + using C::j; // #dr36-ex3-C-j-first + using C::j; + // expected-error@-1 {{redeclaration of using declaration}} + // expected-note@#dr36-ex3-C-j-first {{previous using declaration}} }; } namespace example4 { @@ -532,8 +607,10 @@ namespace example4 { template struct G : E { - using E::k; // expected-note {{previous using declaration}} - using E::k; // expected-error {{redeclaration of using declaration}} + using E::k; // #dr36-E-k-first + using E::k; + // expected-error@-1 {{redeclaration of using declaration}} + // expected-note@#dr36-E-k-first {{previous using declaration}} }; } } @@ -558,24 +635,35 @@ namespace dr39 { // dr39: no namespace example2 { struct A { - int &x(int); // expected-note {{found}} - static int &y(int); // expected-note {{found}} + int &x(int); // #dr39-A-x-decl + static int &y(int); // #dr39-A-y-decl }; struct V { int &z(int); }; struct B : A, virtual V { - using A::x; // expected-note {{found}} + using A::x; // #dr39-using-A-x float &x(float); - using A::y; // expected-note {{found}} + using A::y; // #dr39-using-A-y static float &y(float); using V::z; float &z(float); }; - struct C : A, B, virtual V {} c; // expected-warning {{direct base 'A' is inaccessible due to ambiguity:\n struct dr39::example2::C -> A\n struct dr39::example2::C -> B -> A}} - int &x = c.x(0); // expected-error {{found in multiple base classes}} + struct C : A, B, virtual V {} c; + /* expected-warning@-1 + {{direct base 'A' is inaccessible due to ambiguity: + struct dr39::example2::C -> A + struct dr39::example2::C -> B -> A}} */ + int &x = c.x(0); + // expected-error@-1 {{member 'x' found in multiple base classes of different types}} + // expected-note@#dr39-A-x-decl {{member found by ambiguous name lookup}} + // expected-note@#dr39-using-A-x {{member found by ambiguous name lookup}} + // FIXME: This is valid, because we find the same static data member either way. - int &y = c.y(0); // expected-error {{found in multiple base classes}} + int &y = c.y(0); + // expected-error@-1 {{member 'y' found in multiple base classes of different types}} + // expected-note@#dr39-A-y-decl {{member found by ambiguous name lookup}} + // expected-note@#dr39-using-A-y {{member found by ambiguous name lookup}} int &z = c.z(0); } @@ -588,21 +676,38 @@ namespace dr39 { // dr39: no } namespace example4 { - struct A { int n; }; // expected-note {{found}} + struct A { int n; }; // #dr39-ex4-A-n struct B : A {}; struct C : A {}; - struct D : B, C { int f() { return n; } }; // expected-error {{found in multiple base-class}} + struct D : B, C { int f() { return n; } }; + /* expected-error@-1 + {{non-static member 'n' found in multiple base-class subobjects of type 'A': + struct dr39::example4::D -> B -> A + struct dr39::example4::D -> C -> A}} */ + // expected-note@#dr39-ex4-A-n {{member found by ambiguous name lookup}} } namespace PR5916 { // FIXME: This is valid. - struct A { int n; }; // expected-note +{{found}} + struct A { int n; }; // #dr39-A-n struct B : A {}; struct C : A {}; struct D : B, C {}; - int k = sizeof(D::n); // expected-error {{found in multiple base}} expected-error {{unknown type name}} + int k = sizeof(D::n); // #dr39-sizeof + /* expected-error@#dr39-sizeof + {{non-static member 'n' found in multiple base-class subobjects of type 'A': + struct dr39::PR5916::D -> B -> A + struct dr39::PR5916::D -> C -> A}} */ + // expected-note@#dr39-A-n {{member found by ambiguous name lookup}} + + // expected-error@#dr39-sizeof {{unknown type name}} #if __cplusplus >= 201103L - decltype(D::n) n; // expected-error {{found in multiple base}} + decltype(D::n) n; + /* expected-error@-1 + {{non-static member 'n' found in multiple base-class subobjects of type 'A': + struct dr39::PR5916::D -> B -> A + struct dr39::PR5916::D -> C -> A}} */ + // expected-note@#dr39-A-n {{member found by ambiguous name lookup}} #endif } } @@ -637,15 +742,19 @@ namespace dr45 { // dr45: yes namespace dr46 { // dr46: yes template struct A { template struct B {}; }; - template template struct A::B; // expected-error {{expected unqualified-id}} + template template struct A::B; + // expected-error@-1 {{expected unqualified-id}} } namespace dr47 { // dr47: sup 329 template struct A { - friend void f() { T t; } // expected-error {{redefinition}} expected-note {{previous}} + friend void f() { T t; } // #dr47-f + // expected-error@-1 {{redefinition of 'f'}} + // expected-note@#dr47-b {{in instantiation of template class 'dr47::A' requested here}} + // expected-note@#dr47-f {{previous definition is here}} }; A a; - A b; // expected-note {{instantiation of}} + A b; // #dr47-b void f(); void g() { f(); } @@ -668,39 +777,41 @@ namespace dr48 { // dr48: yes } namespace dr49 { // dr49: 2.8 - template struct A {}; // expected-note 0-2{{here}} + template struct A {}; // #dr49-A int k; #if __has_feature(cxx_constexpr) constexpr #endif - int *const p = &k; // expected-note 0-2{{here}} + int *const p = &k; // #dr49-p A<&k> a; - A

b; -#if __cplusplus <= 201402L - // expected-error@-2 {{must have its address taken}} -#endif -#if __cplusplus < 201103L - // expected-error@-5 {{internal linkage}} -#endif - int *q = &k; - A c; -#if __cplusplus < 201103L - // expected-error@-2 {{must have its address taken}} -#else - // expected-error@-4 {{constant expression}} - // expected-note@-5 {{read of non-constexpr}} - // expected-note@-7 {{declared here}} -#endif + A

b; // #dr49-b + // cxx98-error@#dr49-b {{non-type template argument referring to object 'p' with internal linkage is a C++11 extension}} + // cxx98-note@#dr49-p {{non-type template argument refers to object here}} + // cxx98-14-error@#dr49-b {{non-type template argument for template parameter of pointer type 'int *' must have its address taken}} + // cxx98-14-note@#dr49-A {{template parameter is declared here}} + int *q = &k; // #dr49-q + A c; // #dr49-c + // cxx98-error@#dr49-c {{non-type template argument for template parameter of pointer type 'int *' must have its address taken}} + // cxx98-note@#dr49-A {{template parameter is declared here}} + // cxx11-14-error@#dr49-c {{non-type template argument of type 'int *' is not a constant expression}} + // cxx11-14-note@#dr49-c {{read of non-constexpr variable 'q' is not allowed in a constant expression}} + // cxx11-14-note@#dr49-q {{declared here}} + // cxx11-14-note@#dr49-A {{template parameter is declared here}} + // since-cxx17-error@#dr49-c {{non-type template argument is not a constant expression}} + // since-cxx17-note@#dr49-c {{read of non-constexpr variable 'q' is not allowed in a constant expression}} + // since-cxx17-note@#dr49-q {{declared here}} } namespace dr50 { // dr50: yes - struct X; // expected-note {{forward}} + struct X; // #dr50-X extern X *p; X *q = (X*)p; X *r = static_cast(p); X *s = const_cast(p); X *t = reinterpret_cast(p); - X *u = dynamic_cast(p); // expected-error {{incomplete}} + X *u = dynamic_cast(p); + // expected-error@-1 {{'dr50::X' is an incomplete type}} + // expected-note@#dr50-X {{forward declaration of 'dr50::X'}} } namespace dr51 { // dr51: 2.8 @@ -714,11 +825,15 @@ namespace dr51 { // dr51: 2.8 } namespace dr52 { // dr52: 2.8 - struct A { int n; }; // expected-note {{here}} - struct B : private A {} b; // expected-note 2{{private}} + struct A { int n; }; // #dr52-A + struct B : private A {} b; // #dr52-B + int k = b.A::n; // #dr52-k // FIXME: This first diagnostic is very strangely worded, and seems to be bogus. - int k = b.A::n; // expected-error {{'A' is a private member of 'dr52::A'}} - // expected-error@-1 {{cannot cast 'struct B' to its private base}} + // expected-error@#dr52-k {{'A' is a private member of 'dr52::A'}} + // expected-note@#dr52-B {{constrained by private inheritance here}} + // expected-note@#dr52-A {{member is declared here}} + // expected-error@#dr52-k {{cannot cast 'struct B' to its private base class 'dr52::A'}} + // expected-note@#dr52-B {{declared private here}} } namespace dr53 { // dr53: yes @@ -729,21 +844,37 @@ namespace dr53 { // dr53: yes namespace dr54 { // dr54: 2.8 struct A { int a; } a; struct V { int v; } v; - struct B : private A, virtual V { int b; } b; // expected-note 6{{private here}} - - A &sab = static_cast(b); // expected-error {{private base}} - A *spab = static_cast(&b); // expected-error {{private base}} - int A::*smab = static_cast(&B::b); // expected-error {{private base}} - B &sba = static_cast(a); // expected-error {{private base}} - B *spba = static_cast(&a); // expected-error {{private base}} - int B::*smba = static_cast(&A::a); // expected-error {{private base}} + struct B : private A, virtual V { int b; } b; // #dr54-B + + A &sab = static_cast(b); + // expected-error@-1 {{cannot cast 'struct B' to its private base class 'A'}} + // expected-note@#dr54-B {{declared private here}} + A *spab = static_cast(&b); + // expected-error@-1 {{cannot cast 'struct B' to its private base class 'A'}} + // expected-note@#dr54-B {{declared private here}} + int A::*smab = static_cast(&B::b); + // expected-error@-1 {{cannot cast 'dr54::B' to its private base class 'dr54::A'}} + // expected-note@#dr54-B {{declared private here}} + B &sba = static_cast(a); + // expected-error@-1 {{cannot cast private base class 'dr54::A' to 'dr54::B'}} + // expected-note@#dr54-B {{declared private here}} + B *spba = static_cast(&a); + // expected-error@-1 {{cannot cast private base class 'dr54::A' to 'dr54::B'}} + // expected-note@#dr54-B {{declared private here}} + int B::*smba = static_cast(&A::a); + // expected-error@-1 {{cannot cast private base class 'dr54::A' to 'dr54::B'}} + // expected-note@#dr54-B {{declared private here}} V &svb = static_cast(b); V *spvb = static_cast(&b); - int V::*smvb = static_cast(&B::b); // expected-error {{virtual base}} - B &sbv = static_cast(v); // expected-error {{virtual base}} - B *spbv = static_cast(&v); // expected-error {{virtual base}} - int B::*smbv = static_cast(&V::v); // expected-error {{virtual base}} + int V::*smvb = static_cast(&B::b); + // expected-error@-1 {{conversion from pointer to member of class 'dr54::B' to pointer to member of class 'dr54::V' via virtual base 'dr54::V' is not allowed}} + B &sbv = static_cast(v); + // expected-error@-1 {{cannot cast 'struct V' to 'B &' via virtual base 'dr54::V'}} + B *spbv = static_cast(&v); + // expected-error@-1 {{cannot cast 'dr54::V *' to 'B *' via virtual base 'dr54::V'}} + int B::*smbv = static_cast(&V::v); + // expected-error@-1 {{conversion from pointer to member of class 'dr54::V' to pointer to member of class 'dr54::B' via virtual base 'dr54::V' is not allowed}} A &cab = (A&)(b); A *cpab = (A*)(&b); @@ -754,10 +885,14 @@ namespace dr54 { // dr54: 2.8 V &cvb = (V&)(b); V *cpvb = (V*)(&b); - int V::*cmvb = (int V::*)(&B::b); // expected-error {{virtual base}} - B &cbv = (B&)(v); // expected-error {{virtual base}} - B *cpbv = (B*)(&v); // expected-error {{virtual base}} - int B::*cmbv = (int B::*)(&V::v); // expected-error {{virtual base}} + int V::*cmvb = (int V::*)(&B::b); + // expected-error@-1 {{conversion from pointer to member of class 'dr54::B' to pointer to member of class 'dr54::V' via virtual base 'dr54::V' is not allowed}} + B &cbv = (B&)(v); + // expected-error@-1 {{cannot cast 'struct V' to 'B &' via virtual base 'dr54::V'}} + B *cpbv = (B*)(&v); + // expected-error@-1 {{cannot cast 'dr54::V *' to 'B *' via virtual base 'dr54::V'}} + int B::*cmbv = (int B::*)(&V::v); + // expected-error@-1 {{conversion from pointer to member of class 'dr54::V' to pointer to member of class 'dr54::B' via virtual base 'dr54::V' is not allowed}} } namespace dr55 { // dr55: yes @@ -767,13 +902,17 @@ namespace dr55 { // dr55: yes namespace dr56 { // dr56: yes struct A { - typedef int T; // expected-note {{previous}} - typedef int T; // expected-error {{redefinition}} + typedef int T; // #dr56-typedef-int-T-first + typedef int T; + // expected-error@-1 {{redefinition of 'T'}} + // expected-note@#dr56-typedef-int-T-first {{previous definition is here}} }; struct B { struct X; - typedef X X; // expected-note {{previous}} - typedef X X; // expected-error {{redefinition}} + typedef X X; // #dr56-typedef-X-X-first + typedef X X; + // expected-error@-1 {{redefinition of 'X'}} + // expected-note@#dr56-typedef-X-X-first {{previous definition is here}} }; } @@ -792,32 +931,50 @@ namespace dr59 { // dr59: yes #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-volatile" template struct convert_to { operator T() const; }; - struct A {}; // expected-note 5+{{candidate}} - struct B : A {}; // expected-note 0+{{candidate}} + struct A {}; // #dr59-A + struct B : A {}; // #dr59-B A a1 = convert_to(); A a2 = convert_to(); A a3 = convert_to(); A a4 = convert_to(); -#if __cplusplus <= 201402L - // expected-error@-2 {{no viable}} -#endif - A a5 = convert_to(); // expected-error {{no viable}} + // cxx98-14-error@-1 {{no viable constructor copying variable of type 'const volatile dr59::A'}} + // cxx98-14-note@#dr59-A {{candidate constructor (the implicit copy constructor) not viable: 1st argument ('const volatile dr59::A') would lose volatile qualifier}} + // cxx11-14-note@#dr59-A {{candidate constructor (the implicit move constructor) not viable: 1st argument ('const volatile dr59::A') would lose const and volatile qualifiers}} + // cxx98-14-note@#dr59-A {{candidate constructor (the implicit default constructor) not viable: requires 0 arguments, but 1 was provided}} + A a5 = convert_to(); + // expected-error@-1 {{no viable constructor copying variable of type 'const volatile dr59::A'}} + // expected-note@#dr59-A {{candidate constructor (the implicit copy constructor) not viable: 1st argument ('const volatile dr59::A') would lose volatile qualifier}} + // since-cxx11-note@#dr59-A {{candidate constructor (the implicit move constructor) not viable: 1st argument ('const volatile dr59::A') would lose const and volatile qualifiers}} + // expected-note@#dr59-A {{candidate constructor (the implicit default constructor) not viable: requires 0 arguments, but 1 was provided}} B b1 = convert_to(); B b2 = convert_to(); B b3 = convert_to(); B b4 = convert_to(); -#if __cplusplus <= 201402L - // expected-error@-2 {{no viable}} -#endif - B b5 = convert_to(); // expected-error {{no viable}} + // cxx98-14-error@-1 {{no viable constructor copying variable of type 'const volatile dr59::B'}} + // cxx98-14-note@#dr59-B {{candidate constructor (the implicit copy constructor) not viable: 1st argument ('const volatile dr59::B') would lose volatile qualifier}} + // cxx11-14-note@#dr59-B {{candidate constructor (the implicit move constructor) not viable: 1st argument ('const volatile dr59::B') would lose const and volatile qualifiers}} + // cxx98-14-note@#dr59-B {{candidate constructor (the implicit default constructor) not viable: requires 0 arguments, but 1 was provided}} + B b5 = convert_to(); + // expected-error@-1 {{no viable constructor copying variable of type 'const volatile dr59::B'}} + // expected-note@#dr59-B {{candidate constructor (the implicit copy constructor) not viable: 1st argument ('const volatile dr59::B') would lose volatile qualifier}} + // since-cxx11-note@#dr59-B {{candidate constructor (the implicit move constructor) not viable: 1st argument ('const volatile dr59::B') would lose const and volatile qualifiers}} + // expected-note@#dr59-B {{candidate constructor (the implicit default constructor) not viable: requires 0 arguments, but 1 was provided}} A c1 = convert_to(); A c2 = convert_to(); A c3 = convert_to(); - A c4 = convert_to(); // expected-error {{no viable}} - A c5 = convert_to(); // expected-error {{no viable}} + A c4 = convert_to(); + // expected-error@-1 {{no viable constructor copying variable of type 'const volatile dr59::B'}} + // expected-note@#dr59-A {{candidate constructor (the implicit copy constructor) not viable: no known conversion from 'const volatile dr59::B' to 'const A &' for 1st argument}} + // since-cxx11-note@#dr59-A {{candidate constructor (the implicit move constructor) not viable: no known conversion from 'const volatile dr59::B' to 'A &&' for 1st argument}} + // expected-note@#dr59-A {{candidate constructor (the implicit default constructor) not viable: requires 0 arguments, but 1 was provided}} + A c5 = convert_to(); + // expected-error@-1 {{no viable constructor copying variable of type 'const volatile dr59::B'}} + // expected-note@#dr59-A {{candidate constructor (the implicit copy constructor) not viable: no known conversion from 'const volatile dr59::B' to 'const A &' for 1st argument}} + // since-cxx11-note@#dr59-A {{candidate constructor (the implicit move constructor) not viable: no known conversion from 'const volatile dr59::B' to 'A &&' for 1st argument}} + // expected-note@#dr59-A {{candidate constructor (the implicit default constructor) not viable: requires 0 arguments, but 1 was provided}} int n1 = convert_to(); int n2 = convert_to(); @@ -845,8 +1002,10 @@ namespace dr61 { // dr61: 3.4 // This is (presumably) valid, because x.f does not refer to an overloaded // function name. void (*p)() = &x.f; - void (*q)() = &y.f; // expected-error {{cannot create a non-constant pointer to member function}} - void (*r)() = y.f; // expected-error {{cannot create a non-constant pointer to member function}} + void (*q)() = &y.f; + // expected-error@-1 {{cannot create a non-constant pointer to member function}} + void (*r)() = y.f; + // expected-error@-1 {{cannot create a non-constant pointer to member function}} } namespace dr62 { // dr62: 2.9 @@ -860,10 +1019,7 @@ namespace dr62 { // dr62: 2.9 X x1; A a = get(); - typedef struct { } *NoNameForLinkagePtr; -#if __cplusplus < 201103L - // expected-note@-2 5{{here}} -#endif + typedef struct { } *NoNameForLinkagePtr; // #dr62-unnamed NoNameForLinkagePtr noNameForLinkagePtr; struct Danger { @@ -871,36 +1027,37 @@ namespace dr62 { // dr62: 2.9 }; X x2; + // cxx98-error@-1 {{template argument uses unnamed type}} + // cxx98-note@#dr62-unnamed {{unnamed type used in template argument was declared here}} X x3; + // cxx98-error@-1 {{template argument uses unnamed type}} + // cxx98-note@#dr62-unnamed {{unnamed type used in template argument was declared here}} NoNameForLinkagePtr p1 = get(); + // cxx98-error@-1 {{template argument uses unnamed type}} + // cxx98-note@#dr62-unnamed {{unnamed type used in template argument was declared here}} NoNameForLinkagePtr p2 = get(); + // cxx98-error@-1 {{template argument uses unnamed type}} + // cxx98-note@#dr62-unnamed {{unnamed type used in template argument was declared here}} int n1 = take(noNameForLinkagePtr); -#if __cplusplus < 201103L - // expected-error@-6 {{uses unnamed type}} - // expected-error@-6 {{uses unnamed type}} - // expected-error@-6 {{uses unnamed type}} - // expected-error@-6 {{uses unnamed type}} - // expected-error@-6 {{uses unnamed type}} -#endif + // cxx98-error@-1 {{template argument uses unnamed type}} + // cxx98-note@#dr62-unnamed {{unnamed type used in template argument was declared here}} X x4; void f() { struct NoLinkage {}; X a; + // cxx98-error@-1 {{template argument uses local type }} X b; + // cxx98-error@-1 {{template argument uses local type }} get(); + // cxx98-error@-1 {{template argument uses local type }} get(); + // cxx98-error@-1 {{template argument uses local type }} X c; + // cxx98-error@-1 {{template argument uses local type }} X d; -#if __cplusplus < 201103L - // expected-error@-7 {{uses local type}} - // expected-error@-7 {{uses local type}} - // expected-error@-7 {{uses local type}} - // expected-error@-7 {{uses local type}} - // expected-error@-7 {{uses local type}} - // expected-error@-7 {{uses local type}} -#endif + // cxx98-error@-1 {{template argument uses local type }} } } @@ -922,7 +1079,7 @@ namespace dr64 { // dr64: yes namespace dr66 { // dr66: no namespace X { - int f(int n); // expected-note 2{{candidate}} + int f(int n); // #dr66-f-first } using X::f; namespace X { @@ -930,9 +1087,13 @@ namespace dr66 { // dr66: no int f(int, int); } // FIXME: The first two calls here should be accepted. - int a = f(); // expected-error {{no matching function}} + int a = f(); + // expected-error@-1 {{no matching function for call to 'f'}} + // expected-note@#dr66-f-first {{candidate function not viable: requires single argument 'n', but no arguments were provided}} int b = f(1); - int c = f(1, 2); // expected-error {{no matching function}} + int c = f(1, 2); + // expected-error@-1 {{no matching function for call to 'f'}} + // expected-note@#dr66-f-first {{candidate function not viable: requires single argument 'n', but 2 arguments were provided}} } // dr67: na @@ -941,24 +1102,18 @@ namespace dr68 { // dr68: 2.8 template struct X {}; struct ::dr68::X x1; struct ::dr68::template X x2; -#if __cplusplus < 201103L - // expected-error@-2 {{'template' keyword outside of a template}} -#endif + // cxx98-error@-1 {{'template' keyword outside of a template}} struct Y { friend struct X; friend struct ::dr68::X; friend struct ::dr68::template X; -#if __cplusplus < 201103L - // expected-error@-2 {{'template' keyword outside of a template}} -#endif + // cxx98-error@-1 {{'template' keyword outside of a template}} }; template struct Z { friend struct ::dr68::template X; friend typename ::dr68::X; -#if __cplusplus < 201103L - // expected-error@-2 {{C++11 extension}} -#endif + // cxx98-error@-1 {{unelaborated friend declaration is a C++11 extension; specify 'struct' to befriend 'typename ::dr68::X'}} }; } @@ -966,15 +1121,13 @@ namespace dr69 { // dr69: 9 template static void f() {} // #dr69-f // FIXME: Should we warn here? inline void g() { f(); } - extern template void f(); // expected-error {{explicit instantiation declaration of 'f' with internal linkage}} -#if __cplusplus < 201103L - // expected-error@-2 {{C++11 extension}} -#endif + extern template void f(); + // cxx98-error@-1 {{extern templates are a C++11 extension}} + // expected-error@-2 {{explicit instantiation declaration of 'f' with internal linkage}} template struct Q {}; Q<&f > q; -#if __cplusplus < 201103L - // expected-error@-2 {{internal linkage}} expected-note@#dr69-f {{here}} -#endif + // cxx98-error@-1 {{non-type template argument referring to function 'f' with internal linkage is a C++11 extension}} + // cxx98-note@#dr69-f {{non-type template argument refers to function here}} } namespace dr70 { // dr70: yes @@ -990,8 +1143,9 @@ namespace dr70 { // dr70: yes #if __cplusplus >= 201103L namespace dr73 { // dr73: sup 1652 int a, b; - static_assert(&a + 1 != &b, ""); // expected-error {{not an integral constant expression}} - // expected-note@-1 {{comparison against pointer '&a + 1' that points past the end of a complete object}} + static_assert(&a + 1 != &b, ""); + // expected-error@-1 {{static assertion expression is not an integral constant expression}} + // expected-note@-2 {{comparison against pointer '&a + 1' that points past the end of a complete object has unspecified value}} } #endif @@ -1002,13 +1156,17 @@ namespace dr74 { // dr74: yes namespace dr75 { // dr75: yes struct S { - static int n = 0; // expected-error {{non-const}} + static int n = 0; + // expected-error@-1 {{non-const static data member must be initialized out of line}} }; } namespace dr76 { // dr76: yes const volatile int n = 1; - int arr[n]; // expected-error +{{variable length array}} expected-note {{read of volatile}} + int arr[n]; // #dr76-vla + // expected-error@#dr76-vla {{variable length arrays in C++ are a Clang extension}} + // expected-note@#dr76-vla {{read of volatile-qualified type 'const volatile int' is not allowed in a constant expression}} + // expected-error@#dr76-vla {{variable length array declaration not allowed at file scope}} } namespace dr77 { // dr77: yes @@ -1021,7 +1179,8 @@ namespace dr77 { // dr77: yes namespace dr78 { // dr78: sup ???? // Under DR78, this is valid, because 'k' has static storage duration, so is // zero-initialized. - const int k; // expected-error {{default initialization of an object of const}} + const int k; + // expected-error@-1 {{default initialization of an object of const type 'const int'}} } // dr79: na @@ -1031,15 +1190,18 @@ namespace dr80 { // dr80: 2.9 int A; }; struct B { - static int B; // expected-error {{same name as its class}} + static int B; + // expected-error@-1 {{member 'B' has the same name as its class}} }; struct C { - int C; // expected-error {{same name as its class}} + int C; + // expected-error@-1 {{member 'C' has the same name as its class}} C(); }; struct D { D(); - int D; // expected-error {{same name as its class}} + int D; + // expected-error@-1 {{member 'D' has the same name as its class}} }; } @@ -1057,44 +1219,54 @@ namespace dr84 { // dr84: yes struct A { operator B() const; }; struct C {}; struct B { - B(B&); // expected-note 0-1{{candidate}} - B(C); // expected-note 0-1{{no known conversion from 'B' to 'C'}} + B(B&); // #dr84-copy-ctor + B(C); // #dr84-ctor-from-C operator C() const; }; A a; // Cannot use B(C) / operator C() pair to construct the B from the B temporary // here. In C++17, we initialize the B object directly using 'A::operator B()'. B b = a; -#if __cplusplus <= 201402L - // expected-error@-2 {{no viable}} -#endif + // cxx98-14-error@-1 {{no viable constructor copying variable of type 'B'}} + // cxx98-14-note@#dr84-copy-ctor {{candidate constructor not viable: expects an lvalue for 1st argument}} + // cxx98-14-note@#dr84-ctor-from-C {{candidate constructor not viable: no known conversion from 'B' to 'C' for 1st argument}} } namespace dr85 { // dr85: 3.4 struct A { struct B; - struct B {}; // expected-note{{previous declaration is here}} - struct B; // expected-error{{class member cannot be redeclared}} + struct B {}; // #dr85-B-def + struct B; + // expected-error@-1 {{class member cannot be redeclared}} + // expected-note@#dr85-B-def {{previous declaration is here}} union U; - union U {}; // expected-note{{previous declaration is here}} - union U; // expected-error{{class member cannot be redeclared}} + union U {}; // #dr85-U-def + union U; + // expected-error@-1 {{class member cannot be redeclared}} + // expected-note@#dr85-U-def {{previous declaration is here}} #if __cplusplus >= 201103L enum E1 : int; - enum E1 : int { e1 }; // expected-note{{previous declaration is here}} - enum E1 : int; // expected-error{{class member cannot be redeclared}} + enum E1 : int { e1 }; // #dr85-E1-def + enum E1 : int; + // expected-error@-1 {{class member cannot be redeclared}} + // expected-note@#dr85-E1-def {{previous declaration is here}} enum class E2; - enum class E2 { e2 }; // expected-note{{previous declaration is here}} - enum class E2; // expected-error{{class member cannot be redeclared}} + enum class E2 { e2 }; // #dr85-E2-def + enum class E2; + // expected-error@-1 {{class member cannot be redeclared}} + // expected-note@#dr85-E2-def {{previous declaration is here}} #endif }; template struct C { - struct B {}; // expected-note{{previous declaration is here}} - struct B; // expected-error{{class member cannot be redeclared}} + struct B {}; // #dr85-C-B-def + struct B; + // expected-error@-1 {{class member cannot be redeclared}} + // expected-note@#dr85-C-B-def {{previous declaration is here}} }; } @@ -1111,10 +1283,12 @@ namespace dr87 { // dr87: no namespace dr88 { // dr88: 2.8 template struct S { - static const int a = 1; // expected-note {{previous}} + static const int a = 1; // #dr88-a static const int b; }; - template<> const int S::a = 4; // expected-error {{already has an initializer}} + template<> const int S::a = 4; + // expected-error@-1 {{static data member 'a' already has an initializer}} + // expected-note@#dr88-a {{previous initialization is here}} template<> const int S::b = 4; } @@ -1135,17 +1309,22 @@ namespace dr90 { // dr90: yes void test() { dr90_f(A()); dr90_f(B()); - dr90_f(B::C()); // expected-error {{undeclared identifier}} - dr90_f(B::D()); // expected-error {{undeclared identifier}} + dr90_f(B::C()); + // expected-error@-1 {{use of undeclared identifier 'dr90_f'}} + dr90_f(B::D()); + // expected-error@-1 {{use of undeclared identifier 'dr90_f'}} dr90_f(E()); - dr90_f(F()); // expected-error {{undeclared identifier}} + dr90_f(F()); + // expected-error@-1 {{use of undeclared identifier 'dr90_f'}} - dr90_g(A()); // expected-error {{undeclared identifier}} + dr90_g(A()); + // expected-error@-1 {{use of undeclared identifier 'dr90_g'}} dr90_g(B()); dr90_g(B::C()); dr90_g(B::D()); dr90_g(E()); - dr90_g(F()); // expected-error {{undeclared identifier}} + dr90_g(F()); + // expected-error@-1 {{use of undeclared identifier 'dr90_g'}} } } @@ -1155,25 +1334,32 @@ namespace dr91 { // dr91: yes } namespace dr92 { // dr92: 4 c++17 - void f() throw(int, float); // expected-error 0-1{{ISO C++17 does not allow}} expected-note 0-1{{use 'noexcept}} - void (*p)() throw(int) = &f; // expected-error 0-1{{ISO C++17 does not allow}} expected-note 0-1{{use 'noexcept}} -#if __cplusplus <= 201402L - // expected-error@-2 {{target exception specification is not superset of source}} -#else - // expected-warning@-4 {{target exception specification is not superset of source}} -#endif - void (*q)() throw(int); // expected-error 0-1{{ISO C++17 does not allow}} expected-note 0-1{{use 'noexcept}} + void f() throw(int, float); + // since-cxx17-error@-1 {{ISO C++17 does not allow dynamic exception specifications}} + // since-cxx17-note@-2 {{use 'noexcept(false)' instead}} + void (*p)() throw(int) = &f; // #dr92-p + // since-cxx17-error@#dr92-p {{ISO C++17 does not allow dynamic exception specifications}} + // since-cxx17-note@#dr92-p {{use 'noexcept(false)' instead}} + // cxx98-14-error@#dr92-p {{target exception specification is not superset of source}} + // since-cxx17-warning@#dr92-p {{target exception specification is not superset of source}} + void (*q)() throw(int); + // since-cxx17-error@-1 {{ISO C++17 does not allow dynamic exception specifications}} + // since-cxx17-note@-2 {{use 'noexcept(false)' instead}} void (**pp)() throw() = &q; -#if __cplusplus <= 201402L - // expected-error@-2 {{exception specifications are not allowed}} -#else - // expected-error@-4 {{cannot initialize}} -#endif + // cxx98-14-error@-1 {{exception specifications are not allowed beyond a single level of indirection}} + // since-cxx17-error@-2 {{cannot initialize a variable of type 'void (**)() throw()' with an rvalue of type 'void (**)() throw(int)'}} - void g(void() throw()); // expected-note 0-2 {{no known conversion}} expected-warning 0-1{{mangled name of 'g' will change in C++17}} + void g(void() throw()); // #dr92-g + // cxx98-14-warning@-1 {{mangled name of 'g' will change in C++17 due to non-throwing exception specification in function signature}} void h() throw() { - g(f); // expected-error-re {{{{is not superset|no matching function}}}} - g(q); // expected-error-re {{{{is not superset|no matching function}}}} + g(f); + // cxx98-14-error@-1 {{target exception specification is not superset of source}} + // since-cxx17-error@-2 {{no matching function for call to 'g'}} + // since-cxx17-note@#dr92-g {{candidate function not viable: no known conversion from 'void () throw(int, float)' to 'void (*)() throw()' for 1st argument}} + g(q); + // cxx98-14-error@-1 {{target exception specification is not superset of source}} + // since-cxx17-error@-2 {{no matching function for call to 'g'}} + // since-cxx17-note@#dr92-g {{candidate function not viable: no known conversion from 'void (*)() throw(int)' to 'void (*)() throw()' for 1st argument}} } // Prior to C++17, this is OK because the exception specification is not @@ -1182,11 +1368,11 @@ namespace dr92 { // dr92: 4 c++17 // is part of the type of the parameter, so this is invalid. template struct X {}; X<&f> xp; -#if __cplusplus > 201402L - // expected-error@-2 {{not implicitly convertible}} -#endif + // since-cxx17-error@-1 {{value of type 'void (*)() throw(int, float)' is not implicitly convertible to 'void (*)() throw()'}} - template struct Y {}; // expected-error 0-1{{ISO C++17 does not allow}} expected-note 0-1{{use 'noexcept}} + template struct Y {}; + // since-cxx17-error@-1 {{ISO C++17 does not allow dynamic exception specifications}} + // since-cxx17-note@-2 {{use 'noexcept(false)' instead}} Y<&h> yp; // ok } @@ -1204,12 +1390,14 @@ namespace dr95 { // dr95: 3.3 class C { friend struct A; friend struct B; - static void f(); // expected-note {{here}} + static void f(); // #dr95-C-f }; struct A *p; // dr95::A, not dr95::N::A. } A *q = N::p; // ok, same type - struct B { void f() { N::C::f(); } }; // expected-error {{private}} + struct B { void f() { N::C::f(); } }; + // expected-error@-1 {{'f' is a private member of 'dr95::N::C'}} + // expected-note@#dr95-C-f {{implicitly declared private here}} } namespace dr96 { // dr96: no @@ -1242,19 +1430,27 @@ namespace dr97 { // dr97: yes namespace dr98 { // dr98: yes void test(int n) { switch (n) { - try { // expected-note 2{{bypasses}} - case 0: // expected-error {{cannot jump}} + try { // #dr98-try + case 0: + // expected-error@-1 {{cannot jump from switch statement to this case label}} + // expected-note@#dr98-try {{jump bypasses initialization of try block}} x: throw n; - } catch (...) { // expected-note 2{{bypasses}} - case 1: // expected-error {{cannot jump}} + } catch (...) { // #dr98-catch + case 1: + // expected-error@-1 {{cannot jump from switch statement to this case label}} + // expected-note@#dr98-catch {{jump bypasses initialization of catch block}} y: throw n; } case 2: - goto x; // expected-error {{cannot jump}} + goto x; + // expected-error@-1 {{cannot jump from this goto statement to its label}} + // expected-note@#dr98-try {{jump bypasses initialization of try block}} case 3: - goto y; // expected-error {{cannot jump}} + goto y; + // expected-error@-1 {{cannot jump from this goto statement to its label}} + // expected-note@#dr98-catch {{jump bypasses initialization of catch block}} } } } diff --git a/clang/test/CXX/drs/dr1xx.cpp b/clang/test/CXX/drs/dr1xx.cpp index 60e80a4c0e1c4f98cde1d2a7fc932d682b9056e5..064ecace59067a1ee43f602f957df21fa076e9a6 100644 --- a/clang/test/CXX/drs/dr1xx.cpp +++ b/clang/test/CXX/drs/dr1xx.cpp @@ -1,30 +1,31 @@ -// RUN: %clang_cc1 -std=c++98 -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors -// RUN: %clang_cc1 -std=c++11 -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors -// RUN: %clang_cc1 -std=c++14 -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors -// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors -// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors -// RUN: %clang_cc1 -std=c++23 -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors +// RUN: %clang_cc1 -std=c++98 -triple x86_64-unknown-unknown %s -verify=expected,cxx98,cxx98-11,cxx98-14,cxx98-17 -fexceptions -fcxx-exceptions -pedantic-errors +// RUN: %clang_cc1 -std=c++11 -triple x86_64-unknown-unknown %s -verify=expected,since-cxx11,cxx98-11,cxx98-14,cxx98-17,cxx11-14 -fexceptions -fcxx-exceptions -pedantic-errors +// RUN: %clang_cc1 -std=c++14 -triple x86_64-unknown-unknown %s -verify=expected,since-cxx11,cxx98-14,cxx98-17,cxx11-14 -fexceptions -fcxx-exceptions -pedantic-errors +// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-unknown %s -verify=expected,since-cxx11,since-cxx17,cxx98-17 -fexceptions -fcxx-exceptions -pedantic-errors +// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-unknown %s -verify=expected,since-cxx11,since-cxx17 -fexceptions -fcxx-exceptions -pedantic-errors +// RUN: %clang_cc1 -std=c++23 -triple x86_64-unknown-unknown %s -verify=expected,since-cxx11,since-cxx17 -fexceptions -fcxx-exceptions -pedantic-errors namespace dr100 { // dr100: yes - template struct A {}; // expected-note 0-1{{declared here}} - template struct B {}; // expected-note 0-1{{declared here}} - template struct C {}; // expected-note 0-1{{declared here}} - template struct D {}; // expected-note 0-1{{declared here}} - A<&"foo"> a; // #100a - B<"bar"> b; // #100b - C<"baz"> c; // #100c - D<*"quux"> d; // #100d -#if __cplusplus < 201703L - // expected-error@#100a {{does not refer to any declaration}} - // expected-error@#100b {{does not refer to any declaration}} - // expected-error@#100c {{does not refer to any declaration}} - // expected-error@#100d {{does not refer to any declaration}} -#else - // expected-error@#100a {{pointer to string literal is not allowed in a template argument}} - // expected-error@#100b {{reference to string literal is not allowed in a template argument}} - // expected-error@#100c {{pointer to subobject of string literal is not allowed in a template argument}} - // expected-error@#100d {{reference to subobject of string literal is not allowed in a template argument}} -#endif + template struct A {}; // #dr100-A + template struct B {}; // #dr100-B + template struct C {}; // #dr100-C + template struct D {}; // #dr100-D + A<&"foo"> a; // #dr100-a + // cxx98-14-error@#dr100-a {{non-type template argument does not refer to any declaration}} + // cxx98-14-note@#dr100-A {{template parameter is declared here}} + // since-cxx17-error@#dr100-a {{pointer to string literal is not allowed in a template argument}} + B<"bar"> b; // #dr100-b + // cxx98-14-error@#dr100-b {{non-type template argument does not refer to any declaration}} + // cxx98-14-note@#dr100-B {{template parameter is declared here}} + // since-cxx17-error@#dr100-b {{reference to string literal is not allowed in a template argument}} + C<"baz"> c; // #dr100-c + // cxx98-14-error@#dr100-c {{non-type template argument does not refer to any declaration}} + // cxx98-14-note@#dr100-C {{template parameter is declared here}} + // since-cxx17-error@#dr100-c {{pointer to subobject of string literal is not allowed in a template argument}} + D<*"quux"> d; // #dr100-d + // cxx98-14-error@#dr100-d {{non-type template argument does not refer to any declaration}} + // cxx98-14-note@#dr100-D {{template parameter is declared here}} + // since-cxx17-error@#dr100-d {{reference to subobject of string literal is not allowed in a template argument}} } namespace dr101 { // dr101: 3.5 @@ -42,13 +43,16 @@ namespace dr101 { // dr101: 3.5 namespace dr102 { // dr102: yes namespace A { - template T f(T a, T b) { return a + b; } // expected-error {{neither visible in the template definition nor found by argument-dependent lookup}} + template T f(T a, T b) { return a + b; } + // expected-error@-1 {{call to function 'operator+' that is neither visible in the template definition nor found by argument-dependent lookup}} + // expected-note@#dr102-instantiation {{in instantiation of function template specialization 'dr102::A::f' requested here}} + // expected-note@#dr102-operator-plus {{'operator+' should be declared prior to the call site or in namespace 'dr102::B'}} } namespace B { struct S {}; } - B::S operator+(B::S, B::S); // expected-note {{should be declared prior to the call site or in namespace 'dr102::B'}} - template B::S A::f(B::S, B::S); // expected-note {{in instantiation of}} + B::S operator+(B::S, B::S); // #dr102-operator-plus + template B::S A::f(B::S, B::S); // #dr102-instantiation } // dr103: na @@ -58,13 +62,17 @@ namespace dr102 { // dr102: yes namespace dr106 { // dr106: sup 540 typedef int &r1; typedef r1 &r1; - typedef const r1 r1; // expected-warning {{has no effect}} - typedef const r1 &r1; // expected-warning {{has no effect}} + typedef const r1 r1; + // expected-warning@-1 {{'const' qualifier on reference type 'r1' (aka 'int &') has no effect}} + typedef const r1 &r1; + // expected-warning@-1 {{'const' qualifier on reference type 'r1' (aka 'int &') has no effect}} typedef const int &r2; typedef r2 &r2; - typedef const r2 r2; // expected-warning {{has no effect}} - typedef const r2 &r2; // expected-warning {{has no effect}} + typedef const r2 r2; + // expected-warning@-1 {{'const' qualifier on reference type 'r2' (aka 'const int &') has no effect}} + typedef const r2 &r2; + // expected-warning@-1 {{'const' qualifier on reference type 'r2' (aka 'const int &') has no effect}} } namespace dr107 { // dr107: yes @@ -72,14 +80,13 @@ namespace dr107 { // dr107: yes extern "C" S operator+(S, S) { return S(); } } -namespace dr108 { // dr108: yes +namespace dr108 { // dr108: 2.9 template struct A { struct B { typedef int X; }; B::X x; -#if __cplusplus <= 201703L - // expected-error@-2 {{implicit 'typename' is a C++20 extension}} -#endif - struct C : B { X x; }; // expected-error {{unknown type name}} + // cxx98-17-error@-1 {{missing 'typename' prior to dependent type name B::X; implicit 'typename' is a C++20 extension}} + struct C : B { X x; }; + // expected-error@-1 {{unknown type name 'X'}} }; template<> struct A::B { int X; }; } @@ -87,46 +94,55 @@ namespace dr108 { // dr108: yes namespace dr109 { // dr109: yes struct A { template void f(T); }; template struct B : T { - using T::template f; // expected-error {{'template' keyword not permitted here}} - using T::template f; // expected-error {{'template' keyword not permitted here}} expected-error {{using declaration cannot refer to a template specialization}} + using T::template f; + // expected-error@-1 {{'template' keyword not permitted here}} + using T::template f; + // expected-error@-1 {{'template' keyword not permitted here}} + // expected-error@-2 {{using declaration cannot refer to a template specialization}} // FIXME: We shouldn't suggest using the 'template' keyword in a location where it's not valid. - using T::f; // expected-error {{use 'template' keyword}} expected-error {{using declaration cannot refer to a template specialization}} - void g() { this->f(123); } // expected-error {{use 'template' keyword}} + using T::f; + // expected-error@-1 {{use 'template' keyword to treat 'f' as a dependent template name}} + // expected-error@-2 {{using declaration cannot refer to a template specialization}} + void g() { this->f(123); } + // expected-error@-1 {{use 'template' keyword to treat 'f' as a dependent template name}} }; } namespace dr111 { // dr111: dup 535 struct A { A(); A(volatile A&, int = 0); A(A&, const char * = "foo"); }; - struct B : A { B(); }; // expected-note +{{would lose const qualifier}} expected-note {{requires 0 arguments}} + struct B : A { B(); }; // #dr111-B const B b1; - B b2(b1); // expected-error {{no matching constructor}} + B b2(b1); + // expected-error@-1 {{no matching constructor for initialization of 'B'}} + // expected-note@#dr111-B {{candidate constructor (the implicit copy constructor) not viable: 1st argument ('const B') would lose const qualifier}} + // expected-note@#dr111-B {{candidate constructor not viable: requires 0 arguments, but 1 was provided}} } namespace dr112 { // dr112: yes struct T { int n; }; typedef T Arr[1]; - const T a1[1] = {}; + const T a1[1] = {}; // #dr112-a1 volatile T a2[1] = {}; - const Arr a3 = {}; + const Arr a3 = {}; // #dr112-a3 volatile Arr a4 = {}; template struct X {}; + // FIXME: Test this somehow in C++11 and on. X x1; + // cxx98-error@-1 {{non-type template argument referring to object 'a1' with internal linkage is a C++11 extension}} + // cxx98-note@#dr112-a1 {{non-type template argument refers to object here}} X x2; X x3; + // cxx98-error@-1 {{non-type template argument referring to object 'a3' with internal linkage is a C++11 extension}} + // cxx98-note@#dr112-a3 {{non-type template argument refers to object here}} X x4; -#if __cplusplus < 201103L - // expected-error@-5 {{internal linkage}} expected-note@-10 {{here}} - // expected-error@-4 {{internal linkage}} expected-note@-9 {{here}} -#else - // FIXME: Test this somehow. -#endif } namespace dr113 { // dr113: yes extern void (*p)(); void f() { - no_such_function(); // expected-error {{undeclared}} + no_such_function(); + // expected-error@-1 {{use of undeclared identifier 'no_such_function'}} p(); } void g(); @@ -135,31 +151,48 @@ namespace dr113 { // dr113: yes namespace dr114 { // dr114: yes struct A { - virtual void f(int) = 0; // expected-note {{unimplemented}} + virtual void f(int) = 0; // #dr114-A-f }; struct B : A { template void f(T); void g() { f(0); } - } b; // expected-error {{abstract}} + } b; + // expected-error@-1 {{variable type 'struct B' is an abstract class}} + // expected-note@#dr114-A-f {{unimplemented pure virtual method 'f' in 'B'}} } -namespace dr115 { // dr115: yes - template int f(T); // expected-note +{{}} - template int g(T); // expected-note +{{}} - template int g(T, int); // expected-note +{{}} +namespace dr115 { // dr115: 3.0 + template int f(T); // #dr115-f + template int g(T); // #dr115-g + template int g(T, int); // #dr115-g-int - int k1 = f(&f); // expected-error {{no match}} + int k1 = f(&f); + // expected-error@-1 {{no matching function for call to 'f'}} + // expected-note@#dr115-f {{candidate template ignored: couldn't infer template argument 'T'}} int k2 = f(&f); - int k3 = f(&g); // expected-error {{no match}} + int k3 = f(&g); + // expected-error@-1 {{no matching function for call to 'f'}} + // expected-note@#dr115-f {{candidate template ignored: couldn't infer template argument 'T'}} void h() { - (void)&f; // expected-error {{address of overloaded function 'f' cannot be cast to type 'void'}} + (void)&f; + // expected-error@-1 {{address of overloaded function 'f' cannot be cast to type 'void'}} + // expected-note@#dr115-f {{candidate function template}} (void)&f; - (void)&g; // expected-error {{address of overloaded function 'g' cannot be cast to type 'void'}} - - &f; // expected-error {{reference to overloaded function could not be resolved}} - &f; // expected-warning {{unused}} - &g; // expected-error {{reference to overloaded function could not be resolved}} + (void)&g; + // expected-error@-1 {{address of overloaded function 'g' cannot be cast to type 'void'}} + // expected-note@#dr115-g-int {{candidate function template}} + // expected-note@#dr115-g {{candidate function template}} + + &f; + // expected-error@-1 {{reference to overloaded function could not be resolved; did you mean to call it?}} + // expected-note@#dr115-f {{possible target for call}} + &f; + // expected-warning@-1 {{expression result unused}} + &g; + // expected-error@-1 {{reference to overloaded function could not be resolved; did you mean to call it?}} + // expected-note@#dr115-g-int {{possible target for call}} + // expected-note@#dr115-g {{possible target for call}} } struct S { @@ -168,18 +201,25 @@ namespace dr115 { // dr115: yes template static int g(T, int); } s; - int k4 = f(&s.f); // expected-error {{non-constant pointer to member}} + int k4 = f(&s.f); + // expected-error@-1 {{cannot create a non-constant pointer to member function}} int k5 = f(&s.f); - int k6 = f(&s.g); // expected-error {{non-constant pointer to member}} + int k6 = f(&s.g); + // expected-error@-1 {{cannot create a non-constant pointer to member function}} void i() { - (void)&s.f; // expected-error {{non-constant pointer to member}} + (void)&s.f; + // expected-error@-1 {{cannot create a non-constant pointer to member function}} (void)&s.f; - (void)&s.g; // expected-error {{non-constant pointer to member}} - - &s.f; // expected-error {{non-constant pointer to member}} - &s.f; // expected-warning {{unused}} - &s.g; // expected-error {{non-constant pointer to member}} + (void)&s.g; + // expected-error@-1 {{cannot create a non-constant pointer to member function}} + + &s.f; + // expected-error@-1 {{cannot create a non-constant pointer to member function}} + &s.f; + // expected-warning@-1 {{expression result unused}} + &s.g; + // expected-error@-1 {{cannot create a non-constant pointer to member function}} } struct T { @@ -188,40 +228,58 @@ namespace dr115 { // dr115: yes template int g(T, int); } t; - int k7 = f(&s.f); // expected-error {{non-constant pointer to member}} + int k7 = f(&s.f); + // expected-error@-1 {{cannot create a non-constant pointer to member function}} int k8 = f(&s.f); - int k9 = f(&s.g); // expected-error {{non-constant pointer to member}} + int k9 = f(&s.g); + // expected-error@-1 {{cannot create a non-constant pointer to member function}} void j() { - (void)&s.f; // expected-error {{non-constant pointer to member}} + (void)&s.f; + // expected-error@-1 {{cannot create a non-constant pointer to member function}} (void)&s.f; - (void)&s.g; // expected-error {{non-constant pointer to member}} - - &s.f; // expected-error {{non-constant pointer to member}} - &s.f; // expected-warning {{unused}} - &s.g; // expected-error {{non-constant pointer to member}} + (void)&s.g; + // expected-error@-1 {{cannot create a non-constant pointer to member function}} + + &s.f; + // expected-error@-1 {{cannot create a non-constant pointer to member function}} + &s.f; + // expected-warning@-1 {{expression result unused}} + &s.g; + // expected-error@-1 {{cannot create a non-constant pointer to member function}} } #if __cplusplus >= 201103L // Special case kicks in only if a template argument list is specified. - template void with_default(); // expected-note +{{}} - int k10 = f(&with_default); // expected-error {{no matching function}} + template void with_default(); // #dr115-with-default + int k10 = f(&with_default); + // expected-error@-1 {{no matching function for call to 'f'}} + // expected-note@#dr115-f {{candidate template ignored: couldn't infer template argument 'T'}} int k11 = f(&with_default<>); void k() { - (void)&with_default; // expected-error {{overloaded function}} + (void)&with_default; + // expected-error@-1 {{address of overloaded function 'with_default' cannot be cast to type 'void'}} + // expected-note@#dr115-with-default {{candidate function template}} (void)&with_default<>; - &with_default; // expected-error {{overloaded function}} - &with_default<>; // expected-warning {{unused}} + &with_default; + // expected-error@-1 {{reference to overloaded function could not be resolved; did you mean to call it?}} + // expected-note@#dr115-with-default {{possible target for call}} + &with_default<>; + // expected-warning@-1 {{expression result unused}} } #endif } namespace dr116 { // dr116: yes template struct A {}; - template void f(A) {} // expected-note {{previous}} - template void f(A) {} // expected-error {{redefinition}} - template void f(A) {} // expected-note {{previous}} - template void f(A) {} // expected-error {{redefinition}} + template void f(A) {} // #dr116-f-N + template void f(A) {} + // expected-error@-1 {{redefinition of 'f'}} + // expected-note@#dr116-f-N {{previous definition is here}} + template void f(A) {} // #dr116-f-T + template void f(A) {} + // expected-error@-1 {{redefinition of 'f'}} + // expected-note@#dr116-f-T {{previous definition is here}} } // dr117: na @@ -235,7 +293,9 @@ namespace dr121 { // dr121: yes }; template struct Z { X::Y x; - T::Y y; // expected-error +{{}} + T::Y y; + // expected-error@-1 {{use 'template' keyword to treat 'Y' as a dependent template name}} + // cxx98-17-error@-2 {{missing 'typename' prior to dependent type name T::Y; implicit 'typename' is a C++20 extension}} }; Z z; } @@ -249,15 +309,19 @@ namespace dr122 { // dr122: yes // dr124: dup 201 // dr125: yes -struct dr125_A { struct dr125_B {}; }; // expected-note {{here}} +struct dr125_A { struct dr125_B {}; }; // #dr125_B dr125_A::dr125_B dr125_C(); namespace dr125_B { dr125_A dr125_C(); } namespace dr125 { struct X { friend dr125_A::dr125_B (::dr125_C)(); // ok friend dr125_A (::dr125_B::dr125_C)(); // ok - friend dr125_A::dr125_B::dr125_C(); // expected-error {{did you mean the constructor name 'dr125_B'?}} - // expected-error@-1 {{missing exception specification}} + friend dr125_A::dr125_B::dr125_C(); // #dr125_C + // expected-error@#dr125_C {{missing return type for function 'dr125_C'; did you mean the constructor name 'dr125_B'?}} + // cxx98-error@#dr125_C {{'dr125_B' is missing exception specification 'throw()'}} + // cxx98-note@#dr125_B {{previous declaration is here}} + // since-cxx11-error@#dr125_C {{'dr125_B' is missing exception specification 'noexcept'}} + // since-cxx11-note@#dr125_B {{previous declaration is here}} }; } @@ -275,7 +339,6 @@ namespace dr126 { // dr126: partial // So, when catching by non-const (or volatile) reference to pointer, we // should compare the exception type to the caught type and only accept an // exact match. -#if __cplusplus <= 201402L struct C {}; struct D : C {}; struct E : private C { friend class A; friend class B; }; @@ -283,53 +346,64 @@ namespace dr126 { // dr126: partial struct G : C {}; struct H : D, G {}; +#if __cplusplus <= 201402L struct A { virtual void cp() throw(C*); virtual void dp() throw(C*); - virtual void ep() throw(C*); // expected-note {{overridden}} - virtual void fp() throw(C*); // expected-note {{overridden}} + virtual void ep() throw(C*); // #dr126-ep + virtual void fp() throw(C*); // #dr126-fp virtual void gp() throw(C*); - virtual void hp() throw(C*); // expected-note {{overridden}} + virtual void hp() throw(C*); // #dr126-hp virtual void cr() throw(C&); virtual void dr() throw(C&); - virtual void er() throw(C&); // expected-note {{overridden}} - virtual void fr() throw(C&); // expected-note {{overridden}} + virtual void er() throw(C&); // #dr126-er + virtual void fr() throw(C&); // #dr126-fr virtual void gr() throw(C&); - virtual void hr() throw(C&); // expected-note {{overridden}} + virtual void hr() throw(C&); // #dr126-hr virtual void pv() throw(void*); -#if __cplusplus >= 201103L virtual void np() throw(C*); virtual void npm() throw(int C::*); - virtual void nr() throw(C*&); // expected-note {{overridden}} + virtual void nr() throw(C*&); // #dr126-nr virtual void ncr() throw(C*const&); -#endif virtual void ref1() throw(C *const&); virtual void ref2() throw(C *); virtual void v() throw(int); virtual void w() throw(const int); - virtual void x() throw(int*); // expected-note {{overridden}} + virtual void x() throw(int*); // #dr126-x virtual void y() throw(const int*); - virtual void z() throw(int); // expected-note {{overridden}} + virtual void z() throw(int); // #dr126-z }; struct B : A { virtual void cp() throw(C*); virtual void dp() throw(D*); - virtual void ep() throw(E*); // expected-error {{more lax}} - virtual void fp() throw(F*); // expected-error {{more lax}} + virtual void ep() throw(E*); + // cxx98-14-error@-1 {{exception specification of overriding function is more lax than base version}} + // cxx98-14-note@#dr126-ep {{overridden virtual function is here}} + virtual void fp() throw(F*); + // cxx98-14-error@-1 {{exception specification of overriding function is more lax than base version}} + // cxx98-14-note@#dr126-fp {{overridden virtual function is here}} virtual void gp() throw(G*); - virtual void hp() throw(H*); // expected-error {{more lax}} + virtual void hp() throw(H*); + // cxx98-14-error@-1 {{exception specification of overriding function is more lax than base version}} + // cxx98-14-note@#dr126-hp {{overridden virtual function is here}} virtual void cr() throw(C&); virtual void dr() throw(D&); - virtual void er() throw(E&); // expected-error {{more lax}} - virtual void fr() throw(F&); // expected-error {{more lax}} + virtual void er() throw(E&); + // cxx98-14-error@-1 {{exception specification of overriding function is more lax than base version}} + // cxx98-14-note@#dr126-er {{overridden virtual function is here}} + virtual void fr() throw(F&); + // cxx98-14-error@-1 {{exception specification of overriding function is more lax than base version}} + // cxx98-14-note@#dr126-fr {{overridden virtual function is here}} virtual void gr() throw(G&); - virtual void hr() throw(H&); // expected-error {{more lax}} + virtual void hr() throw(H&); + // cxx98-14-error@-1 {{exception specification of overriding function is more lax than base version}} + // cxx98-14-note@#dr126-hr {{overridden virtual function is here}} virtual void pv() throw(C*); @@ -337,34 +411,45 @@ namespace dr126 { // dr126: partial using nullptr_t = decltype(nullptr); virtual void np() throw(nullptr_t); virtual void npm() throw(nullptr_t&); - virtual void nr() throw(nullptr_t); // expected-error {{more lax}} + virtual void nr() throw(nullptr_t); + // cxx11-14-error@-1 {{exception specification of overriding function is more lax than base version}} + // cxx11-14-note@#dr126-nr {{overridden virtual function is here}} virtual void ncr() throw(nullptr_t); -#endif +#endif // __cplusplus >= 201103L virtual void ref1() throw(D *const &); virtual void ref2() throw(D *); virtual void v() throw(const int); virtual void w() throw(int); - virtual void x() throw(const int*); // expected-error {{more lax}} + virtual void x() throw(const int*); + // cxx98-14-error@-1 {{exception specification of overriding function is more lax than base version}} + // cxx98-14-note@#dr126-x {{overridden virtual function is here}} virtual void y() throw(int*); // ok - virtual void z() throw(long); // expected-error {{more lax}} + virtual void z() throw(long); + // cxx98-14-error@-1 {{exception specification of overriding function is more lax than base version}} + // cxx98-14-note@#dr126-z {{overridden virtual function is here}} }; -#else - void f() throw(int); // expected-error {{ISO C++17 does not allow}} expected-note {{use 'noexcept}} -#endif +#endif // __cplusplus <= 201402L + void f() throw(int); + // since-cxx17-error@-1 {{ISO C++17 does not allow dynamic exception specifications}} + // since-cxx17-note@-2 {{use 'noexcept(false)' instead}} } -namespace dr127 { // dr127: yes +namespace dr127 { // dr127: 2.9 __extension__ typedef __decltype(sizeof(0)) size_t; template struct A { A() { throw 0; } void *operator new(size_t, const char * = 0); - void operator delete(void *, const char *) { T::error; } // expected-error 2{{no members}} + void operator delete(void *, const char *) { T::error; } // #dr127-delete-const-char + // expected-error@#dr127-delete-const-char {{type 'void' cannot be used prior to '::' because it has no members}} + // expected-note@#dr127-p {{in instantiation of member function 'dr127::A::operator delete' requested here}} + // expected-error@#dr127-delete-const-char {{type 'int' cannot be used prior to '::' because it has no members}} + // expected-note@#dr127-q {{in instantiation of member function 'dr127::A::operator delete' requested here}} void operator delete(void *) { T::error; } }; - A *p = new A; // expected-note {{instantiat}} - A *q = new ("") A; // expected-note {{instantiat}} + A *p = new A; // #dr127-p + A *q = new ("") A; // #dr127-q } namespace dr128 { // dr128: yes @@ -401,36 +486,50 @@ namespace dr135 { // dr135: yes } namespace dr136 { // dr136: 3.4 - void f(int, int, int = 0); // expected-note {{previous declaration is here}} - void g(int, int, int); // expected-note {{previous declaration is here}} + void f(int, int, int = 0); // #dr136-f + void g(int, int, int); // #dr136-g struct A { - friend void f(int, int = 0, int); // expected-error {{friend declaration specifying a default argument must be the only declaration}} - friend void g(int, int, int = 0); // expected-error {{friend declaration specifying a default argument must be the only declaration}} - friend void h(int, int, int = 0); // expected-error {{friend declaration specifying a default argument must be a definition}} - friend void i(int, int, int = 0) {} // expected-note {{previous declaration is here}} + friend void f(int, int = 0, int); + // expected-error@-1 {{friend declaration specifying a default argument must be the only declaration}} + // expected-note@#dr136-f {{previous declaration is here}} + friend void g(int, int, int = 0); + // expected-error@-1 {{friend declaration specifying a default argument must be the only declaration}} + // expected-note@#dr136-g {{previous declaration is here}} + friend void h(int, int, int = 0); + // expected-error@-1 {{friend declaration specifying a default argument must be a definition}} + friend void i(int, int, int = 0) {} // #dr136-A-i friend void j(int, int, int = 0) {} operator int(); }; - void i(int, int, int); // expected-error {{friend declaration specifying a default argument must be the only declaration}} + void i(int, int, int); + // expected-error@-1 {{friend declaration specifying a default argument must be the only declaration}} + // expected-note@#dr136-A-i {{previous declaration is here}} void q() { j(A(), A()); // ok, has default argument } - extern "C" void k(int, int, int, int); // expected-note 2{{previous declaration is here}} + extern "C" void k(int, int, int, int); // #dr136-k namespace NSA { struct A { - friend void dr136::k(int, int, int, int = 0); // expected-error {{friend declaration specifying a default argument must be the only declaration}} + friend void dr136::k(int, int, int, int = 0); + // expected-error@-1 {{friend declaration specifying a default argument must be the only declaration}} + // expected-note@#dr136-k {{previous declaration is here}} }; } namespace NSB { struct A { - friend void dr136::k(int, int, int = 0, int); // expected-error {{missing default argument on parameter}} expected-error {{must be the only declaration}} + friend void dr136::k(int, int, int = 0, int); // #dr136-friend-k + // expected-error@#dr136-friend-k {{friend declaration specifying a default argument must be the only declaration}} + // expected-note@#dr136-k {{previous declaration is here}} + // expected-error@#dr136-friend-k {{missing default argument on parameter}} }; } struct B { - void f(int); // expected-note {{previous declaration is here}} + void f(int); // #dr136-B-f }; struct C { - friend void B::f(int = 0); // expected-error {{friend declaration specifying a default argument must be the only declaration}} + friend void B::f(int = 0); + // expected-error@-1 {{friend declaration specifying a default argument must be the only declaration}} + // expected-note@#dr136-B-f {{previous declaration is here}} }; } @@ -440,13 +539,18 @@ namespace dr137 { // dr137: yes extern volatile void *vp; extern const volatile void *cvp; int *q = static_cast(p); - int *qc = static_cast(cp); // expected-error {{casts away qualifiers}} - int *qv = static_cast(vp); // expected-error {{casts away qualifiers}} - int *qcv = static_cast(cvp); // expected-error {{casts away qualifiers}} + int *qc = static_cast(cp); + // expected-error@-1 {{static_cast from 'const void *' to 'int *' casts away qualifiers}} + int *qv = static_cast(vp); + // expected-error@-1 {{static_cast from 'volatile void *' to 'int *' casts away qualifiers}} + int *qcv = static_cast(cvp); + // expected-error@-1 {{static_cast from 'const volatile void *' to 'int *' casts away qualifiers}} const int *cq = static_cast(p); const int *cqc = static_cast(cp); - const int *cqv = static_cast(vp); // expected-error {{casts away qualifiers}} - const int *cqcv = static_cast(cvp); // expected-error {{casts away qualifiers}} + const int *cqv = static_cast(vp); + // expected-error@-1 {{static_cast from 'volatile void *' to 'const int *' casts away qualifiers}} + const int *cqcv = static_cast(cvp); + // expected-error@-1 {{static_cast from 'const volatile void *' to 'const int *' casts away qualifiers}} const volatile int *cvq = static_cast(p); const volatile int *cvqc = static_cast(cp); const volatile int *cvqv = static_cast(vp); @@ -455,9 +559,11 @@ namespace dr137 { // dr137: yes namespace dr139 { // dr139: yes namespace example1 { - typedef int f; // expected-note {{previous}} + typedef int f; // #dr139-typedef-f struct A { - friend void f(A &); // expected-error {{different kind of symbol}} + friend void f(A &); + // expected-error@-1 {{redefinition of 'f' as different kind of symbol}} + // expected-note@#dr139-typedef-f {{previous definition is here}} }; } @@ -474,35 +580,41 @@ namespace dr139 { // dr139: yes } namespace dr140 { // dr140: yes - void f(int *const) {} // expected-note {{previous}} - void f(int[3]) {} // expected-error {{redefinition}} + void f(int *const) {} // #dr140-f-first + void f(int[3]) {} + // expected-error@-1 {{redefinition of 'f'}} + // expected-note@#dr140-f-first {{previous definition is here}} void g(const int); void g(int n) { n = 2; } } -namespace dr141 { // dr141: yes +namespace dr141 { // dr141: 3.1 template void f(); - template struct S { int n; }; // expected-note {{'::dr141::S::n' declared here}} + template struct S { int n; }; // #dr141-S struct A : S { template void f(); - template struct S {}; + template struct S {}; // #dr141-A-S } a; struct B : S {} b; void g() { a.f(); - (void)a.S::n; // expected-error {{no member named 'n' in 'dr141::A::S'; did you mean '::dr141::S::n'?}} -#if __cplusplus < 201103L - // expected-error@-2 {{ambiguous}} - // expected-note@-11 {{lookup from the current scope}} - // expected-note@-9 {{lookup in the object type}} -#endif - b.f(); // expected-error {{no member}} expected-error +{{}} + (void)a.S::n; // #dr141-a + // cxx98-error@#dr141-a {{lookup of 'S' in member access expression is ambiguous; using member of 'struct A'}} + // cxx98-note@#dr141-A-S {{lookup in the object type 'struct A' refers here}} + // cxx98-note@#dr141-S {{lookup from the current scope refers here}} + // expected-error@#dr141-a {{no member named 'n' in 'dr141::A::S'; did you mean '::dr141::S::n'?}} + // expected-note@#dr141-S {{'::dr141::S::n' declared here}} + // FIXME: we issue a useful diagnostic first, then some bogus ones. + b.f(); + // expected-error@-1 {{no member named 'f' in 'dr141::B'}} + // expected-error@-2 +{{}} (void)b.S::n; } template struct C { T t; void g() { - t.f(); // expected-error {{use 'template'}} + t.f(); + // expected-error@-1 {{use 'template' keyword to treat 'f' as a dependent template name}} } void h() { (void)t.S::n; // ok @@ -518,29 +630,54 @@ namespace dr141 { // dr141: yes void i() { C().i(); } // ok!! } -namespace dr142 { // dr142: yes - class B { // expected-note +{{here}} +namespace dr142 { // dr142: 2.8 + class B { // #dr142-B public: - int mi; // expected-note +{{here}} - static int si; // expected-note +{{here}} + int mi; // #dr142-B-mi + static int si; // #dr142-B-si }; - class D : private B { // expected-note +{{here}} + class D : private B { // #dr142-D }; class DD : public D { void f(); }; void DD::f() { - mi = 3; // expected-error {{private member}} - si = 3; // expected-error {{private member}} - B b_old; // expected-error {{private member}} + mi = 3; + // expected-error@-1 {{'mi' is a private member of 'dr142::B'}} + // expected-note@#dr142-D {{constrained by private inheritance here}} + // expected-note@#dr142-B-mi {{member is declared here}} + si = 3; + // expected-error@-1 {{'si' is a private member of 'dr142::B'}} + // expected-note@#dr142-D {{constrained by private inheritance here}} + // expected-note@#dr142-B-si {{member is declared here}} + B b_old; + // expected-error@-1 {{'B' is a private member of 'dr142::B'}} + // expected-note@#dr142-D {{constrained by private inheritance here}} + // expected-note@#dr142-B {{member is declared here}} dr142::B b; b.mi = 3; b.si = 3; - B::si = 3; // expected-error {{private member}} + B::si = 3; + // expected-error@-1 {{'B' is a private member of 'dr142::B'}} + // expected-note@#dr142-D {{constrained by private inheritance here}} + // expected-note@#dr142-B {{member is declared here}} dr142::B::si = 3; - B *bp1_old = this; // expected-error {{private member}} expected-error {{private base class}} - dr142::B *bp1 = this; // expected-error {{private base class}} - B *bp2_old = (B*)this; // expected-error 2{{private member}} + B *bp1_old = this; // #dr142-bp1_old + // expected-error@#dr142-bp1_old {{'B' is a private member of 'dr142::B'}} + // expected-note@#dr142-D {{constrained by private inheritance here}} + // expected-note@#dr142-B {{member is declared here}} + // expected-error@#dr142-bp1_old {{cannot cast 'dr142::DD' to its private base class 'B'}} + // expected-note@#dr142-D {{declared private here}} + dr142::B *bp1 = this; + // expected-error@-1 {{cannot cast 'dr142::DD' to its private base class 'dr142::B'}} + // expected-note@#dr142-D {{declared private here}} + B *bp2_old = (B*)this; // #dr142-bp2_old + // expected-error@#dr142-bp2_old {{'B' is a private member of 'dr142::B'}} + // expected-note@#dr142-D {{constrained by private inheritance here}} + // expected-note@#dr142-B {{member is declared here}} + // expected-error@#dr142-bp2_old {{'B' is a private member of 'dr142::B'}} + // expected-note@#dr142-D {{constrained by private inheritance here}} + // expected-note@#dr142-B {{member is declared here}} dr142::B *bp2 = (dr142::B*)this; bp2->mi = 3; } @@ -553,19 +690,19 @@ namespace dr143 { // dr143: yes struct X { friend void B::f(X); }; } void g(A::X x) { - f(x); // expected-error {{undeclared identifier 'f'}} + f(x); + // expected-error@-1 {{use of undeclared identifier 'f'}} } } namespace dr145 { // dr145: yes void f(bool b) { -#if __cplusplus <= 201402L - ++b; // expected-warning {{deprecated}} - b++; // expected-warning {{deprecated}} -#else - ++b; // expected-error {{increment}} - b++; // expected-error {{increment}} -#endif + ++b; + // cxx98-14-warning@-1 {{incrementing expression of type bool is deprecated and incompatible with C++17}} + // since-cxx17-error@-2 {{ISO C++17 does not allow incrementing expression of type bool}} + b++; + // cxx98-14-warning@-1 {{incrementing expression of type bool is deprecated and incompatible with C++17}} + // since-cxx17-error@-2 {{ISO C++17 does not allow incrementing expression of type bool}} } } @@ -576,13 +713,15 @@ namespace dr147 { // dr147: yes }; // Per core issue 1435, this is ill-formed because A::A does not // name the injected-class-name. (A::A does, though.) - template<> template<> A::A(int) {} // expected-error {{out-of-line constructor for 'A' cannot have template arguments}} + template<> template<> A::A(int) {} + // expected-error@-1 {{out-of-line constructor for 'A' cannot have template arguments}} template<> template<> A::A(float) {} } namespace example2 { struct A { A(); }; struct B : A { B(); }; - A::A a1; // expected-error {{is a constructor}} + A::A a1; + // expected-error@-1 {{qualified reference to 'A' is a constructor name rather than a type in this context}} B::A a2; } namespace example3 { @@ -590,7 +729,8 @@ namespace dr147 { // dr147: yes template A(T); static A a; }; - template<> A::A(A::a); // expected-error {{is a constructor}} + template<> A::A(A::a); + // expected-error@-1 {{qualified reference to 'A' is a constructor name rather than a template name in this context}} } } @@ -602,7 +742,7 @@ namespace dr148 { // dr148: yes // dr149: na -namespace dr151 { // dr151: yes +namespace dr151 { // dr151: 3.1 struct X {}; typedef int X::*p; #if __cplusplus < 201103L @@ -616,24 +756,28 @@ namespace dr151 { // dr151: yes namespace dr152 { // dr152: yes struct A { - A(); // expected-note 0-2{{not viable}} - explicit A(const A&); // expected-note 1-2{{not a candidate}} + A(); // #dr152-A-ctor + explicit A(const A&); // #dr152-A-explicit-ctor }; A a1 = A(); -#if __cplusplus <= 201402L - // expected-error@-2 {{no matching constructor}} -#endif + // cxx98-14-error@-1 {{no matching constructor for initialization of 'A'}} + // cxx98-14-note@#dr152-A-explicit-ctor {{explicit constructor is not a candidate}} + // cxx98-14-note@#dr152-A-ctor {{candidate constructor not viable: requires 0 arguments, but 1 was provided}} A a2((A())); A &f(); - A a3 = f(); // expected-error {{no matching constructor}} + A a3 = f(); + // expected-error@-1 {{no matching constructor for initialization of 'A'}} + // expected-note@#dr152-A-explicit-ctor {{explicit constructor is not a candidate}} + // expected-note@#dr152-A-ctor {{candidate constructor not viable: requires 0 arguments, but 1 was provided}} A a4(f()); } // dr153: na namespace dr154 { // dr154: yes - union { int a; }; // expected-error {{must be declared 'static'}} + union { int a; }; + // expected-error@-1 {{nonymous unions at namespace or global scope must be declared 'static'}} namespace { union { int b; }; } @@ -641,7 +785,8 @@ namespace dr154 { // dr154: yes } namespace dr155 { // dr155: dup 632 - struct S { int n; } s = { { 1 } }; // expected-warning {{braces around scalar initializer}} + struct S { int n; } s = { { 1 } }; + // expected-warning@-1 {{braces around scalar initializer}} } // dr158 is in its own file. @@ -649,18 +794,19 @@ namespace dr155 { // dr155: dup 632 namespace dr159 { // dr159: 3.5 namespace X { void f(); } void f(); - void dr159::f() {} // expected-warning {{extra qualification}} + void dr159::f() {} + // expected-warning@-1 {{extra qualification on member 'f'}} void dr159::X::f() {} } // dr160: na -namespace dr161 { // dr161: yes +namespace dr161 { // dr161: 3.1 class A { protected: - struct B { int n; } b; // expected-note 2{{here}} + struct B { int n; } b; // #dr161-B static B bs; - void f(); // expected-note {{here}} + void f(); // #dr161-f static void sf(); }; struct C : A {}; @@ -669,13 +815,19 @@ namespace dr161 { // dr161: yes (void)b.n; B b1; C::B b2; // ok, accessible as a member of A - (void)&C::b; // expected-error {{protected}} + (void)&C::b; + // expected-error@-1 {{'b' is a protected member of 'dr161::A'}} + // expected-note@#dr161-B {{declared protected here}} (void)&C::bs; - (void)c.b; // expected-error {{protected}} + (void)c.b; + // expected-error@-1 {{'b' is a protected member of 'dr161::A'}} + // expected-note@#dr161-B {{declared protected here}} (void)c.bs; f(); sf(); - c.f(); // expected-error {{protected}} + c.f(); + // expected-error@-1 {{protected}} + // expected-note@#dr161-f {{declared protected here}} c.sf(); A::f(); D::f(); @@ -692,13 +844,17 @@ namespace dr162 { // dr162: no static int &f(int); void g() { - int &a = (&A::f)(0); // FIXME: expected-error {{could not be resolved}} - char &b = (&A::f)('0'); // expected-error {{could not be resolved}} + int &a = (&A::f)(0); + // FIXME: expected-error@-1 {{reference to overloaded function could not be resolved; did you mean to call it?}} + char &b = (&A::f)('0'); + // expected-error@-1 {{reference to overloaded function could not be resolved; did you mean to call it?}} } }; - int &c = (&A::f)(0); // FIXME: expected-error {{could not be resolved}} - char &d = (&A::f)('0'); // expected-error {{could not be resolved}} + int &c = (&A::f)(0); + // FIXME: expected-error@-1 {{reference to overloaded function could not be resolved; did you mean to call it?}} + char &d = (&A::f)('0'); + // expected-error@-1 {{reference to overloaded function could not be resolved; did you mean to call it?}} } // dr163: na @@ -724,12 +880,15 @@ namespace dr165 { // dr165: no void N::g() {} } -namespace dr166 { // dr166: yes +namespace dr166 { // dr166: 2.9 namespace A { class X; } template int f(T t) { return t.n; } int g(A::X); - template int h(T t) { return t.n; } // expected-error {{private}} + template int h(T t) { return t.n; } + // expected-error@-1 {{'n' is a private member of 'dr166::A::X'}} + // expected-note@#dr166-h-instantiation {{in instantiation of function template specialization 'dr166::h' requested here}} + // expected-note@#dr166-X-n {{implicitly declared private here}} int i(A::X); namespace A { @@ -738,7 +897,7 @@ namespace dr166 { // dr166: yes friend int dr166::g(X); friend int h(X); friend int i(X); - int n; // expected-note 2{{here}} + int n; // #dr166-X-n }; int h(X x) { return x.n; } @@ -747,8 +906,10 @@ namespace dr166 { // dr166: yes template int f(A::X); int g(A::X x) { return x.n; } - template int h(A::X); // expected-note {{instantiation}} - int i(A::X x) { return x.n; } // expected-error {{private}} + template int h(A::X); // #dr166-h-instantiation + int i(A::X x) { return x.n; } + // expected-error@-1 {{'n' is a private member of 'dr166::A::X'}} + // expected-note@#dr166-X-n {{implicitly declared private here}} } // dr167: sup 1012 @@ -768,23 +929,29 @@ namespace dr169 { // dr169: yes struct B { template struct C; template void f(); - template static int n; // expected-error 0-1{{extension}} + template static int n; + // cxx98-11-error@-1 {{variable templates are a C++14 extension}} }; struct D : A, B { using A::n; - using B::C; // expected-error {{using declaration cannot refer to a template specialization}} - using B::f; // expected-error {{using declaration cannot refer to a template specialization}} - using B::n; // expected-error {{using declaration cannot refer to a template specialization}} + using B::C; + // expected-error@-1 {{using declaration cannot refer to a template specialization}} + using B::f; + // expected-error@-1 {{using declaration cannot refer to a template specialization}} + using B::n; + // expected-error@-1 {{using declaration cannot refer to a template specialization}} }; } -namespace { // dr171: yes +namespace { // dr171: 3.4 int dr171a; } -int dr171b; // expected-note {{here}} +int dr171b; // #dr171b-int namespace dr171 { extern "C" void dr171a(); - extern "C" void dr171b(); // expected-error {{conflicts}} + extern "C" void dr171b(); + // expected-error@-1 {{declaration of 'dr171b' with C language linkage conflicts with declaration in global scope}} + // expected-note@#dr171b-int {{declared in global scope here}} } namespace dr172 { // dr172: yes @@ -810,12 +977,14 @@ namespace dr172 { // dr172: yes int check6a[sizeof(d) == sizeof(unsigned long) ? 1 : -1]; int check6b[-d > 0 ? 1 : -1]; - enum { e = (unsigned long long)-1 / 2 }; // expected-error 0-1{{extension}} - int check7a[sizeof(e) == sizeof(long) ? 1 : -1]; // expected-error 0-1{{extension}} + enum { e = (unsigned long long)-1 / 2 }; + // cxx98-error@-1 {{'long long' is a C++11 extension}} + int check7a[sizeof(e) == sizeof(long) ? 1 : -1]; int check7b[-e < 0 ? 1 : -1]; - enum { f = (unsigned long long)-1 / 2 + 1 }; // expected-error 0-1{{extension}} - int check8a[sizeof(f) == sizeof(unsigned long) ? 1 : -1]; // expected-error 0-1{{extension}} + enum { f = (unsigned long long)-1 / 2 + 1 }; + // cxx98-error@-1 {{'long long' is a C++11 extension}} + int check8a[sizeof(f) == sizeof(unsigned long) ? 1 : -1]; int check8b[-f > 0 ? 1 : -1]; } @@ -827,25 +996,30 @@ namespace dr173 { // dr173: yes // dr174: sup 1012 -namespace dr175 { // dr175: yes - struct A {}; // expected-note {{here}} - struct B : private A {}; // expected-note {{constrained by private inheritance}} +namespace dr175 { // dr175: 2.8 + struct A {}; // #dr175-A + struct B : private A {}; // #dr175-B struct C : B { - A a; // expected-error {{private}} + A a; + // expected-error@-1 {{'A' is a private member of 'dr175::A'}} + // expected-note@#dr175-B {{constrained by private inheritance here}} + // expected-note@#dr175-A {{member is declared here}} dr175::A b; }; } -namespace dr176 { // dr176: yes +namespace dr176 { // dr176: 3.1 template class Y; template<> class Y { void f() { - typedef Y A; // expected-note {{here}} - typedef Y A; // expected-error {{different types ('Y' vs 'Y')}} + typedef Y A; // #dr176-A-first + typedef Y A; + // expected-error@-1 {{typedef redefinition with different types ('Y' vs 'Y')}} + // expected-note@#dr176-A-first {{previous definition is here}} } }; - template struct Base {}; // expected-note 2{{found}} + template struct Base {}; // #dr176-Base template struct Derived : public Base { void f() { typedef typename Derived::template Base A; @@ -855,35 +1029,44 @@ namespace dr176 { // dr176: yes template struct Derived; template struct Derived2 : Base, Base { - typename Derived2::Base b; // expected-error {{found in multiple base classes}} + typename Derived2::Base b; + // expected-error@-1 {{member 'Base' found in multiple base classes of different types}} + // expected-note@#dr176-Base {{member type 'dr176::Base' found by ambiguous name lookup}} + // expected-note@#dr176-Base {{member type 'dr176::Base' found by ambiguous name lookup}} typename Derived2::Base d; }; - template class X { // expected-note {{here}} + template class X { // #dr176-X X *p1; X *p2; X *p3; - dr176::X *p4; // expected-error {{requires template arguments}} + dr176::X *p4; // #dr176-p4 + // cxx98-14-error@#dr176-p4 {{use of class template 'dr176::X' requires template arguments}} + // cxx98-14-note@#dr176-X {{template is declared here}} + // since-cxx17-error@#dr176-p4 {{use of class template 'X' requires template arguments; argument deduction not allowed in non-static class member}} + // since-cxx17-note@#dr176-X {{template is declared here}} }; } namespace dr177 { // dr177: yes struct B {}; struct A { - A(A &); // expected-note 0-1{{not viable: expects an lvalue}} - A(const B &); // expected-note 0-1{{not viable: no known conversion from 'A' to}} + A(A &); // #dr177-A-copy-ctor + A(const B &); // #dr177-A-ctor-from-B }; B b; A a = b; -#if __cplusplus <= 201402L - // expected-error@-2 {{no viable constructor copying variable}} -#endif + // cxx98-14-error@-1 {{no viable constructor copying variable of type 'A'}} + // cxx98-14-note@#dr177-A-copy-ctor {{candidate constructor not viable: expects an lvalue for 1st argument}} + // cxx98-14-note@#dr177-A-ctor-from-B {{candidate constructor not viable: no known conversion from 'A' to 'const B &' for 1st argument}} - struct C { C(C&); }; // expected-note {{not viable: expects an lvalue for 1st argument}} + struct C { C(C&); }; // #dr177-C-copy-ctor struct D : C {}; struct E { operator D(); }; E e; - C c = e; // expected-error {{no viable constructor copying variable of type 'D'}} + C c = e; + // expected-error@-1 {{no viable constructor copying variable of type 'D'}} + // expected-note@#dr177-C-copy-ctor {{candidate constructor not viable: expects an lvalue for 1st argument}} } namespace dr178 { // dr178: yes @@ -901,10 +1084,11 @@ namespace dr178 { // dr178: yes namespace dr179 { // dr179: yes void f(); - int n = &f - &f; // expected-error {{arithmetic on pointers to the function type 'void ()'}} + int n = &f - &f; + // expected-error@-1 {{arithmetic on pointers to the function type 'void ()'}} } -namespace dr180 { // dr180: yes +namespace dr180 { // dr180: 2.8 template struct X : T, T::some_base { X() : T::some_type_that_might_be_T(), T::some_base() {} friend class T::some_class; @@ -916,8 +1100,10 @@ namespace dr180 { // dr180: yes namespace dr181 { // dr181: yes namespace X { - template