diff --git a/clang/lib/CodeGen/CoverageMappingGen.cpp b/clang/lib/CodeGen/CoverageMappingGen.cpp --- a/clang/lib/CodeGen/CoverageMappingGen.cpp +++ b/clang/lib/CodeGen/CoverageMappingGen.cpp @@ -602,6 +602,13 @@ MostRecentLocation = *StartLoc; } + // If either location is invalid, set it to std::nullopt to avoid + // letting users of RegionStack think that region has a valid start/end + // location. + if (StartLoc && StartLoc->isInvalid()) + StartLoc = std::nullopt; + if (EndLoc && EndLoc->isInvalid()) + EndLoc = std::nullopt; RegionStack.emplace_back(Count, FalseCount, StartLoc, EndLoc); return RegionStack.size() - 1; @@ -624,7 +631,8 @@ assert(RegionStack.size() >= ParentIndex && "parent not in stack"); while (RegionStack.size() > ParentIndex) { SourceMappingRegion &Region = RegionStack.back(); - if (Region.hasStartLoc()) { + if (Region.hasStartLoc() && + (Region.hasEndLoc() || RegionStack[ParentIndex].hasEndLoc())) { SourceLocation StartLoc = Region.getBeginLoc(); SourceLocation EndLoc = Region.hasEndLoc() ? Region.getEndLoc() @@ -691,7 +699,7 @@ assert(SM.isWrittenInSameFile(Region.getBeginLoc(), EndLoc)); assert(SpellingRegion(SM, Region).isInSourceOrder()); SourceRegions.push_back(Region); - } + } RegionStack.pop_back(); } } @@ -716,7 +724,8 @@ // The statement may be spanned by an expansion. Make sure we handle a file // exit out of this expansion before moving to the next statement. - if (SM.isBeforeInTranslationUnit(StartLoc, S->getBeginLoc())) + if (StartLoc.isValid() && + SM.isBeforeInTranslationUnit(StartLoc, S->getBeginLoc())) MostRecentLocation = EndLoc; return ExitCount; @@ -891,6 +900,8 @@ /// Find a valid gap range between \p AfterLoc and \p BeforeLoc. std::optional findGapAreaBetween(SourceLocation AfterLoc, SourceLocation BeforeLoc) { + if (AfterLoc.isInvalid() || BeforeLoc.isInvalid()) + return std::nullopt; // If AfterLoc is in function-like macro, use the right parenthesis // location. if (AfterLoc.isMacroID()) { diff --git a/clang/test/CoverageMapping/invalid_location.cpp b/clang/test/CoverageMapping/invalid_location.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CoverageMapping/invalid_location.cpp @@ -0,0 +1,92 @@ +// RUN: %clang_cc1 -mllvm -emptyline-comment-coverage=false -fprofile-instrument=clang -fcoverage-mapping -dump-coverage-mapping -emit-llvm-only -std=c++20 -stdlib=libc++ -triple %itanium_abi_triple %s | FileCheck %s + +namespace std { template class initializer_list {}; } + +template struct T { + T(std::initializer_list, int = int()); + bool b; +}; + +template struct S1 { + static void foo() { + class C; + 0 ? T{} : T{}; + 0 ? 1 : 2; + } +}; + +void bar() { + S1::foo(); +} + + +// CHECK: _ZN2S1IiE3fooEv: +// CHECK-NEXT: File 0, 11:21 -> 15:4 = #0 +// CHECK-NEXT: File 0, 13:5 -> 13:6 = #0 +// CHECK-NEXT: Branch,File 0, 13:5 -> 13:6 = 0, 0 +// CHECK-NEXT: Gap,File 0, 13:8 -> 13:9 = #1 +// FIXME: It's supposed to have two different counters for true and false +// branches at line 13 as it has for line 14, shown below. It's missing +// now because 'T{}' doesn't have a valid end location for now. +// Once it's fixed, correct the following two "CHECK-NETX" and #3 and #2 +// may need to swap positions. +// CHECK-NETX: File 0, 13:9 -> 13:14 = #3 +// CHECK-NETX: File 0, 13:18 -> 13:23 = (#0 - #3) + +// CHECK-NEXT: File 0, 14:5 -> 14:6 = #0 +// CHECK-NEXT: Branch,File 0, 14:5 -> 14:6 = 0, 0 +// CHECK-NEXT: Gap,File 0, 14:8 -> 14:9 = #2 +// CHECK-NEXT: File 0, 14:9 -> 14:10 = #2 +// CHECK-NEXT: File 0, 14:13 -> 14:14 = (#0 - #2) + +// No crash for following example. +// See https://github.com/llvm/llvm-project/issues/45481 +namespace std { +class strong_ordering; + +// Mock how STD defined unspecified parameters for the operators below. +struct _CmpUnspecifiedParam { + consteval + _CmpUnspecifiedParam(int _CmpUnspecifiedParam::*) noexcept {} +}; + +struct strong_ordering { + signed char value; + + friend constexpr bool operator==(strong_ordering v, + _CmpUnspecifiedParam) noexcept { + return v.value == 0; + } + friend constexpr bool operator<(strong_ordering v, + _CmpUnspecifiedParam) noexcept { + return v.value < 0; + } + friend constexpr bool operator>(strong_ordering v, + _CmpUnspecifiedParam) noexcept { + return v.value > 0; + } + friend constexpr bool operator>=(strong_ordering v, + _CmpUnspecifiedParam) noexcept { + return v.value >= 0; + } + static const strong_ordering equal, greater, less; +}; +constexpr strong_ordering strong_ordering::equal = {0}; +constexpr strong_ordering strong_ordering::greater = {1}; +constexpr strong_ordering strong_ordering::less = {-1}; +} // namespace std + +struct S { + friend bool operator<(const S&, const S&); + friend bool operator==(const S&, const S&); +}; + +struct MyStruct { + friend bool operator==(MyStruct const& lhs, MyStruct const& rhs) = delete; + friend std::strong_ordering operator<=>(MyStruct const& lhs, MyStruct const& rhs) = default; + S value; +}; + +void foo(MyStruct bar){ + (void)(bar <=> bar); +}