diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index ad81bf1684b6cd222591f0191ed7b865956fd1c6..e25b2f50b1b4eafb7c71a2bf6dafa408d27afc50 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -64,8 +64,8 @@ clang/test/AST/Interp/ @tbaederr /mlir/Dialect/*/Transforms/Bufferize.cpp @matthias-springer # Linalg Dialect in MLIR. -/mlir/include/mlir/Dialect/Linalg/* @dcaballe @nicolasvasilache -/mlir/lib/Dialect/Linalg/* @dcaballe @nicolasvasilache +/mlir/include/mlir/Dialect/Linalg/* @dcaballe @nicolasvasilache @rengolin +/mlir/lib/Dialect/Linalg/* @dcaballe @nicolasvasilache @rengolin /mlir/lib/Dialect/Linalg/Transforms/DecomposeLinalgOps.cpp @MaheshRavishankar @nicolasvasilache /mlir/lib/Dialect/Linalg/Transforms/DropUnitDims.cpp @MaheshRavishankar @nicolasvasilache /mlir/lib/Dialect/Linalg/Transforms/ElementwiseOpFusion.cpp @MaheshRavishankar @nicolasvasilache diff --git a/.github/new-prs-labeler.yml b/.github/new-prs-labeler.yml index d608ea449f1d40c841e2ae356f4a0fa8431d2d48..a57ba28faf160b7b6df2ac5ba8f89e9cb3a54b6e 100644 --- a/.github/new-prs-labeler.yml +++ b/.github/new-prs-labeler.yml @@ -239,7 +239,7 @@ mlir:dlti: - mlir/**/DLTI/** mlir:emitc: - - mlir/**/EmitC/** + - mlir/**/*EmitC*/** - mlir/lib/Target/Cpp/** mlir:func: @@ -306,7 +306,7 @@ mlir:tensor: - mlir/**/Tensor/** mlir:tosa: - - mlir/**/Tosa/** + - mlir/**/*Tosa*/** mlir:ub: - mlir/**/UB/** diff --git a/.github/workflows/release-doxygen.yml b/.github/workflows/release-doxygen.yml index 5e322849a1d093fd775066c7a1768b00f52501c6..12c14bea52f624925e1b9ee5f6e108a24e066197 100644 --- a/.github/workflows/release-doxygen.yml +++ b/.github/workflows/release-doxygen.yml @@ -56,12 +56,12 @@ jobs: pip3 install --user -r ./llvm/docs/requirements.txt - name: Build Doxygen - env: - GITHUB_TOKEN: ${{ github.token }} run: | ./llvm/utils/release/build-docs.sh -release "${{ inputs.release-version }}" -no-sphinx - name: Upload Doxygen if: env.upload + env: + GITHUB_TOKEN: ${{ github.token }} run: | ./llvm/utils/release/github-upload-release.py --token "$GITHUB_TOKEN" --release "${{ inputs.release-version }}" --user "${{ github.actor }}" upload --files ./*doxygen*.tar.xz diff --git a/.github/workflows/release-tasks.yml b/.github/workflows/release-tasks.yml index 53da8662b0203a87d29e5ed33f678478db02b8bf..29049ff014288733e638b4f434543e44722c5d6d 100644 --- a/.github/workflows/release-tasks.yml +++ b/.github/workflows/release-tasks.yml @@ -1,7 +1,7 @@ name: Release Task permissions: - contents: write + contents: read on: push: @@ -27,6 +27,8 @@ jobs: release-create: name: Create a New Release runs-on: ubuntu-latest + permissions: + contents: write # For creating the release. needs: validate-tag steps: @@ -55,6 +57,8 @@ jobs: release-doxygen: name: Build and Upload Release Doxygen + permissions: + contents: write needs: - validate-tag - release-create @@ -72,6 +76,8 @@ jobs: release-binaries: name: Build Release Binaries + permissions: + contents: write needs: - validate-tag - release-create diff --git a/bolt/include/bolt/Passes/FrameAnalysis.h b/bolt/include/bolt/Passes/FrameAnalysis.h index 66246bd6647bb3202a2778b63efe9de27adf08af..44b54d4ed45d4d7a84914f811ca21618fe0a87dc 100644 --- a/bolt/include/bolt/Passes/FrameAnalysis.h +++ b/bolt/include/bolt/Passes/FrameAnalysis.h @@ -170,10 +170,6 @@ class FrameAnalysis { std::unique_ptr> SPTMap; - /// A vector that stores ids of the allocators that are used in SPT - /// computation - std::vector SPTAllocatorsId; - public: explicit FrameAnalysis(BinaryContext &BC, BinaryFunctionCallGraph &CG); diff --git a/bolt/include/bolt/Passes/IndirectCallPromotion.h b/bolt/include/bolt/Passes/IndirectCallPromotion.h index adc58d70ec0f4d9e5bb09765d1ce863c0bdf15df..8ec160b867cf8ce143b0227e7e3ef0c1adfadf0a 100644 --- a/bolt/include/bolt/Passes/IndirectCallPromotion.h +++ b/bolt/include/bolt/Passes/IndirectCallPromotion.h @@ -104,7 +104,7 @@ class IndirectCallPromotion : public BinaryFunctionPass { struct Location { MCSymbol *Sym{nullptr}; uint64_t Addr{0}; - bool isValid() const { return Sym || (!Sym && Addr != 0); } + bool isValid() const { return Sym || Addr != 0; } Location() {} explicit Location(MCSymbol *Sym) : Sym(Sym) {} explicit Location(uint64_t Addr) : Addr(Addr) {} diff --git a/bolt/lib/Core/BinaryEmitter.cpp b/bolt/lib/Core/BinaryEmitter.cpp index c3b3dc2e7005b76de4dbb86764cda8566140490f..6f86ddc774544a6e7a65d098b0c22e2962cbf2fd 100644 --- a/bolt/lib/Core/BinaryEmitter.cpp +++ b/bolt/lib/Core/BinaryEmitter.cpp @@ -485,7 +485,6 @@ void BinaryEmitter::emitFunctionBody(BinaryFunction &BF, FunctionFragment &FF, // This assumes the second instruction in the macro-op pair will get // assigned to its own MCRelaxableFragment. Since all JCC instructions // are relaxable, we should be safe. - Streamer.emitNeverAlignCodeAtEnd(/*Alignment to avoid=*/64, *BC.STI); } if (!EmitCodeOnly) { diff --git a/bolt/lib/Passes/BinaryPasses.cpp b/bolt/lib/Passes/BinaryPasses.cpp index c0ba73108f5778b4e385560c2129e6ef3bb056c8..867f977cebca724c82c2c27729b4492821f72e28 100644 --- a/bolt/lib/Passes/BinaryPasses.cpp +++ b/bolt/lib/Passes/BinaryPasses.cpp @@ -715,6 +715,9 @@ static uint64_t fixDoubleJumps(BinaryFunction &Function, bool MarkInvalid) { Pred->removeSuccessor(&BB); Pred->eraseInstruction(Pred->findInstruction(Branch)); Pred->addTailCallInstruction(SuccSym); + MCInst *TailCall = Pred->getLastNonPseudoInstr(); + assert(TailCall); + MIB->setOffset(*TailCall, BB.getOffset()); } else { return false; } @@ -910,6 +913,11 @@ uint64_t SimplifyConditionalTailCalls::fixTailCalls(BinaryFunction &BF) { auto &CTCAnnotation = MIB->getOrCreateAnnotationAs(*CondBranch, "CTCTakenCount"); CTCAnnotation = CTCTakenFreq; + // Preserve Offset annotation, used in BAT. + // Instr is a direct tail call instruction that was created when CTCs are + // first expanded, and has the original CTC offset set. + if (std::optional Offset = MIB->getOffset(*Instr)) + MIB->setOffset(*CondBranch, *Offset); // Remove the unused successor which may be eliminated later // if there are no other users. diff --git a/bolt/lib/Passes/FrameAnalysis.cpp b/bolt/lib/Passes/FrameAnalysis.cpp index 7f1245e39f567b933ade802d06597c09f6edfe56..4ebfd8f158f7f56dbf632974f74cbd079f9cd265 100644 --- a/bolt/lib/Passes/FrameAnalysis.cpp +++ b/bolt/lib/Passes/FrameAnalysis.cpp @@ -561,11 +561,6 @@ FrameAnalysis::FrameAnalysis(BinaryContext &BC, BinaryFunctionCallGraph &CG) NamedRegionTimer T1("clearspt", "clear spt", "FA", "FA breakdown", opts::TimeFA); clearSPTMap(); - - // Clean up memory allocated for annotation values - if (!opts::NoThreads) - for (MCPlusBuilder::AllocatorIdTy Id : SPTAllocatorsId) - BC.MIB->freeValuesAllocator(Id); } } diff --git a/bolt/lib/Passes/ValidateMemRefs.cpp b/bolt/lib/Passes/ValidateMemRefs.cpp index 1d2c230fa7106a633c1992a8900aaa8080b8288b..f29a97c43f497c289edbadfef7a59461f90b9694 100644 --- a/bolt/lib/Passes/ValidateMemRefs.cpp +++ b/bolt/lib/Passes/ValidateMemRefs.cpp @@ -29,7 +29,8 @@ bool ValidateMemRefs::checkAndFixJTReference(BinaryFunction &BF, MCInst &Inst, if (!BD) return false; - JumpTable *JT = BC.getJumpTableContainingAddress(BD->getAddress()); + const uint64_t TargetAddress = BD->getAddress() + Offset; + JumpTable *JT = BC.getJumpTableContainingAddress(TargetAddress); if (!JT) return false; @@ -40,10 +41,10 @@ bool ValidateMemRefs::checkAndFixJTReference(BinaryFunction &BF, MCInst &Inst, // Accessing a jump table in another function. This is not a // legitimate jump table access, we need to replace the reference to // the jump table label with a regular rodata reference. Get a - // non-JT reference by fetching the symbol 1 byte before the JT label. - MCSymbol *NewSym = BC.getOrCreateGlobalSymbol(BD->getAddress() - 1, "DATAat"); - BC.MIB->setOperandToSymbolRef(Inst, OperandNum, NewSym, Offset + 1, &*BC.Ctx, - 0); + // non-JT reference by fetching the symbol 1 byte before the JT + // label. + MCSymbol *NewSym = BC.getOrCreateGlobalSymbol(TargetAddress - 1, "DATAat"); + BC.MIB->setOperandToSymbolRef(Inst, OperandNum, NewSym, 1, &*BC.Ctx, 0); LLVM_DEBUG(dbgs() << "BOLT-DEBUG: replaced reference @" << BF.getPrintName() << " from " << BD->getName() << " to " << NewSym->getName() << " + 1\n"); diff --git a/bolt/lib/Profile/DataAggregator.cpp b/bolt/lib/Profile/DataAggregator.cpp index bbe9f36f9f699f0a407675135c095dc33422dd14..302bcf1f2d87d931aae5cfd476f69140f8276e2a 100644 --- a/bolt/lib/Profile/DataAggregator.cpp +++ b/bolt/lib/Profile/DataAggregator.cpp @@ -23,6 +23,7 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/ScopeExit.h" #include "llvm/Support/CommandLine.h" +#include "llvm/Support/Compiler.h" #include "llvm/Support/Debug.h" #include "llvm/Support/Errc.h" #include "llvm/Support/FileSystem.h" @@ -1999,7 +2000,7 @@ std::error_code DataAggregator::parseMMapEvents() { std::pair FileMMapInfo = FileMMapInfoRes.get(); if (FileMMapInfo.second.PID == -1) continue; - if (FileMMapInfo.first.equals("(deleted)")) + if (FileMMapInfo.first == "(deleted)") continue; // Consider only the first mapping of the file for any given PID @@ -2339,7 +2340,7 @@ std::error_code DataAggregator::writeBATYAML(BinaryContext &BC, continue; BinaryFunction *BF = BC.getBinaryFunctionAtAddress(FuncAddress); assert(BF); - YamlBF.Name = FuncName.str(); + YamlBF.Name = getLocationName(*BF); YamlBF.Id = BF->getFunctionNumber(); YamlBF.Hash = BAT->getBFHash(FuncAddress); YamlBF.ExecCount = BF->getKnownExecutionCount(); @@ -2381,7 +2382,10 @@ std::error_code DataAggregator::writeBATYAML(BinaryContext &BC, // Lookup containing basic block offset and index auto getBlock = [&BlockMap](uint32_t Offset) { auto BlockIt = BlockMap.upper_bound(Offset); - assert(BlockIt != BlockMap.begin()); + if (LLVM_UNLIKELY(BlockIt == BlockMap.begin())) { + errs() << "BOLT-ERROR: invalid BAT section\n"; + exit(1); + } --BlockIt; return std::pair(BlockIt->first, BlockIt->second.getBBIndex()); }; @@ -2406,6 +2410,17 @@ std::error_code DataAggregator::writeBATYAML(BinaryContext &BC, return A.Offset < B.Offset; }); } + // Set entry counts, similar to DataReader::readProfile. + for (const llvm::bolt::BranchInfo &BI : Branches.EntryData) { + if (!BlockMap.isInputBlock(BI.To.Offset)) { + if (opts::Verbosity >= 1) + errs() << "BOLT-WARNING: Unexpected EntryData in " << FuncName + << " at 0x" << Twine::utohexstr(BI.To.Offset) << '\n'; + continue; + } + const unsigned BlockIndex = BlockMap.getBBIndex(BI.To.Offset); + YamlBF.Blocks[BlockIndex].ExecCount += BI.Branches; + } // Drop blocks without a hash, won't be useful for stale matching. llvm::erase_if(YamlBF.Blocks, [](const yaml::bolt::BinaryBasicBlockProfile &YamlBB) { diff --git a/bolt/lib/Profile/DataReader.cpp b/bolt/lib/Profile/DataReader.cpp index 67f357fe4d3f0c42f704a52ca163e328652e7136..b2511ba1039989d3a062544df8681f7df3a1d199 100644 --- a/bolt/lib/Profile/DataReader.cpp +++ b/bolt/lib/Profile/DataReader.cpp @@ -1205,8 +1205,7 @@ std::error_code DataReader::parse() { // Add entry data for branches to another function or branches // to entry points (including recursive calls) - if (BI.To.IsSymbol && - (!BI.From.Name.equals(BI.To.Name) || BI.To.Offset == 0)) { + if (BI.To.IsSymbol && (BI.From.Name != BI.To.Name || BI.To.Offset == 0)) { I = GetOrCreateFuncEntry(BI.To.Name); I->second.EntryData.emplace_back(std::move(BI)); } diff --git a/bolt/lib/Rewrite/DWARFRewriter.cpp b/bolt/lib/Rewrite/DWARFRewriter.cpp index 26e4889faadace0bfd7d2eb5afb4fbec1e72e445..9d4297f913f3a7ada11fffd29d53ae8d4867fe60 100644 --- a/bolt/lib/Rewrite/DWARFRewriter.cpp +++ b/bolt/lib/Rewrite/DWARFRewriter.cpp @@ -1550,7 +1550,7 @@ CUOffsetMap DWARFRewriter::finalizeTypeSections(DIEBuilder &DIEBlder, for (const SectionRef &Section : Obj->sections()) { StringRef Contents = cantFail(Section.getContents()); StringRef Name = cantFail(Section.getName()); - if (Name.equals(".debug_types")) + if (Name == ".debug_types") BC.registerOrUpdateNoteSection(".debug_types", copyByteArray(Contents), Contents.size()); } @@ -1633,10 +1633,10 @@ void DWARFRewriter::finalizeDebugSections( for (const SectionRef &Secs : Obj->sections()) { StringRef Contents = cantFail(Secs.getContents()); StringRef Name = cantFail(Secs.getName()); - if (Name.equals(".debug_abbrev")) { + if (Name == ".debug_abbrev") { BC.registerOrUpdateNoteSection(".debug_abbrev", copyByteArray(Contents), Contents.size()); - } else if (Name.equals(".debug_info")) { + } else if (Name == ".debug_info") { BC.registerOrUpdateNoteSection(".debug_info", copyByteArray(Contents), Contents.size()); } @@ -1771,7 +1771,7 @@ std::optional updateDebugData( }; switch (SectionIter->second.second) { default: { - if (!SectionName.equals("debug_str.dwo")) + if (SectionName != "debug_str.dwo") errs() << "BOLT-WARNING: unsupported debug section: " << SectionName << "\n"; return SectionContents; @@ -1959,7 +1959,7 @@ void DWARFRewriter::updateDWP(DWARFUnit &CU, continue; } - if (SectionName.equals("debug_str.dwo")) { + if (SectionName == "debug_str.dwo") { CurStrSection = OutData; } else { // Since handleDebugDataPatching returned true, we already know this is diff --git a/bolt/lib/Rewrite/SDTRewriter.cpp b/bolt/lib/Rewrite/SDTRewriter.cpp index cc663b28990f8efbc0af0287184a9aff40b63a0e..a3928c554ad66c2c65b6cd3b39c4f070ef37e862 100644 --- a/bolt/lib/Rewrite/SDTRewriter.cpp +++ b/bolt/lib/Rewrite/SDTRewriter.cpp @@ -87,7 +87,7 @@ void SDTRewriter::readSection() { StringRef Name = DE.getCStr(&Offset); - if (!Name.equals("stapsdt")) + if (Name != "stapsdt") errs() << "BOLT-WARNING: SDT note name \"" << Name << "\" is not expected\n"; diff --git a/bolt/test/X86/Inputs/jump-table-fixed-ref-pic.s b/bolt/test/X86/Inputs/jump-table-fixed-ref-pic.s new file mode 100644 index 0000000000000000000000000000000000000000..66629a4880e6433c8f1cbdf5af4d6c93574df026 --- /dev/null +++ b/bolt/test/X86/Inputs/jump-table-fixed-ref-pic.s @@ -0,0 +1,35 @@ + .globl main + .type main, %function +main: + .cfi_startproc + cmpq $0x3, %rdi + jae .L4 + cmpq $0x1, %rdi + jne .L4 + mov .Ljt_pic+8(%rip), %rax + lea .Ljt_pic(%rip), %rdx + add %rdx, %rax + jmpq *%rax +.L1: + movq $0x1, %rax + jmp .L5 +.L2: + movq $0x0, %rax + jmp .L5 +.L3: + movq $0x2, %rax + jmp .L5 +.L4: + mov $0x3, %rax +.L5: + retq + .cfi_endproc + + .section .rodata + .align 16 +.Ljt_pic: + .long .L1 - .Ljt_pic + .long .L2 - .Ljt_pic + .long .L3 - .Ljt_pic + .long .L4 - .Ljt_pic + diff --git a/bolt/test/X86/bb-with-two-tail-calls.s b/bolt/test/X86/bb-with-two-tail-calls.s index caad7b3d735f5f8d1ba64660f61a27231acd62ff..bb2b0cd4cc23a5bb50a3603d2b8e733b99dda79d 100644 --- a/bolt/test/X86/bb-with-two-tail-calls.s +++ b/bolt/test/X86/bb-with-two-tail-calls.s @@ -9,11 +9,11 @@ # RUN: llvm-strip --strip-unneeded %t.o # RUN: %clang %cflags %t.o -o %t.exe -Wl,-q -nostdlib # RUN: llvm-bolt %t.exe -o %t.out --data %t.fdata --lite=0 --dyno-stats \ -# RUN: --print-sctc --print-only=_start 2>&1 | FileCheck %s +# RUN: --print-sctc --print-only=_start -enable-bat 2>&1 | FileCheck %s # CHECK-NOT: Assertion `BranchInfo.size() == 2 && "could only be called for blocks with 2 successors"' failed. # Two tail calls in the same basic block after SCTC: -# CHECK: {{.*}}: ja {{.*}} # TAILCALL # CTCTakenCount: {{.*}} -# CHECK-NEXT: {{.*}}: jmp {{.*}} # TAILCALL +# CHECK: {{.*}}: ja {{.*}} # TAILCALL # Offset: 7 # CTCTakenCount: 4 +# CHECK-NEXT: {{.*}}: jmp {{.*}} # TAILCALL # Offset: 12 .globl _start _start: diff --git a/bolt/test/X86/bolt-address-translation-yaml.test b/bolt/test/X86/bolt-address-translation-yaml.test index f67cc6361c9ef8abe9418d2909946392fef578b0..c15d6ce15ed0df6dbc13fe837b7e7d2ffb7650be 100644 --- a/bolt/test/X86/bolt-address-translation-yaml.test +++ b/bolt/test/X86/bolt-address-translation-yaml.test @@ -59,6 +59,10 @@ YAML-BAT-CHECK-NEXT: hash: 0x6AF7E61EA3966722 YAML-BAT-CHECK-NEXT: exec: 25 YAML-BAT-CHECK-NEXT: nblocks: 15 YAML-BAT-CHECK-NEXT: blocks: +YAML-BAT-CHECK-NEXT: - bid: 0 +YAML-BAT-CHECK-NEXT: insns: [[#]] +YAML-BAT-CHECK-NEXT: hash: 0x700F19D24600000 +YAML-BAT-CHECK-NEXT: exec: 25 YAML-BAT-CHECK: - bid: 3 YAML-BAT-CHECK-NEXT: insns: [[#]] YAML-BAT-CHECK-NEXT: hash: 0xDDA1DC5F69F900AC diff --git a/bolt/test/X86/jump-table-fixed-ref-pic.test b/bolt/test/X86/jump-table-fixed-ref-pic.test new file mode 100644 index 0000000000000000000000000000000000000000..4195b97aac501ebc4a77a22f1c3efb601400a529 --- /dev/null +++ b/bolt/test/X86/jump-table-fixed-ref-pic.test @@ -0,0 +1,9 @@ +# Verify that BOLT detects fixed destination of indirect jump for PIC +# case. + +XFAIL: * + +RUN: %clang %cflags -no-pie %S/Inputs/jump-table-fixed-ref-pic.s -Wl,-q -o %t +RUN: llvm-bolt %t --relocs -o %t.null 2>&1 | FileCheck %s + +CHECK: BOLT-INFO: fixed indirect branch detected in main diff --git a/bolt/test/X86/register-fragments-bolt-symbols.s b/bolt/test/X86/register-fragments-bolt-symbols.s index fa9b70e0b2d8919a371b609263e03c1d902547bb..6478adf19372b2938061dd36389330e4b6ebe79d 100644 --- a/bolt/test/X86/register-fragments-bolt-symbols.s +++ b/bolt/test/X86/register-fragments-bolt-symbols.s @@ -15,6 +15,8 @@ # PREAGG: B X:0 #chain.cold.0# 1 0 # RUN: perf2bolt %t.bolt -p %t.preagg --pa -o %t.bat.fdata -w %t.bat.yaml -v=1 \ # RUN: | FileCheck %s --check-prefix=CHECK-REGISTER +# RUN: FileCheck --input-file %t.bat.fdata --check-prefix=CHECK-FDATA %s +# RUN: FileCheck --input-file %t.bat.yaml --check-prefix=CHECK-YAML %s # CHECK-SYMS: l df *ABS* [[#]] chain.s # CHECK-SYMS: l F .bolt.org.text [[#]] chain @@ -24,6 +26,9 @@ # CHECK-REGISTER: BOLT-INFO: marking chain.cold.0/1(*2) as a fragment of chain/2(*2) +# CHECK-FDATA: 0 [unknown] 0 1 chain/chain.s/2 10 0 1 +# CHECK-YAML: - name: 'chain/chain.s/2' + .file "chain.s" .text .type chain, @function diff --git a/bolt/test/X86/sctc-bug4.test b/bolt/test/X86/sctc-bug4.test index 00f5ee429b635e089c9bcf0693ba3a0e99fe6f07..92aca5110059f4a7fd5ef9cc43db9e1c22286c35 100644 --- a/bolt/test/X86/sctc-bug4.test +++ b/bolt/test/X86/sctc-bug4.test @@ -1,20 +1,23 @@ -# Check that fallthrough blocks are handled properly. +# Check that fallthrough blocks are handled properly and Offset annotation is +# set for conditional tail calls. RUN: %clang %cflags %S/Inputs/sctc_bug4.s -o %t -RUN: llvm-bolt %t -o %t.null \ +RUN: llvm-bolt %t -o %t.null --enable-bat \ RUN: -funcs=test_func -print-sctc -sequential-disassembly 2>&1 | FileCheck %s CHECK: .Ltmp2 (3 instructions, align : 1) CHECK-NEXT: CFI State : 0 +CHECK-NEXT: Input offset: 0x24 CHECK-NEXT: Predecessors: .LFT1 CHECK-NEXT: 00000024: cmpq $0x20, %rsi -CHECK-NEXT: 00000028: ja dummy # TAILCALL {{.*}}# CTCTakenCount: 0 +CHECK-NEXT: 00000028: ja dummy # TAILCALL # Offset: 53 # CTCTakenCount: 0 CHECK-NEXT: 0000002a: jmp .Ltmp4 CHECK-NEXT: Successors: .Ltmp4 CHECK-NEXT: CFI State: 0 CHECK: .Ltmp1 (2 instructions, align : 1) CHECK-NEXT: CFI State : 0 +CHECK-NEXT: Input offset: 0x2c CHECK-NEXT: Predecessors: .LFT0 CHECK-NEXT: 0000002c: xorq %r11, %rax CHECK-NEXT: 0000002f: retq @@ -22,4 +25,5 @@ CHECK-NEXT: CFI State: 0 CHECK: .Ltmp4 (4 instructions, align : 1) CHECK-NEXT: CFI State : 0 +CHECK-NEXT: Input offset: 0x3a CHECK-NEXT: Predecessors: .Ltmp2 diff --git a/bolt/test/runtime/bolt-reserved.cpp b/bolt/test/runtime/bolt-reserved.cpp index 5e93b4f7c3d40b3a3a142daea1e5d378a04aff9c..c88b1e284d074ee3371ccfa2f28019dbce4b0daa 100644 --- a/bolt/test/runtime/bolt-reserved.cpp +++ b/bolt/test/runtime/bolt-reserved.cpp @@ -16,8 +16,8 @@ * not enough for allocating new sections. */ -// RUN: %clang %s -o %t.exe -Wl,--no-eh-frame-hdr -Wl,-q -DTINY -// RUN: not llvm-bolt %t.exe -o %t.bolt.exe 2>&1 | \ +// RUN: %clang %s -o %t.tiny.exe -Wl,--no-eh-frame-hdr -Wl,-q -DTINY +// RUN: not llvm-bolt %t.tiny.exe -o %t.tiny.bolt.exe 2>&1 | \ // RUN: FileCheck %s --check-prefix=CHECK-TINY // CHECK-TINY: BOLT-ERROR: reserved space (1 byte) is smaller than required diff --git a/clang-tools-extra/clang-query/Query.cpp b/clang-tools-extra/clang-query/Query.cpp index c436d6fa9498688b82ee890b872ce5e2c3595a39..9d5807a52fa8ed4f6612e8ef9f2ce52c3c65cb66 100644 --- a/clang-tools-extra/clang-query/Query.cpp +++ b/clang-tools-extra/clang-query/Query.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "Query.h" +#include "QueryParser.h" #include "QuerySession.h" #include "clang/AST/ASTDumper.h" #include "clang/ASTMatchers/ASTMatchFinder.h" @@ -281,5 +282,26 @@ const QueryKind SetQueryKind::value; const QueryKind SetQueryKind::value; #endif +bool FileQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const { + auto Buffer = llvm::MemoryBuffer::getFile(StringRef{File}.trim()); + if (!Buffer) { + if (Prefix.has_value()) + llvm::errs() << *Prefix << ": "; + llvm::errs() << "cannot open " << File << ": " + << Buffer.getError().message() << "\n"; + return false; + } + + StringRef FileContentRef(Buffer.get()->getBuffer()); + + while (!FileContentRef.empty()) { + QueryRef Q = QueryParser::parse(FileContentRef, QS); + if (!Q->run(llvm::outs(), QS)) + return false; + FileContentRef = Q->RemainingContent; + } + return true; +} + } // namespace query } // namespace clang diff --git a/clang-tools-extra/clang-query/Query.h b/clang-tools-extra/clang-query/Query.h index 7aefa6bb5ee0dd5a991c294ae6a09b5bce213723..7242479633c24f8e94e75ff4ff8e3600e182d2a6 100644 --- a/clang-tools-extra/clang-query/Query.h +++ b/clang-tools-extra/clang-query/Query.h @@ -30,7 +30,8 @@ enum QueryKind { QK_SetTraversalKind, QK_EnableOutputKind, QK_DisableOutputKind, - QK_Quit + QK_Quit, + QK_File }; class QuerySession; @@ -188,6 +189,21 @@ struct DisableOutputQuery : SetNonExclusiveOutputQuery { } }; +struct FileQuery : Query { + FileQuery(StringRef File, StringRef Prefix = StringRef()) + : Query(QK_File), File(File), + Prefix(!Prefix.empty() ? std::optional(Prefix) + : std::nullopt) {} + + bool run(llvm::raw_ostream &OS, QuerySession &QS) const override; + + static bool classof(const Query *Q) { return Q->Kind == QK_File; } + +private: + std::string File; + std::optional Prefix; +}; + } // namespace query } // namespace clang diff --git a/clang-tools-extra/clang-query/QueryParser.cpp b/clang-tools-extra/clang-query/QueryParser.cpp index 162acc1a598dd58f36f5244abf70312b6e1bcd99..85a442bdd7dedab0318d2d881580401989519c92 100644 --- a/clang-tools-extra/clang-query/QueryParser.cpp +++ b/clang-tools-extra/clang-query/QueryParser.cpp @@ -183,7 +183,8 @@ enum ParsedQueryKind { PQK_Unlet, PQK_Quit, PQK_Enable, - PQK_Disable + PQK_Disable, + PQK_File }; enum ParsedQueryVariable { @@ -222,12 +223,14 @@ QueryRef QueryParser::doParse() { .Case("let", PQK_Let) .Case("m", PQK_Match, /*IsCompletion=*/false) .Case("match", PQK_Match) - .Case("q", PQK_Quit, /*IsCompletion=*/false) + .Case("q", PQK_Quit, /*IsCompletion=*/false) .Case("quit", PQK_Quit) .Case("set", PQK_Set) .Case("enable", PQK_Enable) .Case("disable", PQK_Disable) .Case("unlet", PQK_Unlet) + .Case("f", PQK_File, /*IsCompletion=*/false) + .Case("file", PQK_File) .Default(PQK_Invalid); switch (QKind) { @@ -351,6 +354,9 @@ QueryRef QueryParser::doParse() { return endQuery(new LetQuery(Name, VariantValue())); } + case PQK_File: + return new FileQuery(Line); + case PQK_Invalid: return new InvalidQuery("unknown command: " + CommandStr); } diff --git a/clang-tools-extra/clang-query/tool/ClangQuery.cpp b/clang-tools-extra/clang-query/tool/ClangQuery.cpp index da7ac27014480966c13035bd676acb5825339fcb..a2de7a2dced86e1ccb0f118676a658562f07d3ee 100644 --- a/clang-tools-extra/clang-query/tool/ClangQuery.cpp +++ b/clang-tools-extra/clang-query/tool/ClangQuery.cpp @@ -74,22 +74,8 @@ static cl::opt PreloadFile( bool runCommandsInFile(const char *ExeName, std::string const &FileName, QuerySession &QS) { - auto Buffer = llvm::MemoryBuffer::getFile(FileName); - if (!Buffer) { - llvm::errs() << ExeName << ": cannot open " << FileName << ": " - << Buffer.getError().message() << "\n"; - return true; - } - - StringRef FileContentRef(Buffer.get()->getBuffer()); - - while (!FileContentRef.empty()) { - QueryRef Q = QueryParser::parse(FileContentRef, QS); - if (!Q->run(llvm::outs(), QS)) - return true; - FileContentRef = Q->RemainingContent; - } - return false; + FileQuery Query(FileName, ExeName); + return !Query.run(llvm::errs(), QS); } int main(int argc, const char **argv) { diff --git a/clang-tools-extra/clang-tidy/ClangTidy.cpp b/clang-tools-extra/clang-tidy/ClangTidy.cpp index b877ea06dc05cd78b5536155efb25afa4ae32906..1cd7cdd10bc25f2402bb3d71692b017644cf41f6 100644 --- a/clang-tools-extra/clang-tidy/ClangTidy.cpp +++ b/clang-tools-extra/clang-tidy/ClangTidy.cpp @@ -373,11 +373,11 @@ static CheckersList getAnalyzerCheckersAndPackages(ClangTidyContext &Context, const auto &RegisteredCheckers = AnalyzerOptions::getRegisteredCheckers(IncludeExperimental); - bool AnalyzerChecksEnabled = false; - for (StringRef CheckName : RegisteredCheckers) { - std::string ClangTidyCheckName((AnalyzerCheckNamePrefix + CheckName).str()); - AnalyzerChecksEnabled |= Context.isCheckEnabled(ClangTidyCheckName); - } + const bool AnalyzerChecksEnabled = + llvm::any_of(RegisteredCheckers, [&](StringRef CheckName) -> bool { + return Context.isCheckEnabled( + (AnalyzerCheckNamePrefix + CheckName).str()); + }); if (!AnalyzerChecksEnabled) return List; diff --git a/clang-tools-extra/clang-tidy/ClangTidyCheck.cpp b/clang-tools-extra/clang-tidy/ClangTidyCheck.cpp index 710b361e16c0a717b562fa32aee9f332032430cb..6028bb2258136bb7bd8336968ed721bcd081d420 100644 --- a/clang-tools-extra/clang-tidy/ClangTidyCheck.cpp +++ b/clang-tools-extra/clang-tidy/ClangTidyCheck.cpp @@ -171,7 +171,7 @@ std::optional ClangTidyCheck::OptionsView::getEnumInt( if (IgnoreCase) { if (Value.equals_insensitive(NameAndEnum.second)) return NameAndEnum.first; - } else if (Value.equals(NameAndEnum.second)) { + } else if (Value == NameAndEnum.second) { return NameAndEnum.first; } else if (Value.equals_insensitive(NameAndEnum.second)) { Closest = NameAndEnum.second; diff --git a/clang-tools-extra/clang-tidy/bugprone/ForwardingReferenceOverloadCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/ForwardingReferenceOverloadCheck.cpp index e7be8134781e48de409fcd36a0e7d9dfe10a06d1..36687a8e761e85fce8b52494d2c68fb20051c2e5 100644 --- a/clang-tools-extra/clang-tidy/bugprone/ForwardingReferenceOverloadCheck.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/ForwardingReferenceOverloadCheck.cpp @@ -25,8 +25,8 @@ AST_MATCHER(QualType, isEnableIf) { const NamedDecl *TypeDecl = Spec->getTemplateName().getAsTemplateDecl()->getTemplatedDecl(); return TypeDecl->isInStdNamespace() && - (TypeDecl->getName().equals("enable_if") || - TypeDecl->getName().equals("enable_if_t")); + (TypeDecl->getName() == "enable_if" || + TypeDecl->getName() == "enable_if_t"); }; const Type *BaseType = Node.getTypePtr(); // Case: pointer or reference to enable_if. diff --git a/clang-tools-extra/clang-tidy/bugprone/OptionalValueConversionCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/OptionalValueConversionCheck.cpp index 9ab59e6b0474f0b70d5d9654a72df2c230b56cc6..600eab37552766ba1fec108bf83a4d642c9437ff 100644 --- a/clang-tools-extra/clang-tidy/bugprone/OptionalValueConversionCheck.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/OptionalValueConversionCheck.cpp @@ -71,7 +71,9 @@ void OptionalValueConversionCheck::registerMatchers(MatchFinder *Finder) { ofClass(matchers::matchesAnyListedName(OptionalTypes)))), hasType(ConstructTypeMatcher), hasArgument(0U, ignoringImpCasts(anyOf(OptionalDereferenceMatcher, - StdMoveCallMatcher)))) + StdMoveCallMatcher))), + unless(anyOf(hasAncestor(typeLoc()), + hasAncestor(expr(matchers::hasUnevaluatedContext()))))) .bind("expr"), this); } diff --git a/clang-tools-extra/clang-tidy/hicpp/SignedBitwiseCheck.cpp b/clang-tools-extra/clang-tidy/hicpp/SignedBitwiseCheck.cpp index 51cc26400f7f382e915af6751daa3ea6ba494353..bf09a6662d9552e59f37611451e3ab2edf50e244 100644 --- a/clang-tools-extra/clang-tidy/hicpp/SignedBitwiseCheck.cpp +++ b/clang-tools-extra/clang-tidy/hicpp/SignedBitwiseCheck.cpp @@ -9,6 +9,7 @@ #include "SignedBitwiseCheck.h" #include "clang/AST/ASTContext.h" #include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/ASTMatchers/ASTMatchers.h" using namespace clang::ast_matchers; using namespace clang::ast_matchers::internal; @@ -29,8 +30,8 @@ void SignedBitwiseCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { void SignedBitwiseCheck::registerMatchers(MatchFinder *Finder) { const auto SignedIntegerOperand = (IgnorePositiveIntegerLiterals - ? expr(ignoringImpCasts(hasType(isSignedInteger())), - unless(integerLiteral())) + ? expr(ignoringImpCasts( + allOf(hasType(isSignedInteger()), unless(integerLiteral())))) : expr(ignoringImpCasts(hasType(isSignedInteger())))) .bind("signed-operand"); diff --git a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt index 8005d6e91c060c6e9b0e08d8f11cffb094c5fa4e..576805c4c7f1811cca003beca447dd4ab4d8608d 100644 --- a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt @@ -41,6 +41,7 @@ add_clang_library(clangTidyModernizeModule UseNullptrCheck.cpp UseOverrideCheck.cpp UseStartsEndsWithCheck.cpp + UseStdFormatCheck.cpp UseStdNumbersCheck.cpp UseStdPrintCheck.cpp UseTrailingReturnTypeCheck.cpp diff --git a/clang-tools-extra/clang-tidy/modernize/LoopConvertCheck.cpp b/clang-tools-extra/clang-tidy/modernize/LoopConvertCheck.cpp index 3229e302eb4322197e1b1369a7fe901e7168d42b..a1786ba5acfdf5f43117d4dafde939d8043b999a 100644 --- a/clang-tools-extra/clang-tidy/modernize/LoopConvertCheck.cpp +++ b/clang-tools-extra/clang-tidy/modernize/LoopConvertCheck.cpp @@ -421,7 +421,7 @@ getContainerFromBeginEndCall(const Expr *Init, bool IsBegin, bool *IsArrow, return {}; if (IsReverse && !Call->Name.consume_back("r")) return {}; - if (!Call->Name.empty() && !Call->Name.equals("c")) + if (!Call->Name.empty() && Call->Name != "c") return {}; return std::make_pair(Call->Container, Call->CallKind); } diff --git a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp index 776558433c5baa492e8f33637becee5d85775de1..b9c7a2dc383e88360ea3fa6483c6c8c1f9ac7d63 100644 --- a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp @@ -42,6 +42,7 @@ #include "UseNullptrCheck.h" #include "UseOverrideCheck.h" #include "UseStartsEndsWithCheck.h" +#include "UseStdFormatCheck.h" #include "UseStdNumbersCheck.h" #include "UseStdPrintCheck.h" #include "UseTrailingReturnTypeCheck.h" @@ -76,6 +77,7 @@ public: "modernize-use-designated-initializers"); CheckFactories.registerCheck( "modernize-use-starts-ends-with"); + CheckFactories.registerCheck("modernize-use-std-format"); CheckFactories.registerCheck( "modernize-use-std-numbers"); CheckFactories.registerCheck("modernize-use-std-print"); diff --git a/clang-tools-extra/clang-tidy/modernize/UseStdFormatCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStdFormatCheck.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6cef21f1318a2a96fa11a2b5d366dc74b723e824 --- /dev/null +++ b/clang-tools-extra/clang-tidy/modernize/UseStdFormatCheck.cpp @@ -0,0 +1,107 @@ +//===--- UseStdFormatCheck.cpp - clang-tidy -------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "UseStdFormatCheck.h" +#include "../utils/FormatStringConverter.h" +#include "../utils/Matchers.h" +#include "../utils/OptionsUtils.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Lex/Lexer.h" +#include "clang/Tooling/FixIt.h" + +using namespace clang::ast_matchers; + +namespace clang::tidy::modernize { + +namespace { +AST_MATCHER(StringLiteral, isOrdinary) { return Node.isOrdinary(); } +} // namespace + +UseStdFormatCheck::UseStdFormatCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context), + StrictMode(Options.getLocalOrGlobal("StrictMode", false)), + StrFormatLikeFunctions(utils::options::parseStringList( + Options.get("StrFormatLikeFunctions", ""))), + ReplacementFormatFunction( + Options.get("ReplacementFormatFunction", "std::format")), + IncludeInserter(Options.getLocalOrGlobal("IncludeStyle", + utils::IncludeSorter::IS_LLVM), + areDiagsSelfContained()), + MaybeHeaderToInclude(Options.get("FormatHeader")) { + if (StrFormatLikeFunctions.empty()) + StrFormatLikeFunctions.push_back("absl::StrFormat"); + + if (!MaybeHeaderToInclude && ReplacementFormatFunction == "std::format") + MaybeHeaderToInclude = ""; +} + +void UseStdFormatCheck::registerPPCallbacks(const SourceManager &SM, + Preprocessor *PP, + Preprocessor *ModuleExpanderPP) { + IncludeInserter.registerPreprocessor(PP); +} + +void UseStdFormatCheck::registerMatchers(MatchFinder *Finder) { + Finder->addMatcher( + callExpr(argumentCountAtLeast(1), + hasArgument(0, stringLiteral(isOrdinary())), + callee(functionDecl(unless(cxxMethodDecl()), + matchers::matchesAnyListedName( + StrFormatLikeFunctions)) + .bind("func_decl"))) + .bind("strformat"), + this); +} + +void UseStdFormatCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { + using utils::options::serializeStringList; + Options.store(Opts, "StrictMode", StrictMode); + Options.store(Opts, "StrFormatLikeFunctions", + serializeStringList(StrFormatLikeFunctions)); + Options.store(Opts, "ReplacementFormatFunction", ReplacementFormatFunction); + Options.store(Opts, "IncludeStyle", IncludeInserter.getStyle()); + if (MaybeHeaderToInclude) + Options.store(Opts, "FormatHeader", *MaybeHeaderToInclude); +} + +void UseStdFormatCheck::check(const MatchFinder::MatchResult &Result) { + const unsigned FormatArgOffset = 0; + const auto *OldFunction = Result.Nodes.getNodeAs("func_decl"); + const auto *StrFormat = Result.Nodes.getNodeAs("strformat"); + + utils::FormatStringConverter::Configuration ConverterConfig; + ConverterConfig.StrictMode = StrictMode; + utils::FormatStringConverter Converter(Result.Context, StrFormat, + FormatArgOffset, ConverterConfig, + getLangOpts()); + const Expr *StrFormatCall = StrFormat->getCallee(); + if (!Converter.canApply()) { + diag(StrFormat->getBeginLoc(), + "unable to use '%0' instead of %1 because %2") + << StrFormatCall->getSourceRange() << ReplacementFormatFunction + << OldFunction->getIdentifier() + << Converter.conversionNotPossibleReason(); + return; + } + + DiagnosticBuilder Diag = + diag(StrFormatCall->getBeginLoc(), "use '%0' instead of %1") + << ReplacementFormatFunction << OldFunction->getIdentifier(); + Diag << FixItHint::CreateReplacement( + CharSourceRange::getTokenRange(StrFormatCall->getSourceRange()), + ReplacementFormatFunction); + Converter.applyFixes(Diag, *Result.SourceManager); + + if (MaybeHeaderToInclude) + Diag << IncludeInserter.createIncludeInsertion( + Result.Context->getSourceManager().getFileID( + StrFormatCall->getBeginLoc()), + *MaybeHeaderToInclude); +} + +} // namespace clang::tidy::modernize diff --git a/clang-tools-extra/clang-tidy/modernize/UseStdFormatCheck.h b/clang-tools-extra/clang-tidy/modernize/UseStdFormatCheck.h new file mode 100644 index 0000000000000000000000000000000000000000..b59a4708c6e4bc6d492581a90ab50a6bb10a715a --- /dev/null +++ b/clang-tools-extra/clang-tidy/modernize/UseStdFormatCheck.h @@ -0,0 +1,51 @@ +//===--- UseStdFormatCheck.h - clang-tidy -----------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USESTDFORMATCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USESTDFORMATCHECK_H + +#include "../ClangTidyCheck.h" +#include "../utils/IncludeInserter.h" + +namespace clang::tidy::modernize { + +/// Converts calls to absl::StrFormat, or other functions via configuration +/// options, to C++20's std::format, or another function via a configuration +/// option, modifying the format string appropriately and removing +/// now-unnecessary calls to std::string::c_str() and std::string::data(). +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/modernize/use-std-format.html +class UseStdFormatCheck : public ClangTidyCheck { +public: + UseStdFormatCheck(StringRef Name, ClangTidyContext *Context); + bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { + if (ReplacementFormatFunction == "std::format") + return LangOpts.CPlusPlus20; + return LangOpts.CPlusPlus; + } + void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP, + Preprocessor *ModuleExpanderPP) override; + void storeOptions(ClangTidyOptions::OptionMap &Opts) override; + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + std::optional getCheckTraversalKind() const override { + return TK_IgnoreUnlessSpelledInSource; + } + +private: + bool StrictMode; + std::vector StrFormatLikeFunctions; + StringRef ReplacementFormatFunction; + utils::IncludeInserter IncludeInserter; + std::optional MaybeHeaderToInclude; +}; + +} // namespace clang::tidy::modernize + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USESTDFORMATCHECK_H diff --git a/clang-tools-extra/clang-tidy/modernize/UseStdPrintCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStdPrintCheck.cpp index aa60c904a363dacb64bb0f15551171dafcc94d32..ff990feadc0c1d944f786b15eecd3f9d35a9a5be 100644 --- a/clang-tools-extra/clang-tidy/modernize/UseStdPrintCheck.cpp +++ b/clang-tools-extra/clang-tidy/modernize/UseStdPrintCheck.cpp @@ -129,8 +129,11 @@ void UseStdPrintCheck::check(const MatchFinder::MatchResult &Result) { FormatArgOffset = 1; } + utils::FormatStringConverter::Configuration ConverterConfig; + ConverterConfig.StrictMode = StrictMode; + ConverterConfig.AllowTrailingNewlineRemoval = true; utils::FormatStringConverter Converter( - Result.Context, Printf, FormatArgOffset, StrictMode, getLangOpts()); + Result.Context, Printf, FormatArgOffset, ConverterConfig, getLangOpts()); const Expr *PrintfCall = Printf->getCallee(); const StringRef ReplacementFunction = Converter.usePrintNewlineFunction() ? ReplacementPrintlnFunction diff --git a/clang-tools-extra/clang-tidy/readability/ElseAfterReturnCheck.cpp b/clang-tools-extra/clang-tidy/readability/ElseAfterReturnCheck.cpp index 1e85caf688355948562524918c132b9b55861818..2b185e7594addc6ba8f5d3453440dbba8cca7861 100644 --- a/clang-tools-extra/clang-tidy/readability/ElseAfterReturnCheck.cpp +++ b/clang-tools-extra/clang-tidy/readability/ElseAfterReturnCheck.cpp @@ -113,7 +113,7 @@ static bool containsDeclInScope(const Stmt *Node) { } static void removeElseAndBrackets(DiagnosticBuilder &Diag, ASTContext &Context, - const Stmt *Else, SourceLocation ElseLoc) { + const Stmt *Else, SourceLocation ElseLoc) { auto Remap = [&](SourceLocation Loc) { return Context.getSourceManager().getExpansionLoc(Loc); }; @@ -172,7 +172,7 @@ void ElseAfterReturnCheck::registerMatchers(MatchFinder *Finder) { breakStmt().bind(InterruptingStr), cxxThrowExpr().bind(InterruptingStr))); Finder->addMatcher( compoundStmt( - forEach(ifStmt(unless(isConstexpr()), + forEach(ifStmt(unless(isConstexpr()), unless(isConsteval()), hasThen(stmt( anyOf(InterruptsControlFlow, compoundStmt(has(InterruptsControlFlow))))), diff --git a/clang-tools-extra/clang-tidy/readability/IdentifierNamingCheck.cpp b/clang-tools-extra/clang-tidy/readability/IdentifierNamingCheck.cpp index 27a12bfc5806820ddf9882eab2ed6ce8adde1554..c3208392df1566f98b38933a71fe40d9cf26d976 100644 --- a/clang-tools-extra/clang-tidy/readability/IdentifierNamingCheck.cpp +++ b/clang-tools-extra/clang-tidy/readability/IdentifierNamingCheck.cpp @@ -1358,7 +1358,7 @@ IdentifierNamingCheck::getFailureInfo( std::replace(KindName.begin(), KindName.end(), '_', ' '); std::string Fixup = fixupWithStyle(Type, Name, Style, HNOption, ND); - if (StringRef(Fixup).equals(Name)) { + if (StringRef(Fixup) == Name) { if (!IgnoreFailedSplit) { LLVM_DEBUG(Location.print(llvm::dbgs(), SM); llvm::dbgs() diff --git a/clang-tools-extra/clang-tidy/readability/SimplifyBooleanExprCheck.cpp b/clang-tools-extra/clang-tidy/readability/SimplifyBooleanExprCheck.cpp index edb67614bd5585de8fa974f8fb3b129ab6360682..fd4730d9c8b9c8652e92743769bb3bb5df1193c3 100644 --- a/clang-tools-extra/clang-tidy/readability/SimplifyBooleanExprCheck.cpp +++ b/clang-tools-extra/clang-tidy/readability/SimplifyBooleanExprCheck.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "SimplifyBooleanExprCheck.h" +#include "clang/AST/Expr.h" #include "clang/AST/RecursiveASTVisitor.h" #include "clang/Lex/Lexer.h" #include "llvm/Support/SaveAndRestore.h" @@ -280,9 +281,8 @@ public: if (!S) { return true; } - if (Check->IgnoreMacros && S->getBeginLoc().isMacroID()) { + if (Check->canBeBypassed(S)) return false; - } if (!shouldIgnore(S)) StmtStack.push_back(S); return true; @@ -513,17 +513,23 @@ public: return true; } - static bool isUnaryLNot(const Expr *E) { - return isa(E) && + bool isExpectedUnaryLNot(const Expr *E) { + return !Check->canBeBypassed(E) && isa(E) && cast(E)->getOpcode() == UO_LNot; } + bool isExpectedBinaryOp(const Expr *E) { + const auto *BinaryOp = dyn_cast(E); + return !Check->canBeBypassed(E) && BinaryOp && BinaryOp->isLogicalOp() && + BinaryOp->getType()->isBooleanType(); + } + template static bool checkEitherSide(const BinaryOperator *BO, Functor Func) { return Func(BO->getLHS()) || Func(BO->getRHS()); } - static bool nestedDemorgan(const Expr *E, unsigned NestingLevel) { + bool nestedDemorgan(const Expr *E, unsigned NestingLevel) { const auto *BO = dyn_cast(E->IgnoreUnlessSpelledInSource()); if (!BO) return false; @@ -539,15 +545,13 @@ public: return true; case BO_LAnd: case BO_LOr: - if (checkEitherSide(BO, isUnaryLNot)) - return true; - if (NestingLevel) { - if (checkEitherSide(BO, [NestingLevel](const Expr *E) { - return nestedDemorgan(E, NestingLevel - 1); - })) - return true; - } - return false; + return checkEitherSide( + BO, + [this](const Expr *E) { return isExpectedUnaryLNot(E); }) || + (NestingLevel && + checkEitherSide(BO, [this, NestingLevel](const Expr *E) { + return nestedDemorgan(E, NestingLevel - 1); + })); default: return false; } @@ -556,19 +560,19 @@ public: bool TraverseUnaryOperator(UnaryOperator *Op) { if (!Check->SimplifyDeMorgan || Op->getOpcode() != UO_LNot) return Base::TraverseUnaryOperator(Op); - Expr *SubImp = Op->getSubExpr()->IgnoreImplicit(); - auto *Parens = dyn_cast(SubImp); - auto *BinaryOp = - Parens - ? dyn_cast(Parens->getSubExpr()->IgnoreImplicit()) - : dyn_cast(SubImp); - if (!BinaryOp || !BinaryOp->isLogicalOp() || - !BinaryOp->getType()->isBooleanType()) + const Expr *SubImp = Op->getSubExpr()->IgnoreImplicit(); + const auto *Parens = dyn_cast(SubImp); + const Expr *SubExpr = + Parens ? Parens->getSubExpr()->IgnoreImplicit() : SubImp; + if (!isExpectedBinaryOp(SubExpr)) return Base::TraverseUnaryOperator(Op); + const auto *BinaryOp = cast(SubExpr); if (Check->SimplifyDeMorganRelaxed || - checkEitherSide(BinaryOp, isUnaryLNot) || - checkEitherSide(BinaryOp, - [](const Expr *E) { return nestedDemorgan(E, 1); })) { + checkEitherSide( + BinaryOp, + [this](const Expr *E) { return isExpectedUnaryLNot(E); }) || + checkEitherSide( + BinaryOp, [this](const Expr *E) { return nestedDemorgan(E, 1); })) { if (Check->reportDeMorgan(Context, Op, BinaryOp, !IsProcessing, parent(), Parens) && !Check->areDiagsSelfContained()) { @@ -694,6 +698,10 @@ void SimplifyBooleanExprCheck::check(const MatchFinder::MatchResult &Result) { Visitor(this, *Result.Context).traverse(); } +bool SimplifyBooleanExprCheck::canBeBypassed(const Stmt *S) const { + return IgnoreMacros && S->getBeginLoc().isMacroID(); +} + void SimplifyBooleanExprCheck::issueDiag(const ASTContext &Context, SourceLocation Loc, StringRef Description, diff --git a/clang-tools-extra/clang-tidy/readability/SimplifyBooleanExprCheck.h b/clang-tools-extra/clang-tidy/readability/SimplifyBooleanExprCheck.h index ccc6f3d879fc02f4a3698ca67643081c0075ac91..63c3caa01e01a7e43f13eff769db763d9a5c8ce5 100644 --- a/clang-tools-extra/clang-tidy/readability/SimplifyBooleanExprCheck.h +++ b/clang-tools-extra/clang-tidy/readability/SimplifyBooleanExprCheck.h @@ -64,6 +64,8 @@ private: StringRef Description, SourceRange ReplacementRange, StringRef Replacement); + bool canBeBypassed(const Stmt *S) const; + const bool IgnoreMacros; const bool ChainedConditionalReturn; const bool ChainedConditionalAssignment; diff --git a/clang-tools-extra/clang-tidy/readability/StaticAccessedThroughInstanceCheck.cpp b/clang-tools-extra/clang-tidy/readability/StaticAccessedThroughInstanceCheck.cpp index 65356cc3929c54e7a28d2c63e2244d60a53d4dc3..08adc7134cfea2ef2ae2cd413da443612c377696 100644 --- a/clang-tools-extra/clang-tidy/readability/StaticAccessedThroughInstanceCheck.cpp +++ b/clang-tools-extra/clang-tidy/readability/StaticAccessedThroughInstanceCheck.cpp @@ -59,10 +59,6 @@ void StaticAccessedThroughInstanceCheck::check( const Expr *BaseExpr = MemberExpression->getBase(); - // Do not warn for overloaded -> operators. - if (isa(BaseExpr)) - return; - const QualType BaseType = BaseExpr->getType()->isPointerType() ? BaseExpr->getType()->getPointeeType().getUnqualifiedType() @@ -89,17 +85,30 @@ void StaticAccessedThroughInstanceCheck::check( return; SourceLocation MemberExprStartLoc = MemberExpression->getBeginLoc(); - auto Diag = - diag(MemberExprStartLoc, "static member accessed through instance"); - - if (BaseExpr->HasSideEffects(*AstContext) || - getNameSpecifierNestingLevel(BaseType) > NameSpecifierNestingThreshold) - return; + auto CreateFix = [&] { + return FixItHint::CreateReplacement( + CharSourceRange::getCharRange(MemberExprStartLoc, + MemberExpression->getMemberLoc()), + BaseTypeName + "::"); + }; + + { + auto Diag = + diag(MemberExprStartLoc, "static member accessed through instance"); + + if (getNameSpecifierNestingLevel(BaseType) > NameSpecifierNestingThreshold) + return; + + if (!BaseExpr->HasSideEffects(*AstContext, + /* IncludePossibleEffects =*/true)) { + Diag << CreateFix(); + return; + } + } - Diag << FixItHint::CreateReplacement( - CharSourceRange::getCharRange(MemberExprStartLoc, - MemberExpression->getMemberLoc()), - BaseTypeName + "::"); + diag(MemberExprStartLoc, "member base expression may carry some side effects", + DiagnosticIDs::Level::Note) + << BaseExpr->getSourceRange() << CreateFix(); } } // namespace clang::tidy::readability diff --git a/clang-tools-extra/clang-tidy/readability/StringCompareCheck.cpp b/clang-tools-extra/clang-tidy/readability/StringCompareCheck.cpp index 3b5d89c8c647196ec57c779e7a93de23d3052994..7c0bbef3ca0878e758a39c21e31f6b93e2a45157 100644 --- a/clang-tools-extra/clang-tidy/readability/StringCompareCheck.cpp +++ b/clang-tools-extra/clang-tidy/readability/StringCompareCheck.cpp @@ -7,12 +7,15 @@ //===----------------------------------------------------------------------===// #include "StringCompareCheck.h" -#include "../utils/FixItHintUtils.h" +#include "../utils/OptionsUtils.h" #include "clang/AST/ASTContext.h" #include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/ASTMatchers/ASTMatchers.h" #include "clang/Tooling/FixIt.h" +#include "llvm/ADT/StringRef.h" using namespace clang::ast_matchers; +namespace optutils = clang::tidy::utils::options; namespace clang::tidy::readability { @@ -20,11 +23,27 @@ static const StringRef CompareMessage = "do not use 'compare' to test equality " "of strings; use the string equality " "operator instead"; +static const StringRef DefaultStringLikeClasses = "::std::basic_string;" + "::std::basic_string_view"; + +StringCompareCheck::StringCompareCheck(StringRef Name, + ClangTidyContext *Context) + : ClangTidyCheck(Name, Context), + StringLikeClasses(optutils::parseStringList( + Options.get("StringLikeClasses", DefaultStringLikeClasses))) {} + +void StringCompareCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { + Options.store(Opts, "StringLikeClasses", + optutils::serializeStringList(StringLikeClasses)); +} + void StringCompareCheck::registerMatchers(MatchFinder *Finder) { + if (StringLikeClasses.empty()) { + return; + } const auto StrCompare = cxxMemberCallExpr( - callee(cxxMethodDecl(hasName("compare"), - ofClass(classTemplateSpecializationDecl( - hasName("::std::basic_string"))))), + callee(cxxMethodDecl(hasName("compare"), ofClass(cxxRecordDecl(hasAnyName( + StringLikeClasses))))), hasArgument(0, expr().bind("str2")), argumentCountIs(1), callee(memberExpr().bind("str1"))); diff --git a/clang-tools-extra/clang-tidy/readability/StringCompareCheck.h b/clang-tools-extra/clang-tidy/readability/StringCompareCheck.h index 812736d806b71da25c9bc1ac433bbb2c47b544fe..150090901a6e97a139078f7d6c635d9b58804f4c 100644 --- a/clang-tools-extra/clang-tidy/readability/StringCompareCheck.h +++ b/clang-tools-extra/clang-tidy/readability/StringCompareCheck.h @@ -10,6 +10,7 @@ #define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_STRINGCOMPARECHECK_H #include "../ClangTidyCheck.h" +#include namespace clang::tidy::readability { @@ -20,13 +21,18 @@ namespace clang::tidy::readability { /// http://clang.llvm.org/extra/clang-tidy/checks/readability/string-compare.html class StringCompareCheck : public ClangTidyCheck { public: - StringCompareCheck(StringRef Name, ClangTidyContext *Context) - : ClangTidyCheck(Name, Context) {} + StringCompareCheck(StringRef Name, ClangTidyContext *Context); + bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { return LangOpts.CPlusPlus; } + void registerMatchers(ast_matchers::MatchFinder *Finder) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + void storeOptions(ClangTidyOptions::OptionMap &Opts) override; + +private: + const std::vector StringLikeClasses; }; } // namespace clang::tidy::readability diff --git a/clang-tools-extra/clang-tidy/readability/SuspiciousCallArgumentCheck.cpp b/clang-tools-extra/clang-tidy/readability/SuspiciousCallArgumentCheck.cpp index 3eb80019ae753e69eacdbd53efffba7de747d3c6..18420d0c8488d2192b23e9033c7827a88021fdc9 100644 --- a/clang-tools-extra/clang-tidy/readability/SuspiciousCallArgumentCheck.cpp +++ b/clang-tools-extra/clang-tidy/readability/SuspiciousCallArgumentCheck.cpp @@ -138,11 +138,11 @@ static bool applyAbbreviationHeuristic( const llvm::StringMap &AbbreviationDictionary, StringRef Arg, StringRef Param) { if (AbbreviationDictionary.contains(Arg) && - Param.equals(AbbreviationDictionary.lookup(Arg))) + Param == AbbreviationDictionary.lookup(Arg)) return true; if (AbbreviationDictionary.contains(Param) && - Arg.equals(AbbreviationDictionary.lookup(Param))) + Arg == AbbreviationDictionary.lookup(Param)) return true; return false; diff --git a/clang-tools-extra/clang-tidy/utils/FormatStringConverter.cpp b/clang-tools-extra/clang-tidy/utils/FormatStringConverter.cpp index ad10f745b6acfb704ac5f7dfefd8ac2c1781d365..845e71c5003b80bbdb2cfc8ecb8bb9751f04dd2f 100644 --- a/clang-tools-extra/clang-tidy/utils/FormatStringConverter.cpp +++ b/clang-tools-extra/clang-tidy/utils/FormatStringConverter.cpp @@ -198,10 +198,11 @@ static bool castMismatchedIntegerTypes(const CallExpr *Call, bool StrictMode) { FormatStringConverter::FormatStringConverter(ASTContext *ContextIn, const CallExpr *Call, unsigned FormatArgOffset, - bool StrictMode, + const Configuration ConfigIn, const LangOptions &LO) - : Context(ContextIn), - CastMismatchedIntegerTypes(castMismatchedIntegerTypes(Call, StrictMode)), + : Context(ContextIn), Config(ConfigIn), + CastMismatchedIntegerTypes( + castMismatchedIntegerTypes(Call, ConfigIn.StrictMode)), Args(Call->getArgs()), NumArgs(Call->getNumArgs()), ArgsOffset(FormatArgOffset + 1), LangOpts(LO) { assert(ArgsOffset <= NumArgs); @@ -627,9 +628,12 @@ void FormatStringConverter::finalizeFormatText() { // It's clearer to convert printf("Hello\r\n"); to std::print("Hello\r\n") // than to std::println("Hello\r"); - if (StringRef(StandardFormatString).ends_with("\\n") && - !StringRef(StandardFormatString).ends_with("\\\\n") && - !StringRef(StandardFormatString).ends_with("\\r\\n")) { + // Use StringRef until C++20 std::string::ends_with() is available. + const auto StandardFormatStringRef = StringRef(StandardFormatString); + if (Config.AllowTrailingNewlineRemoval && + StandardFormatStringRef.ends_with("\\n") && + !StandardFormatStringRef.ends_with("\\\\n") && + !StandardFormatStringRef.ends_with("\\r\\n")) { UsePrintNewlineFunction = true; FormatStringNeededRewriting = true; StandardFormatString.erase(StandardFormatString.end() - 2, diff --git a/clang-tools-extra/clang-tidy/utils/FormatStringConverter.h b/clang-tools-extra/clang-tidy/utils/FormatStringConverter.h index 1949870f62ed68c181dbeddfecf8f2644a1e8804..1109a0b602262fe9793de1e30423aee2565cdbd5 100644 --- a/clang-tools-extra/clang-tidy/utils/FormatStringConverter.h +++ b/clang-tools-extra/clang-tidy/utils/FormatStringConverter.h @@ -32,8 +32,14 @@ class FormatStringConverter public: using ConversionSpecifier = clang::analyze_format_string::ConversionSpecifier; using PrintfSpecifier = analyze_printf::PrintfSpecifier; + + struct Configuration { + bool StrictMode = false; + bool AllowTrailingNewlineRemoval = false; + }; + FormatStringConverter(ASTContext *Context, const CallExpr *Call, - unsigned FormatArgOffset, bool StrictMode, + unsigned FormatArgOffset, Configuration Config, const LangOptions &LO); bool canApply() const { return ConversionNotPossibleReason.empty(); } @@ -45,6 +51,7 @@ public: private: ASTContext *Context; + const Configuration Config; const bool CastMismatchedIntegerTypes; const Expr *const *Args; const unsigned NumArgs; diff --git a/clang-tools-extra/clang-tidy/utils/IncludeSorter.cpp b/clang-tools-extra/clang-tidy/utils/IncludeSorter.cpp index a44720c47eca2d7eb9ef815d7cea3dc18a44c616..0fa54b3847ebc22f6cbcfc90ecfa1a63cab10a91 100644 --- a/clang-tools-extra/clang-tidy/utils/IncludeSorter.cpp +++ b/clang-tools-extra/clang-tidy/utils/IncludeSorter.cpp @@ -88,8 +88,7 @@ determineIncludeKind(StringRef CanonicalFile, StringRef IncludeFile, if (FileCopy.consume_front(Parts.first) && FileCopy.consume_back(Parts.second)) { // Determine the kind of this inclusion. - if (FileCopy.equals("/internal/") || - FileCopy.equals("/proto/")) { + if (FileCopy == "/internal/" || FileCopy == "/proto/") { return IncludeSorter::IK_MainTUInclude; } } diff --git a/clang-tools-extra/clang-tidy/utils/RenamerClangTidyCheck.cpp b/clang-tools-extra/clang-tidy/utils/RenamerClangTidyCheck.cpp index f5ed617365403a17131a10ed9dca7e4ae760500b..e811f5519de2c136c2789aeb26fb569f2095832c 100644 --- a/clang-tools-extra/clang-tidy/utils/RenamerClangTidyCheck.cpp +++ b/clang-tools-extra/clang-tidy/utils/RenamerClangTidyCheck.cpp @@ -86,7 +86,7 @@ static const NamedDecl *findDecl(const RecordDecl &RecDecl, StringRef DeclName) { for (const Decl *D : RecDecl.decls()) { if (const auto *ND = dyn_cast(D)) { - if (ND->getDeclName().isIdentifier() && ND->getName().equals(DeclName)) + if (ND->getDeclName().isIdentifier() && ND->getName() == DeclName) return ND; } } diff --git a/clang-tools-extra/clangd/Preamble.cpp b/clang-tools-extra/clangd/Preamble.cpp index d5818e0ca309b03fafadc20f1fbdfe4f902ab615..ecd490145dd3c4583443b2978a018dc128f220f0 100644 --- a/clang-tools-extra/clangd/Preamble.cpp +++ b/clang-tools-extra/clangd/Preamble.cpp @@ -918,7 +918,9 @@ void PreamblePatch::apply(CompilerInvocation &CI) const { // no guarantees around using arbitrary options when reusing PCHs, and // different target opts can result in crashes, see // ParsedASTTest.PreambleWithDifferentTarget. - CI.TargetOpts = Baseline->TargetOpts; + // Make sure this is a deep copy, as the same Baseline might be used + // concurrently. + *CI.TargetOpts = *Baseline->TargetOpts; // No need to map an empty file. if (PatchContents.empty()) diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index 5d6d0351362e405bfa9a0f2a6989e389bbb86c68..898c7acc1310e4718aac38606cdd2e87358798a1 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -90,7 +90,9 @@ Improvements to clang-doc Improvements to clang-query --------------------------- -The improvements are... +- Added the `file` command to dynamically load a list of commands and matchers + from an external file, allowing the cost of reading the compilation database + and building the AST to be imposed just once for faster prototyping. Improvements to clang-rename ---------------------------- @@ -148,6 +150,15 @@ New checks Finds initializer lists for aggregate types that could be written as designated initializers instead. +- New :doc:`modernize-use-std-format + ` check. + + Converts calls to ``absl::StrFormat``, or other functions via + configuration options, to C++20's ``std::format``, or another function + via a configuration option, modifying the format string appropriately and + removing now-unnecessary calls to ``std::string::c_str()`` and + ``std::string::data()``. + - New :doc:`readability-enum-initial-value ` check. @@ -202,6 +213,10 @@ Changes in existing checks eliminating false positives resulting from direct usage of bitwise operators within parentheses. +- Improved :doc:`bugprone-optional-value-conversion + ` check by eliminating + false positives resulting from use of optionals in unevaluated context. + - Improved :doc:`bugprone-suspicious-include ` check by replacing the local options `HeaderFileExtensions` and `ImplementationFileExtensions` by the @@ -259,6 +274,10 @@ Changes in existing checks - Improved :doc:`google-runtime-int ` check performance through optimizations. +- Improved :doc:`hicpp-signed-bitwise ` + check by ignoring false positives involving positive integer literals behind + implicit casts when `IgnorePositiveIntegerLiterals` is enabled. + - Improved :doc:`hicpp-ignored-remove-result ` check by ignoring other functions with same prefixes as the target specific functions. @@ -333,6 +352,10 @@ Changes in existing checks ` check by excluding include directives that form the filename using macro. +- Improved :doc:`readability-else-after-return + ` check to ignore + `if consteval` statements, for which the `else` branch must not be removed. + - Improved :doc:`readability-identifier-naming ` check in `GetConfigPerFile` mode by resolving symbolic links to header files. Fixed handling of Hungarian @@ -348,11 +371,25 @@ Changes in existing checks ` check to properly emit warnings for static data member with an in-class initializer. +- Improved :doc:`readability-static-accessed-through-instance + ` check to + support calls to overloaded operators as base expression and provide fixes to + expressions with side-effects. + +- Improved :doc:`readability-simplify-boolean-expr + ` check to avoid to emit + warning for macro when IgnoreMacro option is enabled. + - Improved :doc:`readability-static-definition-in-anonymous-namespace ` check by resolving fix-it overlaps in template code by disregarding implicit instances. +- Improved :doc:`readability-string-compare + ` check to also detect + usages of ``std::string_view::compare``. Added a `StringLikeClasses` option + to detect usages of ``compare`` method in custom string-like classes. + Removed checks ^^^^^^^^^^^^^^ diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst index 046a5ff57ad1c9c6800d7dddfe39e359d2c2bbf1..85e4f0352ac22b2b4e23cc36eb6b14814b48a08b 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/list.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -300,6 +300,7 @@ Clang-Tidy Checks :doc:`modernize-use-nullptr `, "Yes" :doc:`modernize-use-override `, "Yes" :doc:`modernize-use-starts-ends-with `, "Yes" + :doc:`modernize-use-std-format `, "Yes" :doc:`modernize-use-std-numbers `, "Yes" :doc:`modernize-use-std-print `, "Yes" :doc:`modernize-use-trailing-return-type `, "Yes" diff --git a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-std-format.rst b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-std-format.rst new file mode 100644 index 0000000000000000000000000000000000000000..a1599f0fc58fe68df702d446ffd6cb6523e084ba --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-std-format.rst @@ -0,0 +1,84 @@ +.. title:: clang-tidy - modernize-use-std-format + +modernize-use-std-format +======================== + +Converts calls to ``absl::StrFormat``, or other functions via +configuration options, to C++20's ``std::format``, or another function +via a configuration option, modifying the format string appropriately and +removing now-unnecessary calls to ``std::string::c_str()`` and +``std::string::data()``. + +For example, it turns lines like + +.. code-block:: c++ + + return absl::StrFormat("The %s is %3d", description.c_str(), value); + +into: + +.. code-block:: c++ + + return std::format("The {} is {:3}", description, value); + +The check uses the same format-string-conversion algorithm as +`modernize-use-std-print <../modernize/use-std-print.html>`_ and its +shortcomings are described in the documentation for that check. + +Options +------- + +.. option:: StrictMode + + When `true`, the check will add casts when converting from variadic + functions and printing signed or unsigned integer types (including + fixed-width integer types from ````, ``ptrdiff_t``, ``size_t`` + and ``ssize_t``) as the opposite signedness to ensure that the output + would matches that of a simple wrapper for ``std::sprintf`` that + accepted a C-style variable argument list. For example, with + `StrictMode` enabled, + + .. code-block:: c++ + + extern std::string strprintf(const char *format, ...); + int i = -42; + unsigned int u = 0xffffffff; + return strprintf("%d %u\n", i, u); + + would be converted to + + .. code-block:: c++ + + return std::format("{} {}\n", static_cast(i), static_cast(u)); + + to ensure that the output will continue to be the unsigned representation + of -42 and the signed representation of 0xffffffff (often 4294967254 + and -1 respectively). When `false` (which is the default), these casts + will not be added which may cause a change in the output. Note that this + option makes no difference for the default value of + `StrFormatLikeFunctions` since ``absl::StrFormat`` takes a function + parameter pack and is not a variadic function. + +.. option:: StrFormatLikeFunctions + + A semicolon-separated list of (fully qualified) function names to + replace, with the requirement that the first parameter contains the + printf-style format string and the arguments to be formatted follow + immediately afterwards. The default value for this option is + `absl::StrFormat`. + +.. option:: ReplacementFormatFunction + + The function that will be used to replace the function set by the + `StrFormatLikeFunctions` option rather than the default + `std::format`. It is expected that the function provides an interface + that is compatible with ``std::format``. A suitable candidate would be + `fmt::format`. + +.. option:: FormatHeader + + The header that must be included for the declaration of + `ReplacementFormatFunction` so that a ``#include`` directive can be added if + required. If `ReplacementFormatFunction` is `std::format` then this option will + default to ````, otherwise this option will default to nothing + and no ``#include`` directive will be added. diff --git a/clang-tools-extra/docs/clang-tidy/checks/readability/static-accessed-through-instance.rst b/clang-tools-extra/docs/clang-tidy/checks/readability/static-accessed-through-instance.rst index 23d12f418366402bb1d97517e19a8c04cbd354fb..ffb3738bf72c92aff474e7bed546843c345f4bb9 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/readability/static-accessed-through-instance.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/readability/static-accessed-through-instance.rst @@ -35,3 +35,6 @@ is changed to: C::E1; C::E2; +The `--fix` commandline option provides default support for safe fixes, whereas +`--fix-notes` enables fixes that may replace expressions with side effects, +potentially altering the program's behavior. diff --git a/clang-tools-extra/docs/clang-tidy/checks/readability/string-compare.rst b/clang-tools-extra/docs/clang-tidy/checks/readability/string-compare.rst index 268632eee61a278ce7899ccc81c1163c5b07879b..4be2473bed2d74808e609e935295b78425077282 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/readability/string-compare.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/readability/string-compare.rst @@ -14,10 +14,12 @@ recommended to avoid the risk of incorrect interpretation of the return value and to simplify the code. The string equality and inequality operators can also be faster than the ``compare`` method due to early termination. -Examples: +Example +------- .. code-block:: c++ + // The same rules apply to std::string_view. std::string str1{"a"}; std::string str2{"b"}; @@ -50,5 +52,36 @@ Examples: } The above code examples show the list of if-statements that this check will -give a warning for. All of them uses ``compare`` to check if equality or +give a warning for. All of them use ``compare`` to check equality or inequality of two strings instead of using the correct operators. + +Options +------- + +.. option:: StringLikeClasses + + A string containing semicolon-separated names of string-like classes. + By default contains only ``::std::basic_string`` + and ``::std::basic_string_view``. If a class from this list has + a ``compare`` method similar to that of ``std::string``, it will be checked + in the same way. + +Example +^^^^^^^ + +.. code-block:: c++ + + struct CustomString { + public: + int compare (const CustomString& other) const; + } + + CustomString str1; + CustomString str2; + + // use str1 != str2 instead. + if (str1.compare(str2)) { + } + +If `StringLikeClasses` contains ``CustomString``, the check will suggest +replacing ``compare`` with equality operator. diff --git a/clang-tools-extra/test/clang-query/Inputs/empty.script b/clang-tools-extra/test/clang-query/Inputs/empty.script new file mode 100644 index 0000000000000000000000000000000000000000..3c30abd1ae5d1526771c5a4ce09674ea6ba01c56 --- /dev/null +++ b/clang-tools-extra/test/clang-query/Inputs/empty.script @@ -0,0 +1 @@ +# This file intentionally has no queries diff --git a/clang-tools-extra/test/clang-query/Inputs/file.script b/clang-tools-extra/test/clang-query/Inputs/file.script new file mode 100644 index 0000000000000000000000000000000000000000..b58e7bbc24bfb917e979c1946e9efae7dfa40f97 --- /dev/null +++ b/clang-tools-extra/test/clang-query/Inputs/file.script @@ -0,0 +1 @@ +f DIRECTORY/runtime_file.script diff --git a/clang-tools-extra/test/clang-query/Inputs/runtime_file.script b/clang-tools-extra/test/clang-query/Inputs/runtime_file.script new file mode 100644 index 0000000000000000000000000000000000000000..714d7f03b1bf6aaa4dc6b40d1c048de29cfd7f20 --- /dev/null +++ b/clang-tools-extra/test/clang-query/Inputs/runtime_file.script @@ -0,0 +1,5 @@ +set bind-root false + +l func functionDecl(hasName("bar")) +m func.bind("f") +m varDecl().bind("v") \ No newline at end of file diff --git a/clang-tools-extra/test/clang-query/errors.c b/clang-tools-extra/test/clang-query/errors.c index bbb742125744f9bc96ad36b22302fa794a67332b..3b9059ab0257f4448a525ee60ad5cb1f2f1cf0c3 100644 --- a/clang-tools-extra/test/clang-query/errors.c +++ b/clang-tools-extra/test/clang-query/errors.c @@ -1,10 +1,12 @@ // RUN: not clang-query -c foo -c bar %s -- | FileCheck %s // RUN: not clang-query -f %S/Inputs/foo.script %s -- | FileCheck %s // RUN: not clang-query -f %S/Inputs/nonexistent.script %s -- 2>&1 | FileCheck --check-prefix=CHECK-NONEXISTENT %s +// RUN: not clang-query -c 'file %S/Inputs/nonexistent.script' %s -- 2>&1 | FileCheck --check-prefix=CHECK-NONEXISTENT-FILEQUERY %s // RUN: not clang-query -c foo -f foo %s -- 2>&1 | FileCheck --check-prefix=CHECK-BOTH %s // CHECK: unknown command: foo // CHECK-NOT: unknown command: bar // CHECK-NONEXISTENT: cannot open {{.*}}nonexistent.script +// CHECK-NONEXISTENT-FILEQUERY: cannot open {{.*}}nonexistent.script // CHECK-BOTH: cannot specify both -c and -f diff --git a/clang-tools-extra/test/clang-query/file-empty.c b/clang-tools-extra/test/clang-query/file-empty.c new file mode 100644 index 0000000000000000000000000000000000000000..15137c57e915ea561e85201d8427021f18bc4336 --- /dev/null +++ b/clang-tools-extra/test/clang-query/file-empty.c @@ -0,0 +1,2 @@ +// RUN: clang-query -c 'file %S/Inputs/empty.script' %s -- +// COM: no output expected; nothing to CHECK diff --git a/clang-tools-extra/test/clang-query/file-query.c b/clang-tools-extra/test/clang-query/file-query.c new file mode 100644 index 0000000000000000000000000000000000000000..10a44e7aaccf24f14d6668620c7ed1251d3fa7ca --- /dev/null +++ b/clang-tools-extra/test/clang-query/file-query.c @@ -0,0 +1,14 @@ +// RUN: rm -rf %/t +// RUN: mkdir %/t +// RUN: cp %/S/Inputs/file.script %/t/file.script +// RUN: cp %/S/Inputs/runtime_file.script %/t/runtime_file.script +// Need to embed the correct temp path in the actual JSON-RPC requests. +// RUN: sed -e "s|DIRECTORY|%/t|" %/t/file.script > %/t/file.script.temp + +// RUN: clang-query -c 'file %/t/file.script.temp' %s -- | FileCheck %s + +// CHECK: file-query.c:11:1: note: "f" binds here +void bar(void) {} + +// CHECK: file-query.c:14:1: note: "v" binds here +int baz{1}; diff --git a/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/string b/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/string index d031f27beb9dfef99d61b44bff19a6dc8e1a885f..0c160bc182b6ebdf4ddce7f460ad38c8fd8a99a4 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/string +++ b/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/string @@ -108,6 +108,8 @@ struct basic_string_view { constexpr bool starts_with(C ch) const noexcept; constexpr bool starts_with(const C* s) const; + constexpr int compare(basic_string_view sv) const noexcept; + static constexpr size_t npos = -1; }; @@ -132,6 +134,14 @@ bool operator==(const std::wstring&, const std::wstring&); bool operator==(const std::wstring&, const wchar_t*); bool operator==(const wchar_t*, const std::wstring&); +bool operator==(const std::string_view&, const std::string_view&); +bool operator==(const std::string_view&, const char*); +bool operator==(const char*, const std::string_view&); + +bool operator!=(const std::string_view&, const std::string_view&); +bool operator!=(const std::string_view&, const char*); +bool operator!=(const char*, const std::string_view&); + size_t strlen(const char* str); } diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/optional-value-conversion.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/optional-value-conversion.cpp index 72ef35c956d2e868849e9bd679e7d09d7d8022b0..1228d64bb6909e93da78943e81a6422da1fc2202 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/optional-value-conversion.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/optional-value-conversion.cpp @@ -210,4 +210,6 @@ void correct(std::optional param) std::optional* p2 = &p; takeOptionalValue(p2->value_or(5U)); takeOptionalRef(p2->value_or(5U)); + + using Type = decltype(takeOptionalValue(*param)); } diff --git a/clang-tools-extra/test/clang-tidy/checkers/hicpp/signed-bitwise-integer-literals.cpp b/clang-tools-extra/test/clang-tidy/checkers/hicpp/signed-bitwise-integer-literals.cpp index edbb56f90cb0e1ea421676b90dd4444b8a638184..aca7ae1fd76fbe6172c9d5b264703cc1d44c0450 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/hicpp/signed-bitwise-integer-literals.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/hicpp/signed-bitwise-integer-literals.cpp @@ -11,6 +11,7 @@ void examples() { // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: use of a signed integer operand with a binary bitwise operator unsigned URes2 = URes << 1; //Ok + unsigned URes3 = URes & 1; //Ok int IResult; IResult = 10 & 2; //Ok @@ -21,6 +22,8 @@ void examples() { IResult = Int << 1; // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: use of a signed integer operand with a binary bitwise operator IResult = ~0; //Ok + IResult = -1 & 1; + // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: use of a signed integer operand with a binary bitwise operator [hicpp-signed-bitwise] } enum EnumConstruction { diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-format-custom.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-format-custom.cpp new file mode 100644 index 0000000000000000000000000000000000000000..815e22b29155153b392a34e5238fc8deba9bb6a9 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-format-custom.cpp @@ -0,0 +1,52 @@ +// RUN: %check_clang_tidy -check-suffixes=,STRICT \ +// RUN: -std=c++20 %s modernize-use-std-format %t -- \ +// RUN: -config="{CheckOptions: { \ +// RUN: modernize-use-std-format.StrictMode: true, \ +// RUN: modernize-use-std-format.StrFormatLikeFunctions: '::strprintf; mynamespace::strprintf2', \ +// RUN: modernize-use-std-format.ReplacementFormatFunction: 'fmt::format', \ +// RUN: modernize-use-std-format.FormatHeader: '' \ +// RUN: }}" \ +// RUN: -- -isystem %clang_tidy_headers +// RUN: %check_clang_tidy -check-suffixes=,NOTSTRICT \ +// RUN: -std=c++20 %s modernize-use-std-format %t -- \ +// RUN: -config="{CheckOptions: { \ +// RUN: modernize-use-std-format.StrFormatLikeFunctions: '::strprintf; mynamespace::strprintf2', \ +// RUN: modernize-use-std-format.ReplacementFormatFunction: 'fmt::format', \ +// RUN: modernize-use-std-format.FormatHeader: '' \ +// RUN: }}" \ +// RUN: -- -isystem %clang_tidy_headers + +#include +#include +// CHECK-FIXES: #include + +std::string strprintf(const char *, ...); + +namespace mynamespace { + std::string strprintf2(const char *, ...); +} + +std::string strprintf_test(const std::string &name, double value) { + return strprintf("'%s'='%f'\n", name.c_str(), value); + // CHECK-MESSAGES: [[@LINE-1]]:10: warning: use 'fmt::format' instead of 'strprintf' [modernize-use-std-format] + // CHECK-FIXES: return fmt::format("'{}'='{:f}'\n", name, value); + + return mynamespace::strprintf2("'%s'='%f'\n", name.c_str(), value); + // CHECK-MESSAGES: [[@LINE-1]]:10: warning: use 'fmt::format' instead of 'strprintf2' [modernize-use-std-format] + // CHECK-FIXES: return fmt::format("'{}'='{:f}'\n", name, value); +} + +std::string StrFormat_strict_conversion() { + const unsigned char uc = 'A'; + return strprintf("Integer %hhd from unsigned char\n", uc); + // CHECK-MESSAGES: [[@LINE-1]]:10: warning: use 'fmt::format' instead of 'strprintf' [modernize-use-std-format] + // CHECK-FIXES-NOTSTRICT: return fmt::format("Integer {} from unsigned char\n", uc); + // CHECK-FIXES-STRICT: return fmt::format("Integer {} from unsigned char\n", static_cast(uc)); +} + +// Ensure that MatchesAnyListedNameMatcher::NameMatcher::match() can cope with a +// NamedDecl that has no name when we're trying to match unqualified_strprintf. +std::string A(const std::string &in) +{ + return "_" + in; +} diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-format-fmt.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-format-fmt.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9d136cf309168d5eac9bd00468e817b902fe6d71 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-format-fmt.cpp @@ -0,0 +1,24 @@ +// RUN: %check_clang_tidy %s modernize-use-std-format %t -- \ +// RUN: -config="{CheckOptions: { \ +// RUN: StrictMode: true, \ +// RUN: modernize-use-std-format.StrFormatLikeFunctions: 'fmt::sprintf', \ +// RUN: modernize-use-std-format.ReplacementFormatFunction: 'fmt::format', \ +// RUN: modernize-use-std-format.FormatHeader: '' \ +// RUN: }}" \ +// RUN: -- -isystem %clang_tidy_headers + +// CHECK-FIXES: #include +#include + +namespace fmt +{ +// Use const char * for the format since the real type is hard to mock up. +template +std::string sprintf(const char *format, const Args&... args); +} // namespace fmt + +std::string fmt_sprintf_simple() { + return fmt::sprintf("Hello %s %d", "world", 42); + // CHECK-MESSAGES: [[@LINE-1]]:10: warning: use 'fmt::format' instead of 'sprintf' [modernize-use-std-format] + // CHECK-FIXES: fmt::format("Hello {} {}", "world", 42); +} diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-format.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-format.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e8dea1dce2c97222c790adc29504db76cc2c432e --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-format.cpp @@ -0,0 +1,120 @@ +// RUN: %check_clang_tidy \ +// RUN: -std=c++20 %s modernize-use-std-format %t -- \ +// RUN: -config="{CheckOptions: {StrictMode: true}}" \ +// RUN: -- -isystem %clang_tidy_headers +// RUN: %check_clang_tidy \ +// RUN: -std=c++20 %s modernize-use-std-format %t -- \ +// RUN: -config="{CheckOptions: {StrictMode: false}}" \ +// RUN: -- -isystem %clang_tidy_headers +#include +// CHECK-FIXES: #include + +namespace absl +{ +// Use const char * for the format since the real type is hard to mock up. +template +std::string StrFormat(const char *format, const Args&... args); +} // namespace absl + +template +struct iterator { + T *operator->(); + T &operator*(); +}; + +std::string StrFormat_simple() { + return absl::StrFormat("Hello"); + // CHECK-MESSAGES: [[@LINE-1]]:10: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format] + // CHECK-FIXES: return std::format("Hello"); +} + +std::string StrFormat_complex(const char *name, double value) { + return absl::StrFormat("'%s'='%f'", name, value); + // CHECK-MESSAGES: [[@LINE-1]]:10: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format] + // CHECK-FIXES: return std::format("'{}'='{:f}'", name, value); +} + +std::string StrFormat_integer_conversions() { + return absl::StrFormat("int:%d int:%d char:%c char:%c", 65, 'A', 66, 'B'); + // CHECK-MESSAGES: [[@LINE-1]]:10: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format] + // CHECK-FIXES: return std::format("int:{} int:{:d} char:{:c} char:{}", 65, 'A', 66, 'B'); +} + +// FormatConverter is capable of removing newlines from the end of the format +// string. Ensure that isn't incorrectly happening for std::format. +std::string StrFormat_no_newline_removal() { + return absl::StrFormat("a line\n"); + // CHECK-MESSAGES: [[@LINE-1]]:10: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format] + // CHECK-FIXES: return std::format("a line\n"); +} + +// FormatConverter is capable of removing newlines from the end of the format +// string. Ensure that isn't incorrectly happening for std::format. +std::string StrFormat_cstr_removal(const std::string &s1, const std::string *s2) { + return absl::StrFormat("%s %s %s %s", s1.c_str(), s1.data(), s2->c_str(), s2->data()); + // CHECK-MESSAGES: [[@LINE-1]]:10: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format] + // CHECK-FIXES: return std::format("{} {} {} {}", s1, s1, *s2, *s2); +} + +std::string StrFormat_strict_conversion() { + const unsigned char uc = 'A'; + return absl::StrFormat("Integer %hhd from unsigned char\n", uc); + // CHECK-MESSAGES: [[@LINE-1]]:10: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format] + // CHECK-FIXES: return std::format("Integer {} from unsigned char\n", uc); +} + +std::string StrFormat_field_width_and_precision() { + auto s1 = absl::StrFormat("width only:%*d width and precision:%*.*f precision only:%.*f", 3, 42, 4, 2, 3.14159265358979323846, 5, 2.718); + // CHECK-MESSAGES: [[@LINE-1]]:13: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format] + // CHECK-FIXES: std::format("width only:{:{}} width and precision:{:{}.{}f} precision only:{:.{}f}", 42, 3, 3.14159265358979323846, 4, 2, 2.718, 5); + + auto s2 = absl::StrFormat("width and precision positional:%1$*2$.*3$f after", 3.14159265358979323846, 4, 2); + // CHECK-MESSAGES: [[@LINE-1]]:13: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format] + // CHECK-FIXES: std::format("width and precision positional:{0:{1}.{2}f} after", 3.14159265358979323846, 4, 2); + + const int width = 10, precision = 3; + const unsigned int ui1 = 42, ui2 = 43, ui3 = 44; + auto s3 = absl::StrFormat("casts width only:%*d width and precision:%*.*d precision only:%.*d\n", 3, ui1, 4, 2, ui2, 5, ui3); + // CHECK-MESSAGES: [[@LINE-1]]:13: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format] + // CHECK-FIXES-NOTSTRICT: std::format("casts width only:{:{}} width and precision:{:{}.{}} precision only:{:.{}}", ui1, 3, ui2, 4, 2, ui3, 5); + // CHECK-FIXES-STRICT: std::format("casts width only:{:{}} width and precision:{:{}.{}} precision only:{:.{}}", static_cast(ui1), 3, static_cast(ui2), 4, 2, static_cast(ui3), 5); + + auto s4 = absl::StrFormat("c_str removal width only:%*s width and precision:%*.*s precision only:%.*s", 3, s1.c_str(), 4, 2, s2.c_str(), 5, s3.c_str()); + // CHECK-MESSAGES: [[@LINE-1]]:13: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format] + // CHECK-FIXES: std::format("c_str removal width only:{:>{}} width and precision:{:>{}.{}} precision only:{:.{}}", s1, 3, s2, 4, 2, s3, 5); + + const std::string *ps1 = &s1, *ps2 = &s2, *ps3 = &s3; + auto s5 = absl::StrFormat("c_str() removal pointer width only:%-*s width and precision:%-*.*s precision only:%-.*s", 3, ps1->c_str(), 4, 2, ps2->c_str(), 5, ps3->c_str()); + // CHECK-MESSAGES: [[@LINE-1]]:13: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format] + // CHECK-FIXES: std::format("c_str() removal pointer width only:{:{}} width and precision:{:{}.{}} precision only:{:.{}}", *ps1, 3, *ps2, 4, 2, *ps3, 5); + + iterator is1, is2, is3; + auto s6 = absl::StrFormat("c_str() removal iterator width only:%-*s width and precision:%-*.*s precision only:%-.*s", 3, is1->c_str(), 4, 2, is2->c_str(), 5, is3->c_str()); + // CHECK-MESSAGES: [[@LINE-1]]:13: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format] + // CHECK-FIXES: std::format("c_str() removal iterator width only:{:{}} width and precision:{:{}.{}} precision only:{:.{}}", *is1, 3, *is2, 4, 2, *is3, 5); + + return s1 + s2 + s3 + s4 + s5 + s6; +} + +std::string StrFormat_macros() { + // The function call is replaced even though it comes from a macro. +#define FORMAT absl::StrFormat + auto s1 = FORMAT("Hello %d", 42); + // CHECK-MESSAGES: [[@LINE-1]]:13: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format] + // CHECK-FIXES: std::format("Hello {}", 42); + + // The format string is replaced even though it comes from a macro, this + // behaviour is required so that that macros are replaced. +#define FORMAT_STRING "Hello %s" + auto s2 = absl::StrFormat(FORMAT_STRING, 42); + // CHECK-MESSAGES: [[@LINE-1]]:13: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format] + // CHECK-FIXES: std::format("Hello {}", 42); + + // Arguments that are macros aren't replaced with their value, even if they are rearranged. +#define VALUE 3.14159265358979323846 +#define WIDTH 10 +#define PRECISION 4 + auto s3 = absl::StrFormat("Hello %*.*f", WIDTH, PRECISION, VALUE); + // CHECK-MESSAGES: [[@LINE-1]]:13: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format] + // CHECK-FIXES: std::format("Hello {:{}.{}f}", VALUE, WIDTH, PRECISION); +} diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/else-after-return-if-consteval.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/else-after-return-if-consteval.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8810d215ee97fc5da4273a6b15421db0caf84231 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/readability/else-after-return-if-consteval.cpp @@ -0,0 +1,17 @@ +// RUN: %check_clang_tidy -std=c++23 %s readability-else-after-return %t + +// Consteval if is an exception to the rule, we cannot remove the else. +void f() { + if (sizeof(int) > 4) { + return; + } else { + return; + } + // CHECK-MESSAGES: [[@LINE-3]]:5: warning: do not use 'else' after 'return' + + if consteval { + return; + } else { + return; + } +} diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/magic-numbers-todo.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/magic-numbers-todo.cpp deleted file mode 100644 index 99d9be262a8979d40d65508615795014403f719a..0000000000000000000000000000000000000000 --- a/clang-tools-extra/test/clang-tidy/checkers/readability/magic-numbers-todo.cpp +++ /dev/null @@ -1,15 +0,0 @@ -// RUN: %check_clang_tidy %s readability-magic-numbers %t -- -// XFAIL: * - -int ProcessSomething(int input); - -int DoWork() -{ - if (((int)4) > ProcessSomething(10)) - // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: 4 is a magic number; consider replacing it with a named constant [readability-magic-numbers] - return 0; - - return 0; -} - - diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/simplify-boolean-expr-macros.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/simplify-boolean-expr-macros.cpp index 7d0cfe7e27dc2206e673d7c590992b342a09bf9b..d1df79e23a1e6f9402e3056f1941b482e19aebab 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/readability/simplify-boolean-expr-macros.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/readability/simplify-boolean-expr-macros.cpp @@ -6,6 +6,7 @@ // RUN: -- #define NEGATE(expr) !(expr) +#define NOT_AND_NOT(a, b) (!a && !b) bool without_macro(bool a, bool b) { return !(!a && b); @@ -13,8 +14,17 @@ bool without_macro(bool a, bool b) { // CHECK-FIXES: return a || !b; } -bool macro(bool a, bool b) { - return NEGATE(!a && b); - // CHECK-MESSAGES-MACROS: :[[@LINE-1]]:12: warning: boolean expression can be simplified by DeMorgan's theorem - // CHECK-FIXES: return NEGATE(!a && b); +void macro(bool a, bool b) { + NEGATE(!a && b); + // CHECK-MESSAGES-MACROS: :[[@LINE-1]]:5: warning: boolean expression can be simplified by DeMorgan's theorem + // CHECK-FIXES: NEGATE(!a && b); + !NOT_AND_NOT(a, b); + // CHECK-MESSAGES-MACROS: :[[@LINE-1]]:5: warning: boolean expression can be simplified by DeMorgan's theorem + // CHECK-FIXES: !NOT_AND_NOT(a, b); + !(NEGATE(a) && b); + // CHECK-MESSAGES-MACROS: :[[@LINE-1]]:5: warning: boolean expression can be simplified by DeMorgan's theorem + // CHECK-FIXES: !(NEGATE(a) && b); + !(a && NEGATE(b)); + // CHECK-MESSAGES-MACROS: :[[@LINE-1]]:5: warning: boolean expression can be simplified by DeMorgan's theorem + // CHECK-FIXES: !(a && NEGATE(b)); } diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/static-accessed-through-instance.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/static-accessed-through-instance.cpp index 81c1cecf607f660548ac7eb351a58c306262a4b1..202fe9be6d00c530e7cce1fadda783101bef9fb4 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/readability/static-accessed-through-instance.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/readability/static-accessed-through-instance.cpp @@ -1,4 +1,4 @@ -// RUN: %check_clang_tidy %s readability-static-accessed-through-instance %t -- -- -isystem %S/Inputs/static-accessed-through-instance +// RUN: %check_clang_tidy %s readability-static-accessed-through-instance %t -- --fix-notes -- -isystem %S/Inputs/static-accessed-through-instance #include <__clang_cuda_builtin_vars.h> enum OutEnum { @@ -47,7 +47,8 @@ C &f(int, int, int, int); void g() { f(1, 2, 3, 4).x; // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: static member accessed through instance [readability-static-accessed-through-instance] - // CHECK-FIXES: {{^}} f(1, 2, 3, 4).x;{{$}} + // CHECK-MESSAGES: :[[@LINE-2]]:3: note: member base expression may carry some side effects + // CHECK-FIXES: {{^}} C::x;{{$}} } int i(int &); @@ -59,12 +60,14 @@ int k(bool); void f(C c) { j(i(h().x)); // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: static member - // CHECK-FIXES: {{^}} j(i(h().x));{{$}} + // CHECK-MESSAGES: :[[@LINE-2]]:7: note: member base expression may carry some side effects + // CHECK-FIXES: {{^}} j(i(C::x));{{$}} // The execution of h() depends on the return value of a(). j(k(a() && h().x)); // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: static member - // CHECK-FIXES: {{^}} j(k(a() && h().x));{{$}} + // CHECK-MESSAGES: :[[@LINE-2]]:14: note: member base expression may carry some side effects + // CHECK-FIXES: {{^}} j(k(a() && C::x));{{$}} if ([c]() { c.ns(); @@ -72,7 +75,8 @@ void f(C c) { }().x == 15) ; // CHECK-MESSAGES: :[[@LINE-5]]:7: warning: static member - // CHECK-FIXES: {{^}} if ([c]() {{{$}} + // CHECK-MESSAGES: :[[@LINE-6]]:7: note: member base expression may carry some side effects + // CHECK-FIXES: {{^}} if (C::x == 15){{$}} } // Nested specifiers @@ -261,8 +265,11 @@ struct Qptr { }; int func(Qptr qp) { - qp->y = 10; // OK, the overloaded operator might have side-effects. - qp->K = 10; // + qp->y = 10; + qp->K = 10; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: static member accessed through instance [readability-static-accessed-through-instance] + // CHECK-MESSAGES: :[[@LINE-2]]:3: note: member base expression may carry some side effects + // CHECK-FIXES: {{^}} Q::K = 10; } namespace { @@ -380,3 +387,20 @@ namespace PR51861 { // CHECK-FIXES: {{^}} PR51861::Foo::getBar();{{$}} } } + +namespace PR75163 { + struct Static { + static void call(); + }; + + struct Ptr { + Static* operator->(); + }; + + void test(Ptr& ptr) { + ptr->call(); + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: static member accessed through instance [readability-static-accessed-through-instance] + // CHECK-MESSAGES: :[[@LINE-2]]:5: note: member base expression may carry some side effects + // CHECK-FIXES: {{^}} PR75163::Static::call();{{$}} + } +} diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/string-compare-custom-string-classes.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/string-compare-custom-string-classes.cpp new file mode 100644 index 0000000000000000000000000000000000000000..faf135833ee15dd823bfcba4f33c0a973db34acf --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/readability/string-compare-custom-string-classes.cpp @@ -0,0 +1,35 @@ +// RUN: %check_clang_tidy %s readability-string-compare %t -- -config='{CheckOptions: {readability-string-compare.StringLikeClasses: "CustomStringTemplateBase;CustomStringNonTemplateBase"}}' -- -isystem %clang_tidy_headers +#include + +struct CustomStringNonTemplateBase { + int compare(const CustomStringNonTemplateBase& Other) const { + return 123; // value is not important for check + } +}; + +template +struct CustomStringTemplateBase { + int compare(const CustomStringTemplateBase& Other) const { + return 123; + } +}; + +struct CustomString1 : CustomStringNonTemplateBase {}; +struct CustomString2 : CustomStringTemplateBase {}; + +void CustomStringClasses() { + std::string_view sv1("a"); + std::string_view sv2("b"); + if (sv1.compare(sv2)) { // No warning - if a std class is not listed in StringLikeClasses, it won't be checked. + } + + CustomString1 custom1; + if (custom1.compare(custom1)) { + } + // CHECK-MESSAGES: [[@LINE-2]]:7: warning: do not use 'compare' to test equality of strings; use the string equality operator instead [readability-string-compare] + + CustomString2 custom2; + if (custom2.compare(custom2)) { + } + // CHECK-MESSAGES: [[@LINE-2]]:7: warning: do not use 'compare' to test equality of strings; use the string equality operator instead [readability-string-compare] +} diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/string-compare.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/string-compare.cpp index 2c08b86cf72fa02c6d0db353c897bf19e9e903ff..c4fea4341617b8c4d59a53ebe07d3ee5fb7e064f 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/readability/string-compare.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/readability/string-compare.cpp @@ -67,11 +67,27 @@ void Test() { if (str1.compare(comp())) { } // CHECK-MESSAGES: [[@LINE-2]]:7: warning: do not use 'compare' to test equality of strings; + + std::string_view sv1("a"); + std::string_view sv2("b"); + if (sv1.compare(sv2)) { + } + // CHECK-MESSAGES: [[@LINE-2]]:7: warning: do not use 'compare' to test equality of strings; use the string equality operator instead [readability-string-compare] +} + +struct DerivedFromStdString : std::string {}; + +void TestDerivedClass() { + DerivedFromStdString derived; + if (derived.compare(derived)) { + } + // CHECK-MESSAGES: [[@LINE-2]]:7: warning: do not use 'compare' to test equality of strings; use the string equality operator instead [readability-string-compare] } void Valid() { std::string str1("a", 1); std::string str2("b", 1); + if (str1 == str2) { } if (str1 != str2) { @@ -96,4 +112,11 @@ void Valid() { } if (str1.compare(str2) == -1) { } + + std::string_view sv1("a"); + std::string_view sv2("b"); + if (sv1 == sv2) { + } + if (sv1.compare(sv2) > 0) { + } } diff --git a/clang-tools-extra/unittests/clang-query/QueryParserTest.cpp b/clang-tools-extra/unittests/clang-query/QueryParserTest.cpp index 06b0d7b365904e74840a2cd4bb6d4135b179d6eb..b561e2bb98332113021670dceea19e1104902465 100644 --- a/clang-tools-extra/unittests/clang-query/QueryParserTest.cpp +++ b/clang-tools-extra/unittests/clang-query/QueryParserTest.cpp @@ -197,7 +197,7 @@ TEST_F(QueryParserTest, Comment) { TEST_F(QueryParserTest, Complete) { std::vector Comps = QueryParser::complete("", 0, QS); - ASSERT_EQ(8u, Comps.size()); + ASSERT_EQ(9u, Comps.size()); EXPECT_EQ("help ", Comps[0].TypedText); EXPECT_EQ("help", Comps[0].DisplayText); EXPECT_EQ("let ", Comps[1].TypedText); @@ -214,6 +214,8 @@ TEST_F(QueryParserTest, Complete) { EXPECT_EQ("disable", Comps[6].DisplayText); EXPECT_EQ("unlet ", Comps[7].TypedText); EXPECT_EQ("unlet", Comps[7].DisplayText); + EXPECT_EQ("file ", Comps[8].TypedText); + EXPECT_EQ("file", Comps[8].DisplayText); Comps = QueryParser::complete("set o", 5, QS); ASSERT_EQ(1u, Comps.size()); diff --git a/clang/CMakeLists.txt b/clang/CMakeLists.txt index cf97e3c6e851aee92dd5378b85515304fc875999..c20ce47a12abbd8390ce66a57efde38ac6b52125 100644 --- a/clang/CMakeLists.txt +++ b/clang/CMakeLists.txt @@ -523,6 +523,8 @@ endif() if( CLANG_INCLUDE_TESTS ) + find_package(Perl) + add_subdirectory(unittests) list(APPEND CLANG_TEST_DEPS ClangUnitTests) list(APPEND CLANG_TEST_PARAMS diff --git a/clang/docs/Block-ABI-Apple.rst b/clang/docs/Block-ABI-Apple.rst index 68f7a3819ca22e81d055566478b9b407f68cdef2..f46f2f991ad7f1d085414e7e77058d0b26686b96 100644 --- a/clang/docs/Block-ABI-Apple.rst +++ b/clang/docs/Block-ABI-Apple.rst @@ -80,7 +80,7 @@ The following flags bits are in use thusly for a possible ABI.2010.3.16: In 10.6.ABI the (1<<29) was usually set and was always ignored by the runtime - it had been a transitional marker that did not get deleted after the transition. This bit is now paired with (1<<30), and represented as the pair -(3<<30), for the following combinations of valid bit settings, and their +(3<<29), for the following combinations of valid bit settings, and their meanings: .. code-block:: c diff --git a/clang/docs/ClangOffloadBundler.rst b/clang/docs/ClangOffloadBundler.rst index 515e6c00a3b8003083508e562812317338314206..3c241027d405cae7c32b33eca7b528ab4ad3a994 100644 --- a/clang/docs/ClangOffloadBundler.rst +++ b/clang/docs/ClangOffloadBundler.rst @@ -245,7 +245,7 @@ Where: object as a data section with the name ``.hip_fatbin``. hipv4 Offload code object for the HIP language. Used for AMD GPU - code objects with at least ABI version V4 when the + code objects with at least ABI version V4 and above when the ``clang-offload-bundler`` is used to create a *fat binary* to be loaded by the HIP runtime. The fat binary can be loaded directly from a file, or be embedded in the host code @@ -254,6 +254,14 @@ Where: openmp Offload code object for the OpenMP language extension. ============= ============================================================== +Note: The distinction between the `hip` and `hipv4` offload kinds is historically based. +Originally, these designations might have indicated different versions of the +code object ABI. However, as the system has evolved, the ABI version is now embedded +directly within the code object itself, making these historical distinctions irrelevant +during the unbundling process. Consequently, `hip` and `hipv4` are treated as compatible +in current implementations, facilitating interchangeable handling of code objects +without differentiation based on offload kind. + **target-triple** The target triple of the code object. See `Target Triple `_. @@ -295,7 +303,7 @@ Compatibility Rules for Bundle Entry ID A code object, specified using its Bundle Entry ID, can be loaded and executed on a target processor, if: - * Their offload kinds are the same. + * Their offload kinds are the same or comptible. * Their target triples are compatible. * Their Target IDs are compatible as defined in :ref:`compatibility-target-id`. diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst index 3627a780886a0a37eedf00460dc810f39aeac4ba..a09c409f8f91a3da3b3a47ba15b846fd8103a5a6 100644 --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -1662,8 +1662,11 @@ The following type trait primitives are supported by Clang. Those traits marked ``T`` from ``U`` is ill-formed. Deprecated, use ``__reference_constructs_from_temporary``. * ``__reference_constructs_from_temporary(T, U)`` (C++) - Returns true if a reference ``T`` can be constructed from a temporary of type + Returns true if a reference ``T`` can be direct-initialized from a temporary of type a non-cv-qualified ``U``. +* ``__reference_converts_from_temporary(T, U)`` (C++) + Returns true if a reference ``T`` can be copy-initialized from a temporary of type + a non-cv-qualified ``U``. * ``__underlying_type`` (C++, GNU, Microsoft) In addition, the following expression traits are supported: diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 0f9728c00e6483a6e7ab556758375ab37fa3afb9..28ac54127383aa6eb9e09553d97fa1a262765587 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -182,6 +182,9 @@ C++23 Feature Support - Implemented `P2448R2: Relaxing some constexpr restrictions `_. +- Added a ``__reference_converts_from_temporary`` builtin, completing the necessary compiler support for + `P2255R2: Type trait to determine if a reference binds to a temporary `_. + C++2c Feature Support ^^^^^^^^^^^^^^^^^^^^^ @@ -699,6 +702,14 @@ Bug Fixes to C++ Support performed incorrectly when checking constraints. Fixes (#GH90349). - Clang now allows constrained member functions to be explicitly specialized for an implicit instantiation of a class template. +- Fix a C++23 bug in implementation of P2564R3 which evaluates immediate invocations in place + within initializers for variables that are usable in constant expressions or are constant + initialized, rather than evaluating them as a part of the larger manifestly constant evaluated + expression. +- Fix a bug in access control checking due to dealyed checking of friend declaration. Fixes (#GH12361). +- Correctly treat the compound statement of an ``if consteval`` as an immediate context. Fixes (#GH91509). +- When partial ordering alias templates against template template parameters, + allow pack expansions when the alias has a fixed-size parameter list. Fixes (#GH62529). Bug Fixes to AST Handling ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -798,6 +809,7 @@ CUDA/HIP Language Changes CUDA Support ^^^^^^^^^^^^ +- Clang now supports CUDA SDK up to 12.4 AIX Support ^^^^^^^^^^^ diff --git a/clang/docs/StandardCPlusPlusModules.rst b/clang/docs/StandardCPlusPlusModules.rst index ee57fb5da6485769213f8485ac0e7a7f9fbd774d..1c3c4d319c0e18970a82c83af65fba33cfd9cd89 100644 --- a/clang/docs/StandardCPlusPlusModules.rst +++ b/clang/docs/StandardCPlusPlusModules.rst @@ -8,109 +8,92 @@ Standard C++ Modules Introduction ============ -The term ``modules`` has a lot of meanings. For the users of Clang, modules may -refer to ``Objective-C Modules``, ``Clang C++ Modules`` (or ``Clang Header Modules``, -etc.) or ``Standard C++ Modules``. The implementation of all these kinds of modules in Clang -has a lot of shared code, but from the perspective of users, their semantics and -command line interfaces are very different. This document focuses on -an introduction of how to use standard C++ modules in Clang. - -There is already a detailed document about `Clang modules `_, it -should be helpful to read `Clang modules `_ if you want to know -more about the general idea of modules. Since standard C++ modules have different semantics -(and work flows) from `Clang modules`, this page describes the background and use of -Clang with standard C++ modules. - -Modules exist in two forms in the C++ Language Specification. They can refer to -either "Named Modules" or to "Header Units". This document covers both forms. +The term ``module`` is ambiguous, as it is used to mean multiple things in +Clang. For Clang users, a module may refer to an ``Objective-C Module``, +`Clang Module `_ (also called a ``Clang Header Module``) or a +``C++20 Module`` (or a ``Standard C++ Module``). The implementation of all +these kinds of modules in Clang shares a lot of code, but from the perspective +of users their semantics and command line interfaces are very different. This +document is an introduction to the use of C++20 modules in Clang. In the +remainder of this document, the term ``module`` will refer to Standard C++20 +modules and the term ``Clang module`` will refer to the Clang Modules +extension. + +In terms of the C++ Standard, modules consist of two components: "Named +Modules" or "Header Units". This document covers both. Standard C++ Named modules ========================== -This document was intended to be a manual first and foremost, however, we consider it helpful to -introduce some language background here for readers who are not familiar with -the new language feature. This document is not intended to be a language -tutorial; it will only introduce necessary concepts about the -structure and building of the project. +In order to better understand the compiler's behavior, it is helpful to +understand some terms and definitions for readers who are not familiar with the +C++ feature. This document is not a tutorial on C++; it only introduces +necessary concepts to better understand use of modules in a project. Background and terminology -------------------------- -Modules -~~~~~~~ - -In this document, the term ``Modules``/``modules`` refers to standard C++ modules -feature if it is not decorated by ``Clang``. - -Clang Modules -~~~~~~~~~~~~~ - -In this document, the term ``Clang Modules``/``Clang modules`` refer to Clang -c++ modules extension. These are also known as ``Clang header modules``, -``Clang module map modules`` or ``Clang c++ modules``. - Module and module unit ~~~~~~~~~~~~~~~~~~~~~~ -A module consists of one or more module units. A module unit is a special -translation unit. Every module unit must have a module declaration. The syntax -of the module declaration is: +A module consists of one or more module units. A module unit is a special kind +of translation unit. A module unit should almost always start with a module +declaration. The syntax of the module declaration is: .. code-block:: c++ [export] module module_name[:partition_name]; -Terms enclosed in ``[]`` are optional. The syntax of ``module_name`` and ``partition_name`` -in regex form corresponds to ``[a-zA-Z_][a-zA-Z_0-9\.]*``. In particular, a literal dot ``.`` -in the name has no semantic meaning (e.g. implying a hierarchy). +Terms enclosed in ``[]`` are optional. ``module_name`` and ``partition_name`` +follow the rules for a C++ identifier, except that they may contain one or more +period (``.``) characters. Note that a ``.`` in the name has no semantic +meaning and does not imply any hierarchy. -In this document, module units are classified into: +In this document, module units are classified as: -* Primary module interface unit. - -* Module implementation unit. - -* Module interface partition unit. - -* Internal module partition unit. +* Primary module interface unit +* Module implementation unit +* Module partition interface unit +* Internal module partition unit A primary module interface unit is a module unit whose module declaration is -``export module module_name;``. The ``module_name`` here denotes the name of the +``export module module_name;`` where ``module_name`` denotes the name of the module. A module should have one and only one primary module interface unit. A module implementation unit is a module unit whose module declaration is -``module module_name;``. A module could have multiple module implementation -units with the same declaration. +``module module_name;``. Multiple module implementation units can be declared +in the same module. -A module interface partition unit is a module unit whose module declaration is +A module partition interface unit is a module unit whose module declaration is ``export module module_name:partition_name;``. The ``partition_name`` should be unique within any given module. -An internal module partition unit is a module unit whose module declaration -is ``module module_name:partition_name;``. The ``partition_name`` should be -unique within any given module. +An internal module partition unit is a module unit whose module +declaration is ``module module_name:partition_name;``. The ``partition_name`` +should be unique within any given module. -In this document, we use the following umbrella terms: +In this document, we use the following terms: * A ``module interface unit`` refers to either a ``primary module interface unit`` - or a ``module interface partition unit``. + or a ``module partition interface unit``. -* An ``importable module unit`` refers to either a ``module interface unit`` - or a ``internal module partition unit``. +* An ``importable module unit`` refers to either a ``module interface unit`` or + an ``internal module partition unit``. -* A ``module partition unit`` refers to either a ``module interface partition unit`` - or a ``internal module partition unit``. +* A ``module partition unit`` refers to either a ``module partition interface unit`` + or an ``internal module partition unit``. -Built Module Interface file -~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Built Module Interface +~~~~~~~~~~~~~~~~~~~~~~ -A ``Built Module Interface file`` stands for the precompiled result of an importable module unit. -It is also called the acronym ``BMI`` generally. +A ``Built Module Interface`` (or ``BMI``) is the precompiled result of an +importable module unit. Global module fragment ~~~~~~~~~~~~~~~~~~~~~~ -In a module unit, the section from ``module;`` to the module declaration is called the global module fragment. +The ``global module fragment`` (or ``GMF``) is the code between the ``module;`` +and the module declaration within a module unit. How to build projects using modules @@ -138,7 +121,7 @@ Let's see a "hello world" example that uses modules. return 0; } -Then we type: +Then, on the command line, invoke Clang like: .. code-block:: console @@ -148,9 +131,9 @@ Then we type: Hello World! In this example, we make and use a simple module ``Hello`` which contains only a -primary module interface unit ``Hello.cppm``. +primary module interface unit named ``Hello.cppm``. -Then let's see a little bit more complex "hello world" example which uses the 4 kinds of module units. +A more complex "hello world" example which uses the 4 kinds of module units is: .. code-block:: c++ @@ -192,7 +175,7 @@ Then let's see a little bit more complex "hello world" example which uses the 4 return 0; } -Then we are able to compile the example by the following command: +Then, back on the command line, invoke Clang with: .. code-block:: console @@ -216,51 +199,57 @@ We explain the options in the following sections. How to enable standard C++ modules ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Currently, standard C++ modules are enabled automatically -if the language standard is ``-std=c++20`` or newer. +Standard C++ modules are enabled automatically when the language standard mode +is ``-std=c++20`` or newer. How to produce a BMI ~~~~~~~~~~~~~~~~~~~~ -We can generate a BMI for an importable module unit by either ``--precompile`` -or ``-fmodule-output`` flags. +To generate a BMI for an importable module unit, use either the ``--precompile`` +or ``-fmodule-output`` command line options. -The ``--precompile`` option generates the BMI as the output of the compilation and the output path -can be specified using the ``-o`` option. +The ``--precompile`` option generates the BMI as the output of the compilation +with the output path specified using the ``-o`` option. -The ``-fmodule-output`` option generates the BMI as a by-product of the compilation. -If ``-fmodule-output=`` is specified, the BMI will be emitted the specified location. Then if -``-fmodule-output`` and ``-c`` are specified, the BMI will be emitted in the directory of the -output file with the name of the input file with the new extension ``.pcm``. Otherwise, the BMI -will be emitted in the working directory with the name of the input file with the new extension +The ``-fmodule-output`` option generates the BMI as a by-product of the +compilation. If ``-fmodule-output=`` is specified, the BMI will be emitted to +the specified location. If ``-fmodule-output`` and ``-c`` are specified, the +BMI will be emitted in the directory of the output file with the name of the +input file with the extension ``.pcm``. Otherwise, the BMI will be emitted in +the working directory with the name of the input file with the extension ``.pcm``. -The style to generate BMIs by ``--precompile`` is called two-phase compilation since it takes -2 steps to compile a source file to an object file. The style to generate BMIs by ``-fmodule-output`` -is called one-phase compilation respectively. The one-phase compilation model is simpler -for build systems to implement and the two-phase compilation has the potential to compile faster due -to higher parallelism. As an example, if there are two module units A and B, and B depends on A, the -one-phase compilation model would need to compile them serially, whereas the two-phase compilation -model may be able to compile them simultaneously if the compilation from A.pcm to A.o takes a long -time. - -File name requirement -~~~~~~~~~~~~~~~~~~~~~ - -The file name of an ``importable module unit`` should end with ``.cppm`` -(or ``.ccm``, ``.cxxm``, ``.c++m``). The file name of a ``module implementation unit`` -should end with ``.cpp`` (or ``.cc``, ``.cxx``, ``.c++``). - -The file name of BMIs should end with ``.pcm``. -The file name of the BMI of a ``primary module interface unit`` should be ``module_name.pcm``. -The file name of BMIs of ``module partition unit`` should be ``module_name-partition_name.pcm``. - -If the file names use different extensions, Clang may fail to build the module. -For example, if the filename of an ``importable module unit`` ends with ``.cpp`` instead of ``.cppm``, -then we can't generate a BMI for the ``importable module unit`` by ``--precompile`` option -since ``--precompile`` option now would only run preprocessor, which is equal to `-E` now. -If we want the filename of an ``importable module unit`` ends with other suffixes instead of ``.cppm``, -we could put ``-x c++-module`` in front of the file. For example, +Generating BMIs with ``--precompile`` is referred to as two-phase compilation +because it takes two steps to compile a source file to an object file. +Generating BMIs with ``-fmodule-output`` is called one-phase compilation. The +one-phase compilation model is simpler for build systems to implement while the +two-phase compilation has the potential to compile faster due to higher +parallelism. As an example, if there are two module units ``A`` and ``B``, and +``B`` depends on ``A``, the one-phase compilation model needs to compile them +serially, whereas the two-phase compilation model is able to be compiled as +soon as ``A.pcm`` is available, and thus can be compiled simultaneously as the +``A.pcm`` to ``A.o`` compilation step. + +File name requirements +~~~~~~~~~~~~~~~~~~~~~~ + +By convention, ``importable module unit`` files should use ``.cppm`` (or +``.ccm``, ``.cxxm``, or ``.c++m``) as a file extension. +``Module implementation unit`` files should use ``.cpp`` (or ``.cc``, ``.cxx``, +or ``.c++``) as a file extension. + +A BMI should use ``.pcm`` as a file extension. The file name of the BMI for a +``primary module interface unit`` should be ``module_name.pcm``. The file name +of a BMI for a ``module partition unit`` should be +``module_name-partition_name.pcm``. + +Clang may fail to build the module if different extensions are used. For +example, if the filename of an ``importable module unit`` ends with ``.cpp`` +instead of ``.cppm``, then Clang cannot generate a BMI for the +``importable module unit`` with the ``--precompile`` option because the +``--precompile`` option would only run the preprocessor (``-E``). If using a +different extension than the conventional one for an ``importable module unit`` +you can specify ``-x c++-module`` before the file. For example, .. code-block:: c++ @@ -279,8 +268,9 @@ we could put ``-x c++-module`` in front of the file. For example, return 0; } -Now the filename of the ``module interface`` ends with ``.cpp`` instead of ``.cppm``, -we can't compile them by the original command lines. But we are still able to do it by: +In this example, the extension used by the ``module interface`` is ``.cpp`` +instead of ``.cppm``, so it cannot be compiled like the previous example, but +it can be compiled with: .. code-block:: console @@ -289,12 +279,12 @@ we can't compile them by the original command lines. But we are still able to do $ ./Hello.out Hello World! -Module name requirement -~~~~~~~~~~~~~~~~~~~~~~~ +Module name requirements +~~~~~~~~~~~~~~~~~~~~~~~~ -[module.unit]p1 says: +.. -.. code-block:: text + [module.unit]p1: All module-names either beginning with an identifier consisting of std followed by zero or more digits or containing a reserved identifier ([lex.name]) are reserved and shall not @@ -302,7 +292,7 @@ Module name requirement module-name is a reserved identifier, the module name is reserved for use by C++ implementations; otherwise it is reserved for future standardization. -So all of the following name is not valid by default: +Therefore, none of the following names are valid by default: .. code-block:: text @@ -312,75 +302,74 @@ So all of the following name is not valid by default: __test // and so on ... -If you still want to use the reserved module names for any reason, use -``-Wno-reserved-module-identifier`` to suppress the warning. +Using a reserved module name is strongly discouraged, but +``-Wno-reserved-module-identifier`` can be used to suppress the warning. -How to specify the dependent BMIs -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Specifying dependent BMIs +~~~~~~~~~~~~~~~~~~~~~~~~~ -There are 3 methods to specify the dependent BMIs: +There are 3 ways to specify a dependent BMI: -* (1) ``-fprebuilt-module-path=``. -* (2) ``-fmodule-file=`` (Deprecated). -* (3) ``-fmodule-file==``. +1. ``-fprebuilt-module-path=``. +2. ``-fmodule-file=`` (Deprecated). +3. ``-fmodule-file==``. -The option ``-fprebuilt-module-path`` tells the compiler the path where to search for dependent BMIs. -It may be used multiple times just like ``-I`` for specifying paths for header files. The look up rule here is: +The ``-fprebuilt-module-path`` option specifies the path to search for +dependent BMIs. Multiple paths may be specified, similar to using ``-I`` to +specify a search path for header files. When importing a module ``M``, the +compiler looks for ``M.pcm`` in the directories specified by +``-fprebuilt-module-path``. Similarly, when importing a partition module unit +``M:P``, the compiler looks for ``M-P.pcm`` in the directories specified by +``-fprebuilt-module-path``. -* (1) When we import module M. The compiler would look up M.pcm in the directories specified - by ``-fprebuilt-module-path``. -* (2) When we import partition module unit M:P. The compiler would look up M-P.pcm in the - directories specified by ``-fprebuilt-module-path``. - -The option ``-fmodule-file=`` tells the compiler to load the specified BMI directly. -The option ``-fmodule-file==`` tells the compiler to load the specified BMI -for the module specified by ```` when necessary. The main difference is that +The ``-fmodule-file=`` option causes the compiler to load the +specified BMI directly. The ``-fmodule-file==`` +option causes the compiler to load the specified BMI for the module specified +by ```` when necessary. The main difference is that ``-fmodule-file=`` will load the BMI eagerly, whereas -``-fmodule-file==`` will only load the BMI lazily, which is similar -with ``-fprebuilt-module-path``. The option ``-fmodule-file=`` for named modules is deprecated -and is planning to be removed in future versions. +``-fmodule-file==`` will only load the BMI lazily, +as will ``-fprebuilt-module-path``. The ``-fmodule-file=`` option +for named modules is deprecated and will be removed in a future version of +Clang. -In case all ``-fprebuilt-module-path=``, ``-fmodule-file=`` and -``-fmodule-file==`` exist, the ``-fmodule-file=`` option -takes highest precedence and ``-fmodule-file==`` will take the second -highest precedence. +When these options are specified in the same invocation of the compiler, the +``-fmodule-file=`` option takes precedence over +``-fmodule-file==``, which takes precedence over +``-fprebuilt-module-path=``. -We need to specify all the dependent (directly and indirectly) BMIs. -See https://github.com/llvm/llvm-project/issues/62707 for detail. +Note: all dependant BMIs must be specified explicitly, either directly or +indirectly dependent BMIs explicitly. See +https://github.com/llvm/llvm-project/issues/62707 for details. -When we compile a ``module implementation unit``, we must specify the BMI of the corresponding -``primary module interface unit``. -Since the language specification says a module implementation unit implicitly imports -the primary module interface unit. +When compiling a ``module implementation unit``, the BMI of the corresponding +``primary module interface unit`` must be specified because a module +implementation unit implicitly imports the primary module interface unit. [module.unit]p8 A module-declaration that contains neither an export-keyword nor a module-partition implicitly imports the primary module interface unit of the module as if by a module-import-declaration. -All of the 3 options ``-fprebuilt-module-path=``, ``-fmodule-file=`` -and ``-fmodule-file==`` may occur multiple times. -For example, the command line to compile ``M.cppm`` in -the above example could be rewritten into: +The ``-fprebuilt-module-path=``, ``-fmodule-file=``, +and ``-fmodule-file==`` options may be specified +multiple times. For example, the command line to compile ``M.cppm`` in +the previous example could be rewritten as: .. code-block:: console $ clang++ -std=c++20 M.cppm --precompile -fmodule-file=M:interface_part=M-interface_part.pcm -fmodule-file=M:impl_part=M-impl_part.pcm -o M.pcm When there are multiple ``-fmodule-file==`` options for the same -````, the last ``-fmodule-file==`` will override the previous -``-fmodule-file==`` options. - -``-fprebuilt-module-path`` is more convenient and ``-fmodule-file`` is faster since -it saves time for file lookup. +````, the last ``-fmodule-file==`` overrides the +previous ``-fmodule-file==`` option. Remember that module units still have an object counterpart to the BMI ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -It is easy to forget to compile BMIs at first since we may envision module interfaces like headers. -However, this is not true. -Module units are translation units. We need to compile them to object files -and link the object files like the example shows. +While module interfaces resemble traditional header files, they still require +compilation. Module units are translation units, and need to be compiled to +object files, which then need to be linked together as the following examples +show. For example, the traditional compilation processes for headers are like: @@ -400,24 +389,27 @@ And the compilation process for module units are like: mod1.cppm -> clang++ mod1.cppm ... -> mod1.pcm --,--> clang++ mod1.pcm ... -> mod1.o -+ src2.cpp ----------------------------------------+> clang++ src2.cpp -------> src2.o -' -As the diagrams show, we need to compile the BMI from module units to object files and link the object files. -(But we can't do this for the BMI from header units. See the later section for the definition of header units) +As the diagrams show, we need to compile the BMI from module units to object +files and then link the object files. (However, this cannot be done for the BMI +from header units. See the section on :ref:`header units ` for +more details. -If we want to create a module library, we can't just ship the BMIs in an archive. -We must compile these BMIs(``*.pcm``) into object files(``*.o``) and add those object files to the archive instead. +BMIs cannot be shipped in an archive to create a module library. Instead, the +BMIs(``*.pcm``) are compiled into object files(``*.o``) and those object files +are added to the archive instead. -Consistency Requirement -~~~~~~~~~~~~~~~~~~~~~~~ +Consistency Requirements +~~~~~~~~~~~~~~~~~~~~~~~~ -If we envision modules as a cache to speed up compilation, then - as with other caching techniques - -it is important to keep cache consistency. -So **currently** Clang will do very strict check for consistency. +Modules can be viewed as a kind of cache to speed up compilation. Thus, like +other caching techniques, it is important to maintain cache consistency which +is why Clang does very strict checking for consistency. Options consistency ^^^^^^^^^^^^^^^^^^^ -The language option of module units and their non-module-unit users should be consistent. -The following example is not allowed: +Compiler options related to the language dialect for a module unit and its +non-module-unit uses need to be consistent. Consider the following example: .. code-block:: c++ @@ -432,9 +424,8 @@ The following example is not allowed: $ clang++ -std=c++20 M.cppm --precompile -o M.pcm $ clang++ -std=c++23 Use.cpp -fprebuilt-module-path=. -The compiler would reject the example due to the inconsistent language options. -Not all options are language options. -For example, the following example is allowed: +Clang rejects the example due to the inconsistent language standard modes. Not +all compiler options are language dialect options, though. For example: .. code-block:: console @@ -444,9 +435,12 @@ For example, the following example is allowed: # Inconsistent debugging level. $ clang++ -std=c++20 -g Use.cpp -fprebuilt-module-path=. -Although the two examples have inconsistent optimization and debugging level, both of them are accepted. +Although the optimization and debugging levels are inconsistent, these +compilations are accepted because the compiler options do not impact the +language dialect. -Note that **currently** the compiler doesn't consider inconsistent macro definition a problem. For example: +Note that the compiler **currently** doesn't reject inconsistent macro +definitions (this may change in the future). For example: .. code-block:: console @@ -454,43 +448,43 @@ Note that **currently** the compiler doesn't consider inconsistent macro definit # Inconsistent optimization level. $ clang++ -std=c++20 -O3 -DNDEBUG Use.cpp -fprebuilt-module-path=. -Currently Clang would accept the above example. But it may produce surprising results if the -debugging code depends on consistent use of ``NDEBUG`` also in other translation units. +Currently, Clang accepts the above example, though it may produce surprising +results if the debugging code depends on consistent use of ``NDEBUG`` in other +translation units. -Definitions consistency -^^^^^^^^^^^^^^^^^^^^^^^ +Object definition consistency +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The C++ language requires that declarations of the same entity in different +translation units have the same definition, which is known as the One +Definition Rule (ODR). Without modules, the compiler cannot perform strong ODR +violation checking because it only sees one translation unit at a time. With +the use of modules, the compiler can perform checks for ODR violations across +translation units. -The C++ language defines that same declarations in different translation units should have -the same definition, as known as ODR (One Definition Rule). Prior to modules, the translation -units don't dependent on each other and the compiler itself can't perform a strong -ODR violation check. With the introduction of modules, now the compiler have -the chance to perform ODR violations with language semantics across translation units. - -However, in the practice, we found the existing ODR checking mechanism is not stable -enough. Many people suffers from the false positive ODR violation diagnostics, AKA, -the compiler are complaining two identical declarations have different definitions -incorrectly. Also the true positive ODR violations are rarely reported. -Also we learned that MSVC don't perform ODR check for declarations in the global module -fragment. - -So in order to get better user experience, save the time checking ODR and keep consistent -behavior with MSVC, we disabled the ODR check for the declarations in the global module -fragment by default. Users who want more strict check can still use the -``-Xclang -fno-skip-odr-check-in-gmf`` flag to get the ODR check enabled. It is also -encouraged to report issues if users find false positive ODR violations or false negative ODR -violations with the flag enabled. +However, the current ODR checking mechanisms are not perfect. There are a +significant number of false positive ODR violation diagnostics, where the +compiler incorrectly diagnoses two identical declarations as having different +definitions. Further, true positive ODR violations are not always reported. + +To give a better user experience, improve compilation performance, and for +consistency with MSVC, ODR checking of declarations in the global module +fragment is disabled by default. These checks can be enabled by specifying +``-Xclang -fno-skip-odr-check-in-gmf`` when compiling. If the check is enabled +and you encounter incorrect or missing diagnostics, please report them via the +`community issue tracker `_. ABI Impacts ----------- -This section describes the new ABI changes brought by modules. - -Only Itanium C++ ABI related change are mentioned +This section describes the new ABI changes brought by modules. Only changes to +the Itanium C++ ABI are covered. -Mangling Names -~~~~~~~~~~~~~~ +Name Mangling +~~~~~~~~~~~~~ -The declarations in a module unit which are not in the global module fragment have new linkage names. +The declarations in a module unit which are not in the global module fragment +have new linkage names. For example, @@ -501,22 +495,24 @@ For example, export int foo(); } -The linkage name of ``NS::foo()`` would be ``_ZN2NSW1M3fooEv``. -This couldn't be demangled by previous versions of the debugger or demangler. -As of LLVM 15.x, users can utilize ``llvm-cxxfilt`` to demangle this: +The linkage name of ``NS::foo()`` is ``_ZN2NSW1M3fooEv``. This couldn't be +demangled by previous versions of the debugger or demangler. As of LLVM 15.x, +``llvm-cxxfilt`` can be used to demangle this: .. code-block:: console $ llvm-cxxfilt _ZN2NSW1M3fooEv + NS::foo@M() -The result would be ``NS::foo@M()``, which reads as ``NS::foo()`` in module ``M``. +The result should be read as ``NS::foo()`` in module ``M``. -The ABI implies that we can't declare something in a module unit and define it in a non-module unit (or vice-versa), -as this would result in linking errors. +The ABI implies that something cannot be declared in a module unit and defined +in a non-module unit (or vice-versa), as this would result in linking errors. -If we still want to implement declarations within the compatible ABI in module unit, -we can use the language-linkage specifier. Since the declarations in the language-linkage specifier -is attached to the global module fragments. For example: +Despite this, it is possible to implement declarations with a compatible ABI in +a module unit by using a language linkage specifier because the declarations in +the language linkage specifier are attached to the global module fragment. For +example: .. code-block:: c++ @@ -530,43 +526,47 @@ Now the linkage name of ``NS::foo()`` will be ``_ZN2NS3fooEv``. Module Initializers ~~~~~~~~~~~~~~~~~~~ -All the importable module units are required to emit an initializer function. -The initializer function should contain calls to importing modules first and -all the dynamic-initializers in the current module unit then. +All importable module units are required to emit an initializer function to +handle the dynamic initialization of non-inline variables in the module unit. +The importable module unit has to emit the initializer even if there is no +dynamic initialization; otherwise, the importer may call a nonexistent +function. The initializer function emits calls to imported modules first +followed by calls to all to of the dynamic initializers in the current module +unit. -Translation units explicitly or implicitly importing named modules must call -the initializer functions of the imported named modules within the sequence of -the dynamic-initializers in the TU. Initializations of entities at namespace -scope are appearance-ordered. This (recursively) extends into imported modules -at the point of appearance of the import declaration. +Translation units that explicitly or implicitly import a named module must call +the initializer functions of the imported named module within the sequence of +the dynamic initializers in the translation unit. Initializations of entities +at namespace scope are appearance-ordered. This (recursively) extends to +imported modules at the point of appearance of the import declaration. -It is allowed to omit calls to importing modules if it is known empty. - -It is allowed to omit calls to importing modules for which is known to be called. +If the imported module is known to be empty, the call to its initializer may be +omitted. Additionally, if the imported module is known to have already been +imported, the call to its initializer may be omitted. Reduced BMI ----------- -To support the 2 phase compilation model, Clang chose to put everything needed to -produce an object into the BMI. But every consumer of the BMI, except itself, doesn't -need such informations. It makes the BMI to larger and so may introduce unnecessary -dependencies into the BMI. To mitigate the problem, we decided to reduce the information -contained in the BMI. - -To be clear, we call the default BMI as Full BMI and the new introduced BMI as Reduced -BMI. +To support the two-phase compilation model, Clang puts everything needed to +produce an object into the BMI. However, other consumers of the BMI generally +don't need that information. This makes the BMI larger and may introduce +unnecessary dependencies for the BMI. To mitigate the problem, Clang has a +compiler option to reduce the information contained in the BMI. These two +formats are known as Full BMI and Reduced BMI, respectively. -Users can use ``-fexperimental-modules-reduced-bmi`` flag to enable the Reduced BMI. +Users can use the ``-fexperimental-modules-reduced-bmi`` option to produce a +Reduced BMI. -For one phase compilation model (CMake implements this model), with -``-fexperimental-modules-reduced-bmi``, the generated BMI will be Reduced BMI automatically. -(The output path of the BMI is specified by ``-fmodule-output=`` as usual one phase -compilation model). +For the one-phase compilation model (CMake implements this model), with +``-fexperimental-modules-reduced-bmi``, the generated BMI will be a Reduced +BMI automatically. (The output path of the BMI is specified by +``-fmodule-output=`` as usual with the one-phase compilation model). -It is still possible to support Reduced BMI in two phase compilation model. With -``-fexperimental-modules-reduced-bmi``, ``--precompile`` and ``-fmodule-output=`` specified, -the generated BMI specified by ``-o`` will be full BMI and the BMI specified by -``-fmodule-output=`` will be Reduced BMI. The dependency graph may be: +It is also possible to produce a Reduced BMI with the two-phase compilation +model. When ``-fexperimental-modules-reduced-bmi``, ``--precompile``, and +``-fmodule-output=`` are specified, the generated BMI specified by ``-o`` will +be a full BMI and the BMI specified by ``-fmodule-output=`` will be a Reduced +BMI. The dependency graph in this case would look like: .. code-block:: none @@ -577,15 +577,16 @@ the generated BMI specified by ``-o`` will be full BMI and the BMI specified by -> ... -> consumer_n.cpp -We don't emit diagnostics if ``-fexperimental-modules-reduced-bmi`` is used with a non-module -unit. This design helps the end users of one phase compilation model to perform experiments -early without asking for the help of build systems. The users of build systems which supports -two phase compilation model still need helps from build systems. +Clang does not emit diagnostics when ``-fexperimental-modules-reduced-bmi`` is +used with a non-module unit. This design permits users of the one-phase +compilation model to try using reduced BMIs without needing to modify the build +system. The two-phase compilation module requires build system support. -Within Reduced BMI, we won't write unreachable entities from GMF, definitions of non-inline -functions and non-inline variables. This may not be a transparent change. -`[module.global.frag]ex2 `_ may be a good -example: +In a Reduced BMI, Clang does not emit unreachable entities from the global +module fragment, or definitions of non-inline functions and non-inline +variables. This may not be a transparent change. + +Consider the following example: .. code-block:: c++ @@ -633,22 +634,23 @@ example: // module M's interface, so is discarded int c = use_h(); // OK -In the above example, the function definition of ``N::g`` is elided from the Reduced -BMI of ``M.cppm``. Then the use of ``use_g`` in ``M-impl.cpp`` fails -to instantiate. For such issues, users can add references to ``N::g`` in the module purview -of ``M.cppm`` to make sure it is reachable, e.g., ``using N::g;``. +In the above example, the function definition of ``N::g`` is elided from the +Reduced BMI of ``M.cppm``. Then the use of ``use_g`` in ``M-impl.cpp`` +fails to instantiate. For such issues, users can add references to ``N::g`` in +the `module purview `_ of ``M.cppm`` to +ensure it is reachable, e.g. ``using N::g;``. -We think the Reduced BMI is the correct direction. But given it is a drastic change, -we'd like to make it experimental first to avoid breaking existing users. The roadmap -of Reduced BMI may be: +Support for Reduced BMIs is still experimental, but it may become the default +in the future. The expected roadmap for Reduced BMIs as of Clang 19.x is: -1. ``-fexperimental-modules-reduced-bmi`` is opt in for 1~2 releases. The period depends -on testing feedbacks. -2. We would announce Reduced BMI is not experimental and introduce ``-fmodules-reduced-bmi``. -and suggest users to enable this mode. This may takes 1~2 releases too. -3. Finally we will enable this by default. When that time comes, the term BMI will refer to -the reduced BMI today and the Full BMI will only be meaningful to build systems which -loves to support two phase compilations. +1. ``-fexperimental-modules-reduced-bmi`` is opt-in for 1~2 releases. The period depends + on user feedback and may be extended. +2. Announce that Reduced BMIs are no longer experimental and introduce + ``-fmodules-reduced-bmi`` as a new option, and recommend use of the new + option. This transition is expected to take 1~2 additional releases as well. +3. Finally, ``-fmodules-reduced-bmi`` will be the default. When that time + comes, the term BMI will refer to the Reduced BMI and the Full BMI will only + be meaningful to build systems which elect to support two-phase compilation. Performance Tips ---------------- @@ -656,13 +658,11 @@ Performance Tips Reduce duplications ~~~~~~~~~~~~~~~~~~~ -While it is legal to have duplicated declarations in the global module fragments -of different module units, it is not free for clang to deal with the duplicated -declarations. In other word, for a translation unit, it will compile slower if the -translation unit itself and its importing module units contains a lot duplicated -declarations. - -For example, +While it is valid to have duplicated declarations in the global module fragments +of different module units, it is not free for Clang to deal with the duplicated +declarations. A translation unit will compile more slowly if there is a lot of +duplicated declarations between the translation unit and modules it imports. +For example: .. code-block:: c++ @@ -698,9 +698,9 @@ For example, import M; ... // use declarations from module M. -When ``big.header.h`` is big enough and there are a lot of partitions, -the compilation of ``use.cpp`` may be slower than -the following style significantly: +When ``big.header.h`` is big enough and there are a lot of partitions, the +compilation of ``use.cpp`` may be significantly slower than the following +approach: .. code-block:: c++ @@ -738,22 +738,21 @@ the following style significantly: import M; ... // use declarations from module M. -The key part of the tip is to reduce the duplications from the text includes. - -Ideas for converting to modules -------------------------------- +Reducing the duplication from textual includes is what improves compile-time +performance. -For new libraries, we encourage them to use modules completely from day one if possible. -This will be pretty helpful to make the whole ecosystems to get ready. +Transitioning to modules +------------------------ -For many existing libraries, it may be a breaking change to refactor themselves -into modules completely. So that many existing libraries need to provide headers and module -interfaces for a while to not break existing users. -Here we provide some ideas to ease the transition process for existing libraries. -**Note that the this section is only about helping ideas instead of requirement from clang**. +It is best for new code and libraries to use modules from the start if +possible. However, it may be a breaking change for existing code or libraries +to switch to modules. As a result, many existing libraries need to provide +both headers and module interfaces for a while to not break existing users. -Let's start with the case that there is no dependency or no dependent libraries providing -modules for your library. +This section suggests some suggestions on how to ease the transition process +for existing libraries. **Note that this information is only intended as +guidance, rather than as requirements to use modules in Clang.** It presumes +the project is starting with no module-based dependencies. ABI non-breaking styles ~~~~~~~~~~~~~~~~~~~~~~~ @@ -776,9 +775,9 @@ export-using style using decl_n; } -As the example shows, you need to include all the headers containing declarations needs -to be exported and `using` such declarations in an `export` block. Then, basically, -we're done. +This example shows how to include all the headers containing declarations which +need to be exported, and uses `using` declarations in an `export` block to +produce the module interface. export extern-C++ style ^^^^^^^^^^^^^^^^^^^^^^^ @@ -799,7 +798,7 @@ export extern-C++ style #include "header_n.h" } -Then in your headers (from ``header_1.h`` to ``header_n.h``), you need to define the macro: +Headers (from ``header_1.h`` to ``header_n.h``) need to define the macro: .. code-block:: c++ @@ -809,9 +808,10 @@ Then in your headers (from ``header_1.h`` to ``header_n.h``), you need to define #define EXPORT #endif -And you should put ``EXPORT`` to the beginning of the declarations you want to export. +and put ``EXPORT`` on the declarations you want to export. -Also it is suggested to refactor your headers to include thirdparty headers conditionally: +Also, it is recommended to refactor headers to include third-party headers +conditionally: .. code-block:: c++ @@ -823,26 +823,25 @@ Also it is suggested to refactor your headers to include thirdparty headers cond ... -This may be helpful to get better diagnostic messages if you forgot to update your module -interface unit file during maintaining. +This can be helpful because it gives better diagnostic messages if the module +interface unit is not properly updated when modifying code. -The reasoning for the practice is that the declarations in the language linkage are considered -to be attached to the global module. So the ABI of your library in the modular version -wouldn't change. +This approach works because the declarations with language linkage are attached +to the global module. Thus, the ABI of the modular form of the library does not +change. -While this style looks not as convenient as the export-using style, it is easier to convert -to other styles. +While this style is more involved than the export-using style, it makes it +easier to further refactor the library to other styles. ABI breaking style ~~~~~~~~~~~~~~~~~~ -The term ``ABI breaking`` sounds terrifying generally. But you may want it here if you want -to force your users to introduce your library in a consistent way. E.g., they either include -your headers all the way or import your modules all the way. -The style prevents the users to include your headers and import your modules at the same time -in the same repo. +The term ``ABI breaking`` may sound like a bad approach. However, this style +forces consumers of the library use it in a consistent way. e.g., either always +include headers for the library or always import modules. The style prevents +the ability to mix includes and imports for the library. -The pattern for ABI breaking style is similar with export extern-C++ style. +The pattern for ABI breaking style is similar to the export extern-C++ style. .. code-block:: c++ @@ -865,7 +864,7 @@ The pattern for ABI breaking style is similar with export extern-C++ style. ... #include "source_n.cpp" #else // the number of .cpp files in your project are a lot - // Using all the declarations from thirdparty libraries which are + // Using all the declarations from third-party libraries which are // used in the .cpp files. namespace third_party_namespace { using third_party_decl_used_in_cpp_1; @@ -875,11 +874,11 @@ The pattern for ABI breaking style is similar with export extern-C++ style. } #endif -(And add `EXPORT` and conditional include to the headers as suggested in the export -extern-C++ style section) +(And add `EXPORT` and conditional include to the headers as suggested in the +export extern-C++ style section.) -Remember that the ABI get changed and we need to compile our source files into the -new ABI format. This is the job of the additional part of the interface unit: +The ABI with modules is different and thus we need to compile the source files +into the new ABI. This is done by an additional part of the interface unit: .. code-block:: c++ @@ -890,7 +889,7 @@ new ABI format. This is the job of the additional part of the interface unit: ... #include "source_n.cpp" #else // the number of .cpp files in your project are a lot - // Using all the declarations from thirdparty libraries which are + // Using all the declarations from third-party libraries which are // used in the .cpp files. namespace third_party_namespace { using third_party_decl_used_in_cpp_1; @@ -900,16 +899,17 @@ new ABI format. This is the job of the additional part of the interface unit: } #endif -In case the number of your source files are small, we may put everything in the private -module fragment directly. (it is suggested to add conditional include to the source -files too). But it will make the compilation of the module interface unit to be slow -when the number of the source files are not small enough. +If the number of source files is small, everything can be put in the private +module fragment directly (it is recommended to add conditional includes to the +source files as well). However, compile time performance will be bad if there +are a lot of source files to compile. -**Note that the private module fragment can only be in the primary module interface unit -and the primary module interface unit containing private module fragment should be the only -module unit of the corresponding module.** +**Note that the private module fragment can only be in the primary module +interface unit and the primary module interface unit containing the private +module fragment should be the only module unit of the corresponding module.** -In that case, you need to convert your source files (.cpp files) to module implementation units: +In this case, source files (.cpp files) must be converted to module +implementation units: .. code-block:: c++ @@ -925,45 +925,40 @@ In that case, you need to convert your source files (.cpp files) to module imple // Following off should be unchanged. ... -The module implementation unit will import the primary module implicitly. -We don't include any headers in the module implementation units -here since we want to avoid duplicated declarations between translation units. -This is the reason why we add non-exported using declarations from the third -party libraries in the primary module interface unit. - -And if you provide your library as ``libyour_library.so``, you probably need to -provide a modular one ``libyour_library_modules.so`` since you changed the ABI. - -What if there are headers only inclued by the source files -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +The module implementation unit will import the primary module implicitly. Do +not include any headers in the module implementation units as it avoids +duplicated declarations between translation units. This is why non-exported +using declarations should be added from third-party libraries in the primary +module interface unit. -The above practice may be problematic if there are headers only included by the source -files. If you're using private module fragment, you may solve the issue by including them -in the private module fragment. While it is OK to solve it by including the implementation -headers in the module purview if you're using implementation module units, it may be -suboptimal since the primary module interface units now containing entities not belongs -to the interface. +If the library is provided as ``libyour_library.so``, a modular library (e.g., +``libyour_library_modules.so``) may also need to be provided for ABI +compatibility. -If you're a perfectionist, maybe you can improve it by introducing internal module partition unit. +What if there are headers only included by the source files +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The internal module partition unit is an importable module unit which is internal -to the module itself. The concept just meets the headers only included by the source files. +The above practice may be problematic if there are headers only included by the +source files. When using a private module fragment, this issue may be solved by +including those headers in the private module fragment. While it is OK to solve +it by including the implementation headers in the module purview when using +implementation module units, it may be suboptimal because the primary module +interface units now contain entities that do not belong to the interface. -We don't show code snippet since it may be too verbose or not good or not general. -But it may not be too hard if you can understand the points of the section. +This can potentially be improved by introducing a module partition +implementation unit. An internal module partition unit is an importable +module unit which is internal to the module itself. Providing a header to skip parsing redundant headers ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -It is a problem for clang to handle redeclarations between translation units. -Also there is a long standing issue in clang (`problematic include after import `_). -But even if the issue get fixed in clang someday, the users may still get slower compilation speed -and larger BMI size. So it is suggested to not include headers after importing the corresponding -library. - -However, it is not easy for users if your library are included by other dependencies. - -So the users may have to write codes like: +Many redeclarations shared between translation units causes Clang to have +slower compile-time performance. Further, there are known issues with +`include after import `_. +Even when that issue is resolved, users may still get slower compilation speed +and larger BMIs. For these reasons, it is recommended to not include headers +after importing the corresponding module. However, it is not always easy if the +library is included by other dependencies, as in: .. code-block:: c++ @@ -977,9 +972,9 @@ or import your_library; #include "third_party/A.h" // #include "your_library/a_header.h" -For such cases, we suggest the libraries providing modules and the headers at the same time -to provide a header to skip parsing all the headers in your libraries. So the users can -import your library as the following style to skip redundant handling: +For such cases, it is best if the library providing both module and header +interfaces also provides a header which skips parsing so that the library can +be imported with the following approach that skips redundant redeclarations: .. code-block:: c++ @@ -987,9 +982,9 @@ import your library as the following style to skip redundant handling: #include "your_library_imported.h" #include "third_party/A.h" // #include "your_library/a_header.h" but got skipped -The implementation of ``your_library_imported.h`` can be a set of controlling macros or -an overall controlling macro if you're using `#pragma once`. So you can convert your -headers to: +The implementation of ``your_library_imported.h`` can be a set of controlling +macros or an overall controlling macro if using `#pragma once`. Then headers +can be refactored to: .. code-block:: c++ @@ -998,25 +993,24 @@ headers to: ... #endif -If the modules imported by your library provides such headers too, remember to add them to -your ``your_library_imported.h`` too. +If the modules imported by the library provide such headers, remember to add +them to ``your_library_imported.h`` too. Importing modules ~~~~~~~~~~~~~~~~~ -When there are dependent libraries providing modules, we suggest you to import that in -your module. - -Most of the existing libraries would fall into this catagory once the std module gets available. +When there are dependent libraries providing modules, they should be imported +in your module as well. Many existing libraries will fall into this category +once the ``std`` module is more widely available. All dependent libraries providing modules ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Life gets easier if all the dependent libraries providing modules. +Of course, most of the complexity disappears if all the dependent libraries +provide modules. -You need to convert your headers to include thirdparty headers conditionally. - -Then for export-using style: +Headers need to be converted to include third-party headers conditionally. Then, +for the export-using style: .. code-block:: c++ @@ -1035,7 +1029,7 @@ Then for export-using style: using decl_n; } -For export extern-C++ style: +or, for the export extern-C++ style: .. code-block:: c++ @@ -1049,7 +1043,7 @@ For export extern-C++ style: #include "header_n.h" } -For ABI breaking style, +or, for the ABI-breaking style, .. code-block:: c++ @@ -1069,35 +1063,39 @@ For ABI breaking style, #include "source_n.cpp" #endif -We don't need the non-exported using declarations if we're using implementation module -units now. We can import thirdparty modules directly in the implementation module -units. +Non-exported ``using`` declarations are unnecessary if using implementation +module units. Instead, third-party modules can be imported directly in +implementation module units. Partial dependent libraries providing modules ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -In this case, we have to mix the use of ``include`` and ``import`` in the module of our -library. The key point here is still to remove duplicated declarations in translation -units as much as possible. If the imported modules provide headers to skip parsing their -headers, we should include that after the including. If the imported modules don't provide -the headers, we can make it ourselves if we still want to optimize it. - -Known Problems --------------- - -The following describes issues in the current implementation of modules. -Please see https://github.com/llvm/llvm-project/labels/clang%3Amodules for more issues -or file a new issue if you don't find an existing one. -If you're going to create a new issue for standard C++ modules, -please start the title with ``[C++20] [Modules]`` (or ``[C++23] [Modules]``, etc) -and add the label ``clang:modules`` (if you have permissions for that). - -For higher level support for proposals, you could visit https://clang.llvm.org/cxx_status.html. - -Including headers after import is problematic -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +If the library has to mix the use of ``include`` and ``import`` in its module, +the primary goal is still the removal of duplicated declarations in translation +units as much as possible. If the imported modules provide headers to skip +parsing their headers, those should be included after the import. If the +imported modules don't provide such a header, one can be made manually for +improved compile time performance. + +Known Issues +------------ + +The following describes issues in the current implementation of modules. Please +see +`the issues list for modules `_ +for a list of issues or to file a new issue if you don't find an existing one. +When creating a new issue for standard C++ modules, please start the title with +``[C++20] [Modules]`` (or ``[C++23] [Modules]``, etc) and add the label +``clang:modules`` if possible. + +A high-level overview of support for standards features, including modules, can +be found on the `C++ Feature Status `_ +page. + +Including headers after import is not well-supported +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -For example, the following example can be accept: +The following example is accepted: .. code-block:: c++ @@ -1110,8 +1108,8 @@ For example, the following example can be accept: return 0; } -but it will get rejected if we reverse the order of ``#include `` and -``import foo;``: +but if the order of ``#include `` and ``import foo;`` is reversed, +then the code is currently rejected: .. code-block:: c++ @@ -1126,33 +1124,31 @@ but it will get rejected if we reverse the order of ``#include `` and Both of the above examples should be accepted. -This is a limitation in the implementation. In the first example, -the compiler will see and parse first then the compiler will see the import. -So the ODR Checking and declarations merging will happen in the deserializer. -In the second example, the compiler will see the import first and the include second. -As a result, the ODR Checking and declarations merging will happen in the semantic analyzer. +This is a limitation of the implementation. In the first example, the compiler +will see and parse ```` first then it will see the ``import``. In +this case, ODR checking and declaration merging will happen in the +deserializer. In the second example, the compiler will see the ``import`` first +and the ``#include`` second which results in ODR checking and declarations +merging happening in the semantic analyzer. This is due to a divergence in the +implementation path. This is tracked by +`#61465 `_. -So there is divergence in the implementation path. It might be understandable that why -the orders matter here in the case. -(Note that "understandable" is different from "makes sense"). - -This is tracked in: https://github.com/llvm/llvm-project/issues/61465 - -Ignored PreferredName Attribute -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Due to a tricky problem, when Clang writes BMIs, Clang will ignore the ``preferred_name`` attribute, if any. -This implies that the ``preferred_name`` wouldn't show in debugger or dumping. +Ignored ``preferred_name`` Attribute +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -This is tracked in: https://github.com/llvm/llvm-project/issues/56490 +When Clang writes BMIs, it will ignore the ``preferred_name`` attribute on +declarations which use it. Thus, the preferred name will not be displayed in +the debugger as expected. This is tracked by +`#56490 `_. Don't emit macros about module declaration ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -This is covered by P1857R3. We mention it again here since users may abuse it before we implement it. +This is covered by `P1857R3 `_. It is mentioned here +because we want users to be aware that we don't yet implement it. -Someone may want to write code which could be compiled both by modules or non-modules. -A direct idea would be use macros like: +A direct approach to write code that can be compiled by both modules and +non-module builds may look like: .. code-block:: c++ @@ -1162,39 +1158,37 @@ A direct idea would be use macros like: IMPORT header_name EXPORT ... -So this file could be triggered like a module unit or a non-module unit depending on the definition -of some macros. -However, this kind of usage is forbidden by P1857R3 but we haven't implemented P1857R3 yet. -This means that is possible to write illegal modules code now, and obviously this will stop working -once P1857R3 is implemented. -A simple suggestion would be "Don't play macro tricks with module declarations". +The intent of this is that this file can be compiled like a module unit or a +non-module unit depending on the definition of some macros. However, this usage +is forbidden by P1857R3 which is not yet implemented in Clang. This means that +is possible to write invalid modules which will no longer be accepted once +P1857R3 is implemented. This is tracked by +`#56917 `_. + +Until then, it is recommended not to mix macros with module declarations. -This is tracked in: https://github.com/llvm/llvm-project/issues/56917 In consistent filename suffix requirement for importable module units ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Currently, clang requires the file name of an ``importable module unit`` should end with ``.cppm`` -(or ``.ccm``, ``.cxxm``, ``.c++m``). However, the behavior is inconsistent with other compilers. - -This is tracked in: https://github.com/llvm/llvm-project/issues/57416 - -clang-cl is not compatible with the standard C++ modules -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Now we can't use the `/clang:-fmodule-file` or `/clang:-fprebuilt-module-path` to specify -the BMI within ``clang-cl.exe``. +Currently, Clang requires the file name of an ``importable module unit`` to +have ``.cppm`` (or ``.ccm``, ``.cxxm``, ``.c++m``) as the file extension. +However, the behavior is inconsistent with other compilers. This is tracked by +`#57416 `_. -This is tracked in: https://github.com/llvm/llvm-project/issues/64118 +clang-cl is not compatible with standard C++ modules +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -false positive ODR violation diagnostic due to using inconsistent qualified but the same type -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +``/clang:-fmodule-file`` and ``/clang:-fprebuilt-module-path`` cannot be used +to specify the BMI with ``clang-cl.exe``. This is tracked by +`#64118 `_. -ODR violation is a pretty common issue when using modules. -Sometimes the program violated the One Definition Rule actually. -But sometimes it shows the compiler gives false positive diagnostics. +Incorrect ODR violation diagnostics +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -One often reported example is: +ODR violations are a common issue when using modules. Clang sometimes produces +false-positive diagnostics or fails to produce true-positive diagnostics of the +One Definition Rule. One often-reported example is: .. code-block:: c++ @@ -1222,51 +1216,49 @@ One often reported example is: export module repro; export import :part; -Currently the compiler complains about the inconsistent definition of `fun()` in -2 module units. This is incorrect. Since both definitions of `fun()` has the same -spelling and `T` refers to the same type entity finally. So the program should be -fine. - -This is tracked in https://github.com/llvm/llvm-project/issues/78850. +Currently the compiler incorrectly diagnoses the inconsistent definition of +``fun()`` in two module units. Because both definitions of ``fun()`` have the +same spelling and ``T`` refers to the same type entity, there is no ODR +violation. This is tracked by +`#78850 `_. Using TU-local entity in other units ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Module units are translation units. So the entities which should only be local to the -module unit itself shouldn't be used by other units in any means. +Module units are translation units, so the entities which should be local to +the module unit itself should never be used by other units. -In the language side, to address the idea formally, the language specification defines -the concept of ``TU-local`` and ``exposure`` in +The C++ standard defines the concept of ``TU-local`` and ``exposure`` in `basic.link/p14 `_, `basic.link/p15 `_, `basic.link/p16 `_, -`basic.link/p17 `_ and +`basic.link/p17 `_, and `basic.link/p18 `_. -However, the compiler doesn't support these 2 ideas formally. -This results in unclear and confusing diagnostic messages. -And it is worse that the compiler may import TU-local entities to other units without any -diagnostics. +However, Clang doesn't formally support these two concepts. This results in +unclear or confusing diagnostic messages. Further, Clang may import +``TU-local`` entities to other units without any diagnostics. This is tracked +by `#78173 `_. -This is tracked in https://github.com/llvm/llvm-project/issues/78173. +.. _header-units: Header Units ============ -How to build projects using header unit ---------------------------------------- +How to build projects using header units +---------------------------------------- .. warning:: - The user interfaces of header units is highly experimental. There are still - many unanswered question about how tools should interact with header units. - The user interfaces described here may change after we have progress on how - tools should support for header units. + The support for header units, including related command line options, is + experimental. There are still many unanswered question about how tools + should interact with header units. The details described here may change in + the future. Quick Start ~~~~~~~~~~~ -For the following example, +The following example: .. code-block:: c++ @@ -1275,7 +1267,7 @@ For the following example, std::cout << "Hello World.\n"; } -we could compile it as +could be compiled with: .. code-block:: console @@ -1285,14 +1277,14 @@ we could compile it as How to produce BMIs ~~~~~~~~~~~~~~~~~~~ -Similar to named modules, we could use ``--precompile`` to produce the BMI. -But we need to specify that the input file is a header by ``-xc++-system-header`` or ``-xc++-user-header``. +Similar to named modules, ``--precompile`` can be used to produce a BMI. +However, that requires specifying that the input file is a header by using +``-xc++-system-header`` or ``-xc++-user-header``. -Also we could use `-fmodule-header={user,system}` option to produce the BMI for header units -which has suffix like `.h` or `.hh`. -The value of `-fmodule-header` means the user search path or the system search path. -The default value for `-fmodule-header` is `user`. -For example, +The ``-fmodule-header={user,system}`` option can also be used to produce a BMI +for header units which have a file extension like `.h` or `.hh`. The argument to +``-fmodule-header`` specifies either the user search path or the system search +path. The default value for ``-fmodule-header`` is ``user``. For example: .. code-block:: c++ @@ -1308,16 +1300,16 @@ For example, Hello(); } -We could compile it as: +could be compiled with: .. code-block:: console $ clang++ -std=c++20 -fmodule-header foo.h -o foo.pcm $ clang++ -std=c++20 -fmodule-file=foo.pcm use.cpp -For headers which don't have a suffix, we need to pass ``-xc++-header`` -(or ``-xc++-system-header`` or ``-xc++-user-header``) to mark it as a header. -For example, +For headers which do not have a file extension, ``-xc++-header`` (or +``-xc++-system-header``, ``-xc++-user-header``) must be used to specify the +file as a header. For example: .. code-block:: c++ @@ -1332,23 +1324,25 @@ For example, $ clang++ -std=c++20 -fmodule-header=system -xc++-header iostream -o iostream.pcm $ clang++ -std=c++20 -fmodule-file=iostream.pcm use.cpp -How to specify the dependent BMIs -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +How to specify dependent BMIs +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -We could use ``-fmodule-file`` to specify the BMIs, and this option may occur multiple times as well. +``-fmodule-file`` can be used to specify a dependent BMI (or multiple times for +more than one dependent BMI). -With the existing implementation ``-fprebuilt-module-path`` cannot be used for header units -(since they are nominally anonymous). -For header units, use ``-fmodule-file`` to include the relevant PCM file for each header unit. +With the existing implementation, ``-fprebuilt-module-path`` cannot be used for +header units (because they are nominally anonymous). For header units, use +``-fmodule-file`` to include the relevant PCM file for each header unit. -This is expect to be solved in future editions of the compiler either by the tooling finding and specifying -the -fmodule-file or by the use of a module-mapper that understands how to map the header name to their PCMs. +This is expect to be solved in a future version of Clang either by the compiler +finding and specifying ``-fmodule-file`` automatically, or by the use of a +module-mapper that understands how to map the header name to their PCMs. -Don't compile the BMI -~~~~~~~~~~~~~~~~~~~~~ +Compiling a header unit to an object file +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Another difference with modules is that we can't compile the BMI from a header unit. -For example: +A header unit cannot be compiled to an object file due to the semantics of +header units. For example: .. code-block:: console @@ -1356,15 +1350,13 @@ For example: # This is not allowed! $ clang++ iostream.pcm -c -o iostream.o -It makes sense due to the semantics of header units, which are just like headers. - Include translation ~~~~~~~~~~~~~~~~~~~ -The C++ spec allows the vendors to convert ``#include header-name`` to ``import header-name;`` when possible. -Currently, Clang would do this translation for the ``#include`` in the global module fragment. - -For example, the following two examples are the same: +The C++ standard allows vendors to convert ``#include header-name`` to +``import header-name;`` when possible. Currently, Clang does this translation +for the ``#include`` in the global module fragment. For example, the following +example: .. code-block:: c++ @@ -1375,7 +1367,7 @@ For example, the following two examples are the same: std::cout << "Hello.\n"; } -with the following one: +is the same as this example: .. code-block:: c++ @@ -1391,17 +1383,17 @@ with the following one: $ clang++ -std=c++20 -xc++-system-header --precompile iostream -o iostream.pcm $ clang++ -std=c++20 -fmodule-file=iostream.pcm --precompile M.cppm -o M.cpp -In the latter example, the Clang could find the BMI for the ```` -so it would try to replace the ``#include `` to ``import ;`` automatically. +In the latter example, Clang can find the BMI for ```` and so it +tries to replace the ``#include `` with ``import ;`` +automatically. -Relationships between Clang modules ------------------------------------ +Differences between Clang modules and header units +-------------------------------------------------- -Header units have pretty similar semantics with Clang modules. -The semantics of both of them are like headers. - -In fact, we could even "mimic" the sytle of header units by Clang modules: +Header units have similar semantics to Clang modules. The semantics of both are +like headers. Therefore, header units can be mimicked by Clang modules as in +the following example: .. code-block:: c++ @@ -1414,46 +1406,45 @@ In fact, we could even "mimic" the sytle of header units by Clang modules: $ clang++ -std=c++20 -fimplicit-modules -fmodule-map-file=.modulemap main.cpp -It would be simpler if we are using libcxx: +This example is simplified when using libc++: .. code-block:: console $ clang++ -std=c++20 main.cpp -fimplicit-modules -fimplicit-module-maps -Since there is already one -`module map `_ -in the source of libcxx. - -Then immediately leads to the question: why don't we implement header units through Clang header modules? +because libc++ already supplies a +`module map `_. -The main reason for this is that Clang modules have more semantics like hierarchy or -wrapping multiple headers together as a big module. -However, these things are not part of Standard C++ Header units, -and we want to avoid the impression that these additional semantics get interpreted as Standard C++ behavior. +This raises the question: why are header units not implemented through Clang +modules? -Another reason is that there are proposals to introduce module mappers to the C++ standard -(for example, https://wg21.link/p1184r2). -If we decide to reuse Clang's modulemap, we may get in trouble once we need to introduce another module mapper. +This is primarily because Clang modules have more hierarchical semantics when +wrapping multiple headers together as one module, which is not supported by +Standard C++ Header units. We want to avoid the impression that these +additional semantics get interpreted as Standard C++ behavior. -So the final answer for why we don't reuse the interface of Clang modules for header units is that -there are some differences between header units and Clang modules and that ignoring those -differences now would likely become a problem in the future. +Another reason is that there are proposals to introduce module mappers to the +C++ standard (for example, https://wg21.link/p1184r2). Reusing Clang's +``modulemap`` may be more difficult if we need to introduce another module +mapper. -Discover Dependencies -===================== +Discovering Dependencies +======================== -Prior to modules, all the translation units can be compiled parallelly. -But it is not true for the module units. The presence of module units requires -us to compile the translation units in a (topological) order. +Without use of modules, all the translation units in a project can be compiled +in parallel. However, the presence of module units requires compiling the +translation units in a topological order. -The clang-scan-deps scanner implemented -`P1689 paper `_ -to describe the order. Only named modules are supported now. +The ``clang-scan-deps`` tool can extract dependency information and produce a +JSON file conforming to the specification described in +`P1689 `_. +Only named modules are supported currently. -We need a compilation database to use clang-scan-deps. See +A compilation database is needed when using ``clang-scan-deps``. See `JSON Compilation Database Format Specification `_ -for example. Note that the ``output`` entry is necessary for clang-scan-deps -to scan P1689 format. Here is an example: +for more information about compilation databases. Note that the ``output`` +JSON attribute is necessary for ``clang-scan-deps`` to scan using the P1689 +format. For example: .. code-block:: c++ @@ -1533,13 +1524,13 @@ And here is the compilation database: } ] -And we can get the dependency information in P1689 format by: +To get the dependency information in P1689 format, use: .. code-block:: console $ clang-scan-deps -format=p1689 -compilation-database P1689.json -And we will get: +to get: .. code-block:: text @@ -1619,14 +1610,14 @@ And we will get: See the P1689 paper for the meaning of the fields. -And if the user want a finer-grained control for any reason, e.g., to scan the generated source files, -the user can choose to get the dependency information per file. For example: +Getting dependency information per file with finer-grained control (such as +scanning generated source files) is possible. For example: .. code-block:: console $ clang-scan-deps -format=p1689 -- /clang++ -std=c++20 impl_part.cppm -c -o impl_part.o -And we'll get: +will produce: .. code-block:: text @@ -1652,22 +1643,23 @@ And we'll get: "version": 1 } -In this way, we can pass the single command line options after the ``--``. -Then clang-scan-deps will extract the necessary information from the options. -Note that we need to specify the path to the compiler executable instead of saying -``clang++`` simply. +Individual command line options can be specified after ``--``. +``clang-scan-deps`` will extract the necessary information from the specified +options. Note that the path to the compiler executable needs to be specified +explicitly instead of using ``clang++`` directly. -The users may want the scanner to get the transitional dependency information for headers. -Otherwise, the users have to scan twice for the project, once for headers and once for modules. -To address the requirement, clang-scan-deps will recognize the specified preprocessor options -in the given command line and generate the corresponding dependency information. For example, +Users may want the scanner to get the transitional dependency information for +headers. Otherwise, the project has to be scanned twice, once for headers and +once for modules. To address this, ``clang-scan-deps`` will recognize the +specified preprocessor options in the given command line and generate the +corresponding dependency information. For example: .. code-block:: console $ clang-scan-deps -format=p1689 -- ../bin/clang++ -std=c++20 impl_part.cppm -c -o impl_part.o -MD -MT impl_part.ddi -MF impl_part.dep $ cat impl_part.dep -We will get: +will produce: .. code-block:: text @@ -1679,41 +1671,41 @@ We will get: /usr/include/bits/types/__locale_t.h \ ... -When clang-scan-deps detects ``-MF`` option, clang-scan-deps will try to write the +When ``clang-scan-deps`` detects the ``-MF`` option, it will try to write the dependency information for headers to the file specified by ``-MF``. Possible Issues: Failed to find system headers ---------------------------------------------- -In case the users encounter errors like ``fatal error: 'stddef.h' file not found``, -probably the specified ``/clang++`` refers to a symlink -instead a real binary. There are 4 potential solutions to the problem: - -* (1) End users can resolve the issue by pointing the specified compiler executable to - the real binary instead of the symlink. -* (2) End users can invoke ``/clang++ -print-resource-dir`` - to get the corresponding resource directory for your compiler and add that directory - to the include search paths manually in the build scripts. -* (3) Build systems that use a compilation database as the input for clang-scan-deps - scanner, the build system can add the flag ``--resource-dir-recipe invoke-compiler`` to - the clang-scan-deps scanner to calculate the resources directory dynamically. - The calculation happens only once for a unique ``/clang++``. -* (4) For build systems that invokes the clang-scan-deps scanner per file, repeatedly - calculating the resource directory may be inefficient. In such cases, the build - system can cache the resource directory by itself and pass ``-resource-dir `` - explicitly in the command line options: +If encountering an error like ``fatal error: 'stddef.h' file not found``, +the specified ``/clang++`` probably refers to a +symlink instead a real binary. There are four potential solutions to the +problem: -.. code-block:: console +1. Point the specified compiler executable to the real binary instead of the + symlink. +2. Invoke ``/clang++ -print-resource-dir`` to get + the corresponding resource directory for your compiler and add that + directory to the include search paths manually in the build scripts. +3. For build systems that use a compilation database as the input for + ``clang-scan-deps``, the build system can add the + ``--resource-dir-recipe invoke-compiler`` option when executing + ``clang-scan-deps`` to calculate the resource directory dynamically. + The calculation happens only once for a unique ``/clang++``. +4. For build systems that invoke ``clang-scan-deps`` per file, repeatedly + calculating the resource directory may be inefficient. In such cases, the + build system can cache the resource directory and specify + ``-resource-dir `` explicitly, as in: + + .. code-block:: console - $ clang-scan-deps -format=p1689 -- /clang++ -std=c++20 -resource-dir mod.cppm -c -o mod.o + $ clang-scan-deps -format=p1689 -- /clang++ -std=c++20 -resource-dir mod.cppm -c -o mod.o Import modules with clang-repl ============================== -We're able to import C++20 named modules with clang-repl. - -Let's start with a simple example: +``clang-repl`` supports importing C++20 named modules. For example: .. code-block:: c++ @@ -1723,7 +1715,7 @@ Let's start with a simple example: return "Hello Interpreter for Modules!"; } -We still need to compile the named module in ahead. +The named module still needs to be compiled ahead of time. .. code-block:: console @@ -1731,10 +1723,9 @@ We still need to compile the named module in ahead. $ clang++ M.pcm -c -o M.o $ clang++ -shared M.o -o libM.so -Note that we need to compile the module unit into a dynamic library so that the clang-repl -can load the object files of the module units. - -Then we are able to import module ``M`` in clang-repl. +Note that the module unit needs to be compiled as a dynamic library so that +``clang-repl`` can load the object files of the module units. Then it is +possible to import module ``M`` in clang-repl. .. code-block:: console @@ -1753,17 +1744,18 @@ Possible Questions How modules speed up compilation -------------------------------- -A classic theory for the reason why modules speed up the compilation is: -if there are ``n`` headers and ``m`` source files and each header is included by each source file, -then the complexity of the compilation is ``O(n*m)``; -But if there are ``n`` module interfaces and ``m`` source files, the complexity of the compilation is -``O(n+m)``. So, using modules would be a big win when scaling. -In a simpler word, we could get rid of many redundant compilations by using modules. +A classic theory for the reason why modules speed up the compilation is: if +there are ``n`` headers and ``m`` source files and each header is included by +each source file, then the complexity of the compilation is ``O(n*m)``. +However, if there are ``n`` module interfaces and ``m`` source files, the +complexity of the compilation is ``O(n+m)``. Therefore, using modules would be +a significant improvement at scale. More simply, use of modules causes many of +the redundant compilations to no longer be necessary. -Roughly, this theory is correct. But the problem is that it is too rough. -The behavior depends on the optimization level, as we will illustrate below. +While this is accurate at a high level, this depends greatly on the +optimization level, as illustrated below. -First is ``O0``. The compilation process is described in the following graph. +First is ``-O0``. The compilation process is described in the following graph. .. code-block:: none @@ -1771,13 +1763,13 @@ First is ``O0``. The compilation process is described in the following graph. │ │ │ │ └---parsing----sema----codegen--┴----- transformations ---- codegen ----┴---- codegen --┘ - ┌---------------------------------------------------------------------------------------┐ + ├---------------------------------------------------------------------------------------┐ | │ | source file │ | │ └---------------------------------------------------------------------------------------┘ - ┌--------┐ + ├--------┐ │ │ │imported│ │ │ @@ -1785,18 +1777,17 @@ First is ``O0``. The compilation process is described in the following graph. │ │ └--------┘ -Here we can see that the source file (could be a non-module unit or a module unit) would get processed by the -whole pipeline. -But the imported code would only get involved in semantic analysis, which is mainly about name lookup, -overload resolution and template instantiation. -All of these processes are fast relative to the whole compilation process. -More importantly, the imported code only needs to be processed once in frontend code generation, -as well as the whole middle end and backend. -So we could get a big win for the compilation time in O0. +In this case, the source file (which could be a non-module unit or a module +unit) would get processed by the entire pipeline. However, the imported code +would only get involved in semantic analysis, which, for the most part, is name +lookup, overload resolution, and template instantiation. All of these processes +are fast relative to the whole compilation process. More importantly, the +imported code only needs to be processed once during frontend code generation, +as well as the whole middle end and backend. So we could get a big win for the +compilation time in ``-O0``. -But with optimizations, things are different: - -(we omit ``code generation`` part for each end due to the limited space) +But with optimizations, things are different (the ``code generation`` part for +each end is omitted due to limited space): .. code-block:: none @@ -1804,12 +1795,12 @@ But with optimizations, things are different: │ │ │ │ └--- parsing ---- sema -----┴--- optimizations --- IPO ---- optimizations---┴--- optimizations -┘ - ┌-----------------------------------------------------------------------------------------------┐ + ├-----------------------------------------------------------------------------------------------┐ │ │ │ source file │ │ │ └-----------------------------------------------------------------------------------------------┘ - ┌---------------------------------------┐ + ├---------------------------------------┐ │ │ │ │ │ imported code │ @@ -1817,27 +1808,29 @@ But with optimizations, things are different: │ │ └---------------------------------------┘ -It would be very unfortunate if we end up with worse performance after using modules. -The main concern is that when we compile a source file, the compiler needs to see the function body -of imported module units so that it can perform IPO (InterProcedural Optimization, primarily inlining -in practice) to optimize functions in current source file with the help of the information provided by -the imported module units. -In other words, the imported code would be processed again and again in importee units -by optimizations (including IPO itself). -The optimizations before IPO and the IPO itself are the most time-consuming part in whole compilation process. -So from this perspective, we might not be able to get the improvements described in the theory. -But we could still save the time for optimizations after IPO and the whole backend. - -Overall, at ``O0`` the implementations of functions defined in a module will not impact module users, -but at higher optimization levels the definitions of such functions are provided to user compilations for the -purposes of optimization (but definitions of these functions are still not included in the use's object file)- -this means the build speedup at higher optimization levels may be lower than expected given ``O0`` experience, -but does provide by more optimization opportunities. +It would be very unfortunate if we end up with worse performance when using +modules. The main concern is that when a source file is compiled, the compiler +needs to see the body of imported module units so that it can perform IPO +(InterProcedural Optimization, primarily inlining in practice) to optimize +functions in the current source file with the help of the information provided +by the imported module units. In other words, the imported code would be +processed again and again in importee units by optimizations (including IPO +itself). The optimizations before IPO and IPO itself are the most time-consuming +part in whole compilation process. So from this perspective, it might not be +possible to get the compile time improvements described, but there could be +time savings for optimizations after IPO and the whole backend. + +Overall, at ``-O0`` the implementations of functions defined in a module will +not impact module users, but at higher optimization levels the definitions of +such functions are provided to user compilations for the purposes of +optimization (but definitions of these functions are still not included in the +use's object file). This means the build speedup at higher optimization levels +may be lower than expected given ``-O0`` experience, but does provide more +optimization opportunities. Interoperability with Clang Modules ----------------------------------- -We **wish** to support clang modules and standard c++ modules at the same time, -but the mixed using form is not well used/tested yet. - -Please file new github issues as you find interoperability problems. +We **wish** to support Clang modules and standard C++ modules at the same time, +but the mixing them together is not well used/tested yet. Please file new +GitHub issues as you find interoperability problems. diff --git a/clang/docs/tools/clang-formatted-files.txt b/clang/docs/tools/clang-formatted-files.txt index 2252d0ccde96d235a7e45df500ee14a77cfc4340..eaeadf2656b0bffc5378fcb5d9de2d14048f161c 100644 --- a/clang/docs/tools/clang-formatted-files.txt +++ b/clang/docs/tools/clang-formatted-files.txt @@ -632,11 +632,12 @@ clang/unittests/Analysis/FlowSensitive/MapLatticeTest.cpp clang/unittests/Analysis/FlowSensitive/MatchSwitchTest.cpp clang/unittests/Analysis/FlowSensitive/MultiVarConstantPropagationTest.cpp clang/unittests/Analysis/FlowSensitive/SingleVarConstantPropagationTest.cpp -clang/unittests/Analysis/FlowSensitive/SolverTest.cpp +clang/unittests/Analysis/FlowSensitive/SolverTest.h clang/unittests/Analysis/FlowSensitive/TestingSupport.cpp clang/unittests/Analysis/FlowSensitive/TestingSupport.h clang/unittests/Analysis/FlowSensitive/TestingSupportTest.cpp clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp +clang/unittests/Analysis/FlowSensitive/WatchedLiteralsSolverTest.cpp clang/unittests/AST/ASTImporterFixtures.cpp clang/unittests/AST/ASTImporterFixtures.h clang/unittests/AST/ASTImporterObjCTest.cpp diff --git a/clang/include/clang/AST/OpenACCClause.h b/clang/include/clang/AST/OpenACCClause.h index e7b0b411b654fd90eaa7c45dcdacff452598bef8..607a2b9d653678836264e0742840d24d1dc69ec4 100644 --- a/clang/include/clang/AST/OpenACCClause.h +++ b/clang/include/clang/AST/OpenACCClause.h @@ -17,6 +17,8 @@ #include "clang/AST/StmtIterator.h" #include "clang/Basic/OpenACCKinds.h" +#include + namespace clang { /// This is the base type for all OpenACC Clauses. class OpenACCClause { @@ -26,14 +28,17 @@ class OpenACCClause { protected: OpenACCClause(OpenACCClauseKind K, SourceLocation BeginLoc, SourceLocation EndLoc) - : Kind(K), Location(BeginLoc, EndLoc) {} + : Kind(K), Location(BeginLoc, EndLoc) { + assert(!BeginLoc.isInvalid() && !EndLoc.isInvalid() && + "Begin and end location must be valid for OpenACCClause"); + } public: OpenACCClauseKind getClauseKind() const { return Kind; } SourceLocation getBeginLoc() const { return Location.getBegin(); } SourceLocation getEndLoc() const { return Location.getEnd(); } - static bool classof(const OpenACCClause *) { return true; } + static bool classof(const OpenACCClause *) { return false; } using child_iterator = StmtIterator; using const_child_iterator = ConstStmtIterator; @@ -60,6 +65,8 @@ protected: : OpenACCClause(K, BeginLoc, EndLoc), LParenLoc(LParenLoc) {} public: + static bool classof(const OpenACCClause *C); + SourceLocation getLParenLoc() const { return LParenLoc; } child_range children() { @@ -70,6 +77,63 @@ public: } }; +using DeviceTypeArgument = std::pair; +/// A 'device_type' or 'dtype' clause, takes a list of either an 'asterisk' or +/// an identifier. The 'asterisk' means 'the rest'. +class OpenACCDeviceTypeClause final + : public OpenACCClauseWithParams, + public llvm::TrailingObjects { + // Data stored in trailing objects as IdentifierInfo* /SourceLocation pairs. A + // nullptr IdentifierInfo* represents an asterisk. + unsigned NumArchs; + OpenACCDeviceTypeClause(OpenACCClauseKind K, SourceLocation BeginLoc, + SourceLocation LParenLoc, + ArrayRef Archs, + SourceLocation EndLoc) + : OpenACCClauseWithParams(K, BeginLoc, LParenLoc, EndLoc), + NumArchs(Archs.size()) { + assert( + (K == OpenACCClauseKind::DeviceType || K == OpenACCClauseKind::DType) && + "Invalid clause kind for device-type"); + + assert(!llvm::any_of(Archs, [](const DeviceTypeArgument &Arg) { + return Arg.second.isInvalid(); + }) && "Invalid SourceLocation for an argument"); + + assert( + (Archs.size() == 1 || !llvm::any_of(Archs, + [](const DeviceTypeArgument &Arg) { + return Arg.first == nullptr; + })) && + "Only a single asterisk version is permitted, and must be the " + "only one"); + + std::uninitialized_copy(Archs.begin(), Archs.end(), + getTrailingObjects()); + } + +public: + static bool classof(const OpenACCClause *C) { + return C->getClauseKind() == OpenACCClauseKind::DType || + C->getClauseKind() == OpenACCClauseKind::DeviceType; + } + bool hasAsterisk() const { + return getArchitectures().size() > 0 && + getArchitectures()[0].first == nullptr; + } + + ArrayRef getArchitectures() const { + return ArrayRef( + getTrailingObjects(), NumArchs); + } + + static OpenACCDeviceTypeClause * + Create(const ASTContext &C, OpenACCClauseKind K, SourceLocation BeginLoc, + SourceLocation LParenLoc, ArrayRef Archs, + SourceLocation EndLoc); +}; + /// A 'default' clause, has the optional 'none' or 'present' argument. class OpenACCDefaultClause : public OpenACCClauseWithParams { friend class ASTReaderStmt; @@ -89,6 +153,9 @@ protected: } public: + static bool classof(const OpenACCClause *C) { + return C->getClauseKind() == OpenACCClauseKind::Default; + } OpenACCDefaultClauseKind getDefaultClauseKind() const { return DefaultClauseKind; } @@ -113,6 +180,8 @@ protected: ConditionExpr(ConditionExpr) {} public: + static bool classof(const OpenACCClause *C); + bool hasConditionExpr() const { return ConditionExpr; } const Expr *getConditionExpr() const { return ConditionExpr; } Expr *getConditionExpr() { return ConditionExpr; } @@ -140,6 +209,9 @@ protected: Expr *ConditionExpr, SourceLocation EndLoc); public: + static bool classof(const OpenACCClause *C) { + return C->getClauseKind() == OpenACCClauseKind::If; + } static OpenACCIfClause *Create(const ASTContext &C, SourceLocation BeginLoc, SourceLocation LParenLoc, Expr *ConditionExpr, SourceLocation EndLoc); @@ -151,6 +223,9 @@ class OpenACCSelfClause : public OpenACCClauseWithCondition { Expr *ConditionExpr, SourceLocation EndLoc); public: + static bool classof(const OpenACCClause *C) { + return C->getClauseKind() == OpenACCClauseKind::Self; + } static OpenACCSelfClause *Create(const ASTContext &C, SourceLocation BeginLoc, SourceLocation LParenLoc, Expr *ConditionExpr, SourceLocation EndLoc); @@ -177,6 +252,7 @@ protected: llvm::ArrayRef getExprs() const { return Exprs; } public: + static bool classof(const OpenACCClause *C); child_range children() { return child_range(reinterpret_cast(Exprs.begin()), reinterpret_cast(Exprs.end())); @@ -189,6 +265,49 @@ public: } }; +// Represents the 'devnum' and expressions lists for the 'wait' clause. +class OpenACCWaitClause final + : public OpenACCClauseWithExprs, + public llvm::TrailingObjects { + SourceLocation QueuesLoc; + OpenACCWaitClause(SourceLocation BeginLoc, SourceLocation LParenLoc, + Expr *DevNumExpr, SourceLocation QueuesLoc, + ArrayRef QueueIdExprs, SourceLocation EndLoc) + : OpenACCClauseWithExprs(OpenACCClauseKind::Wait, BeginLoc, LParenLoc, + EndLoc), + QueuesLoc(QueuesLoc) { + // The first element of the trailing storage is always the devnum expr, + // whether it is used or not. + std::uninitialized_copy(&DevNumExpr, &DevNumExpr + 1, + getTrailingObjects()); + std::uninitialized_copy(QueueIdExprs.begin(), QueueIdExprs.end(), + getTrailingObjects() + 1); + setExprs( + MutableArrayRef(getTrailingObjects(), QueueIdExprs.size() + 1)); + } + +public: + static bool classof(const OpenACCClause *C) { + return C->getClauseKind() == OpenACCClauseKind::Wait; + } + static OpenACCWaitClause *Create(const ASTContext &C, SourceLocation BeginLoc, + SourceLocation LParenLoc, Expr *DevNumExpr, + SourceLocation QueuesLoc, + ArrayRef QueueIdExprs, + SourceLocation EndLoc); + + bool hasQueuesTag() const { return !QueuesLoc.isInvalid(); } + SourceLocation getQueuesLoc() const { return QueuesLoc; } + bool hasDevNumExpr() const { return getExprs()[0]; } + Expr *getDevNumExpr() const { return getExprs()[0]; } + llvm::ArrayRef getQueueIdExprs() { + return OpenACCClauseWithExprs::getExprs().drop_front(); + } + llvm::ArrayRef getQueueIdExprs() const { + return OpenACCClauseWithExprs::getExprs().drop_front(); + } +}; + class OpenACCNumGangsClause final : public OpenACCClauseWithExprs, public llvm::TrailingObjects { @@ -203,6 +322,9 @@ class OpenACCNumGangsClause final } public: + static bool classof(const OpenACCClause *C) { + return C->getClauseKind() == OpenACCClauseKind::NumGangs; + } static OpenACCNumGangsClause * Create(const ASTContext &C, SourceLocation BeginLoc, SourceLocation LParenLoc, ArrayRef IntExprs, SourceLocation EndLoc); @@ -232,6 +354,7 @@ protected: } public: + static bool classof(const OpenACCClause *C); bool hasIntExpr() const { return !getExprs().empty(); } const Expr *getIntExpr() const { return hasIntExpr() ? getExprs()[0] : nullptr; @@ -245,6 +368,9 @@ class OpenACCNumWorkersClause : public OpenACCClauseWithSingleIntExpr { Expr *IntExpr, SourceLocation EndLoc); public: + static bool classof(const OpenACCClause *C) { + return C->getClauseKind() == OpenACCClauseKind::NumWorkers; + } static OpenACCNumWorkersClause *Create(const ASTContext &C, SourceLocation BeginLoc, SourceLocation LParenLoc, @@ -256,6 +382,9 @@ class OpenACCVectorLengthClause : public OpenACCClauseWithSingleIntExpr { Expr *IntExpr, SourceLocation EndLoc); public: + static bool classof(const OpenACCClause *C) { + return C->getClauseKind() == OpenACCClauseKind::VectorLength; + } static OpenACCVectorLengthClause * Create(const ASTContext &C, SourceLocation BeginLoc, SourceLocation LParenLoc, Expr *IntExpr, SourceLocation EndLoc); @@ -266,6 +395,9 @@ class OpenACCAsyncClause : public OpenACCClauseWithSingleIntExpr { Expr *IntExpr, SourceLocation EndLoc); public: + static bool classof(const OpenACCClause *C) { + return C->getClauseKind() == OpenACCClauseKind::Async; + } static OpenACCAsyncClause *Create(const ASTContext &C, SourceLocation BeginLoc, SourceLocation LParenLoc, Expr *IntExpr, @@ -283,6 +415,7 @@ protected: : OpenACCClauseWithExprs(K, BeginLoc, LParenLoc, EndLoc) {} public: + static bool classof(const OpenACCClause *C); ArrayRef getVarList() { return getExprs(); } ArrayRef getVarList() const { return getExprs(); } }; @@ -301,6 +434,9 @@ class OpenACCPrivateClause final } public: + static bool classof(const OpenACCClause *C) { + return C->getClauseKind() == OpenACCClauseKind::Private; + } static OpenACCPrivateClause * Create(const ASTContext &C, SourceLocation BeginLoc, SourceLocation LParenLoc, ArrayRef VarList, SourceLocation EndLoc); @@ -320,6 +456,9 @@ class OpenACCFirstPrivateClause final } public: + static bool classof(const OpenACCClause *C) { + return C->getClauseKind() == OpenACCClauseKind::FirstPrivate; + } static OpenACCFirstPrivateClause * Create(const ASTContext &C, SourceLocation BeginLoc, SourceLocation LParenLoc, ArrayRef VarList, SourceLocation EndLoc); @@ -339,6 +478,9 @@ class OpenACCDevicePtrClause final } public: + static bool classof(const OpenACCClause *C) { + return C->getClauseKind() == OpenACCClauseKind::DevicePtr; + } static OpenACCDevicePtrClause * Create(const ASTContext &C, SourceLocation BeginLoc, SourceLocation LParenLoc, ArrayRef VarList, SourceLocation EndLoc); @@ -358,6 +500,9 @@ class OpenACCAttachClause final } public: + static bool classof(const OpenACCClause *C) { + return C->getClauseKind() == OpenACCClauseKind::Attach; + } static OpenACCAttachClause * Create(const ASTContext &C, SourceLocation BeginLoc, SourceLocation LParenLoc, ArrayRef VarList, SourceLocation EndLoc); @@ -377,6 +522,9 @@ class OpenACCNoCreateClause final } public: + static bool classof(const OpenACCClause *C) { + return C->getClauseKind() == OpenACCClauseKind::NoCreate; + } static OpenACCNoCreateClause * Create(const ASTContext &C, SourceLocation BeginLoc, SourceLocation LParenLoc, ArrayRef VarList, SourceLocation EndLoc); @@ -396,6 +544,9 @@ class OpenACCPresentClause final } public: + static bool classof(const OpenACCClause *C) { + return C->getClauseKind() == OpenACCClauseKind::Present; + } static OpenACCPresentClause * Create(const ASTContext &C, SourceLocation BeginLoc, SourceLocation LParenLoc, ArrayRef VarList, SourceLocation EndLoc); @@ -419,6 +570,11 @@ class OpenACCCopyClause final } public: + static bool classof(const OpenACCClause *C) { + return C->getClauseKind() == OpenACCClauseKind::Copy || + C->getClauseKind() == OpenACCClauseKind::PCopy || + C->getClauseKind() == OpenACCClauseKind::PresentOrCopy; + } static OpenACCCopyClause * Create(const ASTContext &C, OpenACCClauseKind Spelling, SourceLocation BeginLoc, SourceLocation LParenLoc, @@ -445,6 +601,11 @@ class OpenACCCopyInClause final } public: + static bool classof(const OpenACCClause *C) { + return C->getClauseKind() == OpenACCClauseKind::CopyIn || + C->getClauseKind() == OpenACCClauseKind::PCopyIn || + C->getClauseKind() == OpenACCClauseKind::PresentOrCopyIn; + } bool isReadOnly() const { return IsReadOnly; } static OpenACCCopyInClause * Create(const ASTContext &C, OpenACCClauseKind Spelling, @@ -472,6 +633,11 @@ class OpenACCCopyOutClause final } public: + static bool classof(const OpenACCClause *C) { + return C->getClauseKind() == OpenACCClauseKind::CopyOut || + C->getClauseKind() == OpenACCClauseKind::PCopyOut || + C->getClauseKind() == OpenACCClauseKind::PresentOrCopyOut; + } bool isZero() const { return IsZero; } static OpenACCCopyOutClause * Create(const ASTContext &C, OpenACCClauseKind Spelling, @@ -499,6 +665,11 @@ class OpenACCCreateClause final } public: + static bool classof(const OpenACCClause *C) { + return C->getClauseKind() == OpenACCClauseKind::Create || + C->getClauseKind() == OpenACCClauseKind::PCreate || + C->getClauseKind() == OpenACCClauseKind::PresentOrCreate; + } bool isZero() const { return IsZero; } static OpenACCCreateClause * Create(const ASTContext &C, OpenACCClauseKind Spelling, diff --git a/clang/include/clang/AST/StmtOpenACC.h b/clang/include/clang/AST/StmtOpenACC.h index 66f8f844e0b29e1ef34c202751784224809882b7..b706864798baaf3f156aafcd1aafc848261f2944 100644 --- a/clang/include/clang/AST/StmtOpenACC.h +++ b/clang/include/clang/AST/StmtOpenACC.h @@ -93,6 +93,10 @@ protected: } public: + static bool classof(const Stmt *T) { + return false; + } + child_range children() { if (getAssociatedStmt()) return child_range(&AssociatedStmt, &AssociatedStmt + 1); diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 0225598cbbe8ad8f1d5e8085688e22fda4ef75b1..52552ba488560b2462139026789e564ac0387bda 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -4415,6 +4415,18 @@ def HLSLResourceBinding: InheritableAttr { let Documentation = [HLSLResourceBindingDocs]; } +def HLSLPackOffset: HLSLAnnotationAttr { + let Spellings = [HLSLAnnotation<"packoffset">]; + let LangOpts = [HLSL]; + let Args = [IntArgument<"Subcomponent">, IntArgument<"Component">]; + let Documentation = [HLSLPackOffsetDocs]; + let AdditionalMembers = [{ + unsigned getOffset() { + return subcomponent * 4 + component; + } + }]; +} + def HLSLSV_DispatchThreadID: HLSLAnnotationAttr { let Spellings = [HLSLAnnotation<"SV_DispatchThreadID">]; let Subjects = SubjectList<[ParmVar, Field]>; diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index 8e6faabfae647ab9ca947386cf3651d40fc25837..f351822ac74bd5a451d951305dbfcc6c64c95f55 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -7408,6 +7408,26 @@ The full documentation is available here: https://docs.microsoft.com/en-us/windo }]; } +def HLSLPackOffsetDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +The packoffset attribute is used to change the layout of a cbuffer. +Attribute spelling in HLSL is: ``packoffset( c[Subcomponent][.component] )``. +A subcomponent is a register number, which is an integer. A component is in the form of [.xyzw]. + +Examples: + +.. code-block:: c++ + + cbuffer A { + float3 a : packoffset(c0.y); + float4 b : packoffset(c4); + } + +The full documentation is available here: https://learn.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-variable-packoffset + }]; +} + def HLSLSV_DispatchThreadIDDocs : Documentation { let Category = DocCatFunction; let Content = [{ diff --git a/clang/include/clang/Basic/BuiltinsNVPTX.def b/clang/include/clang/Basic/BuiltinsNVPTX.def index 8d3c5e69d55cf492f27e65fee1aa3a6a9bc9d011..9e243d740ed7aec7e7b8b94f305478e6eba9011d 100644 --- a/clang/include/clang/Basic/BuiltinsNVPTX.def +++ b/clang/include/clang/Basic/BuiltinsNVPTX.def @@ -61,7 +61,9 @@ #pragma push_macro("PTX81") #pragma push_macro("PTX82") #pragma push_macro("PTX83") -#define PTX83 "ptx83" +#pragma push_macro("PTX84") +#define PTX84 "ptx84" +#define PTX83 "ptx83|" PTX84 #define PTX82 "ptx82|" PTX83 #define PTX81 "ptx81|" PTX82 #define PTX80 "ptx80|" PTX81 @@ -1091,3 +1093,4 @@ TARGET_BUILTIN(__nvvm_getctarank_shared_cluster, "iv*3", "", AND(SM_90,PTX78)) #pragma pop_macro("PTX81") #pragma pop_macro("PTX82") #pragma pop_macro("PTX83") +#pragma pop_macro("PTX84") diff --git a/clang/include/clang/Basic/BuiltinsWebAssembly.def b/clang/include/clang/Basic/BuiltinsWebAssembly.def index cf54f8f4422f8865a1f6290b9e1e911b9bf88f5a..8645cff1e8679f36d8595f19a165f9236014e71d 100644 --- a/clang/include/clang/Basic/BuiltinsWebAssembly.def +++ b/clang/include/clang/Basic/BuiltinsWebAssembly.def @@ -192,6 +192,7 @@ TARGET_BUILTIN(__builtin_wasm_relaxed_dot_bf16x8_add_f32_f32x4, "V4fV8UsV8UsV4f" // Half-Precision (fp16) TARGET_BUILTIN(__builtin_wasm_loadf16_f32, "fh*", "nU", "half-precision") +TARGET_BUILTIN(__builtin_wasm_storef16_f32, "vfh*", "n", "half-precision") // Reference Types builtins // Some builtins are custom type-checked - see 't' as part of the third argument, diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def index b964e45574782cf9c25e2a93bfd777977cad2d69..07b0ca1691a679a2148a72734e2a658bb5286d58 100644 --- a/clang/include/clang/Basic/CodeGenOptions.def +++ b/clang/include/clang/Basic/CodeGenOptions.def @@ -309,6 +309,7 @@ CODEGENOPT(UnrollLoops , 1, 0) ///< Control whether loops are unrolled. CODEGENOPT(RerollLoops , 1, 0) ///< Control whether loops are rerolled. CODEGENOPT(NoUseJumpTables , 1, 0) ///< Set when -fno-jump-tables is enabled. VALUE_CODEGENOPT(UnwindTables, 2, 0) ///< Unwind tables (1) or asynchronous unwind tables (2) +CODEGENOPT(LinkBitcodePostopt, 1, 0) ///< Link builtin bitcodes after optimization pipeline. CODEGENOPT(VectorizeLoop , 1, 0) ///< Run loop vectorizer. CODEGENOPT(VectorizeSLP , 1, 0) ///< Run SLP vectorizer. CODEGENOPT(ProfileSampleAccurate, 1, 0) ///< Sample profile is accurate. diff --git a/clang/include/clang/Basic/Cuda.h b/clang/include/clang/Basic/Cuda.h index ba0e4465a0f5a0a2f7673d549c7228406be6a9d4..2d67c4181d12957e0e1df2429e3c6a3f6935c40c 100644 --- a/clang/include/clang/Basic/Cuda.h +++ b/clang/include/clang/Basic/Cuda.h @@ -41,9 +41,10 @@ enum class CudaVersion { CUDA_121, CUDA_122, CUDA_123, + CUDA_124, FULLY_SUPPORTED = CUDA_123, PARTIALLY_SUPPORTED = - CUDA_123, // Partially supported. Proceed with a warning. + CUDA_124, // Partially supported. Proceed with a warning. NEW = 10000, // Too new. Issue a warning, but allow using it. }; const char *CudaVersionToString(CudaVersion V); diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td index 60f87da2a7387c25fff72fefb54d5852e627e299..2beb1d45124b4945f6c4fa7d254380673b5f7d8e 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -1507,6 +1507,9 @@ def BranchProtection : DiagGroup<"branch-protection">; // Warnings for HLSL Clang extensions def HLSLExtension : DiagGroup<"hlsl-extensions">; +// Warning for mix packoffset and non-packoffset. +def HLSLMixPackOffset : DiagGroup<"mix-packoffset">; + // Warnings for DXIL validation def DXILValidation : DiagGroup<"dxil-validation">; diff --git a/clang/include/clang/Basic/DiagnosticInstallAPIKinds.td b/clang/include/clang/Basic/DiagnosticInstallAPIKinds.td index 6896e0f5aa593c2e598bec39e2b331a089352bce..674742431dcb2d7dfff7284ca8d071d361275388 100644 --- a/clang/include/clang/Basic/DiagnosticInstallAPIKinds.td +++ b/clang/include/clang/Basic/DiagnosticInstallAPIKinds.td @@ -25,6 +25,7 @@ def err_unsupported_vendor : Error<"vendor '%0' is not supported: '%1'">; def err_unsupported_environment : Error<"environment '%0' is not supported: '%1'">; def err_unsupported_os : Error<"os '%0' is not supported: '%1'">; def err_cannot_read_input_list : Error<"could not read %select{alias list|filelist}0 '%1': %2">; +def err_invalid_label: Error<"label '%0' is reserved: use a different label name for -X