163 changes: 88 additions & 75 deletions bolt/lib/Core/BinaryEmitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ class BinaryEmitter {

void emitCFIInstruction(const MCCFIInstruction &Inst) const;

/// Emit exception handling ranges for the function.
/// Emit exception handling ranges for the function fragment.
void emitLSDA(BinaryFunction &BF, const FunctionFragment &FF);

/// Emit line number information corresponding to \p NewLoc. \p PrevLoc
Expand Down Expand Up @@ -416,17 +416,6 @@ void BinaryEmitter::emitFunctionBody(BinaryFunction &BF, FunctionFragment &FF,
BF.duplicateConstantIslands();
}

if (!FF.empty() && FF.front()->isLandingPad()) {
assert(!FF.front()->isEntryPoint() &&
"Landing pad cannot be entry point of function");
// If the first block of the fragment is a landing pad, it's offset from the
// start of the area that the corresponding LSDA describes is zero. In this
// case, the call site entries in that LSDA have 0 as offset to the landing
// pad, which the runtime interprets as "no handler". To prevent this,
// insert some padding.
Streamer.emitBytes(BC.MIB->getTrapFillValue());
}

// Track the first emitted instruction with debug info.
bool FirstInstr = true;
for (BinaryBasicBlock *const BB : FF) {
Expand Down Expand Up @@ -906,17 +895,6 @@ void BinaryEmitter::emitLSDA(BinaryFunction &BF, const FunctionFragment &FF) {
if (Sites.empty())
return;

// Calculate callsite table size. Size of each callsite entry is:
//
// sizeof(start) + sizeof(length) + sizeof(LP) + sizeof(uleb128(action))
//
// or
//
// sizeof(dwarf::DW_EH_PE_data4) * 3 + sizeof(uleb128(action))
uint64_t CallSiteTableLength = llvm::size(Sites) * 4 * 3;
for (const auto &FragmentCallSite : Sites)
CallSiteTableLength += getULEB128Size(FragmentCallSite.second.Action);

Streamer.switchSection(BC.MOFI->getLSDASection());

const unsigned TTypeEncoding = BF.getLSDATypeEncoding();
Expand All @@ -937,74 +915,97 @@ void BinaryEmitter::emitLSDA(BinaryFunction &BF, const FunctionFragment &FF) {
// Emit the LSDA header.

// If LPStart is omitted, then the start of the FDE is used as a base for
// landing pad displacements. Then if a cold fragment starts with
// landing pad displacements. Then, if a cold fragment starts with
// a landing pad, this means that the first landing pad offset will be 0.
// As a result, the exception handling runtime will ignore this landing pad
// because zero offset denotes the absence of a landing pad.
// For this reason, when the binary has fixed starting address we emit LPStart
// as 0 and output the absolute value of the landing pad in the table.
// However, C++ runtime will treat 0 as if there is no landing pad, thus we
// cannot emit LP offset as 0.
//
// If the base address can change, we cannot use absolute addresses for
// landing pads (at least not without runtime relocations). Hence, we fall
// back to emitting landing pads relative to the FDE start.
// As we are emitting label differences, we have to guarantee both labels are
// defined in the same section and hence cannot place the landing pad into a
// cold fragment when the corresponding call site is in the hot fragment.
// Because of this issue and the previously described issue of possible
// zero-offset landing pad we have to place landing pads in the same section
// as the corresponding invokes for shared objects.
// As a solution, for fixed-address binaries we set LPStart to 0, and for
// position-independent binaries we offset LP start by one byte.
bool NeedsLPAdjustment = false;
std::function<void(const MCSymbol *)> emitLandingPad;
if (BC.HasFixedLoadAddress) {

// Check if there's a symbol associated with a landing pad fragment.
const MCSymbol *LPStartSymbol = BF.getLPStartSymbol(FF.getFragmentNum());
if (!LPStartSymbol) {
// Since landing pads are not in the same fragment, we fall back to emitting
// absolute addresses for this FDE.
if (opts::Verbosity >= 2) {
BC.outs() << "BOLT-INFO: falling back to generating absolute-address "
<< "exception ranges for " << BF << '\n';
}

assert(BC.HasFixedLoadAddress &&
"Cannot emit absolute-address landing pads for PIE/DSO");

Streamer.emitIntValue(dwarf::DW_EH_PE_udata4, 1); // LPStart format
Streamer.emitIntValue(0, 4); // LPStart
emitLandingPad = [&](const MCSymbol *LPSymbol) {
if (!LPSymbol)
Streamer.emitIntValue(0, 4);
else
if (LPSymbol)
Streamer.emitSymbolValue(LPSymbol, 4);
else
Streamer.emitIntValue(0, 4);
};
} else {
Streamer.emitIntValue(dwarf::DW_EH_PE_omit, 1); // LPStart format
std::optional<FragmentNum> LPFN = BF.getLPFragment(FF.getFragmentNum());
const FunctionFragment &LPFragment = BF.getLayout().getFragment(*LPFN);
NeedsLPAdjustment =
(!LPFragment.empty() && LPFragment.front()->isLandingPad());

// Emit LPStart encoding and optionally LPStart.
if (NeedsLPAdjustment || LPStartSymbol != StartSymbol) {
Streamer.emitIntValue(dwarf::DW_EH_PE_pcrel | dwarf::DW_EH_PE_sdata4, 1);
MCSymbol *DotSymbol = BC.Ctx->createTempSymbol("LPBase");
Streamer.emitLabel(DotSymbol);

const MCExpr *LPStartExpr = MCBinaryExpr::createSub(
MCSymbolRefExpr::create(LPStartSymbol, *BC.Ctx),
MCSymbolRefExpr::create(DotSymbol, *BC.Ctx), *BC.Ctx);
if (NeedsLPAdjustment)
LPStartExpr = MCBinaryExpr::createSub(
LPStartExpr, MCConstantExpr::create(1, *BC.Ctx), *BC.Ctx);
Streamer.emitValue(LPStartExpr, 4);
} else {
// DW_EH_PE_omit means FDE start (StartSymbol) will be used as LPStart.
Streamer.emitIntValue(dwarf::DW_EH_PE_omit, 1);
}
emitLandingPad = [&](const MCSymbol *LPSymbol) {
if (!LPSymbol)
Streamer.emitIntValue(0, 4);
else
Streamer.emitAbsoluteSymbolDiff(LPSymbol, StartSymbol, 4);
if (LPSymbol) {
const MCExpr *LPOffsetExpr = MCBinaryExpr::createSub(
MCSymbolRefExpr::create(LPSymbol, *BC.Ctx),
MCSymbolRefExpr::create(LPStartSymbol, *BC.Ctx), *BC.Ctx);
if (NeedsLPAdjustment)
LPOffsetExpr = MCBinaryExpr::createAdd(
LPOffsetExpr, MCConstantExpr::create(1, *BC.Ctx), *BC.Ctx);
Streamer.emitULEB128Value(LPOffsetExpr);
} else {
Streamer.emitULEB128IntValue(0);
}
};
}

Streamer.emitIntValue(TTypeEncoding, 1); // TType format

// See the comment in EHStreamer::emitExceptionTable() on to use
// uleb128 encoding (which can use variable number of bytes to encode the same
// value) to ensure type info table is properly aligned at 4 bytes without
// iteratively fixing sizes of the tables.
unsigned CallSiteTableLengthSize = getULEB128Size(CallSiteTableLength);
unsigned TTypeBaseOffset =
sizeof(int8_t) + // Call site format
CallSiteTableLengthSize + // Call site table length size
CallSiteTableLength + // Call site table length
BF.getLSDAActionTable().size() + // Actions table size
BF.getLSDATypeTable().size() * TTypeEncodingSize; // Types table size
unsigned TTypeBaseOffsetSize = getULEB128Size(TTypeBaseOffset);
unsigned TotalSize = sizeof(int8_t) + // LPStart format
sizeof(int8_t) + // TType format
TTypeBaseOffsetSize + // TType base offset size
TTypeBaseOffset; // TType base offset
unsigned SizeAlign = (4 - TotalSize) & 3;
MCSymbol *TTBaseLabel = nullptr;
if (TTypeEncoding != dwarf::DW_EH_PE_omit) {
TTBaseLabel = BC.Ctx->createTempSymbol("TTBase");
MCSymbol *TTBaseRefLabel = BC.Ctx->createTempSymbol("TTBaseRef");
Streamer.emitAbsoluteSymbolDiffAsULEB128(TTBaseLabel, TTBaseRefLabel);
Streamer.emitLabel(TTBaseRefLabel);
}

if (TTypeEncoding != dwarf::DW_EH_PE_omit)
// Account for any extra padding that will be added to the call site table
// length.
Streamer.emitULEB128IntValue(TTypeBaseOffset,
/*PadTo=*/TTypeBaseOffsetSize + SizeAlign);
// Emit encoding of entries in the call site table. The format is used for the
// call site start, length, and corresponding landing pad.
if (!LPStartSymbol)
Streamer.emitIntValue(dwarf::DW_EH_PE_sdata4, 1);
else
Streamer.emitIntValue(dwarf::DW_EH_PE_uleb128, 1);

// Emit the landing pad call site table. We use signed data4 since we can emit
// a landing pad in a different part of the split function that could appear
// earlier in the address space than LPStart.
Streamer.emitIntValue(dwarf::DW_EH_PE_sdata4, 1);
Streamer.emitULEB128IntValue(CallSiteTableLength);
MCSymbol *CSTStartLabel = BC.Ctx->createTempSymbol("CSTStart");
MCSymbol *CSTEndLabel = BC.Ctx->createTempSymbol("CSTEnd");
Streamer.emitAbsoluteSymbolDiffAsULEB128(CSTEndLabel, CSTStartLabel);

Streamer.emitLabel(CSTStartLabel);
for (const auto &FragmentCallSite : Sites) {
const BinaryFunction::CallSite &CallSite = FragmentCallSite.second;
const MCSymbol *BeginLabel = CallSite.Start;
Expand All @@ -1015,11 +1016,17 @@ void BinaryEmitter::emitLSDA(BinaryFunction &BF, const FunctionFragment &FF) {

// Start of the range is emitted relative to the start of current
// function split part.
Streamer.emitAbsoluteSymbolDiff(BeginLabel, StartSymbol, 4);
Streamer.emitAbsoluteSymbolDiff(EndLabel, BeginLabel, 4);
if (!LPStartSymbol) {
Streamer.emitAbsoluteSymbolDiff(BeginLabel, StartSymbol, 4);
Streamer.emitAbsoluteSymbolDiff(EndLabel, BeginLabel, 4);
} else {
Streamer.emitAbsoluteSymbolDiffAsULEB128(BeginLabel, StartSymbol);
Streamer.emitAbsoluteSymbolDiffAsULEB128(EndLabel, BeginLabel);
}
emitLandingPad(CallSite.LP);
Streamer.emitULEB128IntValue(CallSite.Action);
}
Streamer.emitLabel(CSTEndLabel);

// Write out action, type, and type index tables at the end.
//
Expand All @@ -1038,6 +1045,8 @@ void BinaryEmitter::emitLSDA(BinaryFunction &BF, const FunctionFragment &FF) {
assert(TypeTable.size() == BF.getLSDATypeTable().size() &&
"indirect type table size mismatch");

Streamer.emitValueToAlignment(Align(TTypeAlignment));

for (int Index = TypeTable.size() - 1; Index >= 0; --Index) {
const uint64_t TypeAddress = TypeTable[Index];
switch (TTypeEncoding & 0x70) {
Expand All @@ -1063,6 +1072,10 @@ void BinaryEmitter::emitLSDA(BinaryFunction &BF, const FunctionFragment &FF) {
}
}
}

if (TTypeEncoding != dwarf::DW_EH_PE_omit)
Streamer.emitLabel(TTBaseLabel);

for (uint8_t const &Byte : BF.getLSDATypeIndexTable())
Streamer.emitIntValue(Byte, 1);
}
Expand Down
9 changes: 9 additions & 0 deletions bolt/lib/Core/BinarySection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,15 @@ void BinarySection::emitAsData(MCStreamer &Streamer,
Streamer.emitLabel(BC.Ctx->getOrCreateSymbol("__hot_data_end"));
}

uint64_t BinarySection::write(raw_ostream &OS) const {
const uint64_t NumValidContentBytes =
std::min<uint64_t>(getOutputContents().size(), getOutputSize());
OS.write(getOutputContents().data(), NumValidContentBytes);
if (getOutputSize() > NumValidContentBytes)
OS.write_zeros(getOutputSize() - NumValidContentBytes);
return getOutputSize();
}

void BinarySection::flushPendingRelocations(raw_pwrite_stream &OS,
SymbolResolverFuncTy Resolver) {
if (PendingRelocations.empty() && Patches.empty())
Expand Down
47 changes: 45 additions & 2 deletions bolt/lib/Passes/SplitFunctions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -901,8 +901,47 @@ void SplitFunctions::splitFunction(BinaryFunction &BF, SplitStrategy &S) {
// have to be placed in the same fragment. When we split them, create
// trampoline landing pads that will redirect the execution to real LPs.
TrampolineSetType Trampolines;
if (!BC.HasFixedLoadAddress && BF.hasEHRanges() && BF.isSplit())
Trampolines = createEHTrampolines(BF);
if (BF.hasEHRanges() && BF.isSplit()) {
// If all landing pads for this fragment are grouped in one (potentially
// different) fragment, we can set LPStart to the start of that fragment
// and avoid trampoline code.
bool NeedsTrampolines = false;
for (FunctionFragment &FF : BF.getLayout().fragments()) {
// Vector of fragments that contain landing pads for this fragment.
SmallVector<FragmentNum, 4> LandingPadFragments;
for (const BinaryBasicBlock *BB : FF)
for (const BinaryBasicBlock *LPB : BB->landing_pads())
LandingPadFragments.push_back(LPB->getFragmentNum());

// Eliminate duplicate entries from the vector.
llvm::sort(LandingPadFragments);
auto Last = llvm::unique(LandingPadFragments);
LandingPadFragments.erase(Last, LandingPadFragments.end());

if (LandingPadFragments.size() == 0) {
// If the fragment has no landing pads, we can safely set itself as its
// landing pad fragment.
BF.setLPFragment(FF.getFragmentNum(), FF.getFragmentNum());
} else if (LandingPadFragments.size() == 1) {
BF.setLPFragment(FF.getFragmentNum(), LandingPadFragments.front());
} else {
if (!BC.HasFixedLoadAddress) {
NeedsTrampolines = true;
break;
} else {
BF.setLPFragment(FF.getFragmentNum(), std::nullopt);
}
}
}

// Trampolines guarantee that all landing pads for any given fragment will
// be contained in the same fragment.
if (NeedsTrampolines) {
for (FunctionFragment &FF : BF.getLayout().fragments())
BF.setLPFragment(FF.getFragmentNum(), FF.getFragmentNum());
Trampolines = createEHTrampolines(BF);
}
}

// Check the new size to see if it's worth splitting the function.
if (BC.isX86() && LayoutUpdated) {
Expand Down Expand Up @@ -933,6 +972,10 @@ void SplitFunctions::splitFunction(BinaryFunction &BF, SplitStrategy &S) {
}
}

// Restore LP fragment for the main fragment if the splitting was undone.
if (BF.hasEHRanges() && !BF.isSplit())
BF.setLPFragment(FragmentNum::main(), FragmentNum::main());

// Fix branches if the splitting decision of the pass after function
// reordering is different from that of the pass before function reordering.
if (LayoutUpdated && BC.HasFinalizedFunctionOrder)
Expand Down
136 changes: 96 additions & 40 deletions bolt/lib/Rewrite/RewriteInstance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3887,6 +3887,43 @@ void RewriteInstance::mapCodeSections(BOLTLinker::SectionMapper MapSection) {

void RewriteInstance::mapAllocatableSections(
BOLTLinker::SectionMapper MapSection) {

if (opts::UseOldText || opts::StrictMode) {
auto tryRewriteSection = [&](BinarySection &OldSection,
BinarySection &NewSection) {
if (OldSection.getSize() < NewSection.getOutputSize())
return;

BC->outs() << "BOLT-INFO: rewriting " << OldSection.getName()
<< " in-place\n";

NewSection.setOutputAddress(OldSection.getAddress());
NewSection.setOutputFileOffset(OldSection.getInputFileOffset());
MapSection(NewSection, OldSection.getAddress());

// Pad contents with zeros.
NewSection.addPadding(OldSection.getSize() - NewSection.getOutputSize());

// Prevent the original section name from appearing in the section header
// table.
OldSection.setAnonymous(true);
};

if (EHFrameSection) {
BinarySection *NewEHFrameSection =
getSection(getNewSecPrefix() + getEHFrameSectionName());
assert(NewEHFrameSection && "New contents expected for .eh_frame");
tryRewriteSection(*EHFrameSection, *NewEHFrameSection);
}
BinarySection *EHSection = getSection(".gcc_except_table");
BinarySection *NewEHSection =
getSection(getNewSecPrefix() + ".gcc_except_table");
if (EHSection) {
assert(NewEHSection && "New contents expected for .gcc_except_table");
tryRewriteSection(*EHSection, *NewEHSection);
}
}

// Allocate read-only sections first, then writable sections.
enum : uint8_t { ST_READONLY, ST_READWRITE };
for (uint8_t SType = ST_READONLY; SType <= ST_READWRITE; ++SType) {
Expand Down Expand Up @@ -4164,7 +4201,6 @@ void RewriteInstance::rewriteNoteSections() {
// New section size.
uint64_t Size = 0;
bool DataWritten = false;
uint8_t *SectionData = nullptr;
// Copy over section contents unless it's one of the sections we overwrite.
if (!willOverwriteSection(SectionName)) {
Size = Section.sh_size;
Expand Down Expand Up @@ -4196,12 +4232,7 @@ void RewriteInstance::rewriteNoteSections() {
if (BSec->getAllocAddress()) {
assert(!DataWritten && "Writing section twice.");
(void)DataWritten;
SectionData = BSec->getOutputData();

LLVM_DEBUG(dbgs() << "BOLT-DEBUG: " << (Size ? "appending" : "writing")
<< " contents to section " << SectionName << '\n');
OS.write(reinterpret_cast<char *>(SectionData), BSec->getOutputSize());
Size += BSec->getOutputSize();
Size += BSec->write(OS);
}

BSec->setOutputFileOffset(NextAvailableOffset);
Expand Down Expand Up @@ -4232,8 +4263,7 @@ void RewriteInstance::rewriteNoteSections() {
<< " of size " << Section.getOutputSize() << " at offset 0x"
<< Twine::utohexstr(Section.getOutputFileOffset()) << '\n');

OS.write(Section.getOutputContents().data(), Section.getOutputSize());
NextAvailableOffset += Section.getOutputSize();
NextAvailableOffset += Section.write(OS);
}
}

Expand Down Expand Up @@ -4347,6 +4377,10 @@ RewriteInstance::getOutputSections(ELFObjectFile<ELFT> *File,
BinarySection *BinSec = BC->getSectionForSectionRef(SecRef);
assert(BinSec && "Matching BinarySection should exist.");

// Exclude anonymous sections.
if (BinSec->isAnonymous())
continue;

addSection(Section, *BinSec);
}

Expand Down Expand Up @@ -5699,8 +5733,8 @@ void RewriteInstance::rewriteFile() {
<< Twine::utohexstr(Section.getAllocAddress()) << "\n of size "
<< Section.getOutputSize() << "\n at offset "
<< Section.getOutputFileOffset() << '\n';
OS.pwrite(reinterpret_cast<const char *>(Section.getOutputData()),
Section.getOutputSize(), Section.getOutputFileOffset());
OS.seek(Section.getOutputFileOffset());
Section.write(OS);
}

for (BinarySection &Section : BC->allocatableSections())
Expand Down Expand Up @@ -5791,42 +5825,64 @@ void RewriteInstance::writeEHFrameHeader() {
LLVM_DEBUG(dbgs() << "BOLT: writing a new " << getEHFrameHdrSectionName()
<< '\n');

NextAvailableAddress =
appendPadding(Out->os(), NextAvailableAddress, EHFrameHdrAlign);
// Try to overwrite the original .eh_frame_hdr if the size permits.
uint64_t EHFrameHdrOutputAddress = 0;
uint64_t EHFrameHdrFileOffset = 0;
std::vector<char> NewEHFrameHdr;
BinarySection *OldEHFrameHdrSection = getSection(getEHFrameHdrSectionName());
if (OldEHFrameHdrSection) {
NewEHFrameHdr = CFIRdWrt->generateEHFrameHeader(
RelocatedEHFrame, NewEHFrame, OldEHFrameHdrSection->getAddress());
if (NewEHFrameHdr.size() <= OldEHFrameHdrSection->getSize()) {
BC->outs() << "BOLT-INFO: rewriting " << getEHFrameHdrSectionName()
<< " in-place\n";
EHFrameHdrOutputAddress = OldEHFrameHdrSection->getAddress();
EHFrameHdrFileOffset = OldEHFrameHdrSection->getInputFileOffset();
} else {
OldEHFrameHdrSection->setOutputName(getOrgSecPrefix() +
getEHFrameHdrSectionName());
OldEHFrameHdrSection = nullptr;
}
}

const uint64_t EHFrameHdrOutputAddress = NextAvailableAddress;
const uint64_t EHFrameHdrFileOffset =
getFileOffsetForAddress(NextAvailableAddress);
// If there was not enough space, allocate more memory for .eh_frame_hdr.
if (!OldEHFrameHdrSection) {
NextAvailableAddress =
appendPadding(Out->os(), NextAvailableAddress, EHFrameHdrAlign);

std::vector<char> NewEHFrameHdr = CFIRdWrt->generateEHFrameHeader(
RelocatedEHFrame, NewEHFrame, EHFrameHdrOutputAddress);
EHFrameHdrOutputAddress = NextAvailableAddress;
EHFrameHdrFileOffset = getFileOffsetForAddress(NextAvailableAddress);

NewEHFrameHdr = CFIRdWrt->generateEHFrameHeader(
RelocatedEHFrame, NewEHFrame, EHFrameHdrOutputAddress);

NextAvailableAddress += NewEHFrameHdr.size();
if (!BC->BOLTReserved.empty() &&
(NextAvailableAddress > BC->BOLTReserved.end())) {
BC->errs() << "BOLT-ERROR: unable to fit " << getEHFrameHdrSectionName()
<< " into reserved space\n";
exit(1);
}

// Create a new entry in the section header table.
const unsigned Flags = BinarySection::getFlags(/*IsReadOnly=*/true,
/*IsText=*/false,
/*IsAllocatable=*/true);
BinarySection &EHFrameHdrSec = BC->registerOrUpdateSection(
getNewSecPrefix() + getEHFrameHdrSectionName(), ELF::SHT_PROGBITS,
Flags, nullptr, NewEHFrameHdr.size(), /*Alignment=*/1);
EHFrameHdrSec.setOutputFileOffset(EHFrameHdrFileOffset);
EHFrameHdrSec.setOutputAddress(EHFrameHdrOutputAddress);
EHFrameHdrSec.setOutputName(getEHFrameHdrSectionName());
}

Out->os().seek(EHFrameHdrFileOffset);
Out->os().write(NewEHFrameHdr.data(), NewEHFrameHdr.size());

const unsigned Flags = BinarySection::getFlags(/*IsReadOnly=*/true,
/*IsText=*/false,
/*IsAllocatable=*/true);
BinarySection *OldEHFrameHdrSection = getSection(getEHFrameHdrSectionName());
// Pad the contents if overwriting in-place.
if (OldEHFrameHdrSection)
OldEHFrameHdrSection->setOutputName(getOrgSecPrefix() +
getEHFrameHdrSectionName());

BinarySection &EHFrameHdrSec = BC->registerOrUpdateSection(
getNewSecPrefix() + getEHFrameHdrSectionName(), ELF::SHT_PROGBITS, Flags,
nullptr, NewEHFrameHdr.size(), /*Alignment=*/1);
EHFrameHdrSec.setOutputFileOffset(EHFrameHdrFileOffset);
EHFrameHdrSec.setOutputAddress(EHFrameHdrOutputAddress);
EHFrameHdrSec.setOutputName(getEHFrameHdrSectionName());

NextAvailableAddress += EHFrameHdrSec.getOutputSize();

if (!BC->BOLTReserved.empty() &&
(NextAvailableAddress > BC->BOLTReserved.end())) {
BC->errs() << "BOLT-ERROR: unable to fit " << getEHFrameHdrSectionName()
<< " into reserved space\n";
exit(1);
}
Out->os().write_zeros(OldEHFrameHdrSection->getSize() -
NewEHFrameHdr.size());

// Merge new .eh_frame with the relocated original so that gdb can locate all
// FDEs.
Expand Down
75 changes: 75 additions & 0 deletions bolt/test/X86/exceptions-compact.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
## Check that llvm-bolt is able to overwrite LSDA in ULEB128 format in-place for
## all types of binaries.

# REQUIRES: system-linux

# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %s -o %t.o
# RUN: ld.lld --no-pie %t.o -o %t.exe -q
# RUN: ld.lld --pie %t.o -o %t.pie -q
# RUN: ld.lld --shared %t.o -o %t.so -q
# RUN: llvm-bolt %t.exe -o %t.bolt --strict \
# RUN: | FileCheck --check-prefix=CHECK-BOLT %s
# RUN: llvm-bolt %t.pie -o %t.pie.bolt --strict \
# RUN: | FileCheck --check-prefix=CHECK-BOLT %s
# RUN: llvm-bolt %t.so -o %t.so.bolt --strict \
# RUN: | FileCheck --check-prefix=CHECK-BOLT %s

# CHECK-BOLT: rewriting .gcc_except_table in-place

# RUN: llvm-readelf -WS %t.bolt | FileCheck --check-prefix=CHECK-ELF %s
# RUN: llvm-readelf -WS %t.pie.bolt | FileCheck --check-prefix=CHECK-ELF %s
# RUN: llvm-readelf -WS %t.so.bolt | FileCheck --check-prefix=CHECK-ELF %s

# CHECK-ELF-NOT: .bolt.org.gcc_except_table

.text
.global foo
.type foo, %function
foo:
.cfi_startproc
ret
.cfi_endproc
.size foo, .-foo

.globl _start
.type _start, %function
_start:
.Lfunc_begin0:
.cfi_startproc
.cfi_lsda 27, .Lexception0
call foo
.Ltmp0:
call foo
.Ltmp1:
ret

## Landing pads.
.LLP1:
ret
.LLP0:
ret

.cfi_endproc
.Lfunc_end0:
.size _start, .-_start

## EH table.
.section .gcc_except_table,"a",@progbits
.p2align 2
GCC_except_table0:
.Lexception0:
.byte 255 # @LPStart Encoding = omit
.byte 255 # @TType Encoding = omit
.byte 1 # Call site Encoding = uleb128
.uleb128 .Lcst_end0-.Lcst_begin0
.Lcst_begin0:
.uleb128 .Lfunc_begin0-.Lfunc_begin0 # >> Call Site 1 <<
.uleb128 .Ltmp0-.Lfunc_begin0 # Call between .Lfunc_begin0 and .Ltmp0
.uleb128 .LLP0-.Lfunc_begin0 # jumps to .LLP0
.byte 0 # On action: cleanup
.uleb128 .Ltmp0-.Lfunc_begin0 # >> Call Site 2 <<
.uleb128 .Ltmp1-.Ltmp0 # Call between .Ltmp0 and .Ltmp1
.uleb128 .LLP1-.Lfunc_begin0 # jumps to .LLP1
.byte 0 # On action: cleanup
.Lcst_end0:

86 changes: 86 additions & 0 deletions bolt/test/X86/pie-eh-split-undo.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# REQUIRES: system-linux

# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %s -o %t.o
# RUN: link_fdata %s %t.o %t.fdata
# RUN: llvm-strip --strip-unneeded %t.o
# RUN: ld.lld --pie %t.o -o %t.exe -q
# RUN: llvm-bolt %t.exe -o %t.out --data %t.fdata --split-functions --split-eh \
# RUN: --split-all-cold --print-after-lowering --print-only=_start 2>&1 \
# RUN: | FileCheck %s

## _start has two landing pads: one hot and one cold. Hence, BOLT will introduce
## a landing pad trampoline. However, the trampoline code will make the main
## split fragment larger than the whole function before split. Then BOLT will
## undo the splitting and remove the trampoline.

# CHECK: Binary Function "_start"
# CHECK: IsSplit :
# CHECK-SAME: 0

## Check that a landing pad trampoline was created, but contains no instructions
## and falls though to the real landing pad.

# CHECK: {{^[^[:space:]]+}} (0 instructions
# CHECK-NEXT: Landing Pad{{$}}
# CHECK: Exec Count
# CHECK-SAME: : 0
# CHECK: Successors:
# CHECK-SAME: [[LP:[^[:space:]]+]]
# CHECK-EMPTY:
# CHECK-NEXT: [[LP]]

.text
.global foo
.type foo, %function
foo:
.cfi_startproc
ret
.cfi_endproc
.size foo, .-foo

.globl _start
.type _start, %function
_start:
# FDATA: 0 [unknown] 0 1 _start 0 1 100
.Lfunc_begin0:
.cfi_startproc
.cfi_lsda 27, .Lexception0
call foo
.Ltmp0:
call foo
.Ltmp1:
ret

## Cold landing pad.
.LLP1:
ret

## Hot landing pad.
LLP0:
# FDATA: 0 [unknown] 0 1 _start #LLP0# 1 100
ret

.cfi_endproc
.Lfunc_end0:
.size _start, .-_start

## EH table.
.section .gcc_except_table,"a",@progbits
.p2align 2
GCC_except_table0:
.Lexception0:
.byte 255 # @LPStart Encoding = omit
.byte 255 # @TType Encoding = omit
.byte 1 # Call site Encoding = uleb128
.uleb128 .Lcst_end0-.Lcst_begin0
.Lcst_begin0:
.uleb128 .Lfunc_begin0-.Lfunc_begin0 # >> Call Site 1 <<
.uleb128 .Ltmp0-.Lfunc_begin0 # Call between .Lfunc_begin0 and .Ltmp0
.uleb128 LLP0-.Lfunc_begin0 # jumps to LLP0
.byte 0 # On action: cleanup
.uleb128 .Ltmp0-.Lfunc_begin0 # >> Call Site 2 <<
.uleb128 .Ltmp1-.Ltmp0 # Call between .Ltmp0 and .Ltmp1
.uleb128 .LLP1-.Lfunc_begin0 # jumps to .LLP1
.byte 0 # On action: cleanup
.Lcst_end0:

12 changes: 12 additions & 0 deletions bolt/test/eh-frame-hdr.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Check that llvm-bolt overwrites .eh_frame_hdr in-place.

REQUIRES: system-linux

RUN: %clang %cflags %p/Inputs/hello.c -o %t -Wl,-q
RUN: llvm-bolt %t -o %t.bolt --use-old-text \
RUN: | FileCheck %s --check-prefix=CHECK-BOLT
RUN: llvm-readelf -WS %t.bolt | FileCheck %s

CHECK-BOLT: rewriting .eh_frame_hdr in-place

CHECK-NOT: .bolt.org.eh_frame_hdr
8 changes: 8 additions & 0 deletions bolt/test/eh-frame-overwrite.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Check that llvm-bolt can overwrite .eh_frame section in-place.

REQUIRES: system-linux

RUN: %clang %cflags %p/Inputs/hello.c -o %t -Wl,-q
RUN: llvm-bolt %t -o %t.bolt --strict | FileCheck %s

CHECK: rewriting .eh_frame in-place
2 changes: 1 addition & 1 deletion bolt/test/runtime/X86/Inputs/pie-exceptions-failed-split.s
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Assembly generated from building the followingC++ code with the following
# Assembly generated from building the following C++ code with the following
# command using trunk clang. Then, basic block at .LBB1_7 was moved before the
# landing pad.
#
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,25 +11,16 @@ RUN: llvm-bolt %t -o %t.bolt --data %t.fdata --reorder-blocks=ext-tsp \
RUN: --split-functions --split-eh --print-after-lowering \
RUN: --print-only=_Z10throw_testiPPc 2>&1 | FileCheck %s

## Hot code in the test case gets larger after splitting because of jump
## instruction relaxation. Check that BOLT reverts the split correctly.
## Check that a landing pad is split from its thrower and does not require a
## trampoline LP.
CHECK: Binary Function "_Z10throw_testiPPc"
CHECK: IsSplit :
CHECK-SAME: 0

## Check that the landing pad trampoline was created, but contains no
## instructions and falls to the real landing pad.
CHECK: {{^[^[:space:]]+}} (0 instructions
CHECK-NEXT: Landing Pad{{$}}
CHECK: Exec Count
CHECK-SAME: : 0
CHECK: Successors:
CHECK-SAME: [[LP:[^[:space:]]+]]
CHECK-EMPTY:
CHECK-NEXT: [[LP]]
CHECK-DAG: Exec Count
CHECK-NOT: Exec Count
CHECK-DAG: callq __cxa_begin_catch
CHECK-SAME: 1
CHECK: callq {{.*}} # handler: [[LPAD:.*]];
CHECK-NOT: Landing Pad{{$}}
CHECK: HOT-COLD SPLIT POINT
CHECK: {{^}}[[LPAD]]
CHECK-NEXT: Landing Pad

## Verify the output still executes correctly when the exception path is being
## taken.
Expand Down
5 changes: 3 additions & 2 deletions bolt/unittests/Core/MCPlusBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,14 +90,15 @@ INSTANTIATE_TEST_SUITE_P(AArch64, MCPlusBuilderTester,
::testing::Values(Triple::aarch64));

TEST_P(MCPlusBuilderTester, AliasX0) {
uint64_t AliasesX0[] = {AArch64::W0, AArch64::X0, AArch64::W0_W1,
uint64_t AliasesX0[] = {AArch64::W0, AArch64::W0_HI,
AArch64::X0, AArch64::W0_W1,
AArch64::X0_X1, AArch64::X0_X1_X2_X3_X4_X5_X6_X7};
size_t AliasesX0Count = sizeof(AliasesX0) / sizeof(*AliasesX0);
testRegAliases(Triple::aarch64, AArch64::X0, AliasesX0, AliasesX0Count);
}

TEST_P(MCPlusBuilderTester, AliasSmallerX0) {
uint64_t AliasesX0[] = {AArch64::W0, AArch64::X0};
uint64_t AliasesX0[] = {AArch64::W0, AArch64::W0_HI, AArch64::X0};
size_t AliasesX0Count = sizeof(AliasesX0) / sizeof(*AliasesX0);
testRegAliases(Triple::aarch64, AArch64::X0, AliasesX0, AliasesX0Count, true);
}
Expand Down
3 changes: 2 additions & 1 deletion clang-tools-extra/clang-include-fixer/IncludeFixer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,8 @@ bool IncludeFixerActionFactory::runInvocation(

// Create the compiler's actual diagnostics engine. We want to drop all
// diagnostics here.
Compiler.createDiagnostics(new clang::IgnoringDiagConsumer,
Compiler.createDiagnostics(Files->getVirtualFileSystem(),
new clang::IgnoringDiagConsumer,
/*ShouldOwnClient=*/true);
Compiler.createSourceManager(*Files);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ namespace {

AST_MATCHER_P(FunctionDecl, isEnabled, llvm::StringSet<>,
FunctionsThatShouldNotThrow) {
return FunctionsThatShouldNotThrow.count(Node.getNameAsString()) > 0;
return FunctionsThatShouldNotThrow.contains(Node.getNameAsString());
}

AST_MATCHER(FunctionDecl, isExplicitThrow) {
Expand Down
9 changes: 5 additions & 4 deletions clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -315,9 +315,10 @@ void UseAfterMoveFinder::getReinits(
"::std::unordered_map", "::std::unordered_multiset",
"::std::unordered_multimap"))))));

auto StandardSmartPointerTypeMatcher = hasType(hasUnqualifiedDesugaredType(
recordType(hasDeclaration(cxxRecordDecl(hasAnyName(
"::std::unique_ptr", "::std::shared_ptr", "::std::weak_ptr"))))));
auto StandardResettableOwnerTypeMatcher = hasType(
hasUnqualifiedDesugaredType(recordType(hasDeclaration(cxxRecordDecl(
hasAnyName("::std::unique_ptr", "::std::shared_ptr",
"::std::weak_ptr", "::std::optional", "::std::any"))))));

// Matches different types of reinitialization.
auto ReinitMatcher =
Expand All @@ -340,7 +341,7 @@ void UseAfterMoveFinder::getReinits(
callee(cxxMethodDecl(hasAnyName("clear", "assign")))),
// reset() on standard smart pointers.
cxxMemberCallExpr(
on(expr(DeclRefMatcher, StandardSmartPointerTypeMatcher)),
on(expr(DeclRefMatcher, StandardResettableOwnerTypeMatcher)),
callee(cxxMethodDecl(hasName("reset")))),
// Methods that have the [[clang::reinitializes]] attribute.
cxxMemberCallExpr(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ static bool checkOverridingFunctionReturnType(const ASTContext *Context,

// The class type D should have the same cv-qualification as or less
// cv-qualification than the class type B.
if (DTy.isMoreQualifiedThan(BTy))
if (DTy.isMoreQualifiedThan(BTy, *Context))
return false;

return true;
Expand Down
3 changes: 2 additions & 1 deletion clang-tools-extra/clang-tidy/modernize/UseStdPrintCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ AST_MATCHER(StringLiteral, isOrdinary) { return Node.isOrdinary(); }
} // namespace

UseStdPrintCheck::UseStdPrintCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
: ClangTidyCheck(Name, Context), PP(nullptr),
StrictMode(Options.getLocalOrGlobal("StrictMode", false)),
PrintfLikeFunctions(utils::options::parseStringList(
Options.get("PrintfLikeFunctions", ""))),
Expand Down Expand Up @@ -131,6 +131,7 @@ void UseStdPrintCheck::check(const MatchFinder::MatchResult &Result) {
utils::FormatStringConverter::Configuration ConverterConfig;
ConverterConfig.StrictMode = StrictMode;
ConverterConfig.AllowTrailingNewlineRemoval = true;
assert(PP && "Preprocessor should be set by registerPPCallbacks");
utils::FormatStringConverter Converter(
Result.Context, Printf, FormatArgOffset, ConverterConfig, getLangOpts(),
*Result.SourceManager, *PP);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -299,10 +299,11 @@ static bool applyDiceHeuristic(StringRef Arg, StringRef Param,

/// Checks if ArgType binds to ParamType regarding reference-ness and
/// cv-qualifiers.
static bool areRefAndQualCompatible(QualType ArgType, QualType ParamType) {
static bool areRefAndQualCompatible(QualType ArgType, QualType ParamType,
const ASTContext &Ctx) {
return !ParamType->isReferenceType() ||
ParamType.getNonReferenceType().isAtLeastAsQualifiedAs(
ArgType.getNonReferenceType());
ArgType.getNonReferenceType(), Ctx);
}

static bool isPointerOrArray(QualType TypeToCheck) {
Expand All @@ -311,12 +312,12 @@ static bool isPointerOrArray(QualType TypeToCheck) {

/// Checks whether ArgType is an array type identical to ParamType's array type.
/// Enforces array elements' qualifier compatibility as well.
static bool isCompatibleWithArrayReference(QualType ArgType,
QualType ParamType) {
static bool isCompatibleWithArrayReference(QualType ArgType, QualType ParamType,
const ASTContext &Ctx) {
if (!ArgType->isArrayType())
return false;
// Here, qualifiers belong to the elements of the arrays.
if (!ParamType.isAtLeastAsQualifiedAs(ArgType))
if (!ParamType.isAtLeastAsQualifiedAs(ArgType, Ctx))
return false;

return ParamType.getUnqualifiedType() == ArgType.getUnqualifiedType();
Expand All @@ -342,12 +343,13 @@ static QualType convertToPointeeOrArrayElementQualType(QualType TypeToConvert) {
/// every * in ParamType to the right of that cv-qualifier, except the last
/// one, must also be const-qualified.
static bool arePointersStillQualCompatible(QualType ArgType, QualType ParamType,
bool &IsParamContinuouslyConst) {
bool &IsParamContinuouslyConst,
const ASTContext &Ctx) {
// The types are compatible, if the parameter is at least as qualified as the
// argument, and if it is more qualified, it has to be const on upper pointer
// levels.
bool AreTypesQualCompatible =
ParamType.isAtLeastAsQualifiedAs(ArgType) &&
ParamType.isAtLeastAsQualifiedAs(ArgType, Ctx) &&
(!ParamType.hasQualifiers() || IsParamContinuouslyConst);
// Check whether the parameter's constness continues at the current pointer
// level.
Expand All @@ -359,9 +361,10 @@ static bool arePointersStillQualCompatible(QualType ArgType, QualType ParamType,
/// Checks whether multilevel pointers are compatible in terms of levels,
/// qualifiers and pointee type.
static bool arePointerTypesCompatible(QualType ArgType, QualType ParamType,
bool IsParamContinuouslyConst) {
bool IsParamContinuouslyConst,
const ASTContext &Ctx) {
if (!arePointersStillQualCompatible(ArgType, ParamType,
IsParamContinuouslyConst))
IsParamContinuouslyConst, Ctx))
return false;

do {
Expand All @@ -372,7 +375,7 @@ static bool arePointerTypesCompatible(QualType ArgType, QualType ParamType,
// Check whether cv-qualifiers permit compatibility on
// current level.
if (!arePointersStillQualCompatible(ArgType, ParamType,
IsParamContinuouslyConst))
IsParamContinuouslyConst, Ctx))
return false;

if (ParamType.getUnqualifiedType() == ArgType.getUnqualifiedType())
Expand All @@ -396,7 +399,7 @@ static bool areTypesCompatible(QualType ArgType, QualType ParamType,
return true;

// Check for constness and reference compatibility.
if (!areRefAndQualCompatible(ArgType, ParamType))
if (!areRefAndQualCompatible(ArgType, ParamType, Ctx))
return false;

bool IsParamReference = ParamType->isReferenceType();
Expand Down Expand Up @@ -434,7 +437,7 @@ static bool areTypesCompatible(QualType ArgType, QualType ParamType,
// When ParamType is an array reference, ArgType has to be of the same-sized
// array-type with cv-compatible element type.
if (IsParamReference && ParamType->isArrayType())
return isCompatibleWithArrayReference(ArgType, ParamType);
return isCompatibleWithArrayReference(ArgType, ParamType, Ctx);

bool IsParamContinuouslyConst =
!IsParamReference || ParamType.getNonReferenceType().isConstQualified();
Expand All @@ -444,7 +447,7 @@ static bool areTypesCompatible(QualType ArgType, QualType ParamType,
ParamType = convertToPointeeOrArrayElementQualType(ParamType);

// Check qualifier compatibility on the next level.
if (!ParamType.isAtLeastAsQualifiedAs(ArgType))
if (!ParamType.isAtLeastAsQualifiedAs(ArgType, Ctx))
return false;

if (ParamType.getUnqualifiedType() == ArgType.getUnqualifiedType())
Expand Down Expand Up @@ -472,8 +475,8 @@ static bool areTypesCompatible(QualType ArgType, QualType ParamType,
if (!(ParamType->isAnyPointerType() && ArgType->isAnyPointerType()))
return false;

return arePointerTypesCompatible(ArgType, ParamType,
IsParamContinuouslyConst);
return arePointerTypesCompatible(ArgType, ParamType, IsParamContinuouslyConst,
Ctx);
}

static bool isOverloadedUnaryOrBinarySymbolOperator(const FunctionDecl *FD) {
Expand Down
13 changes: 10 additions & 3 deletions clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,12 @@ bool isQualificationConvertiblePointer(QualType From, QualType To,
} // namespace

static bool canThrow(const FunctionDecl *Func) {
// consteval specifies that every call to the function must produce a
// compile-time constant, which cannot evaluate a throw expression without
// producing a compilation error.
if (Func->isConsteval())
return false;

const auto *FunProto = Func->getType()->getAs<FunctionProtoType>();
if (!FunProto)
return true;
Expand Down Expand Up @@ -418,7 +424,7 @@ ExceptionAnalyzer::ExceptionInfo::filterIgnoredExceptions(
if (TD->getDeclName().isIdentifier()) {
if ((IgnoreBadAlloc &&
(TD->getName() == "bad_alloc" && TD->isInStdNamespace())) ||
(IgnoredTypes.count(TD->getName()) > 0))
(IgnoredTypes.contains(TD->getName())))
TypesToDelete.push_back(T);
}
}
Expand Down Expand Up @@ -449,7 +455,8 @@ void ExceptionAnalyzer::ExceptionInfo::reevaluateBehaviour() {
ExceptionAnalyzer::ExceptionInfo ExceptionAnalyzer::throwsException(
const FunctionDecl *Func, const ExceptionInfo::Throwables &Caught,
llvm::SmallSet<const FunctionDecl *, 32> &CallStack) {
if (!Func || CallStack.count(Func) || (!CallStack.empty() && !canThrow(Func)))
if (!Func || CallStack.contains(Func) ||
(!CallStack.empty() && !canThrow(Func)))
return ExceptionInfo::createNonThrowing();

if (const Stmt *Body = Func->getBody()) {
Expand Down Expand Up @@ -507,7 +514,7 @@ ExceptionAnalyzer::ExceptionInfo ExceptionAnalyzer::throwsException(
for (unsigned I = 0; I < Try->getNumHandlers(); ++I) {
const CXXCatchStmt *Catch = Try->getHandler(I);

// Everything is catched through 'catch(...)'.
// Everything is caught through 'catch(...)'.
if (!Catch->getExceptionDecl()) {
ExceptionInfo Rethrown = throwsException(
Catch->getHandlerBlock(), Uncaught.getExceptionTypes(), CallStack);
Expand Down
4 changes: 2 additions & 2 deletions clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.h
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,8 @@ class ExceptionAnalyzer {
/// Recalculate the 'Behaviour' for example after filtering.
void reevaluateBehaviour();

/// Keep track if the entity related to this 'ExceptionInfo' can in princple
/// throw, if it's unknown or if it won't throw.
/// Keep track if the entity related to this 'ExceptionInfo' can in
/// principle throw, if it's unknown or if it won't throw.
State Behaviour;

/// Keep track if the entity contains any unknown elements to keep track
Expand Down
13 changes: 5 additions & 8 deletions clang-tools-extra/clangd/ClangdLSPServer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1419,15 +1419,12 @@ void ClangdLSPServer::applyConfiguration(
const ConfigurationSettings &Settings) {
// Per-file update to the compilation database.
llvm::StringSet<> ModifiedFiles;
for (auto &Entry : Settings.compilationDatabaseChanges) {
PathRef File = Entry.first;
auto Old = CDB->getCompileCommand(File);
auto New =
tooling::CompileCommand(std::move(Entry.second.workingDirectory), File,
std::move(Entry.second.compilationCommand),
for (auto &[File, Command] : Settings.compilationDatabaseChanges) {
auto Cmd =
tooling::CompileCommand(std::move(Command.workingDirectory), File,
std::move(Command.compilationCommand),
/*Output=*/"");
if (Old != New) {
CDB->setCompileCommand(File, std::move(New));
if (CDB->setCompileCommand(File, std::move(Cmd))) {
ModifiedFiles.insert(File);
}
}
Expand Down
6 changes: 3 additions & 3 deletions clang-tools-extra/clangd/Compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,8 @@ buildCompilerInvocation(const ParseInputs &Inputs, clang::DiagnosticConsumer &D,
CIOpts.VFS = Inputs.TFS->view(Inputs.CompileCommand.Directory);
CIOpts.CC1Args = CC1Args;
CIOpts.RecoverOnError = true;
CIOpts.Diags =
CompilerInstance::createDiagnostics(new DiagnosticOptions, &D, false);
CIOpts.Diags = CompilerInstance::createDiagnostics(
*CIOpts.VFS, new DiagnosticOptions, &D, false);
CIOpts.ProbePrecompiled = false;
std::unique_ptr<CompilerInvocation> CI = createInvocation(ArgStrs, CIOpts);
if (!CI)
Expand Down Expand Up @@ -148,7 +148,7 @@ prepareCompilerInstance(std::unique_ptr<clang::CompilerInvocation> CI,
auto Clang = std::make_unique<CompilerInstance>(
std::make_shared<PCHContainerOperations>());
Clang->setInvocation(std::move(CI));
Clang->createDiagnostics(&DiagsClient, false);
Clang->createDiagnostics(*VFS, &DiagsClient, false);

if (auto VFSWithRemapping = createVFSFromCompilerInvocation(
Clang->getInvocation(), Clang->getDiagnostics(), VFS))
Expand Down
15 changes: 11 additions & 4 deletions clang-tools-extra/clangd/GlobalCompilationDatabase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -807,20 +807,27 @@ tooling::CompileCommand OverlayCDB::getFallbackCommand(PathRef File) const {
return Cmd;
}

void OverlayCDB::setCompileCommand(PathRef File,
bool OverlayCDB::setCompileCommand(PathRef File,
std::optional<tooling::CompileCommand> Cmd) {
// We store a canonical version internally to prevent mismatches between set
// and get compile commands. Also it assures clients listening to broadcasts
// doesn't receive different names for the same file.
std::string CanonPath = removeDots(File);
{
std::unique_lock<std::mutex> Lock(Mutex);
if (Cmd)
Commands[CanonPath] = std::move(*Cmd);
else
if (Cmd) {
if (auto [It, Inserted] =
Commands.try_emplace(CanonPath, std::move(*Cmd));
!Inserted) {
if (It->second == *Cmd)
return false;
It->second = *Cmd;
}
} else
Commands.erase(CanonPath);
}
OnCommandChanged.broadcast({CanonPath});
return true;
}

DelegatingCDB::DelegatingCDB(const GlobalCompilationDatabase *Base)
Expand Down
4 changes: 3 additions & 1 deletion clang-tools-extra/clangd/GlobalCompilationDatabase.h
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,9 @@ class OverlayCDB : public DelegatingCDB {
tooling::CompileCommand getFallbackCommand(PathRef File) const override;

/// Sets or clears the compilation command for a particular file.
void
/// Returns true if the command was changed (including insertion and removal),
/// false if it was unchanged.
bool
setCompileCommand(PathRef File,
std::optional<tooling::CompileCommand> CompilationCommand);

Expand Down
13 changes: 9 additions & 4 deletions clang-tools-extra/clangd/InlayHints.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -626,10 +626,15 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {

bool VisitLambdaExpr(LambdaExpr *E) {
FunctionDecl *D = E->getCallOperator();
if (!E->hasExplicitResultType())
addReturnTypeHint(D, E->hasExplicitParameters()
? D->getFunctionTypeLoc().getRParenLoc()
: E->getIntroducerRange().getEnd());
if (!E->hasExplicitResultType()) {
SourceLocation TypeHintLoc;
if (!E->hasExplicitParameters())
TypeHintLoc = E->getIntroducerRange().getEnd();
else if (auto FTL = D->getFunctionTypeLoc())
TypeHintLoc = FTL.getRParenLoc();
if (TypeHintLoc.isValid())
addReturnTypeHint(D, TypeHintLoc);
}
return true;
}

Expand Down
3 changes: 2 additions & 1 deletion clang-tools-extra/clangd/ModulesBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,8 @@ bool IsModuleFileUpToDate(PathRef ModuleFilePath,

clang::clangd::IgnoreDiagnostics IgnoreDiags;
IntrusiveRefCntPtr<DiagnosticsEngine> Diags =
CompilerInstance::createDiagnostics(new DiagnosticOptions, &IgnoreDiags,
CompilerInstance::createDiagnostics(*VFS, new DiagnosticOptions,
&IgnoreDiags,
/*ShouldOwnClient=*/false);

LangOptions LangOpts;
Expand Down
4 changes: 2 additions & 2 deletions clang-tools-extra/clangd/Preamble.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -613,8 +613,9 @@ buildPreamble(PathRef FileName, CompilerInvocation CI,
for (const auto &L : ASTListeners)
L->sawDiagnostic(D, Diag);
});
auto VFS = Inputs.TFS->view(Inputs.CompileCommand.Directory);
llvm::IntrusiveRefCntPtr<DiagnosticsEngine> PreambleDiagsEngine =
CompilerInstance::createDiagnostics(&CI.getDiagnosticOpts(),
CompilerInstance::createDiagnostics(*VFS, &CI.getDiagnosticOpts(),
&PreambleDiagnostics,
/*ShouldOwnClient=*/false);
const Config &Cfg = Config::current();
Expand Down Expand Up @@ -651,7 +652,6 @@ buildPreamble(PathRef FileName, CompilerInvocation CI,
for (const auto &L : ASTListeners)
L->beforeExecute(CI);
});
auto VFS = Inputs.TFS->view(Inputs.CompileCommand.Directory);
llvm::SmallString<32> AbsFileName(FileName);
VFS->makeAbsolute(AbsFileName);
auto StatCache = std::make_shared<PreambleFileStatusCache>(AbsFileName);
Expand Down
29 changes: 29 additions & 0 deletions clang-tools-extra/clangd/Protocol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,35 @@ bool fromJSON(const llvm::json::Value &Params, ClientCapabilities &R,
if (auto EditsNearCursor = Completion->getBoolean("editsNearCursor"))
R.CompletionFixes |= *EditsNearCursor;
}
if (auto *References = TextDocument->getObject("references")) {
if (auto ContainerSupport = References->getBoolean("container")) {
R.ReferenceContainer |= *ContainerSupport;
}
}
if (auto *Diagnostics = TextDocument->getObject("publishDiagnostics")) {
if (auto CodeActions = Diagnostics->getBoolean("codeActionsInline")) {
R.DiagnosticFixes |= *CodeActions;
}
}
if (auto *InactiveRegions =
TextDocument->getObject("inactiveRegionsCapabilities")) {
if (auto InactiveRegionsSupport =
InactiveRegions->getBoolean("inactiveRegions")) {
R.InactiveRegions |= *InactiveRegionsSupport;
}
}
}
if (auto *Window = Experimental->getObject("window")) {
if (auto Implicit =
Window->getBoolean("implicitWorkDoneProgressCreate")) {
R.ImplicitProgressCreation |= *Implicit;
}
}
if (auto *OffsetEncoding = Experimental->get("offsetEncoding")) {
R.offsetEncoding.emplace();
if (!fromJSON(*OffsetEncoding, *R.offsetEncoding,
P.field("offsetEncoding")))
return false;
}
}

Expand Down
3 changes: 3 additions & 0 deletions clang-tools-extra/clangd/Protocol.h
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,7 @@ struct ClientCapabilities {
std::optional<SymbolKindBitset> WorkspaceSymbolKinds;

/// Whether the client accepts diagnostics with codeActions attached inline.
/// This is a clangd extension.
/// textDocument.publishDiagnostics.codeActionsInline.
bool DiagnosticFixes = false;

Expand All @@ -475,6 +476,7 @@ struct ClientCapabilities {

/// Client supports displaying a container string for results of
/// textDocument/reference (clangd extension)
/// textDocument.references.container
bool ReferenceContainer = false;

/// Client supports hierarchical document symbols.
Expand Down Expand Up @@ -563,6 +565,7 @@ struct ClientCapabilities {

/// Whether the client supports the textDocument/inactiveRegions
/// notification. This is a clangd extension.
/// textDocument.inactiveRegionsCapabilities.inactiveRegions
bool InactiveRegions = false;
};
bool fromJSON(const llvm::json::Value &, ClientCapabilities &,
Expand Down
21 changes: 17 additions & 4 deletions clang-tools-extra/clangd/XRefs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h"
#include <optional>
Expand Down Expand Up @@ -2275,7 +2276,7 @@ incomingCalls(const CallHierarchyItem &Item, const SymbolIndex *Index) {
// Initially store the ranges in a map keyed by SymbolID of the caller.
// This allows us to group different calls with the same caller
// into the same CallHierarchyIncomingCall.
llvm::DenseMap<SymbolID, std::vector<Range>> CallsIn;
llvm::DenseMap<SymbolID, std::vector<Location>> CallsIn;
// We can populate the ranges based on a refs request only. As we do so, we
// also accumulate the container IDs into a lookup request.
LookupRequest ContainerLookup;
Expand All @@ -2285,7 +2286,7 @@ incomingCalls(const CallHierarchyItem &Item, const SymbolIndex *Index) {
elog("incomingCalls failed to convert location: {0}", Loc.takeError());
return;
}
CallsIn[R.Container].push_back(Loc->range);
CallsIn[R.Container].push_back(*Loc);

ContainerLookup.IDs.insert(R.Container);
});
Expand All @@ -2294,9 +2295,21 @@ incomingCalls(const CallHierarchyItem &Item, const SymbolIndex *Index) {
Index->lookup(ContainerLookup, [&](const Symbol &Caller) {
auto It = CallsIn.find(Caller.ID);
assert(It != CallsIn.end());
if (auto CHI = symbolToCallHierarchyItem(Caller, Item.uri.file()))
if (auto CHI = symbolToCallHierarchyItem(Caller, Item.uri.file())) {
std::vector<Range> FromRanges;
for (const Location &L : It->second) {
if (L.uri != CHI->uri) {
// Call location not in same file as caller.
// This can happen in some edge cases. There's not much we can do,
// since the protocol only allows returning ranges interpreted as
// being in the caller's file.
continue;
}
FromRanges.push_back(L.range);
}
Results.push_back(
CallHierarchyIncomingCall{std::move(*CHI), std::move(It->second)});
CallHierarchyIncomingCall{std::move(*CHI), std::move(FromRanges)});
}
});
// Sort results by name of container.
llvm::sort(Results, [](const CallHierarchyIncomingCall &A,
Expand Down
69 changes: 50 additions & 19 deletions clang-tools-extra/clangd/refactor/tweaks/DefineOutline.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -109,14 +109,13 @@ findContextForNS(llvm::StringRef TargetNS, const DeclContext *CurContext) {
// afterwards it can be shared with define-inline code action.
llvm::Expected<std::string>
getFunctionSourceAfterReplacements(const FunctionDecl *FD,
const tooling::Replacements &Replacements) {
const tooling::Replacements &Replacements,
bool TargetFileIsHeader) {
const auto &SM = FD->getASTContext().getSourceManager();
auto OrigFuncRange = toHalfOpenFileRange(
SM, FD->getASTContext().getLangOpts(), FD->getSourceRange());
if (!OrigFuncRange)
return error("Couldn't get range for function.");
assert(!FD->getDescribedFunctionTemplate() &&
"Define out-of-line doesn't apply to function templates.");

// Get new begin and end positions for the qualified function definition.
unsigned FuncBegin = SM.getFileOffset(OrigFuncRange->getBegin());
Expand All @@ -129,24 +128,38 @@ getFunctionSourceAfterReplacements(const FunctionDecl *FD,
if (!QualifiedFunc)
return QualifiedFunc.takeError();

auto Source = QualifiedFunc->substr(FuncBegin, FuncEnd - FuncBegin + 1);
std::string TemplatePrefix;
auto AddToTemplatePrefixIfApplicable = [&](const Decl *D) {
const TemplateParameterList *Params = D->getDescribedTemplateParams();
if (!Params)
return;
for (Decl *P : *Params) {
if (auto *TTP = dyn_cast<TemplateTypeParmDecl>(P))
TTP->removeDefaultArgument();
else if (auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(P))
NTTP->removeDefaultArgument();
else if (auto *TTPD = dyn_cast<TemplateTemplateParmDecl>(P))
TTPD->removeDefaultArgument();
}
std::string S;
llvm::raw_string_ostream Stream(S);
Params->print(Stream, FD->getASTContext());
if (!S.empty())
*S.rbegin() = '\n'; // Replace space with newline
TemplatePrefix.insert(0, S);
};
AddToTemplatePrefixIfApplicable(FD);
if (auto *MD = llvm::dyn_cast<CXXMethodDecl>(FD)) {
for (const CXXRecordDecl *Parent = MD->getParent(); Parent;
Parent =
llvm::dyn_cast_or_null<const CXXRecordDecl>(Parent->getParent())) {
if (const TemplateParameterList *Params =
Parent->getDescribedTemplateParams()) {
std::string S;
llvm::raw_string_ostream Stream(S);
Params->print(Stream, FD->getASTContext());
if (!S.empty())
*S.rbegin() = '\n'; // Replace space with newline
TemplatePrefix.insert(0, S);
}
AddToTemplatePrefixIfApplicable(Parent);
}
}

auto Source = QualifiedFunc->substr(FuncBegin, FuncEnd - FuncBegin + 1);
if (TargetFileIsHeader)
Source.insert(0, "inline ");
if (!TemplatePrefix.empty())
Source.insert(0, TemplatePrefix);
return Source;
Expand Down Expand Up @@ -202,7 +215,8 @@ deleteTokensWithKind(const syntax::TokenBuffer &TokBuf, tok::TokenKind Kind,
llvm::Expected<std::string>
getFunctionSourceCode(const FunctionDecl *FD, const DeclContext *TargetContext,
const syntax::TokenBuffer &TokBuf,
const HeuristicResolver *Resolver) {
const HeuristicResolver *Resolver,
bool TargetFileIsHeader) {
auto &AST = FD->getASTContext();
auto &SM = AST.getSourceManager();

Expand All @@ -225,6 +239,8 @@ getFunctionSourceCode(const FunctionDecl *FD, const DeclContext *TargetContext,
return;

for (const NamedDecl *ND : Ref.Targets) {
if (ND->getKind() == Decl::TemplateTypeParm)
return;
if (ND->getDeclContext() != Ref.Targets.front()->getDeclContext()) {
elog("Targets from multiple contexts: {0}, {1}",
printQualifiedName(*Ref.Targets.front()),
Expand Down Expand Up @@ -337,7 +353,8 @@ getFunctionSourceCode(const FunctionDecl *FD, const DeclContext *TargetContext,

if (Errors)
return std::move(Errors);
return getFunctionSourceAfterReplacements(FD, DeclarationCleanups);
return getFunctionSourceAfterReplacements(FD, DeclarationCleanups,
TargetFileIsHeader);
}

struct InsertionPoint {
Expand Down Expand Up @@ -419,15 +436,15 @@ class DefineOutline : public Tweak {
Source->isOutOfLine())
return false;

// Bail out if this is a function template or specialization, as their
// Bail out if this is a function template specialization, as their
// definitions need to be visible in all including translation units.
if (Source->getDescribedFunctionTemplate())
return false;
if (Source->getTemplateSpecializationInfo())
return false;

auto *MD = llvm::dyn_cast<CXXMethodDecl>(Source);
if (!MD) {
if (Source->getDescribedFunctionTemplate())
return false;
// Can't outline free-standing functions in the same file.
return !SameFile;
}
Expand All @@ -450,6 +467,19 @@ class DefineOutline : public Tweak {
}
}

// For function templates, the same limitations as for class templates
// apply.
if (const TemplateParameterList *Params =
MD->getDescribedTemplateParams()) {
// FIXME: Is this really needed? It inhibits application on
// e.g. std::enable_if.
for (NamedDecl *P : *Params) {
if (!P->getIdentifier())
return false;
}
SameFile = true;
}

// The refactoring is meaningless for unnamed classes and namespaces,
// unless we're outlining in the same file
for (const DeclContext *DC = MD->getParent(); DC; DC = DC->getParent()) {
Expand Down Expand Up @@ -485,7 +515,8 @@ class DefineOutline : public Tweak {

auto FuncDef = getFunctionSourceCode(
Source, InsertionPoint->EnclosingNamespace, Sel.AST->getTokens(),
Sel.AST->getHeuristicResolver());
Sel.AST->getHeuristicResolver(),
SameFile && isHeaderFile(Sel.AST->tuPath(), Sel.AST->getLangOpts()));
if (!FuncDef)
return FuncDef.takeError();

Expand Down
19 changes: 11 additions & 8 deletions clang-tools-extra/clangd/refactor/tweaks/ExtractFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclBase.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/NestedNameSpecifier.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/AST/Stmt.h"
Expand All @@ -70,7 +71,6 @@
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/raw_os_ostream.h"
#include <optional>

namespace clang {
Expand All @@ -95,18 +95,21 @@ enum FunctionDeclKind {
OutOfLineDefinition
};

// A RootStmt is a statement that's fully selected including all it's children
// and it's parent is unselected.
// A RootStmt is a statement that's fully selected including all its children
// and its parent is unselected.
// Check if a node is a root statement.
bool isRootStmt(const Node *N) {
if (!N->ASTNode.get<Stmt>())
return false;
// Root statement cannot be partially selected.
if (N->Selected == SelectionTree::Partial)
return false;
// Only DeclStmt can be an unselected RootStmt since VarDecls claim the entire
// selection range in selectionTree.
if (N->Selected == SelectionTree::Unselected && !N->ASTNode.get<DeclStmt>())
// A DeclStmt can be an unselected RootStmt since VarDecls claim the entire
// selection range in selectionTree. Additionally, a CXXOperatorCallExpr of a
// binary operation can be unselected because its children claim the entire
// selection range in the selection tree (e.g. <<).
if (N->Selected == SelectionTree::Unselected && !N->ASTNode.get<DeclStmt>() &&
!N->ASTNode.get<CXXOperatorCallExpr>())
return false;
return true;
}
Expand Down Expand Up @@ -913,8 +916,8 @@ Expected<Tweak::Effect> ExtractFunction::apply(const Selection &Inputs) {

tooling::Replacements OtherEdit(
createForwardDeclaration(*ExtractedFunc, SM));
if (auto PathAndEdit = Tweak::Effect::fileEdit(SM, SM.getFileID(*FwdLoc),
OtherEdit))
if (auto PathAndEdit =
Tweak::Effect::fileEdit(SM, SM.getFileID(*FwdLoc), OtherEdit))
MultiFileEffect->ApplyEdits.try_emplace(PathAndEdit->first,
PathAndEdit->second);
else
Expand Down
29 changes: 29 additions & 0 deletions clang-tools-extra/clangd/unittests/CallHierarchyTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,35 @@ TEST(CallHierarchy, HierarchyOnVar) {
fromRanges(Source.range("Callee")))));
}

TEST(CallHierarchy, CallInDifferentFileThanCaller) {
Annotations Header(R"cpp(
#define WALDO void caller() {
)cpp");
Annotations Source(R"cpp(
void call^ee();
WALDO
callee();
}
)cpp");
auto TU = TestTU::withCode(Source.code());
TU.HeaderCode = Header.code();
auto AST = TU.build();
auto Index = TU.index();

std::vector<CallHierarchyItem> Items =
prepareCallHierarchy(AST, Source.point(), testPath(TU.Filename));
ASSERT_THAT(Items, ElementsAre(withName("callee")));

auto Incoming = incomingCalls(Items[0], Index.get());

// The only call site is in the source file, which is a different file from
// the declaration of the function containing the call, which is in the
// header. The protocol does not allow us to represent such calls, so we drop
// them. (The call hierarchy item itself is kept.)
EXPECT_THAT(Incoming,
ElementsAre(AllOf(from(withName("caller")), fromRanges())));
}

} // namespace
} // namespace clangd
} // namespace clang
Original file line number Diff line number Diff line change
Expand Up @@ -92,11 +92,13 @@ TEST_F(OverlayCDBTest, GetCompileCommand) {
EXPECT_EQ(CDB.getCompileCommand(testPath("missing.cc")), std::nullopt);

auto Override = cmd(testPath("foo.cc"), "-DA=3");
CDB.setCompileCommand(testPath("foo.cc"), Override);
EXPECT_TRUE(CDB.setCompileCommand(testPath("foo.cc"), Override));
EXPECT_FALSE(CDB.setCompileCommand(testPath("foo.cc"), Override));
EXPECT_THAT(CDB.getCompileCommand(testPath("foo.cc"))->CommandLine,
Contains("-DA=3"));
EXPECT_EQ(CDB.getCompileCommand(testPath("missing.cc")), std::nullopt);
CDB.setCompileCommand(testPath("missing.cc"), Override);
EXPECT_TRUE(CDB.setCompileCommand(testPath("missing.cc"), Override));
EXPECT_FALSE(CDB.setCompileCommand(testPath("missing.cc"), Override));
EXPECT_THAT(CDB.getCompileCommand(testPath("missing.cc"))->CommandLine,
Contains("-DA=3"));
}
Expand All @@ -111,7 +113,7 @@ TEST_F(OverlayCDBTest, NoBase) {
OverlayCDB CDB(nullptr, {"-DA=6"});
EXPECT_EQ(CDB.getCompileCommand(testPath("bar.cc")), std::nullopt);
auto Override = cmd(testPath("bar.cc"), "-DA=5");
CDB.setCompileCommand(testPath("bar.cc"), Override);
EXPECT_TRUE(CDB.setCompileCommand(testPath("bar.cc"), Override));
EXPECT_THAT(CDB.getCompileCommand(testPath("bar.cc"))->CommandLine,
Contains("-DA=5"));

Expand All @@ -128,10 +130,10 @@ TEST_F(OverlayCDBTest, Watch) {
Changes.push_back(ChangedFiles);
});

Inner.setCompileCommand("A.cpp", tooling::CompileCommand());
Outer.setCompileCommand("B.cpp", tooling::CompileCommand());
Inner.setCompileCommand("A.cpp", std::nullopt);
Outer.setCompileCommand("C.cpp", std::nullopt);
EXPECT_TRUE(Inner.setCompileCommand("A.cpp", tooling::CompileCommand()));
EXPECT_TRUE(Outer.setCompileCommand("B.cpp", tooling::CompileCommand()));
EXPECT_TRUE(Inner.setCompileCommand("A.cpp", std::nullopt));
EXPECT_TRUE(Outer.setCompileCommand("C.cpp", std::nullopt));
EXPECT_THAT(Changes, ElementsAre(ElementsAre("A.cpp"), ElementsAre("B.cpp"),
ElementsAre("A.cpp"), ElementsAre("C.cpp")));
}
Expand All @@ -151,7 +153,7 @@ TEST_F(OverlayCDBTest, Adjustments) {
tooling::CompileCommand BarCommand;
BarCommand.Filename = testPath("bar.cc");
BarCommand.CommandLine = {"clang++", "-DB=1", testPath("bar.cc")};
CDB.setCompileCommand(testPath("bar.cc"), BarCommand);
EXPECT_TRUE(CDB.setCompileCommand(testPath("bar.cc"), BarCommand));
Cmd = *CDB.getCompileCommand(testPath("bar.cc"));
EXPECT_THAT(
Cmd.CommandLine,
Expand Down Expand Up @@ -412,7 +414,7 @@ TEST(GlobalCompilationDatabaseTest, NonCanonicalFilenames) {

llvm::SmallString<128> Root(testRoot());
llvm::sys::path::append(Root, "build", "..", "a.cc");
DB.setCompileCommand(Root.str(), tooling::CompileCommand());
EXPECT_TRUE(DB.setCompileCommand(Root.str(), tooling::CompileCommand()));
EXPECT_THAT(DiscoveredFiles, UnorderedElementsAre(testPath("a.cc")));
DiscoveredFiles.clear();

Expand All @@ -432,7 +434,7 @@ TEST_F(OverlayCDBTest, GetProjectInfo) {
EXPECT_EQ(DB.getProjectInfo(Header)->SourceRoot, testRoot());

// Shouldn't change after an override.
DB.setCompileCommand(File, tooling::CompileCommand());
EXPECT_TRUE(DB.setCompileCommand(File, tooling::CompileCommand()));
EXPECT_EQ(DB.getProjectInfo(File)->SourceRoot, testRoot());
EXPECT_EQ(DB.getProjectInfo(Header)->SourceRoot, testRoot());
}
Expand Down
16 changes: 16 additions & 0 deletions clang-tools-extra/clangd/unittests/InlayHintTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1576,6 +1576,22 @@ TEST(TypeHints, Aliased) {
EXPECT_THAT(hintsOfKind(AST, InlayHintKind::Type), IsEmpty());
}

TEST(TypeHints, CallingConvention) {
// Check that we don't crash for lambdas without a FunctionTypeLoc
// https://github.com/clangd/clangd/issues/2223
std::string Code = R"cpp(
void test() {
[]() __cdecl {};
}
)cpp";
TestTU TU = TestTU::withCode(Code);
TU.ExtraArgs.push_back("--target=x86_64-w64-mingw32");
TU.PredefineMacros = true; // for the __cdecl
auto AST = TU.build();

EXPECT_THAT(hintsOfKind(AST, InlayHintKind::Type), IsEmpty());
}

TEST(TypeHints, Decltype) {
assertTypeHints(R"cpp(
$a[[decltype(0)]] a;
Expand Down
49 changes: 43 additions & 6 deletions clang-tools-extra/clangd/unittests/tweaks/DefineOutlineTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -111,11 +111,17 @@ TEST_F(DefineOutlineTest, TriggersOnFunctionDecl) {
template <typename> struct Foo { void fo^o(){} };
)cpp");

// Not available on function templates and specializations, as definition must
// be visible to all translation units.
// Not available on function template specializations and free function
// templates.
EXPECT_UNAVAILABLE(R"cpp(
template <typename> void fo^o() {};
template <> void fo^o<int>() {};
template <typename T> void fo^o() {}
template <> void fo^o<int>() {}
)cpp");

// Not available on member function templates with unnamed template
// parameters.
EXPECT_UNAVAILABLE(R"cpp(
struct Foo { template <typename> void ba^r() {} };
)cpp");

// Not available on methods of unnamed classes.
Expand Down Expand Up @@ -237,7 +243,7 @@ TEST_F(DefineOutlineTest, ApplyTest) {
Foo(T z) __attribute__((weak)) ;
int bar;
};template <typename T>
Foo<T>::Foo(T z) __attribute__((weak)) : bar(2){}
inline Foo<T>::Foo(T z) __attribute__((weak)) : bar(2){}
)cpp",
""},
// Virt specifiers.
Expand Down Expand Up @@ -390,7 +396,7 @@ Foo<T>::Foo(T z) __attribute__((weak)) : bar(2){}
};
};template <typename T, typename ...U>
template <class V, int A>
typename O1<T, U...>::template O2<V, A>::E O1<T, U...>::template O2<V, A>::I::foo(T, U..., V, E) { return E1; }
inline typename O1<T, U...>::template O2<V, A>::E O1<T, U...>::template O2<V, A>::I::foo(T, U..., V, E) { return E1; }
)cpp",
""},
// Destructors
Expand All @@ -399,6 +405,37 @@ typename O1<T, U...>::template O2<V, A>::E O1<T, U...>::template O2<V, A>::I::fo
"class A { ~A(); };",
"A::~A(){} ",
},

// Member template
{
R"cpp(
struct Foo {
template <typename T, bool B = true>
T ^bar() { return {}; }
};)cpp",
R"cpp(
struct Foo {
template <typename T, bool B = true>
T bar() ;
};template <typename T, bool B>
inline T Foo::bar() { return {}; }
)cpp",
""},

// Class template with member template
{
R"cpp(
template <typename T> struct Foo {
template <typename U> T ^bar(const T& t, const U& u) { return {}; }
};)cpp",
R"cpp(
template <typename T> struct Foo {
template <typename U> T bar(const T& t, const U& u) ;
};template <typename T>
template <typename U>
inline T Foo<T>::bar(const T& t, const U& u) { return {}; }
)cpp",
""},
};
for (const auto &Case : Cases) {
SCOPED_TRACE(Case.Test);
Expand Down
59 changes: 59 additions & 0 deletions clang-tools-extra/clangd/unittests/tweaks/ExtractFunctionTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,18 @@ F (extracted();)
}]]
)cpp";
EXPECT_EQ(apply(CompoundFailInput), "unavailable");

ExtraArgs.push_back("-std=c++14");
// FIXME: Expressions are currently not extracted
EXPECT_EQ(apply(R"cpp(
void call() { [[1+1]]; }
)cpp"),
"unavailable");
// FIXME: Single expression statements are currently not extracted
EXPECT_EQ(apply(R"cpp(
void call() { [[1+1;]] }
)cpp"),
"unavailable");
}

TEST_F(ExtractFunctionTest, DifferentHeaderSourceTest) {
Expand Down Expand Up @@ -571,6 +583,53 @@ int getNum(bool Superstitious, int Min, int Max) {
EXPECT_EQ(apply(Before), After);
}

TEST_F(ExtractFunctionTest, OverloadedOperators) {
Context = File;
std::string Before = R"cpp(struct A {
int operator+(int x) { return x; }
};
A &operator<<(A &, int);
A &operator|(A &, int);

A stream{};

void foo(int, int);

int main() {
[[foo(1, 2);
foo(3, 4);
stream << 42;
stream + 42;
stream | 42;
foo(1, 2);
foo(3, 4);]]
})cpp";
std::string After =
R"cpp(struct A {
int operator+(int x) { return x; }
};
A &operator<<(A &, int);
A &operator|(A &, int);

A stream{};

void foo(int, int);

void extracted() {
foo(1, 2);
foo(3, 4);
stream << 42;
stream + 42;
stream | 42;
foo(1, 2);
foo(3, 4);
}
int main() {
extracted();
})cpp";
EXPECT_EQ(apply(Before), After);
}

} // namespace
} // namespace clangd
} // namespace clang
12 changes: 12 additions & 0 deletions clang-tools-extra/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ Code actions

- Added `Swap operands` tweak for certain binary operators.

- Improved the extract-to-function code action to allow extracting statements
with overloaded operators like ``<<`` of ``std::ostream``.

Signature help
^^^^^^^^^^^^^^

Expand Down Expand Up @@ -159,6 +162,10 @@ Changes in existing checks
<clang-tidy/checks/bugprone/dangling-handle>` check to treat `std::span` as a
handle class.

- Improved :doc:`bugprone-exception-escape
<clang-tidy/checks/bugprone/exception-escape>` by fixing false positives
when a consteval function with throw statements.

- Improved :doc:`bugprone-forwarding-reference-overload
<clang-tidy/checks/bugprone/forwarding-reference-overload>` check by fixing
a crash when determining if an ``enable_if[_t]`` was found.
Expand Down Expand Up @@ -191,6 +198,11 @@ Changes in existing checks
<clang-tidy/checks/bugprone/unsafe-functions>` check to allow specifying
additional functions to match.

- Improved :doc:`bugprone-use-after-move
<clang-tidy/checks/bugprone/use-after-move>` to avoid triggering on
``reset()`` calls on moved-from ``std::optional`` and ``std::any`` objects,
similarly to smart pointers.

- Improved :doc:`cert-flp30-c <clang-tidy/checks/cert/flp30-c>` check to
fix false positive that floating point variable is only used in increment
expression.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -196,11 +196,13 @@ Any occurrence of the moved variable that is not a reinitialization (see below)
is considered to be a use.

An exception to this are objects of type ``std::unique_ptr``,
``std::shared_ptr`` and ``std::weak_ptr``, which have defined move behavior
(objects of these classes are guaranteed to be empty after they have been moved
from). Therefore, an object of these classes will only be considered to be used
if it is dereferenced, i.e. if ``operator*``, ``operator->`` or ``operator[]``
(in the case of ``std::unique_ptr<T []>``) is called on it.
``std::shared_ptr``, ``std::weak_ptr``, ``std::optional``, and ``std::any``.
An exception to this are objects of type ``std::unique_ptr``,
``std::shared_ptr``, ``std::weak_ptr``, ``std::optional``, and ``std::any``, which
can be reinitialized via ``reset``. For smart pointers specifically, the
moved-from objects have a well-defined state of being ``nullptr``s, and only
``operator*``, ``operator->`` and ``operator[]`` are considered bad accesses as
they would be dereferencing a ``nullptr``.

If multiple uses occur after a move, only the first of these is flagged.

Expand All @@ -222,7 +224,8 @@ The check considers a variable to be reinitialized in the following cases:
``unordered_multimap``.

- ``reset()`` is called on the variable and the variable is of type
``std::unique_ptr``, ``std::shared_ptr`` or ``std::weak_ptr``.
``std::unique_ptr``, ``std::shared_ptr``, ``std::weak_ptr``,
``std::optional``, or ``std::any``.

- A member function marked with the ``[[clang::reinitializes]]`` attribute is
called on the variable.
Expand Down
19 changes: 10 additions & 9 deletions clang-tools-extra/include-cleaner/unittests/RecordTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -609,15 +609,6 @@ TEST_F(PragmaIncludeTest, ExportInUnnamedBuffer) {
)cpp";
Inputs.ExtraFiles["foo.h"] = "";

auto Clang = std::make_unique<CompilerInstance>(
std::make_shared<PCHContainerOperations>());
Clang->createDiagnostics();

Clang->setInvocation(std::make_unique<CompilerInvocation>());
ASSERT_TRUE(CompilerInvocation::CreateFromArgs(
Clang->getInvocation(), {Filename.data()}, Clang->getDiagnostics(),
"clang"));

// Create unnamed memory buffers for all the files.
auto VFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
VFS->addFile(Filename, /*ModificationTime=*/0,
Expand All @@ -626,6 +617,16 @@ TEST_F(PragmaIncludeTest, ExportInUnnamedBuffer) {
VFS->addFile(Extra.getKey(), /*ModificationTime=*/0,
llvm::MemoryBuffer::getMemBufferCopy(Extra.getValue(),
/*BufferName=*/""));

auto Clang = std::make_unique<CompilerInstance>(
std::make_shared<PCHContainerOperations>());
Clang->createDiagnostics(*VFS);

Clang->setInvocation(std::make_unique<CompilerInvocation>());
ASSERT_TRUE(CompilerInvocation::CreateFromArgs(
Clang->getInvocation(), {Filename.data()}, Clang->getDiagnostics(),
"clang"));

auto *FM = Clang->createFileManager(VFS);
ASSERT_TRUE(Clang->ExecuteAction(*Inputs.MakeAction()));
EXPECT_THAT(
Expand Down
2 changes: 0 additions & 2 deletions clang-tools-extra/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,6 @@ set(CLANG_TOOLS_TEST_DEPS
clang-resource-headers

clang-tidy
# Clang-tidy tests need clang for building modules.
clang
)

# Add lit test dependencies.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// RUN: %check_clang_tidy -std=c++20 %s bugprone-exception-escape %t -- \
// RUN: -- -fexceptions -Wno-everything

namespace GH104457 {

consteval int consteval_fn(int a) {
if (a == 0)
throw 1;
return a;
}

int test() noexcept { return consteval_fn(1); }

} // namespace GH104457
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,19 @@ struct weak_ptr {
bool expired() const;
};

template <typename T>
struct optional {
optional();
T& operator*();
const T& operator*() const;
void reset();
};

struct any {
any();
void reset();
};

template <typename T1, typename T2>
struct pair {};

Expand Down Expand Up @@ -257,6 +270,14 @@ void standardSmartPtr() {
// CHECK-NOTES: [[@LINE-1]]:5: warning: 'ptr' used after it was moved
// CHECK-NOTES: [[@LINE-3]]:5: note: move occurred here
}
{
std::optional<A> opt;
std::move(opt);
A val = *opt;
(void)val;
// CHECK-NOTES: [[@LINE-2]]:14: warning: 'opt' used after it was moved
// CHECK-NOTES: [[@LINE-4]]:5: note: move occurred here
}
{
// std::weak_ptr<> cannot be dereferenced directly, so we only check that
// member functions may be called on it after a move.
Expand Down Expand Up @@ -994,10 +1015,10 @@ void standardContainerAssignIsReinit() {
}
}

// Resetting the standard smart pointer types using reset() is treated as a
// Resetting the standard smart owning types using reset() is treated as a
// re-initialization. (We don't test std::weak_ptr<> because it can't be
// dereferenced directly.)
void standardSmartPointerResetIsReinit() {
void resetIsReinit() {
{
std::unique_ptr<A> ptr;
std::move(ptr);
Expand All @@ -1010,6 +1031,20 @@ void standardSmartPointerResetIsReinit() {
ptr.reset(new A);
*ptr;
}
{
std::optional<A> opt;
std::move(opt);
opt.reset();
std::optional<A> opt2 = opt;
(void)opt2;
}
{
std::any a;
std::move(a);
a.reset();
std::any a2 = a;
(void)a2;
}
}

void reinitAnnotation() {
Expand Down
6 changes: 6 additions & 0 deletions clang/Maintainers.rst
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,12 @@ Constant Expressions
| mariya.podchishchaeva\@intel.com (email), Fznamznon (GitHub), fznamznon (Discord), Fznamznon (Discourse)


Thread Safety Analysis
~~~~~~~~~~~~~~~~~~~~~~
| Aaron Puchert
| aaron.puchert\@sap.com (email), aaronpuchert (GitHub), aaronpuchert (Discourse)


Tools
-----
These maintainers are responsible for user-facing tools under the Clang
Expand Down
6 changes: 5 additions & 1 deletion clang/cmake/caches/CrossWinToARMLinux.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,6 @@ if (NOT DEFINED CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "Release" CACHE STRING "")
endif()

set(CMAKE_CROSSCOMPILING ON CACHE BOOL "")
set(CMAKE_CL_SHOWINCLUDES_PREFIX "Note: including file: " CACHE STRING "")
# Required if COMPILER_RT_DEFAULT_TARGET_ONLY is ON
set(CMAKE_C_COMPILER_TARGET "${TOOLCHAIN_TARGET_TRIPLE}" CACHE STRING "")
Expand Down Expand Up @@ -219,6 +218,11 @@ set(RUNTIMES_${TOOLCHAIN_TARGET_TRIPLE}_LIBCXX_CXX_ABI
set(RUNTIMES_${TOOLCHAIN_TARGET_TRIPLE}_LIBCXX_ENABLE_NEW_DELETE_DEFINITIONS ON CACHE BOOL "")
# Merge libc++ and libc++abi libraries into the single libc++ library file.
set(RUNTIMES_${TOOLCHAIN_TARGET_TRIPLE}_LIBCXX_ENABLE_STATIC_ABI_LIBRARY ON CACHE BOOL "")
# Forcely disable the libc++ benchmarks on Windows build hosts
# (current benchmark test configuration does not support the cross builds there).
if (WIN32)
set(RUNTIMES_${TOOLCHAIN_TARGET_TRIPLE}_LIBCXX_INCLUDE_BENCHMARKS OFF CACHE BOOL "")
endif(WIN32)

# Avoid searching for the python3 interpreter during the runtimes configuration for the cross builds.
# It starts searching the python3 package using the target's sysroot path, that usually is not compatible with the build host.
Expand Down
14 changes: 9 additions & 5 deletions clang/cmake/caches/Fuchsia-stage2.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@ if(FUCHSIA_SDK)
set(LLVM_RUNTIME_MULTILIB_hwasan+noexcept_TARGETS "aarch64-unknown-fuchsia;riscv64-unknown-fuchsia" CACHE STRING "")
endif()

foreach(target armv6m-none-eabi;armv7m-none-eabi;armv8m.main-none-eabi)
foreach(target armv6m-none-eabi;armv7m-none-eabi;armv8m.main-none-eabi;armv8.1m.main-none-eabi)
list(APPEND BUILTIN_TARGETS "${target}")
set(BUILTINS_${target}_CMAKE_SYSTEM_NAME Generic CACHE STRING "")
set(BUILTINS_${target}_CMAKE_SYSTEM_PROCESSOR arm CACHE STRING "")
Expand All @@ -313,6 +313,9 @@ foreach(target armv6m-none-eabi;armv7m-none-eabi;armv8m.main-none-eabi)
if(${target} STREQUAL "armv8m.main-none-eabi")
set(BUILTINS_${target}_CMAKE_${lang}_local_flags "${BUILTINS_${target}_CMAKE_${lang}_local_flags} -mfloat-abi=softfp -march=armv8m.main+fp+dsp -mcpu=cortex-m33" CACHE STRING "")
endif()
if(${target} STREQUAL "armv8.1m.main-none-eabi")
set(BUILTINS_${target}_CMAKE_${lang}_local_flags "${BUILTINS_${target}_CMAKE_${lang}_local_flags} -mfloat-abi=hard -march=armv8.1-m.main+mve.fp+fp.dp -mcpu=cortex-m55" CACHE STRING "")
endif()
set(BUILTINS_${target}_CMAKE_${lang}_FLAGS "${BUILTINS_${target}_CMAKE_${lang}_local_flags}" CACHE STRING "")
endforeach()
foreach(type SHARED;MODULE;EXE)
Expand All @@ -329,18 +332,20 @@ foreach(target armv6m-none-eabi;armv7m-none-eabi;armv8m.main-none-eabi)
foreach(lang C;CXX;ASM)
# TODO: The preprocessor defines workaround various issues in libc and libc++ integration.
# These should be addressed and removed over time.
set(RUNTIMES_${target}_CMAKE_${lang}_local_flags "--target=${target} -mthumb -Wno-atomic-alignment \"-Dvfprintf(stream, format, vlist)=vprintf(format, vlist)\" \"-Dfprintf(stream, format, ...)=printf(format)\" \"-Dtimeval=struct timeval{int tv_sec; int tv_usec;}\" \"-Dgettimeofday(tv, tz)\" -D_LIBCPP_PRINT=1")
set(RUNTIMES_${target}_CMAKE_${lang}_local_flags "--target=${target} -mthumb -Wno-atomic-alignment \"-Dvfprintf(stream, format, vlist)=vprintf(format, vlist)\" \"-Dfprintf(stream, format, ...)=printf(format)\" \"-Dgettimeofday(tv, tz)\" -D_LIBCPP_PRINT=1")
if(${target} STREQUAL "armv8m.main-none-eabi")
set(RUNTIMES_${target}_CMAKE_${lang}_local_flags "${RUNTIMES_${target}_CMAKE_${lang}_local_flags} -mfloat-abi=softfp -march=armv8m.main+fp+dsp -mcpu=cortex-m33" CACHE STRING "")
endif()
if(${target} STREQUAL "armv8.1m.main-none-eabi")
set(RUNTIMES_${target}_CMAKE_${lang}_local_flags "${RUNTIMES_${target}_CMAKE_${lang}_local_flags} -mfloat-abi=hard -march=armv8.1-m.main+mve.fp+fp.dp -mcpu=cortex-m55" CACHE STRING "")
endif()
set(RUNTIMES_${target}_CMAKE_${lang}_FLAGS "${RUNTIMES_${target}_CMAKE_${lang}_local_flags}" CACHE STRING "")
endforeach()
foreach(type SHARED;MODULE;EXE)
set(RUNTIMES_${target}_CMAKE_${type}_LINKER_FLAGS "-fuse-ld=lld" CACHE STRING "")
endforeach()
set(RUNTIMES_${target}_LLVM_LIBC_FULL_BUILD ON CACHE BOOL "")
set(RUNTIMES_${target}_LIBC_ENABLE_USE_BY_CLANG ON CACHE BOOL "")
set(RUNTIMES_${target}_LIBC_USE_NEW_HEADER_GEN OFF CACHE BOOL "")
set(RUNTIMES_${target}_LIBCXX_ABI_VERSION 2 CACHE STRING "")
set(RUNTIMES_${target}_LIBCXX_CXX_ABI none CACHE STRING "")
set(RUNTIMES_${target}_LIBCXX_ENABLE_SHARED OFF CACHE BOOL "")
Expand Down Expand Up @@ -385,14 +390,13 @@ foreach(target riscv32-unknown-elf)
foreach(lang C;CXX;ASM)
# TODO: The preprocessor defines workaround various issues in libc and libc++ integration.
# These should be addressed and removed over time.
set(RUNTIMES_${target}_CMAKE_${lang}_FLAGS "--target=${target} -march=rv32imafc -mabi=ilp32f -Wno-atomic-alignment \"-Dvfprintf(stream, format, vlist)=vprintf(format, vlist)\" \"-Dfprintf(stream, format, ...)=printf(format)\" \"-Dtimeval=struct timeval{int tv_sec; int tv_usec;}\" \"-Dgettimeofday(tv, tz)\" -D_LIBCPP_PRINT=1" CACHE STRING "")
set(RUNTIMES_${target}_CMAKE_${lang}_FLAGS "--target=${target} -march=rv32imafc -mabi=ilp32f -Wno-atomic-alignment \"-Dvfprintf(stream, format, vlist)=vprintf(format, vlist)\" \"-Dfprintf(stream, format, ...)=printf(format)\" \"-Dgettimeofday(tv, tz)\" -D_LIBCPP_PRINT=1" CACHE STRING "")
endforeach()
foreach(type SHARED;MODULE;EXE)
set(RUNTIMES_${target}_CMAKE_${type}_LINKER_FLAGS "-fuse-ld=lld" CACHE STRING "")
endforeach()
set(RUNTIMES_${target}_LLVM_LIBC_FULL_BUILD ON CACHE BOOL "")
set(RUNTIMES_${target}_LIBC_ENABLE_USE_BY_CLANG ON CACHE BOOL "")
set(RUNTIMES_${target}_LIBC_USE_NEW_HEADER_GEN OFF CACHE BOOL "")
set(RUNTIMES_${target}_LIBCXX_ABI_VERSION 2 CACHE STRING "")
set(RUNTIMES_${target}_LIBCXX_CXX_ABI none CACHE STRING "")
set(RUNTIMES_${target}_LIBCXX_ENABLE_SHARED OFF CACHE BOOL "")
Expand Down
14 changes: 6 additions & 8 deletions clang/docs/AddressSanitizer.rst
Original file line number Diff line number Diff line change
Expand Up @@ -326,15 +326,13 @@ Supported Platforms

AddressSanitizer is supported on:

* Linux i386/x86\_64 (tested on Ubuntu 12.04)
* macOS 10.7 - 10.11 (i386/x86\_64)
* Linux
* macOS
* iOS Simulator
* Android ARM
* NetBSD i386/x86\_64
* FreeBSD i386/x86\_64 (tested on FreeBSD 11-current)
* Windows 8.1+ (i386/x86\_64)

Ports to various other platforms are in progress.
* Android
* NetBSD
* FreeBSD
* Windows 8.1+

Current Status
==============
Expand Down
4 changes: 4 additions & 0 deletions clang/docs/InternalsManual.rst
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,10 @@ wording a diagnostic.
named in a diagnostic message. e.g., prefer wording like ``'this' pointer
cannot be null in well-defined C++ code`` over wording like ``this pointer
cannot be null in well-defined C++ code``.
* Prefer diagnostic wording without contractions whenever possible. The single
quote in a contraction can be visually distracting due to its use with
syntactic constructs and contractions can be harder to understand for non-
native English speakers.

The Format String
^^^^^^^^^^^^^^^^^
Expand Down
4 changes: 4 additions & 0 deletions clang/docs/LanguageExtensions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -732,6 +732,10 @@ at the end to the next power of 2.

These reductions support both fixed-sized and scalable vector types.

The integer reduction intrinsics, including ``__builtin_reduce_add``,
``__builtin_reduce_mul``, ``__builtin_reduce_and``, ``__builtin_reduce_or``,
and ``__builtin_reduce_xor``, can be called in a ``constexpr`` context.

Example:

.. code-block:: c++
Expand Down
10 changes: 5 additions & 5 deletions clang/docs/LeakSanitizer.rst
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,11 @@ constraints in mind and may compromise the security of the resulting executable.
Supported Platforms
===================

* Android aarch64/i386/x86_64
* Fuchsia aarch64/x86_64
* Linux arm/aarch64/mips64/ppc64/ppc64le/riscv64/s390x/i386/x86\_64
* macOS aarch64/i386/x86\_64
* NetBSD i386/x86_64
* Android
* Fuchsia
* Linux
* macOS
* NetBSD

More Information
================
Expand Down
44 changes: 43 additions & 1 deletion clang/docs/RealtimeSanitizer.rst
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,11 @@ A **partial** list of flags RealtimeSanitizer respects:
* - ``halt_on_error``
- ``true``
- boolean
- Exit after first reported error. If false (continue after a detected error), deduplicates error stacks so errors appear only once.
- Exit after first reported error.
* - ``suppress_equal_stacks``
- ``true``
- boolean
- If true, suppress duplicate reports (i.e. only print each unique error once). Only particularly useful when ``halt_on_error=false``.
* - ``print_stats_on_exit``
- ``false``
- boolean
Expand Down Expand Up @@ -203,6 +207,44 @@ Some issues with flags can be debugged using the ``verbosity=$NUM`` flag:
misspelled_flag
...

Additional customization
------------------------

In addition to ``__rtsan_default_options`` outlined above, you can provide definitions of other functions that affect how RTSan operates.

To be notified on every error reported by RTsan, provide a definition of ``__sanitizer_report_error_summary``.

.. code-block:: c

extern "C" void __sanitizer_report_error_summary(const char *error_summary) {
fprintf(stderr, "%s %s\n", "In custom handler! ", error_summary);
/* do other custom things */
}

The error summary will be of the form:

.. code-block:: console

SUMMARY: RealtimeSanitizer: unsafe-library-call main.cpp:8 in process(std::__1::vector<int, std::__1::allocator<int>>&)

To register a callback which will be invoked before a RTSan kills the process:

.. code-block:: c

extern "C" void __sanitizer_set_death_callback(void (*callback)(void));

void custom_on_die_callback() {
fprintf(stderr, "In custom handler!")
/* do other custom things */
}

int main()
{
__sanitizer_set_death_callback(custom_on_die_callback);
...
}


Disabling and suppressing
-------------------------

Expand Down
70 changes: 70 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,17 @@ C++ Specific Potentially Breaking Changes
// Now diagnoses with an error.
void f(int& i [[clang::lifetimebound]]);

- Clang now rejects all field accesses on null pointers in constant expressions. The following code
used to work but will now be rejected:

.. code-block:: c++

struct S { int a; int b; };
constexpr const int *p = &((S*)nullptr)->b;

Previously, this code was erroneously accepted.


ABI Changes in This Version
---------------------------

Expand Down Expand Up @@ -271,6 +282,8 @@ Resolutions to C++ Defect Reports
C Language Changes
------------------

- Extend clang's ``<limits.h>`` to define ``LONG_LONG_*`` macros for Android's bionic.

C2y Feature Support
^^^^^^^^^^^^^^^^^^^

Expand Down Expand Up @@ -353,6 +366,11 @@ Non-comprehensive list of changes in this release
The flexible array member (FAM) can now be accessed immediately without causing
issues with the sanitizer because the counter is automatically set.

- ``__builtin_reduce_add`` function can now be used in constant expressions.
- ``__builtin_reduce_mul`` function can now be used in constant expressions.
- ``__builtin_reduce_and`` function can now be used in constant expressions.
- ``__builtin_reduce_or`` and ``__builtin_reduce_xor`` functions can now be used in constant expressions.

New Compiler Flags
------------------

Expand Down Expand Up @@ -533,6 +551,38 @@ Improvements to Clang's diagnostics

- Improved diagnostic message for ``__builtin_bit_cast`` size mismatch (#GH115870).

- Clang now omits shadow warnings for enum constants in separate class scopes (#GH62588).

- When diagnosing an unused return value of a type declared ``[[nodiscard]]``, the type
itself is now included in the diagnostic.

- Clang will now prefer the ``[[nodiscard]]`` declaration on function declarations over ``[[nodiscard]]``
declaration on the return type of a function. Previously, when both have a ``[[nodiscard]]`` declaration attached,
the one on the return type would be preferred. This may affect the generated warning message:

.. code-block:: c++

struct [[nodiscard("Reason 1")]] S {};
[[nodiscard("Reason 2")]] S getS();
void use()
{
getS(); // Now diagnoses "Reason 2", previously diagnoses "Reason 1"
}

- Clang now diagnoses ``= delete("reason")`` extension warnings only in pedantic mode rather than on by default. (#GH109311).

- Clang now diagnoses missing return value in functions containing ``if consteval`` (#GH116485).

- Clang now correctly recognises code after a call to a ``[[noreturn]]`` constructor
as unreachable (#GH63009).

- Clang now omits shadowing warnings for parameter names in explicit object member functions (#GH95707).

- Improved error recovery for function call arguments with trailing commas (#GH100921).

- For an rvalue reference bound to a temporary struct with an integer member, Clang will detect constant integer overflow
in the initializer for the integer member (#GH46755).

Improvements to Clang's time-trace
----------------------------------

Expand All @@ -553,6 +603,8 @@ Bug Fixes in This Version
the unsupported type instead of the ``register`` keyword (#GH109776).
- Fixed a crash when emit ctor for global variant with flexible array init (#GH113187).
- Fixed a crash when GNU statement expression contains invalid statement (#GH113468).
- Fixed a failed assertion when using ``__attribute__((noderef))`` on an
``_Atomic``-qualified type (#GH116124).

Bug Fixes to Compiler Builtins
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down Expand Up @@ -660,6 +712,8 @@ Bug Fixes to C++ Support
- Name independent data members were not correctly initialized from default member initializers. (#GH114069)
- Fixed expression transformation for ``[[assume(...)]]``, allowing using pack indexing expressions within the
assumption if they also occur inside of a dependent lambda. (#GH114787)
- Clang now uses valid deduced type locations when diagnosing functions with trailing return type
missing placeholder return type. (#GH78694)

Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down Expand Up @@ -706,6 +760,19 @@ Target Specific Changes
AMDGPU Support
^^^^^^^^^^^^^^

- Initial support for gfx950

- Added headers ``gpuintrin.h`` and ``amdgpuintrin.h`` that contains common
definitions for GPU builtin functions. This header can be included for OpenMP,
CUDA, HIP, OpenCL, and C/C++.

NVPTX Support
^^^^^^^^^^^^^^

- Added headers ``gpuintrin.h`` and ``nvptxintrin.h`` that contains common
definitions for GPU builtin functions. This header can be included for OpenMP,
CUDA, HIP, OpenCL, and C/C++.

X86 Support
^^^^^^^^^^^

Expand Down Expand Up @@ -752,6 +819,9 @@ X86 Support
- Support ISA of ``AMX-MOVRS``.
- Support ISA of ``AMX-AVX512``.
- Support ISA of ``AMX-TF32``.
- Support ISA of ``MOVRS``.

- Supported ``-march/tune=diamondrapids``

Arm and AArch64 Support
^^^^^^^^^^^^^^^^^^^^^^^
Expand Down
24 changes: 19 additions & 5 deletions clang/docs/ThreadSafetyAnalysis.rst
Original file line number Diff line number Diff line change
Expand Up @@ -933,11 +933,25 @@ implementation.
MutexLocker(Mutex *mu, defer_lock_t) EXCLUDES(mu) : mut(mu), locked(false) {}

// Same as constructors, but without tag types. (Requires C++17 copy elision.)
static MutexLocker Lock(Mutex *mu) ACQUIRE(mu);
static MutexLocker Adopt(Mutex *mu) REQUIRES(mu);
static MutexLocker ReaderLock(Mutex *mu) ACQUIRE_SHARED(mu);
static MutexLocker AdoptReaderLock(Mutex *mu) REQUIRES_SHARED(mu);
static MutexLocker DeferLock(Mutex *mu) EXCLUDES(mu);
static MutexLocker Lock(Mutex *mu) ACQUIRE(mu) {
return MutexLocker(mu);
}

static MutexLocker Adopt(Mutex *mu) REQUIRES(mu) {
return MutexLocker(mu, adopt_lock);
}

static MutexLocker ReaderLock(Mutex *mu) ACQUIRE_SHARED(mu) {
return MutexLocker(mu, shared_lock);
}

static MutexLocker AdoptReaderLock(Mutex *mu) REQUIRES_SHARED(mu) {
return MutexLocker(mu, adopt_lock, shared_lock);
}

static MutexLocker DeferLock(Mutex *mu) EXCLUDES(mu) {
return MutexLocker(mu, defer_lock);
}

// Release *this and all associated mutexes, if they are still held.
// There is no warning if the scope was already unlocked before.
Expand Down
7 changes: 7 additions & 0 deletions clang/include/clang/AST/ASTNodeTraverser.h
Original file line number Diff line number Diff line change
Expand Up @@ -800,6 +800,13 @@ class ASTNodeTraverser
Visit(A);
}

void VisitLabelStmt(const LabelStmt *Node) {
if (Node->getDecl()->hasAttrs()) {
for (const auto *A : Node->getDecl()->getAttrs())
Visit(A);
}
}

void VisitCXXCatchStmt(const CXXCatchStmt *Node) {
Visit(Node->getExceptionDecl());
}
Expand Down
9 changes: 5 additions & 4 deletions clang/include/clang/AST/CanonicalType.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ namespace clang {

template<typename T> class CanProxy;
template<typename T> struct CanProxyAdaptor;
class ASTContext;
class CXXRecordDecl;
class EnumDecl;
class Expr;
Expand Down Expand Up @@ -164,14 +165,14 @@ class CanQual {

/// Determines whether this canonical type is more qualified than
/// the @p Other canonical type.
bool isMoreQualifiedThan(CanQual<T> Other) const {
return Stored.isMoreQualifiedThan(Other.Stored);
bool isMoreQualifiedThan(CanQual<T> Other, const ASTContext &Ctx) const {
return Stored.isMoreQualifiedThan(Other.Stored, Ctx);
}

/// Determines whether this canonical type is at least as qualified as
/// the @p Other canonical type.
bool isAtLeastAsQualifiedAs(CanQual<T> Other) const {
return Stored.isAtLeastAsQualifiedAs(Other.Stored);
bool isAtLeastAsQualifiedAs(CanQual<T> Other, const ASTContext &Ctx) const {
return Stored.isAtLeastAsQualifiedAs(Other.Stored, Ctx);
}

/// If the canonical type is a reference type, returns the type that
Expand Down
8 changes: 5 additions & 3 deletions clang/include/clang/AST/Expr.h
Original file line number Diff line number Diff line change
Expand Up @@ -3181,12 +3181,14 @@ class CallExpr : public Expr {
QualType getCallReturnType(const ASTContext &Ctx) const;

/// Returns the WarnUnusedResultAttr that is either declared on the called
/// function, or its return type declaration.
const Attr *getUnusedResultAttr(const ASTContext &Ctx) const;
/// function, or its return type declaration, together with a NamedDecl that
/// refers to the declaration the attribute is attached onto.
std::pair<const NamedDecl *, const Attr *>
getUnusedResultAttr(const ASTContext &Ctx) const;

/// Returns true if this call expression should warn on unused results.
bool hasUnusedResultAttr(const ASTContext &Ctx) const {
return getUnusedResultAttr(Ctx) != nullptr;
return getUnusedResultAttr(Ctx).second != nullptr;
}

SourceLocation getRParenLoc() const { return RParenLoc; }
Expand Down
60 changes: 23 additions & 37 deletions clang/include/clang/AST/Type.h
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,7 @@ class PointerAuthQualifier {
/// * Objective C: the GC attributes (none, weak, or strong)
class Qualifiers {
public:
Qualifiers() = default;
enum TQ : uint64_t {
// NOTE: These flags must be kept in sync with DeclSpec::TQ.
Const = 0x1,
Expand Down Expand Up @@ -697,45 +698,27 @@ class Qualifiers {
/// every address space is a superset of itself.
/// CL2.0 adds:
/// __generic is a superset of any address space except for __constant.
static bool isAddressSpaceSupersetOf(LangAS A, LangAS B) {
static bool isAddressSpaceSupersetOf(LangAS A, LangAS B,
const ASTContext &Ctx) {
// Address spaces must match exactly.
return A == B ||
// Otherwise in OpenCLC v2.0 s6.5.5: every address space except
// for __constant can be used as __generic.
(A == LangAS::opencl_generic && B != LangAS::opencl_constant) ||
// We also define global_device and global_host address spaces,
// to distinguish global pointers allocated on host from pointers
// allocated on device, which are a subset of __global.
(A == LangAS::opencl_global && (B == LangAS::opencl_global_device ||
B == LangAS::opencl_global_host)) ||
(A == LangAS::sycl_global && (B == LangAS::sycl_global_device ||
B == LangAS::sycl_global_host)) ||
// Consider pointer size address spaces to be equivalent to default.
((isPtrSizeAddressSpace(A) || A == LangAS::Default) &&
(isPtrSizeAddressSpace(B) || B == LangAS::Default)) ||
// Default is a superset of SYCL address spaces.
(A == LangAS::Default &&
(B == LangAS::sycl_private || B == LangAS::sycl_local ||
B == LangAS::sycl_global || B == LangAS::sycl_global_device ||
B == LangAS::sycl_global_host)) ||
// In HIP device compilation, any cuda address space is allowed
// to implicitly cast into the default address space.
(A == LangAS::Default &&
(B == LangAS::cuda_constant || B == LangAS::cuda_device ||
B == LangAS::cuda_shared));
return A == B || isTargetAddressSpaceSupersetOf(A, B, Ctx);
}

static bool isTargetAddressSpaceSupersetOf(LangAS A, LangAS B,
const ASTContext &Ctx);

/// Returns true if the address space in these qualifiers is equal to or
/// a superset of the address space in the argument qualifiers.
bool isAddressSpaceSupersetOf(Qualifiers other) const {
return isAddressSpaceSupersetOf(getAddressSpace(), other.getAddressSpace());
bool isAddressSpaceSupersetOf(Qualifiers other, const ASTContext &Ctx) const {
return isAddressSpaceSupersetOf(getAddressSpace(), other.getAddressSpace(),
Ctx);
}

/// Determines if these qualifiers compatibly include another set.
/// Generally this answers the question of whether an object with the other
/// qualifiers can be safely used as an object with these qualifiers.
bool compatiblyIncludes(Qualifiers other) const {
return isAddressSpaceSupersetOf(other) &&
bool compatiblyIncludes(Qualifiers other, const ASTContext &Ctx) const {
return isAddressSpaceSupersetOf(other, Ctx) &&
// ObjC GC qualifiers can match, be added, or be removed, but can't
// be changed.
(getObjCGCAttr() == other.getObjCGCAttr() || !hasObjCGCAttr() ||
Expand Down Expand Up @@ -1273,11 +1256,11 @@ class QualType {

/// Determine whether this type is more qualified than the other
/// given type, requiring exact equality for non-CVR qualifiers.
bool isMoreQualifiedThan(QualType Other) const;
bool isMoreQualifiedThan(QualType Other, const ASTContext &Ctx) const;

/// Determine whether this type is at least as qualified as the other
/// given type, requiring exact equality for non-CVR qualifiers.
bool isAtLeastAsQualifiedAs(QualType Other) const;
bool isAtLeastAsQualifiedAs(QualType Other, const ASTContext &Ctx) const;

QualType getNonReferenceType() const;

Expand Down Expand Up @@ -1425,11 +1408,12 @@ class QualType {
/// address spaces overlap iff they are they same.
/// OpenCL C v2.0 s6.5.5 adds:
/// __generic overlaps with any address space except for __constant.
bool isAddressSpaceOverlapping(QualType T) const {
bool isAddressSpaceOverlapping(QualType T, const ASTContext &Ctx) const {
Qualifiers Q = getQualifiers();
Qualifiers TQ = T.getQualifiers();
// Address spaces overlap if at least one of them is a superset of another
return Q.isAddressSpaceSupersetOf(TQ) || TQ.isAddressSpaceSupersetOf(Q);
return Q.isAddressSpaceSupersetOf(TQ, Ctx) ||
TQ.isAddressSpaceSupersetOf(Q, Ctx);
}

/// Returns gc attribute of this type.
Expand Down Expand Up @@ -8112,24 +8096,26 @@ inline FunctionType::ExtInfo getFunctionExtInfo(QualType t) {
/// is more qualified than "const int", "volatile int", and
/// "int". However, it is not more qualified than "const volatile
/// int".
inline bool QualType::isMoreQualifiedThan(QualType other) const {
inline bool QualType::isMoreQualifiedThan(QualType other,
const ASTContext &Ctx) const {
Qualifiers MyQuals = getQualifiers();
Qualifiers OtherQuals = other.getQualifiers();
return (MyQuals != OtherQuals && MyQuals.compatiblyIncludes(OtherQuals));
return (MyQuals != OtherQuals && MyQuals.compatiblyIncludes(OtherQuals, Ctx));
}

/// Determine whether this type is at last
/// as qualified as the Other type. For example, "const volatile
/// int" is at least as qualified as "const int", "volatile int",
/// "int", and "const volatile int".
inline bool QualType::isAtLeastAsQualifiedAs(QualType other) const {
inline bool QualType::isAtLeastAsQualifiedAs(QualType other,
const ASTContext &Ctx) const {
Qualifiers OtherQuals = other.getQualifiers();

// Ignore __unaligned qualifier if this type is a void.
if (getUnqualifiedType()->isVoidType())
OtherQuals.removeUnaligned();

return getQualifiers().compatiblyIncludes(OtherQuals);
return getQualifiers().compatiblyIncludes(OtherQuals, Ctx);
}

/// If Type is a reference type (e.g., const
Expand Down
Loading