Demangle C++ requires-expressions without parameters (rq ... E).

PiperOrigin-RevId: 637052089
Change-Id: I8e22c2479749c05d8ca5f9e8eeba480a93da2d18
diff --git a/absl/debugging/internal/demangle.cc b/absl/debugging/internal/demangle.cc
index 6012a59..cfcd64b 100644
--- a/absl/debugging/internal/demangle.cc
+++ b/absl/debugging/internal/demangle.cc
@@ -602,6 +602,7 @@
 static bool ParseExprPrimary(State *state);
 static bool ParseExprCastValueAndTrailingE(State *state);
 static bool ParseQRequiresClauseExpr(State *state);
+static bool ParseRequirement(State *state);
 static bool ParseLocalName(State *state);
 static bool ParseLocalNameSuffix(State *state);
 static bool ParseDiscriminator(State *state);
@@ -1852,6 +1853,7 @@
 //              ::= sr <type> <unqualified-name> <template-args>
 //              ::= sr <type> <unqualified-name>
 //              ::= u <source-name> <template-arg>* E  # vendor extension
+//              ::= rq <requirement>+ E
 static bool ParseExpression(State *state) {
   ComplexityGuard guard(state);
   if (guard.IsTooComplex()) return false;
@@ -1982,6 +1984,15 @@
   }
   state->parse_state = copy;
 
+  // <expression> ::= rq <requirement>+ E
+  //
+  // https://github.com/itanium-cxx-abi/cxx-abi/issues/24
+  if (ParseTwoCharToken(state, "rq") && OneOrMore(ParseRequirement, state) &&
+      ParseOneCharToken(state, 'E')) {
+    return true;
+  }
+  state->parse_state = copy;
+
   return ParseUnresolvedName(state);
 }
 
@@ -2098,6 +2109,39 @@
   return false;
 }
 
+// <requirement> ::= X <expression> [N] [R <type-constraint>]
+// <requirement> ::= T <type>
+// <requirement> ::= Q <constraint-expression>
+//
+// <type-constraint> ::= <name>
+//
+// <constraint-expression> ::= <expression>
+//
+// https://github.com/itanium-cxx-abi/cxx-abi/issues/24
+static bool ParseRequirement(State *state) {
+  ComplexityGuard guard(state);
+  if (guard.IsTooComplex()) return false;
+
+  ParseState copy = state->parse_state;
+
+  if (ParseOneCharToken(state, 'X') && ParseExpression(state) &&
+      Optional(ParseOneCharToken(state, 'N')) &&
+      // This logic backtracks cleanly if we eat an R but a valid type doesn't
+      // follow it.
+      (!ParseOneCharToken(state, 'R') || ParseName(state))) {
+    return true;
+  }
+  state->parse_state = copy;
+
+  if (ParseOneCharToken(state, 'T') && ParseType(state)) return true;
+  state->parse_state = copy;
+
+  if (ParseOneCharToken(state, 'Q') && ParseExpression(state)) return true;
+  state->parse_state = copy;
+
+  return false;
+}
+
 // <local-name> ::= Z <(function) encoding> E <(entity) name> [<discriminator>]
 //              ::= Z <(function) encoding> E s [<discriminator>]
 //
diff --git a/absl/debugging/internal/demangle_test.cc b/absl/debugging/internal/demangle_test.cc
index 51e3664..23556a8 100644
--- a/absl/debugging/internal/demangle_test.cc
+++ b/absl/debugging/internal/demangle_test.cc
@@ -212,6 +212,97 @@
   EXPECT_STREQ(tmp, "Fooer<>::foo<>()");
 }
 
+TEST(Demangle, LambdaRequiresTrue) {
+  char tmp[100];
+
+  // auto $_0::operator()<int>(int) const requires true
+  ASSERT_TRUE(Demangle("_ZNK3$_0clIiEEDaT_QLb1E", tmp, sizeof(tmp)));
+  EXPECT_STREQ(tmp, "$_0::operator()<>()");
+}
+
+TEST(Demangle, LambdaRequiresSimpleExpression) {
+  char tmp[100];
+
+  // auto $_0::operator()<int>(int) const requires 2 + 2 == 4
+  ASSERT_TRUE(Demangle("_ZNK3$_0clIiEEDaT_QeqplLi2ELi2ELi4E",
+                       tmp, sizeof(tmp)));
+  EXPECT_STREQ(tmp, "$_0::operator()<>()");
+}
+
+TEST(Demangle, LambdaRequiresRequiresExpressionContainingTrue) {
+  char tmp[100];
+
+  // auto $_0::operator()<int>(int) const requires requires { true; }
+  ASSERT_TRUE(Demangle("_ZNK3$_0clIiEEDaT_QrqXLb1EE", tmp, sizeof(tmp)));
+  EXPECT_STREQ(tmp, "$_0::operator()<>()");
+}
+
+TEST(Demangle, LambdaRequiresRequiresExpressionContainingConcept) {
+  char tmp[100];
+
+  // auto $_0::operator()<int>(int) const
+  // requires requires { std::same_as<decltype(fp), int>; }
+  ASSERT_TRUE(Demangle("_ZNK3$_0clIiEEDaT_QrqXsr3stdE7same_asIDtfp_EiEE",
+                       tmp, sizeof(tmp)));
+  EXPECT_STREQ(tmp, "$_0::operator()<>()");
+}
+
+TEST(Demangle, LambdaRequiresRequiresExpressionContainingNoexceptExpression) {
+  char tmp[100];
+
+  // auto $_0::operator()<int>(int) const
+  // requires requires { {fp + fp} noexcept; }
+  ASSERT_TRUE(Demangle("_ZNK3$_0clIiEEDaT_QrqXplfp_fp_NE", tmp, sizeof(tmp)));
+  EXPECT_STREQ(tmp, "$_0::operator()<>()");
+}
+
+TEST(Demangle, LambdaRequiresRequiresExpressionContainingReturnTypeConstraint) {
+  char tmp[100];
+
+  // auto $_0::operator()<int>(int) const
+  // requires requires { {fp + fp} -> std::same_as<decltype(fp)>; }
+  ASSERT_TRUE(Demangle("_ZNK3$_0clIiEEDaT_QrqXplfp_fp_RNSt7same_asIDtfp_EEEE",
+                       tmp, sizeof(tmp)));
+  EXPECT_STREQ(tmp, "$_0::operator()<>()");
+}
+
+TEST(Demangle, LambdaRequiresRequiresExpressionWithBothNoexceptAndReturnType) {
+  char tmp[100];
+
+  // auto $_0::operator()<int>(int) const
+  // requires requires { {fp + fp} noexcept -> std::same_as<decltype(fp)>; }
+  ASSERT_TRUE(Demangle("_ZNK3$_0clIiEEDaT_QrqXplfp_fp_NRNSt7same_asIDtfp_EEEE",
+                       tmp, sizeof(tmp)));
+  EXPECT_STREQ(tmp, "$_0::operator()<>()");
+}
+
+TEST(Demangle, LambdaRequiresRequiresExpressionContainingType) {
+  char tmp[100];
+
+  // auto $_0::operator()<S>(S) const
+  // requires requires { typename S::T; }
+  ASSERT_TRUE(Demangle("_ZNK3$_0clI1SEEDaT_QrqTNS2_1TEE", tmp, sizeof(tmp)));
+  EXPECT_STREQ(tmp, "$_0::operator()<>()");
+}
+
+TEST(Demangle, LambdaRequiresRequiresExpressionNestingAnotherRequires) {
+  char tmp[100];
+
+  // auto $_0::operator()<int>(int) const requires requires { requires true; }
+  ASSERT_TRUE(Demangle("_ZNK3$_0clIiEEDaT_QrqQLb1EE", tmp, sizeof(tmp)));
+  EXPECT_STREQ(tmp, "$_0::operator()<>()");
+}
+
+TEST(Demangle, LambdaRequiresRequiresExpressionContainingTwoRequirements) {
+  char tmp[100];
+
+  // auto $_0::operator()<int>(int) const
+  // requires requires { requires true; requires 2 + 2 == 4; }
+  ASSERT_TRUE(Demangle("_ZNK3$_0clIiEEDaT_QrqXLb1EXeqplLi2ELi2ELi4EE",
+                       tmp, sizeof(tmp)));
+  EXPECT_STREQ(tmp, "$_0::operator()<>()");
+}
+
 // Test corner cases of boundary conditions.
 TEST(Demangle, CornerCases) {
   char tmp[10];