[clang] Revert "Reapply "[Clang][Sema] Always use latest redeclaration of primary template" (#114569)" (PR #115156)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Nov 6 05:38:22 PST 2024
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Krystian Stasiowski (sdkrystian)
<details>
<summary>Changes</summary>
This reverts commit b24650e814e55d90acfc40acf045456c98f32b9c.
---
Patch is 21.55 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/115156.diff
12 Files Affected:
- (modified) clang/include/clang/AST/DeclTemplate.h (+47-5)
- (modified) clang/lib/AST/ASTImporter.cpp (+1-2)
- (modified) clang/lib/AST/Decl.cpp (+5-5)
- (modified) clang/lib/AST/DeclCXX.cpp (+2-2)
- (modified) clang/lib/AST/DeclTemplate.cpp (+2-54)
- (modified) clang/lib/Sema/SemaDecl.cpp (+1-3)
- (modified) clang/lib/Sema/SemaInit.cpp (+1-1)
- (modified) clang/lib/Sema/SemaTemplateInstantiate.cpp (+7-7)
- (modified) clang/test/AST/ast-dump-decl.cpp (+1-1)
- (removed) clang/test/ASTMerge/class-template-spec/Inputs/class-template-spec.cpp (-47)
- (removed) clang/test/ASTMerge/class-template-spec/test.cpp (-8)
- (modified) clang/test/CXX/temp/temp.spec/temp.expl.spec/p7.cpp (-87)
``````````diff
diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h
index 0ca3fd48e81cf4..a572e3380f1655 100644
--- a/clang/include/clang/AST/DeclTemplate.h
+++ b/clang/include/clang/AST/DeclTemplate.h
@@ -857,6 +857,16 @@ class RedeclarableTemplateDecl : public TemplateDecl,
/// \endcode
bool isMemberSpecialization() const { return Common.getInt(); }
+ /// Determines whether any redeclaration of this template was
+ /// a specialization of a member template.
+ bool hasMemberSpecialization() const {
+ for (const auto *D : redecls()) {
+ if (D->isMemberSpecialization())
+ return true;
+ }
+ return false;
+ }
+
/// Note that this member template is a specialization.
void setMemberSpecialization() {
assert(!isMemberSpecialization() && "already a member specialization");
@@ -1955,7 +1965,13 @@ class ClassTemplateSpecializationDecl : public CXXRecordDecl,
/// specialization which was specialized by this.
llvm::PointerUnion<ClassTemplateDecl *,
ClassTemplatePartialSpecializationDecl *>
- getSpecializedTemplateOrPartial() const;
+ getSpecializedTemplateOrPartial() const {
+ if (const auto *PartialSpec =
+ SpecializedTemplate.dyn_cast<SpecializedPartialSpecialization *>())
+ return PartialSpec->PartialSpecialization;
+
+ return SpecializedTemplate.get<ClassTemplateDecl*>();
+ }
/// Retrieve the set of template arguments that should be used
/// to instantiate members of the class template or class template partial
@@ -2192,6 +2208,17 @@ class ClassTemplatePartialSpecializationDecl
return InstantiatedFromMember.getInt();
}
+ /// Determines whether any redeclaration of this this class template partial
+ /// specialization was a specialization of a member partial specialization.
+ bool hasMemberSpecialization() const {
+ for (const auto *D : redecls()) {
+ if (cast<ClassTemplatePartialSpecializationDecl>(D)
+ ->isMemberSpecialization())
+ return true;
+ }
+ return false;
+ }
+
/// Note that this member template is a specialization.
void setMemberSpecialization() { return InstantiatedFromMember.setInt(true); }
@@ -2713,7 +2740,13 @@ class VarTemplateSpecializationDecl : public VarDecl,
/// Retrieve the variable template or variable template partial
/// specialization which was specialized by this.
llvm::PointerUnion<VarTemplateDecl *, VarTemplatePartialSpecializationDecl *>
- getSpecializedTemplateOrPartial() const;
+ getSpecializedTemplateOrPartial() const {
+ if (const auto *PartialSpec =
+ SpecializedTemplate.dyn_cast<SpecializedPartialSpecialization *>())
+ return PartialSpec->PartialSpecialization;
+
+ return SpecializedTemplate.get<VarTemplateDecl *>();
+ }
/// Retrieve the set of template arguments that should be used
/// to instantiate the initializer of the variable template or variable
@@ -2947,6 +2980,18 @@ class VarTemplatePartialSpecializationDecl
return InstantiatedFromMember.getInt();
}
+ /// Determines whether any redeclaration of this this variable template
+ /// partial specialization was a specialization of a member partial
+ /// specialization.
+ bool hasMemberSpecialization() const {
+ for (const auto *D : redecls()) {
+ if (cast<VarTemplatePartialSpecializationDecl>(D)
+ ->isMemberSpecialization())
+ return true;
+ }
+ return false;
+ }
+
/// Note that this member template is a specialization.
void setMemberSpecialization() { return InstantiatedFromMember.setInt(true); }
@@ -3119,9 +3164,6 @@ class VarTemplateDecl : public RedeclarableTemplateDecl {
return makeSpecIterator(getSpecializations(), true);
}
- /// Merge \p Prev with our RedeclarableTemplateDecl::Common.
- void mergePrevDecl(VarTemplateDecl *Prev);
-
// Implement isa/cast/dyncast support
static bool classof(const Decl *D) { return classofKind(D->getKind()); }
static bool classofKind(Kind K) { return K == VarTemplate; }
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index 9d0b77566f6747..6e31df691fa104 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -6190,8 +6190,7 @@ ExpectedDecl ASTNodeImporter::VisitClassTemplateDecl(ClassTemplateDecl *D) {
ExpectedDecl ASTNodeImporter::VisitClassTemplateSpecializationDecl(
ClassTemplateSpecializationDecl *D) {
ClassTemplateDecl *ClassTemplate;
- if (Error Err = importInto(ClassTemplate,
- D->getSpecializedTemplate()->getCanonicalDecl()))
+ if (Error Err = importInto(ClassTemplate, D->getSpecializedTemplate()))
return std::move(Err);
// Import the context of this declaration.
diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp
index cd173d17263792..86913763ef9ff5 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -2708,7 +2708,7 @@ VarDecl *VarDecl::getTemplateInstantiationPattern() const {
if (isTemplateInstantiation(VDTemplSpec->getTemplateSpecializationKind())) {
auto From = VDTemplSpec->getInstantiatedFrom();
if (auto *VTD = From.dyn_cast<VarTemplateDecl *>()) {
- while (!VTD->isMemberSpecialization()) {
+ while (!VTD->hasMemberSpecialization()) {
if (auto *NewVTD = VTD->getInstantiatedFromMemberTemplate())
VTD = NewVTD;
else
@@ -2718,7 +2718,7 @@ VarDecl *VarDecl::getTemplateInstantiationPattern() const {
}
if (auto *VTPSD =
From.dyn_cast<VarTemplatePartialSpecializationDecl *>()) {
- while (!VTPSD->isMemberSpecialization()) {
+ while (!VTPSD->hasMemberSpecialization()) {
if (auto *NewVTPSD = VTPSD->getInstantiatedFromMember())
VTPSD = NewVTPSD;
else
@@ -2732,7 +2732,7 @@ VarDecl *VarDecl::getTemplateInstantiationPattern() const {
// If this is the pattern of a variable template, find where it was
// instantiated from. FIXME: Is this necessary?
if (VarTemplateDecl *VTD = VD->getDescribedVarTemplate()) {
- while (!VTD->isMemberSpecialization()) {
+ while (!VTD->hasMemberSpecialization()) {
if (auto *NewVTD = VTD->getInstantiatedFromMemberTemplate())
VTD = NewVTD;
else
@@ -4153,7 +4153,7 @@ FunctionDecl::getTemplateInstantiationPattern(bool ForDefinition) const {
if (FunctionTemplateDecl *Primary = getPrimaryTemplate()) {
// If we hit a point where the user provided a specialization of this
// template, we're done looking.
- while (!ForDefinition || !Primary->isMemberSpecialization()) {
+ while (!ForDefinition || !Primary->hasMemberSpecialization()) {
if (auto *NewPrimary = Primary->getInstantiatedFromMemberTemplate())
Primary = NewPrimary;
else
@@ -4170,7 +4170,7 @@ FunctionTemplateDecl *FunctionDecl::getPrimaryTemplate() const {
if (FunctionTemplateSpecializationInfo *Info
= TemplateOrSpecialization
.dyn_cast<FunctionTemplateSpecializationInfo*>()) {
- return Info->getTemplate()->getMostRecentDecl();
+ return Info->getTemplate();
}
return nullptr;
}
diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp
index 1c92fd9e3ff067..db0ea62a2323eb 100644
--- a/clang/lib/AST/DeclCXX.cpp
+++ b/clang/lib/AST/DeclCXX.cpp
@@ -2030,7 +2030,7 @@ const CXXRecordDecl *CXXRecordDecl::getTemplateInstantiationPattern() const {
if (auto *TD = dyn_cast<ClassTemplateSpecializationDecl>(this)) {
auto From = TD->getInstantiatedFrom();
if (auto *CTD = From.dyn_cast<ClassTemplateDecl *>()) {
- while (!CTD->isMemberSpecialization()) {
+ while (!CTD->hasMemberSpecialization()) {
if (auto *NewCTD = CTD->getInstantiatedFromMemberTemplate())
CTD = NewCTD;
else
@@ -2040,7 +2040,7 @@ const CXXRecordDecl *CXXRecordDecl::getTemplateInstantiationPattern() const {
}
if (auto *CTPSD =
From.dyn_cast<ClassTemplatePartialSpecializationDecl *>()) {
- while (!CTPSD->isMemberSpecialization()) {
+ while (!CTPSD->hasMemberSpecialization()) {
if (auto *NewCTPSD = CTPSD->getInstantiatedFromMemberTemplate())
CTPSD = NewCTPSD;
else
diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp
index 1db02d0d04448c..755ec72f00bf77 100644
--- a/clang/lib/AST/DeclTemplate.cpp
+++ b/clang/lib/AST/DeclTemplate.cpp
@@ -993,17 +993,7 @@ ClassTemplateSpecializationDecl::getSpecializedTemplate() const {
if (const auto *PartialSpec =
SpecializedTemplate.dyn_cast<SpecializedPartialSpecialization*>())
return PartialSpec->PartialSpecialization->getSpecializedTemplate();
- return SpecializedTemplate.get<ClassTemplateDecl *>()->getMostRecentDecl();
-}
-
-llvm::PointerUnion<ClassTemplateDecl *,
- ClassTemplatePartialSpecializationDecl *>
-ClassTemplateSpecializationDecl::getSpecializedTemplateOrPartial() const {
- if (const auto *PartialSpec =
- SpecializedTemplate.dyn_cast<SpecializedPartialSpecialization *>())
- return PartialSpec->PartialSpecialization->getMostRecentDecl();
-
- return SpecializedTemplate.get<ClassTemplateDecl *>()->getMostRecentDecl();
+ return SpecializedTemplate.get<ClassTemplateDecl*>();
}
SourceRange
@@ -1293,39 +1283,6 @@ VarTemplateDecl::newCommon(ASTContext &C) const {
return CommonPtr;
}
-void VarTemplateDecl::mergePrevDecl(VarTemplateDecl *Prev) {
- // If we haven't created a common pointer yet, then it can just be created
- // with the usual method.
- if (!getCommonPtrInternal())
- return;
-
- Common *ThisCommon = static_cast<Common *>(getCommonPtrInternal());
- Common *PrevCommon = nullptr;
- SmallVector<VarTemplateDecl *, 8> PreviousDecls;
- for (; Prev; Prev = Prev->getPreviousDecl()) {
- if (CommonBase *C = Prev->getCommonPtrInternal()) {
- PrevCommon = static_cast<Common *>(C);
- break;
- }
- PreviousDecls.push_back(Prev);
- }
-
- // If the previous redecl chain hasn't created a common pointer yet, then just
- // use this common pointer.
- if (!PrevCommon) {
- for (auto *D : PreviousDecls)
- D->setCommonPtr(ThisCommon);
- return;
- }
-
- // Ensure we don't leak any important state.
- assert(ThisCommon->Specializations.empty() &&
- ThisCommon->PartialSpecializations.empty() &&
- "Can't merge incompatible declarations!");
-
- setCommonPtr(PrevCommon);
-}
-
VarTemplateSpecializationDecl *
VarTemplateDecl::findSpecialization(ArrayRef<TemplateArgument> Args,
void *&InsertPos) {
@@ -1448,16 +1405,7 @@ VarTemplateDecl *VarTemplateSpecializationDecl::getSpecializedTemplate() const {
if (const auto *PartialSpec =
SpecializedTemplate.dyn_cast<SpecializedPartialSpecialization *>())
return PartialSpec->PartialSpecialization->getSpecializedTemplate();
- return SpecializedTemplate.get<VarTemplateDecl *>()->getMostRecentDecl();
-}
-
-llvm::PointerUnion<VarTemplateDecl *, VarTemplatePartialSpecializationDecl *>
-VarTemplateSpecializationDecl::getSpecializedTemplateOrPartial() const {
- if (const auto *PartialSpec =
- SpecializedTemplate.dyn_cast<SpecializedPartialSpecialization *>())
- return PartialSpec->PartialSpecialization->getMostRecentDecl();
-
- return SpecializedTemplate.get<VarTemplateDecl *>()->getMostRecentDecl();
+ return SpecializedTemplate.get<VarTemplateDecl *>();
}
SourceRange VarTemplateSpecializationDecl::getSourceRange() const {
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 00c8f871bbb38c..acb1bee8cef319 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -4694,10 +4694,8 @@ void Sema::MergeVarDecl(VarDecl *New, LookupResult &Previous) {
// Keep a chain of previous declarations.
New->setPreviousDecl(Old);
- if (NewTemplate) {
- NewTemplate->mergePrevDecl(OldTemplate);
+ if (NewTemplate)
NewTemplate->setPreviousDecl(OldTemplate);
- }
// Inherit access appropriately.
New->setAccess(Old->getAccess());
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index f13355bb93cbeb..cba66d82406cdd 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -9957,7 +9957,7 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer(
auto SynthesizeAggrGuide = [&](InitListExpr *ListInit) {
auto *Pattern = Template;
while (Pattern->getInstantiatedFromMemberTemplate()) {
- if (Pattern->isMemberSpecialization())
+ if (Pattern->hasMemberSpecialization())
break;
Pattern = Pattern->getInstantiatedFromMemberTemplate();
}
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index de0ec0128905ff..b63063813f1b56 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -343,7 +343,7 @@ struct TemplateInstantiationArgumentCollecter
// If this function was instantiated from a specialized member that is
// a function template, we're done.
assert(FD->getPrimaryTemplate() && "No function template?");
- if (FD->getPrimaryTemplate()->isMemberSpecialization())
+ if (FD->getPrimaryTemplate()->hasMemberSpecialization())
return Done();
// If this function is a generic lambda specialization, we are done.
@@ -442,11 +442,11 @@ struct TemplateInstantiationArgumentCollecter
Specialized = CTSD->getSpecializedTemplateOrPartial();
if (auto *CTPSD =
Specialized.dyn_cast<ClassTemplatePartialSpecializationDecl *>()) {
- if (CTPSD->isMemberSpecialization())
+ if (CTPSD->hasMemberSpecialization())
return Done();
} else {
auto *CTD = Specialized.get<ClassTemplateDecl *>();
- if (CTD->isMemberSpecialization())
+ if (CTD->hasMemberSpecialization())
return Done();
}
return UseNextDecl(CTSD);
@@ -478,11 +478,11 @@ struct TemplateInstantiationArgumentCollecter
Specialized = VTSD->getSpecializedTemplateOrPartial();
if (auto *VTPSD =
Specialized.dyn_cast<VarTemplatePartialSpecializationDecl *>()) {
- if (VTPSD->isMemberSpecialization())
+ if (VTPSD->hasMemberSpecialization())
return Done();
} else {
auto *VTD = Specialized.get<VarTemplateDecl *>();
- if (VTD->isMemberSpecialization())
+ if (VTD->hasMemberSpecialization())
return Done();
}
return UseNextDecl(VTSD);
@@ -4141,7 +4141,7 @@ getPatternForClassTemplateSpecialization(
CXXRecordDecl *Pattern = nullptr;
Specialized = ClassTemplateSpec->getSpecializedTemplateOrPartial();
if (auto *CTD = Specialized.dyn_cast<ClassTemplateDecl *>()) {
- while (!CTD->isMemberSpecialization()) {
+ while (!CTD->hasMemberSpecialization()) {
if (auto *NewCTD = CTD->getInstantiatedFromMemberTemplate())
CTD = NewCTD;
else
@@ -4151,7 +4151,7 @@ getPatternForClassTemplateSpecialization(
} else if (auto *CTPSD =
Specialized
.dyn_cast<ClassTemplatePartialSpecializationDecl *>()) {
- while (!CTPSD->isMemberSpecialization()) {
+ while (!CTPSD->hasMemberSpecialization()) {
if (auto *NewCTPSD = CTPSD->getInstantiatedFromMemberTemplate())
CTPSD = NewCTPSD;
else
diff --git a/clang/test/AST/ast-dump-decl.cpp b/clang/test/AST/ast-dump-decl.cpp
index 7b998f20944f49..e84241cee922f5 100644
--- a/clang/test/AST/ast-dump-decl.cpp
+++ b/clang/test/AST/ast-dump-decl.cpp
@@ -530,7 +530,7 @@ namespace testCanonicalTemplate {
// CHECK-NEXT: | `-ClassTemplateDecl 0x{{.+}} parent 0x{{.+}} <col:5, col:40> col:40 friend_undeclared TestClassTemplate{{$}}
// CHECK-NEXT: | |-TemplateTypeParmDecl 0x{{.+}} <col:14, col:23> col:23 typename depth 1 index 0 T2{{$}}
// CHECK-NEXT: | `-CXXRecordDecl 0x{{.+}} parent 0x{{.+}} <col:34, col:40> col:40 class TestClassTemplate{{$}}
- // CHECK-NEXT: `-ClassTemplateSpecializationDecl 0x{{.+}} <col:5, col:40> line:[[@LINE-19]]:31 class TestClassTemplate definition implicit_instantiation{{$}}
+ // CHECK-NEXT: `-ClassTemplateSpecializationDecl 0x{{.+}} <line:[[@LINE-19]]:3, line:[[@LINE-17]]:3> line:[[@LINE-19]]:31 class TestClassTemplate definition implicit_instantiation{{$}}
// CHECK-NEXT: |-DefinitionData pass_in_registers empty aggregate standard_layout trivially_copyable pod trivial literal has_constexpr_non_copy_move_ctor can_const_default_init{{$}}
// CHECK-NEXT: | |-DefaultConstructor exists trivial constexpr defaulted_is_constexpr{{$}}
// CHECK-NEXT: | |-CopyConstructor simple trivial has_const_param implicit_has_const_param{{$}}
diff --git a/clang/test/ASTMerge/class-template-spec/Inputs/class-template-spec.cpp b/clang/test/ASTMerge/class-template-spec/Inputs/class-template-spec.cpp
deleted file mode 100644
index 332bf24d25b29d..00000000000000
--- a/clang/test/ASTMerge/class-template-spec/Inputs/class-template-spec.cpp
+++ /dev/null
@@ -1,47 +0,0 @@
-namespace N0 {
- template<typename T>
- struct A {
- template<typename U>
- friend struct A;
- };
-
- template struct A<long>;
-} // namespace N0
-
-namespace N1 {
- template<typename T>
- struct A;
-
- template<typename T>
- struct A {
- template<typename U>
- friend struct A;
- };
-
- template struct A<long>;
-} // namespace N1
-
-namespace N2 {
- template<typename T>
- struct A {
- template<typename U>
- friend struct A;
- };
-
- template<typename T>
- struct A;
-
- template struct A<long>;
-} // namespace N2
-
-namespace N3 {
- struct A {
- template<typename T>
- friend struct B;
- };
-
- template<typename T>
- struct B { };
-
- template struct B<long>;
-} // namespace N3
diff --git a/clang/test/ASTMerge/class-template-spec/test.cpp b/clang/test/ASTMerge/class-template-spec/test.cpp
deleted file mode 100644
index adbce483503278..00000000000000
--- a/clang/test/ASTMerge/class-template-spec/test.cpp
+++ /dev/null
@@ -1,8 +0,0 @@
-// RUN: %clang_cc1 -emit-pch -o %t.1.ast %S/Inputs/class-template-spec.cpp
-// RUN: %clang_cc1 -ast-merge %t.1.ast -fsyntax-only -verify %s
-// expected-no-diagnostics
-
-template struct N0::A<short>;
-template struct N1::A<short>;
-template struct N2::A<short>;
-template struct N3::B<short>;
diff --git a/clang/test/CXX/temp/temp.spec/temp.expl.spec/p7.cpp b/clang/test/CXX/temp/temp.spec/temp.expl.spec/p7.cpp
index e7e4738032f647..87127366eb58a5 100644
--- a/clang/test/CXX/temp/temp.spec/temp.expl.spec/p7.cpp
+++ b/clang/test/CXX/temp/temp.spec/temp.expl.spec/p7.cpp
@@ -177,93 +177,6 @@ namespace Defined {
static_assert(A<short>::B<int*>::y == 2);
} // namespace Defined
-namespace Constrained {
- template<typename T>
- struct A {
- template<typename U, bool V> requires V
- static constexpr int f(); // expected-note {{declared here}}
-
- template<typename U, bool V> requires V
- static const int x; // expected-note {{declared here}}
-
- template<typename U, bool V> requires V
- static const int x<U*, V>; // expected-note {{declared here}}
-
- template<typename U, bool V> requires V
- struct B; // expected-note {{template is declared here}}
-
- template<typename U, bool V> requires V
- struct B<U*, V>; // expected-note {{template is declared here}}
- };
-
- template<>
- template<typename U, bool V> requires V
- constexpr int A<short>::f() {
- return A<long>::f<U, V>();
- }
-
- template<>
- template<typename U, bool V> requires V
- constexpr int A<short>::x = A<long>::x<U, V>;
-
- template<>
- template<typename U, bool V> requires V
- constexpr int A<short>::x<U*, V> = A<long>::x<U*, V>;
-
- template<>
- template<typename U, bool V> requires V
- struct A<short>::B<U*, V> {
- static constexpr int y = A<long>::B<U*, V>::y;
- };
-
- template<>
- template<typename U, bool V> requires V
- struct A<short>::B {
- static constexpr int y = A<long>::B<U, V>::y;
- };
-
- template<>
- template<typename U, bool V> requires V
- constexpr int A<long>::f() {
- return 1;
- }
-
- template<>
- template<typename U, bool V> requires V
- constexpr int A<long>::x = 1;
-
- template<>
- template<typename U, bool V> requires V
- constexpr int A<long>::x<U*, V> = 2;
-
- template<>
- template<typename U, bool V> requires V
- struct A<long>...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/115156
More information about the cfe-commits
mailing list