[clang] [Sema] Substitute parameter packs when deduced from function arguments (PR #79371)
Gábor Spaits via cfe-commits
cfe-commits at lists.llvm.org
Thu Jan 25 11:47:21 PST 2024
https://github.com/spaits updated https://github.com/llvm/llvm-project/pull/79371
>From a27fe8c4179a58a741121eec9e8368c9ab44d7cc Mon Sep 17 00:00:00 2001
From: Gabor Spaits <gaborspaits1 at gmail.com>
Date: Wed, 24 Jan 2024 21:21:26 +0100
Subject: [PATCH 01/12] [Sema]Substitue template parameter packs when deduced
from function parameter
---
clang/lib/Sema/SemaTemplateDeduction.cpp | 63 +++++++++++++++++++++++-
1 file changed, 62 insertions(+), 1 deletion(-)
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index e9e7ab5bb6698a..46fa9eece3747a 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -730,6 +730,7 @@ class PackDeductionScope {
void addPack(unsigned Index) {
// Save the deduced template argument for the parameter pack expanded
// by this pack expansion, then clear out the deduction.
+ DeducedFromEarlierParameter = !Deduced[Index].isNull();
DeducedPack Pack(Index);
Pack.Saved = Deduced[Index];
Deduced[Index] = TemplateArgument();
@@ -858,6 +859,29 @@ class PackDeductionScope {
Info.PendingDeducedPacks[Pack.Index] = Pack.Outer;
}
+ std::optional<unsigned> getSavedPackSize(unsigned Index,
+ TemplateArgument Pattern) const {
+
+ SmallVector<UnexpandedParameterPack, 2> Unexpanded;
+ S.collectUnexpandedParameterPacks(Pattern, Unexpanded);
+ if (Unexpanded.size() == 0 ||
+ Packs[0].Saved.getKind() != clang::TemplateArgument::Pack)
+ return {};
+ unsigned PackSize = Packs[0].Saved.pack_size();
+
+ if (std::all_of(Packs.begin() + 1, Packs.end(),
+ [&PackSize](auto P) {
+ return P.Saved.getKind() == TemplateArgument::Pack &&
+ P.Saved.pack_size() == PackSize;
+ }))
+ return PackSize;
+ return {};
+ }
+
+ /// Determine whether this pack has already been deduced from a previous
+ /// argument.
+ bool isDeducedFromEarlierParameter() const {return DeducedFromEarlierParameter;}
+
/// Determine whether this pack has already been partially expanded into a
/// sequence of (prior) function parameters / template arguments.
bool isPartiallyExpanded() { return IsPartiallyExpanded; }
@@ -970,7 +994,6 @@ class PackDeductionScope {
NewPack = Pack.DeferredDeduction;
Result = checkDeducedTemplateArguments(S.Context, OldPack, NewPack);
}
-
NamedDecl *Param = TemplateParams->getParam(Pack.Index);
if (Result.isNull()) {
Info.Param = makeTemplateParameter(Param);
@@ -1003,9 +1026,12 @@ class PackDeductionScope {
unsigned PackElements = 0;
bool IsPartiallyExpanded = false;
bool DeducePackIfNotAlreadyDeduced = false;
+ bool DeducedFromEarlierParameter = false;
+
/// The number of expansions, if we have a fully-expanded pack in this scope.
std::optional<unsigned> FixedNumExpansions;
+
SmallVector<DeducedPack, 2> Packs;
};
@@ -4371,6 +4397,41 @@ Sema::TemplateDeductionResult Sema::DeduceTemplateArguments(
// corresponding argument is a list?
PackScope.nextPackElement();
}
+ } else if (!IsTrailingPack && !PackScope.isPartiallyExpanded() &&
+ PackScope.isDeducedFromEarlierParameter() &&
+ !isa<PackExpansionType>(ParamTypes[ParamIdx + 1])) {
+ // [temp.deduct.general#3]
+ // When all template arguments have been deduced
+ // or obtained from default template arguments, all uses of template
+ // parameters in the template parameter list of the template are
+ // replaced with the corresponding deduced or default argument values
+ //
+ // If we have a trailing parameter pack, that has been deduced perviously
+ // we substitute the pack here in a similar fashion as seen above with
+ // the trailing parameter packs. The main difference here is that, in
+ // this case we are not processing all of the remaining arguments. We
+ // are only process as many arguments as much we have in the already
+ // deduced parameter.
+ SmallVector<UnexpandedParameterPack, 2> Unexpanded;
+ collectUnexpandedParameterPacks(ParamPattern, Unexpanded);
+ if (Unexpanded.size() == 0)
+ continue;
+
+ std::optional<unsigned> ArgPosAfterSubstitution =
+ PackScope.getSavedPackSize(getDepthAndIndex(Unexpanded[0]).second,
+ ParamPattern);
+ if (!ArgPosAfterSubstitution)
+ continue;
+
+ unsigned PackArgEnd = ArgIdx + *ArgPosAfterSubstitution;
+ for (; ArgIdx < PackArgEnd && ArgIdx < Args.size(); ArgIdx++) {
+ ParamTypesForArgChecking.push_back(ParamPattern);
+ if (auto Result = DeduceCallArgument(ParamPattern, ArgIdx,
+ /*ExplicitObjetArgument=*/false))
+ return Result;
+
+ PackScope.nextPackElement();
+ }
}
}
>From 44771539c48b3d5418c3726999b78b70b9a059a3 Mon Sep 17 00:00:00 2001
From: Gabor Spaits <gaborspaits1 at gmail.com>
Date: Wed, 24 Jan 2024 22:33:02 +0100
Subject: [PATCH 02/12] Format changes
---
clang/lib/Sema/SemaTemplateDeduction.cpp | 25 ++++++++++++------------
1 file changed, 13 insertions(+), 12 deletions(-)
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index 46fa9eece3747a..e682ca6dc0f7d4 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -869,18 +869,19 @@ class PackDeductionScope {
return {};
unsigned PackSize = Packs[0].Saved.pack_size();
- if (std::all_of(Packs.begin() + 1, Packs.end(),
- [&PackSize](auto P) {
- return P.Saved.getKind() == TemplateArgument::Pack &&
- P.Saved.pack_size() == PackSize;
- }))
+ if (std::all_of(Packs.begin() + 1, Packs.end(), [&PackSize](auto P) {
+ return P.Saved.getKind() == TemplateArgument::Pack &&
+ P.Saved.pack_size() == PackSize;
+ }))
return PackSize;
return {};
}
/// Determine whether this pack has already been deduced from a previous
/// argument.
- bool isDeducedFromEarlierParameter() const {return DeducedFromEarlierParameter;}
+ bool isDeducedFromEarlierParameter() const {
+ return DeducedFromEarlierParameter;
+ }
/// Determine whether this pack has already been partially expanded into a
/// sequence of (prior) function parameters / template arguments.
@@ -4406,12 +4407,12 @@ Sema::TemplateDeductionResult Sema::DeduceTemplateArguments(
// parameters in the template parameter list of the template are
// replaced with the corresponding deduced or default argument values
//
- // If we have a trailing parameter pack, that has been deduced perviously
- // we substitute the pack here in a similar fashion as seen above with
- // the trailing parameter packs. The main difference here is that, in
- // this case we are not processing all of the remaining arguments. We
- // are only process as many arguments as much we have in the already
- // deduced parameter.
+ // If we have a trailing parameter pack, that has been deduced
+ // perviously we substitute the pack here in a similar fashion as seen
+ // above with the trailing parameter packs. The main difference here is
+ // that, in this case we are not processing all of the remaining
+ // arguments. We are only process as many arguments as much we have in
+ // the already deduced parameter.
SmallVector<UnexpandedParameterPack, 2> Unexpanded;
collectUnexpandedParameterPacks(ParamPattern, Unexpanded);
if (Unexpanded.size() == 0)
>From b57b86278a0c17aa00e100bc41de80b870468cdb Mon Sep 17 00:00:00 2001
From: Gabor Spaits <gaborspaits1 at gmail.com>
Date: Thu, 25 Jan 2024 08:22:29 +0100
Subject: [PATCH 03/12] Adjust test case and format
---
clang/lib/Sema/SemaTemplateDeduction.cpp | 3 +--
.../temp/temp.fct.spec/temp.deduct/temp.deduct.call/p1-0x.cpp | 2 +-
2 files changed, 2 insertions(+), 3 deletions(-)
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index e682ca6dc0f7d4..7df522b93324b7 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -995,6 +995,7 @@ class PackDeductionScope {
NewPack = Pack.DeferredDeduction;
Result = checkDeducedTemplateArguments(S.Context, OldPack, NewPack);
}
+
NamedDecl *Param = TemplateParams->getParam(Pack.Index);
if (Result.isNull()) {
Info.Param = makeTemplateParameter(Param);
@@ -1028,11 +1029,9 @@ class PackDeductionScope {
bool IsPartiallyExpanded = false;
bool DeducePackIfNotAlreadyDeduced = false;
bool DeducedFromEarlierParameter = false;
-
/// The number of expansions, if we have a fully-expanded pack in this scope.
std::optional<unsigned> FixedNumExpansions;
-
SmallVector<DeducedPack, 2> Packs;
};
diff --git a/clang/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.call/p1-0x.cpp b/clang/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.call/p1-0x.cpp
index 1da599ed3c27aa..2ac66b5055ffd5 100644
--- a/clang/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.call/p1-0x.cpp
+++ b/clang/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.call/p1-0x.cpp
@@ -81,7 +81,7 @@ void test_pair_deduction(int *ip, float *fp, double *dp) {
template<typename ...Types> struct tuple { };
template<typename ...Types>
-void pack_not_at_end(tuple<Types...>, Types... values, int); // expected-note {{<int *, double *> vs. <>}}
+void pack_not_at_end(tuple<Types...>, Types... values, int); // expected-note {{<int *, double *> vs. <int, int>}}
void test_pack_not_at_end(tuple<int*, double*> t2) {
pack_not_at_end(t2, 0, 0, 0); // expected-error {{no match}}
>From d30447f1ecda996a0888916fe2cfde3d4951f17e Mon Sep 17 00:00:00 2001
From: Gabor Spaits <gaborspaits1 at gmail.com>
Date: Thu, 25 Jan 2024 08:36:02 +0100
Subject: [PATCH 04/12] Add tests
---
clang/test/SemaTemplate/deduction.cpp | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
diff --git a/clang/test/SemaTemplate/deduction.cpp b/clang/test/SemaTemplate/deduction.cpp
index ad98de61d9e127..5f18471becc9e8 100644
--- a/clang/test/SemaTemplate/deduction.cpp
+++ b/clang/test/SemaTemplate/deduction.cpp
@@ -13,6 +13,17 @@ struct X0<int, A> {
static const unsigned value = 1;
};
+template<class T>
+struct type_identity {
+ using type = T;
+};
+
+template<class T>
+using type_identity_t = typename type_identity<T>::type;
+
+template <typename... T>
+struct args_tag {};
+
template<int> struct X0i;
template<long> struct X0l;
int array_x0a[X0<long, X0l>::value == 0? 1 : -1];
@@ -431,6 +442,17 @@ namespace deduction_after_explicit_pack {
i<int, int>(0, 1, 2, 3, 4, 5); // expected-error {{no match}}
}
+ template <typename... T>
+ void bar(args_tag<T...>, type_identity_t<T>..., int mid, type_identity_t<T>...) {}
+ void call_bar() {
+ bar(args_tag<int, int>{}, 4, 8, 1001, 16, 23);
+ }
+ template <typename... Y, typename... T>
+ void foo(args_tag<Y...>, args_tag<T...>, type_identity_t<T>..., int mid, type_identity_t<T>...) {}
+ void call_foo() {
+ foo(args_tag<const int,const int, const int>{}, args_tag<int, int, int>{}, 4, 8, 9, 15, 16, 23, 1);
+ }
+
// GCC alarmingly accepts this by deducing T={int} by matching the second
// parameter against the first argument, then passing the first argument
// through the first parameter.
>From 1798f79a6878a7d5f9ec2925882eeeb25c2e4831 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=A1bor=20Spaits?=
<48805437+spaits at users.noreply.github.com>
Date: Thu, 25 Jan 2024 11:05:42 +0100
Subject: [PATCH 05/12] Fix typo
Co-authored-by: cor3ntin <corentinjabot at gmail.com>
---
clang/lib/Sema/SemaTemplateDeduction.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index 7df522b93324b7..15c5a30371994d 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -4407,7 +4407,7 @@ Sema::TemplateDeductionResult Sema::DeduceTemplateArguments(
// replaced with the corresponding deduced or default argument values
//
// If we have a trailing parameter pack, that has been deduced
- // perviously we substitute the pack here in a similar fashion as seen
+ // previously we substitute the pack here in a similar fashion as seen
// above with the trailing parameter packs. The main difference here is
// that, in this case we are not processing all of the remaining
// arguments. We are only process as many arguments as much we have in
>From 8788bebd89b1e768b47d032d1d4b2e5009bbc2e6 Mon Sep 17 00:00:00 2001
From: Gabor Spaits <gaborspaits1 at gmail.com>
Date: Thu, 25 Jan 2024 11:51:15 +0100
Subject: [PATCH 06/12] Fix typo #2
---
clang/lib/Sema/SemaTemplateDeduction.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index 15c5a30371994d..e3eed657e93f1d 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -4407,7 +4407,7 @@ Sema::TemplateDeductionResult Sema::DeduceTemplateArguments(
// replaced with the corresponding deduced or default argument values
//
// If we have a trailing parameter pack, that has been deduced
- // previously we substitute the pack here in a similar fashion as seen
+ // previously we substitute the pack here in a similar fashion as
// above with the trailing parameter packs. The main difference here is
// that, in this case we are not processing all of the remaining
// arguments. We are only process as many arguments as much we have in
>From 6cff26f8e4890d19c8856ceb2b32f3027ba896c3 Mon Sep 17 00:00:00 2001
From: Gabor Spaits <gaborspaits1 at gmail.com>
Date: Thu, 25 Jan 2024 13:46:52 +0100
Subject: [PATCH 07/12] Substitue pack even when no separator between packs
---
clang/lib/Sema/SemaTemplateDeduction.cpp | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index e3eed657e93f1d..0b04a66ce9d3f8 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -4398,8 +4398,7 @@ Sema::TemplateDeductionResult Sema::DeduceTemplateArguments(
PackScope.nextPackElement();
}
} else if (!IsTrailingPack && !PackScope.isPartiallyExpanded() &&
- PackScope.isDeducedFromEarlierParameter() &&
- !isa<PackExpansionType>(ParamTypes[ParamIdx + 1])) {
+ PackScope.isDeducedFromEarlierParameter()) {
// [temp.deduct.general#3]
// When all template arguments have been deduced
// or obtained from default template arguments, all uses of template
>From fc282396961c7d5e975c0e189528410a5fdeaf59 Mon Sep 17 00:00:00 2001
From: Gabor Spaits <gaborspaits1 at gmail.com>
Date: Thu, 25 Jan 2024 18:13:05 +0100
Subject: [PATCH 08/12] Use assertion where we know that pack is not empty
---
clang/lib/Sema/SemaTemplateDeduction.cpp | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index 0b04a66ce9d3f8..0194f2da75f01e 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -4413,8 +4413,7 @@ Sema::TemplateDeductionResult Sema::DeduceTemplateArguments(
// the already deduced parameter.
SmallVector<UnexpandedParameterPack, 2> Unexpanded;
collectUnexpandedParameterPacks(ParamPattern, Unexpanded);
- if (Unexpanded.size() == 0)
- continue;
+ assert(Unexpanded.size() != 0 && "We must have an unexpanded pack\n");
std::optional<unsigned> ArgPosAfterSubstitution =
PackScope.getSavedPackSize(getDepthAndIndex(Unexpanded[0]).second,
>From 845b7c1ea98a91bd95ab162096187781d5453693 Mon Sep 17 00:00:00 2001
From: Gabor Spaits <gaborspaits1 at gmail.com>
Date: Thu, 25 Jan 2024 18:56:30 +0100
Subject: [PATCH 09/12] Simplify logic for pack size computing
---
clang/lib/Sema/SemaTemplateDeduction.cpp | 16 ++++------------
1 file changed, 4 insertions(+), 12 deletions(-)
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index 0194f2da75f01e..808b307729835e 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -859,12 +859,9 @@ class PackDeductionScope {
Info.PendingDeducedPacks[Pack.Index] = Pack.Outer;
}
- std::optional<unsigned> getSavedPackSize(unsigned Index,
- TemplateArgument Pattern) const {
-
- SmallVector<UnexpandedParameterPack, 2> Unexpanded;
- S.collectUnexpandedParameterPacks(Pattern, Unexpanded);
- if (Unexpanded.size() == 0 ||
+ // Return the size of the saved packs if all of them has the same size.
+ std::optional<unsigned> getSavedPackSizeIfAllEqual() const {
+ if (Packs.size() == 0 ||
Packs[0].Saved.getKind() != clang::TemplateArgument::Pack)
return {};
unsigned PackSize = Packs[0].Saved.pack_size();
@@ -4411,13 +4408,8 @@ Sema::TemplateDeductionResult Sema::DeduceTemplateArguments(
// that, in this case we are not processing all of the remaining
// arguments. We are only process as many arguments as much we have in
// the already deduced parameter.
- SmallVector<UnexpandedParameterPack, 2> Unexpanded;
- collectUnexpandedParameterPacks(ParamPattern, Unexpanded);
- assert(Unexpanded.size() != 0 && "We must have an unexpanded pack\n");
-
std::optional<unsigned> ArgPosAfterSubstitution =
- PackScope.getSavedPackSize(getDepthAndIndex(Unexpanded[0]).second,
- ParamPattern);
+ PackScope.getSavedPackSizeIfAllEqual();
if (!ArgPosAfterSubstitution)
continue;
>From 630c5c79db846402cad4b4d14c242980b9baa482 Mon Sep 17 00:00:00 2001
From: Gabor Spaits <gaborspaits1 at gmail.com>
Date: Thu, 25 Jan 2024 19:14:58 +0100
Subject: [PATCH 10/12] Add a test case for deduction without separator
---
clang/test/SemaTemplate/deduction.cpp | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/clang/test/SemaTemplate/deduction.cpp b/clang/test/SemaTemplate/deduction.cpp
index 5f18471becc9e8..b7988648431c67 100644
--- a/clang/test/SemaTemplate/deduction.cpp
+++ b/clang/test/SemaTemplate/deduction.cpp
@@ -447,12 +447,18 @@ namespace deduction_after_explicit_pack {
void call_bar() {
bar(args_tag<int, int>{}, 4, 8, 1001, 16, 23);
}
+
template <typename... Y, typename... T>
void foo(args_tag<Y...>, args_tag<T...>, type_identity_t<T>..., int mid, type_identity_t<T>...) {}
void call_foo() {
foo(args_tag<const int,const int, const int>{}, args_tag<int, int, int>{}, 4, 8, 9, 15, 16, 23, 1);
}
+ template <typename... Y, typename... T> void baz(args_tag<T...>, T..., T...) {}
+ void call_baz() {
+ baz(args_tag<int, int>{}, 1, 2, 3, 4);
+ }
+
// GCC alarmingly accepts this by deducing T={int} by matching the second
// parameter against the first argument, then passing the first argument
// through the first parameter.
>From 42f4b78d10d0bb0802cd2c08c23c6b41bf8c67fe Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=A1bor=20Spaits?=
<48805437+spaits at users.noreply.github.com>
Date: Thu, 25 Jan 2024 20:16:33 +0100
Subject: [PATCH 11/12] Add const to lambda parameter
Co-authored-by: Erich Keane <ekeane at nvidia.com>
---
clang/lib/Sema/SemaTemplateDeduction.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index 808b307729835e..9f11c4614fbed6 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -866,7 +866,7 @@ class PackDeductionScope {
return {};
unsigned PackSize = Packs[0].Saved.pack_size();
- if (std::all_of(Packs.begin() + 1, Packs.end(), [&PackSize](auto P) {
+ if (std::all_of(Packs.begin() + 1, Packs.end(), [&PackSize](const auto &P) {
return P.Saved.getKind() == TemplateArgument::Pack &&
P.Saved.pack_size() == PackSize;
}))
>From 18befe27a57b8ed6a153280b450576cb1d15f92e Mon Sep 17 00:00:00 2001
From: Gabor Spaits <gaborspaits1 at gmail.com>
Date: Thu, 25 Jan 2024 20:37:42 +0100
Subject: [PATCH 12/12] Update release notes
---
clang/docs/ReleaseNotes.rst | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 05c42f8485d4ee..4d5dc20e23dbfd 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -70,7 +70,9 @@ C++2c Feature Support
Resolutions to C++ Defect Reports
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
+- Substitute template parameter pack, when it is not explicitly specified
+ in the template parameters, but is deduced from a previous argument.
+ (`#78449: <https://github.com/llvm/llvm-project/issues/78449>`_).
C Language Changes
------------------
More information about the cfe-commits
mailing list