summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Dino Radakovic <dinor@google.com>2024-02-09 18:46:09 -0800
committerGravatar Copybara-Service <copybara-worker@google.com>2024-02-09 18:46:50 -0800
commit4358cb2f8cb304e64d9a2d2845f472297724e19f (patch)
tree524ff2f5c3c6b5325f7e013b28aee1c879276e4a
parentdf2c771ec596b385448117f237ee70be35efe4ce (diff)
`demangle`: Parse `requires` clauses on template params, before function return type
For example, this covers the following: ``` template <typename T> requires std::integral<T> int foo(); ``` Refactor parsing of `Q <requires-clause expr>` into a single function that performs backtracking to avoid reimplementing `ParseTemplateArgs` in terms of nested if-else blocks. PiperOrigin-RevId: 605785418 Change-Id: I118998a75e050dcf46af125b613b690312fd3cbe
-rw-r--r--absl/debugging/internal/demangle.cc40
-rw-r--r--absl/debugging/internal/demangle_test.cc36
2 files changed, 59 insertions, 17 deletions
diff --git a/absl/debugging/internal/demangle.cc b/absl/debugging/internal/demangle.cc
index c2f19451..f86421bf 100644
--- a/absl/debugging/internal/demangle.cc
+++ b/absl/debugging/internal/demangle.cc
@@ -579,7 +579,7 @@ static bool ParseUnresolvedName(State *state);
static bool ParseExpression(State *state);
static bool ParseExprPrimary(State *state);
static bool ParseExprCastValue(State *state);
-static bool ParseRequiresClauseExpression(State *state);
+static bool ParseQRequiresClauseExpr(State *state);
static bool ParseLocalName(State *state);
static bool ParseLocalNameSuffix(State *state);
static bool ParseDiscriminator(State *state);
@@ -646,12 +646,8 @@ static bool ParseEncoding(State *state) {
// Parsed: <(function) name> <bare-function-type>
// Pending: [`Q` <requires-clause expr>]
- ParseState copy = state->parse_state;
- if (ParseOneCharToken(state, 'Q') && ParseRequiresClauseExpression(state)) {
- return true; // <(function) name> <bare-function-type> `Q` <requires>
- }
- state->parse_state = copy;
- return true; // <(function) name> <bare-function-type>
+ ParseQRequiresClauseExpr(state); // restores state on failure
+ return true;
}
if (ParseSpecialName(state)) {
@@ -1505,16 +1501,14 @@ static bool ParseTemplateTemplateParam(State *state) {
ParseSubstitution(state, /*accept_std=*/false));
}
-// <template-args> ::= I <template-arg>+ E
-//
-// TODO(b/324066279): Implement optional [Q <requires-clause expr>] before E.
-// See: http://shortn/_Z7yM7PonSD
+// <template-args> ::= I <template-arg>+ [Q <requires-clause expr>] E
static bool ParseTemplateArgs(State *state) {
ComplexityGuard guard(state);
if (guard.IsTooComplex()) return false;
ParseState copy = state->parse_state;
DisableAppend(state);
if (ParseOneCharToken(state, 'I') && OneOrMore(ParseTemplateArg, state) &&
+ Optional(ParseQRequiresClauseExpr(state)) &&
ParseOneCharToken(state, 'E')) {
RestoreAppend(state, copy.append);
MaybeAppend(state, "<>");
@@ -1930,7 +1924,12 @@ static bool ParseExprCastValue(State *state) {
return false;
}
-// <requires-clause expr> is just an <expression>: http://shortn/_9E1Ul0rIM8
+// Parses `Q <requires-clause expr>`.
+// If parsing fails, applies backtracking to `state`.
+//
+// This function covers two symbols instead of one for convenience,
+// because in LLVM's Itanium ABI mangling grammar, <requires-clause expr>
+// always appears after Q.
//
// Does not emit the parsed `requires` clause to simplify the implementation.
// In other words, these two functions' mangled names will demangle identically:
@@ -1942,12 +1941,19 @@ static bool ParseExprCastValue(State *state) {
//
// template <typename T>
// int foo(T);
-static bool ParseRequiresClauseExpression(State *state) {
- bool original_append = state->parse_state.append;
+static bool ParseQRequiresClauseExpr(State *state) {
+ ParseState copy = state->parse_state;
DisableAppend(state);
- bool result = ParseExpression(state);
- RestoreAppend(state, original_append);
- return result;
+
+ // <requires-clause expr> is just an <expression>: http://shortn/_9E1Ul0rIM8
+ if (ParseOneCharToken(state, 'Q') && ParseExpression(state)) {
+ RestoreAppend(state, copy.append);
+ return true;
+ }
+
+ // also restores append
+ state->parse_state = copy;
+ return false;
}
// <local-name> ::= Z <(function) encoding> E <(entity) name> [<discriminator>]
diff --git a/absl/debugging/internal/demangle_test.cc b/absl/debugging/internal/demangle_test.cc
index 4b9d7711..86e66886 100644
--- a/absl/debugging/internal/demangle_test.cc
+++ b/absl/debugging/internal/demangle_test.cc
@@ -75,6 +75,42 @@ TEST(Demangle, FunctionTemplateWithFunctionRequiresClause) {
EXPECT_STREQ(tmp, "foo<>()");
}
+TEST(Demangle, FunctionWithTemplateParamRequiresClause) {
+ char tmp[100];
+
+ // template <typename T>
+ // requires std::integral<T>
+ // int foo();
+ //
+ // foo<int>();
+ ASSERT_TRUE(Demangle("_Z3fooIiQsr3stdE8integralIT_EEiv", tmp, sizeof(tmp)));
+ EXPECT_STREQ(tmp, "foo<>()");
+}
+
+TEST(Demangle, FunctionWithTemplateParamAndFunctionRequiresClauses) {
+ char tmp[100];
+
+ // template <typename T>
+ // requires std::integral<T>
+ // int foo() requires std::integral<T>;
+ //
+ // foo<int>();
+ ASSERT_TRUE(Demangle("_Z3fooIiQsr3stdE8integralIT_EEivQsr3stdE8integralIS0_E",
+ tmp, sizeof(tmp)));
+ EXPECT_STREQ(tmp, "foo<>()");
+}
+
+TEST(Demangle, FunctionTemplateBacktracksOnMalformedRequiresClause) {
+ char tmp[100];
+
+ // template <typename T>
+ // int foo(T);
+ //
+ // foo<int>(5);
+ // Except there's an extra `Q` where the mangled requires clause would be.
+ ASSERT_FALSE(Demangle("_Z3fooIiQEiT_", tmp, sizeof(tmp)));
+}
+
TEST(Demangle, FunctionTemplateWithAutoParam) {
char tmp[100];