diff --git a/.github/new-prs-labeler.yml b/.github/new-prs-labeler.yml index 50b962a463eb2..9deb167f1e174 100644 --- a/.github/new-prs-labeler.yml +++ b/.github/new-prs-labeler.yml @@ -90,6 +90,10 @@ compiler-rt:sanitizer: - compiler-rt/lib/scudo/** - compiler-rt/test/scudo/** +compiler-rt:scudo: + - compiler-rt/lib/scudo/** + - compiler-rt/test/scudo/** + xray: - llvm/tools/llvm-xray/** - compiler-rt/*/xray/** diff --git a/.github/workflows/pr-code-format.yml b/.github/workflows/pr-code-format.yml new file mode 100644 index 0000000000000..9b4f4fd6b24d4 --- /dev/null +++ b/.github/workflows/pr-code-format.yml @@ -0,0 +1,54 @@ +name: "Check code formatting" +on: pull_request_target +permissions: + pull-requests: write + +jobs: + code_formatter: + runs-on: ubuntu-latest + steps: + - name: Fetch LLVM sources + uses: actions/checkout@v4 + with: + fetch-depth: 2 + + - name: Get changed files + id: changed-files + uses: tj-actions/changed-files@v39 + with: + separator: "," + fetch_depth: 10 # Fetches only the last 10 commits + + - name: "Listed files" + run: | + echo "Formatting files:" + echo "${{ steps.changed-files.outputs.all_changed_files }}" + + - name: Install clang-format + uses: aminya/setup-cpp@v1 + with: + clangformat: 16.0.6 + + - name: Setup Python env + uses: actions/setup-python@v4 + with: + python-version: '3.11' + cache: 'pip' + cache-dependency-path: 'llvm/utils/git/requirements_formatting.txt' + + - name: Install python dependencies + run: pip install -r llvm/utils/git/requirements_formatting.txt + + - name: Run code formatter + env: + GITHUB_PR_NUMBER: ${{ github.event.pull_request.number }} + START_REV: ${{ github.event.pull_request.base.sha }} + END_REV: ${{ github.event.pull_request.head.sha }} + CHANGED_FILES: ${{ steps.changed-files.outputs.all_changed_files }} + run: | + python llvm/utils/git/code-format-helper.py \ + --token ${{ secrets.GITHUB_TOKEN }} \ + --issue-number $GITHUB_PR_NUMBER \ + --start-rev $START_REV \ + --end-rev $END_REV \ + --changed-files "$CHANGED_FILES" diff --git a/.github/workflows/pr-python-format.yml b/.github/workflows/pr-python-format.yml deleted file mode 100644 index c612295882654..0000000000000 --- a/.github/workflows/pr-python-format.yml +++ /dev/null @@ -1,39 +0,0 @@ -name: "Check Python Formatting" -on: - pull_request: - # run on .py - paths: - - '**.py' - -jobs: - python_formatting: - runs-on: ubuntu-latest - steps: - - name: Fetch LLVM sources - uses: actions/checkout@v4 - with: - persist-credentials: false - fetch-depth: 2 - - - name: Get changed files - id: changed-files - uses: tj-actions/changed-files@v39 - with: - files: '**/*.py' - - - name: "Listed files" - run: | - echo "Formatting files:" - echo "${{ steps.changed-files.outputs.all_changed_files }}" - - - name: Setup Python env - uses: actions/setup-python@v4 - with: - python-version: '3.11' - - - name: Python Formatting - uses: akaihola/darker@1.7.2 - with: - options: "--check --diff --color" - version: "~=1.7.2" - src: "${{ steps.changed-files.outputs.all_changed_files }}" diff --git a/bolt/include/bolt/Core/MCPlusBuilder.h b/bolt/include/bolt/Core/MCPlusBuilder.h index b815904f9672b..5e12a4ac14c9e 100644 --- a/bolt/include/bolt/Core/MCPlusBuilder.h +++ b/bolt/include/bolt/Core/MCPlusBuilder.h @@ -29,6 +29,7 @@ #include "llvm/Support/Allocator.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/ErrorOr.h" +#include "llvm/Support/RWMutex.h" #include #include #include @@ -166,10 +167,15 @@ class MCPlusBuilder { /// Names of non-standard annotations. SmallVector AnnotationNames; + /// A mutex that is used to control parallel accesses to + /// AnnotationNameIndexMap and AnnotationsNames. + mutable llvm::sys::RWMutex AnnotationNameMutex; + /// Allocate the TailCall annotation value. Clients of the target-specific /// MCPlusBuilder classes must use convert/lower/create* interfaces instead. void setTailCall(MCInst &Inst); +public: /// Transfer annotations from \p SrcInst to \p DstInst. void moveAnnotations(MCInst &&SrcInst, MCInst &DstInst) const { assert(!getAnnotationInst(DstInst) && @@ -182,7 +188,6 @@ class MCPlusBuilder { removeAnnotationInst(SrcInst); } -public: /// Return iterator range covering def operands. iterator_range defOperands(MCInst &Inst) const { return make_range(Inst.begin(), @@ -621,6 +626,11 @@ class MCPlusBuilder { return Info->get(Inst.getOpcode()).mayStore(); } + virtual bool isAArch64Exclusive(const MCInst &Inst) const { + llvm_unreachable("not implemented"); + return false; + } + virtual bool isCleanRegXOR(const MCInst &Inst) const { llvm_unreachable("not implemented"); return false; @@ -1775,6 +1785,7 @@ class MCPlusBuilder { /// Return annotation index matching the \p Name. std::optional getAnnotationIndex(StringRef Name) const { + std::shared_lock Lock(AnnotationNameMutex); auto AI = AnnotationNameIndexMap.find(Name); if (AI != AnnotationNameIndexMap.end()) return AI->second; @@ -1784,10 +1795,10 @@ class MCPlusBuilder { /// Return annotation index matching the \p Name. Create a new index if the /// \p Name wasn't registered previously. unsigned getOrCreateAnnotationIndex(StringRef Name) { - auto AI = AnnotationNameIndexMap.find(Name); - if (AI != AnnotationNameIndexMap.end()) - return AI->second; + if (std::optional Index = getAnnotationIndex(Name)) + return *Index; + std::unique_lock Lock(AnnotationNameMutex); const unsigned Index = AnnotationNameIndexMap.size() + MCPlus::MCAnnotation::kGeneric; AnnotationNameIndexMap.insert(std::make_pair(Name, Index)); diff --git a/bolt/lib/Passes/FixRISCVCallsPass.cpp b/bolt/lib/Passes/FixRISCVCallsPass.cpp index 34821ec80c1b4..963a8b04cf9db 100644 --- a/bolt/lib/Passes/FixRISCVCallsPass.cpp +++ b/bolt/lib/Passes/FixRISCVCallsPass.cpp @@ -19,6 +19,7 @@ void FixRISCVCallsPass::runOnFunction(BinaryFunction &BF) { auto *Target = MIB->getTargetSymbol(*II); assert(Target && "Cannot find call target"); + MCInst OldCall = *II; auto L = BC.scopeLock(); if (MIB->isTailCall(*II)) @@ -26,6 +27,7 @@ void FixRISCVCallsPass::runOnFunction(BinaryFunction &BF) { else MIB->createCall(*II, Target, Ctx); + MIB->moveAnnotations(std::move(OldCall), *II); ++II; continue; } @@ -39,8 +41,19 @@ void FixRISCVCallsPass::runOnFunction(BinaryFunction &BF) { auto *Target = MIB->getTargetSymbol(*II); assert(Target && "Cannot find call target"); + MCInst OldCall = *NextII; auto L = BC.scopeLock(); MIB->createCall(*II, Target, Ctx); + MIB->moveAnnotations(std::move(OldCall), *II); + + // The original offset was set on the jalr of the auipc+jalr pair. Since + // the whole pair is replaced by a call, adjust the offset by -4 (the + // size of a auipc). + if (std::optional Offset = MIB->getOffset(*II)) { + assert(*Offset >= 4 && "Illegal jalr offset"); + MIB->setOffset(*II, *Offset - 4); + } + II = BB.eraseInstruction(NextII); continue; } diff --git a/bolt/lib/Passes/Instrumentation.cpp b/bolt/lib/Passes/Instrumentation.cpp index 98044599d497e..72adb319d71dc 100644 --- a/bolt/lib/Passes/Instrumentation.cpp +++ b/bolt/lib/Passes/Instrumentation.cpp @@ -13,6 +13,7 @@ #include "bolt/Passes/Instrumentation.h" #include "bolt/Core/ParallelUtilities.h" #include "bolt/RuntimeLibs/InstrumentationRuntimeLibrary.h" +#include "bolt/Utils/CommandLineOpts.h" #include "bolt/Utils/Utils.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/RWMutex.h" @@ -85,6 +86,24 @@ cl::opt InstrumentCalls("instrument-calls", namespace llvm { namespace bolt { +static bool hasAArch64ExclusiveMemop(BinaryFunction &Function) { + // FIXME ARMv8-a architecture reference manual says that software must avoid + // having any explicit memory accesses between exclusive load and associated + // store instruction. So for now skip instrumentation for functions that have + // these instructions, since it might lead to runtime deadlock. + BinaryContext &BC = Function.getBinaryContext(); + for (const BinaryBasicBlock &BB : Function) + for (const MCInst &Inst : BB) + if (BC.MIB->isAArch64Exclusive(Inst)) { + if (opts::Verbosity >= 1) + outs() << "BOLT-INSTRUMENTER: Function " << Function + << " has exclusive instructions, skip instrumentation\n"; + return true; + } + + return false; +} + uint32_t Instrumentation::getFunctionNameIndex(const BinaryFunction &Function) { auto Iter = FuncToStringIdx.find(&Function); if (Iter != FuncToStringIdx.end()) @@ -288,6 +307,9 @@ void Instrumentation::instrumentFunction(BinaryFunction &Function, if (BC.isMachO() && Function.hasName("___GLOBAL_init_65535/1")) return; + if (BC.isAArch64() && hasAArch64ExclusiveMemop(Function)) + return; + SplitWorklistTy SplitWorklist; SplitInstrsTy SplitInstrs; diff --git a/bolt/lib/Passes/ReorderAlgorithm.cpp b/bolt/lib/Passes/ReorderAlgorithm.cpp index b5052cdaddb13..3c3365e1d3d71 100644 --- a/bolt/lib/Passes/ReorderAlgorithm.cpp +++ b/bolt/lib/Passes/ReorderAlgorithm.cpp @@ -531,21 +531,21 @@ void ExtTSPReorderAlgorithm::reorderBasicBlocks(BinaryFunction &BF, } // Initialize CFG edges - using JumpT = std::pair; - std::vector> JumpCounts; + std::vector JumpCounts; for (BinaryBasicBlock *BB : BF.getLayout().blocks()) { auto BI = BB->branch_info_begin(); for (BinaryBasicBlock *SuccBB : BB->successors()) { assert(BI->Count != BinaryBasicBlock::COUNT_NO_PROFILE && "missing profile for a jump"); - auto It = std::make_pair(BB->getLayoutIndex(), SuccBB->getLayoutIndex()); - JumpCounts.push_back(std::make_pair(It, BI->Count)); + JumpCounts.push_back( + {BB->getLayoutIndex(), SuccBB->getLayoutIndex(), BI->Count}); ++BI; } } // Run the layout algorithm - auto Result = applyExtTspLayout(BlockSizes, BlockCounts, JumpCounts); + auto Result = + codelayout::computeExtTspLayout(BlockSizes, BlockCounts, JumpCounts); Order.reserve(BF.getLayout().block_size()); for (uint64_t R : Result) Order.push_back(OrigOrder[R]); diff --git a/bolt/lib/Passes/ReorderFunctions.cpp b/bolt/lib/Passes/ReorderFunctions.cpp index 72d58a4327eb8..d656499cada37 100644 --- a/bolt/lib/Passes/ReorderFunctions.cpp +++ b/bolt/lib/Passes/ReorderFunctions.cpp @@ -331,23 +331,21 @@ void ReorderFunctions::runOnFunctions(BinaryContext &BC) { // Initialize CFG nodes and their data std::vector FuncSizes; std::vector FuncCounts; - using JumpT = std::pair; - std::vector> CallCounts; + std::vector CallCounts; std::vector CallOffsets; for (NodeId F = 0; F < Cg.numNodes(); ++F) { FuncSizes.push_back(Cg.size(F)); FuncCounts.push_back(Cg.samples(F)); for (NodeId Succ : Cg.successors(F)) { const Arc &Arc = *Cg.findArc(F, Succ); - auto It = std::make_pair(F, Succ); - CallCounts.push_back(std::make_pair(It, Arc.weight())); + CallCounts.push_back({F, Succ, uint64_t(Arc.weight())}); CallOffsets.push_back(uint64_t(Arc.avgCallOffset())); } } // Run the layout algorithm. - std::vector Result = - applyCDSLayout(FuncSizes, FuncCounts, CallCounts, CallOffsets); + std::vector Result = codelayout::computeCacheDirectedLayout( + FuncSizes, FuncCounts, CallCounts, CallOffsets); // Create a single cluster from the computed order of hot functions. std::vector NodeOrder(Result.begin(), Result.end()); diff --git a/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp b/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp index 6623f9f8e0a3e..466fedeb01713 100644 --- a/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp +++ b/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp @@ -272,6 +272,34 @@ class AArch64MCPlusBuilder : public MCPlusBuilder { return isLDRB(Inst) || isLDRH(Inst) || isLDRW(Inst) || isLDRX(Inst); } + bool isAArch64Exclusive(const MCInst &Inst) const override { + return (Inst.getOpcode() == AArch64::LDXPX || + Inst.getOpcode() == AArch64::LDXPW || + Inst.getOpcode() == AArch64::LDXRX || + Inst.getOpcode() == AArch64::LDXRW || + Inst.getOpcode() == AArch64::LDXRH || + Inst.getOpcode() == AArch64::LDXRB || + Inst.getOpcode() == AArch64::STXPX || + Inst.getOpcode() == AArch64::STXPW || + Inst.getOpcode() == AArch64::STXRX || + Inst.getOpcode() == AArch64::STXRW || + Inst.getOpcode() == AArch64::STXRH || + Inst.getOpcode() == AArch64::STXRB || + Inst.getOpcode() == AArch64::LDAXPX || + Inst.getOpcode() == AArch64::LDAXPW || + Inst.getOpcode() == AArch64::LDAXRX || + Inst.getOpcode() == AArch64::LDAXRW || + Inst.getOpcode() == AArch64::LDAXRH || + Inst.getOpcode() == AArch64::LDAXRB || + Inst.getOpcode() == AArch64::STLXPX || + Inst.getOpcode() == AArch64::STLXPW || + Inst.getOpcode() == AArch64::STLXRX || + Inst.getOpcode() == AArch64::STLXRW || + Inst.getOpcode() == AArch64::STLXRH || + Inst.getOpcode() == AArch64::STLXRB || + Inst.getOpcode() == AArch64::CLREX); + } + bool isLoadFromStack(const MCInst &Inst) const { if (!mayLoad(Inst)) return false; diff --git a/bolt/test/AArch64/exclusive-instrument.s b/bolt/test/AArch64/exclusive-instrument.s new file mode 100644 index 0000000000000..502dd83b2f2a5 --- /dev/null +++ b/bolt/test/AArch64/exclusive-instrument.s @@ -0,0 +1,39 @@ +// This test checks that the foo function having exclusive memory access +// instructions won't be instrumented. + +// REQUIRES: system-linux,bolt-runtime,target=aarch64{{.*}} + +// RUN: llvm-mc -filetype=obj -triple aarch64-unknown-unknown \ +// RUN: %s -o %t.o +// RUN: %clang %cflags -fPIC -pie %t.o -o %t.exe -nostdlib -Wl,-q -Wl,-fini=dummy +// RUN: llvm-bolt %t.exe -o %t.bolt -instrument -v=1 | FileCheck %s + +// CHECK: Function foo has exclusive instructions, skip instrumentation + +.global foo +.type foo, %function +foo: + ldaxr w9, [x10] + cbnz w9, .Lret + stlxr w12, w11, [x9] + cbz w12, foo + clrex +.Lret: + ret +.size foo, .-foo + +.global _start +.type _start, %function +_start: + cmp x0, #0 + b.eq .Lexit + bl foo +.Lexit: + ret +.size _start, .-_start + +.global dummy +.type dummy, %function +dummy: + ret +.size dummy, .-dummy diff --git a/bolt/test/RISCV/call-annotations.s b/bolt/test/RISCV/call-annotations.s new file mode 100644 index 0000000000000..e2c5a1040faee --- /dev/null +++ b/bolt/test/RISCV/call-annotations.s @@ -0,0 +1,33 @@ +/// Test that annotations are properly carried over to fixed calls. +/// Note that --enable-bat is used to force offsets to be kept. + +// RUN: llvm-mc -triple riscv64 -filetype obj -o %t.o %s +// RUN: ld.lld --emit-relocs -o %t %t.o +// RUN: llvm-bolt --enable-bat --print-cfg --print-fix-riscv-calls \ +// RUN: --print-only=_start -o /dev/null %t | FileCheck %s + + .text + .global f + .p2align 1 +f: + ret + .size f, .-f + +// CHECK-LABEL: Binary Function "_start" after building cfg { +// CHECK: auipc ra, f +// CHECK-NEXT: jalr ra, -4(ra) # Offset: 4 +// CHECK-NEXT: jal ra, f # Offset: 8 +// CHECK-NEXT: jal zero, f # TAILCALL # Offset: 12 + +// CHECK-LABEL: Binary Function "_start" after fix-riscv-calls { +// CHECK: call f # Offset: 0 +// CHECK-NEXT: call f # Offset: 8 +// CHECK-NEXT: tail f # TAILCALL # Offset: 12 + + .globl _start + .p2align 1 +_start: + call f + jal f + jal zero, f + .size _start, .-_start diff --git a/clang-tools-extra/clang-tidy/bugprone/UndefinedMemoryManipulationCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/UndefinedMemoryManipulationCheck.cpp index c2631ce9b4b0a..4f6bc18151789 100644 --- a/clang-tools-extra/clang-tidy/bugprone/UndefinedMemoryManipulationCheck.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/UndefinedMemoryManipulationCheck.cpp @@ -22,9 +22,14 @@ AST_MATCHER(CXXRecordDecl, isNotTriviallyCopyable) { } // namespace void UndefinedMemoryManipulationCheck::registerMatchers(MatchFinder *Finder) { - const auto NotTriviallyCopyableObject = - hasType(ast_matchers::hasCanonicalType( - pointsTo(cxxRecordDecl(isNotTriviallyCopyable())))); + const auto HasNotTriviallyCopyableDecl = + hasDeclaration(cxxRecordDecl(isNotTriviallyCopyable())); + const auto ArrayOfNotTriviallyCopyable = + arrayType(hasElementType(HasNotTriviallyCopyableDecl)); + const auto NotTriviallyCopyableObject = hasType(hasCanonicalType( + anyOf(pointsTo(qualType(anyOf(HasNotTriviallyCopyableDecl, + ArrayOfNotTriviallyCopyable))), + ArrayOfNotTriviallyCopyable))); // Check whether destination object is not TriviallyCopyable. // Applicable to all three memory manipulation functions. diff --git a/clang-tools-extra/clang-tidy/bugprone/UndefinedMemoryManipulationCheck.h b/clang-tools-extra/clang-tidy/bugprone/UndefinedMemoryManipulationCheck.h index 1050d39130610..5e2d7d8ce48ec 100644 --- a/clang-tools-extra/clang-tidy/bugprone/UndefinedMemoryManipulationCheck.h +++ b/clang-tools-extra/clang-tidy/bugprone/UndefinedMemoryManipulationCheck.h @@ -14,7 +14,7 @@ namespace clang::tidy::bugprone { /// Finds calls of memory manipulation functions ``memset()``, ``memcpy()`` and -/// ``memmove()`` on not TriviallyCopyable objects resulting in undefined +/// ``memmove()`` on non-TriviallyCopyable objects resulting in undefined /// behavior. /// /// For the user-facing documentation see: diff --git a/clang-tools-extra/clang-tidy/llvmlibc/ImplementationInNamespaceCheck.cpp b/clang-tools-extra/clang-tidy/llvmlibc/ImplementationInNamespaceCheck.cpp index d05310f09ef77..4489179c44234 100644 --- a/clang-tools-extra/clang-tidy/llvmlibc/ImplementationInNamespaceCheck.cpp +++ b/clang-tools-extra/clang-tidy/llvmlibc/ImplementationInNamespaceCheck.cpp @@ -14,11 +14,16 @@ using namespace clang::ast_matchers; namespace clang::tidy::llvm_libc { -const static StringRef RequiredNamespace = "__llvm_libc"; +const static StringRef RequiredNamespaceStart = "__llvm_libc"; +const static StringRef RequiredNamespaceMacroName = "LIBC_NAMESPACE"; + void ImplementationInNamespaceCheck::registerMatchers(MatchFinder *Finder) { Finder->addMatcher( - decl(hasParent(translationUnitDecl()), unless(linkageSpecDecl())) - .bind("child_of_translation_unit"), + translationUnitDecl( + forEach(decl(isExpansionInMainFile(), unless(linkageSpecDecl()), + // anonymous namespaces generate usingDirective + unless(usingDirectiveDecl(isImplicit()))) + .bind("child_of_translation_unit"))), this); } @@ -26,19 +31,24 @@ void ImplementationInNamespaceCheck::check( const MatchFinder::MatchResult &Result) { const auto *MatchedDecl = Result.Nodes.getNodeAs("child_of_translation_unit"); - if (!Result.SourceManager->isInMainFile(MatchedDecl->getLocation())) + MatchedDecl->dump(); + const auto *NS = dyn_cast(MatchedDecl); + if (NS == nullptr || NS->isAnonymousNamespace()) { + diag(MatchedDecl->getLocation(), + "declaration must be enclosed within the '%0' namespace") + << RequiredNamespaceMacroName; return; - - if (const auto *NS = dyn_cast(MatchedDecl)) { - if (NS->getName() != RequiredNamespace) { - diag(NS->getLocation(), "'%0' needs to be the outermost namespace") - << RequiredNamespace; - } + } + if (Result.SourceManager->isMacroBodyExpansion(NS->getLocation()) == false) { + diag(NS->getLocation(), "the outermost namespace should be the '%0' macro") + << RequiredNamespaceMacroName; + return; + } + if (NS->getName().starts_with(RequiredNamespaceStart) == false) { + diag(NS->getLocation(), "the '%0' macro should start with '%1'") + << RequiredNamespaceMacroName << RequiredNamespaceStart; return; } - diag(MatchedDecl->getLocation(), - "declaration must be declared within the '%0' namespace") - << RequiredNamespace; } } // namespace clang::tidy::llvm_libc diff --git a/clang-tools-extra/clang-tidy/utils/RenamerClangTidyCheck.cpp b/clang-tools-extra/clang-tidy/utils/RenamerClangTidyCheck.cpp index d24b7a65b1c43..da1433aa2d05d 100644 --- a/clang-tools-extra/clang-tidy/utils/RenamerClangTidyCheck.cpp +++ b/clang-tools-extra/clang-tidy/utils/RenamerClangTidyCheck.cpp @@ -256,26 +256,6 @@ class RenamerClangTidyVisitor return true; } - // Fix type aliases in value declarations. - if (const auto *Value = dyn_cast(Decl)) { - if (const Type *TypePtr = Value->getType().getTypePtrOrNull()) { - if (const auto *Typedef = TypePtr->getAs()) - Check->addUsage(Typedef->getDecl(), Value->getSourceRange(), SM); - } - } - - // Fix type aliases in function declarations. - if (const auto *Value = dyn_cast(Decl)) { - if (const auto *Typedef = - Value->getReturnType().getTypePtr()->getAs()) - Check->addUsage(Typedef->getDecl(), Value->getSourceRange(), SM); - for (const ParmVarDecl *Param : Value->parameters()) { - if (const TypedefType *Typedef = - Param->getType().getTypePtr()->getAs()) - Check->addUsage(Typedef->getDecl(), Value->getSourceRange(), SM); - } - } - // Fix overridden methods if (const auto *Method = dyn_cast(Decl)) { if (const CXXMethodDecl *Overridden = getOverrideMethod(Method)) { @@ -340,6 +320,11 @@ class RenamerClangTidyVisitor return true; } + bool VisitTypedefTypeLoc(const TypedefTypeLoc &Loc) { + Check->addUsage(Loc.getTypedefNameDecl(), Loc.getSourceRange(), SM); + return true; + } + bool VisitTagTypeLoc(const TagTypeLoc &Loc) { Check->addUsage(Loc.getDecl(), Loc.getSourceRange(), SM); return true; diff --git a/clang-tools-extra/clangd/IncludeCleaner.cpp b/clang-tools-extra/clangd/IncludeCleaner.cpp index d3ee7591edf09..a299347e8c0c3 100644 --- a/clang-tools-extra/clangd/IncludeCleaner.cpp +++ b/clang-tools-extra/clangd/IncludeCleaner.cpp @@ -390,7 +390,7 @@ IncludeCleanerFindings computeIncludeCleanerFindings(ParsedAST &AST) { const auto &SM = AST.getSourceManager(); include_cleaner::Includes ConvertedIncludes = convertIncludes(AST); const FileEntry *MainFile = SM.getFileEntryForID(SM.getMainFileID()); - auto *PreamblePatch = PreamblePatch::getPatchEntry(AST.tuPath(), SM); + auto PreamblePatch = PreamblePatch::getPatchEntry(AST.tuPath(), SM); std::vector Macros = collectMacroReferences(AST); diff --git a/clang-tools-extra/clangd/Preamble.cpp b/clang-tools-extra/clangd/Preamble.cpp index 60c0f936d6f38..8084e9644b426 100644 --- a/clang-tools-extra/clangd/Preamble.cpp +++ b/clang-tools-extra/clangd/Preamble.cpp @@ -953,12 +953,10 @@ const MainFileMacros &PreamblePatch::mainFileMacros() const { return PatchedMacros; } -const FileEntry *PreamblePatch::getPatchEntry(llvm::StringRef MainFilePath, - const SourceManager &SM) { +OptionalFileEntryRef PreamblePatch::getPatchEntry(llvm::StringRef MainFilePath, + const SourceManager &SM) { auto PatchFilePath = getPatchName(MainFilePath); - if (auto File = SM.getFileManager().getFile(PatchFilePath)) - return *File; - return nullptr; + return SM.getFileManager().getOptionalFileRef(PatchFilePath); } } // namespace clangd } // namespace clang diff --git a/clang-tools-extra/clangd/Preamble.h b/clang-tools-extra/clangd/Preamble.h index 21a281aac0cce..37da3833748a9 100644 --- a/clang-tools-extra/clangd/Preamble.h +++ b/clang-tools-extra/clangd/Preamble.h @@ -180,8 +180,8 @@ class PreamblePatch { const PreambleData &Baseline); /// Returns the FileEntry for the preamble patch of MainFilePath in SM, if /// any. - static const FileEntry *getPatchEntry(llvm::StringRef MainFilePath, - const SourceManager &SM); + static OptionalFileEntryRef getPatchEntry(llvm::StringRef MainFilePath, + const SourceManager &SM); /// Adjusts CI (which compiles the modified inputs) to be used with the /// baseline preamble. This is done by inserting an artificial include to the diff --git a/clang-tools-extra/clangd/SystemIncludeExtractor.cpp b/clang-tools-extra/clangd/SystemIncludeExtractor.cpp index 88df5b04ccb09..74bae786425c8 100644 --- a/clang-tools-extra/clangd/SystemIncludeExtractor.cpp +++ b/clang-tools-extra/clangd/SystemIncludeExtractor.cpp @@ -343,7 +343,13 @@ extractSystemIncludesAndTarget(const DriverArgs &InputArgs, SPAN_ATTACH(Tracer, "driver", Driver); SPAN_ATTACH(Tracer, "lang", InputArgs.Lang); - if (!QueryDriverRegex.match(Driver)) { + // If driver was "../foo" then having to allowlist "/path/a/../foo" rather + // than "/path/foo" is absurd. + // Allow either to match the allowlist, then proceed with "/path/a/../foo". + // This was our historical behavior, and it *could* resolve to something else. + llvm::SmallString<256> NoDots(Driver); + llvm::sys::path::remove_dots(NoDots, /*remove_dot_dot=*/true); + if (!QueryDriverRegex.match(Driver) && !QueryDriverRegex.match(NoDots)) { vlog("System include extraction: not allowed driver {0}", Driver); return std::nullopt; } diff --git a/clang-tools-extra/clangd/index/SymbolCollector.cpp b/clang-tools-extra/clangd/index/SymbolCollector.cpp index 8fe1146bc2752..74aca9b99c8a5 100644 --- a/clang-tools-extra/clangd/index/SymbolCollector.cpp +++ b/clang-tools-extra/clangd/index/SymbolCollector.cpp @@ -363,14 +363,15 @@ class SymbolCollector::HeaderFileURICache { // named `Framework`, e.g. `NSObject.h` in framework `Foundation` would // give if the umbrella header exists, otherwise // . - std::optional getFrameworkHeaderIncludeSpelling( - const FileEntry *FE, llvm::StringRef Framework, HeaderSearch &HS) { - auto Res = CachePathToFrameworkSpelling.try_emplace(FE->getName()); + std::optional + getFrameworkHeaderIncludeSpelling(FileEntryRef FE, llvm::StringRef Framework, + HeaderSearch &HS) { + auto Res = CachePathToFrameworkSpelling.try_emplace(FE.getName()); auto *CachedHeaderSpelling = &Res.first->second; if (!Res.second) return llvm::StringRef(*CachedHeaderSpelling); - auto HeaderPath = splitFrameworkHeaderPath(FE->getName()); + auto HeaderPath = splitFrameworkHeaderPath(FE.getName()); if (!HeaderPath) { // Unexpected: must not be a proper framework header, don't cache the // failure. diff --git a/clang-tools-extra/clangd/unittests/PreambleTests.cpp b/clang-tools-extra/clangd/unittests/PreambleTests.cpp index 6da98c55e3927..3e9ed53043764 100644 --- a/clang-tools-extra/clangd/unittests/PreambleTests.cpp +++ b/clang-tools-extra/clangd/unittests/PreambleTests.cpp @@ -892,9 +892,9 @@ TEST(PreamblePatch, PatchFileEntry) { } { auto AST = createPatchedAST(Code.code(), NewCode.code()); - auto *FE = + auto FE = PreamblePatch::getPatchEntry(AST->tuPath(), AST->getSourceManager()); - ASSERT_NE(FE, nullptr); + ASSERT_NE(FE, std::nullopt); EXPECT_THAT(FE->getName().str(), testing::EndsWith(PreamblePatch::HeaderName.str())); } diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index 6d6f51998a01e..466c9b08c5788 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -202,6 +202,10 @@ Changes in existing checks ` check, so that it does not warn on macros starting with underscore and lowercase letter. +- Improved :doc:`bugprone-undefined-memory-manipulation + ` check to support + fixed-size arrays of non-trivial types. + - Improved :doc:`cppcoreguidelines-avoid-non-const-global-variables ` check to ignore ``static`` variables declared within the scope of @@ -237,6 +241,11 @@ Changes in existing checks ` check to provide fixes for ``inline`` namespaces in the same format as :program:`clang-format`. +- Improved :doc:`llvmlibc-implementation-in-namespace + ` to support + customizable namespace. This further allows for testing the libc when the + system-libc is also LLVM's libc. + - Improved :doc:`misc-include-cleaner ` check by adding option `DeduplicateFindings` to output one finding per symbol occurrence. @@ -291,7 +300,9 @@ Changes in existing checks warnings when a type's forward declaration precedes its definition. Additionally, it now provides appropriate warnings for ``struct`` and ``union`` in C, while also incorporating support for the - ``Leading_upper_snake_case`` naming convention. + ``Leading_upper_snake_case`` naming convention. The handling of ``typedef`` + has been enhanced, particularly within complex types like function pointers + and cases where style checks were omitted when functions started with macros. - Improved :doc:`readability-implicit-bool-conversion ` check to take diff --git a/clang-tools-extra/docs/clang-tidy/checks/bugprone/undefined-memory-manipulation.rst b/clang-tools-extra/docs/clang-tidy/checks/bugprone/undefined-memory-manipulation.rst index 2735184507bbf..bad1f6d0a8615 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/bugprone/undefined-memory-manipulation.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/bugprone/undefined-memory-manipulation.rst @@ -4,4 +4,31 @@ bugprone-undefined-memory-manipulation ====================================== Finds calls of memory manipulation functions ``memset()``, ``memcpy()`` and -``memmove()`` on not TriviallyCopyable objects resulting in undefined behavior. +``memmove()`` on non-TriviallyCopyable objects resulting in undefined behavior. + +Using memory manipulation functions on non-TriviallyCopyable objects can lead +to a range of subtle and challenging issues in C++ code. The most immediate +concern is the potential for undefined behavior, where the state of the object +may become corrupted or invalid. This can manifest as crashes, data corruption, +or unexpected behavior at runtime, making it challenging to identify and +diagnose the root cause. Additionally, misuse of memory manipulation functions +can bypass essential object-specific operations, such as constructors and +destructors, leading to resource leaks or improper initialization. + +For example, when using ``memcpy`` to copy ``std::string``, pointer data is +being copied, and it can result in a double free issue. + +.. code-block:: c++ + + #include + #include + + int main() { + std::string source = "Hello"; + std::string destination; + + std::memcpy(&destination, &source, sizeof(std::string)); + + // Undefined behavior may occur here, during std::string destructor call. + return 0; + } diff --git a/clang-tools-extra/docs/clang-tidy/checks/llvmlibc/implementation-in-namespace.rst b/clang-tools-extra/docs/clang-tidy/checks/llvmlibc/implementation-in-namespace.rst index 33d6dc8ff125c..47ea2b866a934 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/llvmlibc/implementation-in-namespace.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/llvmlibc/implementation-in-namespace.rst @@ -8,21 +8,30 @@ correct namespace. .. code-block:: c++ - // Correct: implementation inside the correct namespace. - namespace __llvm_libc { + // Implementation inside the LIBC_NAMESPACE namespace. + // Correct if: + // - LIBC_NAMESPACE is a macro + // - LIBC_NAMESPACE expansion starts with `__llvm_libc` + namespace LIBC_NAMESPACE { void LLVM_LIBC_ENTRYPOINT(strcpy)(char *dest, const char *src) {} - // Namespaces within __llvm_libc namespace are allowed. - namespace inner{ + // Namespaces within LIBC_NAMESPACE namespace are allowed. + namespace inner { int localVar = 0; } // Functions with C linkage are allowed. - extern "C" void str_fuzz(){} + extern "C" void str_fuzz() {} } - // Incorrect: implementation not in a namespace. + // Incorrect: implementation not in the LIBC_NAMESPACE namespace. void LLVM_LIBC_ENTRYPOINT(strcpy)(char *dest, const char *src) {} - // Incorrect: outer most namespace is not correct. + // Incorrect: outer most namespace is not the LIBC_NAMESPACE macro. namespace something_else { void LLVM_LIBC_ENTRYPOINT(strcpy)(char *dest, const char *src) {} } + + // Incorrect: outer most namespace expansion does not start with `__llvm_libc`. + #define LIBC_NAMESPACE custom_namespace + namespace LIBC_NAMESPACE { + void LLVM_LIBC_ENTRYPOINT(strcpy)(char *dest, const char *src) {} + } diff --git a/clang-tools-extra/include-cleaner/lib/FindHeaders.cpp b/clang-tools-extra/include-cleaner/lib/FindHeaders.cpp index 06e5e1812ba72..fd2de6a17ad4a 100644 --- a/clang-tools-extra/include-cleaner/lib/FindHeaders.cpp +++ b/clang-tools-extra/include-cleaner/lib/FindHeaders.cpp @@ -13,6 +13,7 @@ #include "clang/AST/ASTContext.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclBase.h" +#include "clang/AST/DeclCXX.h" #include "clang/Basic/Builtins.h" #include "clang/Basic/FileEntry.h" #include "clang/Basic/SourceLocation.h" @@ -116,6 +117,8 @@ std::optional headerForAmbiguousStdSymbol(const NamedDecl *ND) { if (!ND->isInStdNamespace()) return {}; + if (auto* USD = llvm::dyn_cast(ND)) + ND = USD->getTargetDecl(); const auto *FD = ND->getAsFunction(); if (!FD) return std::nullopt; diff --git a/clang-tools-extra/include-cleaner/unittests/FindHeadersTest.cpp b/clang-tools-extra/include-cleaner/unittests/FindHeadersTest.cpp index 4cdcde1184a0a..5a2a41b2d99bd 100644 --- a/clang-tools-extra/include-cleaner/unittests/FindHeadersTest.cpp +++ b/clang-tools-extra/include-cleaner/unittests/FindHeadersTest.cpp @@ -11,6 +11,7 @@ #include "clang-include-cleaner/Analysis.h" #include "clang-include-cleaner/Record.h" #include "clang-include-cleaner/Types.h" +#include "clang/AST/Expr.h" #include "clang/AST/RecursiveASTVisitor.h" #include "clang/Basic/FileEntry.h" #include "clang/Basic/FileManager.h" @@ -587,6 +588,36 @@ TEST_F(HeadersForSymbolTest, AmbiguousStdSymbols) { } } +TEST_F(HeadersForSymbolTest, AmbiguousStdSymbolsUsingShadow) { + Inputs.Code = R"cpp( + void remove(char*); + namespace std { using ::remove; } + + void k() { + std::remove("abc"); + } + )cpp"; + buildAST(); + + // Find the DeclRefExpr in the std::remove("abc") function call. + struct Visitor : public RecursiveASTVisitor { + const DeclRefExpr *Out = nullptr; + bool VisitDeclRefExpr(const DeclRefExpr *DRE) { + EXPECT_TRUE(Out == nullptr) << "Found multiple DeclRefExpr!"; + Out = DRE; + return true; + } + }; + Visitor V; + V.TraverseDecl(AST->context().getTranslationUnitDecl()); + ASSERT_TRUE(V.Out) << "Couldn't find a DeclRefExpr!"; + EXPECT_THAT(headersForSymbol(*(V.Out->getFoundDecl()), + AST->sourceManager(), &PI), + UnorderedElementsAre( + Header(*tooling::stdlib::Header::named("")))); +} + + TEST_F(HeadersForSymbolTest, StandardHeaders) { Inputs.Code = "void assert();"; buildAST(); diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/inc-dec-in-conditions-bitint-no-crash.c b/clang-tools-extra/test/clang-tidy/checkers/bugprone/inc-dec-in-conditions-bitint-no-crash.c new file mode 100644 index 0000000000000..cfb64c10fe46c --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/inc-dec-in-conditions-bitint-no-crash.c @@ -0,0 +1,9 @@ +// RUN: %check_clang_tidy %s bugprone-inc-dec-in-conditions %t + +_BitInt(8) v_401_0() { + 0 && ({ + _BitInt(5) y = 0; + 16777215wb ?: ++y; + }); +} +// CHECK-MESSAGES: warning diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/undefined-memory-manipulation.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/undefined-memory-manipulation.cpp index 805501096d8c3..d368c5f067fd7 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/undefined-memory-manipulation.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/undefined-memory-manipulation.cpp @@ -126,6 +126,12 @@ void notTriviallyCopyable() { ::memmove(&p, &vb, sizeof(int)); // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, source object type 'types::VirtualBase' + types::Copy ca[10]; + memset(ca, 0, sizeof(ca)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, destination object type 'types::Copy[10]' + memset(&ca, 0, sizeof(ca)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, destination object type 'types::Copy[10]' + #define MEMSET memset(&vf, 0, sizeof(int)); MEMSET // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, destination object type 'types::VirtualFunc' @@ -159,6 +165,17 @@ void notTriviallyCopyable() { // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, destination object type 'aliases::Copy2' memset(pc3, 0, sizeof(int)); // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, destination object type 'Copy3' + using Copy3Arr = Copy3[5]; + Copy3Arr c3a; + memset(c3a, 0, sizeof(c3a)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, destination object type 'Copy3Arr' + memset(&c3a, 0, sizeof(c3a)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, destination object type 'Copy3Arr' + + typedef Copy3 Copy3Arr2[5]; + Copy3Arr2 c3a2; + memset(c3a2, 0, sizeof(c3a2)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, destination object type 'Copy3Arr2' } void triviallyCopyable() { diff --git a/clang-tools-extra/test/clang-tidy/checkers/llvmlibc/implementation-in-namespace.cpp b/clang-tools-extra/test/clang-tidy/checkers/llvmlibc/implementation-in-namespace.cpp index e75556a623b65..2246a430dd1f5 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/llvmlibc/implementation-in-namespace.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/llvmlibc/implementation-in-namespace.cpp @@ -3,19 +3,30 @@ #define MACRO_A "defining macros outside namespace is valid" class ClassB; -// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: declaration must be declared within the '__llvm_libc' namespace +// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: declaration must be enclosed within the 'LIBC_NAMESPACE' namespace struct StructC {}; -// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: declaration must be declared within the '__llvm_libc' namespace +// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: declaration must be enclosed within the 'LIBC_NAMESPACE' namespace char *VarD = MACRO_A; -// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: declaration must be declared within the '__llvm_libc' namespace +// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: declaration must be enclosed within the 'LIBC_NAMESPACE' namespace typedef int typeE; -// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: declaration must be declared within the '__llvm_libc' namespace +// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: declaration must be enclosed within the 'LIBC_NAMESPACE' namespace void funcF() {} -// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: declaration must be declared within the '__llvm_libc' namespace +// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: declaration must be enclosed within the 'LIBC_NAMESPACE' namespace + +namespace outer_most { +// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: the outermost namespace should be the 'LIBC_NAMESPACE' macro + class A {}; +} + +// Wrapped in anonymous namespace. +namespace { +// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: declaration must be enclosed within the 'LIBC_NAMESPACE' namespace + class A {}; +} namespace namespaceG { -// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: '__llvm_libc' needs to be the outermost namespace -namespace __llvm_libc{ +// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: the outermost namespace should be the 'LIBC_NAMESPACE' macro +namespace __llvm_libc { namespace namespaceH { class ClassB; } // namespace namespaceH @@ -26,9 +37,20 @@ typedef int typeE; void funcF() {} } // namespace namespaceG -// Wrapped in correct namespace. -namespace __llvm_libc { -// Namespaces within __llvim_libc namespace allowed. +// Wrapped in macro namespace but with an incorrect name +#define LIBC_NAMESPACE custom_namespace +namespace LIBC_NAMESPACE { +// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: the 'LIBC_NAMESPACE' macro should start with '__llvm_libc' +namespace namespaceH { +class ClassB; +} // namespace namespaceH +} // namespace LIBC_NAMESPACE + + +// Wrapped in macro namespace with a valid name, LIBC_NAMESPACE starts with '__llvm_libc' +#undef LIBC_NAMESPACE +#define LIBC_NAMESPACE __llvm_libc_xyz +namespace LIBC_NAMESPACE { namespace namespaceI { class ClassB; } // namespace namespaceI @@ -37,4 +59,4 @@ char *VarD = MACRO_A; typedef int typeE; void funcF() {} extern "C" void extern_funcJ() {} -} // namespace __llvm_libc +} // namespace LIBC_NAMESPACE diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/identifier-naming.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/identifier-naming.cpp index 3445a21bfdbc4..84bf7764583e8 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/readability/identifier-naming.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/readability/identifier-naming.cpp @@ -729,3 +729,29 @@ struct forward_declared_as_struct; class forward_declared_as_struct { }; +namespace pr55156 { + +template struct Wrap; + +typedef enum { + VALUE0, + VALUE1, +} ValueType; +// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: invalid case style for typedef 'ValueType' [readability-identifier-naming] +// CHECK-FIXES: {{^}}} value_type_t; + +typedef ValueType (*MyFunPtr)(const ValueType&, Wrap*); +// CHECK-MESSAGES: :[[@LINE-1]]:21: warning: invalid case style for typedef 'MyFunPtr' [readability-identifier-naming] +// CHECK-FIXES: {{^}}typedef value_type_t (*my_fun_ptr_t)(const value_type_t&, Wrap*); + +#define STATIC_MACRO static +STATIC_MACRO void someFunc(ValueType a_v1, const ValueType& a_v2) {} +// CHECK-FIXES: {{^}}STATIC_MACRO void someFunc(value_type_t a_v1, const value_type_t& a_v2) {} +STATIC_MACRO void someFunc(const ValueType** p_a_v1, ValueType (*p_a_v2)()) {} +// CHECK-FIXES: {{^}}STATIC_MACRO void someFunc(const value_type_t** p_a_v1, value_type_t (*p_a_v2)()) {} +STATIC_MACRO ValueType someFunc() {} +// CHECK-FIXES: {{^}}STATIC_MACRO value_type_t someFunc() {} +STATIC_MACRO void someFunc(MyFunPtr, const MyFunPtr****) {} +// CHECK-FIXES: {{^}}STATIC_MACRO void someFunc(my_fun_ptr_t, const my_fun_ptr_t****) {} +#undef STATIC_MACRO +} diff --git a/clang/cmake/caches/CrossWinToARMLinux.cmake b/clang/cmake/caches/CrossWinToARMLinux.cmake index e6f5650eac466..bbb4b9e71be2d 100644 --- a/clang/cmake/caches/CrossWinToARMLinux.cmake +++ b/clang/cmake/caches/CrossWinToARMLinux.cmake @@ -95,7 +95,6 @@ set(CLANG_DEFAULT_LINKER "lld" CACHE STRING "") if(WIN32) set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded" CACHE STRING "") - set(LLVM_USE_CRT_RELEASE "MT" CACHE STRING "") endif() # Set up RPATH for the target runtime/builtin libraries. diff --git a/clang/cmake/caches/Fuchsia-stage2.cmake b/clang/cmake/caches/Fuchsia-stage2.cmake index bc4d9f1462b18..0dcc35ee1495f 100644 --- a/clang/cmake/caches/Fuchsia-stage2.cmake +++ b/clang/cmake/caches/Fuchsia-stage2.cmake @@ -30,7 +30,6 @@ set(LLDB_ENABLE_CURSES OFF CACHE BOOL "") set(LLDB_ENABLE_LIBEDIT OFF CACHE BOOL "") if(WIN32) - set(LLVM_USE_CRT_RELEASE "MT" CACHE STRING "") set(FUCHSIA_DISABLE_DRIVER_BUILD ON) endif() @@ -51,6 +50,12 @@ set(CLANG_PLUGIN_SUPPORT OFF CACHE BOOL "") set(ENABLE_LINKER_BUILD_ID ON CACHE BOOL "") set(ENABLE_X86_RELAX_RELOCATIONS ON CACHE BOOL "") +# TODO(#67176): relative-vtables doesn't play well with different default +# visibilities. Making everything hidden visibility causes other complications +# let's choose default visibility for our entire toolchain. +set(CMAKE_C_VISIBILITY_PRESET default CACHE STRING "") +set(CMAKE_CXX_VISIBILITY_PRESET default CACHE STRING "") + set(CMAKE_BUILD_TYPE Release CACHE STRING "") if (APPLE) set(CMAKE_OSX_DEPLOYMENT_TARGET "10.13" CACHE STRING "") diff --git a/clang/cmake/caches/Fuchsia.cmake b/clang/cmake/caches/Fuchsia.cmake index c599f141f9e5b..9c68be0bfe54b 100644 --- a/clang/cmake/caches/Fuchsia.cmake +++ b/clang/cmake/caches/Fuchsia.cmake @@ -70,10 +70,6 @@ foreach(variable ${_FUCHSIA_BOOTSTRAP_PASSTHROUGH}) endif() endforeach() -if(WIN32) - set(LLVM_USE_CRT_RELEASE "MT" CACHE STRING "") -endif() - set(CLANG_DEFAULT_CXX_STDLIB libc++ CACHE STRING "") set(CLANG_DEFAULT_LINKER lld CACHE STRING "") set(CLANG_DEFAULT_OBJCOPY llvm-objcopy CACHE STRING "") diff --git a/clang/docs/OpenMPSupport.rst b/clang/docs/OpenMPSupport.rst index 641ec78a56da8..e5d95ee76f191 100644 --- a/clang/docs/OpenMPSupport.rst +++ b/clang/docs/OpenMPSupport.rst @@ -16,15 +16,12 @@ OpenMP Support ============== -Clang fully supports OpenMP 4.5. Clang supports offloading to X86_64, AArch64, -PPC64[LE] and has `basic support for Cuda devices`_. - -* #pragma omp declare simd: :part:`Partial`. We support parsing/semantic - analysis + generation of special attributes for X86 target, but still - missing the LLVM pass for vectorization. +Clang fully supports OpenMP 4.5, almost all of 5.0 and most of 5.1/2. +Clang supports offloading to X86_64, AArch64, PPC64[LE], NVIDIA GPUs (all models) and AMD GPUs (all models). In addition, the LLVM OpenMP runtime `libomp` supports the OpenMP Tools Interface (OMPT) on x86, x86_64, AArch64, and PPC64 on Linux, Windows, and macOS. +OMPT is also supported for NVIDIA and AMD GPUs. For the list of supported features from OpenMP 5.0 and 5.1 see `OpenMP implementation details`_ and `OpenMP 51 implementation details`_. @@ -36,17 +33,6 @@ General improvements collapse clause by replacing the expensive remainder operation with multiplications and additions. -- The default schedules for the `distribute` and `for` constructs in a - parallel region and in SPMD mode have changed to ensure coalesced - accesses. For the `distribute` construct, a static schedule is used - with a chunk size equal to the number of threads per team (default - value of threads or as specified by the `thread_limit` clause if - present). For the `for` construct, the schedule is static with chunk - size of one. - -- Simplified SPMD code generation for `distribute parallel for` when - the new default schedules are applicable. - - When using the collapse clause on a loop nest the default behavior is to automatically extend the representation of the loop counter to 64 bits for the cases where the sizes of the collapsed loops are not @@ -54,24 +40,9 @@ General improvements at most 32 bits, compile your program with the `-fopenmp-optimistic-collapse`. -.. _basic support for Cuda devices: - -Cuda devices support -==================== -Directives execution modes --------------------------- - -Clang code generation for target regions supports two modes: the SPMD and -non-SPMD modes. Clang chooses one of these two modes automatically based on the -way directives and clauses on those directives are used. The SPMD mode uses a -simplified set of runtime functions thus increasing performance at the cost of -supporting some OpenMP features. The non-SPMD mode is the most generic mode and -supports all currently available OpenMP features. The compiler will always -attempt to use the SPMD mode wherever possible. SPMD mode will not be used if: - - - The target region contains user code (other than OpenMP-specific - directives) in between the `target` and the `parallel` directives. +GPU devices support +=================== Data-sharing modes ------------------ @@ -82,8 +53,9 @@ performance and can be activated using the `-fopenmp-cuda-mode` flag. In `Generic` mode all local variables that can be shared in the parallel regions are stored in the global memory. In `Cuda` mode local variables are not shared between the threads and it is user responsibility to share the required data -between the threads in the parallel regions. - +between the threads in the parallel regions. Often, the optimizer is able to +reduce the cost of `Generic` mode to the level of `Cuda` mode, but the flag, +as well as other assumption flags, can be used for tuning. Features not supported or with limited support for Cuda devices --------------------------------------------------------------- @@ -96,9 +68,6 @@ Features not supported or with limited support for Cuda devices - Nested parallelism: inner parallel regions are executed sequentially. -- Automatic translation of math functions in target regions to device-specific - math functions is not implemented yet. - - Debug information for OpenMP target regions is supported, but sometimes it may be required to manually specify the address class of the inspected variables. In some cases the local variables are actually allocated in the global memory, @@ -139,7 +108,7 @@ implementation. +------------------------------+--------------------------------------------------------------+--------------------------+-----------------------------------------------------------------------+ | memory management | allocate directive and allocate clause | :good:`done` | r355614,r335952 | +------------------------------+--------------------------------------------------------------+--------------------------+-----------------------------------------------------------------------+ -| OMPD | OMPD interfaces | :part:`done` | https://reviews.llvm.org/D99914 (Supports only HOST(CPU) and Linux | +| OMPD | OMPD interfaces | :good:`done` | https://reviews.llvm.org/D99914 (Supports only HOST(CPU) and Linux | +------------------------------+--------------------------------------------------------------+--------------------------+-----------------------------------------------------------------------+ | OMPT | OMPT interfaces (callback support) | :good:`done` | | +------------------------------+--------------------------------------------------------------+--------------------------+-----------------------------------------------------------------------+ @@ -171,7 +140,7 @@ implementation. +------------------------------+--------------------------------------------------------------+--------------------------+-----------------------------------------------------------------------+ | device | infer target functions from initializers | :part:`worked on` | | +------------------------------+--------------------------------------------------------------+--------------------------+-----------------------------------------------------------------------+ -| device | infer target variables from initializers | :part:`done` | D146418 | +| device | infer target variables from initializers | :good:`done` | D146418 | +------------------------------+--------------------------------------------------------------+--------------------------+-----------------------------------------------------------------------+ | device | OMP_TARGET_OFFLOAD environment variable | :good:`done` | D50522 | +------------------------------+--------------------------------------------------------------+--------------------------+-----------------------------------------------------------------------+ @@ -217,7 +186,7 @@ implementation. +------------------------------+--------------------------------------------------------------+--------------------------+-----------------------------------------------------------------------+ | device | support close modifier on map clause | :good:`done` | D55719,D55892 | +------------------------------+--------------------------------------------------------------+--------------------------+-----------------------------------------------------------------------+ -| device | teams construct on the host device | :part:`done` | r371553 | +| device | teams construct on the host device | :good:`done` | r371553 | +------------------------------+--------------------------------------------------------------+--------------------------+-----------------------------------------------------------------------+ | device | support non-contiguous array sections for target update | :good:`done` | | +------------------------------+--------------------------------------------------------------+--------------------------+-----------------------------------------------------------------------+ @@ -235,7 +204,7 @@ implementation. +------------------------------+--------------------------------------------------------------+--------------------------+-----------------------------------------------------------------------+ | misc | library shutdown (omp_pause_resource[_all]) | :good:`done` | D55078 | +------------------------------+--------------------------------------------------------------+--------------------------+-----------------------------------------------------------------------+ -| misc | metadirectives | :part:`worked on` | D91944 | +| misc | metadirectives | :part:`mostly done` | D91944 | +------------------------------+--------------------------------------------------------------+--------------------------+-----------------------------------------------------------------------+ | misc | conditional modifier for lastprivate clause | :good:`done` | | +------------------------------+--------------------------------------------------------------+--------------------------+-----------------------------------------------------------------------+ @@ -243,7 +212,7 @@ implementation. +------------------------------+--------------------------------------------------------------+--------------------------+-----------------------------------------------------------------------+ | misc | depobj directive and depobj dependency kind | :good:`done` | | +------------------------------+--------------------------------------------------------------+--------------------------+-----------------------------------------------------------------------+ -| misc | user-defined function variants | :part:`worked on` | D67294, D64095, D71847, D71830, D109635 | +| misc | user-defined function variants | :good:`done`. | D67294, D64095, D71847, D71830, D109635 | +------------------------------+--------------------------------------------------------------+--------------------------+-----------------------------------------------------------------------+ | misc | pointer/reference to pointer based array reductions | :good:`done` | | +------------------------------+--------------------------------------------------------------+--------------------------+-----------------------------------------------------------------------+ @@ -298,7 +267,7 @@ implementation. +------------------------------+--------------------------------------------------------------+--------------------------+-----------------------------------------------------------------------+ | device | indirect clause on declare target directive | :none:`unclaimed` | | +------------------------------+--------------------------------------------------------------+--------------------------+-----------------------------------------------------------------------+ -| device | allow virtual functions calls for mapped object on device | :none:`unclaimed` | | +| device | allow virtual functions calls for mapped object on device | :part:`partial` | | +------------------------------+--------------------------------------------------------------+--------------------------+-----------------------------------------------------------------------+ | device | interop construct | :part:`partial` | parsing/sema done: D98558, D98834, D98815 | +------------------------------+--------------------------------------------------------------+--------------------------+-----------------------------------------------------------------------+ diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index ba91f9481fe98..477a40630f110 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -46,6 +46,26 @@ C/C++ Language Potentially Breaking Changes C++ Specific Potentially Breaking Changes ----------------------------------------- +- The name mangling rules for function templates has been changed to take into + account the possibility that functions could be overloaded on their template + parameter lists or requires-clauses. This causes mangled names to change for + function templates in the following cases: + + - When the function has any constraints, whether from constrained template + parameters or requires-clauses. + - When the template parameter list includes a deduced type -- either + ``auto``, ``decltype(auto)``, or a deduced class template specialization + type. + - When a template template parameter is given a template template argument + that has a different template parameter list. + + This fixes a number of issues where valid programs would be rejected due to + mangling collisions, or would in some cases be silently miscompiled. Clang + will use the old manglings if ``-fclang-abi-compat=17`` or lower is + specified. + (`#48216 `_), + (`#49884 `_), and + (`#61273 `_) ABI Changes in This Version --------------------------- @@ -224,8 +244,12 @@ Bug Fixes in This Version (`#65156 `_) - Clang no longer considers the loss of ``__unaligned`` qualifier from objects as an invalid conversion during method function overload resolution. +- Fix lack of comparison of declRefExpr in ASTStructuralEquivalence + (`#66047 `_) - Fix parser crash when dealing with ill-formed objective C++ header code. Fixes (`#64836 `_) +- Clang now allows an ``_Atomic`` qualified integer in a switch statement. Fixes + (`#65557 `_) Bug Fixes to Compiler Builtins ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -287,6 +311,9 @@ Bug Fixes to C++ Support a non-template inner-class between the function and the class template. (`#65810 `_) +- Fix a crash when calling a non-constant immediate function + in the initializer of a static data member. + (`#65985 _`). - Clang now properly converts static lambda call operator to function pointers on win32. (`#62594 `_) @@ -308,6 +335,10 @@ Bug Fixes to C++ Support - Clang now no longer asserts when an UnresolvedLookupExpr is used as an expression requirement. (`#66612 https://github.com/llvm/llvm-project/issues/66612`) +- Clang now disambiguates NTTP types when printing diagnostics where the + NTTP types are compared with the 'diff' method. + (`#66744 https://github.com/llvm/llvm-project/issues/66744`) + Bug Fixes to AST Handling ^^^^^^^^^^^^^^^^^^^^^^^^^ - Fixed an import failure of recursive friend class template. diff --git a/clang/include/clang/AST/ExprConcepts.h b/clang/include/clang/AST/ExprConcepts.h index 6f38b2c4b0571..4f34799bad050 100644 --- a/clang/include/clang/AST/ExprConcepts.h +++ b/clang/include/clang/AST/ExprConcepts.h @@ -511,6 +511,8 @@ class RequiresExpr final : public Expr, unsigned NumLocalParameters; unsigned NumRequirements; RequiresExprBodyDecl *Body; + SourceLocation LParenLoc; + SourceLocation RParenLoc; SourceLocation RBraceLoc; unsigned numTrailingObjects(OverloadToken) const { @@ -522,19 +524,22 @@ class RequiresExpr final : public Expr, } RequiresExpr(ASTContext &C, SourceLocation RequiresKWLoc, - RequiresExprBodyDecl *Body, + RequiresExprBodyDecl *Body, SourceLocation LParenLoc, ArrayRef LocalParameters, + SourceLocation RParenLoc, ArrayRef Requirements, SourceLocation RBraceLoc); RequiresExpr(ASTContext &C, EmptyShell Empty, unsigned NumLocalParameters, unsigned NumRequirements); public: - static RequiresExpr * - Create(ASTContext &C, SourceLocation RequiresKWLoc, - RequiresExprBodyDecl *Body, ArrayRef LocalParameters, - ArrayRef Requirements, - SourceLocation RBraceLoc); + static RequiresExpr *Create(ASTContext &C, SourceLocation RequiresKWLoc, + RequiresExprBodyDecl *Body, + SourceLocation LParenLoc, + ArrayRef LocalParameters, + SourceLocation RParenLoc, + ArrayRef Requirements, + SourceLocation RBraceLoc); static RequiresExpr * Create(ASTContext &C, EmptyShell Empty, unsigned NumLocalParameters, unsigned NumRequirements); @@ -567,6 +572,8 @@ class RequiresExpr final : public Expr, return RequiresExprBits.RequiresKWLoc; } + SourceLocation getLParenLoc() const { return LParenLoc; } + SourceLocation getRParenLoc() const { return RParenLoc; } SourceLocation getRBraceLoc() const { return RBraceLoc; } static bool classof(const Stmt *T) { diff --git a/clang/include/clang/AST/PrettyPrinter.h b/clang/include/clang/AST/PrettyPrinter.h index cee3cce7729c3..17d4e1a326d31 100644 --- a/clang/include/clang/AST/PrettyPrinter.h +++ b/clang/include/clang/AST/PrettyPrinter.h @@ -76,8 +76,8 @@ struct PrintingPolicy { SuppressImplicitBase(false), FullyQualifiedName(false), PrintCanonicalTypes(false), PrintInjectedClassNameWithArguments(true), UsePreferredNames(true), AlwaysIncludeTypeForTemplateArgument(false), - CleanUglifiedParameters(false), EntireContentsOfLargeArray(true), - UseEnumerators(true) {} + UseClassForTemplateArgument(false), CleanUglifiedParameters(false), + EntireContentsOfLargeArray(true), UseEnumerators(true) {} /// Adjust this printing policy for cases where it's known that we're /// printing C++ code (for instance, if AST dumping reaches a C++-only @@ -291,6 +291,10 @@ struct PrintingPolicy { /// parameters. unsigned AlwaysIncludeTypeForTemplateArgument : 1; + // Prints "class" keyword before type template arguments. This is used when + // printing a function via the _FUNCTION__ or __func__ macro in MSVC mode. + unsigned UseClassForTemplateArgument : 1; + /// Whether to strip underscores when printing reserved parameter names. /// e.g. std::vector becomes std::vector. /// This only affects parameter names, and so describes a compatible API. diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h index 46dbadd8b878b..4799f89db82fa 100644 --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -6642,7 +6642,7 @@ class BitIntType final : public Type, public llvm::FoldingSetNode { bool isSugared() const { return false; } QualType desugar() const { return QualType(this, 0); } - void Profile(llvm::FoldingSetNodeID &ID) { + void Profile(llvm::FoldingSetNodeID &ID) const { Profile(ID, isUnsigned(), getNumBits()); } diff --git a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h index c865b2e8bdb37..8a2d56668e32f 100644 --- a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h +++ b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h @@ -31,7 +31,14 @@ class VariableGroupsManager { /// together in one step. /// /// `Var` must be a variable that needs fix (so it must be in a group). - virtual VarGrpRef getGroupOfVar(const VarDecl *Var) const =0; + /// `HasParm` is an optional argument that will be set to true if the set of + /// variables, where `Var` is in, contains parameters. + virtual VarGrpRef getGroupOfVar(const VarDecl *Var, + bool *HasParm = nullptr) const =0; + + /// Returns the non-empty group of variables that include parameters of the + /// analyzing function, if such a group exists. An empty group, otherwise. + virtual VarGrpRef getGroupOfParms() const =0; }; /// The interface that lets the caller handle unsafe buffer usage analysis @@ -62,11 +69,14 @@ class UnsafeBufferUsageHandler { bool IsRelatedToDecl) = 0; /// Invoked when a fix is suggested against a variable. This function groups - /// all variables that must be fixed together (i.e their types must be changed to the - /// same target type to prevent type mismatches) into a single fixit. + /// all variables that must be fixed together (i.e their types must be changed + /// to the same target type to prevent type mismatches) into a single fixit. + /// + /// `D` is the declaration of the callable under analysis that owns `Variable` + /// and all of its group mates. virtual void handleUnsafeVariableGroup(const VarDecl *Variable, const VariableGroupsManager &VarGrpMgr, - FixItList &&Fixes) = 0; + FixItList &&Fixes, const Decl *D) = 0; #ifndef NDEBUG public: diff --git a/clang/include/clang/Analysis/FlowSensitive/Arena.h b/clang/include/clang/Analysis/FlowSensitive/Arena.h index 373697dc7379c..4e07053aae1af 100644 --- a/clang/include/clang/Analysis/FlowSensitive/Arena.h +++ b/clang/include/clang/Analysis/FlowSensitive/Arena.h @@ -11,6 +11,7 @@ #include "clang/Analysis/FlowSensitive/Formula.h" #include "clang/Analysis/FlowSensitive/StorageLocation.h" #include "clang/Analysis/FlowSensitive/Value.h" +#include "llvm/ADT/StringRef.h" #include namespace clang::dataflow { @@ -109,6 +110,10 @@ class Arena { return makeAtomRef(Value ? True : False); } + // Parses a formula from its textual representation. + // This may refer to atoms that were not produced by makeAtom() yet! + llvm::Expected parseFormula(llvm::StringRef); + /// Returns a new atomic boolean variable, distinct from any other. Atom makeAtom() { return static_cast(NextAtom++); }; diff --git a/clang/include/clang/Analysis/FlowSensitive/Formula.h b/clang/include/clang/Analysis/FlowSensitive/Formula.h index 64fe8f5b630a0..51264444fda84 100644 --- a/clang/include/clang/Analysis/FlowSensitive/Formula.h +++ b/clang/include/clang/Analysis/FlowSensitive/Formula.h @@ -18,7 +18,6 @@ #include "llvm/Support/raw_ostream.h" #include #include -#include namespace clang::dataflow { diff --git a/clang/include/clang/Analysis/FlowSensitive/Logger.h b/clang/include/clang/Analysis/FlowSensitive/Logger.h index 6836488003a97..f4bd39f6ed49e 100644 --- a/clang/include/clang/Analysis/FlowSensitive/Logger.h +++ b/clang/include/clang/Analysis/FlowSensitive/Logger.h @@ -50,7 +50,9 @@ class Logger { /// Called when we start (re-)processing a block in the CFG. /// The target program point is the entry to the specified block. /// Calls to log() describe transferBranch(), join() etc. - virtual void enterBlock(const CFGBlock &) {} + /// `PostVisit` specifies whether we're processing the block for the + /// post-visit callback. + virtual void enterBlock(const CFGBlock &, bool PostVisit) {} /// Called when we start processing an element in the current CFG block. /// The target program point is after the specified element. /// Calls to log() describe the transfer() function. diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index c3fe7cea29afb..b13baa46754cf 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -1163,7 +1163,7 @@ caveats to this use of name mangling: * The ``overloadable`` attribute has almost no meaning when used in C++, because names will already be mangled and functions are already overloadable. However, when an ``overloadable`` function occurs within an ``extern "C"`` - linkage specification, it's name *will* be mangled in the same way as it + linkage specification, its name *will* be mangled in the same way as it would in C. For the purpose of backwards compatibility, at most one function with the same diff --git a/clang/include/clang/Basic/BuiltinsAArch64.def b/clang/include/clang/Basic/BuiltinsAArch64.def index eaae6c9ad8468..12c7a371e0fbd 100644 --- a/clang/include/clang/Basic/BuiltinsAArch64.def +++ b/clang/include/clang/Basic/BuiltinsAArch64.def @@ -259,7 +259,6 @@ TARGET_HEADER_BUILTIN(__umulh, "ULLiULLiULLi", "nh", INTRIN_H, ALL_MS_LANGUAGES, TARGET_HEADER_BUILTIN(__break, "vi", "nh", INTRIN_H, ALL_MS_LANGUAGES, "") - TARGET_HEADER_BUILTIN(__writex18byte, "vUNiUc", "nh", INTRIN_H, ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(__writex18word, "vUNiUs", "nh", INTRIN_H, ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(__writex18dword, "vUNiUNi", "nh", INTRIN_H, ALL_MS_LANGUAGES, "") @@ -270,6 +269,20 @@ TARGET_HEADER_BUILTIN(__readx18word, "UsUNi", "nh", INTRIN_H, ALL_MS_LANGUAGES, TARGET_HEADER_BUILTIN(__readx18dword, "UNiUNi", "nh", INTRIN_H, ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(__readx18qword, "ULLiUNi", "nh", INTRIN_H, ALL_MS_LANGUAGES, "") +TARGET_HEADER_BUILTIN(_CopyDoubleFromInt64, "dSLLi", "nh", INTRIN_H, ALL_MS_LANGUAGES, "") +TARGET_HEADER_BUILTIN(_CopyFloatFromInt32, "fSi", "nh", INTRIN_H, ALL_MS_LANGUAGES, "") +TARGET_HEADER_BUILTIN(_CopyInt32FromFloat, "Sif", "nh", INTRIN_H, ALL_MS_LANGUAGES, "") +TARGET_HEADER_BUILTIN(_CopyInt64FromDouble, "SLLid", "nh", INTRIN_H, ALL_MS_LANGUAGES, "") + +TARGET_HEADER_BUILTIN(_CountLeadingOnes, "UiUNi", "nh", INTRIN_H, ALL_MS_LANGUAGES, "") +TARGET_HEADER_BUILTIN(_CountLeadingOnes64, "UiULLi", "nh", INTRIN_H, ALL_MS_LANGUAGES, "") +TARGET_HEADER_BUILTIN(_CountLeadingSigns, "UiSNi", "nh", INTRIN_H, ALL_MS_LANGUAGES, "") +TARGET_HEADER_BUILTIN(_CountLeadingSigns64, "UiSLLi", "nh", INTRIN_H, ALL_MS_LANGUAGES, "") +TARGET_HEADER_BUILTIN(_CountLeadingZeros, "UiUNi", "nh", INTRIN_H, ALL_MS_LANGUAGES, "") +TARGET_HEADER_BUILTIN(_CountLeadingZeros64, "UiULLi", "nh", INTRIN_H, ALL_MS_LANGUAGES, "") +TARGET_HEADER_BUILTIN(_CountOneBits, "UiUNi", "nh", INTRIN_H, ALL_MS_LANGUAGES, "") +TARGET_HEADER_BUILTIN(_CountOneBits64, "UiULLi", "nh", INTRIN_H, ALL_MS_LANGUAGES, "") + #undef BUILTIN #undef LANGBUILTIN #undef TARGET_BUILTIN diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 0ac4df8edb242..9613cca63193f 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -11916,6 +11916,9 @@ def note_unsafe_buffer_operation : Note< "used%select{| in pointer arithmetic| in buffer access}0 here">; def note_unsafe_buffer_variable_fixit_group : Note< "change type of %0 to '%select{std::span|std::array|std::span::iterator}1' to preserve bounds information%select{|, and change %2 to '%select{std::span|std::array|std::span::iterator}1' to propagate bounds information between them}3">; +def note_unsafe_buffer_variable_fixit_together : Note< + "change type of %0 to '%select{std::span|std::array|std::span::iterator}1' to preserve bounds information" + "%select{|, and change %2 to safe types to make function %4 bounds-safe}3">; def note_safe_buffer_usage_suggestions_disabled : Note< "pass -fsafe-buffer-usage-suggestions to receive code hardening suggestions">; #ifndef NDEBUG diff --git a/clang/include/clang/Basic/LangOptions.h b/clang/include/clang/Basic/LangOptions.h index fb073bf16bbf2..e0e95f6d26f45 100644 --- a/clang/include/clang/Basic/LangOptions.h +++ b/clang/include/clang/Basic/LangOptions.h @@ -231,6 +231,15 @@ class LangOptions : public LangOptionsBase { /// - consider classes with defaulted special member functions non-pod. Ver15, + /// Attempt to be ABI-compatible with code generated by Clang 17.0.x. + /// This causes clang to revert some fixes to its implementation of the + /// Itanium name mangling scheme, with the consequence that overloaded + /// function templates are mangled the same if they differ only by: + /// - constraints + /// - whether a non-type template parameter has a deduced type + /// - the parameter list of a template template parameter + Ver17, + /// Conform to the underlying platform's C and C++ ABIs as closely /// as we can. Latest diff --git a/clang/include/clang/Basic/TargetOptions.h b/clang/include/clang/Basic/TargetOptions.h index b192c856384b9..0dc324ff9ed43 100644 --- a/clang/include/clang/Basic/TargetOptions.h +++ b/clang/include/clang/Basic/TargetOptions.h @@ -82,7 +82,7 @@ class TargetOptions { /// code object version times 100. enum CodeObjectVersionKind { COV_None, - COV_2 = 200, + COV_2 = 200, // Unsupported. COV_3 = 300, COV_4 = 400, COV_5 = 500, diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index 553c7928c4f94..0f93479170d73 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -4624,9 +4624,9 @@ defm amdgpu_ieee : BoolOption<"m", "amdgpu-ieee", def mcode_object_version_EQ : Joined<["-"], "mcode-object-version=">, Group, HelpText<"Specify code object ABI version. Defaults to 4. (AMDGPU only)">, Visibility<[ClangOption, CC1Option]>, - Values<"none,2,3,4,5">, + Values<"none,3,4,5">, NormalizedValuesScope<"TargetOptions">, - NormalizedValues<["COV_None", "COV_2", "COV_3", "COV_4", "COV_5"]>, + NormalizedValues<["COV_None", "COV_3", "COV_4", "COV_5"]>, MarshallingInfoEnum, "COV_4">; defm cumode : SimpleMFlag<"cumode", diff --git a/clang/include/clang/Lex/Preprocessor.h b/clang/include/clang/Lex/Preprocessor.h index bc1d94a61508d..575d08b83fd3a 100644 --- a/clang/include/clang/Lex/Preprocessor.h +++ b/clang/include/clang/Lex/Preprocessor.h @@ -277,6 +277,9 @@ class Preprocessor { /// Empty line handler. EmptylineHandler *Emptyline = nullptr; + /// True to avoid tearing down the lexer etc on EOF + bool IncrementalProcessing = false; + public: /// The kind of translation unit we are processing. const TranslationUnitKind TUKind; @@ -1910,14 +1913,11 @@ class Preprocessor { void recomputeCurLexerKind(); /// Returns true if incremental processing is enabled - bool isIncrementalProcessingEnabled() const { - return getLangOpts().IncrementalExtensions; - } + bool isIncrementalProcessingEnabled() const { return IncrementalProcessing; } /// Enables the incremental processing void enableIncrementalProcessing(bool value = true) { - // FIXME: Drop this interface. - const_cast(getLangOpts()).IncrementalExtensions = value; + IncrementalProcessing = value; } /// Specify the point at which code-completion will be performed. diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 47379e00a7445..712db0a3dd895 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -8724,7 +8724,9 @@ class Sema final { const ASTConstraintSatisfaction &Satisfaction); ExprResult ActOnRequiresExpr(SourceLocation RequiresKWLoc, RequiresExprBodyDecl *Body, + SourceLocation LParenLoc, ArrayRef LocalParameters, + SourceLocation RParenLoc, ArrayRef Requirements, SourceLocation ClosingBraceLoc); diff --git a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningFilesystem.h b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningFilesystem.h index 4b4e3c7eb2ecd..dbe219b6dd8d7 100644 --- a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningFilesystem.h +++ b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningFilesystem.h @@ -215,6 +215,7 @@ class DependencyScanningFilesystemLocalCache { public: /// Returns entry associated with the filename or nullptr if none is found. const CachedFileSystemEntry *findEntryByFilename(StringRef Filename) const { + assert(llvm::sys::path::is_absolute_gnu(Filename)); auto It = Cache.find(Filename); return It == Cache.end() ? nullptr : It->getValue(); } @@ -224,6 +225,7 @@ class DependencyScanningFilesystemLocalCache { const CachedFileSystemEntry & insertEntryForFilename(StringRef Filename, const CachedFileSystemEntry &Entry) { + assert(llvm::sys::path::is_absolute_gnu(Filename)); const auto *InsertedEntry = Cache.insert({Filename, &Entry}).first->second; assert(InsertedEntry == &Entry && "entry already present"); return *InsertedEntry; @@ -282,13 +284,14 @@ class DependencyScanningWorkerFilesystem : public llvm::vfs::ProxyFileSystem { public: DependencyScanningWorkerFilesystem( DependencyScanningFilesystemSharedCache &SharedCache, - IntrusiveRefCntPtr FS) - : ProxyFileSystem(std::move(FS)), SharedCache(SharedCache) {} + IntrusiveRefCntPtr FS); llvm::ErrorOr status(const Twine &Path) override; llvm::ErrorOr> openFileForRead(const Twine &Path) override; + std::error_code setCurrentWorkingDirectory(const Twine &Path) override; + /// Returns entry for the given filename. /// /// Attempts to use the local and shared caches first, then falls back to @@ -304,8 +307,11 @@ class DependencyScanningWorkerFilesystem : public llvm::vfs::ProxyFileSystem { /// For a filename that's not yet associated with any entry in the caches, /// uses the underlying filesystem to either look up the entry based in the /// shared cache indexed by unique ID, or creates new entry from scratch. + /// \p FilenameForLookup will always be an absolute path, and different than + /// \p OriginalFilename if \p OriginalFilename is relative. llvm::ErrorOr - computeAndStoreResult(StringRef Filename); + computeAndStoreResult(StringRef OriginalFilename, + StringRef FilenameForLookup); /// Scan for preprocessor directives for the given entry if necessary and /// returns a wrapper object with reference semantics. @@ -388,6 +394,12 @@ class DependencyScanningWorkerFilesystem : public llvm::vfs::ProxyFileSystem { /// The local cache is used by the worker thread to cache file system queries /// locally instead of querying the global cache every time. DependencyScanningFilesystemLocalCache LocalCache; + + /// The working directory to use for making relative paths absolute before + /// using them for cache lookups. + llvm::ErrorOr WorkingDirForCacheLookup; + + void updateWorkingDirForCacheLookup(); }; } // end namespace dependencies diff --git a/clang/lib/AST/ASTDiagnostic.cpp b/clang/lib/AST/ASTDiagnostic.cpp index f96a4fa3c35b0..7b0d5f9cc1a93 100644 --- a/clang/lib/AST/ASTDiagnostic.cpp +++ b/clang/lib/AST/ASTDiagnostic.cpp @@ -1890,6 +1890,7 @@ class TemplateDiff { // FIXME: Diffing the APValue would be neat. // FIXME: Suppress this and use the full name of the declaration if the // parameter is a pointer or reference. + TPO->getType().getUnqualifiedType().print(OS, Policy); TPO->printAsInit(OS, Policy); return; } diff --git a/clang/lib/AST/ASTStructuralEquivalence.cpp b/clang/lib/AST/ASTStructuralEquivalence.cpp index 544420234ef0e..5b98d14dd3d91 100644 --- a/clang/lib/AST/ASTStructuralEquivalence.cpp +++ b/clang/lib/AST/ASTStructuralEquivalence.cpp @@ -214,6 +214,15 @@ class StmtComparer { return E1->size() == E2->size(); } + bool IsStmtEquivalent(const DeclRefExpr *DRE1, const DeclRefExpr *DRE2) { + const ValueDecl *Decl1 = DRE1->getDecl(); + const ValueDecl *Decl2 = DRE2->getDecl(); + if (!Decl1 || !Decl2) + return false; + return IsStructurallyEquivalent(Context, const_cast(Decl1), + const_cast(Decl2)); + } + bool IsStmtEquivalent(const DependentScopeDeclRefExpr *DE1, const DependentScopeDeclRefExpr *DE2) { if (!IsStructurallyEquivalent(Context, DE1->getDeclName(), @@ -277,6 +286,17 @@ class StmtComparer { bool IsStmtEquivalent(const Stmt *S1, const Stmt *S2) { return true; } + bool IsStmtEquivalent(const GotoStmt *S1, const GotoStmt *S2) { + LabelDecl *L1 = S1->getLabel(); + LabelDecl *L2 = S2->getLabel(); + if (!L1 || !L2) + return L1 == L2; + + IdentifierInfo *Name1 = L1->getIdentifier(); + IdentifierInfo *Name2 = L2->getIdentifier(); + return ::IsStructurallyEquivalent(Name1, Name2); + } + bool IsStmtEquivalent(const SourceLocExpr *E1, const SourceLocExpr *E2) { return E1->getIdentKind() == E2->getIdentKind(); } @@ -1295,6 +1315,22 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, return true; } +static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, + VarDecl *D1, VarDecl *D2) { + if (D1->getStorageClass() != D2->getStorageClass()) + return false; + + IdentifierInfo *Name1 = D1->getIdentifier(); + IdentifierInfo *Name2 = D2->getIdentifier(); + if (!::IsStructurallyEquivalent(Name1, Name2)) + return false; + + if (!IsStructurallyEquivalent(Context, D1->getType(), D2->getType())) + return false; + + return IsStructurallyEquivalent(Context, D1->getInit(), D2->getInit()); +} + static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, FieldDecl *Field1, FieldDecl *Field2, QualType Owner2Type) { diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index 4f3837371b3fc..af82ca0784af4 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -772,18 +772,21 @@ std::string PredefinedExpr::ComputeName(IdentKind IK, const Decl *CurrentDecl) { return std::string(Out.str()); } if (const FunctionDecl *FD = dyn_cast(CurrentDecl)) { - if (IK != PrettyFunction && IK != PrettyFunctionNoVirtual && - IK != FuncSig && IK != LFuncSig) + const auto &LO = Context.getLangOpts(); + if (((IK == Func || IK == Function) && !LO.MicrosoftExt) || + (IK == LFunction && LO.MicrosoftExt)) return FD->getNameAsString(); SmallString<256> Name; llvm::raw_svector_ostream Out(Name); - if (const CXXMethodDecl *MD = dyn_cast(FD)) { - if (MD->isVirtual() && IK != PrettyFunctionNoVirtual) - Out << "virtual "; - if (MD->isStatic()) - Out << "static "; + if (IK != Function) { + if (const CXXMethodDecl *MD = dyn_cast(FD)) { + if (MD->isVirtual() && IK != PrettyFunctionNoVirtual) + Out << "virtual "; + if (MD->isStatic()) + Out << "static "; + } } class PrettyCallbacks final : public PrintingCallbacks { @@ -798,9 +801,10 @@ std::string PredefinedExpr::ComputeName(IdentKind IK, const Decl *CurrentDecl) { private: const LangOptions &LO; }; - PrintingPolicy Policy(Context.getLangOpts()); - PrettyCallbacks PrettyCB(Context.getLangOpts()); + PrintingPolicy Policy(LO); + PrettyCallbacks PrettyCB(LO); Policy.Callbacks = &PrettyCB; + Policy.UseClassForTemplateArgument = LO.MicrosoftExt; std::string Proto; llvm::raw_string_ostream POut(Proto); @@ -827,6 +831,11 @@ std::string PredefinedExpr::ComputeName(IdentKind IK, const Decl *CurrentDecl) { FD->printQualifiedName(POut, Policy); + if ((IK == Function || IK == Func) && LO.MicrosoftExt) { + Out << Proto; + return std::string(Name); + } + POut << "("; if (FT) { for (unsigned i = 0, e = Decl->getNumParams(); i != e; ++i) { diff --git a/clang/lib/AST/ExprConcepts.cpp b/clang/lib/AST/ExprConcepts.cpp index 2aa15049a7ff2..0704630c0fc26 100644 --- a/clang/lib/AST/ExprConcepts.cpp +++ b/clang/lib/AST/ExprConcepts.cpp @@ -117,13 +117,15 @@ static bool RequirementContainsError(concepts::Requirement *R) { } RequiresExpr::RequiresExpr(ASTContext &C, SourceLocation RequiresKWLoc, - RequiresExprBodyDecl *Body, + RequiresExprBodyDecl *Body, SourceLocation LParenLoc, ArrayRef LocalParameters, + SourceLocation RParenLoc, ArrayRef Requirements, SourceLocation RBraceLoc) : Expr(RequiresExprClass, C.BoolTy, VK_PRValue, OK_Ordinary), NumLocalParameters(LocalParameters.size()), - NumRequirements(Requirements.size()), Body(Body), RBraceLoc(RBraceLoc) { + NumRequirements(Requirements.size()), Body(Body), LParenLoc(LParenLoc), + RParenLoc(RParenLoc), RBraceLoc(RBraceLoc) { RequiresExprBits.IsSatisfied = false; RequiresExprBits.RequiresKWLoc = RequiresKWLoc; bool Dependent = false; @@ -168,18 +170,18 @@ RequiresExpr::RequiresExpr(ASTContext &C, EmptyShell Empty, : Expr(RequiresExprClass, Empty), NumLocalParameters(NumLocalParameters), NumRequirements(NumRequirements) { } -RequiresExpr * -RequiresExpr::Create(ASTContext &C, SourceLocation RequiresKWLoc, - RequiresExprBodyDecl *Body, - ArrayRef LocalParameters, - ArrayRef Requirements, - SourceLocation RBraceLoc) { +RequiresExpr *RequiresExpr::Create( + ASTContext &C, SourceLocation RequiresKWLoc, RequiresExprBodyDecl *Body, + SourceLocation LParenLoc, ArrayRef LocalParameters, + SourceLocation RParenLoc, ArrayRef Requirements, + SourceLocation RBraceLoc) { void *Mem = C.Allocate(totalSizeToAlloc( LocalParameters.size(), Requirements.size()), alignof(RequiresExpr)); - return new (Mem) RequiresExpr(C, RequiresKWLoc, Body, LocalParameters, - Requirements, RBraceLoc); + return new (Mem) + RequiresExpr(C, RequiresKWLoc, Body, LParenLoc, LocalParameters, + RParenLoc, Requirements, RBraceLoc); } RequiresExpr * diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp index c813f9b6d9991..e813d4fa651ce 100644 --- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp +++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp @@ -1934,15 +1934,13 @@ template const RecordType *ByteCodeExprGen::getRecordTy(QualType Ty) { if (const PointerType *PT = dyn_cast(Ty)) return PT->getPointeeType()->getAs(); - else - return Ty->getAs(); + return Ty->getAs(); } template Record *ByteCodeExprGen::getRecord(QualType Ty) { - if (auto *RecordTy = getRecordTy(Ty)) { + if (const auto *RecordTy = getRecordTy(Ty)) return getRecord(RecordTy->getDecl()); - } return nullptr; } diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp index 53963d2a91752..e7a5a6b6b8119 100644 --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -234,6 +234,11 @@ class CXXNameMangler { const NamedDecl *Structor; unsigned StructorType = 0; + // An offset to add to all template parameter depths while mangling. Used + // when mangling a template parameter list to see if it matches a template + // template parameter exactly. + unsigned TemplateDepthOffset = 0; + /// The next substitution sequence number. unsigned SeqID = 0; @@ -392,6 +397,10 @@ class CXXNameMangler { ASTContext &getASTContext() const { return Context.getASTContext(); } + bool isCompatibleWith(LangOptions::ClangABI Ver) { + return Context.getASTContext().getLangOpts().getClangABICompat() <= Ver; + } + bool isStd(const NamespaceDecl *NS); bool isStdNamespace(const DeclContext *DC); @@ -434,6 +443,13 @@ class CXXNameMangler { NullOut = true; } + struct WithTemplateDepthOffset { unsigned Offset; }; + CXXNameMangler(ItaniumMangleContextImpl &C, raw_ostream &Out, + WithTemplateDepthOffset Offset) + : CXXNameMangler(C, Out) { + TemplateDepthOffset = Offset.Offset; + } + raw_ostream &getStream() { return Out; } void disableDerivedAbiTags() { DisableDerivedAbiTags = true; } @@ -517,6 +533,11 @@ class CXXNameMangler { void mangleBlockForPrefix(const BlockDecl *Block); void mangleUnqualifiedBlock(const BlockDecl *Block); void mangleTemplateParamDecl(const NamedDecl *Decl); + void mangleTemplateParameterList(const TemplateParameterList *Params); + void mangleTypeConstraint(const ConceptDecl *Concept, + ArrayRef Arguments); + void mangleTypeConstraint(const TypeConstraint *Constraint); + void mangleRequiresClause(const Expr *RequiresClause); void mangleLambda(const CXXRecordDecl *Lambda); void mangleNestedName(GlobalDecl GD, const DeclContext *DC, const AbiTagList *AdditionalAbiTags, @@ -580,16 +601,21 @@ class CXXNameMangler { unsigned knownArity); void mangleCastExpression(const Expr *E, StringRef CastEncoding); void mangleInitListElements(const InitListExpr *InitList); + void mangleRequirement(SourceLocation RequiresExprLoc, + const concepts::Requirement *Req); void mangleExpression(const Expr *E, unsigned Arity = UnknownArity, bool AsTemplateArg = false); void mangleCXXCtorType(CXXCtorType T, const CXXRecordDecl *InheritedFrom); void mangleCXXDtorType(CXXDtorType T); + struct TemplateArgManglingInfo; void mangleTemplateArgs(TemplateName TN, const TemplateArgumentLoc *TemplateArgs, unsigned NumTemplateArgs); void mangleTemplateArgs(TemplateName TN, ArrayRef Args); void mangleTemplateArgs(TemplateName TN, const TemplateArgumentList &AL); + void mangleTemplateArg(TemplateArgManglingInfo &Info, unsigned Index, + TemplateArgument A); void mangleTemplateArg(TemplateArgument A, bool NeedExactType); void mangleTemplateArgExpr(const Expr *E); void mangleValueInTemplateArg(QualType T, const APValue &V, bool TopLevel, @@ -667,9 +693,16 @@ ItaniumMangleContextImpl::getEffectiveDeclContext(const Decl *D) { if (VD->isExternC()) return getASTContext().getTranslationUnitDecl(); - if (const auto *FD = dyn_cast(D)) + if (const auto *FD = dyn_cast(D)) { if (FD->isExternC()) return getASTContext().getTranslationUnitDecl(); + // Member-like constrained friends are mangled as if they were members of + // the enclosing class. + if (FD->isMemberLikeConstrainedFriend() && + getASTContext().getLangOpts().getClangABICompat() > + LangOptions::ClangABI::Ver17) + return D->getLexicalDeclContext()->getRedeclContext(); + } return DC->getRedeclContext(); } @@ -858,16 +891,15 @@ void CXXNameMangler::mangleFunctionEncodingBareType(const FunctionDecl *FD) { EnableIfAttr *EIA = dyn_cast(*I); if (!EIA) continue; - if (Context.getASTContext().getLangOpts().getClangABICompat() > - LangOptions::ClangABI::Ver11) { - mangleTemplateArgExpr(EIA->getCond()); - } else { + if (isCompatibleWith(LangOptions::ClangABI::Ver11)) { // Prior to Clang 12, we hardcoded the X/E around enable-if's argument, // even though should not include an X/E around // . Out << 'X'; mangleExpression(EIA->getCond()); Out << 'E'; + } else { + mangleTemplateArgExpr(EIA->getCond()); } } Out << 'E'; @@ -1415,14 +1447,24 @@ void CXXNameMangler::mangleUnqualifiedName( GlobalDecl GD, DeclarationName Name, const DeclContext *DC, unsigned KnownArity, const AbiTagList *AdditionalAbiTags) { const NamedDecl *ND = cast_or_null(GD.getDecl()); - // ::= [] + // ::= [] [F] // ::= - // ::= [] + // ::= [] [F] // ::= [] DC * E if (ND && DC && DC->isFileContext()) mangleModuleName(ND); + // A member-like constrained friend is mangled with a leading 'F'. + // Proposed on https://github.com/itanium-cxx-abi/cxx-abi/issues/24. + auto *FD = dyn_cast(ND); + auto *FTD = dyn_cast(ND); + if ((FD && FD->isMemberLikeConstrainedFriend()) || + (FTD && FTD->getTemplatedDecl()->isMemberLikeConstrainedFriend())) { + if (!isCompatibleWith(LangOptions::ClangABI::Ver17)) + Out << 'F'; + } + unsigned Arity = KnownArity; switch (Name.getNameKind()) { case DeclarationName::Identifier: { @@ -1478,7 +1520,6 @@ void CXXNameMangler::mangleUnqualifiedName( if (Context.isInternalLinkageDecl(ND)) Out << 'L'; - auto *FD = dyn_cast(ND); bool IsRegCall = FD && FD->getType()->castAs()->getCallConv() == clang::CC_X86RegCall; @@ -1911,8 +1952,7 @@ void CXXNameMangler::mangleUnqualifiedBlock(const BlockDecl *Block) { // When trying to be ABI-compatibility with clang 12 and before, mangle a // now, with no substitutions and no . if (Decl *Context = Block->getBlockManglingContextDecl()) { - if (getASTContext().getLangOpts().getClangABICompat() <= - LangOptions::ClangABI::Ver12 && + if (isCompatibleWith(LangOptions::ClangABI::Ver12) && (isa(Context) || isa(Context)) && Context->getDeclContext()->isRecord()) { const auto *ND = cast(Context); @@ -1940,15 +1980,25 @@ void CXXNameMangler::mangleUnqualifiedBlock(const BlockDecl *Block) { } // -// ::= Ty # template type parameter -// ::= Tn # template non-type parameter -// ::= Tt * E # template template parameter -// ::= Tp # template parameter pack +// ::= Ty # template type parameter +// ::= Tk [] # constrained type parameter +// ::= Tn # template non-type parameter +// ::= Tt * E [Q ] +// # template template parameter +// ::= Tp # template parameter pack void CXXNameMangler::mangleTemplateParamDecl(const NamedDecl *Decl) { + // Proposed on https://github.com/itanium-cxx-abi/cxx-abi/issues/47. if (auto *Ty = dyn_cast(Decl)) { if (Ty->isParameterPack()) Out << "Tp"; - Out << "Ty"; + const TypeConstraint *Constraint = Ty->getTypeConstraint(); + if (Constraint && !isCompatibleWith(LangOptions::ClangABI::Ver17)) { + // Proposed on https://github.com/itanium-cxx-abi/cxx-abi/issues/24. + Out << "Tk"; + mangleTypeConstraint(Constraint); + } else { + Out << "Ty"; + } } else if (auto *Tn = dyn_cast(Decl)) { if (Tn->isExpandedParameterPack()) { for (unsigned I = 0, N = Tn->getNumExpansionTypes(); I != N; ++I) { @@ -1968,29 +2018,59 @@ void CXXNameMangler::mangleTemplateParamDecl(const NamedDecl *Decl) { } else if (auto *Tt = dyn_cast(Decl)) { if (Tt->isExpandedParameterPack()) { for (unsigned I = 0, N = Tt->getNumExpansionTemplateParameters(); I != N; - ++I) { - Out << "Tt"; - for (auto *Param : *Tt->getExpansionTemplateParameters(I)) - mangleTemplateParamDecl(Param); - Out << "E"; - } + ++I) + mangleTemplateParameterList(Tt->getExpansionTemplateParameters(I)); } else { if (Tt->isParameterPack()) Out << "Tp"; - Out << "Tt"; - for (auto *Param : *Tt->getTemplateParameters()) - mangleTemplateParamDecl(Param); - Out << "E"; + mangleTemplateParameterList(Tt->getTemplateParameters()); } } } +void CXXNameMangler::mangleTemplateParameterList( + const TemplateParameterList *Params) { + Out << "Tt"; + for (auto *Param : *Params) + mangleTemplateParamDecl(Param); + mangleRequiresClause(Params->getRequiresClause()); + Out << "E"; +} + +void CXXNameMangler::mangleTypeConstraint( + const ConceptDecl *Concept, ArrayRef Arguments) { + const DeclContext *DC = Context.getEffectiveDeclContext(Concept); + if (!Arguments.empty()) + mangleTemplateName(Concept, Arguments); + else if (DC->isTranslationUnit() || isStdNamespace(DC)) + mangleUnscopedName(Concept, DC, nullptr); + else + mangleNestedName(Concept, DC, nullptr); +} + +void CXXNameMangler::mangleTypeConstraint(const TypeConstraint *Constraint) { + llvm::SmallVector Args; + if (Constraint->getTemplateArgsAsWritten()) { + for (const TemplateArgumentLoc &ArgLoc : + Constraint->getTemplateArgsAsWritten()->arguments()) + Args.push_back(ArgLoc.getArgument()); + } + return mangleTypeConstraint(Constraint->getNamedConcept(), Args); +} + +void CXXNameMangler::mangleRequiresClause(const Expr *RequiresClause) { + // Proposed on https://github.com/itanium-cxx-abi/cxx-abi/issues/24. + if (RequiresClause && !isCompatibleWith(LangOptions::ClangABI::Ver17)) { + Out << 'Q'; + mangleExpression(RequiresClause); + } +} + void CXXNameMangler::mangleLambda(const CXXRecordDecl *Lambda) { // When trying to be ABI-compatibility with clang 12 and before, mangle a // now, with no substitutions. if (Decl *Context = Lambda->getLambdaContextDecl()) { - if (getASTContext().getLangOpts().getClangABICompat() <= - LangOptions::ClangABI::Ver12 && + if (isCompatibleWith(LangOptions::ClangABI::Ver12) && (isa(Context) || isa(Context)) && !isa(Context)) { if (const IdentifierInfo *Name @@ -2031,8 +2111,14 @@ void CXXNameMangler::mangleLambda(const CXXRecordDecl *Lambda) { } void CXXNameMangler::mangleLambdaSig(const CXXRecordDecl *Lambda) { + // Proposed on https://github.com/itanium-cxx-abi/cxx-abi/issues/31. for (auto *D : Lambda->getLambdaExplicitTemplateParameters()) mangleTemplateParamDecl(D); + + // Proposed on https://github.com/itanium-cxx-abi/cxx-abi/issues/24. + if (auto *TPL = Lambda->getGenericLambdaTemplateParameterList()) + mangleRequiresClause(TPL->getRequiresClause()); + auto *Proto = Lambda->getLambdaTypeInfo()->getType()->castAs(); mangleBareFunctionType(Proto, /*MangleReturnType=*/false, @@ -2063,8 +2149,7 @@ void CXXNameMangler::manglePrefix(NestedNameSpecifier *qualifier) { case NestedNameSpecifier::Identifier: // Clang 14 and before did not consider this substitutable. - bool Clang14Compat = getASTContext().getLangOpts().getClangABICompat() <= - LangOptions::ClangABI::Ver14; + bool Clang14Compat = isCompatibleWith(LangOptions::ClangABI::Ver14); if (!Clang14Compat && mangleSubstitution(qualifier)) return; @@ -2134,8 +2219,7 @@ void CXXNameMangler::mangleTemplatePrefix(TemplateName Template) { // Clang 11 and before mangled the substitution for a dependent template name // after already having emitted (a substitution for) the prefix. - bool Clang11Compat = getASTContext().getLangOpts().getClangABICompat() <= - LangOptions::ClangABI::Ver11; + bool Clang11Compat = isCompatibleWith(LangOptions::ClangABI::Ver11); if (!Clang11Compat && mangleSubstitution(Template)) return; @@ -2182,8 +2266,7 @@ void CXXNameMangler::mangleTemplatePrefix(GlobalDecl GD, } const NamedDecl *CXXNameMangler::getClosurePrefix(const Decl *ND) { - if (getASTContext().getLangOpts().getClangABICompat() <= - LangOptions::ClangABI::Ver12) + if (isCompatibleWith(LangOptions::ClangABI::Ver12)) return nullptr; const NamedDecl *Context = nullptr; @@ -3430,39 +3513,42 @@ void CXXNameMangler::mangleBareFunctionType(const FunctionProtoType *Proto, if (Proto->getNumParams() == 0 && !Proto->isVariadic()) { // ::= v # void Out << 'v'; + } else { + assert(!FD || FD->getNumParams() == Proto->getNumParams()); + for (unsigned I = 0, E = Proto->getNumParams(); I != E; ++I) { + // Mangle extended parameter info as order-sensitive qualifiers here. + if (Proto->hasExtParameterInfos() && FD == nullptr) { + mangleExtParameterInfo(Proto->getExtParameterInfo(I)); + } - FunctionTypeDepth.pop(saved); - return; - } - - assert(!FD || FD->getNumParams() == Proto->getNumParams()); - for (unsigned I = 0, E = Proto->getNumParams(); I != E; ++I) { - // Mangle extended parameter info as order-sensitive qualifiers here. - if (Proto->hasExtParameterInfos() && FD == nullptr) { - mangleExtParameterInfo(Proto->getExtParameterInfo(I)); + // Mangle the type. + QualType ParamTy = Proto->getParamType(I); + mangleType(Context.getASTContext().getSignatureParameterType(ParamTy)); + + if (FD) { + if (auto *Attr = FD->getParamDecl(I)->getAttr()) { + // Attr can only take 1 character, so we can hardcode the length + // below. + assert(Attr->getType() <= 9 && Attr->getType() >= 0); + if (Attr->isDynamic()) + Out << "U25pass_dynamic_object_size" << Attr->getType(); + else + Out << "U17pass_object_size" << Attr->getType(); + } + } } - // Mangle the type. - QualType ParamTy = Proto->getParamType(I); - mangleType(Context.getASTContext().getSignatureParameterType(ParamTy)); + // ::= z # ellipsis + if (Proto->isVariadic()) + Out << 'z'; + } - if (FD) { - if (auto *Attr = FD->getParamDecl(I)->getAttr()) { - // Attr can only take 1 character, so we can hardcode the length below. - assert(Attr->getType() <= 9 && Attr->getType() >= 0); - if (Attr->isDynamic()) - Out << "U25pass_dynamic_object_size" << Attr->getType(); - else - Out << "U17pass_object_size" << Attr->getType(); - } - } + if (FD) { + FunctionTypeDepth.enterResultType(); + mangleRequiresClause(FD->getTrailingRequiresClause()); } FunctionTypeDepth.pop(saved); - - // ::= z # ellipsis - if (Proto->isVariadic()) - Out << 'z'; } // ::= @@ -4198,7 +4284,15 @@ void CXXNameMangler::mangleType(const AutoType *T) { "shouldn't need to mangle __auto_type!"); // ::= Da # auto // ::= Dc # decltype(auto) - Out << (T->isDecltypeAuto() ? "Dc" : "Da"); + // ::= Dk # constrained auto + // ::= DK # constrained decltype(auto) + if (T->isConstrained() && !isCompatibleWith(LangOptions::ClangABI::Ver17)) { + Out << (T->isDecltypeAuto() ? "DK" : "Dk"); + mangleTypeConstraint(T->getTypeConstraintConcept(), + T->getTypeConstraintArguments()); + } else { + Out << (T->isDecltypeAuto() ? "Dc" : "Da"); + } } void CXXNameMangler::mangleType(const DeducedTemplateSpecializationType *T) { @@ -4346,6 +4440,74 @@ void CXXNameMangler::mangleInitListElements(const InitListExpr *InitList) { mangleExpression(InitList->getInit(i)); } +void CXXNameMangler::mangleRequirement(SourceLocation RequiresExprLoc, + const concepts::Requirement *Req) { + using concepts::Requirement; + + // TODO: We can't mangle the result of a failed substitution. It's not clear + // whether we should be mangling the original form prior to any substitution + // instead. See https://lists.isocpp.org/core/2023/04/14118.php + auto HandleSubstitutionFailure = + [&](SourceLocation Loc) { + DiagnosticsEngine &Diags = Context.getDiags(); + unsigned DiagID = Diags.getCustomDiagID( + DiagnosticsEngine::Error, "cannot mangle this requires-expression " + "containing a substitution failure"); + Diags.Report(Loc, DiagID); + Out << 'F'; + }; + + switch (Req->getKind()) { + case Requirement::RK_Type: { + const auto *TR = cast(Req); + if (TR->isSubstitutionFailure()) + return HandleSubstitutionFailure( + TR->getSubstitutionDiagnostic()->DiagLoc); + + Out << 'T'; + mangleType(TR->getType()->getType()); + break; + } + + case Requirement::RK_Simple: + case Requirement::RK_Compound: { + const auto *ER = cast(Req); + if (ER->isExprSubstitutionFailure()) + return HandleSubstitutionFailure( + ER->getExprSubstitutionDiagnostic()->DiagLoc); + + Out << 'X'; + mangleExpression(ER->getExpr()); + + if (ER->hasNoexceptRequirement()) + Out << 'N'; + + if (!ER->getReturnTypeRequirement().isEmpty()) { + if (ER->getReturnTypeRequirement().isSubstitutionFailure()) + return HandleSubstitutionFailure(ER->getReturnTypeRequirement() + .getSubstitutionDiagnostic() + ->DiagLoc); + + Out << 'R'; + mangleTypeConstraint(ER->getReturnTypeRequirement().getTypeConstraint()); + } + break; + } + + case Requirement::RK_Nested: + const auto *NR = cast(Req); + if (NR->hasInvalidConstraint()) { + // FIXME: NestedRequirement should track the location of its requires + // keyword. + return HandleSubstitutionFailure(RequiresExprLoc); + } + + Out << 'Q'; + mangleExpression(NR->getConstraintExpr()); + break; + } +} + void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity, bool AsTemplateArg) { // ::= @@ -4478,8 +4640,6 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity, case Expr::ShuffleVectorExprClass: case Expr::ConvertVectorExprClass: case Expr::StmtExprClass: - case Expr::TypeTraitExprClass: - case Expr::RequiresExprClass: case Expr::ArrayTypeTraitExprClass: case Expr::ExpressionTraitExprClass: case Expr::VAArgExprClass: @@ -4508,8 +4668,7 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity, const CXXUuidofExpr *UE = cast(E); // As of clang 12, uuidof uses the vendor extended expression // mangling. Previously, it used a special-cased nonstandard extension. - if (Context.getASTContext().getLangOpts().getClangABICompat() > - LangOptions::ClangABI::Ver11) { + if (!isCompatibleWith(LangOptions::ClangABI::Ver11)) { Out << "u8__uuidof"; if (UE->isTypeOperand()) mangleType(UE->getTypeOperand(Context.getASTContext())); @@ -4832,6 +4991,10 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity, // If the result of the operator is implicitly converted to a known // integer type, that type is used for the literal; otherwise, the type // of std::size_t or std::ptrdiff_t is used. + // + // FIXME: We still include the operand in the profile in this case. This + // can lead to mangling collisions between function templates that we + // consider to be different. QualType T = (ImplicitlyConvertedToType.isNull() || !ImplicitlyConvertedToType->isIntegerType())? SAE->getType() : ImplicitlyConvertedToType; @@ -4861,8 +5024,7 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity, // As of clang 12, we mangle __alignof__ differently than alignof. (They // have acted differently since Clang 8, but were previously mangled the // same.) - if (Context.getASTContext().getLangOpts().getClangABICompat() > - LangOptions::ClangABI::Ver11) { + if (!isCompatibleWith(LangOptions::ClangABI::Ver11)) { Out << "u11__alignof__"; if (SAE->isArgumentType()) mangleType(SAE->getArgumentType()); @@ -4895,6 +5057,20 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity, break; } + case Expr::TypeTraitExprClass: { + // ::= u * E # vendor extension + const TypeTraitExpr *TTE = cast(E); + NotPrimaryExpr(); + Out << 'u'; + llvm::StringRef Spelling = getTraitSpelling(TTE->getTrait()); + Out << Spelling.size() << Spelling; + for (TypeSourceInfo *TSI : TTE->getArgs()) { + mangleType(TSI->getType()); + } + Out << 'E'; + break; + } + case Expr::CXXThrowExprClass: { NotPrimaryExpr(); const CXXThrowExpr *TE = cast(E); @@ -5083,11 +5259,57 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity, goto recurse; case Expr::ConceptSpecializationExprClass: { - // ::= L E # external name - Out << "L_Z"; auto *CSE = cast(E); - mangleTemplateName(CSE->getNamedConcept(), CSE->getTemplateArguments()); - Out << 'E'; + if (isCompatibleWith(LangOptions::ClangABI::Ver17)) { + // Clang 17 and before mangled concept-ids as if they resolved to an + // entity, meaning that references to enclosing template arguments don't + // work. + Out << "L_Z"; + mangleTemplateName(CSE->getNamedConcept(), CSE->getTemplateArguments()); + Out << 'E'; + break; + } + // Proposed on https://github.com/itanium-cxx-abi/cxx-abi/issues/24. + NotPrimaryExpr(); + mangleUnresolvedName( + CSE->getNestedNameSpecifierLoc().getNestedNameSpecifier(), + CSE->getConceptNameInfo().getName(), + CSE->getTemplateArgsAsWritten()->getTemplateArgs(), + CSE->getTemplateArgsAsWritten()->getNumTemplateArgs()); + break; + } + + case Expr::RequiresExprClass: { + // Proposed on https://github.com/itanium-cxx-abi/cxx-abi/issues/24. + auto *RE = cast(E); + // This is a primary-expression in the C++ grammar, but does not have an + // mangling (starting with 'L'). + NotPrimaryExpr(); + if (RE->getLParenLoc().isValid()) { + Out << "rQ"; + FunctionTypeDepthState saved = FunctionTypeDepth.push(); + if (RE->getLocalParameters().empty()) { + Out << 'v'; + } else { + for (ParmVarDecl *Param : RE->getLocalParameters()) { + mangleType(Context.getASTContext().getSignatureParameterType( + Param->getType())); + } + } + Out << '_'; + + // The rest of the mangling is in the immediate scope of the parameters. + FunctionTypeDepth.enterResultType(); + for (const concepts::Requirement *Req : RE->getRequirements()) + mangleRequirement(RE->getExprLoc(), Req); + FunctionTypeDepth.pop(saved); + Out << 'E'; + } else { + Out << "rq"; + for (const concepts::Requirement *Req : RE->getRequirements()) + mangleRequirement(RE->getExprLoc(), Req); + Out << 'E'; + } break; } @@ -5448,28 +5670,116 @@ void CXXNameMangler::mangleCXXDtorType(CXXDtorType T) { } } -namespace { // Helper to provide ancillary information on a template used to mangle its // arguments. -struct TemplateArgManglingInfo { +struct CXXNameMangler::TemplateArgManglingInfo { + const CXXNameMangler &Mangler; TemplateDecl *ResolvedTemplate = nullptr; bool SeenPackExpansionIntoNonPack = false; const NamedDecl *UnresolvedExpandedPack = nullptr; - TemplateArgManglingInfo(TemplateName TN) { + TemplateArgManglingInfo(const CXXNameMangler &Mangler, TemplateName TN) + : Mangler(Mangler) { if (TemplateDecl *TD = TN.getAsTemplateDecl()) ResolvedTemplate = TD; } - /// Do we need to mangle template arguments with exactly correct types? - /// + /// Information about how to mangle a template argument. + struct Info { + /// Do we need to mangle the template argument with an exactly correct type? + bool NeedExactType; + /// If we need to prefix the mangling with a mangling of the template + /// parameter, the corresponding parameter. + const NamedDecl *TemplateParameterToMangle; + }; + + /// Determine whether the resolved template might be overloaded on its + /// template parameter list. If so, the mangling needs to include enough + /// information to reconstruct the template parameter list. + bool isOverloadable() { + // Function templates are generally overloadable. As a special case, a + // member function template of a generic lambda is not overloadable. + if (auto *FTD = dyn_cast_or_null(ResolvedTemplate)) { + auto *RD = dyn_cast(FTD->getDeclContext()); + if (!RD || !RD->isGenericLambda()) + return true; + } + + // All other templates are not overloadable. Partial specializations would + // be, but we never mangle them. + return false; + } + + /// Determine whether we need to prefix this mangling with a + /// . This happens if the natural template parameter for + /// the argument mangling is not the same as the actual template parameter. + bool needToMangleTemplateParam(const NamedDecl *Param, + const TemplateArgument &Arg) { + // For a template type parameter, the natural parameter is 'typename T'. + // The actual parameter might be constrained. + if (auto *TTP = dyn_cast(Param)) + return TTP->hasTypeConstraint(); + + if (Arg.getKind() == TemplateArgument::Pack) { + // For an empty pack, the natural parameter is `typename...`. + if (Arg.pack_size() == 0) + return true; + + // For any other pack, we use the first argument to determine the natural + // template parameter. + return needToMangleTemplateParam(Param, *Arg.pack_begin()); + } + + // For a non-type template parameter, the natural parameter is `T V` (for a + // prvalue argument) or `T &V` (for a glvalue argument), where `T` is the + // type of the argument, which we require to exactly match. If the actual + // parameter has a deduced or instantiation-dependent type, it is not + // equivalent to the natural parameter. + if (auto *NTTP = dyn_cast(Param)) + return NTTP->getType()->isInstantiationDependentType() || + NTTP->getType()->getContainedDeducedType(); + + // For a template template parameter, the template-head might differ from + // that of the template. + auto *TTP = cast(Param); + TemplateName ArgTemplateName = Arg.getAsTemplateOrTemplatePattern(); + const TemplateDecl *ArgTemplate = ArgTemplateName.getAsTemplateDecl(); + if (!ArgTemplate) + return true; + + // Mangle the template parameter list of the parameter and argument to see + // if they are the same. We can't use Profile for this, because it can't + // model the depth difference between parameter and argument and might not + // necessarily have the same definition of "identical" that we use here -- + // that is, same mangling. + auto MangleTemplateParamListToString = + [&](SmallVectorImpl &Buffer, const TemplateParameterList *Params, + unsigned DepthOffset) { + llvm::raw_svector_ostream Stream(Buffer); + CXXNameMangler(Mangler.Context, Stream, + WithTemplateDepthOffset{DepthOffset}) + .mangleTemplateParameterList(Params); + }; + llvm::SmallString<128> ParamTemplateHead, ArgTemplateHead; + MangleTemplateParamListToString(ParamTemplateHead, + TTP->getTemplateParameters(), 0); + // Add the depth of the parameter's template parameter list to all + // parameters appearing in the argument to make the indexes line up + // properly. + MangleTemplateParamListToString(ArgTemplateHead, + ArgTemplate->getTemplateParameters(), + TTP->getTemplateParameters()->getDepth()); + return ParamTemplateHead != ArgTemplateHead; + } + + /// Determine information about how this template argument should be mangled. /// This should be called exactly once for each parameter / argument pair, in /// order. - bool needExactType(unsigned ParamIdx, const TemplateArgument &Arg) { + Info getArgInfo(unsigned ParamIdx, const TemplateArgument &Arg) { // We need correct types when the template-name is unresolved or when it // names a template that is able to be overloaded. if (!ResolvedTemplate || SeenPackExpansionIntoNonPack) - return true; + return {true, nullptr}; // Move to the next parameter. const NamedDecl *Param = UnresolvedExpandedPack; @@ -5495,17 +5805,13 @@ struct TemplateArgManglingInfo { if (Arg.isPackExpansion() && (!Param->isParameterPack() || UnresolvedExpandedPack)) { SeenPackExpansionIntoNonPack = true; - return true; + return {true, nullptr}; } - // We need exact types for function template arguments because they might be - // overloaded on template parameter type. As a special case, a member - // function template of a generic lambda is not overloadable. - if (auto *FTD = dyn_cast(ResolvedTemplate)) { - auto *RD = dyn_cast(FTD->getDeclContext()); - if (!RD || !RD->isGenericLambda()) - return true; - } + // We need exact types for arguments of a template that might be overloaded + // on template parameter type. + if (isOverloadable()) + return {true, needToMangleTemplateParam(Param, Arg) ? Param : nullptr}; // Otherwise, we only need a correct type if the parameter has a deduced // type. @@ -5515,43 +5821,75 @@ struct TemplateArgManglingInfo { // but it doesn't matter because substitution and expansion don't affect // whether a deduced type appears in the type. auto *NTTP = dyn_cast(Param); - return NTTP && NTTP->getType()->getContainedDeducedType(); + bool NeedExactType = NTTP && NTTP->getType()->getContainedDeducedType(); + return {NeedExactType, nullptr}; + } + + /// Determine if we should mangle a requires-clause after the template + /// argument list. If so, returns the expression to mangle. + const Expr *getTrailingRequiresClauseToMangle() { + if (!isOverloadable()) + return nullptr; + return ResolvedTemplate->getTemplateParameters()->getRequiresClause(); } }; -} void CXXNameMangler::mangleTemplateArgs(TemplateName TN, const TemplateArgumentLoc *TemplateArgs, unsigned NumTemplateArgs) { - // ::= I + E + // ::= I + [Q ] E Out << 'I'; - TemplateArgManglingInfo Info(TN); - for (unsigned i = 0; i != NumTemplateArgs; ++i) - mangleTemplateArg(TemplateArgs[i].getArgument(), - Info.needExactType(i, TemplateArgs[i].getArgument())); + TemplateArgManglingInfo Info(*this, TN); + for (unsigned i = 0; i != NumTemplateArgs; ++i) { + mangleTemplateArg(Info, i, TemplateArgs[i].getArgument()); + } + mangleRequiresClause(Info.getTrailingRequiresClauseToMangle()); Out << 'E'; } void CXXNameMangler::mangleTemplateArgs(TemplateName TN, const TemplateArgumentList &AL) { - // ::= I + E + // ::= I + [Q ] E Out << 'I'; - TemplateArgManglingInfo Info(TN); - for (unsigned i = 0, e = AL.size(); i != e; ++i) - mangleTemplateArg(AL[i], Info.needExactType(i, AL[i])); + TemplateArgManglingInfo Info(*this, TN); + for (unsigned i = 0, e = AL.size(); i != e; ++i) { + mangleTemplateArg(Info, i, AL[i]); + } + mangleRequiresClause(Info.getTrailingRequiresClauseToMangle()); Out << 'E'; } void CXXNameMangler::mangleTemplateArgs(TemplateName TN, ArrayRef Args) { - // ::= I + E + // ::= I + [Q ] E Out << 'I'; - TemplateArgManglingInfo Info(TN); - for (unsigned i = 0; i != Args.size(); ++i) - mangleTemplateArg(Args[i], Info.needExactType(i, Args[i])); + TemplateArgManglingInfo Info(*this, TN); + for (unsigned i = 0; i != Args.size(); ++i) { + mangleTemplateArg(Info, i, Args[i]); + } + mangleRequiresClause(Info.getTrailingRequiresClauseToMangle()); Out << 'E'; } +void CXXNameMangler::mangleTemplateArg(TemplateArgManglingInfo &Info, + unsigned Index, TemplateArgument A) { + TemplateArgManglingInfo::Info ArgInfo = Info.getArgInfo(Index, A); + + // Proposed on https://github.com/itanium-cxx-abi/cxx-abi/issues/47. + if (ArgInfo.TemplateParameterToMangle && + !isCompatibleWith(LangOptions::ClangABI::Ver17)) { + // The template parameter is mangled if the mangling would otherwise be + // ambiguous. + // + // ::= + // + // Clang 17 and before did not do this. + mangleTemplateParamDecl(ArgInfo.TemplateParameterToMangle); + } + + mangleTemplateArg(A, ArgInfo.NeedExactType); +} + void CXXNameMangler::mangleTemplateArg(TemplateArgument A, bool NeedExactType) { // ::= # type or template // ::= X E # expression @@ -5604,8 +5942,7 @@ void CXXNameMangler::mangleTemplateArg(TemplateArgument A, bool NeedExactType) { else if (D->getType()->isArrayType() && Ctx.hasSimilarType(Ctx.getDecayedType(D->getType()), A.getParamTypeForDecl()) && - Ctx.getLangOpts().getClangABICompat() > - LangOptions::ClangABI::Ver11) + !isCompatibleWith(LangOptions::ClangABI::Ver11)) // Build a value corresponding to this implicit array-to-pointer decay. Value = APValue(APValue::LValueBase(D), CharUnits::Zero(), {APValue::LValuePathEntry::ArrayIndex(0)}, @@ -5634,8 +5971,7 @@ void CXXNameMangler::mangleTemplateArg(TemplateArgument A, bool NeedExactType) { } void CXXNameMangler::mangleTemplateArgExpr(const Expr *E) { - ASTContext &Ctx = Context.getASTContext(); - if (Ctx.getLangOpts().getClangABICompat() > LangOptions::ClangABI::Ver11) { + if (!isCompatibleWith(LangOptions::ClangABI::Ver11)) { mangleExpression(E, UnknownArity, /*AsTemplateArg=*/true); return; } @@ -6051,8 +6387,7 @@ void CXXNameMangler::mangleValueInTemplateArg(QualType T, const APValue &V, } else { if (NeedExactType && !Ctx.hasSameType(T->getPointeeType(), getLValueType(Ctx, V)) && - Ctx.getLangOpts().getClangABICompat() > - LangOptions::ClangABI::Ver11) { + !isCompatibleWith(LangOptions::ClangABI::Ver11)) { NotPrimaryExpr(); Out << "cv"; mangleType(T); @@ -6150,8 +6485,7 @@ void CXXNameMangler::mangleValueInTemplateArg(QualType T, const APValue &V, !Ctx.hasSameType( T->castAs()->getPointeeType(), V.getMemberPointerDecl()->getType()) && - Ctx.getLangOpts().getClangABICompat() > - LangOptions::ClangABI::Ver11) { + !isCompatibleWith(LangOptions::ClangABI::Ver11)) { Out << "cv"; mangleType(T); } @@ -6182,6 +6516,7 @@ void CXXNameMangler::mangleTemplateParameter(unsigned Depth, unsigned Index) { // The latter two manglings are from a proposal here: // https://github.com/itanium-cxx-abi/cxx-abi/issues/31#issuecomment-528122117 Out << 'T'; + Depth += TemplateDepthOffset; if (Depth != 0) Out << 'L' << (Depth - 1) << '_'; if (Index != 0) diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp index 27f71edd6f99b..2e4f15f83ac26 100644 --- a/clang/lib/AST/StmtProfile.cpp +++ b/clang/lib/AST/StmtProfile.cpp @@ -1333,7 +1333,13 @@ void StmtProfiler::VisitPredefinedExpr(const PredefinedExpr *S) { void StmtProfiler::VisitIntegerLiteral(const IntegerLiteral *S) { VisitExpr(S); S->getValue().Profile(ID); - ID.AddInteger(S->getType()->castAs()->getKind()); + + QualType T = S->getType(); + ID.AddInteger(T->getTypeClass()); + if (auto BitIntT = T->getAs()) + BitIntT->Profile(ID); + else + ID.AddInteger(T->castAs()->getKind()); } void StmtProfiler::VisitFixedPointLiteral(const FixedPointLiteral *S) { diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp index eb69d0bb8755b..3771a29f26b17 100644 --- a/clang/lib/AST/TypePrinter.cpp +++ b/clang/lib/AST/TypePrinter.cpp @@ -2218,6 +2218,10 @@ printTo(raw_ostream &OS, ArrayRef Args, const PrintingPolicy &Policy, } else { if (!FirstArg) OS << Comma; + if (Policy.UseClassForTemplateArgument && + Argument.getKind() == TemplateArgument::Type) + OS << "class "; + // Tries to print the argument with location info if exists. printArgument(Arg, Policy, ArgOS, TemplateParameterList::shouldIncludeTypeForArgument( diff --git a/clang/lib/Analysis/FlowSensitive/Arena.cpp b/clang/lib/Analysis/FlowSensitive/Arena.cpp index a12da2d9b555e..b043a52b609df 100644 --- a/clang/lib/Analysis/FlowSensitive/Arena.cpp +++ b/clang/lib/Analysis/FlowSensitive/Arena.cpp @@ -7,7 +7,10 @@ //===----------------------------------------------------------------------===// #include "clang/Analysis/FlowSensitive/Arena.h" +#include "clang/Analysis/FlowSensitive/Formula.h" #include "clang/Analysis/FlowSensitive/Value.h" +#include "llvm/Support/Error.h" +#include namespace clang::dataflow { @@ -95,4 +98,96 @@ BoolValue &Arena::makeBoolValue(const Formula &F) { return *It->second; } +namespace { +const Formula *parse(Arena &A, llvm::StringRef &In) { + auto EatSpaces = [&] { In = In.ltrim(' '); }; + EatSpaces(); + + if (In.consume_front("!")) { + if (auto *Arg = parse(A, In)) + return &A.makeNot(*Arg); + return nullptr; + } + + if (In.consume_front("(")) { + auto *Arg1 = parse(A, In); + if (!Arg1) + return nullptr; + + EatSpaces(); + decltype(&Arena::makeOr) Op; + if (In.consume_front("|")) + Op = &Arena::makeOr; + else if (In.consume_front("&")) + Op = &Arena::makeAnd; + else if (In.consume_front("=>")) + Op = &Arena::makeImplies; + else if (In.consume_front("=")) + Op = &Arena::makeEquals; + else + return nullptr; + + auto *Arg2 = parse(A, In); + if (!Arg2) + return nullptr; + + EatSpaces(); + if (!In.consume_front(")")) + return nullptr; + + return &(A.*Op)(*Arg1, *Arg2); + } + + // For now, only support unnamed variables V0, V1 etc. + // FIXME: parse e.g. "X" by allocating an atom and storing a name somewhere. + if (In.consume_front("V")) { + std::underlying_type_t At; + if (In.consumeInteger(10, At)) + return nullptr; + return &A.makeAtomRef(static_cast(At)); + } + + if (In.consume_front("true")) + return &A.makeLiteral(true); + if (In.consume_front("false")) + return &A.makeLiteral(false); + + return nullptr; +} + +class FormulaParseError : public llvm::ErrorInfo { + std::string Formula; + unsigned Offset; + +public: + static char ID; + FormulaParseError(llvm::StringRef Formula, unsigned Offset) + : Formula(Formula), Offset(Offset) {} + + void log(raw_ostream &OS) const override { + OS << "bad formula at offset " << Offset << "\n"; + OS << Formula << "\n"; + OS.indent(Offset) << "^"; + } + + std::error_code convertToErrorCode() const override { + return std::make_error_code(std::errc::invalid_argument); + } +}; + +char FormulaParseError::ID = 0; + +} // namespace + +llvm::Expected Arena::parseFormula(llvm::StringRef In) { + llvm::StringRef Rest = In; + auto *Result = parse(*this, Rest); + if (!Result) // parse() hit something unparseable + return llvm::make_error(In, In.size() - Rest.size()); + Rest = Rest.ltrim(); + if (!Rest.empty()) // parse didn't consume all the input + return llvm::make_error(In, In.size() - Rest.size()); + return *Result; +} + } // namespace clang::dataflow diff --git a/clang/lib/Analysis/FlowSensitive/Formula.cpp b/clang/lib/Analysis/FlowSensitive/Formula.cpp index 504ad2fb7938a..6d22efc5db07b 100644 --- a/clang/lib/Analysis/FlowSensitive/Formula.cpp +++ b/clang/lib/Analysis/FlowSensitive/Formula.cpp @@ -13,6 +13,7 @@ #include "llvm/Support/Allocator.h" #include "llvm/Support/ErrorHandling.h" #include +#include namespace clang::dataflow { diff --git a/clang/lib/Analysis/FlowSensitive/HTMLLogger.cpp b/clang/lib/Analysis/FlowSensitive/HTMLLogger.cpp index a5f64021eb6ba..47915958750d1 100644 --- a/clang/lib/Analysis/FlowSensitive/HTMLLogger.cpp +++ b/clang/lib/Analysis/FlowSensitive/HTMLLogger.cpp @@ -146,15 +146,21 @@ class ModelDumper { }; class HTMLLogger : public Logger { + struct Iteration { + const CFGBlock *Block; + unsigned Iter; + bool PostVisit; + }; + StreamFactory Streams; std::unique_ptr OS; std::optional JOS; const ControlFlowContext *CFG; // Timeline of iterations of CFG block visitation. - std::vector> Iters; + std::vector Iters; // Number of times each CFG block has been seen. - llvm::DenseMap BlockIters; + llvm::DenseMap> BlockIters; // The messages logged in the current context but not yet written. std::string ContextLogs; // The number of elements we have visited within the current CFG block. @@ -198,8 +204,9 @@ class HTMLLogger : public Logger { JOS->attributeArray("timeline", [&] { for (const auto &E : Iters) { JOS->object([&] { - JOS->attribute("block", blockID(E.first->getBlockID())); - JOS->attribute("iter", E.second); + JOS->attribute("block", blockID(E.Block->getBlockID())); + JOS->attribute("iter", E.Iter); + JOS->attribute("post_visit", E.PostVisit); }); } }); @@ -214,8 +221,11 @@ class HTMLLogger : public Logger { *OS << llvm::StringRef(HTMLLogger_html).split("").second; } - void enterBlock(const CFGBlock &B) override { - Iters.emplace_back(&B, ++BlockIters[&B]); + void enterBlock(const CFGBlock &B, bool PostVisit) override { + llvm::SmallVector &BIter = BlockIters[&B]; + unsigned IterNum = BIter.size() + 1; + BIter.push_back({&B, IterNum, PostVisit}); + Iters.push_back({&B, IterNum, PostVisit}); ElementIndex = 0; } void enterElement(const CFGElement &E) override { @@ -243,17 +253,19 @@ class HTMLLogger : public Logger { // - meaningful names for values // - which boolean values are implied true/false by the flow condition void recordState(TypeErasedDataflowAnalysisState &State) override { - unsigned Block = Iters.back().first->getBlockID(); - unsigned Iter = Iters.back().second; + unsigned Block = Iters.back().Block->getBlockID(); + unsigned Iter = Iters.back().Iter; + bool PostVisit = Iters.back().PostVisit; JOS->attributeObject(elementIterID(Block, Iter, ElementIndex), [&] { JOS->attribute("block", blockID(Block)); JOS->attribute("iter", Iter); + JOS->attribute("post_visit", PostVisit); JOS->attribute("element", ElementIndex); // If this state immediately follows an Expr, show its built-in model. if (ElementIndex > 0) { auto S = - Iters.back().first->Elements[ElementIndex - 1].getAs(); + Iters.back().Block->Elements[ElementIndex - 1].getAs(); if (const Expr *E = S ? llvm::dyn_cast(S->getStmt()) : nullptr) { if (E->isPRValue()) { if (auto *V = State.Env.getValue(*E)) @@ -289,9 +301,16 @@ class HTMLLogger : public Logger { // Write the CFG block details. // Currently this is just the list of elements in execution order. // FIXME: an AST dump would be a useful view, too. - void writeBlock(const CFGBlock &B, unsigned Iters) { + void writeBlock(const CFGBlock &B, llvm::ArrayRef ItersForB) { JOS->attributeObject(blockID(B.getBlockID()), [&] { - JOS->attribute("iters", Iters); + JOS->attributeArray("iters", [&] { + for (const auto &Iter : ItersForB) { + JOS->object([&] { + JOS->attribute("iter", Iter.Iter); + JOS->attribute("post_visit", Iter.PostVisit); + }); + } + }); JOS->attributeArray("elements", [&] { for (const auto &Elt : B.Elements) { std::string Dump; diff --git a/clang/lib/Analysis/FlowSensitive/HTMLLogger.html b/clang/lib/Analysis/FlowSensitive/HTMLLogger.html index a60259a99cce0..87695623cb318 100644 --- a/clang/lib/Analysis/FlowSensitive/HTMLLogger.html +++ b/clang/lib/Analysis/FlowSensitive/HTMLLogger.html @@ -41,7 +41,11 @@
Timeline
@@ -54,8 +58,11 @@
-